blob: f856cf6a2b6bf3f765cf446e625ee71ca739ef90 [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001# BB Class inspired by ebuild.sh
2#
3# This class will test files after installation for certain
4# security issues and other kind of issues.
5#
6# Checks we do:
7# -Check the ownership and permissions
8# -Check the RUNTIME path for the $TMPDIR
9# -Check if .la files wrongly point to workdir
10# -Check if .pc files wrongly point to workdir
11# -Check if packages contains .debug directories or .so files
12# where they should be in -dev or -dbg
13# -Check if config.log contains traces to broken autoconf tests
14# -Check invalid characters (non-utf8) on some package metadata
15# -Ensure that binaries in base_[bindir|sbindir|libdir] do not link
16# into exec_prefix
17# -Check that scripts in base_[bindir|sbindir|libdir] do not reference
18# files under exec_prefix
Brad Bishopd7bf8c12018-02-25 22:55:05 -050019# -Check if the package name is upper case
Patrick Williamsc124f4f2015-09-15 14:41:29 -050020
Patrick Williamsc124f4f2015-09-15 14:41:29 -050021QA_SANE = "True"
22
23# Elect whether a given type of error is a warning or error, they may
24# have been set by other files.
25WARN_QA ?= "ldflags useless-rpaths rpaths staticdev libdir xorg-driver-abi \
26 textrel already-stripped incompatible-license files-invalid \
27 installed-vs-shipped compile-host-path install-host-path \
Brad Bishop15ae2502019-06-18 21:44:24 -040028 pn-overrides infodir build-deps src-uri-bad \
Patrick Williamsc124f4f2015-09-15 14:41:29 -050029 unknown-configure-option symlink-to-sysroot multilib \
Brad Bishopd89cb5f2019-04-10 09:02:41 -040030 invalid-packageconfig host-user-contaminated uppercase-pn patch-fuzz \
Patrick Williamsc124f4f2015-09-15 14:41:29 -050031 "
32ERROR_QA ?= "dev-so debug-deps dev-deps debug-files arch pkgconfig la \
33 perms dep-cmp pkgvarcheck perm-config perm-line perm-link \
34 split-strip packages-list pkgv-undefined var-undefined \
35 version-going-backwards expanded-d invalid-chars \
Brad Bishop19323692019-04-05 15:28:33 -040036 license-checksum dev-elf file-rdeps configure-unsafe \
Brad Bishopf3f93bb2019-10-16 14:33:32 -040037 configure-gettext perllocalpod \
Patrick Williamsc124f4f2015-09-15 14:41:29 -050038 "
Brad Bishopd7bf8c12018-02-25 22:55:05 -050039# Add usrmerge QA check based on distro feature
40ERROR_QA_append = "${@bb.utils.contains('DISTRO_FEATURES', 'usrmerge', ' usrmerge', '', d)}"
41
Patrick Williamsc124f4f2015-09-15 14:41:29 -050042FAKEROOT_QA = "host-user-contaminated"
43FAKEROOT_QA[doc] = "QA tests which need to run under fakeroot. If any \
44enabled tests are listed here, the do_package_qa task will run under fakeroot."
45
46ALL_QA = "${WARN_QA} ${ERROR_QA}"
47
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050048UNKNOWN_CONFIGURE_WHITELIST ?= "--enable-nls --disable-nls --disable-silent-rules --disable-dependency-tracking --with-libtool-sysroot --disable-static"
Patrick Williamsc124f4f2015-09-15 14:41:29 -050049
Patrick Williamsc0f7c042017-02-23 20:41:17 -060050def package_qa_clean_path(path, d, pkg=None):
51 """
52 Remove redundant paths from the path for display. If pkg isn't set then
53 TMPDIR is stripped, otherwise PKGDEST/pkg is stripped.
54 """
55 if pkg:
Brad Bishop6e60e8b2018-02-01 10:27:11 -050056 path = path.replace(os.path.join(d.getVar("PKGDEST"), pkg), "/")
57 return path.replace(d.getVar("TMPDIR"), "/").replace("//", "/")
Patrick Williamsc124f4f2015-09-15 14:41:29 -050058
59def package_qa_write_error(type, error, d):
Brad Bishop6e60e8b2018-02-01 10:27:11 -050060 logfile = d.getVar('QA_LOGFILE')
Patrick Williamsc124f4f2015-09-15 14:41:29 -050061 if logfile:
Brad Bishop6e60e8b2018-02-01 10:27:11 -050062 p = d.getVar('P')
Patrick Williamsc0f7c042017-02-23 20:41:17 -060063 with open(logfile, "a+") as f:
64 f.write("%s: %s [%s]\n" % (p, error, type))
Patrick Williamsc124f4f2015-09-15 14:41:29 -050065
66def package_qa_handle_error(error_class, error_msg, d):
Brad Bishop6e60e8b2018-02-01 10:27:11 -050067 if error_class in (d.getVar("ERROR_QA") or "").split():
Brad Bishopd7bf8c12018-02-25 22:55:05 -050068 package_qa_write_error(error_class, error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050069 bb.error("QA Issue: %s [%s]" % (error_msg, error_class))
70 d.setVar("QA_SANE", False)
71 return False
Brad Bishop6e60e8b2018-02-01 10:27:11 -050072 elif error_class in (d.getVar("WARN_QA") or "").split():
Brad Bishopd7bf8c12018-02-25 22:55:05 -050073 package_qa_write_error(error_class, error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050074 bb.warn("QA Issue: %s [%s]" % (error_msg, error_class))
75 else:
76 bb.note("QA Issue: %s [%s]" % (error_msg, error_class))
77 return True
78
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050079def package_qa_add_message(messages, section, new_msg):
80 if section not in messages:
81 messages[section] = new_msg
82 else:
83 messages[section] = messages[section] + "\n" + new_msg
84
Patrick Williamsc124f4f2015-09-15 14:41:29 -050085QAPATHTEST[libexec] = "package_qa_check_libexec"
86def package_qa_check_libexec(path,name, d, elf, messages):
87
88 # Skip the case where the default is explicitly /usr/libexec
Brad Bishop6e60e8b2018-02-01 10:27:11 -050089 libexec = d.getVar('libexecdir')
Patrick Williamsc124f4f2015-09-15 14:41:29 -050090 if libexec == "/usr/libexec":
91 return True
92
93 if 'libexec' in path.split(os.path.sep):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050094 package_qa_add_message(messages, "libexec", "%s: %s is using libexec please relocate to %s" % (name, package_qa_clean_path(path, d), libexec))
Patrick Williamsc124f4f2015-09-15 14:41:29 -050095 return False
96
97 return True
98
99QAPATHTEST[rpaths] = "package_qa_check_rpath"
100def package_qa_check_rpath(file,name, d, elf, messages):
101 """
102 Check for dangerous RPATHs
103 """
104 if not elf:
105 return
106
107 if os.path.islink(file):
108 return
109
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500110 bad_dirs = [d.getVar('BASE_WORKDIR'), d.getVar('STAGING_DIR_TARGET')]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500111
112 phdrs = elf.run_objdump("-p", d)
113
114 import re
Brad Bishop977dc1a2019-02-06 16:01:43 -0500115 rpath_re = re.compile(r"\s+RPATH\s+(.*)")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500116 for line in phdrs.split("\n"):
117 m = rpath_re.match(line)
118 if m:
119 rpath = m.group(1)
120 for dir in bad_dirs:
121 if dir in rpath:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500122 package_qa_add_message(messages, "rpaths", "package %s contains bad RPATH %s in file %s" % (name, rpath, file))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500123
124QAPATHTEST[useless-rpaths] = "package_qa_check_useless_rpaths"
125def package_qa_check_useless_rpaths(file, name, d, elf, messages):
126 """
127 Check for RPATHs that are useless but not dangerous
128 """
129 def rpath_eq(a, b):
130 return os.path.normpath(a) == os.path.normpath(b)
131
132 if not elf:
133 return
134
135 if os.path.islink(file):
136 return
137
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500138 libdir = d.getVar("libdir")
139 base_libdir = d.getVar("base_libdir")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500140
141 phdrs = elf.run_objdump("-p", d)
142
143 import re
Brad Bishop977dc1a2019-02-06 16:01:43 -0500144 rpath_re = re.compile(r"\s+RPATH\s+(.*)")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500145 for line in phdrs.split("\n"):
146 m = rpath_re.match(line)
147 if m:
148 rpath = m.group(1)
149 if rpath_eq(rpath, libdir) or rpath_eq(rpath, base_libdir):
150 # The dynamic linker searches both these places anyway. There is no point in
151 # looking there again.
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500152 package_qa_add_message(messages, "useless-rpaths", "%s: %s contains probably-redundant RPATH %s" % (name, package_qa_clean_path(file, d), rpath))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500153
154QAPATHTEST[dev-so] = "package_qa_check_dev"
155def package_qa_check_dev(path, name, d, elf, messages):
156 """
157 Check for ".so" library symlinks in non-dev packages
158 """
159
160 if not name.endswith("-dev") and not name.endswith("-dbg") and not name.endswith("-ptest") and not name.startswith("nativesdk-") and path.endswith(".so") and os.path.islink(path):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500161 package_qa_add_message(messages, "dev-so", "non -dev/-dbg/nativesdk- package contains symlink .so: %s path '%s'" % \
162 (name, package_qa_clean_path(path,d)))
163
164QAPATHTEST[dev-elf] = "package_qa_check_dev_elf"
165def package_qa_check_dev_elf(path, name, d, elf, messages):
166 """
167 Check that -dev doesn't contain real shared libraries. The test has to
168 check that the file is not a link and is an ELF object as some recipes
169 install link-time .so files that are linker scripts.
170 """
171 if name.endswith("-dev") and path.endswith(".so") and not os.path.islink(path) and elf:
172 package_qa_add_message(messages, "dev-elf", "-dev package contains non-symlink .so: %s path '%s'" % \
173 (name, package_qa_clean_path(path,d)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500174
175QAPATHTEST[staticdev] = "package_qa_check_staticdev"
176def package_qa_check_staticdev(path, name, d, elf, messages):
177 """
178 Check for ".a" library in non-staticdev packages
179 There are a number of exceptions to this rule, -pic packages can contain
180 static libraries, the _nonshared.a belong with their -dev packages and
181 libgcc.a, libgcov.a will be skipped in their packages
182 """
183
184 if not name.endswith("-pic") and not name.endswith("-staticdev") and not name.endswith("-ptest") and path.endswith(".a") and not path.endswith("_nonshared.a"):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500185 package_qa_add_message(messages, "staticdev", "non -staticdev package contains static .a library: %s path '%s'" % \
186 (name, package_qa_clean_path(path,d)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500187
188def package_qa_check_libdir(d):
189 """
190 Check for wrong library installation paths. For instance, catch
191 recipes installing /lib/bar.so when ${base_libdir}="lib32" or
192 installing in /usr/lib64 when ${libdir}="/usr/lib"
193 """
194 import re
195
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500196 pkgdest = d.getVar('PKGDEST')
197 base_libdir = d.getVar("base_libdir") + os.sep
198 libdir = d.getVar("libdir") + os.sep
199 libexecdir = d.getVar("libexecdir") + os.sep
200 exec_prefix = d.getVar("exec_prefix") + os.sep
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500201
202 messages = []
203
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500204 # The re's are purposely fuzzy, as some there are some .so.x.y.z files
205 # that don't follow the standard naming convention. It checks later
206 # that they are actual ELF files
Brad Bishop977dc1a2019-02-06 16:01:43 -0500207 lib_re = re.compile(r"^/lib.+\.so(\..+)?$")
208 exec_re = re.compile(r"^%s.*/lib.+\.so(\..+)?$" % exec_prefix)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500209
210 for root, dirs, files in os.walk(pkgdest):
211 if root == pkgdest:
212 # Skip subdirectories for any packages with libdir in INSANE_SKIP
213 skippackages = []
214 for package in dirs:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500215 if 'libdir' in (d.getVar('INSANE_SKIP_' + package) or "").split():
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500216 bb.note("Package %s skipping libdir QA test" % (package))
217 skippackages.append(package)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500218 elif d.getVar('PACKAGE_DEBUG_SPLIT_STYLE') == 'debug-file-directory' and package.endswith("-dbg"):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500219 bb.note("Package %s skipping libdir QA test for PACKAGE_DEBUG_SPLIT_STYLE equals debug-file-directory" % (package))
220 skippackages.append(package)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500221 for package in skippackages:
222 dirs.remove(package)
223 for file in files:
224 full_path = os.path.join(root, file)
225 rel_path = os.path.relpath(full_path, pkgdest)
226 if os.sep in rel_path:
227 package, rel_path = rel_path.split(os.sep, 1)
228 rel_path = os.sep + rel_path
229 if lib_re.match(rel_path):
230 if base_libdir not in rel_path:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500231 # make sure it's an actual ELF file
232 elf = oe.qa.ELFFile(full_path)
233 try:
234 elf.open()
235 messages.append("%s: found library in wrong location: %s" % (package, rel_path))
236 except (oe.qa.NotELFFileError):
237 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500238 if exec_re.match(rel_path):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500239 if libdir not in rel_path and libexecdir not in rel_path:
240 # make sure it's an actual ELF file
241 elf = oe.qa.ELFFile(full_path)
242 try:
243 elf.open()
244 messages.append("%s: found library in wrong location: %s" % (package, rel_path))
245 except (oe.qa.NotELFFileError):
246 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500247
248 if messages:
249 package_qa_handle_error("libdir", "\n".join(messages), d)
250
251QAPATHTEST[debug-files] = "package_qa_check_dbg"
252def package_qa_check_dbg(path, name, d, elf, messages):
253 """
254 Check for ".debug" files or directories outside of the dbg package
255 """
256
257 if not "-dbg" in name and not "-ptest" in name:
258 if '.debug' in path.split(os.path.sep):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500259 package_qa_add_message(messages, "debug-files", "non debug package contains .debug directory: %s path %s" % \
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500260 (name, package_qa_clean_path(path,d)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500261
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500262QAPATHTEST[arch] = "package_qa_check_arch"
263def package_qa_check_arch(path,name,d, elf, messages):
264 """
265 Check if archs are compatible
266 """
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800267 import re, oe.elf
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600268
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500269 if not elf:
270 return
271
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500272 target_os = d.getVar('TARGET_OS')
273 target_arch = d.getVar('TARGET_ARCH')
274 provides = d.getVar('PROVIDES')
275 bpn = d.getVar('BPN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500276
277 if target_arch == "allarch":
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500278 pn = d.getVar('PN')
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500279 package_qa_add_message(messages, "arch", pn + ": Recipe inherits the allarch class, but has packaged architecture-specific binaries")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500280 return
281
282 # FIXME: Cross package confuse this check, so just skip them
283 for s in ['cross', 'nativesdk', 'cross-canadian']:
284 if bb.data.inherits_class(s, d):
285 return
286
287 # avoid following links to /usr/bin (e.g. on udev builds)
288 # we will check the files pointed to anyway...
289 if os.path.islink(path):
290 return
291
292 #if this will throw an exception, then fix the dict above
293 (machine, osabi, abiversion, littleendian, bits) \
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800294 = oe.elf.machine_dict(d)[target_os][target_arch]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500295
296 # Check the architecture and endiannes of the binary
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600297 is_32 = (("virtual/kernel" in provides) or bb.data.inherits_class("module", d)) and \
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800298 (target_os == "linux-gnux32" or target_os == "linux-muslx32" or \
Brad Bishop977dc1a2019-02-06 16:01:43 -0500299 target_os == "linux-gnu_ilp32" or re.match(r'mips64.*32', d.getVar('DEFAULTTUNE')))
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800300 is_bpf = (oe.qa.elf_machine_to_string(elf.machine()) == "BPF")
301 if not ((machine == elf.machine()) or is_32 or is_bpf):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600302 package_qa_add_message(messages, "arch", "Architecture did not match (%s, expected %s) on %s" % \
303 (oe.qa.elf_machine_to_string(elf.machine()), oe.qa.elf_machine_to_string(machine), package_qa_clean_path(path,d)))
Brad Bishop19323692019-04-05 15:28:33 -0400304 elif not ((bits == elf.abiSize()) or is_32 or is_bpf):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500305 package_qa_add_message(messages, "arch", "Bit size did not match (%d to %d) %s on %s" % \
306 (bits, elf.abiSize(), bpn, package_qa_clean_path(path,d)))
Brad Bishop19323692019-04-05 15:28:33 -0400307 elif not ((littleendian == elf.isLittleEndian()) or is_bpf):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500308 package_qa_add_message(messages, "arch", "Endiannes did not match (%d to %d) on %s" % \
309 (littleendian, elf.isLittleEndian(), package_qa_clean_path(path,d)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500310
311QAPATHTEST[desktop] = "package_qa_check_desktop"
312def package_qa_check_desktop(path, name, d, elf, messages):
313 """
314 Run all desktop files through desktop-file-validate.
315 """
316 if path.endswith(".desktop"):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500317 desktop_file_validate = os.path.join(d.getVar('STAGING_BINDIR_NATIVE'),'desktop-file-validate')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500318 output = os.popen("%s %s" % (desktop_file_validate, path))
319 # This only produces output on errors
320 for l in output:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500321 package_qa_add_message(messages, "desktop", "Desktop file issue: " + l.strip())
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500322
323QAPATHTEST[textrel] = "package_qa_textrel"
324def package_qa_textrel(path, name, d, elf, messages):
325 """
326 Check if the binary contains relocations in .text
327 """
328
329 if not elf:
330 return
331
332 if os.path.islink(path):
333 return
334
335 phdrs = elf.run_objdump("-p", d)
336 sane = True
337
338 import re
Brad Bishop977dc1a2019-02-06 16:01:43 -0500339 textrel_re = re.compile(r"\s+TEXTREL\s+")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500340 for line in phdrs.split("\n"):
341 if textrel_re.match(line):
342 sane = False
Brad Bishop1d80a2e2019-11-15 16:35:03 -0500343 break
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500344
345 if not sane:
Brad Bishop1d80a2e2019-11-15 16:35:03 -0500346 path = package_qa_clean_path(path, d, name)
347 package_qa_add_message(messages, "textrel", "%s: ELF binary %s has relocations in .text" % (name, path))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500348
349QAPATHTEST[ldflags] = "package_qa_hash_style"
350def package_qa_hash_style(path, name, d, elf, messages):
351 """
352 Check if the binary has the right hash style...
353 """
354
355 if not elf:
356 return
357
358 if os.path.islink(path):
359 return
360
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500361 gnu_hash = "--hash-style=gnu" in d.getVar('LDFLAGS')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500362 if not gnu_hash:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500363 gnu_hash = "--hash-style=both" in d.getVar('LDFLAGS')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500364 if not gnu_hash:
365 return
366
367 sane = False
368 has_syms = False
369
370 phdrs = elf.run_objdump("-p", d)
371
372 # If this binary has symbols, we expect it to have GNU_HASH too.
373 for line in phdrs.split("\n"):
374 if "SYMTAB" in line:
375 has_syms = True
376 if "GNU_HASH" in line:
377 sane = True
378 if "[mips32]" in line or "[mips64]" in line:
379 sane = True
380
381 if has_syms and not sane:
Brad Bishopa5c52ff2018-11-23 10:55:50 +1300382 package_qa_add_message(messages, "ldflags", "No GNU_HASH in the ELF binary %s, didn't pass LDFLAGS?" % path)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500383
384
385QAPATHTEST[buildpaths] = "package_qa_check_buildpaths"
386def package_qa_check_buildpaths(path, name, d, elf, messages):
387 """
388 Check for build paths inside target files and error if not found in the whitelist
389 """
390 # Ignore .debug files, not interesting
391 if path.find(".debug") != -1:
392 return
393
394 # Ignore symlinks
395 if os.path.islink(path):
396 return
397
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500398 # Ignore ipk and deb's CONTROL dir
399 if path.find(name + "/CONTROL/") != -1 or path.find(name + "/DEBIAN/") != -1:
400 return
401
Brad Bishopd5ae7d92018-06-14 09:52:03 -0700402 tmpdir = bytes(d.getVar('TMPDIR'), encoding="utf-8")
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500403 with open(path, 'rb') as f:
Brad Bishopd5ae7d92018-06-14 09:52:03 -0700404 file_content = f.read()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500405 if tmpdir in file_content:
Brad Bishopf3fd2882019-06-21 08:06:37 -0400406 trimmed = path.replace(os.path.join (d.getVar("PKGDEST"), name), "")
407 package_qa_add_message(messages, "buildpaths", "File %s in package %s contains reference to TMPDIR" % (trimmed, name))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500408
409
410QAPATHTEST[xorg-driver-abi] = "package_qa_check_xorg_driver_abi"
411def package_qa_check_xorg_driver_abi(path, name, d, elf, messages):
412 """
413 Check that all packages containing Xorg drivers have ABI dependencies
414 """
415
416 # Skip dev, dbg or nativesdk packages
417 if name.endswith("-dev") or name.endswith("-dbg") or name.startswith("nativesdk-"):
418 return
419
420 driverdir = d.expand("${libdir}/xorg/modules/drivers/")
421 if driverdir in path and path.endswith(".so"):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500422 mlprefix = d.getVar('MLPREFIX') or ''
423 for rdep in bb.utils.explode_deps(d.getVar('RDEPENDS_' + name) or ""):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500424 if rdep.startswith("%sxorg-abi-" % mlprefix):
425 return
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500426 package_qa_add_message(messages, "xorg-driver-abi", "Package %s contains Xorg driver (%s) but no xorg-abi- dependencies" % (name, os.path.basename(path)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500427
428QAPATHTEST[infodir] = "package_qa_check_infodir"
429def package_qa_check_infodir(path, name, d, elf, messages):
430 """
431 Check that /usr/share/info/dir isn't shipped in a particular package
432 """
433 infodir = d.expand("${infodir}/dir")
434
435 if infodir in path:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500436 package_qa_add_message(messages, "infodir", "The /usr/share/info/dir file is not meant to be shipped in a particular package.")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500437
438QAPATHTEST[symlink-to-sysroot] = "package_qa_check_symlink_to_sysroot"
439def package_qa_check_symlink_to_sysroot(path, name, d, elf, messages):
440 """
441 Check that the package doesn't contain any absolute symlinks to the sysroot.
442 """
443 if os.path.islink(path):
444 target = os.readlink(path)
445 if os.path.isabs(target):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500446 tmpdir = d.getVar('TMPDIR')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500447 if target.startswith(tmpdir):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500448 trimmed = path.replace(os.path.join (d.getVar("PKGDEST"), name), "")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500449 package_qa_add_message(messages, "symlink-to-sysroot", "Symlink %s in %s points to TMPDIR" % (trimmed, name))
450
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600451# Check license variables
452do_populate_lic[postfuncs] += "populate_lic_qa_checksum"
453python populate_lic_qa_checksum() {
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500454 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600455 Check for changes in the license files.
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500456 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500457 sane = True
458
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500459 lic_files = d.getVar('LIC_FILES_CHKSUM') or ''
460 lic = d.getVar('LICENSE')
461 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500462
463 if lic == "CLOSED":
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500464 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500465
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500466 if not lic_files and d.getVar('SRC_URI'):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800467 sane &= package_qa_handle_error("license-checksum", pn + ": Recipe file fetches files and does not have license file information (LIC_FILES_CHKSUM)", d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500468
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500469 srcdir = d.getVar('S')
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500470 corebase_licensefile = d.getVar('COREBASE') + "/LICENSE"
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500471 for url in lic_files.split():
472 try:
473 (type, host, path, user, pswd, parm) = bb.fetch.decodeurl(url)
474 except bb.fetch.MalformedUrl:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800475 sane &= package_qa_handle_error("license-checksum", pn + ": LIC_FILES_CHKSUM contains an invalid URL: " + url, d)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500476 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500477 srclicfile = os.path.join(srcdir, path)
478 if not os.path.isfile(srclicfile):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800479 sane &= package_qa_handle_error("license-checksum", pn + ": LIC_FILES_CHKSUM points to an invalid file: " + srclicfile, d)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500480 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500481
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500482 if (srclicfile == corebase_licensefile):
483 bb.warn("${COREBASE}/LICENSE is not a valid license file, please use '${COMMON_LICENSE_DIR}/MIT' for a MIT License file in LIC_FILES_CHKSUM. This will become an error in the future")
484
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500485 recipemd5 = parm.get('md5', '')
486 beginline, endline = 0, 0
487 if 'beginline' in parm:
488 beginline = int(parm['beginline'])
489 if 'endline' in parm:
490 endline = int(parm['endline'])
491
492 if (not beginline) and (not endline):
493 md5chksum = bb.utils.md5_file(srclicfile)
Brad Bishop19323692019-04-05 15:28:33 -0400494 with open(srclicfile, 'r', errors='replace') as f:
495 license = f.read().splitlines()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500496 else:
Brad Bishop19323692019-04-05 15:28:33 -0400497 with open(srclicfile, 'rb') as f:
498 import hashlib
499 lineno = 0
500 license = []
501 m = hashlib.md5()
502 for line in f:
503 lineno += 1
504 if (lineno >= beginline):
505 if ((lineno <= endline) or not endline):
506 m.update(line)
507 license.append(line.decode('utf-8', errors='replace').rstrip())
508 else:
509 break
510 md5chksum = m.hexdigest()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500511 if recipemd5 == md5chksum:
512 bb.note (pn + ": md5 checksum matched for ", url)
513 else:
514 if recipemd5:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500515 msg = pn + ": The LIC_FILES_CHKSUM does not match for " + url
516 msg = msg + "\n" + pn + ": The new md5 checksum is " + md5chksum
Brad Bishop19323692019-04-05 15:28:33 -0400517 max_lines = int(d.getVar('QA_MAX_LICENSE_LINES') or 20)
518 if not license or license[-1] != '':
519 # Ensure that our license text ends with a line break
520 # (will be added with join() below).
521 license.append('')
522 remove = len(license) - max_lines
523 if remove > 0:
524 start = max_lines // 2
525 end = start + remove - 1
526 del license[start:end]
527 license.insert(start, '...')
528 msg = msg + "\n" + pn + ": Here is the selected license text:" + \
529 "\n" + \
530 "{:v^70}".format(" beginline=%d " % beginline if beginline else "") + \
531 "\n" + "\n".join(license) + \
532 "{:^^70}".format(" endline=%d " % endline if endline else "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500533 if beginline:
534 if endline:
535 srcfiledesc = "%s (lines %d through to %d)" % (srclicfile, beginline, endline)
536 else:
537 srcfiledesc = "%s (beginning on line %d)" % (srclicfile, beginline)
538 elif endline:
539 srcfiledesc = "%s (ending on line %d)" % (srclicfile, endline)
540 else:
541 srcfiledesc = srclicfile
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500542 msg = msg + "\n" + pn + ": Check if the license information has changed in %s to verify that the LICENSE value \"%s\" remains valid" % (srcfiledesc, lic)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500543
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500544 else:
545 msg = pn + ": LIC_FILES_CHKSUM is not specified for " + url
546 msg = msg + "\n" + pn + ": The md5 checksum is " + md5chksum
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800547 sane &= package_qa_handle_error("license-checksum", msg, d)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600548
549 if not sane:
550 bb.fatal("Fatal QA errors found, failing task.")
551}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500552
Brad Bishop19323692019-04-05 15:28:33 -0400553def qa_check_staged(path,d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500554 """
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500555 Check staged la and pc files for common problems like references to the work
556 directory.
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500557
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500558 As this is run after every stage we should be able to find the one
559 responsible for the errors easily even if we look at every .pc and .la file.
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500560 """
561
562 sane = True
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500563 tmpdir = d.getVar('TMPDIR')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500564 workdir = os.path.join(tmpdir, "work")
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500565 recipesysroot = d.getVar("RECIPE_SYSROOT")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500566
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500567 if bb.data.inherits_class("native", d) or bb.data.inherits_class("cross", d):
568 pkgconfigcheck = workdir
569 else:
570 pkgconfigcheck = tmpdir
571
Brad Bishop19323692019-04-05 15:28:33 -0400572 skip = (d.getVar('INSANE_SKIP') or "").split()
573 skip_la = False
574 if 'la' in skip:
575 bb.note("Recipe %s skipping qa checking: la" % d.getVar('PN'))
576 skip_la = True
577
578 skip_pkgconfig = False
579 if 'pkgconfig' in skip:
580 bb.note("Recipe %s skipping qa checking: pkgconfig" % d.getVar('PN'))
581 skip_pkgconfig = True
582
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500583 # find all .la and .pc files
584 # read the content
585 # and check for stuff that looks wrong
586 for root, dirs, files in os.walk(path):
587 for file in files:
588 path = os.path.join(root,file)
Brad Bishop19323692019-04-05 15:28:33 -0400589 if file.endswith(".la") and not skip_la:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500590 with open(path) as f:
591 file_content = f.read()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500592 file_content = file_content.replace(recipesysroot, "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500593 if workdir in file_content:
594 error_msg = "%s failed sanity test (workdir) in path %s" % (file,root)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800595 sane &= package_qa_handle_error("la", error_msg, d)
Brad Bishop19323692019-04-05 15:28:33 -0400596 elif file.endswith(".pc") and not skip_pkgconfig:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500597 with open(path) as f:
598 file_content = f.read()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500599 file_content = file_content.replace(recipesysroot, "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500600 if pkgconfigcheck in file_content:
601 error_msg = "%s failed sanity test (tmpdir) in path %s" % (file,root)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800602 sane &= package_qa_handle_error("pkgconfig", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500603
604 return sane
605
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500606# Run all package-wide warnfuncs and errorfuncs
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500607def package_qa_package(warnfuncs, errorfuncs, package, d):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500608 warnings = {}
609 errors = {}
610
611 for func in warnfuncs:
612 func(package, d, warnings)
613 for func in errorfuncs:
614 func(package, d, errors)
615
616 for w in warnings:
617 package_qa_handle_error(w, warnings[w], d)
618 for e in errors:
619 package_qa_handle_error(e, errors[e], d)
620
621 return len(errors) == 0
622
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500623# Run all recipe-wide warnfuncs and errorfuncs
624def package_qa_recipe(warnfuncs, errorfuncs, pn, d):
625 warnings = {}
626 errors = {}
627
628 for func in warnfuncs:
629 func(pn, d, warnings)
630 for func in errorfuncs:
631 func(pn, d, errors)
632
633 for w in warnings:
634 package_qa_handle_error(w, warnings[w], d)
635 for e in errors:
636 package_qa_handle_error(e, errors[e], d)
637
638 return len(errors) == 0
639
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500640# Walk over all files in a directory and call func
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500641def package_qa_walk(warnfuncs, errorfuncs, package, d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500642 import oe.qa
643
644 #if this will throw an exception, then fix the dict above
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500645 target_os = d.getVar('TARGET_OS')
646 target_arch = d.getVar('TARGET_ARCH')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500647
648 warnings = {}
649 errors = {}
650 for path in pkgfiles[package]:
651 elf = oe.qa.ELFFile(path)
652 try:
653 elf.open()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500654 except (IOError, oe.qa.NotELFFileError):
655 # IOError can happen if the packaging control files disappear,
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500656 elf = None
657 for func in warnfuncs:
658 func(path, package, d, elf, warnings)
659 for func in errorfuncs:
660 func(path, package, d, elf, errors)
661
662 for w in warnings:
663 package_qa_handle_error(w, warnings[w], d)
664 for e in errors:
665 package_qa_handle_error(e, errors[e], d)
666
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500667def package_qa_check_rdepends(pkg, pkgdest, skip, taskdeps, packages, d):
668 # Don't do this check for kernel/module recipes, there aren't too many debug/development
669 # packages and you can get false positives e.g. on kernel-module-lirc-dev
670 if bb.data.inherits_class("kernel", d) or bb.data.inherits_class("module-base", d):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500671 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500672
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500673 if not "-dbg" in pkg and not "packagegroup-" in pkg and not "-image" in pkg:
674 localdata = bb.data.createCopy(d)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500675 localdata.setVar('OVERRIDES', localdata.getVar('OVERRIDES') + ':' + pkg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500676
677 # Now check the RDEPENDS
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500678 rdepends = bb.utils.explode_deps(localdata.getVar('RDEPENDS') or "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500679
680 # Now do the sanity check!!!
681 if "build-deps" not in skip:
682 for rdepend in rdepends:
683 if "-dbg" in rdepend and "debug-deps" not in skip:
684 error_msg = "%s rdepends on %s" % (pkg,rdepend)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500685 package_qa_handle_error("debug-deps", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500686 if (not "-dev" in pkg and not "-staticdev" in pkg) and rdepend.endswith("-dev") and "dev-deps" not in skip:
687 error_msg = "%s rdepends on %s" % (pkg, rdepend)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500688 package_qa_handle_error("dev-deps", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500689 if rdepend not in packages:
690 rdep_data = oe.packagedata.read_subpkgdata(rdepend, d)
691 if rdep_data and 'PN' in rdep_data and rdep_data['PN'] in taskdeps:
692 continue
693 if not rdep_data or not 'PN' in rdep_data:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500694 pkgdata_dir = d.getVar("PKGDATA_DIR")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500695 try:
696 possibles = os.listdir("%s/runtime-rprovides/%s/" % (pkgdata_dir, rdepend))
697 except OSError:
698 possibles = []
699 for p in possibles:
700 rdep_data = oe.packagedata.read_subpkgdata(p, d)
701 if rdep_data and 'PN' in rdep_data and rdep_data['PN'] in taskdeps:
702 break
703 if rdep_data and 'PN' in rdep_data and rdep_data['PN'] in taskdeps:
704 continue
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500705 if rdep_data and 'PN' in rdep_data:
706 error_msg = "%s rdepends on %s, but it isn't a build dependency, missing %s in DEPENDS or PACKAGECONFIG?" % (pkg, rdepend, rdep_data['PN'])
707 else:
708 error_msg = "%s rdepends on %s, but it isn't a build dependency?" % (pkg, rdepend)
709 package_qa_handle_error("build-deps", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500710
711 if "file-rdeps" not in skip:
712 ignored_file_rdeps = set(['/bin/sh', '/usr/bin/env', 'rtld(GNU_HASH)'])
713 if bb.data.inherits_class('nativesdk', d):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500714 ignored_file_rdeps |= set(['/bin/bash', '/usr/bin/perl', 'perl'])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500715 # For Saving the FILERDEPENDS
716 filerdepends = {}
717 rdep_data = oe.packagedata.read_subpkgdata(pkg, d)
718 for key in rdep_data:
719 if key.startswith("FILERDEPENDS_"):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500720 for subkey in bb.utils.explode_deps(rdep_data[key]):
721 if subkey not in ignored_file_rdeps and \
722 not subkey.startswith('perl('):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500723 # We already know it starts with FILERDEPENDS_
724 filerdepends[subkey] = key[13:]
725
726 if filerdepends:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500727 done = rdepends[:]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500728 # Add the rprovides of itself
729 if pkg not in done:
730 done.insert(0, pkg)
731
732 # The python is not a package, but python-core provides it, so
733 # skip checking /usr/bin/python if python is in the rdeps, in
734 # case there is a RDEPENDS_pkg = "python" in the recipe.
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500735 for py in [ d.getVar('MLPREFIX') + "python", "python" ]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500736 if py in done:
737 filerdepends.pop("/usr/bin/python",None)
738 done.remove(py)
739 for rdep in done:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500740 # The file dependencies may contain package names, e.g.,
741 # perl
742 filerdepends.pop(rdep,None)
743
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500744 # For Saving the FILERPROVIDES, RPROVIDES and FILES_INFO
745 rdep_data = oe.packagedata.read_subpkgdata(rdep, d)
746 for key in rdep_data:
747 if key.startswith("FILERPROVIDES_") or key.startswith("RPROVIDES_"):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500748 for subkey in bb.utils.explode_deps(rdep_data[key]):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500749 filerdepends.pop(subkey,None)
750 # Add the files list to the rprovides
751 if key == "FILES_INFO":
752 # Use eval() to make it as a dict
753 for subkey in eval(rdep_data[key]):
754 filerdepends.pop(subkey,None)
755 if not filerdepends:
756 # Break if all the file rdepends are met
757 break
758 if filerdepends:
759 for key in filerdepends:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500760 error_msg = "%s contained in package %s requires %s, but no providers found in RDEPENDS_%s?" % \
761 (filerdepends[key].replace("_%s" % pkg, "").replace("@underscore@", "_"), pkg, key, pkg)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500762 package_qa_handle_error("file-rdeps", error_msg, d)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500763package_qa_check_rdepends[vardepsexclude] = "OVERRIDES"
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500764
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500765def package_qa_check_deps(pkg, pkgdest, d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500766
767 localdata = bb.data.createCopy(d)
768 localdata.setVar('OVERRIDES', pkg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500769
770 def check_valid_deps(var):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500771 try:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500772 rvar = bb.utils.explode_dep_versions2(localdata.getVar(var) or "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500773 except ValueError as e:
774 bb.fatal("%s_%s: %s" % (var, pkg, e))
775 for dep in rvar:
776 for v in rvar[dep]:
777 if v and not v.startswith(('< ', '= ', '> ', '<= ', '>=')):
778 error_msg = "%s_%s is invalid: %s (%s) only comparisons <, =, >, <=, and >= are allowed" % (var, pkg, dep, v)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500779 package_qa_handle_error("dep-cmp", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500780
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500781 check_valid_deps('RDEPENDS')
782 check_valid_deps('RRECOMMENDS')
783 check_valid_deps('RSUGGESTS')
784 check_valid_deps('RPROVIDES')
785 check_valid_deps('RREPLACES')
786 check_valid_deps('RCONFLICTS')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500787
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500788QAPKGTEST[usrmerge] = "package_qa_check_usrmerge"
789def package_qa_check_usrmerge(pkg, d, messages):
790 pkgdest = d.getVar('PKGDEST')
791 pkg_dir = pkgdest + os.sep + pkg + os.sep
792 merged_dirs = ['bin', 'sbin', 'lib'] + d.getVar('MULTILIB_VARIANTS').split()
793 for f in merged_dirs:
794 if os.path.exists(pkg_dir + f) and not os.path.islink(pkg_dir + f):
795 msg = "%s package is not obeying usrmerge distro feature. /%s should be relocated to /usr." % (pkg, f)
796 package_qa_add_message(messages, "usrmerge", msg)
797 return False
798 return True
799
Brad Bishopf3f93bb2019-10-16 14:33:32 -0400800QAPKGTEST[perllocalpod] = "package_qa_check_perllocalpod"
801def package_qa_check_perllocalpod(pkg, d, messages):
802 """
803 Check that the recipe didn't ship a perlocal.pod file, which shouldn't be
804 installed in a distribution package. cpan.bbclass sets NO_PERLLOCAL=1 to
805 handle this for most recipes.
806 """
807 import glob
808 pkgd = oe.path.join(d.getVar('PKGDEST'), pkg)
809 podpath = oe.path.join(pkgd, d.getVar("libdir"), "perl*", "*", "*", "perllocal.pod")
810
811 matches = glob.glob(podpath)
812 if matches:
813 matches = [package_qa_clean_path(path, d, pkg) for path in matches]
814 msg = "%s contains perllocal.pod (%s), should not be installed" % (pkg, " ".join(matches))
815 package_qa_add_message(messages, "perllocalpod", msg)
816
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500817QAPKGTEST[expanded-d] = "package_qa_check_expanded_d"
818def package_qa_check_expanded_d(package, d, messages):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500819 """
820 Check for the expanded D (${D}) value in pkg_* and FILES
821 variables, warn the user to use it correctly.
822 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500823 sane = True
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500824 expanded_d = d.getVar('D')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500825
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500826 for var in 'FILES','pkg_preinst', 'pkg_postinst', 'pkg_prerm', 'pkg_postrm':
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500827 bbvar = d.getVar(var + "_" + package) or ""
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500828 if expanded_d in bbvar:
829 if var == 'FILES':
830 package_qa_add_message(messages, "expanded-d", "FILES in %s recipe should not contain the ${D} variable as it references the local build directory not the target filesystem, best solution is to remove the ${D} reference" % package)
831 sane = False
832 else:
833 package_qa_add_message(messages, "expanded-d", "%s in %s recipe contains ${D}, it should be replaced by $D instead" % (var, package))
834 sane = False
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500835 return sane
836
837def package_qa_check_encoding(keys, encode, d):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600838 def check_encoding(key, enc):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500839 sane = True
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500840 value = d.getVar(key)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500841 if value:
842 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600843 s = value.encode(enc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500844 except UnicodeDecodeError as e:
845 error_msg = "%s has non %s characters" % (key,enc)
846 sane = False
847 package_qa_handle_error("invalid-chars", error_msg, d)
848 return sane
849
850 for key in keys:
851 sane = check_encoding(key, encode)
852 if not sane:
853 break
854
855HOST_USER_UID := "${@os.getuid()}"
856HOST_USER_GID := "${@os.getgid()}"
857
858QAPATHTEST[host-user-contaminated] = "package_qa_check_host_user"
859def package_qa_check_host_user(path, name, d, elf, messages):
860 """Check for paths outside of /home which are owned by the user running bitbake."""
861
862 if not os.path.lexists(path):
863 return
864
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500865 dest = d.getVar('PKGDEST')
866 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500867 home = os.path.join(dest, 'home')
868 if path == home or path.startswith(home + os.sep):
869 return
870
871 try:
872 stat = os.lstat(path)
873 except OSError as exc:
874 import errno
875 if exc.errno != errno.ENOENT:
876 raise
877 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500878 check_uid = int(d.getVar('HOST_USER_UID'))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500879 if stat.st_uid == check_uid:
Brad Bishop96ff1982019-08-19 13:50:42 -0400880 package_qa_add_message(messages, "host-user-contaminated", "%s: %s is owned by uid %d, which is the same as the user running bitbake. This may be due to host contamination" % (pn, package_qa_clean_path(path, d, name), check_uid))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500881 return False
882
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500883 check_gid = int(d.getVar('HOST_USER_GID'))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500884 if stat.st_gid == check_gid:
Brad Bishop96ff1982019-08-19 13:50:42 -0400885 package_qa_add_message(messages, "host-user-contaminated", "%s: %s is owned by gid %d, which is the same as the user running bitbake. This may be due to host contamination" % (pn, package_qa_clean_path(path, d, name), check_gid))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500886 return False
887 return True
888
Brad Bishop15ae2502019-06-18 21:44:24 -0400889QARECIPETEST[src-uri-bad] = "package_qa_check_src_uri"
890def package_qa_check_src_uri(pn, d, messages):
891 import re
892
893 if "${PN}" in d.getVar("SRC_URI", False):
894 package_qa_handle_error("src-uri-bad", "%s: SRC_URI uses PN not BPN" % pn, d)
895
896 pn = d.getVar("SRC_URI")
897 if re.search(r"github\.com/.+/.+/archive/.+", pn):
898 package_qa_handle_error("src-uri-bad", "%s: SRC_URI uses unstable GitHub archives" % pn, d)
899
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500900
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500901# The PACKAGE FUNC to scan each package
902python do_package_qa () {
903 import subprocess
904 import oe.packagedata
905
906 bb.note("DO PACKAGE QA")
907
908 bb.build.exec_func("read_subpackage_metadata", d)
909
910 # Check non UTF-8 characters on recipe's metadata
911 package_qa_check_encoding(['DESCRIPTION', 'SUMMARY', 'LICENSE', 'SECTION'], 'utf-8', d)
912
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500913 logdir = d.getVar('T')
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500914 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500915
916 # Check the compile log for host contamination
917 compilelog = os.path.join(logdir,"log.do_compile")
918
919 if os.path.exists(compilelog):
920 statement = "grep -e 'CROSS COMPILE Badness:' -e 'is unsafe for cross-compilation' %s > /dev/null" % compilelog
921 if subprocess.call(statement, shell=True) == 0:
922 msg = "%s: The compile log indicates that host include and/or library paths were used.\n \
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500923 Please check the log '%s' for more information." % (pn, compilelog)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500924 package_qa_handle_error("compile-host-path", msg, d)
925
926 # Check the install log for host contamination
927 installlog = os.path.join(logdir,"log.do_install")
928
929 if os.path.exists(installlog):
930 statement = "grep -e 'CROSS COMPILE Badness:' -e 'is unsafe for cross-compilation' %s > /dev/null" % installlog
931 if subprocess.call(statement, shell=True) == 0:
932 msg = "%s: The install log indicates that host include and/or library paths were used.\n \
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500933 Please check the log '%s' for more information." % (pn, installlog)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500934 package_qa_handle_error("install-host-path", msg, d)
935
936 # Scan the packages...
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500937 pkgdest = d.getVar('PKGDEST')
938 packages = set((d.getVar('PACKAGES') or '').split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500939
940 cpath = oe.cachedpath.CachedPath()
941 global pkgfiles
942 pkgfiles = {}
943 for pkg in packages:
944 pkgfiles[pkg] = []
945 for walkroot, dirs, files in cpath.walk(pkgdest + "/" + pkg):
946 for file in files:
947 pkgfiles[pkg].append(walkroot + os.sep + file)
948
949 # no packages should be scanned
950 if not packages:
951 return
952
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500953 import re
954 # The package name matches the [a-z0-9.+-]+ regular expression
Brad Bishop977dc1a2019-02-06 16:01:43 -0500955 pkgname_pattern = re.compile(r"^[a-z0-9.+-]+$")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500956
957 taskdepdata = d.getVar("BB_TASKDEPDATA", False)
958 taskdeps = set()
959 for dep in taskdepdata:
960 taskdeps.add(taskdepdata[dep][0])
961
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500962 def parse_test_matrix(matrix_name):
963 testmatrix = d.getVarFlags(matrix_name) or {}
964 g = globals()
965 warnchecks = []
966 for w in (d.getVar("WARN_QA") or "").split():
967 if w in skip:
968 continue
969 if w in testmatrix and testmatrix[w] in g:
970 warnchecks.append(g[testmatrix[w]])
971
972 errorchecks = []
973 for e in (d.getVar("ERROR_QA") or "").split():
974 if e in skip:
975 continue
976 if e in testmatrix and testmatrix[e] in g:
977 errorchecks.append(g[testmatrix[e]])
978 return warnchecks, errorchecks
979
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500980 for package in packages:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500981 skip = set((d.getVar('INSANE_SKIP') or "").split() +
982 (d.getVar('INSANE_SKIP_' + package) or "").split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500983 if skip:
984 bb.note("Package %s skipping QA tests: %s" % (package, str(skip)))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500985
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500986 bb.note("Checking Package: %s" % package)
987 # Check package name
988 if not pkgname_pattern.match(package):
989 package_qa_handle_error("pkgname",
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500990 "%s doesn't match the [a-z0-9.+-]+ regex" % package, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500991
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500992 warn_checks, error_checks = parse_test_matrix("QAPATHTEST")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500993 package_qa_walk(warn_checks, error_checks, package, d)
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500994
995 warn_checks, error_checks = parse_test_matrix("QAPKGTEST")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500996 package_qa_package(warn_checks, error_checks, package, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500997
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500998 package_qa_check_rdepends(package, pkgdest, skip, taskdeps, packages, d)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500999 package_qa_check_deps(package, pkgdest, d)
1000
1001 warn_checks, error_checks = parse_test_matrix("QARECIPETEST")
1002 package_qa_recipe(warn_checks, error_checks, pn, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001003
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001004 if 'libdir' in d.getVar("ALL_QA").split():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001005 package_qa_check_libdir(d)
1006
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001007 qa_sane = d.getVar("QA_SANE")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001008 if not qa_sane:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001009 bb.fatal("QA run found fatal errors. Please consider fixing them.")
1010 bb.note("DONE with PACKAGE QA")
1011}
1012
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001013# binutils is used for most checks, so need to set as dependency
1014# POPULATESYSROOTDEPS is defined in staging class.
1015do_package_qa[depends] += "${POPULATESYSROOTDEPS}"
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001016do_package_qa[vardepsexclude] = "BB_TASKDEPDATA"
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001017do_package_qa[rdeptask] = "do_packagedata"
1018addtask do_package_qa after do_packagedata do_package before do_build
1019
Brad Bishop19323692019-04-05 15:28:33 -04001020# Add the package specific INSANE_SKIPs to the sstate dependencies
1021python() {
1022 pkgs = (d.getVar('PACKAGES') or '').split()
1023 for pkg in pkgs:
1024 d.appendVarFlag("do_package_qa", "vardeps", " INSANE_SKIP_{}".format(pkg))
1025}
1026
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001027SSTATETASKS += "do_package_qa"
1028do_package_qa[sstate-inputdirs] = ""
1029do_package_qa[sstate-outputdirs] = ""
1030python do_package_qa_setscene () {
1031 sstate_setscene(d)
1032}
1033addtask do_package_qa_setscene
1034
1035python do_qa_staging() {
1036 bb.note("QA checking staging")
Brad Bishop19323692019-04-05 15:28:33 -04001037 if not qa_check_staged(d.expand('${SYSROOT_DESTDIR}${libdir}'), d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001038 bb.fatal("QA staging was broken by the package built above")
1039}
1040
Brad Bishopd89cb5f2019-04-10 09:02:41 -04001041python do_qa_patch() {
1042 import subprocess
1043
1044 ###########################################################################
1045 # Check patch.log for fuzz warnings
1046 #
1047 # Further information on why we check for patch fuzz warnings:
1048 # http://lists.openembedded.org/pipermail/openembedded-core/2018-March/148675.html
1049 # https://bugzilla.yoctoproject.org/show_bug.cgi?id=10450
1050 ###########################################################################
1051
1052 logdir = d.getVar('T')
1053 patchlog = os.path.join(logdir,"log.do_patch")
1054
1055 if os.path.exists(patchlog):
1056 fuzzheader = '--- Patch fuzz start ---'
1057 fuzzfooter = '--- Patch fuzz end ---'
1058 statement = "grep -e '%s' %s > /dev/null" % (fuzzheader, patchlog)
1059 if subprocess.call(statement, shell=True) == 0:
1060 msg = "Fuzz detected:\n\n"
1061 fuzzmsg = ""
1062 inFuzzInfo = False
1063 f = open(patchlog, "r")
1064 for line in f:
1065 if fuzzheader in line:
1066 inFuzzInfo = True
1067 fuzzmsg = ""
1068 elif fuzzfooter in line:
1069 fuzzmsg = fuzzmsg.replace('\n\n', '\n')
1070 msg += fuzzmsg
1071 msg += "\n"
1072 inFuzzInfo = False
1073 elif inFuzzInfo and not 'Now at patch' in line:
1074 fuzzmsg += line
1075 f.close()
1076 msg += "The context lines in the patches can be updated with devtool:\n"
1077 msg += "\n"
1078 msg += " devtool modify %s\n" % d.getVar('PN')
1079 msg += " devtool finish --force-patch-refresh %s <layer_path>\n\n" % d.getVar('PN')
1080 msg += "Don't forget to review changes done by devtool!\n"
1081 if 'patch-fuzz' in d.getVar('ERROR_QA'):
1082 bb.error(msg)
1083 elif 'patch-fuzz' in d.getVar('WARN_QA'):
1084 bb.warn(msg)
1085 msg = "Patch log indicates that patches do not apply cleanly."
1086 package_qa_handle_error("patch-fuzz", msg, d)
1087}
1088
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001089python do_qa_configure() {
1090 import subprocess
1091
1092 ###########################################################################
1093 # Check config.log for cross compile issues
1094 ###########################################################################
1095
1096 configs = []
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001097 workdir = d.getVar('WORKDIR')
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001098
Brad Bishop19323692019-04-05 15:28:33 -04001099 skip = (d.getVar('INSANE_SKIP') or "").split()
1100 skip_configure_unsafe = False
1101 if 'configure-unsafe' in skip:
1102 bb.note("Recipe %s skipping qa checking: configure-unsafe" % d.getVar('PN'))
1103 skip_configure_unsafe = True
1104
1105 if bb.data.inherits_class('autotools', d) and not skip_configure_unsafe:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001106 bb.note("Checking autotools environment for common misconfiguration")
1107 for root, dirs, files in os.walk(workdir):
1108 statement = "grep -q -F -e 'CROSS COMPILE Badness:' -e 'is unsafe for cross-compilation' %s" % \
1109 os.path.join(root,"config.log")
1110 if "config.log" in files:
1111 if subprocess.call(statement, shell=True) == 0:
Brad Bishop19323692019-04-05 15:28:33 -04001112 error_msg = """This autoconf log indicates errors, it looked at host include and/or library paths while determining system capabilities.
1113Rerun configure task after fixing this."""
1114 package_qa_handle_error("configure-unsafe", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001115
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001116 if "configure.ac" in files:
1117 configs.append(os.path.join(root,"configure.ac"))
1118 if "configure.in" in files:
1119 configs.append(os.path.join(root, "configure.in"))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001120
1121 ###########################################################################
1122 # Check gettext configuration and dependencies are correct
1123 ###########################################################################
1124
Brad Bishop19323692019-04-05 15:28:33 -04001125 skip_configure_gettext = False
1126 if 'configure-gettext' in skip:
1127 bb.note("Recipe %s skipping qa checking: configure-gettext" % d.getVar('PN'))
1128 skip_configure_gettext = True
1129
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001130 cnf = d.getVar('EXTRA_OECONF') or ""
Brad Bishop19323692019-04-05 15:28:33 -04001131 if not ("gettext" in d.getVar('P') or "gcc-runtime" in d.getVar('P') or \
1132 "--disable-nls" in cnf or skip_configure_gettext):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001133 ml = d.getVar("MLPREFIX") or ""
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001134 if bb.data.inherits_class('cross-canadian', d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001135 gt = "nativesdk-gettext"
1136 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001137 gt = "gettext-native"
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001138 deps = bb.utils.explode_deps(d.getVar('DEPENDS') or "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001139 if gt not in deps:
1140 for config in configs:
1141 gnu = "grep \"^[[:space:]]*AM_GNU_GETTEXT\" %s >/dev/null" % config
1142 if subprocess.call(gnu, shell=True) == 0:
Brad Bishopd89cb5f2019-04-10 09:02:41 -04001143 error_msg = "AM_GNU_GETTEXT used but no inherit gettext"
Brad Bishop19323692019-04-05 15:28:33 -04001144 package_qa_handle_error("configure-gettext", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001145
1146 ###########################################################################
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001147 # Check unrecognised configure options (with a white list)
1148 ###########################################################################
Brad Bishopc342db32019-05-15 21:57:59 -04001149 if bb.data.inherits_class("autotools", d) or bb.data.inherits_class("meson", d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001150 bb.note("Checking configure output for unrecognised options")
1151 try:
Brad Bishopc342db32019-05-15 21:57:59 -04001152 if bb.data.inherits_class("autotools", d):
1153 flag = "WARNING: unrecognized options:"
1154 log = os.path.join(d.getVar('B'), 'config.log')
1155 if bb.data.inherits_class("meson", d):
1156 flag = "WARNING: Unknown options:"
1157 log = os.path.join(d.getVar('T'), 'log.do_configure')
1158 output = subprocess.check_output(['grep', '-F', flag, log]).decode("utf-8").replace(', ', ' ').replace('"', '')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001159 options = set()
1160 for line in output.splitlines():
1161 options |= set(line.partition(flag)[2].split())
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001162 whitelist = set(d.getVar("UNKNOWN_CONFIGURE_WHITELIST").split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001163 options -= whitelist
1164 if options:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001165 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001166 error_msg = pn + ": configure was passed unrecognised options: " + " ".join(options)
1167 package_qa_handle_error("unknown-configure-option", error_msg, d)
1168 except subprocess.CalledProcessError:
1169 pass
1170
1171 # Check invalid PACKAGECONFIG
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001172 pkgconfig = (d.getVar("PACKAGECONFIG") or "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001173 if pkgconfig:
1174 pkgconfigflags = d.getVarFlags("PACKAGECONFIG") or {}
1175 for pconfig in pkgconfig:
1176 if pconfig not in pkgconfigflags:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001177 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001178 error_msg = "%s: invalid PACKAGECONFIG: %s" % (pn, pconfig)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001179 package_qa_handle_error("invalid-packageconfig", error_msg, d)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001180
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001181 qa_sane = d.getVar("QA_SANE")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001182 if not qa_sane:
1183 bb.fatal("Fatal QA errors found, failing task.")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001184}
1185
1186python do_qa_unpack() {
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001187 src_uri = d.getVar('SRC_URI')
1188 s_dir = d.getVar('S')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001189 if src_uri and not os.path.exists(s_dir):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001190 bb.warn('%s: the directory %s (%s) pointed to by the S variable doesn\'t exist - please set S within the recipe to point to where the source has been unpacked to' % (d.getVar('PN'), d.getVar('S', False), s_dir))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001191}
1192
1193# The Staging Func, to check all staging
1194#addtask qa_staging after do_populate_sysroot before do_build
1195do_populate_sysroot[postfuncs] += "do_qa_staging "
1196
Brad Bishopd89cb5f2019-04-10 09:02:41 -04001197# Check for patch fuzz
1198do_patch[postfuncs] += "do_qa_patch "
1199
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001200# Check broken config.log files, for packages requiring Gettext which
1201# don't have it in DEPENDS.
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001202#addtask qa_configure after do_configure before do_compile
1203do_configure[postfuncs] += "do_qa_configure "
1204
1205# Check does S exist.
1206do_unpack[postfuncs] += "do_qa_unpack"
1207
1208python () {
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001209 import re
1210
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001211 tests = d.getVar('ALL_QA').split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001212 if "desktop" in tests:
1213 d.appendVar("PACKAGE_DEPENDS", " desktop-file-utils-native")
1214
1215 ###########################################################################
1216 # Check various variables
1217 ###########################################################################
1218
1219 # Checking ${FILESEXTRAPATHS}
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001220 extrapaths = (d.getVar("FILESEXTRAPATHS") or "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001221 if '__default' not in extrapaths.split(":"):
1222 msg = "FILESEXTRAPATHS-variable, must always use _prepend (or _append)\n"
1223 msg += "type of assignment, and don't forget the colon.\n"
1224 msg += "Please assign it with the format of:\n"
1225 msg += " FILESEXTRAPATHS_append := \":${THISDIR}/Your_Files_Path\" or\n"
1226 msg += " FILESEXTRAPATHS_prepend := \"${THISDIR}/Your_Files_Path:\"\n"
1227 msg += "in your bbappend file\n\n"
1228 msg += "Your incorrect assignment is:\n"
1229 msg += "%s\n" % extrapaths
1230 bb.warn(msg)
1231
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001232 overrides = d.getVar('OVERRIDES').split(':')
1233 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001234 if pn in overrides:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001235 msg = 'Recipe %s has PN of "%s" which is in OVERRIDES, this can result in unexpected behaviour.' % (d.getVar("FILE"), pn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001236 package_qa_handle_error("pn-overrides", msg, d)
Brad Bishop977dc1a2019-02-06 16:01:43 -05001237 prog = re.compile(r'[A-Z]')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001238 if prog.search(pn):
1239 package_qa_handle_error("uppercase-pn", 'PN: %s is upper case, this can result in unexpected behavior.' % pn, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001240
Brad Bishop08902b02019-08-20 09:16:51 -04001241 # Some people mistakenly use DEPENDS_${PN} instead of DEPENDS and wonder
1242 # why it doesn't work.
1243 if (d.getVar(d.expand('DEPENDS_${PN}'))):
1244 package_qa_handle_error("pkgvarcheck", "recipe uses DEPENDS_${PN}, should use DEPENDS", d)
1245
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001246 issues = []
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001247 if (d.getVar('PACKAGES') or "").split():
1248 for dep in (d.getVar('QADEPENDS') or "").split():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001249 d.appendVarFlag('do_package_qa', 'depends', " %s:do_populate_sysroot" % dep)
1250 for var in 'RDEPENDS', 'RRECOMMENDS', 'RSUGGESTS', 'RCONFLICTS', 'RPROVIDES', 'RREPLACES', 'FILES', 'pkg_preinst', 'pkg_postinst', 'pkg_prerm', 'pkg_postrm', 'ALLOW_EMPTY':
1251 if d.getVar(var, False):
1252 issues.append(var)
1253
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001254 fakeroot_tests = d.getVar('FAKEROOT_QA').split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001255 if set(tests) & set(fakeroot_tests):
1256 d.setVarFlag('do_package_qa', 'fakeroot', '1')
1257 d.appendVarFlag('do_package_qa', 'depends', ' virtual/fakeroot-native:do_populate_sysroot')
1258 else:
1259 d.setVarFlag('do_package_qa', 'rdeptask', '')
1260 for i in issues:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001261 package_qa_handle_error("pkgvarcheck", "%s: Variable %s is set as not being package specific, please fix this." % (d.getVar("FILE"), i), d)
1262 qa_sane = d.getVar("QA_SANE")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001263 if not qa_sane:
1264 bb.fatal("Fatal QA errors found, failing task.")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001265}