blob: ee19ef673e07955311e0e6ac4ec55c8bb096598f [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.
Andrew Geisslerc182c622020-05-15 14:13:32 -050025WARN_QA ?= " libdir xorg-driver-abi \
26 textrel incompatible-license files-invalid \
27 infodir build-deps src-uri-bad symlink-to-sysroot multilib \
Brad Bishopd89cb5f2019-04-10 09:02:41 -040028 invalid-packageconfig host-user-contaminated uppercase-pn patch-fuzz \
Andrew Geissler5a43b432020-06-13 10:46:56 -050029 mime mime-xdg unlisted-pkg-lics unhandled-features-check \
Patrick Williamsc124f4f2015-09-15 14:41:29 -050030 "
31ERROR_QA ?= "dev-so debug-deps dev-deps debug-files arch pkgconfig la \
32 perms dep-cmp pkgvarcheck perm-config perm-line perm-link \
33 split-strip packages-list pkgv-undefined var-undefined \
34 version-going-backwards expanded-d invalid-chars \
Brad Bishop19323692019-04-05 15:28:33 -040035 license-checksum dev-elf file-rdeps configure-unsafe \
Andrew Geissler82c905d2020-04-13 13:39:40 -050036 configure-gettext perllocalpod shebang-size \
Andrew Geisslerc182c622020-05-15 14:13:32 -050037 already-stripped installed-vs-shipped ldflags compile-host-path \
38 install-host-path pn-overrides unknown-configure-option \
39 useless-rpaths rpaths staticdev \
Patrick Williamsc124f4f2015-09-15 14:41:29 -050040 "
Brad Bishopd7bf8c12018-02-25 22:55:05 -050041# Add usrmerge QA check based on distro feature
42ERROR_QA_append = "${@bb.utils.contains('DISTRO_FEATURES', 'usrmerge', ' usrmerge', '', d)}"
43
Patrick Williamsc124f4f2015-09-15 14:41:29 -050044FAKEROOT_QA = "host-user-contaminated"
45FAKEROOT_QA[doc] = "QA tests which need to run under fakeroot. If any \
46enabled tests are listed here, the do_package_qa task will run under fakeroot."
47
48ALL_QA = "${WARN_QA} ${ERROR_QA}"
49
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050050UNKNOWN_CONFIGURE_WHITELIST ?= "--enable-nls --disable-nls --disable-silent-rules --disable-dependency-tracking --with-libtool-sysroot --disable-static"
Patrick Williamsc124f4f2015-09-15 14:41:29 -050051
Patrick Williamsc0f7c042017-02-23 20:41:17 -060052def package_qa_clean_path(path, d, pkg=None):
53 """
54 Remove redundant paths from the path for display. If pkg isn't set then
55 TMPDIR is stripped, otherwise PKGDEST/pkg is stripped.
56 """
57 if pkg:
Brad Bishop6e60e8b2018-02-01 10:27:11 -050058 path = path.replace(os.path.join(d.getVar("PKGDEST"), pkg), "/")
59 return path.replace(d.getVar("TMPDIR"), "/").replace("//", "/")
Patrick Williamsc124f4f2015-09-15 14:41:29 -050060
61def package_qa_write_error(type, error, d):
Brad Bishop6e60e8b2018-02-01 10:27:11 -050062 logfile = d.getVar('QA_LOGFILE')
Patrick Williamsc124f4f2015-09-15 14:41:29 -050063 if logfile:
Brad Bishop6e60e8b2018-02-01 10:27:11 -050064 p = d.getVar('P')
Patrick Williamsc0f7c042017-02-23 20:41:17 -060065 with open(logfile, "a+") as f:
66 f.write("%s: %s [%s]\n" % (p, error, type))
Patrick Williamsc124f4f2015-09-15 14:41:29 -050067
68def package_qa_handle_error(error_class, error_msg, d):
Brad Bishop6e60e8b2018-02-01 10:27:11 -050069 if error_class in (d.getVar("ERROR_QA") or "").split():
Brad Bishopd7bf8c12018-02-25 22:55:05 -050070 package_qa_write_error(error_class, error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050071 bb.error("QA Issue: %s [%s]" % (error_msg, error_class))
72 d.setVar("QA_SANE", False)
73 return False
Brad Bishop6e60e8b2018-02-01 10:27:11 -050074 elif error_class in (d.getVar("WARN_QA") or "").split():
Brad Bishopd7bf8c12018-02-25 22:55:05 -050075 package_qa_write_error(error_class, error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050076 bb.warn("QA Issue: %s [%s]" % (error_msg, error_class))
77 else:
78 bb.note("QA Issue: %s [%s]" % (error_msg, error_class))
79 return True
80
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050081def package_qa_add_message(messages, section, new_msg):
82 if section not in messages:
83 messages[section] = new_msg
84 else:
85 messages[section] = messages[section] + "\n" + new_msg
86
Andrew Geissler82c905d2020-04-13 13:39:40 -050087QAPATHTEST[shebang-size] = "package_qa_check_shebang_size"
88def package_qa_check_shebang_size(path, name, d, elf, messages):
89 if os.path.islink(path) or elf:
90 return
91
92 try:
93 with open(path, 'rb') as f:
94 stanza = f.readline(130)
95 except IOError:
96 return
97
98 if stanza.startswith(b'#!'):
99 #Shebang not found
100 try:
101 stanza = stanza.decode("utf-8")
102 except UnicodeDecodeError:
103 #If it is not a text file, it is not a script
104 return
105
106 if len(stanza) > 129:
107 package_qa_add_message(messages, "shebang-size", "%s: %s maximum shebang size exceeded, the maximum size is 128." % (name, package_qa_clean_path(path, d)))
108 return
109
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500110QAPATHTEST[libexec] = "package_qa_check_libexec"
111def package_qa_check_libexec(path,name, d, elf, messages):
112
113 # Skip the case where the default is explicitly /usr/libexec
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500114 libexec = d.getVar('libexecdir')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500115 if libexec == "/usr/libexec":
116 return True
117
118 if 'libexec' in path.split(os.path.sep):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500119 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 -0500120 return False
121
122 return True
123
124QAPATHTEST[rpaths] = "package_qa_check_rpath"
125def package_qa_check_rpath(file,name, d, elf, messages):
126 """
127 Check for dangerous RPATHs
128 """
129 if not elf:
130 return
131
132 if os.path.islink(file):
133 return
134
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500135 bad_dirs = [d.getVar('BASE_WORKDIR'), d.getVar('STAGING_DIR_TARGET')]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500136
137 phdrs = elf.run_objdump("-p", d)
138
139 import re
Brad Bishop977dc1a2019-02-06 16:01:43 -0500140 rpath_re = re.compile(r"\s+RPATH\s+(.*)")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500141 for line in phdrs.split("\n"):
142 m = rpath_re.match(line)
143 if m:
144 rpath = m.group(1)
145 for dir in bad_dirs:
146 if dir in rpath:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500147 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 -0500148
149QAPATHTEST[useless-rpaths] = "package_qa_check_useless_rpaths"
150def package_qa_check_useless_rpaths(file, name, d, elf, messages):
151 """
152 Check for RPATHs that are useless but not dangerous
153 """
154 def rpath_eq(a, b):
155 return os.path.normpath(a) == os.path.normpath(b)
156
157 if not elf:
158 return
159
160 if os.path.islink(file):
161 return
162
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500163 libdir = d.getVar("libdir")
164 base_libdir = d.getVar("base_libdir")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500165
166 phdrs = elf.run_objdump("-p", d)
167
168 import re
Brad Bishop977dc1a2019-02-06 16:01:43 -0500169 rpath_re = re.compile(r"\s+RPATH\s+(.*)")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500170 for line in phdrs.split("\n"):
171 m = rpath_re.match(line)
172 if m:
173 rpath = m.group(1)
174 if rpath_eq(rpath, libdir) or rpath_eq(rpath, base_libdir):
175 # The dynamic linker searches both these places anyway. There is no point in
176 # looking there again.
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500177 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 -0500178
179QAPATHTEST[dev-so] = "package_qa_check_dev"
180def package_qa_check_dev(path, name, d, elf, messages):
181 """
182 Check for ".so" library symlinks in non-dev packages
183 """
184
185 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 -0500186 package_qa_add_message(messages, "dev-so", "non -dev/-dbg/nativesdk- package contains symlink .so: %s path '%s'" % \
187 (name, package_qa_clean_path(path,d)))
188
189QAPATHTEST[dev-elf] = "package_qa_check_dev_elf"
190def package_qa_check_dev_elf(path, name, d, elf, messages):
191 """
192 Check that -dev doesn't contain real shared libraries. The test has to
193 check that the file is not a link and is an ELF object as some recipes
194 install link-time .so files that are linker scripts.
195 """
196 if name.endswith("-dev") and path.endswith(".so") and not os.path.islink(path) and elf:
197 package_qa_add_message(messages, "dev-elf", "-dev package contains non-symlink .so: %s path '%s'" % \
198 (name, package_qa_clean_path(path,d)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500199
200QAPATHTEST[staticdev] = "package_qa_check_staticdev"
201def package_qa_check_staticdev(path, name, d, elf, messages):
202 """
203 Check for ".a" library in non-staticdev packages
204 There are a number of exceptions to this rule, -pic packages can contain
205 static libraries, the _nonshared.a belong with their -dev packages and
206 libgcc.a, libgcov.a will be skipped in their packages
207 """
208
Andrew Geissler82c905d2020-04-13 13:39:40 -0500209 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") and not '/usr/lib/debug-static/' in path and not '/.debug-static/' in path:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500210 package_qa_add_message(messages, "staticdev", "non -staticdev package contains static .a library: %s path '%s'" % \
211 (name, package_qa_clean_path(path,d)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500212
Andrew Geissler82c905d2020-04-13 13:39:40 -0500213QAPATHTEST[mime] = "package_qa_check_mime"
214def package_qa_check_mime(path, name, d, elf, messages):
215 """
216 Check if package installs mime types to /usr/share/mime/packages
217 while no inheriting mime.bbclass
218 """
219
220 if d.getVar("datadir") + "/mime/packages" in path and path.endswith('.xml') and not bb.data.inherits_class("mime", d):
221 package_qa_add_message(messages, "mime", "package contains mime types but does not inherit mime: %s path '%s'" % \
222 (name, package_qa_clean_path(path,d)))
223
224QAPATHTEST[mime-xdg] = "package_qa_check_mime_xdg"
225def package_qa_check_mime_xdg(path, name, d, elf, messages):
226 """
227 Check if package installs desktop file containing MimeType and requires
228 mime-types.bbclass to create /usr/share/applications/mimeinfo.cache
229 """
230
231 if d.getVar("datadir") + "/applications" in path and path.endswith('.desktop') and not bb.data.inherits_class("mime-xdg", d):
232 mime_type_found = False
233 try:
234 with open(path, 'r') as f:
235 for line in f.read().split('\n'):
236 if 'MimeType' in line:
237 mime_type_found = True
238 break;
239 except:
240 # At least libreoffice installs symlinks with absolute paths that are dangling here.
241 # We could implement some magic but for few (one) recipes it is not worth the effort so just warn:
242 wstr = "%s cannot open %s - is it a symlink with absolute path?\n" % (name, package_qa_clean_path(path,d))
243 wstr += "Please check if (linked) file contains key 'MimeType'.\n"
244 pkgname = name
245 if name == d.getVar('PN'):
246 pkgname = '${PN}'
247 wstr += "If yes: add \'inhert mime-xdg\' and \'MIME_XDG_PACKAGES += \"%s\"\' / if no add \'INSANE_SKIP_%s += \"mime-xdg\"\' to recipe." % (pkgname, pkgname)
248 package_qa_add_message(messages, "mime-xdg", wstr)
249 if mime_type_found:
250 package_qa_add_message(messages, "mime-xdg", "package contains desktop file with key 'MimeType' but does not inhert mime-xdg: %s path '%s'" % \
251 (name, package_qa_clean_path(path,d)))
252
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500253def package_qa_check_libdir(d):
254 """
255 Check for wrong library installation paths. For instance, catch
256 recipes installing /lib/bar.so when ${base_libdir}="lib32" or
257 installing in /usr/lib64 when ${libdir}="/usr/lib"
258 """
259 import re
260
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500261 pkgdest = d.getVar('PKGDEST')
262 base_libdir = d.getVar("base_libdir") + os.sep
263 libdir = d.getVar("libdir") + os.sep
264 libexecdir = d.getVar("libexecdir") + os.sep
265 exec_prefix = d.getVar("exec_prefix") + os.sep
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500266
267 messages = []
268
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500269 # The re's are purposely fuzzy, as some there are some .so.x.y.z files
270 # that don't follow the standard naming convention. It checks later
271 # that they are actual ELF files
Brad Bishop977dc1a2019-02-06 16:01:43 -0500272 lib_re = re.compile(r"^/lib.+\.so(\..+)?$")
273 exec_re = re.compile(r"^%s.*/lib.+\.so(\..+)?$" % exec_prefix)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500274
275 for root, dirs, files in os.walk(pkgdest):
276 if root == pkgdest:
277 # Skip subdirectories for any packages with libdir in INSANE_SKIP
278 skippackages = []
279 for package in dirs:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500280 if 'libdir' in (d.getVar('INSANE_SKIP_' + package) or "").split():
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500281 bb.note("Package %s skipping libdir QA test" % (package))
282 skippackages.append(package)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500283 elif d.getVar('PACKAGE_DEBUG_SPLIT_STYLE') == 'debug-file-directory' and package.endswith("-dbg"):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500284 bb.note("Package %s skipping libdir QA test for PACKAGE_DEBUG_SPLIT_STYLE equals debug-file-directory" % (package))
285 skippackages.append(package)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500286 for package in skippackages:
287 dirs.remove(package)
288 for file in files:
289 full_path = os.path.join(root, file)
290 rel_path = os.path.relpath(full_path, pkgdest)
291 if os.sep in rel_path:
292 package, rel_path = rel_path.split(os.sep, 1)
293 rel_path = os.sep + rel_path
294 if lib_re.match(rel_path):
295 if base_libdir not in rel_path:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500296 # make sure it's an actual ELF file
297 elf = oe.qa.ELFFile(full_path)
298 try:
299 elf.open()
300 messages.append("%s: found library in wrong location: %s" % (package, rel_path))
301 except (oe.qa.NotELFFileError):
302 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500303 if exec_re.match(rel_path):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500304 if libdir not in rel_path and libexecdir not in rel_path:
305 # make sure it's an actual ELF file
306 elf = oe.qa.ELFFile(full_path)
307 try:
308 elf.open()
309 messages.append("%s: found library in wrong location: %s" % (package, rel_path))
310 except (oe.qa.NotELFFileError):
311 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500312
313 if messages:
314 package_qa_handle_error("libdir", "\n".join(messages), d)
315
316QAPATHTEST[debug-files] = "package_qa_check_dbg"
317def package_qa_check_dbg(path, name, d, elf, messages):
318 """
319 Check for ".debug" files or directories outside of the dbg package
320 """
321
322 if not "-dbg" in name and not "-ptest" in name:
323 if '.debug' in path.split(os.path.sep):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500324 package_qa_add_message(messages, "debug-files", "non debug package contains .debug directory: %s path %s" % \
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500325 (name, package_qa_clean_path(path,d)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500326
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500327QAPATHTEST[arch] = "package_qa_check_arch"
328def package_qa_check_arch(path,name,d, elf, messages):
329 """
330 Check if archs are compatible
331 """
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800332 import re, oe.elf
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600333
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500334 if not elf:
335 return
336
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500337 target_os = d.getVar('TARGET_OS')
338 target_arch = d.getVar('TARGET_ARCH')
339 provides = d.getVar('PROVIDES')
340 bpn = d.getVar('BPN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500341
342 if target_arch == "allarch":
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500343 pn = d.getVar('PN')
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500344 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 -0500345 return
346
347 # FIXME: Cross package confuse this check, so just skip them
348 for s in ['cross', 'nativesdk', 'cross-canadian']:
349 if bb.data.inherits_class(s, d):
350 return
351
352 # avoid following links to /usr/bin (e.g. on udev builds)
353 # we will check the files pointed to anyway...
354 if os.path.islink(path):
355 return
356
357 #if this will throw an exception, then fix the dict above
358 (machine, osabi, abiversion, littleendian, bits) \
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800359 = oe.elf.machine_dict(d)[target_os][target_arch]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500360
361 # Check the architecture and endiannes of the binary
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600362 is_32 = (("virtual/kernel" in provides) or bb.data.inherits_class("module", d)) and \
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800363 (target_os == "linux-gnux32" or target_os == "linux-muslx32" or \
Brad Bishop977dc1a2019-02-06 16:01:43 -0500364 target_os == "linux-gnu_ilp32" or re.match(r'mips64.*32', d.getVar('DEFAULTTUNE')))
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800365 is_bpf = (oe.qa.elf_machine_to_string(elf.machine()) == "BPF")
366 if not ((machine == elf.machine()) or is_32 or is_bpf):
Andrew Geisslerb7d28612020-07-24 16:15:54 -0500367 package_qa_add_message(messages, "arch", "Architecture did not match (%s, expected %s) in %s" % \
368 (oe.qa.elf_machine_to_string(elf.machine()), oe.qa.elf_machine_to_string(machine), package_qa_clean_path(path, d, name)))
Brad Bishop19323692019-04-05 15:28:33 -0400369 elif not ((bits == elf.abiSize()) or is_32 or is_bpf):
Andrew Geisslerb7d28612020-07-24 16:15:54 -0500370 package_qa_add_message(messages, "arch", "Bit size did not match (%d, expected %d) in %s" % \
371 (elf.abiSize(), bits, package_qa_clean_path(path, d, name)))
Brad Bishop19323692019-04-05 15:28:33 -0400372 elif not ((littleendian == elf.isLittleEndian()) or is_bpf):
Andrew Geisslerb7d28612020-07-24 16:15:54 -0500373 package_qa_add_message(messages, "arch", "Endiannes did not match (%d, expected %d) in %s" % \
374 (elf.isLittleEndian(), littleendian, package_qa_clean_path(path,d, name)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500375
376QAPATHTEST[desktop] = "package_qa_check_desktop"
377def package_qa_check_desktop(path, name, d, elf, messages):
378 """
379 Run all desktop files through desktop-file-validate.
380 """
381 if path.endswith(".desktop"):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500382 desktop_file_validate = os.path.join(d.getVar('STAGING_BINDIR_NATIVE'),'desktop-file-validate')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500383 output = os.popen("%s %s" % (desktop_file_validate, path))
384 # This only produces output on errors
385 for l in output:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500386 package_qa_add_message(messages, "desktop", "Desktop file issue: " + l.strip())
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500387
388QAPATHTEST[textrel] = "package_qa_textrel"
389def package_qa_textrel(path, name, d, elf, messages):
390 """
391 Check if the binary contains relocations in .text
392 """
393
394 if not elf:
395 return
396
397 if os.path.islink(path):
398 return
399
400 phdrs = elf.run_objdump("-p", d)
401 sane = True
402
403 import re
Brad Bishop977dc1a2019-02-06 16:01:43 -0500404 textrel_re = re.compile(r"\s+TEXTREL\s+")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500405 for line in phdrs.split("\n"):
406 if textrel_re.match(line):
407 sane = False
Brad Bishop1d80a2e2019-11-15 16:35:03 -0500408 break
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500409
410 if not sane:
Brad Bishop1d80a2e2019-11-15 16:35:03 -0500411 path = package_qa_clean_path(path, d, name)
412 package_qa_add_message(messages, "textrel", "%s: ELF binary %s has relocations in .text" % (name, path))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500413
414QAPATHTEST[ldflags] = "package_qa_hash_style"
415def package_qa_hash_style(path, name, d, elf, messages):
416 """
417 Check if the binary has the right hash style...
418 """
419
420 if not elf:
421 return
422
423 if os.path.islink(path):
424 return
425
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500426 gnu_hash = "--hash-style=gnu" in d.getVar('LDFLAGS')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500427 if not gnu_hash:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500428 gnu_hash = "--hash-style=both" in d.getVar('LDFLAGS')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500429 if not gnu_hash:
430 return
431
432 sane = False
433 has_syms = False
434
435 phdrs = elf.run_objdump("-p", d)
436
437 # If this binary has symbols, we expect it to have GNU_HASH too.
438 for line in phdrs.split("\n"):
439 if "SYMTAB" in line:
440 has_syms = True
Andrew Geissler82c905d2020-04-13 13:39:40 -0500441 if "GNU_HASH" or "DT_MIPS_XHASH" in line:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500442 sane = True
Andrew Geissler82c905d2020-04-13 13:39:40 -0500443 if ("[mips32]" in line or "[mips64]" in line) and d.getVar('TCLIBC') == "musl":
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500444 sane = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500445 if has_syms and not sane:
Brad Bishopa5c52ff2018-11-23 10:55:50 +1300446 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 -0500447
448
449QAPATHTEST[buildpaths] = "package_qa_check_buildpaths"
450def package_qa_check_buildpaths(path, name, d, elf, messages):
451 """
452 Check for build paths inside target files and error if not found in the whitelist
453 """
454 # Ignore .debug files, not interesting
455 if path.find(".debug") != -1:
456 return
457
458 # Ignore symlinks
459 if os.path.islink(path):
460 return
461
Brad Bishopd5ae7d92018-06-14 09:52:03 -0700462 tmpdir = bytes(d.getVar('TMPDIR'), encoding="utf-8")
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500463 with open(path, 'rb') as f:
Brad Bishopd5ae7d92018-06-14 09:52:03 -0700464 file_content = f.read()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500465 if tmpdir in file_content:
Brad Bishopf3fd2882019-06-21 08:06:37 -0400466 trimmed = path.replace(os.path.join (d.getVar("PKGDEST"), name), "")
467 package_qa_add_message(messages, "buildpaths", "File %s in package %s contains reference to TMPDIR" % (trimmed, name))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500468
469
470QAPATHTEST[xorg-driver-abi] = "package_qa_check_xorg_driver_abi"
471def package_qa_check_xorg_driver_abi(path, name, d, elf, messages):
472 """
473 Check that all packages containing Xorg drivers have ABI dependencies
474 """
475
476 # Skip dev, dbg or nativesdk packages
477 if name.endswith("-dev") or name.endswith("-dbg") or name.startswith("nativesdk-"):
478 return
479
480 driverdir = d.expand("${libdir}/xorg/modules/drivers/")
481 if driverdir in path and path.endswith(".so"):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500482 mlprefix = d.getVar('MLPREFIX') or ''
483 for rdep in bb.utils.explode_deps(d.getVar('RDEPENDS_' + name) or ""):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500484 if rdep.startswith("%sxorg-abi-" % mlprefix):
485 return
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500486 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 -0500487
488QAPATHTEST[infodir] = "package_qa_check_infodir"
489def package_qa_check_infodir(path, name, d, elf, messages):
490 """
491 Check that /usr/share/info/dir isn't shipped in a particular package
492 """
493 infodir = d.expand("${infodir}/dir")
494
495 if infodir in path:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500496 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 -0500497
498QAPATHTEST[symlink-to-sysroot] = "package_qa_check_symlink_to_sysroot"
499def package_qa_check_symlink_to_sysroot(path, name, d, elf, messages):
500 """
501 Check that the package doesn't contain any absolute symlinks to the sysroot.
502 """
503 if os.path.islink(path):
504 target = os.readlink(path)
505 if os.path.isabs(target):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500506 tmpdir = d.getVar('TMPDIR')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500507 if target.startswith(tmpdir):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500508 trimmed = path.replace(os.path.join (d.getVar("PKGDEST"), name), "")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500509 package_qa_add_message(messages, "symlink-to-sysroot", "Symlink %s in %s points to TMPDIR" % (trimmed, name))
510
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600511# Check license variables
512do_populate_lic[postfuncs] += "populate_lic_qa_checksum"
513python populate_lic_qa_checksum() {
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500514 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600515 Check for changes in the license files.
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500516 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500517 sane = True
518
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500519 lic_files = d.getVar('LIC_FILES_CHKSUM') or ''
520 lic = d.getVar('LICENSE')
521 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500522
523 if lic == "CLOSED":
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500524 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500525
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500526 if not lic_files and d.getVar('SRC_URI'):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800527 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 -0500528
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500529 srcdir = d.getVar('S')
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500530 corebase_licensefile = d.getVar('COREBASE') + "/LICENSE"
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500531 for url in lic_files.split():
532 try:
533 (type, host, path, user, pswd, parm) = bb.fetch.decodeurl(url)
534 except bb.fetch.MalformedUrl:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800535 sane &= package_qa_handle_error("license-checksum", pn + ": LIC_FILES_CHKSUM contains an invalid URL: " + url, d)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500536 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500537 srclicfile = os.path.join(srcdir, path)
538 if not os.path.isfile(srclicfile):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800539 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 -0500540 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500541
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500542 if (srclicfile == corebase_licensefile):
543 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")
544
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500545 recipemd5 = parm.get('md5', '')
546 beginline, endline = 0, 0
547 if 'beginline' in parm:
548 beginline = int(parm['beginline'])
549 if 'endline' in parm:
550 endline = int(parm['endline'])
551
552 if (not beginline) and (not endline):
553 md5chksum = bb.utils.md5_file(srclicfile)
Brad Bishop19323692019-04-05 15:28:33 -0400554 with open(srclicfile, 'r', errors='replace') as f:
555 license = f.read().splitlines()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500556 else:
Brad Bishop19323692019-04-05 15:28:33 -0400557 with open(srclicfile, 'rb') as f:
558 import hashlib
559 lineno = 0
560 license = []
561 m = hashlib.md5()
562 for line in f:
563 lineno += 1
564 if (lineno >= beginline):
565 if ((lineno <= endline) or not endline):
566 m.update(line)
567 license.append(line.decode('utf-8', errors='replace').rstrip())
568 else:
569 break
570 md5chksum = m.hexdigest()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500571 if recipemd5 == md5chksum:
572 bb.note (pn + ": md5 checksum matched for ", url)
573 else:
574 if recipemd5:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500575 msg = pn + ": The LIC_FILES_CHKSUM does not match for " + url
576 msg = msg + "\n" + pn + ": The new md5 checksum is " + md5chksum
Brad Bishop19323692019-04-05 15:28:33 -0400577 max_lines = int(d.getVar('QA_MAX_LICENSE_LINES') or 20)
578 if not license or license[-1] != '':
579 # Ensure that our license text ends with a line break
580 # (will be added with join() below).
581 license.append('')
582 remove = len(license) - max_lines
583 if remove > 0:
584 start = max_lines // 2
585 end = start + remove - 1
586 del license[start:end]
587 license.insert(start, '...')
588 msg = msg + "\n" + pn + ": Here is the selected license text:" + \
589 "\n" + \
590 "{:v^70}".format(" beginline=%d " % beginline if beginline else "") + \
591 "\n" + "\n".join(license) + \
592 "{:^^70}".format(" endline=%d " % endline if endline else "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500593 if beginline:
594 if endline:
595 srcfiledesc = "%s (lines %d through to %d)" % (srclicfile, beginline, endline)
596 else:
597 srcfiledesc = "%s (beginning on line %d)" % (srclicfile, beginline)
598 elif endline:
599 srcfiledesc = "%s (ending on line %d)" % (srclicfile, endline)
600 else:
601 srcfiledesc = srclicfile
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500602 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 -0500603
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500604 else:
605 msg = pn + ": LIC_FILES_CHKSUM is not specified for " + url
606 msg = msg + "\n" + pn + ": The md5 checksum is " + md5chksum
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800607 sane &= package_qa_handle_error("license-checksum", msg, d)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600608
609 if not sane:
610 bb.fatal("Fatal QA errors found, failing task.")
611}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500612
Brad Bishop19323692019-04-05 15:28:33 -0400613def qa_check_staged(path,d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500614 """
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500615 Check staged la and pc files for common problems like references to the work
616 directory.
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500617
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500618 As this is run after every stage we should be able to find the one
619 responsible for the errors easily even if we look at every .pc and .la file.
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500620 """
621
622 sane = True
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500623 tmpdir = d.getVar('TMPDIR')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500624 workdir = os.path.join(tmpdir, "work")
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500625 recipesysroot = d.getVar("RECIPE_SYSROOT")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500626
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500627 if bb.data.inherits_class("native", d) or bb.data.inherits_class("cross", d):
628 pkgconfigcheck = workdir
629 else:
630 pkgconfigcheck = tmpdir
631
Brad Bishop19323692019-04-05 15:28:33 -0400632 skip = (d.getVar('INSANE_SKIP') or "").split()
633 skip_la = False
634 if 'la' in skip:
635 bb.note("Recipe %s skipping qa checking: la" % d.getVar('PN'))
636 skip_la = True
637
638 skip_pkgconfig = False
639 if 'pkgconfig' in skip:
640 bb.note("Recipe %s skipping qa checking: pkgconfig" % d.getVar('PN'))
641 skip_pkgconfig = True
642
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500643 # find all .la and .pc files
644 # read the content
645 # and check for stuff that looks wrong
646 for root, dirs, files in os.walk(path):
647 for file in files:
648 path = os.path.join(root,file)
Brad Bishop19323692019-04-05 15:28:33 -0400649 if file.endswith(".la") and not skip_la:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500650 with open(path) as f:
651 file_content = f.read()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500652 file_content = file_content.replace(recipesysroot, "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500653 if workdir in file_content:
654 error_msg = "%s failed sanity test (workdir) in path %s" % (file,root)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800655 sane &= package_qa_handle_error("la", error_msg, d)
Brad Bishop19323692019-04-05 15:28:33 -0400656 elif file.endswith(".pc") and not skip_pkgconfig:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500657 with open(path) as f:
658 file_content = f.read()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500659 file_content = file_content.replace(recipesysroot, "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500660 if pkgconfigcheck in file_content:
661 error_msg = "%s failed sanity test (tmpdir) in path %s" % (file,root)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800662 sane &= package_qa_handle_error("pkgconfig", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500663
664 return sane
665
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500666# Run all package-wide warnfuncs and errorfuncs
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500667def package_qa_package(warnfuncs, errorfuncs, package, d):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500668 warnings = {}
669 errors = {}
670
671 for func in warnfuncs:
672 func(package, d, warnings)
673 for func in errorfuncs:
674 func(package, d, errors)
675
676 for w in warnings:
677 package_qa_handle_error(w, warnings[w], d)
678 for e in errors:
679 package_qa_handle_error(e, errors[e], d)
680
681 return len(errors) == 0
682
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500683# Run all recipe-wide warnfuncs and errorfuncs
684def package_qa_recipe(warnfuncs, errorfuncs, pn, d):
685 warnings = {}
686 errors = {}
687
688 for func in warnfuncs:
689 func(pn, d, warnings)
690 for func in errorfuncs:
691 func(pn, d, errors)
692
693 for w in warnings:
694 package_qa_handle_error(w, warnings[w], d)
695 for e in errors:
696 package_qa_handle_error(e, errors[e], d)
697
698 return len(errors) == 0
699
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500700# Walk over all files in a directory and call func
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500701def package_qa_walk(warnfuncs, errorfuncs, package, d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500702 import oe.qa
703
704 #if this will throw an exception, then fix the dict above
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500705 target_os = d.getVar('TARGET_OS')
706 target_arch = d.getVar('TARGET_ARCH')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500707
708 warnings = {}
709 errors = {}
710 for path in pkgfiles[package]:
711 elf = oe.qa.ELFFile(path)
712 try:
713 elf.open()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500714 except (IOError, oe.qa.NotELFFileError):
715 # IOError can happen if the packaging control files disappear,
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500716 elf = None
717 for func in warnfuncs:
718 func(path, package, d, elf, warnings)
719 for func in errorfuncs:
720 func(path, package, d, elf, errors)
721
722 for w in warnings:
723 package_qa_handle_error(w, warnings[w], d)
724 for e in errors:
725 package_qa_handle_error(e, errors[e], d)
726
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500727def package_qa_check_rdepends(pkg, pkgdest, skip, taskdeps, packages, d):
728 # Don't do this check for kernel/module recipes, there aren't too many debug/development
729 # packages and you can get false positives e.g. on kernel-module-lirc-dev
730 if bb.data.inherits_class("kernel", d) or bb.data.inherits_class("module-base", d):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500731 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500732
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500733 if not "-dbg" in pkg and not "packagegroup-" in pkg and not "-image" in pkg:
734 localdata = bb.data.createCopy(d)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500735 localdata.setVar('OVERRIDES', localdata.getVar('OVERRIDES') + ':' + pkg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500736
737 # Now check the RDEPENDS
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500738 rdepends = bb.utils.explode_deps(localdata.getVar('RDEPENDS') or "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500739
740 # Now do the sanity check!!!
741 if "build-deps" not in skip:
742 for rdepend in rdepends:
743 if "-dbg" in rdepend and "debug-deps" not in skip:
744 error_msg = "%s rdepends on %s" % (pkg,rdepend)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500745 package_qa_handle_error("debug-deps", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500746 if (not "-dev" in pkg and not "-staticdev" in pkg) and rdepend.endswith("-dev") and "dev-deps" not in skip:
747 error_msg = "%s rdepends on %s" % (pkg, rdepend)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500748 package_qa_handle_error("dev-deps", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500749 if rdepend not in packages:
750 rdep_data = oe.packagedata.read_subpkgdata(rdepend, d)
751 if rdep_data and 'PN' in rdep_data and rdep_data['PN'] in taskdeps:
752 continue
753 if not rdep_data or not 'PN' in rdep_data:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500754 pkgdata_dir = d.getVar("PKGDATA_DIR")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500755 try:
756 possibles = os.listdir("%s/runtime-rprovides/%s/" % (pkgdata_dir, rdepend))
757 except OSError:
758 possibles = []
759 for p in possibles:
760 rdep_data = oe.packagedata.read_subpkgdata(p, d)
761 if rdep_data and 'PN' in rdep_data and rdep_data['PN'] in taskdeps:
762 break
763 if rdep_data and 'PN' in rdep_data and rdep_data['PN'] in taskdeps:
764 continue
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500765 if rdep_data and 'PN' in rdep_data:
766 error_msg = "%s rdepends on %s, but it isn't a build dependency, missing %s in DEPENDS or PACKAGECONFIG?" % (pkg, rdepend, rdep_data['PN'])
767 else:
768 error_msg = "%s rdepends on %s, but it isn't a build dependency?" % (pkg, rdepend)
769 package_qa_handle_error("build-deps", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500770
771 if "file-rdeps" not in skip:
772 ignored_file_rdeps = set(['/bin/sh', '/usr/bin/env', 'rtld(GNU_HASH)'])
773 if bb.data.inherits_class('nativesdk', d):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500774 ignored_file_rdeps |= set(['/bin/bash', '/usr/bin/perl', 'perl'])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500775 # For Saving the FILERDEPENDS
776 filerdepends = {}
777 rdep_data = oe.packagedata.read_subpkgdata(pkg, d)
778 for key in rdep_data:
779 if key.startswith("FILERDEPENDS_"):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500780 for subkey in bb.utils.explode_deps(rdep_data[key]):
781 if subkey not in ignored_file_rdeps and \
782 not subkey.startswith('perl('):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500783 # We already know it starts with FILERDEPENDS_
784 filerdepends[subkey] = key[13:]
785
786 if filerdepends:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500787 done = rdepends[:]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500788 # Add the rprovides of itself
789 if pkg not in done:
790 done.insert(0, pkg)
791
792 # The python is not a package, but python-core provides it, so
793 # skip checking /usr/bin/python if python is in the rdeps, in
794 # case there is a RDEPENDS_pkg = "python" in the recipe.
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500795 for py in [ d.getVar('MLPREFIX') + "python", "python" ]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500796 if py in done:
797 filerdepends.pop("/usr/bin/python",None)
798 done.remove(py)
799 for rdep in done:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500800 # The file dependencies may contain package names, e.g.,
801 # perl
802 filerdepends.pop(rdep,None)
803
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500804 # For Saving the FILERPROVIDES, RPROVIDES and FILES_INFO
805 rdep_data = oe.packagedata.read_subpkgdata(rdep, d)
806 for key in rdep_data:
807 if key.startswith("FILERPROVIDES_") or key.startswith("RPROVIDES_"):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500808 for subkey in bb.utils.explode_deps(rdep_data[key]):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500809 filerdepends.pop(subkey,None)
810 # Add the files list to the rprovides
811 if key == "FILES_INFO":
812 # Use eval() to make it as a dict
813 for subkey in eval(rdep_data[key]):
814 filerdepends.pop(subkey,None)
815 if not filerdepends:
816 # Break if all the file rdepends are met
817 break
818 if filerdepends:
819 for key in filerdepends:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500820 error_msg = "%s contained in package %s requires %s, but no providers found in RDEPENDS_%s?" % \
821 (filerdepends[key].replace("_%s" % pkg, "").replace("@underscore@", "_"), pkg, key, pkg)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500822 package_qa_handle_error("file-rdeps", error_msg, d)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500823package_qa_check_rdepends[vardepsexclude] = "OVERRIDES"
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500824
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500825def package_qa_check_deps(pkg, pkgdest, d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500826
827 localdata = bb.data.createCopy(d)
828 localdata.setVar('OVERRIDES', pkg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500829
830 def check_valid_deps(var):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500831 try:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500832 rvar = bb.utils.explode_dep_versions2(localdata.getVar(var) or "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500833 except ValueError as e:
834 bb.fatal("%s_%s: %s" % (var, pkg, e))
835 for dep in rvar:
836 for v in rvar[dep]:
837 if v and not v.startswith(('< ', '= ', '> ', '<= ', '>=')):
838 error_msg = "%s_%s is invalid: %s (%s) only comparisons <, =, >, <=, and >= are allowed" % (var, pkg, dep, v)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500839 package_qa_handle_error("dep-cmp", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500840
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500841 check_valid_deps('RDEPENDS')
842 check_valid_deps('RRECOMMENDS')
843 check_valid_deps('RSUGGESTS')
844 check_valid_deps('RPROVIDES')
845 check_valid_deps('RREPLACES')
846 check_valid_deps('RCONFLICTS')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500847
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500848QAPKGTEST[usrmerge] = "package_qa_check_usrmerge"
849def package_qa_check_usrmerge(pkg, d, messages):
850 pkgdest = d.getVar('PKGDEST')
851 pkg_dir = pkgdest + os.sep + pkg + os.sep
852 merged_dirs = ['bin', 'sbin', 'lib'] + d.getVar('MULTILIB_VARIANTS').split()
853 for f in merged_dirs:
854 if os.path.exists(pkg_dir + f) and not os.path.islink(pkg_dir + f):
855 msg = "%s package is not obeying usrmerge distro feature. /%s should be relocated to /usr." % (pkg, f)
856 package_qa_add_message(messages, "usrmerge", msg)
857 return False
858 return True
859
Brad Bishopf3f93bb2019-10-16 14:33:32 -0400860QAPKGTEST[perllocalpod] = "package_qa_check_perllocalpod"
861def package_qa_check_perllocalpod(pkg, d, messages):
862 """
863 Check that the recipe didn't ship a perlocal.pod file, which shouldn't be
864 installed in a distribution package. cpan.bbclass sets NO_PERLLOCAL=1 to
865 handle this for most recipes.
866 """
867 import glob
868 pkgd = oe.path.join(d.getVar('PKGDEST'), pkg)
869 podpath = oe.path.join(pkgd, d.getVar("libdir"), "perl*", "*", "*", "perllocal.pod")
870
871 matches = glob.glob(podpath)
872 if matches:
873 matches = [package_qa_clean_path(path, d, pkg) for path in matches]
874 msg = "%s contains perllocal.pod (%s), should not be installed" % (pkg, " ".join(matches))
875 package_qa_add_message(messages, "perllocalpod", msg)
876
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500877QAPKGTEST[expanded-d] = "package_qa_check_expanded_d"
878def package_qa_check_expanded_d(package, d, messages):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500879 """
880 Check for the expanded D (${D}) value in pkg_* and FILES
881 variables, warn the user to use it correctly.
882 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500883 sane = True
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500884 expanded_d = d.getVar('D')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500885
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500886 for var in 'FILES','pkg_preinst', 'pkg_postinst', 'pkg_prerm', 'pkg_postrm':
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500887 bbvar = d.getVar(var + "_" + package) or ""
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500888 if expanded_d in bbvar:
889 if var == 'FILES':
890 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)
891 sane = False
892 else:
893 package_qa_add_message(messages, "expanded-d", "%s in %s recipe contains ${D}, it should be replaced by $D instead" % (var, package))
894 sane = False
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500895 return sane
896
Andrew Geissler1e34c2d2020-05-29 16:02:59 -0500897QAPKGTEST[unlisted-pkg-lics] = "package_qa_check_unlisted_pkg_lics"
898def package_qa_check_unlisted_pkg_lics(package, d, messages):
899 """
900 Check that all licenses for a package are among the licenses for the recipe.
901 """
902 pkg_lics = d.getVar('LICENSE_' + package)
903 if not pkg_lics:
904 return True
905
906 recipe_lics_set = oe.license.list_licenses(d.getVar('LICENSE'))
907 unlisted = oe.license.list_licenses(pkg_lics) - recipe_lics_set
908 if not unlisted:
909 return True
910
911 package_qa_add_message(messages, "unlisted-pkg-lics",
912 "LICENSE_%s includes licenses (%s) that are not "
913 "listed in LICENSE" % (package, ' '.join(unlisted)))
914 return False
915
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500916def package_qa_check_encoding(keys, encode, d):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600917 def check_encoding(key, enc):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500918 sane = True
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500919 value = d.getVar(key)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500920 if value:
921 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600922 s = value.encode(enc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500923 except UnicodeDecodeError as e:
924 error_msg = "%s has non %s characters" % (key,enc)
925 sane = False
926 package_qa_handle_error("invalid-chars", error_msg, d)
927 return sane
928
929 for key in keys:
930 sane = check_encoding(key, encode)
931 if not sane:
932 break
933
934HOST_USER_UID := "${@os.getuid()}"
935HOST_USER_GID := "${@os.getgid()}"
936
937QAPATHTEST[host-user-contaminated] = "package_qa_check_host_user"
938def package_qa_check_host_user(path, name, d, elf, messages):
939 """Check for paths outside of /home which are owned by the user running bitbake."""
940
941 if not os.path.lexists(path):
942 return
943
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500944 dest = d.getVar('PKGDEST')
945 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500946 home = os.path.join(dest, 'home')
947 if path == home or path.startswith(home + os.sep):
948 return
949
950 try:
951 stat = os.lstat(path)
952 except OSError as exc:
953 import errno
954 if exc.errno != errno.ENOENT:
955 raise
956 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500957 check_uid = int(d.getVar('HOST_USER_UID'))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500958 if stat.st_uid == check_uid:
Brad Bishop96ff1982019-08-19 13:50:42 -0400959 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 -0500960 return False
961
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500962 check_gid = int(d.getVar('HOST_USER_GID'))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500963 if stat.st_gid == check_gid:
Brad Bishop96ff1982019-08-19 13:50:42 -0400964 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 -0500965 return False
966 return True
967
Brad Bishop15ae2502019-06-18 21:44:24 -0400968QARECIPETEST[src-uri-bad] = "package_qa_check_src_uri"
969def package_qa_check_src_uri(pn, d, messages):
970 import re
971
972 if "${PN}" in d.getVar("SRC_URI", False):
973 package_qa_handle_error("src-uri-bad", "%s: SRC_URI uses PN not BPN" % pn, d)
974
Andrew Geissler82c905d2020-04-13 13:39:40 -0500975 for url in d.getVar("SRC_URI").split():
976 if re.search(r"github\.com/.+/.+/archive/.+", url):
977 package_qa_handle_error("src-uri-bad", "%s: SRC_URI uses unstable GitHub archives" % pn, d)
Brad Bishop15ae2502019-06-18 21:44:24 -0400978
Andrew Geissler5a43b432020-06-13 10:46:56 -0500979QARECIPETEST[unhandled-features-check] = "package_qa_check_unhandled_features_check"
980def package_qa_check_unhandled_features_check(pn, d, messages):
981 if not bb.data.inherits_class('features_check', d):
982 var_set = False
983 for kind in ['DISTRO', 'MACHINE', 'COMBINED']:
984 for var in ['ANY_OF_' + kind + '_FEATURES', 'REQUIRED_' + kind + '_FEATURES', 'CONFLICT_' + kind + '_FEATURES']:
985 if d.getVar(var) is not None or d.overridedata.get(var) is not None:
986 var_set = True
987 if var_set:
988 package_qa_handle_error("unhandled-features-check", "%s: recipe doesn't inherit features_check" % pn, d)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500989
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500990# The PACKAGE FUNC to scan each package
991python do_package_qa () {
992 import subprocess
993 import oe.packagedata
994
995 bb.note("DO PACKAGE QA")
996
997 bb.build.exec_func("read_subpackage_metadata", d)
998
999 # Check non UTF-8 characters on recipe's metadata
1000 package_qa_check_encoding(['DESCRIPTION', 'SUMMARY', 'LICENSE', 'SECTION'], 'utf-8', d)
1001
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001002 logdir = d.getVar('T')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001003 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001004
1005 # Check the compile log for host contamination
1006 compilelog = os.path.join(logdir,"log.do_compile")
1007
1008 if os.path.exists(compilelog):
1009 statement = "grep -e 'CROSS COMPILE Badness:' -e 'is unsafe for cross-compilation' %s > /dev/null" % compilelog
1010 if subprocess.call(statement, shell=True) == 0:
1011 msg = "%s: The compile log indicates that host include and/or library paths were used.\n \
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001012 Please check the log '%s' for more information." % (pn, compilelog)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001013 package_qa_handle_error("compile-host-path", msg, d)
1014
1015 # Check the install log for host contamination
1016 installlog = os.path.join(logdir,"log.do_install")
1017
1018 if os.path.exists(installlog):
1019 statement = "grep -e 'CROSS COMPILE Badness:' -e 'is unsafe for cross-compilation' %s > /dev/null" % installlog
1020 if subprocess.call(statement, shell=True) == 0:
1021 msg = "%s: The install log indicates that host include and/or library paths were used.\n \
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001022 Please check the log '%s' for more information." % (pn, installlog)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001023 package_qa_handle_error("install-host-path", msg, d)
1024
1025 # Scan the packages...
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001026 pkgdest = d.getVar('PKGDEST')
1027 packages = set((d.getVar('PACKAGES') or '').split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001028
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001029 global pkgfiles
1030 pkgfiles = {}
1031 for pkg in packages:
1032 pkgfiles[pkg] = []
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001033 pkgdir = os.path.join(pkgdest, pkg)
1034 for walkroot, dirs, files in os.walk(pkgdir):
1035 # Don't walk into top-level CONTROL or DEBIAN directories as these
1036 # are temporary directories created by do_package.
1037 if walkroot == pkgdir:
1038 for control in ("CONTROL", "DEBIAN"):
1039 if control in dirs:
1040 dirs.remove(control)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001041 for file in files:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001042 pkgfiles[pkg].append(os.path.join(walkroot, file))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001043
1044 # no packages should be scanned
1045 if not packages:
1046 return
1047
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001048 import re
1049 # The package name matches the [a-z0-9.+-]+ regular expression
Brad Bishop977dc1a2019-02-06 16:01:43 -05001050 pkgname_pattern = re.compile(r"^[a-z0-9.+-]+$")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001051
1052 taskdepdata = d.getVar("BB_TASKDEPDATA", False)
1053 taskdeps = set()
1054 for dep in taskdepdata:
1055 taskdeps.add(taskdepdata[dep][0])
1056
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001057 def parse_test_matrix(matrix_name):
1058 testmatrix = d.getVarFlags(matrix_name) or {}
1059 g = globals()
1060 warnchecks = []
1061 for w in (d.getVar("WARN_QA") or "").split():
1062 if w in skip:
1063 continue
1064 if w in testmatrix and testmatrix[w] in g:
1065 warnchecks.append(g[testmatrix[w]])
1066
1067 errorchecks = []
1068 for e in (d.getVar("ERROR_QA") or "").split():
1069 if e in skip:
1070 continue
1071 if e in testmatrix and testmatrix[e] in g:
1072 errorchecks.append(g[testmatrix[e]])
1073 return warnchecks, errorchecks
1074
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001075 for package in packages:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001076 skip = set((d.getVar('INSANE_SKIP') or "").split() +
1077 (d.getVar('INSANE_SKIP_' + package) or "").split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001078 if skip:
1079 bb.note("Package %s skipping QA tests: %s" % (package, str(skip)))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001080
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001081 bb.note("Checking Package: %s" % package)
1082 # Check package name
1083 if not pkgname_pattern.match(package):
1084 package_qa_handle_error("pkgname",
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001085 "%s doesn't match the [a-z0-9.+-]+ regex" % package, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001086
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001087 warn_checks, error_checks = parse_test_matrix("QAPATHTEST")
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001088 package_qa_walk(warn_checks, error_checks, package, d)
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001089
1090 warn_checks, error_checks = parse_test_matrix("QAPKGTEST")
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001091 package_qa_package(warn_checks, error_checks, package, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001092
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001093 package_qa_check_rdepends(package, pkgdest, skip, taskdeps, packages, d)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001094 package_qa_check_deps(package, pkgdest, d)
1095
1096 warn_checks, error_checks = parse_test_matrix("QARECIPETEST")
1097 package_qa_recipe(warn_checks, error_checks, pn, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001098
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001099 if 'libdir' in d.getVar("ALL_QA").split():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001100 package_qa_check_libdir(d)
1101
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001102 qa_sane = d.getVar("QA_SANE")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001103 if not qa_sane:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001104 bb.fatal("QA run found fatal errors. Please consider fixing them.")
1105 bb.note("DONE with PACKAGE QA")
1106}
1107
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001108# binutils is used for most checks, so need to set as dependency
1109# POPULATESYSROOTDEPS is defined in staging class.
1110do_package_qa[depends] += "${POPULATESYSROOTDEPS}"
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001111do_package_qa[vardepsexclude] = "BB_TASKDEPDATA"
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001112do_package_qa[rdeptask] = "do_packagedata"
1113addtask do_package_qa after do_packagedata do_package before do_build
1114
Brad Bishop19323692019-04-05 15:28:33 -04001115# Add the package specific INSANE_SKIPs to the sstate dependencies
1116python() {
1117 pkgs = (d.getVar('PACKAGES') or '').split()
1118 for pkg in pkgs:
1119 d.appendVarFlag("do_package_qa", "vardeps", " INSANE_SKIP_{}".format(pkg))
1120}
1121
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001122SSTATETASKS += "do_package_qa"
1123do_package_qa[sstate-inputdirs] = ""
1124do_package_qa[sstate-outputdirs] = ""
1125python do_package_qa_setscene () {
1126 sstate_setscene(d)
1127}
1128addtask do_package_qa_setscene
1129
1130python do_qa_staging() {
1131 bb.note("QA checking staging")
Brad Bishop19323692019-04-05 15:28:33 -04001132 if not qa_check_staged(d.expand('${SYSROOT_DESTDIR}${libdir}'), d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001133 bb.fatal("QA staging was broken by the package built above")
1134}
1135
Brad Bishopd89cb5f2019-04-10 09:02:41 -04001136python do_qa_patch() {
1137 import subprocess
1138
1139 ###########################################################################
1140 # Check patch.log for fuzz warnings
1141 #
1142 # Further information on why we check for patch fuzz warnings:
1143 # http://lists.openembedded.org/pipermail/openembedded-core/2018-March/148675.html
1144 # https://bugzilla.yoctoproject.org/show_bug.cgi?id=10450
1145 ###########################################################################
1146
1147 logdir = d.getVar('T')
1148 patchlog = os.path.join(logdir,"log.do_patch")
1149
1150 if os.path.exists(patchlog):
1151 fuzzheader = '--- Patch fuzz start ---'
1152 fuzzfooter = '--- Patch fuzz end ---'
1153 statement = "grep -e '%s' %s > /dev/null" % (fuzzheader, patchlog)
1154 if subprocess.call(statement, shell=True) == 0:
1155 msg = "Fuzz detected:\n\n"
1156 fuzzmsg = ""
1157 inFuzzInfo = False
1158 f = open(patchlog, "r")
1159 for line in f:
1160 if fuzzheader in line:
1161 inFuzzInfo = True
1162 fuzzmsg = ""
1163 elif fuzzfooter in line:
1164 fuzzmsg = fuzzmsg.replace('\n\n', '\n')
1165 msg += fuzzmsg
1166 msg += "\n"
1167 inFuzzInfo = False
1168 elif inFuzzInfo and not 'Now at patch' in line:
1169 fuzzmsg += line
1170 f.close()
1171 msg += "The context lines in the patches can be updated with devtool:\n"
1172 msg += "\n"
1173 msg += " devtool modify %s\n" % d.getVar('PN')
1174 msg += " devtool finish --force-patch-refresh %s <layer_path>\n\n" % d.getVar('PN')
1175 msg += "Don't forget to review changes done by devtool!\n"
1176 if 'patch-fuzz' in d.getVar('ERROR_QA'):
1177 bb.error(msg)
1178 elif 'patch-fuzz' in d.getVar('WARN_QA'):
1179 bb.warn(msg)
1180 msg = "Patch log indicates that patches do not apply cleanly."
1181 package_qa_handle_error("patch-fuzz", msg, d)
1182}
1183
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001184python do_qa_configure() {
1185 import subprocess
1186
1187 ###########################################################################
1188 # Check config.log for cross compile issues
1189 ###########################################################################
1190
1191 configs = []
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001192 workdir = d.getVar('WORKDIR')
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001193
Brad Bishop19323692019-04-05 15:28:33 -04001194 skip = (d.getVar('INSANE_SKIP') or "").split()
1195 skip_configure_unsafe = False
1196 if 'configure-unsafe' in skip:
1197 bb.note("Recipe %s skipping qa checking: configure-unsafe" % d.getVar('PN'))
1198 skip_configure_unsafe = True
1199
1200 if bb.data.inherits_class('autotools', d) and not skip_configure_unsafe:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001201 bb.note("Checking autotools environment for common misconfiguration")
1202 for root, dirs, files in os.walk(workdir):
1203 statement = "grep -q -F -e 'CROSS COMPILE Badness:' -e 'is unsafe for cross-compilation' %s" % \
1204 os.path.join(root,"config.log")
1205 if "config.log" in files:
1206 if subprocess.call(statement, shell=True) == 0:
Brad Bishop19323692019-04-05 15:28:33 -04001207 error_msg = """This autoconf log indicates errors, it looked at host include and/or library paths while determining system capabilities.
1208Rerun configure task after fixing this."""
1209 package_qa_handle_error("configure-unsafe", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001210
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001211 if "configure.ac" in files:
1212 configs.append(os.path.join(root,"configure.ac"))
1213 if "configure.in" in files:
1214 configs.append(os.path.join(root, "configure.in"))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001215
1216 ###########################################################################
1217 # Check gettext configuration and dependencies are correct
1218 ###########################################################################
1219
Brad Bishop19323692019-04-05 15:28:33 -04001220 skip_configure_gettext = False
1221 if 'configure-gettext' in skip:
1222 bb.note("Recipe %s skipping qa checking: configure-gettext" % d.getVar('PN'))
1223 skip_configure_gettext = True
1224
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001225 cnf = d.getVar('EXTRA_OECONF') or ""
Brad Bishop19323692019-04-05 15:28:33 -04001226 if not ("gettext" in d.getVar('P') or "gcc-runtime" in d.getVar('P') or \
1227 "--disable-nls" in cnf or skip_configure_gettext):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001228 ml = d.getVar("MLPREFIX") or ""
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001229 if bb.data.inherits_class('cross-canadian', d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001230 gt = "nativesdk-gettext"
1231 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001232 gt = "gettext-native"
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001233 deps = bb.utils.explode_deps(d.getVar('DEPENDS') or "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001234 if gt not in deps:
1235 for config in configs:
1236 gnu = "grep \"^[[:space:]]*AM_GNU_GETTEXT\" %s >/dev/null" % config
1237 if subprocess.call(gnu, shell=True) == 0:
Brad Bishopd89cb5f2019-04-10 09:02:41 -04001238 error_msg = "AM_GNU_GETTEXT used but no inherit gettext"
Brad Bishop19323692019-04-05 15:28:33 -04001239 package_qa_handle_error("configure-gettext", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001240
1241 ###########################################################################
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001242 # Check unrecognised configure options (with a white list)
1243 ###########################################################################
Brad Bishopc342db32019-05-15 21:57:59 -04001244 if bb.data.inherits_class("autotools", d) or bb.data.inherits_class("meson", d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001245 bb.note("Checking configure output for unrecognised options")
1246 try:
Brad Bishopc342db32019-05-15 21:57:59 -04001247 if bb.data.inherits_class("autotools", d):
1248 flag = "WARNING: unrecognized options:"
1249 log = os.path.join(d.getVar('B'), 'config.log')
1250 if bb.data.inherits_class("meson", d):
1251 flag = "WARNING: Unknown options:"
1252 log = os.path.join(d.getVar('T'), 'log.do_configure')
1253 output = subprocess.check_output(['grep', '-F', flag, log]).decode("utf-8").replace(', ', ' ').replace('"', '')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001254 options = set()
1255 for line in output.splitlines():
1256 options |= set(line.partition(flag)[2].split())
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001257 whitelist = set(d.getVar("UNKNOWN_CONFIGURE_WHITELIST").split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001258 options -= whitelist
1259 if options:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001260 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001261 error_msg = pn + ": configure was passed unrecognised options: " + " ".join(options)
1262 package_qa_handle_error("unknown-configure-option", error_msg, d)
1263 except subprocess.CalledProcessError:
1264 pass
1265
1266 # Check invalid PACKAGECONFIG
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001267 pkgconfig = (d.getVar("PACKAGECONFIG") or "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001268 if pkgconfig:
1269 pkgconfigflags = d.getVarFlags("PACKAGECONFIG") or {}
1270 for pconfig in pkgconfig:
1271 if pconfig not in pkgconfigflags:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001272 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001273 error_msg = "%s: invalid PACKAGECONFIG: %s" % (pn, pconfig)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001274 package_qa_handle_error("invalid-packageconfig", error_msg, d)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001275
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001276 qa_sane = d.getVar("QA_SANE")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001277 if not qa_sane:
1278 bb.fatal("Fatal QA errors found, failing task.")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001279}
1280
1281python do_qa_unpack() {
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001282 src_uri = d.getVar('SRC_URI')
1283 s_dir = d.getVar('S')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001284 if src_uri and not os.path.exists(s_dir):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001285 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 -05001286}
1287
1288# The Staging Func, to check all staging
1289#addtask qa_staging after do_populate_sysroot before do_build
1290do_populate_sysroot[postfuncs] += "do_qa_staging "
1291
Brad Bishopd89cb5f2019-04-10 09:02:41 -04001292# Check for patch fuzz
1293do_patch[postfuncs] += "do_qa_patch "
1294
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001295# Check broken config.log files, for packages requiring Gettext which
1296# don't have it in DEPENDS.
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001297#addtask qa_configure after do_configure before do_compile
1298do_configure[postfuncs] += "do_qa_configure "
1299
1300# Check does S exist.
1301do_unpack[postfuncs] += "do_qa_unpack"
1302
1303python () {
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001304 import re
1305
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001306 tests = d.getVar('ALL_QA').split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001307 if "desktop" in tests:
1308 d.appendVar("PACKAGE_DEPENDS", " desktop-file-utils-native")
1309
1310 ###########################################################################
1311 # Check various variables
1312 ###########################################################################
1313
1314 # Checking ${FILESEXTRAPATHS}
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001315 extrapaths = (d.getVar("FILESEXTRAPATHS") or "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001316 if '__default' not in extrapaths.split(":"):
1317 msg = "FILESEXTRAPATHS-variable, must always use _prepend (or _append)\n"
1318 msg += "type of assignment, and don't forget the colon.\n"
1319 msg += "Please assign it with the format of:\n"
1320 msg += " FILESEXTRAPATHS_append := \":${THISDIR}/Your_Files_Path\" or\n"
1321 msg += " FILESEXTRAPATHS_prepend := \"${THISDIR}/Your_Files_Path:\"\n"
1322 msg += "in your bbappend file\n\n"
1323 msg += "Your incorrect assignment is:\n"
1324 msg += "%s\n" % extrapaths
1325 bb.warn(msg)
1326
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001327 overrides = d.getVar('OVERRIDES').split(':')
1328 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001329 if pn in overrides:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001330 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 -05001331 package_qa_handle_error("pn-overrides", msg, d)
Brad Bishop977dc1a2019-02-06 16:01:43 -05001332 prog = re.compile(r'[A-Z]')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001333 if prog.search(pn):
1334 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 -05001335
Brad Bishop08902b02019-08-20 09:16:51 -04001336 # Some people mistakenly use DEPENDS_${PN} instead of DEPENDS and wonder
1337 # why it doesn't work.
1338 if (d.getVar(d.expand('DEPENDS_${PN}'))):
1339 package_qa_handle_error("pkgvarcheck", "recipe uses DEPENDS_${PN}, should use DEPENDS", d)
1340
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001341 issues = []
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001342 if (d.getVar('PACKAGES') or "").split():
1343 for dep in (d.getVar('QADEPENDS') or "").split():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001344 d.appendVarFlag('do_package_qa', 'depends', " %s:do_populate_sysroot" % dep)
1345 for var in 'RDEPENDS', 'RRECOMMENDS', 'RSUGGESTS', 'RCONFLICTS', 'RPROVIDES', 'RREPLACES', 'FILES', 'pkg_preinst', 'pkg_postinst', 'pkg_prerm', 'pkg_postrm', 'ALLOW_EMPTY':
1346 if d.getVar(var, False):
1347 issues.append(var)
1348
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001349 fakeroot_tests = d.getVar('FAKEROOT_QA').split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001350 if set(tests) & set(fakeroot_tests):
1351 d.setVarFlag('do_package_qa', 'fakeroot', '1')
1352 d.appendVarFlag('do_package_qa', 'depends', ' virtual/fakeroot-native:do_populate_sysroot')
1353 else:
1354 d.setVarFlag('do_package_qa', 'rdeptask', '')
1355 for i in issues:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001356 package_qa_handle_error("pkgvarcheck", "%s: Variable %s is set as not being package specific, please fix this." % (d.getVar("FILE"), i), d)
1357 qa_sane = d.getVar("QA_SANE")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001358 if not qa_sane:
1359 bb.fatal("Fatal QA errors found, failing task.")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001360}