blob: 649aea1da197ca65799b565c8150317f79aefc3f [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):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600367 package_qa_add_message(messages, "arch", "Architecture did not match (%s, expected %s) on %s" % \
368 (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 -0400369 elif not ((bits == elf.abiSize()) or is_32 or is_bpf):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500370 package_qa_add_message(messages, "arch", "Bit size did not match (%d to %d) %s on %s" % \
371 (bits, elf.abiSize(), bpn, package_qa_clean_path(path,d)))
Brad Bishop19323692019-04-05 15:28:33 -0400372 elif not ((littleendian == elf.isLittleEndian()) or is_bpf):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500373 package_qa_add_message(messages, "arch", "Endiannes did not match (%d to %d) on %s" % \
374 (littleendian, elf.isLittleEndian(), package_qa_clean_path(path,d)))
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
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500462 # Ignore ipk and deb's CONTROL dir
463 if path.find(name + "/CONTROL/") != -1 or path.find(name + "/DEBIAN/") != -1:
464 return
465
Brad Bishopd5ae7d92018-06-14 09:52:03 -0700466 tmpdir = bytes(d.getVar('TMPDIR'), encoding="utf-8")
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500467 with open(path, 'rb') as f:
Brad Bishopd5ae7d92018-06-14 09:52:03 -0700468 file_content = f.read()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500469 if tmpdir in file_content:
Brad Bishopf3fd2882019-06-21 08:06:37 -0400470 trimmed = path.replace(os.path.join (d.getVar("PKGDEST"), name), "")
471 package_qa_add_message(messages, "buildpaths", "File %s in package %s contains reference to TMPDIR" % (trimmed, name))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500472
473
474QAPATHTEST[xorg-driver-abi] = "package_qa_check_xorg_driver_abi"
475def package_qa_check_xorg_driver_abi(path, name, d, elf, messages):
476 """
477 Check that all packages containing Xorg drivers have ABI dependencies
478 """
479
480 # Skip dev, dbg or nativesdk packages
481 if name.endswith("-dev") or name.endswith("-dbg") or name.startswith("nativesdk-"):
482 return
483
484 driverdir = d.expand("${libdir}/xorg/modules/drivers/")
485 if driverdir in path and path.endswith(".so"):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500486 mlprefix = d.getVar('MLPREFIX') or ''
487 for rdep in bb.utils.explode_deps(d.getVar('RDEPENDS_' + name) or ""):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500488 if rdep.startswith("%sxorg-abi-" % mlprefix):
489 return
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500490 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 -0500491
492QAPATHTEST[infodir] = "package_qa_check_infodir"
493def package_qa_check_infodir(path, name, d, elf, messages):
494 """
495 Check that /usr/share/info/dir isn't shipped in a particular package
496 """
497 infodir = d.expand("${infodir}/dir")
498
499 if infodir in path:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500500 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 -0500501
502QAPATHTEST[symlink-to-sysroot] = "package_qa_check_symlink_to_sysroot"
503def package_qa_check_symlink_to_sysroot(path, name, d, elf, messages):
504 """
505 Check that the package doesn't contain any absolute symlinks to the sysroot.
506 """
507 if os.path.islink(path):
508 target = os.readlink(path)
509 if os.path.isabs(target):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500510 tmpdir = d.getVar('TMPDIR')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500511 if target.startswith(tmpdir):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500512 trimmed = path.replace(os.path.join (d.getVar("PKGDEST"), name), "")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500513 package_qa_add_message(messages, "symlink-to-sysroot", "Symlink %s in %s points to TMPDIR" % (trimmed, name))
514
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600515# Check license variables
516do_populate_lic[postfuncs] += "populate_lic_qa_checksum"
517python populate_lic_qa_checksum() {
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500518 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600519 Check for changes in the license files.
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500520 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500521 sane = True
522
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500523 lic_files = d.getVar('LIC_FILES_CHKSUM') or ''
524 lic = d.getVar('LICENSE')
525 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500526
527 if lic == "CLOSED":
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500528 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500529
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500530 if not lic_files and d.getVar('SRC_URI'):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800531 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 -0500532
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500533 srcdir = d.getVar('S')
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500534 corebase_licensefile = d.getVar('COREBASE') + "/LICENSE"
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500535 for url in lic_files.split():
536 try:
537 (type, host, path, user, pswd, parm) = bb.fetch.decodeurl(url)
538 except bb.fetch.MalformedUrl:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800539 sane &= package_qa_handle_error("license-checksum", pn + ": LIC_FILES_CHKSUM contains an invalid URL: " + url, d)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500540 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500541 srclicfile = os.path.join(srcdir, path)
542 if not os.path.isfile(srclicfile):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800543 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 -0500544 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500545
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500546 if (srclicfile == corebase_licensefile):
547 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")
548
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500549 recipemd5 = parm.get('md5', '')
550 beginline, endline = 0, 0
551 if 'beginline' in parm:
552 beginline = int(parm['beginline'])
553 if 'endline' in parm:
554 endline = int(parm['endline'])
555
556 if (not beginline) and (not endline):
557 md5chksum = bb.utils.md5_file(srclicfile)
Brad Bishop19323692019-04-05 15:28:33 -0400558 with open(srclicfile, 'r', errors='replace') as f:
559 license = f.read().splitlines()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500560 else:
Brad Bishop19323692019-04-05 15:28:33 -0400561 with open(srclicfile, 'rb') as f:
562 import hashlib
563 lineno = 0
564 license = []
565 m = hashlib.md5()
566 for line in f:
567 lineno += 1
568 if (lineno >= beginline):
569 if ((lineno <= endline) or not endline):
570 m.update(line)
571 license.append(line.decode('utf-8', errors='replace').rstrip())
572 else:
573 break
574 md5chksum = m.hexdigest()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500575 if recipemd5 == md5chksum:
576 bb.note (pn + ": md5 checksum matched for ", url)
577 else:
578 if recipemd5:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500579 msg = pn + ": The LIC_FILES_CHKSUM does not match for " + url
580 msg = msg + "\n" + pn + ": The new md5 checksum is " + md5chksum
Brad Bishop19323692019-04-05 15:28:33 -0400581 max_lines = int(d.getVar('QA_MAX_LICENSE_LINES') or 20)
582 if not license or license[-1] != '':
583 # Ensure that our license text ends with a line break
584 # (will be added with join() below).
585 license.append('')
586 remove = len(license) - max_lines
587 if remove > 0:
588 start = max_lines // 2
589 end = start + remove - 1
590 del license[start:end]
591 license.insert(start, '...')
592 msg = msg + "\n" + pn + ": Here is the selected license text:" + \
593 "\n" + \
594 "{:v^70}".format(" beginline=%d " % beginline if beginline else "") + \
595 "\n" + "\n".join(license) + \
596 "{:^^70}".format(" endline=%d " % endline if endline else "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500597 if beginline:
598 if endline:
599 srcfiledesc = "%s (lines %d through to %d)" % (srclicfile, beginline, endline)
600 else:
601 srcfiledesc = "%s (beginning on line %d)" % (srclicfile, beginline)
602 elif endline:
603 srcfiledesc = "%s (ending on line %d)" % (srclicfile, endline)
604 else:
605 srcfiledesc = srclicfile
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500606 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 -0500607
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500608 else:
609 msg = pn + ": LIC_FILES_CHKSUM is not specified for " + url
610 msg = msg + "\n" + pn + ": The md5 checksum is " + md5chksum
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800611 sane &= package_qa_handle_error("license-checksum", msg, d)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600612
613 if not sane:
614 bb.fatal("Fatal QA errors found, failing task.")
615}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500616
Brad Bishop19323692019-04-05 15:28:33 -0400617def qa_check_staged(path,d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500618 """
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500619 Check staged la and pc files for common problems like references to the work
620 directory.
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500621
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500622 As this is run after every stage we should be able to find the one
623 responsible for the errors easily even if we look at every .pc and .la file.
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500624 """
625
626 sane = True
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500627 tmpdir = d.getVar('TMPDIR')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500628 workdir = os.path.join(tmpdir, "work")
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500629 recipesysroot = d.getVar("RECIPE_SYSROOT")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500630
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500631 if bb.data.inherits_class("native", d) or bb.data.inherits_class("cross", d):
632 pkgconfigcheck = workdir
633 else:
634 pkgconfigcheck = tmpdir
635
Brad Bishop19323692019-04-05 15:28:33 -0400636 skip = (d.getVar('INSANE_SKIP') or "").split()
637 skip_la = False
638 if 'la' in skip:
639 bb.note("Recipe %s skipping qa checking: la" % d.getVar('PN'))
640 skip_la = True
641
642 skip_pkgconfig = False
643 if 'pkgconfig' in skip:
644 bb.note("Recipe %s skipping qa checking: pkgconfig" % d.getVar('PN'))
645 skip_pkgconfig = True
646
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500647 # find all .la and .pc files
648 # read the content
649 # and check for stuff that looks wrong
650 for root, dirs, files in os.walk(path):
651 for file in files:
652 path = os.path.join(root,file)
Brad Bishop19323692019-04-05 15:28:33 -0400653 if file.endswith(".la") and not skip_la:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500654 with open(path) as f:
655 file_content = f.read()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500656 file_content = file_content.replace(recipesysroot, "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500657 if workdir in file_content:
658 error_msg = "%s failed sanity test (workdir) in path %s" % (file,root)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800659 sane &= package_qa_handle_error("la", error_msg, d)
Brad Bishop19323692019-04-05 15:28:33 -0400660 elif file.endswith(".pc") and not skip_pkgconfig:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500661 with open(path) as f:
662 file_content = f.read()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500663 file_content = file_content.replace(recipesysroot, "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500664 if pkgconfigcheck in file_content:
665 error_msg = "%s failed sanity test (tmpdir) in path %s" % (file,root)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800666 sane &= package_qa_handle_error("pkgconfig", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500667
668 return sane
669
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500670# Run all package-wide warnfuncs and errorfuncs
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500671def package_qa_package(warnfuncs, errorfuncs, package, d):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500672 warnings = {}
673 errors = {}
674
675 for func in warnfuncs:
676 func(package, d, warnings)
677 for func in errorfuncs:
678 func(package, d, errors)
679
680 for w in warnings:
681 package_qa_handle_error(w, warnings[w], d)
682 for e in errors:
683 package_qa_handle_error(e, errors[e], d)
684
685 return len(errors) == 0
686
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500687# Run all recipe-wide warnfuncs and errorfuncs
688def package_qa_recipe(warnfuncs, errorfuncs, pn, d):
689 warnings = {}
690 errors = {}
691
692 for func in warnfuncs:
693 func(pn, d, warnings)
694 for func in errorfuncs:
695 func(pn, d, errors)
696
697 for w in warnings:
698 package_qa_handle_error(w, warnings[w], d)
699 for e in errors:
700 package_qa_handle_error(e, errors[e], d)
701
702 return len(errors) == 0
703
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500704# Walk over all files in a directory and call func
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500705def package_qa_walk(warnfuncs, errorfuncs, package, d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500706 import oe.qa
707
708 #if this will throw an exception, then fix the dict above
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500709 target_os = d.getVar('TARGET_OS')
710 target_arch = d.getVar('TARGET_ARCH')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500711
712 warnings = {}
713 errors = {}
714 for path in pkgfiles[package]:
715 elf = oe.qa.ELFFile(path)
716 try:
717 elf.open()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500718 except (IOError, oe.qa.NotELFFileError):
719 # IOError can happen if the packaging control files disappear,
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500720 elf = None
721 for func in warnfuncs:
722 func(path, package, d, elf, warnings)
723 for func in errorfuncs:
724 func(path, package, d, elf, errors)
725
726 for w in warnings:
727 package_qa_handle_error(w, warnings[w], d)
728 for e in errors:
729 package_qa_handle_error(e, errors[e], d)
730
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500731def package_qa_check_rdepends(pkg, pkgdest, skip, taskdeps, packages, d):
732 # Don't do this check for kernel/module recipes, there aren't too many debug/development
733 # packages and you can get false positives e.g. on kernel-module-lirc-dev
734 if bb.data.inherits_class("kernel", d) or bb.data.inherits_class("module-base", d):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500735 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500736
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500737 if not "-dbg" in pkg and not "packagegroup-" in pkg and not "-image" in pkg:
738 localdata = bb.data.createCopy(d)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500739 localdata.setVar('OVERRIDES', localdata.getVar('OVERRIDES') + ':' + pkg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500740
741 # Now check the RDEPENDS
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500742 rdepends = bb.utils.explode_deps(localdata.getVar('RDEPENDS') or "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500743
744 # Now do the sanity check!!!
745 if "build-deps" not in skip:
746 for rdepend in rdepends:
747 if "-dbg" in rdepend and "debug-deps" not in skip:
748 error_msg = "%s rdepends on %s" % (pkg,rdepend)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500749 package_qa_handle_error("debug-deps", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500750 if (not "-dev" in pkg and not "-staticdev" in pkg) and rdepend.endswith("-dev") and "dev-deps" not in skip:
751 error_msg = "%s rdepends on %s" % (pkg, rdepend)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500752 package_qa_handle_error("dev-deps", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500753 if rdepend not in packages:
754 rdep_data = oe.packagedata.read_subpkgdata(rdepend, d)
755 if rdep_data and 'PN' in rdep_data and rdep_data['PN'] in taskdeps:
756 continue
757 if not rdep_data or not 'PN' in rdep_data:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500758 pkgdata_dir = d.getVar("PKGDATA_DIR")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500759 try:
760 possibles = os.listdir("%s/runtime-rprovides/%s/" % (pkgdata_dir, rdepend))
761 except OSError:
762 possibles = []
763 for p in possibles:
764 rdep_data = oe.packagedata.read_subpkgdata(p, d)
765 if rdep_data and 'PN' in rdep_data and rdep_data['PN'] in taskdeps:
766 break
767 if rdep_data and 'PN' in rdep_data and rdep_data['PN'] in taskdeps:
768 continue
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500769 if rdep_data and 'PN' in rdep_data:
770 error_msg = "%s rdepends on %s, but it isn't a build dependency, missing %s in DEPENDS or PACKAGECONFIG?" % (pkg, rdepend, rdep_data['PN'])
771 else:
772 error_msg = "%s rdepends on %s, but it isn't a build dependency?" % (pkg, rdepend)
773 package_qa_handle_error("build-deps", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500774
775 if "file-rdeps" not in skip:
776 ignored_file_rdeps = set(['/bin/sh', '/usr/bin/env', 'rtld(GNU_HASH)'])
777 if bb.data.inherits_class('nativesdk', d):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500778 ignored_file_rdeps |= set(['/bin/bash', '/usr/bin/perl', 'perl'])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500779 # For Saving the FILERDEPENDS
780 filerdepends = {}
781 rdep_data = oe.packagedata.read_subpkgdata(pkg, d)
782 for key in rdep_data:
783 if key.startswith("FILERDEPENDS_"):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500784 for subkey in bb.utils.explode_deps(rdep_data[key]):
785 if subkey not in ignored_file_rdeps and \
786 not subkey.startswith('perl('):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500787 # We already know it starts with FILERDEPENDS_
788 filerdepends[subkey] = key[13:]
789
790 if filerdepends:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500791 done = rdepends[:]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500792 # Add the rprovides of itself
793 if pkg not in done:
794 done.insert(0, pkg)
795
796 # The python is not a package, but python-core provides it, so
797 # skip checking /usr/bin/python if python is in the rdeps, in
798 # case there is a RDEPENDS_pkg = "python" in the recipe.
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500799 for py in [ d.getVar('MLPREFIX') + "python", "python" ]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500800 if py in done:
801 filerdepends.pop("/usr/bin/python",None)
802 done.remove(py)
803 for rdep in done:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500804 # The file dependencies may contain package names, e.g.,
805 # perl
806 filerdepends.pop(rdep,None)
807
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500808 # For Saving the FILERPROVIDES, RPROVIDES and FILES_INFO
809 rdep_data = oe.packagedata.read_subpkgdata(rdep, d)
810 for key in rdep_data:
811 if key.startswith("FILERPROVIDES_") or key.startswith("RPROVIDES_"):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500812 for subkey in bb.utils.explode_deps(rdep_data[key]):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500813 filerdepends.pop(subkey,None)
814 # Add the files list to the rprovides
815 if key == "FILES_INFO":
816 # Use eval() to make it as a dict
817 for subkey in eval(rdep_data[key]):
818 filerdepends.pop(subkey,None)
819 if not filerdepends:
820 # Break if all the file rdepends are met
821 break
822 if filerdepends:
823 for key in filerdepends:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500824 error_msg = "%s contained in package %s requires %s, but no providers found in RDEPENDS_%s?" % \
825 (filerdepends[key].replace("_%s" % pkg, "").replace("@underscore@", "_"), pkg, key, pkg)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500826 package_qa_handle_error("file-rdeps", error_msg, d)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500827package_qa_check_rdepends[vardepsexclude] = "OVERRIDES"
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500828
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500829def package_qa_check_deps(pkg, pkgdest, d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500830
831 localdata = bb.data.createCopy(d)
832 localdata.setVar('OVERRIDES', pkg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500833
834 def check_valid_deps(var):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500835 try:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500836 rvar = bb.utils.explode_dep_versions2(localdata.getVar(var) or "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500837 except ValueError as e:
838 bb.fatal("%s_%s: %s" % (var, pkg, e))
839 for dep in rvar:
840 for v in rvar[dep]:
841 if v and not v.startswith(('< ', '= ', '> ', '<= ', '>=')):
842 error_msg = "%s_%s is invalid: %s (%s) only comparisons <, =, >, <=, and >= are allowed" % (var, pkg, dep, v)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500843 package_qa_handle_error("dep-cmp", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500844
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500845 check_valid_deps('RDEPENDS')
846 check_valid_deps('RRECOMMENDS')
847 check_valid_deps('RSUGGESTS')
848 check_valid_deps('RPROVIDES')
849 check_valid_deps('RREPLACES')
850 check_valid_deps('RCONFLICTS')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500851
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500852QAPKGTEST[usrmerge] = "package_qa_check_usrmerge"
853def package_qa_check_usrmerge(pkg, d, messages):
854 pkgdest = d.getVar('PKGDEST')
855 pkg_dir = pkgdest + os.sep + pkg + os.sep
856 merged_dirs = ['bin', 'sbin', 'lib'] + d.getVar('MULTILIB_VARIANTS').split()
857 for f in merged_dirs:
858 if os.path.exists(pkg_dir + f) and not os.path.islink(pkg_dir + f):
859 msg = "%s package is not obeying usrmerge distro feature. /%s should be relocated to /usr." % (pkg, f)
860 package_qa_add_message(messages, "usrmerge", msg)
861 return False
862 return True
863
Brad Bishopf3f93bb2019-10-16 14:33:32 -0400864QAPKGTEST[perllocalpod] = "package_qa_check_perllocalpod"
865def package_qa_check_perllocalpod(pkg, d, messages):
866 """
867 Check that the recipe didn't ship a perlocal.pod file, which shouldn't be
868 installed in a distribution package. cpan.bbclass sets NO_PERLLOCAL=1 to
869 handle this for most recipes.
870 """
871 import glob
872 pkgd = oe.path.join(d.getVar('PKGDEST'), pkg)
873 podpath = oe.path.join(pkgd, d.getVar("libdir"), "perl*", "*", "*", "perllocal.pod")
874
875 matches = glob.glob(podpath)
876 if matches:
877 matches = [package_qa_clean_path(path, d, pkg) for path in matches]
878 msg = "%s contains perllocal.pod (%s), should not be installed" % (pkg, " ".join(matches))
879 package_qa_add_message(messages, "perllocalpod", msg)
880
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500881QAPKGTEST[expanded-d] = "package_qa_check_expanded_d"
882def package_qa_check_expanded_d(package, d, messages):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500883 """
884 Check for the expanded D (${D}) value in pkg_* and FILES
885 variables, warn the user to use it correctly.
886 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500887 sane = True
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500888 expanded_d = d.getVar('D')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500889
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500890 for var in 'FILES','pkg_preinst', 'pkg_postinst', 'pkg_prerm', 'pkg_postrm':
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500891 bbvar = d.getVar(var + "_" + package) or ""
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500892 if expanded_d in bbvar:
893 if var == 'FILES':
894 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)
895 sane = False
896 else:
897 package_qa_add_message(messages, "expanded-d", "%s in %s recipe contains ${D}, it should be replaced by $D instead" % (var, package))
898 sane = False
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500899 return sane
900
Andrew Geissler1e34c2d2020-05-29 16:02:59 -0500901QAPKGTEST[unlisted-pkg-lics] = "package_qa_check_unlisted_pkg_lics"
902def package_qa_check_unlisted_pkg_lics(package, d, messages):
903 """
904 Check that all licenses for a package are among the licenses for the recipe.
905 """
906 pkg_lics = d.getVar('LICENSE_' + package)
907 if not pkg_lics:
908 return True
909
910 recipe_lics_set = oe.license.list_licenses(d.getVar('LICENSE'))
911 unlisted = oe.license.list_licenses(pkg_lics) - recipe_lics_set
912 if not unlisted:
913 return True
914
915 package_qa_add_message(messages, "unlisted-pkg-lics",
916 "LICENSE_%s includes licenses (%s) that are not "
917 "listed in LICENSE" % (package, ' '.join(unlisted)))
918 return False
919
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500920def package_qa_check_encoding(keys, encode, d):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600921 def check_encoding(key, enc):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500922 sane = True
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500923 value = d.getVar(key)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500924 if value:
925 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600926 s = value.encode(enc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500927 except UnicodeDecodeError as e:
928 error_msg = "%s has non %s characters" % (key,enc)
929 sane = False
930 package_qa_handle_error("invalid-chars", error_msg, d)
931 return sane
932
933 for key in keys:
934 sane = check_encoding(key, encode)
935 if not sane:
936 break
937
938HOST_USER_UID := "${@os.getuid()}"
939HOST_USER_GID := "${@os.getgid()}"
940
941QAPATHTEST[host-user-contaminated] = "package_qa_check_host_user"
942def package_qa_check_host_user(path, name, d, elf, messages):
943 """Check for paths outside of /home which are owned by the user running bitbake."""
944
945 if not os.path.lexists(path):
946 return
947
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500948 dest = d.getVar('PKGDEST')
949 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500950 home = os.path.join(dest, 'home')
951 if path == home or path.startswith(home + os.sep):
952 return
953
954 try:
955 stat = os.lstat(path)
956 except OSError as exc:
957 import errno
958 if exc.errno != errno.ENOENT:
959 raise
960 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500961 check_uid = int(d.getVar('HOST_USER_UID'))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500962 if stat.st_uid == check_uid:
Brad Bishop96ff1982019-08-19 13:50:42 -0400963 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 -0500964 return False
965
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500966 check_gid = int(d.getVar('HOST_USER_GID'))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500967 if stat.st_gid == check_gid:
Brad Bishop96ff1982019-08-19 13:50:42 -0400968 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 -0500969 return False
970 return True
971
Brad Bishop15ae2502019-06-18 21:44:24 -0400972QARECIPETEST[src-uri-bad] = "package_qa_check_src_uri"
973def package_qa_check_src_uri(pn, d, messages):
974 import re
975
976 if "${PN}" in d.getVar("SRC_URI", False):
977 package_qa_handle_error("src-uri-bad", "%s: SRC_URI uses PN not BPN" % pn, d)
978
Andrew Geissler82c905d2020-04-13 13:39:40 -0500979 for url in d.getVar("SRC_URI").split():
980 if re.search(r"github\.com/.+/.+/archive/.+", url):
981 package_qa_handle_error("src-uri-bad", "%s: SRC_URI uses unstable GitHub archives" % pn, d)
Brad Bishop15ae2502019-06-18 21:44:24 -0400982
Andrew Geissler5a43b432020-06-13 10:46:56 -0500983QARECIPETEST[unhandled-features-check] = "package_qa_check_unhandled_features_check"
984def package_qa_check_unhandled_features_check(pn, d, messages):
985 if not bb.data.inherits_class('features_check', d):
986 var_set = False
987 for kind in ['DISTRO', 'MACHINE', 'COMBINED']:
988 for var in ['ANY_OF_' + kind + '_FEATURES', 'REQUIRED_' + kind + '_FEATURES', 'CONFLICT_' + kind + '_FEATURES']:
989 if d.getVar(var) is not None or d.overridedata.get(var) is not None:
990 var_set = True
991 if var_set:
992 package_qa_handle_error("unhandled-features-check", "%s: recipe doesn't inherit features_check" % pn, d)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500993
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500994# The PACKAGE FUNC to scan each package
995python do_package_qa () {
996 import subprocess
997 import oe.packagedata
998
999 bb.note("DO PACKAGE QA")
1000
1001 bb.build.exec_func("read_subpackage_metadata", d)
1002
1003 # Check non UTF-8 characters on recipe's metadata
1004 package_qa_check_encoding(['DESCRIPTION', 'SUMMARY', 'LICENSE', 'SECTION'], 'utf-8', d)
1005
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001006 logdir = d.getVar('T')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001007 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001008
1009 # Check the compile log for host contamination
1010 compilelog = os.path.join(logdir,"log.do_compile")
1011
1012 if os.path.exists(compilelog):
1013 statement = "grep -e 'CROSS COMPILE Badness:' -e 'is unsafe for cross-compilation' %s > /dev/null" % compilelog
1014 if subprocess.call(statement, shell=True) == 0:
1015 msg = "%s: The compile log indicates that host include and/or library paths were used.\n \
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001016 Please check the log '%s' for more information." % (pn, compilelog)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001017 package_qa_handle_error("compile-host-path", msg, d)
1018
1019 # Check the install log for host contamination
1020 installlog = os.path.join(logdir,"log.do_install")
1021
1022 if os.path.exists(installlog):
1023 statement = "grep -e 'CROSS COMPILE Badness:' -e 'is unsafe for cross-compilation' %s > /dev/null" % installlog
1024 if subprocess.call(statement, shell=True) == 0:
1025 msg = "%s: The install log indicates that host include and/or library paths were used.\n \
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001026 Please check the log '%s' for more information." % (pn, installlog)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001027 package_qa_handle_error("install-host-path", msg, d)
1028
1029 # Scan the packages...
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001030 pkgdest = d.getVar('PKGDEST')
1031 packages = set((d.getVar('PACKAGES') or '').split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001032
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001033 global pkgfiles
1034 pkgfiles = {}
1035 for pkg in packages:
1036 pkgfiles[pkg] = []
Andrew Geissler82c905d2020-04-13 13:39:40 -05001037 for walkroot, dirs, files in os.walk(os.path.join(pkgdest, pkg)):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001038 for file in files:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001039 pkgfiles[pkg].append(os.path.join(walkroot, file))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001040
1041 # no packages should be scanned
1042 if not packages:
1043 return
1044
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001045 import re
1046 # The package name matches the [a-z0-9.+-]+ regular expression
Brad Bishop977dc1a2019-02-06 16:01:43 -05001047 pkgname_pattern = re.compile(r"^[a-z0-9.+-]+$")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001048
1049 taskdepdata = d.getVar("BB_TASKDEPDATA", False)
1050 taskdeps = set()
1051 for dep in taskdepdata:
1052 taskdeps.add(taskdepdata[dep][0])
1053
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001054 def parse_test_matrix(matrix_name):
1055 testmatrix = d.getVarFlags(matrix_name) or {}
1056 g = globals()
1057 warnchecks = []
1058 for w in (d.getVar("WARN_QA") or "").split():
1059 if w in skip:
1060 continue
1061 if w in testmatrix and testmatrix[w] in g:
1062 warnchecks.append(g[testmatrix[w]])
1063
1064 errorchecks = []
1065 for e in (d.getVar("ERROR_QA") or "").split():
1066 if e in skip:
1067 continue
1068 if e in testmatrix and testmatrix[e] in g:
1069 errorchecks.append(g[testmatrix[e]])
1070 return warnchecks, errorchecks
1071
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001072 for package in packages:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001073 skip = set((d.getVar('INSANE_SKIP') or "").split() +
1074 (d.getVar('INSANE_SKIP_' + package) or "").split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001075 if skip:
1076 bb.note("Package %s skipping QA tests: %s" % (package, str(skip)))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001077
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001078 bb.note("Checking Package: %s" % package)
1079 # Check package name
1080 if not pkgname_pattern.match(package):
1081 package_qa_handle_error("pkgname",
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001082 "%s doesn't match the [a-z0-9.+-]+ regex" % package, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001083
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001084 warn_checks, error_checks = parse_test_matrix("QAPATHTEST")
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001085 package_qa_walk(warn_checks, error_checks, package, d)
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001086
1087 warn_checks, error_checks = parse_test_matrix("QAPKGTEST")
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001088 package_qa_package(warn_checks, error_checks, package, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001089
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001090 package_qa_check_rdepends(package, pkgdest, skip, taskdeps, packages, d)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001091 package_qa_check_deps(package, pkgdest, d)
1092
1093 warn_checks, error_checks = parse_test_matrix("QARECIPETEST")
1094 package_qa_recipe(warn_checks, error_checks, pn, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001095
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001096 if 'libdir' in d.getVar("ALL_QA").split():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001097 package_qa_check_libdir(d)
1098
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001099 qa_sane = d.getVar("QA_SANE")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001100 if not qa_sane:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001101 bb.fatal("QA run found fatal errors. Please consider fixing them.")
1102 bb.note("DONE with PACKAGE QA")
1103}
1104
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001105# binutils is used for most checks, so need to set as dependency
1106# POPULATESYSROOTDEPS is defined in staging class.
1107do_package_qa[depends] += "${POPULATESYSROOTDEPS}"
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001108do_package_qa[vardepsexclude] = "BB_TASKDEPDATA"
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001109do_package_qa[rdeptask] = "do_packagedata"
1110addtask do_package_qa after do_packagedata do_package before do_build
1111
Brad Bishop19323692019-04-05 15:28:33 -04001112# Add the package specific INSANE_SKIPs to the sstate dependencies
1113python() {
1114 pkgs = (d.getVar('PACKAGES') or '').split()
1115 for pkg in pkgs:
1116 d.appendVarFlag("do_package_qa", "vardeps", " INSANE_SKIP_{}".format(pkg))
1117}
1118
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001119SSTATETASKS += "do_package_qa"
1120do_package_qa[sstate-inputdirs] = ""
1121do_package_qa[sstate-outputdirs] = ""
1122python do_package_qa_setscene () {
1123 sstate_setscene(d)
1124}
1125addtask do_package_qa_setscene
1126
1127python do_qa_staging() {
1128 bb.note("QA checking staging")
Brad Bishop19323692019-04-05 15:28:33 -04001129 if not qa_check_staged(d.expand('${SYSROOT_DESTDIR}${libdir}'), d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001130 bb.fatal("QA staging was broken by the package built above")
1131}
1132
Brad Bishopd89cb5f2019-04-10 09:02:41 -04001133python do_qa_patch() {
1134 import subprocess
1135
1136 ###########################################################################
1137 # Check patch.log for fuzz warnings
1138 #
1139 # Further information on why we check for patch fuzz warnings:
1140 # http://lists.openembedded.org/pipermail/openembedded-core/2018-March/148675.html
1141 # https://bugzilla.yoctoproject.org/show_bug.cgi?id=10450
1142 ###########################################################################
1143
1144 logdir = d.getVar('T')
1145 patchlog = os.path.join(logdir,"log.do_patch")
1146
1147 if os.path.exists(patchlog):
1148 fuzzheader = '--- Patch fuzz start ---'
1149 fuzzfooter = '--- Patch fuzz end ---'
1150 statement = "grep -e '%s' %s > /dev/null" % (fuzzheader, patchlog)
1151 if subprocess.call(statement, shell=True) == 0:
1152 msg = "Fuzz detected:\n\n"
1153 fuzzmsg = ""
1154 inFuzzInfo = False
1155 f = open(patchlog, "r")
1156 for line in f:
1157 if fuzzheader in line:
1158 inFuzzInfo = True
1159 fuzzmsg = ""
1160 elif fuzzfooter in line:
1161 fuzzmsg = fuzzmsg.replace('\n\n', '\n')
1162 msg += fuzzmsg
1163 msg += "\n"
1164 inFuzzInfo = False
1165 elif inFuzzInfo and not 'Now at patch' in line:
1166 fuzzmsg += line
1167 f.close()
1168 msg += "The context lines in the patches can be updated with devtool:\n"
1169 msg += "\n"
1170 msg += " devtool modify %s\n" % d.getVar('PN')
1171 msg += " devtool finish --force-patch-refresh %s <layer_path>\n\n" % d.getVar('PN')
1172 msg += "Don't forget to review changes done by devtool!\n"
1173 if 'patch-fuzz' in d.getVar('ERROR_QA'):
1174 bb.error(msg)
1175 elif 'patch-fuzz' in d.getVar('WARN_QA'):
1176 bb.warn(msg)
1177 msg = "Patch log indicates that patches do not apply cleanly."
1178 package_qa_handle_error("patch-fuzz", msg, d)
1179}
1180
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001181python do_qa_configure() {
1182 import subprocess
1183
1184 ###########################################################################
1185 # Check config.log for cross compile issues
1186 ###########################################################################
1187
1188 configs = []
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001189 workdir = d.getVar('WORKDIR')
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001190
Brad Bishop19323692019-04-05 15:28:33 -04001191 skip = (d.getVar('INSANE_SKIP') or "").split()
1192 skip_configure_unsafe = False
1193 if 'configure-unsafe' in skip:
1194 bb.note("Recipe %s skipping qa checking: configure-unsafe" % d.getVar('PN'))
1195 skip_configure_unsafe = True
1196
1197 if bb.data.inherits_class('autotools', d) and not skip_configure_unsafe:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001198 bb.note("Checking autotools environment for common misconfiguration")
1199 for root, dirs, files in os.walk(workdir):
1200 statement = "grep -q -F -e 'CROSS COMPILE Badness:' -e 'is unsafe for cross-compilation' %s" % \
1201 os.path.join(root,"config.log")
1202 if "config.log" in files:
1203 if subprocess.call(statement, shell=True) == 0:
Brad Bishop19323692019-04-05 15:28:33 -04001204 error_msg = """This autoconf log indicates errors, it looked at host include and/or library paths while determining system capabilities.
1205Rerun configure task after fixing this."""
1206 package_qa_handle_error("configure-unsafe", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001207
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001208 if "configure.ac" in files:
1209 configs.append(os.path.join(root,"configure.ac"))
1210 if "configure.in" in files:
1211 configs.append(os.path.join(root, "configure.in"))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001212
1213 ###########################################################################
1214 # Check gettext configuration and dependencies are correct
1215 ###########################################################################
1216
Brad Bishop19323692019-04-05 15:28:33 -04001217 skip_configure_gettext = False
1218 if 'configure-gettext' in skip:
1219 bb.note("Recipe %s skipping qa checking: configure-gettext" % d.getVar('PN'))
1220 skip_configure_gettext = True
1221
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001222 cnf = d.getVar('EXTRA_OECONF') or ""
Brad Bishop19323692019-04-05 15:28:33 -04001223 if not ("gettext" in d.getVar('P') or "gcc-runtime" in d.getVar('P') or \
1224 "--disable-nls" in cnf or skip_configure_gettext):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001225 ml = d.getVar("MLPREFIX") or ""
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001226 if bb.data.inherits_class('cross-canadian', d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001227 gt = "nativesdk-gettext"
1228 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001229 gt = "gettext-native"
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001230 deps = bb.utils.explode_deps(d.getVar('DEPENDS') or "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001231 if gt not in deps:
1232 for config in configs:
1233 gnu = "grep \"^[[:space:]]*AM_GNU_GETTEXT\" %s >/dev/null" % config
1234 if subprocess.call(gnu, shell=True) == 0:
Brad Bishopd89cb5f2019-04-10 09:02:41 -04001235 error_msg = "AM_GNU_GETTEXT used but no inherit gettext"
Brad Bishop19323692019-04-05 15:28:33 -04001236 package_qa_handle_error("configure-gettext", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001237
1238 ###########################################################################
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001239 # Check unrecognised configure options (with a white list)
1240 ###########################################################################
Brad Bishopc342db32019-05-15 21:57:59 -04001241 if bb.data.inherits_class("autotools", d) or bb.data.inherits_class("meson", d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001242 bb.note("Checking configure output for unrecognised options")
1243 try:
Brad Bishopc342db32019-05-15 21:57:59 -04001244 if bb.data.inherits_class("autotools", d):
1245 flag = "WARNING: unrecognized options:"
1246 log = os.path.join(d.getVar('B'), 'config.log')
1247 if bb.data.inherits_class("meson", d):
1248 flag = "WARNING: Unknown options:"
1249 log = os.path.join(d.getVar('T'), 'log.do_configure')
1250 output = subprocess.check_output(['grep', '-F', flag, log]).decode("utf-8").replace(', ', ' ').replace('"', '')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001251 options = set()
1252 for line in output.splitlines():
1253 options |= set(line.partition(flag)[2].split())
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001254 whitelist = set(d.getVar("UNKNOWN_CONFIGURE_WHITELIST").split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001255 options -= whitelist
1256 if options:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001257 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001258 error_msg = pn + ": configure was passed unrecognised options: " + " ".join(options)
1259 package_qa_handle_error("unknown-configure-option", error_msg, d)
1260 except subprocess.CalledProcessError:
1261 pass
1262
1263 # Check invalid PACKAGECONFIG
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001264 pkgconfig = (d.getVar("PACKAGECONFIG") or "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001265 if pkgconfig:
1266 pkgconfigflags = d.getVarFlags("PACKAGECONFIG") or {}
1267 for pconfig in pkgconfig:
1268 if pconfig not in pkgconfigflags:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001269 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001270 error_msg = "%s: invalid PACKAGECONFIG: %s" % (pn, pconfig)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001271 package_qa_handle_error("invalid-packageconfig", error_msg, d)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001272
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001273 qa_sane = d.getVar("QA_SANE")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001274 if not qa_sane:
1275 bb.fatal("Fatal QA errors found, failing task.")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001276}
1277
1278python do_qa_unpack() {
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001279 src_uri = d.getVar('SRC_URI')
1280 s_dir = d.getVar('S')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001281 if src_uri and not os.path.exists(s_dir):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001282 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 -05001283}
1284
1285# The Staging Func, to check all staging
1286#addtask qa_staging after do_populate_sysroot before do_build
1287do_populate_sysroot[postfuncs] += "do_qa_staging "
1288
Brad Bishopd89cb5f2019-04-10 09:02:41 -04001289# Check for patch fuzz
1290do_patch[postfuncs] += "do_qa_patch "
1291
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001292# Check broken config.log files, for packages requiring Gettext which
1293# don't have it in DEPENDS.
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001294#addtask qa_configure after do_configure before do_compile
1295do_configure[postfuncs] += "do_qa_configure "
1296
1297# Check does S exist.
1298do_unpack[postfuncs] += "do_qa_unpack"
1299
1300python () {
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001301 import re
1302
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001303 tests = d.getVar('ALL_QA').split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001304 if "desktop" in tests:
1305 d.appendVar("PACKAGE_DEPENDS", " desktop-file-utils-native")
1306
1307 ###########################################################################
1308 # Check various variables
1309 ###########################################################################
1310
1311 # Checking ${FILESEXTRAPATHS}
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001312 extrapaths = (d.getVar("FILESEXTRAPATHS") or "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001313 if '__default' not in extrapaths.split(":"):
1314 msg = "FILESEXTRAPATHS-variable, must always use _prepend (or _append)\n"
1315 msg += "type of assignment, and don't forget the colon.\n"
1316 msg += "Please assign it with the format of:\n"
1317 msg += " FILESEXTRAPATHS_append := \":${THISDIR}/Your_Files_Path\" or\n"
1318 msg += " FILESEXTRAPATHS_prepend := \"${THISDIR}/Your_Files_Path:\"\n"
1319 msg += "in your bbappend file\n\n"
1320 msg += "Your incorrect assignment is:\n"
1321 msg += "%s\n" % extrapaths
1322 bb.warn(msg)
1323
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001324 overrides = d.getVar('OVERRIDES').split(':')
1325 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001326 if pn in overrides:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001327 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 -05001328 package_qa_handle_error("pn-overrides", msg, d)
Brad Bishop977dc1a2019-02-06 16:01:43 -05001329 prog = re.compile(r'[A-Z]')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001330 if prog.search(pn):
1331 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 -05001332
Brad Bishop08902b02019-08-20 09:16:51 -04001333 # Some people mistakenly use DEPENDS_${PN} instead of DEPENDS and wonder
1334 # why it doesn't work.
1335 if (d.getVar(d.expand('DEPENDS_${PN}'))):
1336 package_qa_handle_error("pkgvarcheck", "recipe uses DEPENDS_${PN}, should use DEPENDS", d)
1337
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001338 issues = []
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001339 if (d.getVar('PACKAGES') or "").split():
1340 for dep in (d.getVar('QADEPENDS') or "").split():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001341 d.appendVarFlag('do_package_qa', 'depends', " %s:do_populate_sysroot" % dep)
1342 for var in 'RDEPENDS', 'RRECOMMENDS', 'RSUGGESTS', 'RCONFLICTS', 'RPROVIDES', 'RREPLACES', 'FILES', 'pkg_preinst', 'pkg_postinst', 'pkg_prerm', 'pkg_postrm', 'ALLOW_EMPTY':
1343 if d.getVar(var, False):
1344 issues.append(var)
1345
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001346 fakeroot_tests = d.getVar('FAKEROOT_QA').split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001347 if set(tests) & set(fakeroot_tests):
1348 d.setVarFlag('do_package_qa', 'fakeroot', '1')
1349 d.appendVarFlag('do_package_qa', 'depends', ' virtual/fakeroot-native:do_populate_sysroot')
1350 else:
1351 d.setVarFlag('do_package_qa', 'rdeptask', '')
1352 for i in issues:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001353 package_qa_handle_error("pkgvarcheck", "%s: Variable %s is set as not being package specific, please fix this." % (d.getVar("FILE"), i), d)
1354 qa_sane = d.getVar("QA_SANE")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001355 if not qa_sane:
1356 bb.fatal("Fatal QA errors found, failing task.")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001357}