blob: 0695a04438732be7b66088367b13c14b4aa1d458 [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 \
37 configure-gettext \
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
343
344 if not sane:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500345 package_qa_add_message(messages, "textrel", "ELF binary '%s' has relocations in .text" % path)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500346
347QAPATHTEST[ldflags] = "package_qa_hash_style"
348def package_qa_hash_style(path, name, d, elf, messages):
349 """
350 Check if the binary has the right hash style...
351 """
352
353 if not elf:
354 return
355
356 if os.path.islink(path):
357 return
358
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500359 gnu_hash = "--hash-style=gnu" in d.getVar('LDFLAGS')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500360 if not gnu_hash:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500361 gnu_hash = "--hash-style=both" in d.getVar('LDFLAGS')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500362 if not gnu_hash:
363 return
364
365 sane = False
366 has_syms = False
367
368 phdrs = elf.run_objdump("-p", d)
369
370 # If this binary has symbols, we expect it to have GNU_HASH too.
371 for line in phdrs.split("\n"):
372 if "SYMTAB" in line:
373 has_syms = True
374 if "GNU_HASH" in line:
375 sane = True
376 if "[mips32]" in line or "[mips64]" in line:
377 sane = True
378
379 if has_syms and not sane:
Brad Bishopa5c52ff2018-11-23 10:55:50 +1300380 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 -0500381
382
383QAPATHTEST[buildpaths] = "package_qa_check_buildpaths"
384def package_qa_check_buildpaths(path, name, d, elf, messages):
385 """
386 Check for build paths inside target files and error if not found in the whitelist
387 """
388 # Ignore .debug files, not interesting
389 if path.find(".debug") != -1:
390 return
391
392 # Ignore symlinks
393 if os.path.islink(path):
394 return
395
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500396 # Ignore ipk and deb's CONTROL dir
397 if path.find(name + "/CONTROL/") != -1 or path.find(name + "/DEBIAN/") != -1:
398 return
399
Brad Bishopd5ae7d92018-06-14 09:52:03 -0700400 tmpdir = bytes(d.getVar('TMPDIR'), encoding="utf-8")
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500401 with open(path, 'rb') as f:
Brad Bishopd5ae7d92018-06-14 09:52:03 -0700402 file_content = f.read()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500403 if tmpdir in file_content:
Brad Bishopf3fd2882019-06-21 08:06:37 -0400404 trimmed = path.replace(os.path.join (d.getVar("PKGDEST"), name), "")
405 package_qa_add_message(messages, "buildpaths", "File %s in package %s contains reference to TMPDIR" % (trimmed, name))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500406
407
408QAPATHTEST[xorg-driver-abi] = "package_qa_check_xorg_driver_abi"
409def package_qa_check_xorg_driver_abi(path, name, d, elf, messages):
410 """
411 Check that all packages containing Xorg drivers have ABI dependencies
412 """
413
414 # Skip dev, dbg or nativesdk packages
415 if name.endswith("-dev") or name.endswith("-dbg") or name.startswith("nativesdk-"):
416 return
417
418 driverdir = d.expand("${libdir}/xorg/modules/drivers/")
419 if driverdir in path and path.endswith(".so"):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500420 mlprefix = d.getVar('MLPREFIX') or ''
421 for rdep in bb.utils.explode_deps(d.getVar('RDEPENDS_' + name) or ""):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500422 if rdep.startswith("%sxorg-abi-" % mlprefix):
423 return
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500424 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 -0500425
426QAPATHTEST[infodir] = "package_qa_check_infodir"
427def package_qa_check_infodir(path, name, d, elf, messages):
428 """
429 Check that /usr/share/info/dir isn't shipped in a particular package
430 """
431 infodir = d.expand("${infodir}/dir")
432
433 if infodir in path:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500434 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 -0500435
436QAPATHTEST[symlink-to-sysroot] = "package_qa_check_symlink_to_sysroot"
437def package_qa_check_symlink_to_sysroot(path, name, d, elf, messages):
438 """
439 Check that the package doesn't contain any absolute symlinks to the sysroot.
440 """
441 if os.path.islink(path):
442 target = os.readlink(path)
443 if os.path.isabs(target):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500444 tmpdir = d.getVar('TMPDIR')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500445 if target.startswith(tmpdir):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500446 trimmed = path.replace(os.path.join (d.getVar("PKGDEST"), name), "")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500447 package_qa_add_message(messages, "symlink-to-sysroot", "Symlink %s in %s points to TMPDIR" % (trimmed, name))
448
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600449# Check license variables
450do_populate_lic[postfuncs] += "populate_lic_qa_checksum"
451python populate_lic_qa_checksum() {
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500452 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600453 Check for changes in the license files.
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500454 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500455 sane = True
456
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500457 lic_files = d.getVar('LIC_FILES_CHKSUM') or ''
458 lic = d.getVar('LICENSE')
459 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500460
461 if lic == "CLOSED":
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500462 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500463
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500464 if not lic_files and d.getVar('SRC_URI'):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800465 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 -0500466
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500467 srcdir = d.getVar('S')
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500468 corebase_licensefile = d.getVar('COREBASE') + "/LICENSE"
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500469 for url in lic_files.split():
470 try:
471 (type, host, path, user, pswd, parm) = bb.fetch.decodeurl(url)
472 except bb.fetch.MalformedUrl:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800473 sane &= package_qa_handle_error("license-checksum", pn + ": LIC_FILES_CHKSUM contains an invalid URL: " + url, d)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500474 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500475 srclicfile = os.path.join(srcdir, path)
476 if not os.path.isfile(srclicfile):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800477 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 -0500478 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500479
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500480 if (srclicfile == corebase_licensefile):
481 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")
482
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500483 recipemd5 = parm.get('md5', '')
484 beginline, endline = 0, 0
485 if 'beginline' in parm:
486 beginline = int(parm['beginline'])
487 if 'endline' in parm:
488 endline = int(parm['endline'])
489
490 if (not beginline) and (not endline):
491 md5chksum = bb.utils.md5_file(srclicfile)
Brad Bishop19323692019-04-05 15:28:33 -0400492 with open(srclicfile, 'r', errors='replace') as f:
493 license = f.read().splitlines()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500494 else:
Brad Bishop19323692019-04-05 15:28:33 -0400495 with open(srclicfile, 'rb') as f:
496 import hashlib
497 lineno = 0
498 license = []
499 m = hashlib.md5()
500 for line in f:
501 lineno += 1
502 if (lineno >= beginline):
503 if ((lineno <= endline) or not endline):
504 m.update(line)
505 license.append(line.decode('utf-8', errors='replace').rstrip())
506 else:
507 break
508 md5chksum = m.hexdigest()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500509 if recipemd5 == md5chksum:
510 bb.note (pn + ": md5 checksum matched for ", url)
511 else:
512 if recipemd5:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500513 msg = pn + ": The LIC_FILES_CHKSUM does not match for " + url
514 msg = msg + "\n" + pn + ": The new md5 checksum is " + md5chksum
Brad Bishop19323692019-04-05 15:28:33 -0400515 max_lines = int(d.getVar('QA_MAX_LICENSE_LINES') or 20)
516 if not license or license[-1] != '':
517 # Ensure that our license text ends with a line break
518 # (will be added with join() below).
519 license.append('')
520 remove = len(license) - max_lines
521 if remove > 0:
522 start = max_lines // 2
523 end = start + remove - 1
524 del license[start:end]
525 license.insert(start, '...')
526 msg = msg + "\n" + pn + ": Here is the selected license text:" + \
527 "\n" + \
528 "{:v^70}".format(" beginline=%d " % beginline if beginline else "") + \
529 "\n" + "\n".join(license) + \
530 "{:^^70}".format(" endline=%d " % endline if endline else "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500531 if beginline:
532 if endline:
533 srcfiledesc = "%s (lines %d through to %d)" % (srclicfile, beginline, endline)
534 else:
535 srcfiledesc = "%s (beginning on line %d)" % (srclicfile, beginline)
536 elif endline:
537 srcfiledesc = "%s (ending on line %d)" % (srclicfile, endline)
538 else:
539 srcfiledesc = srclicfile
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500540 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 -0500541
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500542 else:
543 msg = pn + ": LIC_FILES_CHKSUM is not specified for " + url
544 msg = msg + "\n" + pn + ": The md5 checksum is " + md5chksum
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800545 sane &= package_qa_handle_error("license-checksum", msg, d)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600546
547 if not sane:
548 bb.fatal("Fatal QA errors found, failing task.")
549}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500550
Brad Bishop19323692019-04-05 15:28:33 -0400551def qa_check_staged(path,d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500552 """
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500553 Check staged la and pc files for common problems like references to the work
554 directory.
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500555
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500556 As this is run after every stage we should be able to find the one
557 responsible for the errors easily even if we look at every .pc and .la file.
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500558 """
559
560 sane = True
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500561 tmpdir = d.getVar('TMPDIR')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500562 workdir = os.path.join(tmpdir, "work")
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500563 recipesysroot = d.getVar("RECIPE_SYSROOT")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500564
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500565 if bb.data.inherits_class("native", d) or bb.data.inherits_class("cross", d):
566 pkgconfigcheck = workdir
567 else:
568 pkgconfigcheck = tmpdir
569
Brad Bishop19323692019-04-05 15:28:33 -0400570 skip = (d.getVar('INSANE_SKIP') or "").split()
571 skip_la = False
572 if 'la' in skip:
573 bb.note("Recipe %s skipping qa checking: la" % d.getVar('PN'))
574 skip_la = True
575
576 skip_pkgconfig = False
577 if 'pkgconfig' in skip:
578 bb.note("Recipe %s skipping qa checking: pkgconfig" % d.getVar('PN'))
579 skip_pkgconfig = True
580
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500581 # find all .la and .pc files
582 # read the content
583 # and check for stuff that looks wrong
584 for root, dirs, files in os.walk(path):
585 for file in files:
586 path = os.path.join(root,file)
Brad Bishop19323692019-04-05 15:28:33 -0400587 if file.endswith(".la") and not skip_la:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500588 with open(path) as f:
589 file_content = f.read()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500590 file_content = file_content.replace(recipesysroot, "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500591 if workdir in file_content:
592 error_msg = "%s failed sanity test (workdir) in path %s" % (file,root)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800593 sane &= package_qa_handle_error("la", error_msg, d)
Brad Bishop19323692019-04-05 15:28:33 -0400594 elif file.endswith(".pc") and not skip_pkgconfig:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500595 with open(path) as f:
596 file_content = f.read()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500597 file_content = file_content.replace(recipesysroot, "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500598 if pkgconfigcheck in file_content:
599 error_msg = "%s failed sanity test (tmpdir) in path %s" % (file,root)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800600 sane &= package_qa_handle_error("pkgconfig", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500601
602 return sane
603
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500604# Run all package-wide warnfuncs and errorfuncs
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500605def package_qa_package(warnfuncs, errorfuncs, package, d):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500606 warnings = {}
607 errors = {}
608
609 for func in warnfuncs:
610 func(package, d, warnings)
611 for func in errorfuncs:
612 func(package, d, errors)
613
614 for w in warnings:
615 package_qa_handle_error(w, warnings[w], d)
616 for e in errors:
617 package_qa_handle_error(e, errors[e], d)
618
619 return len(errors) == 0
620
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500621# Run all recipe-wide warnfuncs and errorfuncs
622def package_qa_recipe(warnfuncs, errorfuncs, pn, d):
623 warnings = {}
624 errors = {}
625
626 for func in warnfuncs:
627 func(pn, d, warnings)
628 for func in errorfuncs:
629 func(pn, d, errors)
630
631 for w in warnings:
632 package_qa_handle_error(w, warnings[w], d)
633 for e in errors:
634 package_qa_handle_error(e, errors[e], d)
635
636 return len(errors) == 0
637
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500638# Walk over all files in a directory and call func
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500639def package_qa_walk(warnfuncs, errorfuncs, package, d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500640 import oe.qa
641
642 #if this will throw an exception, then fix the dict above
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500643 target_os = d.getVar('TARGET_OS')
644 target_arch = d.getVar('TARGET_ARCH')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500645
646 warnings = {}
647 errors = {}
648 for path in pkgfiles[package]:
649 elf = oe.qa.ELFFile(path)
650 try:
651 elf.open()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500652 except (IOError, oe.qa.NotELFFileError):
653 # IOError can happen if the packaging control files disappear,
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500654 elf = None
655 for func in warnfuncs:
656 func(path, package, d, elf, warnings)
657 for func in errorfuncs:
658 func(path, package, d, elf, errors)
659
660 for w in warnings:
661 package_qa_handle_error(w, warnings[w], d)
662 for e in errors:
663 package_qa_handle_error(e, errors[e], d)
664
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500665def package_qa_check_rdepends(pkg, pkgdest, skip, taskdeps, packages, d):
666 # Don't do this check for kernel/module recipes, there aren't too many debug/development
667 # packages and you can get false positives e.g. on kernel-module-lirc-dev
668 if bb.data.inherits_class("kernel", d) or bb.data.inherits_class("module-base", d):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500669 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500670
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500671 if not "-dbg" in pkg and not "packagegroup-" in pkg and not "-image" in pkg:
672 localdata = bb.data.createCopy(d)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500673 localdata.setVar('OVERRIDES', localdata.getVar('OVERRIDES') + ':' + pkg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500674
675 # Now check the RDEPENDS
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500676 rdepends = bb.utils.explode_deps(localdata.getVar('RDEPENDS') or "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500677
678 # Now do the sanity check!!!
679 if "build-deps" not in skip:
680 for rdepend in rdepends:
681 if "-dbg" in rdepend and "debug-deps" not in skip:
682 error_msg = "%s rdepends on %s" % (pkg,rdepend)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500683 package_qa_handle_error("debug-deps", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500684 if (not "-dev" in pkg and not "-staticdev" in pkg) and rdepend.endswith("-dev") and "dev-deps" not in skip:
685 error_msg = "%s rdepends on %s" % (pkg, rdepend)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500686 package_qa_handle_error("dev-deps", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500687 if rdepend not in packages:
688 rdep_data = oe.packagedata.read_subpkgdata(rdepend, d)
689 if rdep_data and 'PN' in rdep_data and rdep_data['PN'] in taskdeps:
690 continue
691 if not rdep_data or not 'PN' in rdep_data:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500692 pkgdata_dir = d.getVar("PKGDATA_DIR")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500693 try:
694 possibles = os.listdir("%s/runtime-rprovides/%s/" % (pkgdata_dir, rdepend))
695 except OSError:
696 possibles = []
697 for p in possibles:
698 rdep_data = oe.packagedata.read_subpkgdata(p, d)
699 if rdep_data and 'PN' in rdep_data and rdep_data['PN'] in taskdeps:
700 break
701 if rdep_data and 'PN' in rdep_data and rdep_data['PN'] in taskdeps:
702 continue
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500703 if rdep_data and 'PN' in rdep_data:
704 error_msg = "%s rdepends on %s, but it isn't a build dependency, missing %s in DEPENDS or PACKAGECONFIG?" % (pkg, rdepend, rdep_data['PN'])
705 else:
706 error_msg = "%s rdepends on %s, but it isn't a build dependency?" % (pkg, rdepend)
707 package_qa_handle_error("build-deps", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500708
709 if "file-rdeps" not in skip:
710 ignored_file_rdeps = set(['/bin/sh', '/usr/bin/env', 'rtld(GNU_HASH)'])
711 if bb.data.inherits_class('nativesdk', d):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500712 ignored_file_rdeps |= set(['/bin/bash', '/usr/bin/perl', 'perl'])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500713 # For Saving the FILERDEPENDS
714 filerdepends = {}
715 rdep_data = oe.packagedata.read_subpkgdata(pkg, d)
716 for key in rdep_data:
717 if key.startswith("FILERDEPENDS_"):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500718 for subkey in bb.utils.explode_deps(rdep_data[key]):
719 if subkey not in ignored_file_rdeps and \
720 not subkey.startswith('perl('):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500721 # We already know it starts with FILERDEPENDS_
722 filerdepends[subkey] = key[13:]
723
724 if filerdepends:
725 next = rdepends
726 done = rdepends[:]
727 # Find all the rdepends on the dependency chain
728 while next:
729 new = []
730 for rdep in next:
731 rdep_data = oe.packagedata.read_subpkgdata(rdep, d)
732 sub_rdeps = rdep_data.get("RDEPENDS_" + rdep)
733 if not sub_rdeps:
734 continue
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500735 for sub_rdep in bb.utils.explode_deps(sub_rdeps):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500736 if sub_rdep in done:
737 continue
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500738 if oe.packagedata.has_subpkgdata(sub_rdep, d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500739 # It's a new rdep
740 done.append(sub_rdep)
741 new.append(sub_rdep)
742 next = new
743
744 # Add the rprovides of itself
745 if pkg not in done:
746 done.insert(0, pkg)
747
748 # The python is not a package, but python-core provides it, so
749 # skip checking /usr/bin/python if python is in the rdeps, in
750 # case there is a RDEPENDS_pkg = "python" in the recipe.
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500751 for py in [ d.getVar('MLPREFIX') + "python", "python" ]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500752 if py in done:
753 filerdepends.pop("/usr/bin/python",None)
754 done.remove(py)
755 for rdep in done:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500756 # The file dependencies may contain package names, e.g.,
757 # perl
758 filerdepends.pop(rdep,None)
759
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500760 # For Saving the FILERPROVIDES, RPROVIDES and FILES_INFO
761 rdep_data = oe.packagedata.read_subpkgdata(rdep, d)
762 for key in rdep_data:
763 if key.startswith("FILERPROVIDES_") or key.startswith("RPROVIDES_"):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500764 for subkey in bb.utils.explode_deps(rdep_data[key]):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500765 filerdepends.pop(subkey,None)
766 # Add the files list to the rprovides
767 if key == "FILES_INFO":
768 # Use eval() to make it as a dict
769 for subkey in eval(rdep_data[key]):
770 filerdepends.pop(subkey,None)
771 if not filerdepends:
772 # Break if all the file rdepends are met
773 break
774 if filerdepends:
775 for key in filerdepends:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500776 error_msg = "%s contained in package %s requires %s, but no providers found in RDEPENDS_%s?" % \
777 (filerdepends[key].replace("_%s" % pkg, "").replace("@underscore@", "_"), pkg, key, pkg)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500778 package_qa_handle_error("file-rdeps", error_msg, d)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500779package_qa_check_rdepends[vardepsexclude] = "OVERRIDES"
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500780
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500781def package_qa_check_deps(pkg, pkgdest, d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500782
783 localdata = bb.data.createCopy(d)
784 localdata.setVar('OVERRIDES', pkg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500785
786 def check_valid_deps(var):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500787 try:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500788 rvar = bb.utils.explode_dep_versions2(localdata.getVar(var) or "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500789 except ValueError as e:
790 bb.fatal("%s_%s: %s" % (var, pkg, e))
791 for dep in rvar:
792 for v in rvar[dep]:
793 if v and not v.startswith(('< ', '= ', '> ', '<= ', '>=')):
794 error_msg = "%s_%s is invalid: %s (%s) only comparisons <, =, >, <=, and >= are allowed" % (var, pkg, dep, v)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500795 package_qa_handle_error("dep-cmp", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500796
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500797 check_valid_deps('RDEPENDS')
798 check_valid_deps('RRECOMMENDS')
799 check_valid_deps('RSUGGESTS')
800 check_valid_deps('RPROVIDES')
801 check_valid_deps('RREPLACES')
802 check_valid_deps('RCONFLICTS')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500803
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500804QAPKGTEST[usrmerge] = "package_qa_check_usrmerge"
805def package_qa_check_usrmerge(pkg, d, messages):
806 pkgdest = d.getVar('PKGDEST')
807 pkg_dir = pkgdest + os.sep + pkg + os.sep
808 merged_dirs = ['bin', 'sbin', 'lib'] + d.getVar('MULTILIB_VARIANTS').split()
809 for f in merged_dirs:
810 if os.path.exists(pkg_dir + f) and not os.path.islink(pkg_dir + f):
811 msg = "%s package is not obeying usrmerge distro feature. /%s should be relocated to /usr." % (pkg, f)
812 package_qa_add_message(messages, "usrmerge", msg)
813 return False
814 return True
815
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500816QAPKGTEST[expanded-d] = "package_qa_check_expanded_d"
817def package_qa_check_expanded_d(package, d, messages):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500818 """
819 Check for the expanded D (${D}) value in pkg_* and FILES
820 variables, warn the user to use it correctly.
821 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500822 sane = True
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500823 expanded_d = d.getVar('D')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500824
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500825 for var in 'FILES','pkg_preinst', 'pkg_postinst', 'pkg_prerm', 'pkg_postrm':
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500826 bbvar = d.getVar(var + "_" + package) or ""
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500827 if expanded_d in bbvar:
828 if var == 'FILES':
829 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)
830 sane = False
831 else:
832 package_qa_add_message(messages, "expanded-d", "%s in %s recipe contains ${D}, it should be replaced by $D instead" % (var, package))
833 sane = False
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500834 return sane
835
836def package_qa_check_encoding(keys, encode, d):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600837 def check_encoding(key, enc):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500838 sane = True
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500839 value = d.getVar(key)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500840 if value:
841 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600842 s = value.encode(enc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500843 except UnicodeDecodeError as e:
844 error_msg = "%s has non %s characters" % (key,enc)
845 sane = False
846 package_qa_handle_error("invalid-chars", error_msg, d)
847 return sane
848
849 for key in keys:
850 sane = check_encoding(key, encode)
851 if not sane:
852 break
853
854HOST_USER_UID := "${@os.getuid()}"
855HOST_USER_GID := "${@os.getgid()}"
856
857QAPATHTEST[host-user-contaminated] = "package_qa_check_host_user"
858def package_qa_check_host_user(path, name, d, elf, messages):
859 """Check for paths outside of /home which are owned by the user running bitbake."""
860
861 if not os.path.lexists(path):
862 return
863
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500864 dest = d.getVar('PKGDEST')
865 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500866 home = os.path.join(dest, 'home')
867 if path == home or path.startswith(home + os.sep):
868 return
869
870 try:
871 stat = os.lstat(path)
872 except OSError as exc:
873 import errno
874 if exc.errno != errno.ENOENT:
875 raise
876 else:
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500877 rootfs_path = path[len(dest):]
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:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500880 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, rootfs_path, 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:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500885 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, rootfs_path, 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
1241 issues = []
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001242 if (d.getVar('PACKAGES') or "").split():
1243 for dep in (d.getVar('QADEPENDS') or "").split():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001244 d.appendVarFlag('do_package_qa', 'depends', " %s:do_populate_sysroot" % dep)
1245 for var in 'RDEPENDS', 'RRECOMMENDS', 'RSUGGESTS', 'RCONFLICTS', 'RPROVIDES', 'RREPLACES', 'FILES', 'pkg_preinst', 'pkg_postinst', 'pkg_prerm', 'pkg_postrm', 'ALLOW_EMPTY':
1246 if d.getVar(var, False):
1247 issues.append(var)
1248
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001249 fakeroot_tests = d.getVar('FAKEROOT_QA').split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001250 if set(tests) & set(fakeroot_tests):
1251 d.setVarFlag('do_package_qa', 'fakeroot', '1')
1252 d.appendVarFlag('do_package_qa', 'depends', ' virtual/fakeroot-native:do_populate_sysroot')
1253 else:
1254 d.setVarFlag('do_package_qa', 'rdeptask', '')
1255 for i in issues:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001256 package_qa_handle_error("pkgvarcheck", "%s: Variable %s is set as not being package specific, please fix this." % (d.getVar("FILE"), i), d)
1257 qa_sane = d.getVar("QA_SANE")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001258 if not qa_sane:
1259 bb.fatal("Fatal QA errors found, failing task.")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001260}