blob: 9605ac2baeefb506d81e78b7b5ae59a2d1ca581b [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
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:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500725 done = rdepends[:]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500726 # Add the rprovides of itself
727 if pkg not in done:
728 done.insert(0, pkg)
729
730 # The python is not a package, but python-core provides it, so
731 # skip checking /usr/bin/python if python is in the rdeps, in
732 # case there is a RDEPENDS_pkg = "python" in the recipe.
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500733 for py in [ d.getVar('MLPREFIX') + "python", "python" ]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500734 if py in done:
735 filerdepends.pop("/usr/bin/python",None)
736 done.remove(py)
737 for rdep in done:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500738 # The file dependencies may contain package names, e.g.,
739 # perl
740 filerdepends.pop(rdep,None)
741
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500742 # For Saving the FILERPROVIDES, RPROVIDES and FILES_INFO
743 rdep_data = oe.packagedata.read_subpkgdata(rdep, d)
744 for key in rdep_data:
745 if key.startswith("FILERPROVIDES_") or key.startswith("RPROVIDES_"):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500746 for subkey in bb.utils.explode_deps(rdep_data[key]):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500747 filerdepends.pop(subkey,None)
748 # Add the files list to the rprovides
749 if key == "FILES_INFO":
750 # Use eval() to make it as a dict
751 for subkey in eval(rdep_data[key]):
752 filerdepends.pop(subkey,None)
753 if not filerdepends:
754 # Break if all the file rdepends are met
755 break
756 if filerdepends:
757 for key in filerdepends:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500758 error_msg = "%s contained in package %s requires %s, but no providers found in RDEPENDS_%s?" % \
759 (filerdepends[key].replace("_%s" % pkg, "").replace("@underscore@", "_"), pkg, key, pkg)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500760 package_qa_handle_error("file-rdeps", error_msg, d)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500761package_qa_check_rdepends[vardepsexclude] = "OVERRIDES"
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500762
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500763def package_qa_check_deps(pkg, pkgdest, d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500764
765 localdata = bb.data.createCopy(d)
766 localdata.setVar('OVERRIDES', pkg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500767
768 def check_valid_deps(var):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500769 try:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500770 rvar = bb.utils.explode_dep_versions2(localdata.getVar(var) or "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500771 except ValueError as e:
772 bb.fatal("%s_%s: %s" % (var, pkg, e))
773 for dep in rvar:
774 for v in rvar[dep]:
775 if v and not v.startswith(('< ', '= ', '> ', '<= ', '>=')):
776 error_msg = "%s_%s is invalid: %s (%s) only comparisons <, =, >, <=, and >= are allowed" % (var, pkg, dep, v)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500777 package_qa_handle_error("dep-cmp", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500778
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500779 check_valid_deps('RDEPENDS')
780 check_valid_deps('RRECOMMENDS')
781 check_valid_deps('RSUGGESTS')
782 check_valid_deps('RPROVIDES')
783 check_valid_deps('RREPLACES')
784 check_valid_deps('RCONFLICTS')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500785
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500786QAPKGTEST[usrmerge] = "package_qa_check_usrmerge"
787def package_qa_check_usrmerge(pkg, d, messages):
788 pkgdest = d.getVar('PKGDEST')
789 pkg_dir = pkgdest + os.sep + pkg + os.sep
790 merged_dirs = ['bin', 'sbin', 'lib'] + d.getVar('MULTILIB_VARIANTS').split()
791 for f in merged_dirs:
792 if os.path.exists(pkg_dir + f) and not os.path.islink(pkg_dir + f):
793 msg = "%s package is not obeying usrmerge distro feature. /%s should be relocated to /usr." % (pkg, f)
794 package_qa_add_message(messages, "usrmerge", msg)
795 return False
796 return True
797
Brad Bishopf3f93bb2019-10-16 14:33:32 -0400798QAPKGTEST[perllocalpod] = "package_qa_check_perllocalpod"
799def package_qa_check_perllocalpod(pkg, d, messages):
800 """
801 Check that the recipe didn't ship a perlocal.pod file, which shouldn't be
802 installed in a distribution package. cpan.bbclass sets NO_PERLLOCAL=1 to
803 handle this for most recipes.
804 """
805 import glob
806 pkgd = oe.path.join(d.getVar('PKGDEST'), pkg)
807 podpath = oe.path.join(pkgd, d.getVar("libdir"), "perl*", "*", "*", "perllocal.pod")
808
809 matches = glob.glob(podpath)
810 if matches:
811 matches = [package_qa_clean_path(path, d, pkg) for path in matches]
812 msg = "%s contains perllocal.pod (%s), should not be installed" % (pkg, " ".join(matches))
813 package_qa_add_message(messages, "perllocalpod", msg)
814
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500815QAPKGTEST[expanded-d] = "package_qa_check_expanded_d"
816def package_qa_check_expanded_d(package, d, messages):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500817 """
818 Check for the expanded D (${D}) value in pkg_* and FILES
819 variables, warn the user to use it correctly.
820 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500821 sane = True
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500822 expanded_d = d.getVar('D')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500823
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500824 for var in 'FILES','pkg_preinst', 'pkg_postinst', 'pkg_prerm', 'pkg_postrm':
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500825 bbvar = d.getVar(var + "_" + package) or ""
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500826 if expanded_d in bbvar:
827 if var == 'FILES':
828 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)
829 sane = False
830 else:
831 package_qa_add_message(messages, "expanded-d", "%s in %s recipe contains ${D}, it should be replaced by $D instead" % (var, package))
832 sane = False
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500833 return sane
834
835def package_qa_check_encoding(keys, encode, d):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600836 def check_encoding(key, enc):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500837 sane = True
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500838 value = d.getVar(key)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500839 if value:
840 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600841 s = value.encode(enc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500842 except UnicodeDecodeError as e:
843 error_msg = "%s has non %s characters" % (key,enc)
844 sane = False
845 package_qa_handle_error("invalid-chars", error_msg, d)
846 return sane
847
848 for key in keys:
849 sane = check_encoding(key, encode)
850 if not sane:
851 break
852
853HOST_USER_UID := "${@os.getuid()}"
854HOST_USER_GID := "${@os.getgid()}"
855
856QAPATHTEST[host-user-contaminated] = "package_qa_check_host_user"
857def package_qa_check_host_user(path, name, d, elf, messages):
858 """Check for paths outside of /home which are owned by the user running bitbake."""
859
860 if not os.path.lexists(path):
861 return
862
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500863 dest = d.getVar('PKGDEST')
864 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500865 home = os.path.join(dest, 'home')
866 if path == home or path.startswith(home + os.sep):
867 return
868
869 try:
870 stat = os.lstat(path)
871 except OSError as exc:
872 import errno
873 if exc.errno != errno.ENOENT:
874 raise
875 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500876 check_uid = int(d.getVar('HOST_USER_UID'))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500877 if stat.st_uid == check_uid:
Brad Bishop96ff1982019-08-19 13:50:42 -0400878 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 -0500879 return False
880
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500881 check_gid = int(d.getVar('HOST_USER_GID'))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500882 if stat.st_gid == check_gid:
Brad Bishop96ff1982019-08-19 13:50:42 -0400883 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 -0500884 return False
885 return True
886
Brad Bishop15ae2502019-06-18 21:44:24 -0400887QARECIPETEST[src-uri-bad] = "package_qa_check_src_uri"
888def package_qa_check_src_uri(pn, d, messages):
889 import re
890
891 if "${PN}" in d.getVar("SRC_URI", False):
892 package_qa_handle_error("src-uri-bad", "%s: SRC_URI uses PN not BPN" % pn, d)
893
894 pn = d.getVar("SRC_URI")
895 if re.search(r"github\.com/.+/.+/archive/.+", pn):
896 package_qa_handle_error("src-uri-bad", "%s: SRC_URI uses unstable GitHub archives" % pn, d)
897
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500898
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500899# The PACKAGE FUNC to scan each package
900python do_package_qa () {
901 import subprocess
902 import oe.packagedata
903
904 bb.note("DO PACKAGE QA")
905
906 bb.build.exec_func("read_subpackage_metadata", d)
907
908 # Check non UTF-8 characters on recipe's metadata
909 package_qa_check_encoding(['DESCRIPTION', 'SUMMARY', 'LICENSE', 'SECTION'], 'utf-8', d)
910
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500911 logdir = d.getVar('T')
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500912 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500913
914 # Check the compile log for host contamination
915 compilelog = os.path.join(logdir,"log.do_compile")
916
917 if os.path.exists(compilelog):
918 statement = "grep -e 'CROSS COMPILE Badness:' -e 'is unsafe for cross-compilation' %s > /dev/null" % compilelog
919 if subprocess.call(statement, shell=True) == 0:
920 msg = "%s: The compile log indicates that host include and/or library paths were used.\n \
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500921 Please check the log '%s' for more information." % (pn, compilelog)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500922 package_qa_handle_error("compile-host-path", msg, d)
923
924 # Check the install log for host contamination
925 installlog = os.path.join(logdir,"log.do_install")
926
927 if os.path.exists(installlog):
928 statement = "grep -e 'CROSS COMPILE Badness:' -e 'is unsafe for cross-compilation' %s > /dev/null" % installlog
929 if subprocess.call(statement, shell=True) == 0:
930 msg = "%s: The install log indicates that host include and/or library paths were used.\n \
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500931 Please check the log '%s' for more information." % (pn, installlog)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500932 package_qa_handle_error("install-host-path", msg, d)
933
934 # Scan the packages...
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500935 pkgdest = d.getVar('PKGDEST')
936 packages = set((d.getVar('PACKAGES') or '').split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500937
938 cpath = oe.cachedpath.CachedPath()
939 global pkgfiles
940 pkgfiles = {}
941 for pkg in packages:
942 pkgfiles[pkg] = []
943 for walkroot, dirs, files in cpath.walk(pkgdest + "/" + pkg):
944 for file in files:
945 pkgfiles[pkg].append(walkroot + os.sep + file)
946
947 # no packages should be scanned
948 if not packages:
949 return
950
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500951 import re
952 # The package name matches the [a-z0-9.+-]+ regular expression
Brad Bishop977dc1a2019-02-06 16:01:43 -0500953 pkgname_pattern = re.compile(r"^[a-z0-9.+-]+$")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500954
955 taskdepdata = d.getVar("BB_TASKDEPDATA", False)
956 taskdeps = set()
957 for dep in taskdepdata:
958 taskdeps.add(taskdepdata[dep][0])
959
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500960 def parse_test_matrix(matrix_name):
961 testmatrix = d.getVarFlags(matrix_name) or {}
962 g = globals()
963 warnchecks = []
964 for w in (d.getVar("WARN_QA") or "").split():
965 if w in skip:
966 continue
967 if w in testmatrix and testmatrix[w] in g:
968 warnchecks.append(g[testmatrix[w]])
969
970 errorchecks = []
971 for e in (d.getVar("ERROR_QA") or "").split():
972 if e in skip:
973 continue
974 if e in testmatrix and testmatrix[e] in g:
975 errorchecks.append(g[testmatrix[e]])
976 return warnchecks, errorchecks
977
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500978 for package in packages:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500979 skip = set((d.getVar('INSANE_SKIP') or "").split() +
980 (d.getVar('INSANE_SKIP_' + package) or "").split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500981 if skip:
982 bb.note("Package %s skipping QA tests: %s" % (package, str(skip)))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500983
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500984 bb.note("Checking Package: %s" % package)
985 # Check package name
986 if not pkgname_pattern.match(package):
987 package_qa_handle_error("pkgname",
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500988 "%s doesn't match the [a-z0-9.+-]+ regex" % package, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500989
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500990 warn_checks, error_checks = parse_test_matrix("QAPATHTEST")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500991 package_qa_walk(warn_checks, error_checks, package, d)
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500992
993 warn_checks, error_checks = parse_test_matrix("QAPKGTEST")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500994 package_qa_package(warn_checks, error_checks, package, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500995
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500996 package_qa_check_rdepends(package, pkgdest, skip, taskdeps, packages, d)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500997 package_qa_check_deps(package, pkgdest, d)
998
999 warn_checks, error_checks = parse_test_matrix("QARECIPETEST")
1000 package_qa_recipe(warn_checks, error_checks, pn, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001001
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001002 if 'libdir' in d.getVar("ALL_QA").split():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001003 package_qa_check_libdir(d)
1004
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001005 qa_sane = d.getVar("QA_SANE")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001006 if not qa_sane:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001007 bb.fatal("QA run found fatal errors. Please consider fixing them.")
1008 bb.note("DONE with PACKAGE QA")
1009}
1010
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001011# binutils is used for most checks, so need to set as dependency
1012# POPULATESYSROOTDEPS is defined in staging class.
1013do_package_qa[depends] += "${POPULATESYSROOTDEPS}"
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001014do_package_qa[vardepsexclude] = "BB_TASKDEPDATA"
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001015do_package_qa[rdeptask] = "do_packagedata"
1016addtask do_package_qa after do_packagedata do_package before do_build
1017
Brad Bishop19323692019-04-05 15:28:33 -04001018# Add the package specific INSANE_SKIPs to the sstate dependencies
1019python() {
1020 pkgs = (d.getVar('PACKAGES') or '').split()
1021 for pkg in pkgs:
1022 d.appendVarFlag("do_package_qa", "vardeps", " INSANE_SKIP_{}".format(pkg))
1023}
1024
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001025SSTATETASKS += "do_package_qa"
1026do_package_qa[sstate-inputdirs] = ""
1027do_package_qa[sstate-outputdirs] = ""
1028python do_package_qa_setscene () {
1029 sstate_setscene(d)
1030}
1031addtask do_package_qa_setscene
1032
1033python do_qa_staging() {
1034 bb.note("QA checking staging")
Brad Bishop19323692019-04-05 15:28:33 -04001035 if not qa_check_staged(d.expand('${SYSROOT_DESTDIR}${libdir}'), d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001036 bb.fatal("QA staging was broken by the package built above")
1037}
1038
Brad Bishopd89cb5f2019-04-10 09:02:41 -04001039python do_qa_patch() {
1040 import subprocess
1041
1042 ###########################################################################
1043 # Check patch.log for fuzz warnings
1044 #
1045 # Further information on why we check for patch fuzz warnings:
1046 # http://lists.openembedded.org/pipermail/openembedded-core/2018-March/148675.html
1047 # https://bugzilla.yoctoproject.org/show_bug.cgi?id=10450
1048 ###########################################################################
1049
1050 logdir = d.getVar('T')
1051 patchlog = os.path.join(logdir,"log.do_patch")
1052
1053 if os.path.exists(patchlog):
1054 fuzzheader = '--- Patch fuzz start ---'
1055 fuzzfooter = '--- Patch fuzz end ---'
1056 statement = "grep -e '%s' %s > /dev/null" % (fuzzheader, patchlog)
1057 if subprocess.call(statement, shell=True) == 0:
1058 msg = "Fuzz detected:\n\n"
1059 fuzzmsg = ""
1060 inFuzzInfo = False
1061 f = open(patchlog, "r")
1062 for line in f:
1063 if fuzzheader in line:
1064 inFuzzInfo = True
1065 fuzzmsg = ""
1066 elif fuzzfooter in line:
1067 fuzzmsg = fuzzmsg.replace('\n\n', '\n')
1068 msg += fuzzmsg
1069 msg += "\n"
1070 inFuzzInfo = False
1071 elif inFuzzInfo and not 'Now at patch' in line:
1072 fuzzmsg += line
1073 f.close()
1074 msg += "The context lines in the patches can be updated with devtool:\n"
1075 msg += "\n"
1076 msg += " devtool modify %s\n" % d.getVar('PN')
1077 msg += " devtool finish --force-patch-refresh %s <layer_path>\n\n" % d.getVar('PN')
1078 msg += "Don't forget to review changes done by devtool!\n"
1079 if 'patch-fuzz' in d.getVar('ERROR_QA'):
1080 bb.error(msg)
1081 elif 'patch-fuzz' in d.getVar('WARN_QA'):
1082 bb.warn(msg)
1083 msg = "Patch log indicates that patches do not apply cleanly."
1084 package_qa_handle_error("patch-fuzz", msg, d)
1085}
1086
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001087python do_qa_configure() {
1088 import subprocess
1089
1090 ###########################################################################
1091 # Check config.log for cross compile issues
1092 ###########################################################################
1093
1094 configs = []
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001095 workdir = d.getVar('WORKDIR')
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001096
Brad Bishop19323692019-04-05 15:28:33 -04001097 skip = (d.getVar('INSANE_SKIP') or "").split()
1098 skip_configure_unsafe = False
1099 if 'configure-unsafe' in skip:
1100 bb.note("Recipe %s skipping qa checking: configure-unsafe" % d.getVar('PN'))
1101 skip_configure_unsafe = True
1102
1103 if bb.data.inherits_class('autotools', d) and not skip_configure_unsafe:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001104 bb.note("Checking autotools environment for common misconfiguration")
1105 for root, dirs, files in os.walk(workdir):
1106 statement = "grep -q -F -e 'CROSS COMPILE Badness:' -e 'is unsafe for cross-compilation' %s" % \
1107 os.path.join(root,"config.log")
1108 if "config.log" in files:
1109 if subprocess.call(statement, shell=True) == 0:
Brad Bishop19323692019-04-05 15:28:33 -04001110 error_msg = """This autoconf log indicates errors, it looked at host include and/or library paths while determining system capabilities.
1111Rerun configure task after fixing this."""
1112 package_qa_handle_error("configure-unsafe", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001113
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001114 if "configure.ac" in files:
1115 configs.append(os.path.join(root,"configure.ac"))
1116 if "configure.in" in files:
1117 configs.append(os.path.join(root, "configure.in"))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001118
1119 ###########################################################################
1120 # Check gettext configuration and dependencies are correct
1121 ###########################################################################
1122
Brad Bishop19323692019-04-05 15:28:33 -04001123 skip_configure_gettext = False
1124 if 'configure-gettext' in skip:
1125 bb.note("Recipe %s skipping qa checking: configure-gettext" % d.getVar('PN'))
1126 skip_configure_gettext = True
1127
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001128 cnf = d.getVar('EXTRA_OECONF') or ""
Brad Bishop19323692019-04-05 15:28:33 -04001129 if not ("gettext" in d.getVar('P') or "gcc-runtime" in d.getVar('P') or \
1130 "--disable-nls" in cnf or skip_configure_gettext):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001131 ml = d.getVar("MLPREFIX") or ""
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001132 if bb.data.inherits_class('cross-canadian', d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001133 gt = "nativesdk-gettext"
1134 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001135 gt = "gettext-native"
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001136 deps = bb.utils.explode_deps(d.getVar('DEPENDS') or "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001137 if gt not in deps:
1138 for config in configs:
1139 gnu = "grep \"^[[:space:]]*AM_GNU_GETTEXT\" %s >/dev/null" % config
1140 if subprocess.call(gnu, shell=True) == 0:
Brad Bishopd89cb5f2019-04-10 09:02:41 -04001141 error_msg = "AM_GNU_GETTEXT used but no inherit gettext"
Brad Bishop19323692019-04-05 15:28:33 -04001142 package_qa_handle_error("configure-gettext", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001143
1144 ###########################################################################
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001145 # Check unrecognised configure options (with a white list)
1146 ###########################################################################
Brad Bishopc342db32019-05-15 21:57:59 -04001147 if bb.data.inherits_class("autotools", d) or bb.data.inherits_class("meson", d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001148 bb.note("Checking configure output for unrecognised options")
1149 try:
Brad Bishopc342db32019-05-15 21:57:59 -04001150 if bb.data.inherits_class("autotools", d):
1151 flag = "WARNING: unrecognized options:"
1152 log = os.path.join(d.getVar('B'), 'config.log')
1153 if bb.data.inherits_class("meson", d):
1154 flag = "WARNING: Unknown options:"
1155 log = os.path.join(d.getVar('T'), 'log.do_configure')
1156 output = subprocess.check_output(['grep', '-F', flag, log]).decode("utf-8").replace(', ', ' ').replace('"', '')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001157 options = set()
1158 for line in output.splitlines():
1159 options |= set(line.partition(flag)[2].split())
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001160 whitelist = set(d.getVar("UNKNOWN_CONFIGURE_WHITELIST").split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001161 options -= whitelist
1162 if options:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001163 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001164 error_msg = pn + ": configure was passed unrecognised options: " + " ".join(options)
1165 package_qa_handle_error("unknown-configure-option", error_msg, d)
1166 except subprocess.CalledProcessError:
1167 pass
1168
1169 # Check invalid PACKAGECONFIG
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001170 pkgconfig = (d.getVar("PACKAGECONFIG") or "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001171 if pkgconfig:
1172 pkgconfigflags = d.getVarFlags("PACKAGECONFIG") or {}
1173 for pconfig in pkgconfig:
1174 if pconfig not in pkgconfigflags:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001175 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001176 error_msg = "%s: invalid PACKAGECONFIG: %s" % (pn, pconfig)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001177 package_qa_handle_error("invalid-packageconfig", error_msg, d)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001178
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001179 qa_sane = d.getVar("QA_SANE")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001180 if not qa_sane:
1181 bb.fatal("Fatal QA errors found, failing task.")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001182}
1183
1184python do_qa_unpack() {
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001185 src_uri = d.getVar('SRC_URI')
1186 s_dir = d.getVar('S')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001187 if src_uri and not os.path.exists(s_dir):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001188 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 -05001189}
1190
1191# The Staging Func, to check all staging
1192#addtask qa_staging after do_populate_sysroot before do_build
1193do_populate_sysroot[postfuncs] += "do_qa_staging "
1194
Brad Bishopd89cb5f2019-04-10 09:02:41 -04001195# Check for patch fuzz
1196do_patch[postfuncs] += "do_qa_patch "
1197
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001198# Check broken config.log files, for packages requiring Gettext which
1199# don't have it in DEPENDS.
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001200#addtask qa_configure after do_configure before do_compile
1201do_configure[postfuncs] += "do_qa_configure "
1202
1203# Check does S exist.
1204do_unpack[postfuncs] += "do_qa_unpack"
1205
1206python () {
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001207 import re
1208
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001209 tests = d.getVar('ALL_QA').split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001210 if "desktop" in tests:
1211 d.appendVar("PACKAGE_DEPENDS", " desktop-file-utils-native")
1212
1213 ###########################################################################
1214 # Check various variables
1215 ###########################################################################
1216
1217 # Checking ${FILESEXTRAPATHS}
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001218 extrapaths = (d.getVar("FILESEXTRAPATHS") or "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001219 if '__default' not in extrapaths.split(":"):
1220 msg = "FILESEXTRAPATHS-variable, must always use _prepend (or _append)\n"
1221 msg += "type of assignment, and don't forget the colon.\n"
1222 msg += "Please assign it with the format of:\n"
1223 msg += " FILESEXTRAPATHS_append := \":${THISDIR}/Your_Files_Path\" or\n"
1224 msg += " FILESEXTRAPATHS_prepend := \"${THISDIR}/Your_Files_Path:\"\n"
1225 msg += "in your bbappend file\n\n"
1226 msg += "Your incorrect assignment is:\n"
1227 msg += "%s\n" % extrapaths
1228 bb.warn(msg)
1229
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001230 overrides = d.getVar('OVERRIDES').split(':')
1231 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001232 if pn in overrides:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001233 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 -05001234 package_qa_handle_error("pn-overrides", msg, d)
Brad Bishop977dc1a2019-02-06 16:01:43 -05001235 prog = re.compile(r'[A-Z]')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001236 if prog.search(pn):
1237 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 -05001238
Brad Bishop08902b02019-08-20 09:16:51 -04001239 # Some people mistakenly use DEPENDS_${PN} instead of DEPENDS and wonder
1240 # why it doesn't work.
1241 if (d.getVar(d.expand('DEPENDS_${PN}'))):
1242 package_qa_handle_error("pkgvarcheck", "recipe uses DEPENDS_${PN}, should use DEPENDS", d)
1243
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001244 issues = []
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001245 if (d.getVar('PACKAGES') or "").split():
1246 for dep in (d.getVar('QADEPENDS') or "").split():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001247 d.appendVarFlag('do_package_qa', 'depends', " %s:do_populate_sysroot" % dep)
1248 for var in 'RDEPENDS', 'RRECOMMENDS', 'RSUGGESTS', 'RCONFLICTS', 'RPROVIDES', 'RREPLACES', 'FILES', 'pkg_preinst', 'pkg_postinst', 'pkg_prerm', 'pkg_postrm', 'ALLOW_EMPTY':
1249 if d.getVar(var, False):
1250 issues.append(var)
1251
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001252 fakeroot_tests = d.getVar('FAKEROOT_QA').split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001253 if set(tests) & set(fakeroot_tests):
1254 d.setVarFlag('do_package_qa', 'fakeroot', '1')
1255 d.appendVarFlag('do_package_qa', 'depends', ' virtual/fakeroot-native:do_populate_sysroot')
1256 else:
1257 d.setVarFlag('do_package_qa', 'rdeptask', '')
1258 for i in issues:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001259 package_qa_handle_error("pkgvarcheck", "%s: Variable %s is set as not being package specific, please fix this." % (d.getVar("FILE"), i), d)
1260 qa_sane = d.getVar("QA_SANE")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001261 if not qa_sane:
1262 bb.fatal("Fatal QA errors found, failing task.")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001263}