blob: 7fc8f33a9824e8e494077300954e21f16b65b69a [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001# BB Class inspired by ebuild.sh
2#
3# This class will test files after installation for certain
4# security issues and other kind of issues.
5#
6# Checks we do:
7# -Check the ownership and permissions
8# -Check the RUNTIME path for the $TMPDIR
9# -Check if .la files wrongly point to workdir
10# -Check if .pc files wrongly point to workdir
11# -Check if packages contains .debug directories or .so files
12# where they should be in -dev or -dbg
13# -Check if config.log contains traces to broken autoconf tests
14# -Check invalid characters (non-utf8) on some package metadata
15# -Ensure that binaries in base_[bindir|sbindir|libdir] do not link
16# into exec_prefix
17# -Check that scripts in base_[bindir|sbindir|libdir] do not reference
18# files under exec_prefix
Brad Bishopd7bf8c12018-02-25 22:55:05 -050019# -Check if the package name is upper case
Patrick Williamsc124f4f2015-09-15 14:41:29 -050020
Patrick Williamsc124f4f2015-09-15 14:41:29 -050021QA_SANE = "True"
22
23# Elect whether a given type of error is a warning or error, they may
24# have been set by other files.
25WARN_QA ?= "ldflags useless-rpaths rpaths staticdev libdir xorg-driver-abi \
26 textrel already-stripped incompatible-license files-invalid \
27 installed-vs-shipped compile-host-path install-host-path \
Brad Bishop15ae2502019-06-18 21:44:24 -040028 pn-overrides infodir build-deps src-uri-bad \
Patrick Williamsc124f4f2015-09-15 14:41:29 -050029 unknown-configure-option symlink-to-sysroot multilib \
Brad Bishopd89cb5f2019-04-10 09:02:41 -040030 invalid-packageconfig host-user-contaminated uppercase-pn patch-fuzz \
Andrew Geissler82c905d2020-04-13 13:39:40 -050031 mime mime-xdg \
Patrick Williamsc124f4f2015-09-15 14:41:29 -050032 "
33ERROR_QA ?= "dev-so debug-deps dev-deps debug-files arch pkgconfig la \
34 perms dep-cmp pkgvarcheck perm-config perm-line perm-link \
35 split-strip packages-list pkgv-undefined var-undefined \
36 version-going-backwards expanded-d invalid-chars \
Brad Bishop19323692019-04-05 15:28:33 -040037 license-checksum dev-elf file-rdeps configure-unsafe \
Andrew Geissler82c905d2020-04-13 13:39:40 -050038 configure-gettext perllocalpod shebang-size \
Patrick Williamsc124f4f2015-09-15 14:41:29 -050039 "
Brad Bishopd7bf8c12018-02-25 22:55:05 -050040# Add usrmerge QA check based on distro feature
41ERROR_QA_append = "${@bb.utils.contains('DISTRO_FEATURES', 'usrmerge', ' usrmerge', '', d)}"
42
Patrick Williamsc124f4f2015-09-15 14:41:29 -050043FAKEROOT_QA = "host-user-contaminated"
44FAKEROOT_QA[doc] = "QA tests which need to run under fakeroot. If any \
45enabled tests are listed here, the do_package_qa task will run under fakeroot."
46
47ALL_QA = "${WARN_QA} ${ERROR_QA}"
48
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050049UNKNOWN_CONFIGURE_WHITELIST ?= "--enable-nls --disable-nls --disable-silent-rules --disable-dependency-tracking --with-libtool-sysroot --disable-static"
Patrick Williamsc124f4f2015-09-15 14:41:29 -050050
Patrick Williamsc0f7c042017-02-23 20:41:17 -060051def package_qa_clean_path(path, d, pkg=None):
52 """
53 Remove redundant paths from the path for display. If pkg isn't set then
54 TMPDIR is stripped, otherwise PKGDEST/pkg is stripped.
55 """
56 if pkg:
Brad Bishop6e60e8b2018-02-01 10:27:11 -050057 path = path.replace(os.path.join(d.getVar("PKGDEST"), pkg), "/")
58 return path.replace(d.getVar("TMPDIR"), "/").replace("//", "/")
Patrick Williamsc124f4f2015-09-15 14:41:29 -050059
60def package_qa_write_error(type, error, d):
Brad Bishop6e60e8b2018-02-01 10:27:11 -050061 logfile = d.getVar('QA_LOGFILE')
Patrick Williamsc124f4f2015-09-15 14:41:29 -050062 if logfile:
Brad Bishop6e60e8b2018-02-01 10:27:11 -050063 p = d.getVar('P')
Patrick Williamsc0f7c042017-02-23 20:41:17 -060064 with open(logfile, "a+") as f:
65 f.write("%s: %s [%s]\n" % (p, error, type))
Patrick Williamsc124f4f2015-09-15 14:41:29 -050066
67def package_qa_handle_error(error_class, error_msg, d):
Brad Bishop6e60e8b2018-02-01 10:27:11 -050068 if error_class in (d.getVar("ERROR_QA") or "").split():
Brad Bishopd7bf8c12018-02-25 22:55:05 -050069 package_qa_write_error(error_class, error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050070 bb.error("QA Issue: %s [%s]" % (error_msg, error_class))
71 d.setVar("QA_SANE", False)
72 return False
Brad Bishop6e60e8b2018-02-01 10:27:11 -050073 elif error_class in (d.getVar("WARN_QA") or "").split():
Brad Bishopd7bf8c12018-02-25 22:55:05 -050074 package_qa_write_error(error_class, error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050075 bb.warn("QA Issue: %s [%s]" % (error_msg, error_class))
76 else:
77 bb.note("QA Issue: %s [%s]" % (error_msg, error_class))
78 return True
79
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050080def package_qa_add_message(messages, section, new_msg):
81 if section not in messages:
82 messages[section] = new_msg
83 else:
84 messages[section] = messages[section] + "\n" + new_msg
85
Andrew Geissler82c905d2020-04-13 13:39:40 -050086QAPATHTEST[shebang-size] = "package_qa_check_shebang_size"
87def package_qa_check_shebang_size(path, name, d, elf, messages):
88 if os.path.islink(path) or elf:
89 return
90
91 try:
92 with open(path, 'rb') as f:
93 stanza = f.readline(130)
94 except IOError:
95 return
96
97 if stanza.startswith(b'#!'):
98 #Shebang not found
99 try:
100 stanza = stanza.decode("utf-8")
101 except UnicodeDecodeError:
102 #If it is not a text file, it is not a script
103 return
104
105 if len(stanza) > 129:
106 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)))
107 return
108
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500109QAPATHTEST[libexec] = "package_qa_check_libexec"
110def package_qa_check_libexec(path,name, d, elf, messages):
111
112 # Skip the case where the default is explicitly /usr/libexec
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500113 libexec = d.getVar('libexecdir')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500114 if libexec == "/usr/libexec":
115 return True
116
117 if 'libexec' in path.split(os.path.sep):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500118 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 -0500119 return False
120
121 return True
122
123QAPATHTEST[rpaths] = "package_qa_check_rpath"
124def package_qa_check_rpath(file,name, d, elf, messages):
125 """
126 Check for dangerous RPATHs
127 """
128 if not elf:
129 return
130
131 if os.path.islink(file):
132 return
133
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500134 bad_dirs = [d.getVar('BASE_WORKDIR'), d.getVar('STAGING_DIR_TARGET')]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500135
136 phdrs = elf.run_objdump("-p", d)
137
138 import re
Brad Bishop977dc1a2019-02-06 16:01:43 -0500139 rpath_re = re.compile(r"\s+RPATH\s+(.*)")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500140 for line in phdrs.split("\n"):
141 m = rpath_re.match(line)
142 if m:
143 rpath = m.group(1)
144 for dir in bad_dirs:
145 if dir in rpath:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500146 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 -0500147
148QAPATHTEST[useless-rpaths] = "package_qa_check_useless_rpaths"
149def package_qa_check_useless_rpaths(file, name, d, elf, messages):
150 """
151 Check for RPATHs that are useless but not dangerous
152 """
153 def rpath_eq(a, b):
154 return os.path.normpath(a) == os.path.normpath(b)
155
156 if not elf:
157 return
158
159 if os.path.islink(file):
160 return
161
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500162 libdir = d.getVar("libdir")
163 base_libdir = d.getVar("base_libdir")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500164
165 phdrs = elf.run_objdump("-p", d)
166
167 import re
Brad Bishop977dc1a2019-02-06 16:01:43 -0500168 rpath_re = re.compile(r"\s+RPATH\s+(.*)")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500169 for line in phdrs.split("\n"):
170 m = rpath_re.match(line)
171 if m:
172 rpath = m.group(1)
173 if rpath_eq(rpath, libdir) or rpath_eq(rpath, base_libdir):
174 # The dynamic linker searches both these places anyway. There is no point in
175 # looking there again.
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500176 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 -0500177
178QAPATHTEST[dev-so] = "package_qa_check_dev"
179def package_qa_check_dev(path, name, d, elf, messages):
180 """
181 Check for ".so" library symlinks in non-dev packages
182 """
183
184 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 -0500185 package_qa_add_message(messages, "dev-so", "non -dev/-dbg/nativesdk- package contains symlink .so: %s path '%s'" % \
186 (name, package_qa_clean_path(path,d)))
187
188QAPATHTEST[dev-elf] = "package_qa_check_dev_elf"
189def package_qa_check_dev_elf(path, name, d, elf, messages):
190 """
191 Check that -dev doesn't contain real shared libraries. The test has to
192 check that the file is not a link and is an ELF object as some recipes
193 install link-time .so files that are linker scripts.
194 """
195 if name.endswith("-dev") and path.endswith(".so") and not os.path.islink(path) and elf:
196 package_qa_add_message(messages, "dev-elf", "-dev package contains non-symlink .so: %s path '%s'" % \
197 (name, package_qa_clean_path(path,d)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500198
199QAPATHTEST[staticdev] = "package_qa_check_staticdev"
200def package_qa_check_staticdev(path, name, d, elf, messages):
201 """
202 Check for ".a" library in non-staticdev packages
203 There are a number of exceptions to this rule, -pic packages can contain
204 static libraries, the _nonshared.a belong with their -dev packages and
205 libgcc.a, libgcov.a will be skipped in their packages
206 """
207
Andrew Geissler82c905d2020-04-13 13:39:40 -0500208 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 -0500209 package_qa_add_message(messages, "staticdev", "non -staticdev package contains static .a library: %s path '%s'" % \
210 (name, package_qa_clean_path(path,d)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500211
Andrew Geissler82c905d2020-04-13 13:39:40 -0500212QAPATHTEST[mime] = "package_qa_check_mime"
213def package_qa_check_mime(path, name, d, elf, messages):
214 """
215 Check if package installs mime types to /usr/share/mime/packages
216 while no inheriting mime.bbclass
217 """
218
219 if d.getVar("datadir") + "/mime/packages" in path and path.endswith('.xml') and not bb.data.inherits_class("mime", d):
220 package_qa_add_message(messages, "mime", "package contains mime types but does not inherit mime: %s path '%s'" % \
221 (name, package_qa_clean_path(path,d)))
222
223QAPATHTEST[mime-xdg] = "package_qa_check_mime_xdg"
224def package_qa_check_mime_xdg(path, name, d, elf, messages):
225 """
226 Check if package installs desktop file containing MimeType and requires
227 mime-types.bbclass to create /usr/share/applications/mimeinfo.cache
228 """
229
230 if d.getVar("datadir") + "/applications" in path and path.endswith('.desktop') and not bb.data.inherits_class("mime-xdg", d):
231 mime_type_found = False
232 try:
233 with open(path, 'r') as f:
234 for line in f.read().split('\n'):
235 if 'MimeType' in line:
236 mime_type_found = True
237 break;
238 except:
239 # At least libreoffice installs symlinks with absolute paths that are dangling here.
240 # We could implement some magic but for few (one) recipes it is not worth the effort so just warn:
241 wstr = "%s cannot open %s - is it a symlink with absolute path?\n" % (name, package_qa_clean_path(path,d))
242 wstr += "Please check if (linked) file contains key 'MimeType'.\n"
243 pkgname = name
244 if name == d.getVar('PN'):
245 pkgname = '${PN}'
246 wstr += "If yes: add \'inhert mime-xdg\' and \'MIME_XDG_PACKAGES += \"%s\"\' / if no add \'INSANE_SKIP_%s += \"mime-xdg\"\' to recipe." % (pkgname, pkgname)
247 package_qa_add_message(messages, "mime-xdg", wstr)
248 if mime_type_found:
249 package_qa_add_message(messages, "mime-xdg", "package contains desktop file with key 'MimeType' but does not inhert mime-xdg: %s path '%s'" % \
250 (name, package_qa_clean_path(path,d)))
251
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500252def package_qa_check_libdir(d):
253 """
254 Check for wrong library installation paths. For instance, catch
255 recipes installing /lib/bar.so when ${base_libdir}="lib32" or
256 installing in /usr/lib64 when ${libdir}="/usr/lib"
257 """
258 import re
259
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500260 pkgdest = d.getVar('PKGDEST')
261 base_libdir = d.getVar("base_libdir") + os.sep
262 libdir = d.getVar("libdir") + os.sep
263 libexecdir = d.getVar("libexecdir") + os.sep
264 exec_prefix = d.getVar("exec_prefix") + os.sep
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500265
266 messages = []
267
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500268 # The re's are purposely fuzzy, as some there are some .so.x.y.z files
269 # that don't follow the standard naming convention. It checks later
270 # that they are actual ELF files
Brad Bishop977dc1a2019-02-06 16:01:43 -0500271 lib_re = re.compile(r"^/lib.+\.so(\..+)?$")
272 exec_re = re.compile(r"^%s.*/lib.+\.so(\..+)?$" % exec_prefix)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500273
274 for root, dirs, files in os.walk(pkgdest):
275 if root == pkgdest:
276 # Skip subdirectories for any packages with libdir in INSANE_SKIP
277 skippackages = []
278 for package in dirs:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500279 if 'libdir' in (d.getVar('INSANE_SKIP_' + package) or "").split():
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500280 bb.note("Package %s skipping libdir QA test" % (package))
281 skippackages.append(package)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500282 elif d.getVar('PACKAGE_DEBUG_SPLIT_STYLE') == 'debug-file-directory' and package.endswith("-dbg"):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500283 bb.note("Package %s skipping libdir QA test for PACKAGE_DEBUG_SPLIT_STYLE equals debug-file-directory" % (package))
284 skippackages.append(package)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500285 for package in skippackages:
286 dirs.remove(package)
287 for file in files:
288 full_path = os.path.join(root, file)
289 rel_path = os.path.relpath(full_path, pkgdest)
290 if os.sep in rel_path:
291 package, rel_path = rel_path.split(os.sep, 1)
292 rel_path = os.sep + rel_path
293 if lib_re.match(rel_path):
294 if base_libdir not in rel_path:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500295 # make sure it's an actual ELF file
296 elf = oe.qa.ELFFile(full_path)
297 try:
298 elf.open()
299 messages.append("%s: found library in wrong location: %s" % (package, rel_path))
300 except (oe.qa.NotELFFileError):
301 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500302 if exec_re.match(rel_path):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500303 if libdir not in rel_path and libexecdir not in rel_path:
304 # make sure it's an actual ELF file
305 elf = oe.qa.ELFFile(full_path)
306 try:
307 elf.open()
308 messages.append("%s: found library in wrong location: %s" % (package, rel_path))
309 except (oe.qa.NotELFFileError):
310 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500311
312 if messages:
313 package_qa_handle_error("libdir", "\n".join(messages), d)
314
315QAPATHTEST[debug-files] = "package_qa_check_dbg"
316def package_qa_check_dbg(path, name, d, elf, messages):
317 """
318 Check for ".debug" files or directories outside of the dbg package
319 """
320
321 if not "-dbg" in name and not "-ptest" in name:
322 if '.debug' in path.split(os.path.sep):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500323 package_qa_add_message(messages, "debug-files", "non debug package contains .debug directory: %s path %s" % \
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500324 (name, package_qa_clean_path(path,d)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500325
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500326QAPATHTEST[arch] = "package_qa_check_arch"
327def package_qa_check_arch(path,name,d, elf, messages):
328 """
329 Check if archs are compatible
330 """
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800331 import re, oe.elf
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600332
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500333 if not elf:
334 return
335
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500336 target_os = d.getVar('TARGET_OS')
337 target_arch = d.getVar('TARGET_ARCH')
338 provides = d.getVar('PROVIDES')
339 bpn = d.getVar('BPN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500340
341 if target_arch == "allarch":
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500342 pn = d.getVar('PN')
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500343 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 -0500344 return
345
346 # FIXME: Cross package confuse this check, so just skip them
347 for s in ['cross', 'nativesdk', 'cross-canadian']:
348 if bb.data.inherits_class(s, d):
349 return
350
351 # avoid following links to /usr/bin (e.g. on udev builds)
352 # we will check the files pointed to anyway...
353 if os.path.islink(path):
354 return
355
356 #if this will throw an exception, then fix the dict above
357 (machine, osabi, abiversion, littleendian, bits) \
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800358 = oe.elf.machine_dict(d)[target_os][target_arch]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500359
360 # Check the architecture and endiannes of the binary
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600361 is_32 = (("virtual/kernel" in provides) or bb.data.inherits_class("module", d)) and \
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800362 (target_os == "linux-gnux32" or target_os == "linux-muslx32" or \
Brad Bishop977dc1a2019-02-06 16:01:43 -0500363 target_os == "linux-gnu_ilp32" or re.match(r'mips64.*32', d.getVar('DEFAULTTUNE')))
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800364 is_bpf = (oe.qa.elf_machine_to_string(elf.machine()) == "BPF")
365 if not ((machine == elf.machine()) or is_32 or is_bpf):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600366 package_qa_add_message(messages, "arch", "Architecture did not match (%s, expected %s) on %s" % \
367 (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 -0400368 elif not ((bits == elf.abiSize()) or is_32 or is_bpf):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500369 package_qa_add_message(messages, "arch", "Bit size did not match (%d to %d) %s on %s" % \
370 (bits, elf.abiSize(), bpn, package_qa_clean_path(path,d)))
Brad Bishop19323692019-04-05 15:28:33 -0400371 elif not ((littleendian == elf.isLittleEndian()) or is_bpf):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500372 package_qa_add_message(messages, "arch", "Endiannes did not match (%d to %d) on %s" % \
373 (littleendian, elf.isLittleEndian(), package_qa_clean_path(path,d)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500374
375QAPATHTEST[desktop] = "package_qa_check_desktop"
376def package_qa_check_desktop(path, name, d, elf, messages):
377 """
378 Run all desktop files through desktop-file-validate.
379 """
380 if path.endswith(".desktop"):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500381 desktop_file_validate = os.path.join(d.getVar('STAGING_BINDIR_NATIVE'),'desktop-file-validate')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500382 output = os.popen("%s %s" % (desktop_file_validate, path))
383 # This only produces output on errors
384 for l in output:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500385 package_qa_add_message(messages, "desktop", "Desktop file issue: " + l.strip())
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500386
387QAPATHTEST[textrel] = "package_qa_textrel"
388def package_qa_textrel(path, name, d, elf, messages):
389 """
390 Check if the binary contains relocations in .text
391 """
392
393 if not elf:
394 return
395
396 if os.path.islink(path):
397 return
398
399 phdrs = elf.run_objdump("-p", d)
400 sane = True
401
402 import re
Brad Bishop977dc1a2019-02-06 16:01:43 -0500403 textrel_re = re.compile(r"\s+TEXTREL\s+")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500404 for line in phdrs.split("\n"):
405 if textrel_re.match(line):
406 sane = False
Brad Bishop1d80a2e2019-11-15 16:35:03 -0500407 break
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500408
409 if not sane:
Brad Bishop1d80a2e2019-11-15 16:35:03 -0500410 path = package_qa_clean_path(path, d, name)
411 package_qa_add_message(messages, "textrel", "%s: ELF binary %s has relocations in .text" % (name, path))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500412
413QAPATHTEST[ldflags] = "package_qa_hash_style"
414def package_qa_hash_style(path, name, d, elf, messages):
415 """
416 Check if the binary has the right hash style...
417 """
418
419 if not elf:
420 return
421
422 if os.path.islink(path):
423 return
424
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500425 gnu_hash = "--hash-style=gnu" in d.getVar('LDFLAGS')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500426 if not gnu_hash:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500427 gnu_hash = "--hash-style=both" in d.getVar('LDFLAGS')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500428 if not gnu_hash:
429 return
430
431 sane = False
432 has_syms = False
433
434 phdrs = elf.run_objdump("-p", d)
435
436 # If this binary has symbols, we expect it to have GNU_HASH too.
437 for line in phdrs.split("\n"):
438 if "SYMTAB" in line:
439 has_syms = True
Andrew Geissler82c905d2020-04-13 13:39:40 -0500440 if "GNU_HASH" or "DT_MIPS_XHASH" in line:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500441 sane = True
Andrew Geissler82c905d2020-04-13 13:39:40 -0500442 if ("[mips32]" in line or "[mips64]" in line) and d.getVar('TCLIBC') == "musl":
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500443 sane = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500444 if has_syms and not sane:
Brad Bishopa5c52ff2018-11-23 10:55:50 +1300445 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 -0500446
447
448QAPATHTEST[buildpaths] = "package_qa_check_buildpaths"
449def package_qa_check_buildpaths(path, name, d, elf, messages):
450 """
451 Check for build paths inside target files and error if not found in the whitelist
452 """
453 # Ignore .debug files, not interesting
454 if path.find(".debug") != -1:
455 return
456
457 # Ignore symlinks
458 if os.path.islink(path):
459 return
460
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500461 # Ignore ipk and deb's CONTROL dir
462 if path.find(name + "/CONTROL/") != -1 or path.find(name + "/DEBIAN/") != -1:
463 return
464
Brad Bishopd5ae7d92018-06-14 09:52:03 -0700465 tmpdir = bytes(d.getVar('TMPDIR'), encoding="utf-8")
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500466 with open(path, 'rb') as f:
Brad Bishopd5ae7d92018-06-14 09:52:03 -0700467 file_content = f.read()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500468 if tmpdir in file_content:
Brad Bishopf3fd2882019-06-21 08:06:37 -0400469 trimmed = path.replace(os.path.join (d.getVar("PKGDEST"), name), "")
470 package_qa_add_message(messages, "buildpaths", "File %s in package %s contains reference to TMPDIR" % (trimmed, name))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500471
472
473QAPATHTEST[xorg-driver-abi] = "package_qa_check_xorg_driver_abi"
474def package_qa_check_xorg_driver_abi(path, name, d, elf, messages):
475 """
476 Check that all packages containing Xorg drivers have ABI dependencies
477 """
478
479 # Skip dev, dbg or nativesdk packages
480 if name.endswith("-dev") or name.endswith("-dbg") or name.startswith("nativesdk-"):
481 return
482
483 driverdir = d.expand("${libdir}/xorg/modules/drivers/")
484 if driverdir in path and path.endswith(".so"):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500485 mlprefix = d.getVar('MLPREFIX') or ''
486 for rdep in bb.utils.explode_deps(d.getVar('RDEPENDS_' + name) or ""):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500487 if rdep.startswith("%sxorg-abi-" % mlprefix):
488 return
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500489 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 -0500490
491QAPATHTEST[infodir] = "package_qa_check_infodir"
492def package_qa_check_infodir(path, name, d, elf, messages):
493 """
494 Check that /usr/share/info/dir isn't shipped in a particular package
495 """
496 infodir = d.expand("${infodir}/dir")
497
498 if infodir in path:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500499 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 -0500500
501QAPATHTEST[symlink-to-sysroot] = "package_qa_check_symlink_to_sysroot"
502def package_qa_check_symlink_to_sysroot(path, name, d, elf, messages):
503 """
504 Check that the package doesn't contain any absolute symlinks to the sysroot.
505 """
506 if os.path.islink(path):
507 target = os.readlink(path)
508 if os.path.isabs(target):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500509 tmpdir = d.getVar('TMPDIR')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500510 if target.startswith(tmpdir):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500511 trimmed = path.replace(os.path.join (d.getVar("PKGDEST"), name), "")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500512 package_qa_add_message(messages, "symlink-to-sysroot", "Symlink %s in %s points to TMPDIR" % (trimmed, name))
513
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600514# Check license variables
515do_populate_lic[postfuncs] += "populate_lic_qa_checksum"
516python populate_lic_qa_checksum() {
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500517 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600518 Check for changes in the license files.
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500519 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500520 sane = True
521
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500522 lic_files = d.getVar('LIC_FILES_CHKSUM') or ''
523 lic = d.getVar('LICENSE')
524 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500525
526 if lic == "CLOSED":
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500527 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500528
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500529 if not lic_files and d.getVar('SRC_URI'):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800530 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 -0500531
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500532 srcdir = d.getVar('S')
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500533 corebase_licensefile = d.getVar('COREBASE') + "/LICENSE"
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500534 for url in lic_files.split():
535 try:
536 (type, host, path, user, pswd, parm) = bb.fetch.decodeurl(url)
537 except bb.fetch.MalformedUrl:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800538 sane &= package_qa_handle_error("license-checksum", pn + ": LIC_FILES_CHKSUM contains an invalid URL: " + url, d)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500539 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500540 srclicfile = os.path.join(srcdir, path)
541 if not os.path.isfile(srclicfile):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800542 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 -0500543 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500544
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500545 if (srclicfile == corebase_licensefile):
546 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")
547
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500548 recipemd5 = parm.get('md5', '')
549 beginline, endline = 0, 0
550 if 'beginline' in parm:
551 beginline = int(parm['beginline'])
552 if 'endline' in parm:
553 endline = int(parm['endline'])
554
555 if (not beginline) and (not endline):
556 md5chksum = bb.utils.md5_file(srclicfile)
Brad Bishop19323692019-04-05 15:28:33 -0400557 with open(srclicfile, 'r', errors='replace') as f:
558 license = f.read().splitlines()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500559 else:
Brad Bishop19323692019-04-05 15:28:33 -0400560 with open(srclicfile, 'rb') as f:
561 import hashlib
562 lineno = 0
563 license = []
564 m = hashlib.md5()
565 for line in f:
566 lineno += 1
567 if (lineno >= beginline):
568 if ((lineno <= endline) or not endline):
569 m.update(line)
570 license.append(line.decode('utf-8', errors='replace').rstrip())
571 else:
572 break
573 md5chksum = m.hexdigest()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500574 if recipemd5 == md5chksum:
575 bb.note (pn + ": md5 checksum matched for ", url)
576 else:
577 if recipemd5:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500578 msg = pn + ": The LIC_FILES_CHKSUM does not match for " + url
579 msg = msg + "\n" + pn + ": The new md5 checksum is " + md5chksum
Brad Bishop19323692019-04-05 15:28:33 -0400580 max_lines = int(d.getVar('QA_MAX_LICENSE_LINES') or 20)
581 if not license or license[-1] != '':
582 # Ensure that our license text ends with a line break
583 # (will be added with join() below).
584 license.append('')
585 remove = len(license) - max_lines
586 if remove > 0:
587 start = max_lines // 2
588 end = start + remove - 1
589 del license[start:end]
590 license.insert(start, '...')
591 msg = msg + "\n" + pn + ": Here is the selected license text:" + \
592 "\n" + \
593 "{:v^70}".format(" beginline=%d " % beginline if beginline else "") + \
594 "\n" + "\n".join(license) + \
595 "{:^^70}".format(" endline=%d " % endline if endline else "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500596 if beginline:
597 if endline:
598 srcfiledesc = "%s (lines %d through to %d)" % (srclicfile, beginline, endline)
599 else:
600 srcfiledesc = "%s (beginning on line %d)" % (srclicfile, beginline)
601 elif endline:
602 srcfiledesc = "%s (ending on line %d)" % (srclicfile, endline)
603 else:
604 srcfiledesc = srclicfile
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500605 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 -0500606
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500607 else:
608 msg = pn + ": LIC_FILES_CHKSUM is not specified for " + url
609 msg = msg + "\n" + pn + ": The md5 checksum is " + md5chksum
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800610 sane &= package_qa_handle_error("license-checksum", msg, d)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600611
612 if not sane:
613 bb.fatal("Fatal QA errors found, failing task.")
614}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500615
Brad Bishop19323692019-04-05 15:28:33 -0400616def qa_check_staged(path,d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500617 """
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500618 Check staged la and pc files for common problems like references to the work
619 directory.
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500620
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500621 As this is run after every stage we should be able to find the one
622 responsible for the errors easily even if we look at every .pc and .la file.
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500623 """
624
625 sane = True
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500626 tmpdir = d.getVar('TMPDIR')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500627 workdir = os.path.join(tmpdir, "work")
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500628 recipesysroot = d.getVar("RECIPE_SYSROOT")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500629
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500630 if bb.data.inherits_class("native", d) or bb.data.inherits_class("cross", d):
631 pkgconfigcheck = workdir
632 else:
633 pkgconfigcheck = tmpdir
634
Brad Bishop19323692019-04-05 15:28:33 -0400635 skip = (d.getVar('INSANE_SKIP') or "").split()
636 skip_la = False
637 if 'la' in skip:
638 bb.note("Recipe %s skipping qa checking: la" % d.getVar('PN'))
639 skip_la = True
640
641 skip_pkgconfig = False
642 if 'pkgconfig' in skip:
643 bb.note("Recipe %s skipping qa checking: pkgconfig" % d.getVar('PN'))
644 skip_pkgconfig = True
645
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500646 # find all .la and .pc files
647 # read the content
648 # and check for stuff that looks wrong
649 for root, dirs, files in os.walk(path):
650 for file in files:
651 path = os.path.join(root,file)
Brad Bishop19323692019-04-05 15:28:33 -0400652 if file.endswith(".la") and not skip_la:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500653 with open(path) as f:
654 file_content = f.read()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500655 file_content = file_content.replace(recipesysroot, "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500656 if workdir in file_content:
657 error_msg = "%s failed sanity test (workdir) in path %s" % (file,root)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800658 sane &= package_qa_handle_error("la", error_msg, d)
Brad Bishop19323692019-04-05 15:28:33 -0400659 elif file.endswith(".pc") and not skip_pkgconfig:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500660 with open(path) as f:
661 file_content = f.read()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500662 file_content = file_content.replace(recipesysroot, "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500663 if pkgconfigcheck in file_content:
664 error_msg = "%s failed sanity test (tmpdir) in path %s" % (file,root)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800665 sane &= package_qa_handle_error("pkgconfig", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500666
667 return sane
668
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500669# Run all package-wide warnfuncs and errorfuncs
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500670def package_qa_package(warnfuncs, errorfuncs, package, d):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500671 warnings = {}
672 errors = {}
673
674 for func in warnfuncs:
675 func(package, d, warnings)
676 for func in errorfuncs:
677 func(package, d, errors)
678
679 for w in warnings:
680 package_qa_handle_error(w, warnings[w], d)
681 for e in errors:
682 package_qa_handle_error(e, errors[e], d)
683
684 return len(errors) == 0
685
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500686# Run all recipe-wide warnfuncs and errorfuncs
687def package_qa_recipe(warnfuncs, errorfuncs, pn, d):
688 warnings = {}
689 errors = {}
690
691 for func in warnfuncs:
692 func(pn, d, warnings)
693 for func in errorfuncs:
694 func(pn, d, errors)
695
696 for w in warnings:
697 package_qa_handle_error(w, warnings[w], d)
698 for e in errors:
699 package_qa_handle_error(e, errors[e], d)
700
701 return len(errors) == 0
702
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500703# Walk over all files in a directory and call func
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500704def package_qa_walk(warnfuncs, errorfuncs, package, d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500705 import oe.qa
706
707 #if this will throw an exception, then fix the dict above
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500708 target_os = d.getVar('TARGET_OS')
709 target_arch = d.getVar('TARGET_ARCH')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500710
711 warnings = {}
712 errors = {}
713 for path in pkgfiles[package]:
714 elf = oe.qa.ELFFile(path)
715 try:
716 elf.open()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500717 except (IOError, oe.qa.NotELFFileError):
718 # IOError can happen if the packaging control files disappear,
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500719 elf = None
720 for func in warnfuncs:
721 func(path, package, d, elf, warnings)
722 for func in errorfuncs:
723 func(path, package, d, elf, errors)
724
725 for w in warnings:
726 package_qa_handle_error(w, warnings[w], d)
727 for e in errors:
728 package_qa_handle_error(e, errors[e], d)
729
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500730def package_qa_check_rdepends(pkg, pkgdest, skip, taskdeps, packages, d):
731 # Don't do this check for kernel/module recipes, there aren't too many debug/development
732 # packages and you can get false positives e.g. on kernel-module-lirc-dev
733 if bb.data.inherits_class("kernel", d) or bb.data.inherits_class("module-base", d):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500734 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500735
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500736 if not "-dbg" in pkg and not "packagegroup-" in pkg and not "-image" in pkg:
737 localdata = bb.data.createCopy(d)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500738 localdata.setVar('OVERRIDES', localdata.getVar('OVERRIDES') + ':' + pkg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500739
740 # Now check the RDEPENDS
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500741 rdepends = bb.utils.explode_deps(localdata.getVar('RDEPENDS') or "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500742
743 # Now do the sanity check!!!
744 if "build-deps" not in skip:
745 for rdepend in rdepends:
746 if "-dbg" in rdepend and "debug-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("debug-deps", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500749 if (not "-dev" in pkg and not "-staticdev" in pkg) and rdepend.endswith("-dev") and "dev-deps" not in skip:
750 error_msg = "%s rdepends on %s" % (pkg, rdepend)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500751 package_qa_handle_error("dev-deps", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500752 if rdepend not in packages:
753 rdep_data = oe.packagedata.read_subpkgdata(rdepend, d)
754 if rdep_data and 'PN' in rdep_data and rdep_data['PN'] in taskdeps:
755 continue
756 if not rdep_data or not 'PN' in rdep_data:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500757 pkgdata_dir = d.getVar("PKGDATA_DIR")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500758 try:
759 possibles = os.listdir("%s/runtime-rprovides/%s/" % (pkgdata_dir, rdepend))
760 except OSError:
761 possibles = []
762 for p in possibles:
763 rdep_data = oe.packagedata.read_subpkgdata(p, d)
764 if rdep_data and 'PN' in rdep_data and rdep_data['PN'] in taskdeps:
765 break
766 if rdep_data and 'PN' in rdep_data and rdep_data['PN'] in taskdeps:
767 continue
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500768 if rdep_data and 'PN' in rdep_data:
769 error_msg = "%s rdepends on %s, but it isn't a build dependency, missing %s in DEPENDS or PACKAGECONFIG?" % (pkg, rdepend, rdep_data['PN'])
770 else:
771 error_msg = "%s rdepends on %s, but it isn't a build dependency?" % (pkg, rdepend)
772 package_qa_handle_error("build-deps", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500773
774 if "file-rdeps" not in skip:
775 ignored_file_rdeps = set(['/bin/sh', '/usr/bin/env', 'rtld(GNU_HASH)'])
776 if bb.data.inherits_class('nativesdk', d):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500777 ignored_file_rdeps |= set(['/bin/bash', '/usr/bin/perl', 'perl'])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500778 # For Saving the FILERDEPENDS
779 filerdepends = {}
780 rdep_data = oe.packagedata.read_subpkgdata(pkg, d)
781 for key in rdep_data:
782 if key.startswith("FILERDEPENDS_"):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500783 for subkey in bb.utils.explode_deps(rdep_data[key]):
784 if subkey not in ignored_file_rdeps and \
785 not subkey.startswith('perl('):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500786 # We already know it starts with FILERDEPENDS_
787 filerdepends[subkey] = key[13:]
788
789 if filerdepends:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500790 done = rdepends[:]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500791 # Add the rprovides of itself
792 if pkg not in done:
793 done.insert(0, pkg)
794
795 # The python is not a package, but python-core provides it, so
796 # skip checking /usr/bin/python if python is in the rdeps, in
797 # case there is a RDEPENDS_pkg = "python" in the recipe.
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500798 for py in [ d.getVar('MLPREFIX') + "python", "python" ]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500799 if py in done:
800 filerdepends.pop("/usr/bin/python",None)
801 done.remove(py)
802 for rdep in done:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500803 # The file dependencies may contain package names, e.g.,
804 # perl
805 filerdepends.pop(rdep,None)
806
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500807 # For Saving the FILERPROVIDES, RPROVIDES and FILES_INFO
808 rdep_data = oe.packagedata.read_subpkgdata(rdep, d)
809 for key in rdep_data:
810 if key.startswith("FILERPROVIDES_") or key.startswith("RPROVIDES_"):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500811 for subkey in bb.utils.explode_deps(rdep_data[key]):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500812 filerdepends.pop(subkey,None)
813 # Add the files list to the rprovides
814 if key == "FILES_INFO":
815 # Use eval() to make it as a dict
816 for subkey in eval(rdep_data[key]):
817 filerdepends.pop(subkey,None)
818 if not filerdepends:
819 # Break if all the file rdepends are met
820 break
821 if filerdepends:
822 for key in filerdepends:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500823 error_msg = "%s contained in package %s requires %s, but no providers found in RDEPENDS_%s?" % \
824 (filerdepends[key].replace("_%s" % pkg, "").replace("@underscore@", "_"), pkg, key, pkg)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500825 package_qa_handle_error("file-rdeps", error_msg, d)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500826package_qa_check_rdepends[vardepsexclude] = "OVERRIDES"
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500827
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500828def package_qa_check_deps(pkg, pkgdest, d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500829
830 localdata = bb.data.createCopy(d)
831 localdata.setVar('OVERRIDES', pkg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500832
833 def check_valid_deps(var):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500834 try:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500835 rvar = bb.utils.explode_dep_versions2(localdata.getVar(var) or "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500836 except ValueError as e:
837 bb.fatal("%s_%s: %s" % (var, pkg, e))
838 for dep in rvar:
839 for v in rvar[dep]:
840 if v and not v.startswith(('< ', '= ', '> ', '<= ', '>=')):
841 error_msg = "%s_%s is invalid: %s (%s) only comparisons <, =, >, <=, and >= are allowed" % (var, pkg, dep, v)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500842 package_qa_handle_error("dep-cmp", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500843
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500844 check_valid_deps('RDEPENDS')
845 check_valid_deps('RRECOMMENDS')
846 check_valid_deps('RSUGGESTS')
847 check_valid_deps('RPROVIDES')
848 check_valid_deps('RREPLACES')
849 check_valid_deps('RCONFLICTS')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500850
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500851QAPKGTEST[usrmerge] = "package_qa_check_usrmerge"
852def package_qa_check_usrmerge(pkg, d, messages):
853 pkgdest = d.getVar('PKGDEST')
854 pkg_dir = pkgdest + os.sep + pkg + os.sep
855 merged_dirs = ['bin', 'sbin', 'lib'] + d.getVar('MULTILIB_VARIANTS').split()
856 for f in merged_dirs:
857 if os.path.exists(pkg_dir + f) and not os.path.islink(pkg_dir + f):
858 msg = "%s package is not obeying usrmerge distro feature. /%s should be relocated to /usr." % (pkg, f)
859 package_qa_add_message(messages, "usrmerge", msg)
860 return False
861 return True
862
Brad Bishopf3f93bb2019-10-16 14:33:32 -0400863QAPKGTEST[perllocalpod] = "package_qa_check_perllocalpod"
864def package_qa_check_perllocalpod(pkg, d, messages):
865 """
866 Check that the recipe didn't ship a perlocal.pod file, which shouldn't be
867 installed in a distribution package. cpan.bbclass sets NO_PERLLOCAL=1 to
868 handle this for most recipes.
869 """
870 import glob
871 pkgd = oe.path.join(d.getVar('PKGDEST'), pkg)
872 podpath = oe.path.join(pkgd, d.getVar("libdir"), "perl*", "*", "*", "perllocal.pod")
873
874 matches = glob.glob(podpath)
875 if matches:
876 matches = [package_qa_clean_path(path, d, pkg) for path in matches]
877 msg = "%s contains perllocal.pod (%s), should not be installed" % (pkg, " ".join(matches))
878 package_qa_add_message(messages, "perllocalpod", msg)
879
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500880QAPKGTEST[expanded-d] = "package_qa_check_expanded_d"
881def package_qa_check_expanded_d(package, d, messages):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500882 """
883 Check for the expanded D (${D}) value in pkg_* and FILES
884 variables, warn the user to use it correctly.
885 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500886 sane = True
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500887 expanded_d = d.getVar('D')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500888
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500889 for var in 'FILES','pkg_preinst', 'pkg_postinst', 'pkg_prerm', 'pkg_postrm':
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500890 bbvar = d.getVar(var + "_" + package) or ""
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500891 if expanded_d in bbvar:
892 if var == 'FILES':
893 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)
894 sane = False
895 else:
896 package_qa_add_message(messages, "expanded-d", "%s in %s recipe contains ${D}, it should be replaced by $D instead" % (var, package))
897 sane = False
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500898 return sane
899
900def package_qa_check_encoding(keys, encode, d):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600901 def check_encoding(key, enc):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500902 sane = True
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500903 value = d.getVar(key)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500904 if value:
905 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600906 s = value.encode(enc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500907 except UnicodeDecodeError as e:
908 error_msg = "%s has non %s characters" % (key,enc)
909 sane = False
910 package_qa_handle_error("invalid-chars", error_msg, d)
911 return sane
912
913 for key in keys:
914 sane = check_encoding(key, encode)
915 if not sane:
916 break
917
918HOST_USER_UID := "${@os.getuid()}"
919HOST_USER_GID := "${@os.getgid()}"
920
921QAPATHTEST[host-user-contaminated] = "package_qa_check_host_user"
922def package_qa_check_host_user(path, name, d, elf, messages):
923 """Check for paths outside of /home which are owned by the user running bitbake."""
924
925 if not os.path.lexists(path):
926 return
927
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500928 dest = d.getVar('PKGDEST')
929 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500930 home = os.path.join(dest, 'home')
931 if path == home or path.startswith(home + os.sep):
932 return
933
934 try:
935 stat = os.lstat(path)
936 except OSError as exc:
937 import errno
938 if exc.errno != errno.ENOENT:
939 raise
940 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500941 check_uid = int(d.getVar('HOST_USER_UID'))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500942 if stat.st_uid == check_uid:
Brad Bishop96ff1982019-08-19 13:50:42 -0400943 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 -0500944 return False
945
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500946 check_gid = int(d.getVar('HOST_USER_GID'))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500947 if stat.st_gid == check_gid:
Brad Bishop96ff1982019-08-19 13:50:42 -0400948 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 -0500949 return False
950 return True
951
Brad Bishop15ae2502019-06-18 21:44:24 -0400952QARECIPETEST[src-uri-bad] = "package_qa_check_src_uri"
953def package_qa_check_src_uri(pn, d, messages):
954 import re
955
956 if "${PN}" in d.getVar("SRC_URI", False):
957 package_qa_handle_error("src-uri-bad", "%s: SRC_URI uses PN not BPN" % pn, d)
958
Andrew Geissler82c905d2020-04-13 13:39:40 -0500959 for url in d.getVar("SRC_URI").split():
960 if re.search(r"github\.com/.+/.+/archive/.+", url):
961 package_qa_handle_error("src-uri-bad", "%s: SRC_URI uses unstable GitHub archives" % pn, d)
Brad Bishop15ae2502019-06-18 21:44:24 -0400962
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500963
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500964# The PACKAGE FUNC to scan each package
965python do_package_qa () {
966 import subprocess
967 import oe.packagedata
968
969 bb.note("DO PACKAGE QA")
970
971 bb.build.exec_func("read_subpackage_metadata", d)
972
973 # Check non UTF-8 characters on recipe's metadata
974 package_qa_check_encoding(['DESCRIPTION', 'SUMMARY', 'LICENSE', 'SECTION'], 'utf-8', d)
975
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500976 logdir = d.getVar('T')
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500977 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500978
979 # Check the compile log for host contamination
980 compilelog = os.path.join(logdir,"log.do_compile")
981
982 if os.path.exists(compilelog):
983 statement = "grep -e 'CROSS COMPILE Badness:' -e 'is unsafe for cross-compilation' %s > /dev/null" % compilelog
984 if subprocess.call(statement, shell=True) == 0:
985 msg = "%s: The compile log indicates that host include and/or library paths were used.\n \
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500986 Please check the log '%s' for more information." % (pn, compilelog)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500987 package_qa_handle_error("compile-host-path", msg, d)
988
989 # Check the install log for host contamination
990 installlog = os.path.join(logdir,"log.do_install")
991
992 if os.path.exists(installlog):
993 statement = "grep -e 'CROSS COMPILE Badness:' -e 'is unsafe for cross-compilation' %s > /dev/null" % installlog
994 if subprocess.call(statement, shell=True) == 0:
995 msg = "%s: The install log indicates that host include and/or library paths were used.\n \
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500996 Please check the log '%s' for more information." % (pn, installlog)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500997 package_qa_handle_error("install-host-path", msg, d)
998
999 # Scan the packages...
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001000 pkgdest = d.getVar('PKGDEST')
1001 packages = set((d.getVar('PACKAGES') or '').split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001002
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001003 global pkgfiles
1004 pkgfiles = {}
1005 for pkg in packages:
1006 pkgfiles[pkg] = []
Andrew Geissler82c905d2020-04-13 13:39:40 -05001007 for walkroot, dirs, files in os.walk(os.path.join(pkgdest, pkg)):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001008 for file in files:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001009 pkgfiles[pkg].append(os.path.join(walkroot, file))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001010
1011 # no packages should be scanned
1012 if not packages:
1013 return
1014
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001015 import re
1016 # The package name matches the [a-z0-9.+-]+ regular expression
Brad Bishop977dc1a2019-02-06 16:01:43 -05001017 pkgname_pattern = re.compile(r"^[a-z0-9.+-]+$")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001018
1019 taskdepdata = d.getVar("BB_TASKDEPDATA", False)
1020 taskdeps = set()
1021 for dep in taskdepdata:
1022 taskdeps.add(taskdepdata[dep][0])
1023
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001024 def parse_test_matrix(matrix_name):
1025 testmatrix = d.getVarFlags(matrix_name) or {}
1026 g = globals()
1027 warnchecks = []
1028 for w in (d.getVar("WARN_QA") or "").split():
1029 if w in skip:
1030 continue
1031 if w in testmatrix and testmatrix[w] in g:
1032 warnchecks.append(g[testmatrix[w]])
1033
1034 errorchecks = []
1035 for e in (d.getVar("ERROR_QA") or "").split():
1036 if e in skip:
1037 continue
1038 if e in testmatrix and testmatrix[e] in g:
1039 errorchecks.append(g[testmatrix[e]])
1040 return warnchecks, errorchecks
1041
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001042 for package in packages:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001043 skip = set((d.getVar('INSANE_SKIP') or "").split() +
1044 (d.getVar('INSANE_SKIP_' + package) or "").split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001045 if skip:
1046 bb.note("Package %s skipping QA tests: %s" % (package, str(skip)))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001047
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001048 bb.note("Checking Package: %s" % package)
1049 # Check package name
1050 if not pkgname_pattern.match(package):
1051 package_qa_handle_error("pkgname",
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001052 "%s doesn't match the [a-z0-9.+-]+ regex" % package, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001053
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001054 warn_checks, error_checks = parse_test_matrix("QAPATHTEST")
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001055 package_qa_walk(warn_checks, error_checks, package, d)
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001056
1057 warn_checks, error_checks = parse_test_matrix("QAPKGTEST")
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001058 package_qa_package(warn_checks, error_checks, package, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001059
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001060 package_qa_check_rdepends(package, pkgdest, skip, taskdeps, packages, d)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001061 package_qa_check_deps(package, pkgdest, d)
1062
1063 warn_checks, error_checks = parse_test_matrix("QARECIPETEST")
1064 package_qa_recipe(warn_checks, error_checks, pn, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001065
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001066 if 'libdir' in d.getVar("ALL_QA").split():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001067 package_qa_check_libdir(d)
1068
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001069 qa_sane = d.getVar("QA_SANE")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001070 if not qa_sane:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001071 bb.fatal("QA run found fatal errors. Please consider fixing them.")
1072 bb.note("DONE with PACKAGE QA")
1073}
1074
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001075# binutils is used for most checks, so need to set as dependency
1076# POPULATESYSROOTDEPS is defined in staging class.
1077do_package_qa[depends] += "${POPULATESYSROOTDEPS}"
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001078do_package_qa[vardepsexclude] = "BB_TASKDEPDATA"
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001079do_package_qa[rdeptask] = "do_packagedata"
1080addtask do_package_qa after do_packagedata do_package before do_build
1081
Brad Bishop19323692019-04-05 15:28:33 -04001082# Add the package specific INSANE_SKIPs to the sstate dependencies
1083python() {
1084 pkgs = (d.getVar('PACKAGES') or '').split()
1085 for pkg in pkgs:
1086 d.appendVarFlag("do_package_qa", "vardeps", " INSANE_SKIP_{}".format(pkg))
1087}
1088
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001089SSTATETASKS += "do_package_qa"
1090do_package_qa[sstate-inputdirs] = ""
1091do_package_qa[sstate-outputdirs] = ""
1092python do_package_qa_setscene () {
1093 sstate_setscene(d)
1094}
1095addtask do_package_qa_setscene
1096
1097python do_qa_staging() {
1098 bb.note("QA checking staging")
Brad Bishop19323692019-04-05 15:28:33 -04001099 if not qa_check_staged(d.expand('${SYSROOT_DESTDIR}${libdir}'), d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001100 bb.fatal("QA staging was broken by the package built above")
1101}
1102
Brad Bishopd89cb5f2019-04-10 09:02:41 -04001103python do_qa_patch() {
1104 import subprocess
1105
1106 ###########################################################################
1107 # Check patch.log for fuzz warnings
1108 #
1109 # Further information on why we check for patch fuzz warnings:
1110 # http://lists.openembedded.org/pipermail/openembedded-core/2018-March/148675.html
1111 # https://bugzilla.yoctoproject.org/show_bug.cgi?id=10450
1112 ###########################################################################
1113
1114 logdir = d.getVar('T')
1115 patchlog = os.path.join(logdir,"log.do_patch")
1116
1117 if os.path.exists(patchlog):
1118 fuzzheader = '--- Patch fuzz start ---'
1119 fuzzfooter = '--- Patch fuzz end ---'
1120 statement = "grep -e '%s' %s > /dev/null" % (fuzzheader, patchlog)
1121 if subprocess.call(statement, shell=True) == 0:
1122 msg = "Fuzz detected:\n\n"
1123 fuzzmsg = ""
1124 inFuzzInfo = False
1125 f = open(patchlog, "r")
1126 for line in f:
1127 if fuzzheader in line:
1128 inFuzzInfo = True
1129 fuzzmsg = ""
1130 elif fuzzfooter in line:
1131 fuzzmsg = fuzzmsg.replace('\n\n', '\n')
1132 msg += fuzzmsg
1133 msg += "\n"
1134 inFuzzInfo = False
1135 elif inFuzzInfo and not 'Now at patch' in line:
1136 fuzzmsg += line
1137 f.close()
1138 msg += "The context lines in the patches can be updated with devtool:\n"
1139 msg += "\n"
1140 msg += " devtool modify %s\n" % d.getVar('PN')
1141 msg += " devtool finish --force-patch-refresh %s <layer_path>\n\n" % d.getVar('PN')
1142 msg += "Don't forget to review changes done by devtool!\n"
1143 if 'patch-fuzz' in d.getVar('ERROR_QA'):
1144 bb.error(msg)
1145 elif 'patch-fuzz' in d.getVar('WARN_QA'):
1146 bb.warn(msg)
1147 msg = "Patch log indicates that patches do not apply cleanly."
1148 package_qa_handle_error("patch-fuzz", msg, d)
1149}
1150
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001151python do_qa_configure() {
1152 import subprocess
1153
1154 ###########################################################################
1155 # Check config.log for cross compile issues
1156 ###########################################################################
1157
1158 configs = []
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001159 workdir = d.getVar('WORKDIR')
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001160
Brad Bishop19323692019-04-05 15:28:33 -04001161 skip = (d.getVar('INSANE_SKIP') or "").split()
1162 skip_configure_unsafe = False
1163 if 'configure-unsafe' in skip:
1164 bb.note("Recipe %s skipping qa checking: configure-unsafe" % d.getVar('PN'))
1165 skip_configure_unsafe = True
1166
1167 if bb.data.inherits_class('autotools', d) and not skip_configure_unsafe:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001168 bb.note("Checking autotools environment for common misconfiguration")
1169 for root, dirs, files in os.walk(workdir):
1170 statement = "grep -q -F -e 'CROSS COMPILE Badness:' -e 'is unsafe for cross-compilation' %s" % \
1171 os.path.join(root,"config.log")
1172 if "config.log" in files:
1173 if subprocess.call(statement, shell=True) == 0:
Brad Bishop19323692019-04-05 15:28:33 -04001174 error_msg = """This autoconf log indicates errors, it looked at host include and/or library paths while determining system capabilities.
1175Rerun configure task after fixing this."""
1176 package_qa_handle_error("configure-unsafe", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001177
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001178 if "configure.ac" in files:
1179 configs.append(os.path.join(root,"configure.ac"))
1180 if "configure.in" in files:
1181 configs.append(os.path.join(root, "configure.in"))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001182
1183 ###########################################################################
1184 # Check gettext configuration and dependencies are correct
1185 ###########################################################################
1186
Brad Bishop19323692019-04-05 15:28:33 -04001187 skip_configure_gettext = False
1188 if 'configure-gettext' in skip:
1189 bb.note("Recipe %s skipping qa checking: configure-gettext" % d.getVar('PN'))
1190 skip_configure_gettext = True
1191
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001192 cnf = d.getVar('EXTRA_OECONF') or ""
Brad Bishop19323692019-04-05 15:28:33 -04001193 if not ("gettext" in d.getVar('P') or "gcc-runtime" in d.getVar('P') or \
1194 "--disable-nls" in cnf or skip_configure_gettext):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001195 ml = d.getVar("MLPREFIX") or ""
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001196 if bb.data.inherits_class('cross-canadian', d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001197 gt = "nativesdk-gettext"
1198 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001199 gt = "gettext-native"
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001200 deps = bb.utils.explode_deps(d.getVar('DEPENDS') or "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001201 if gt not in deps:
1202 for config in configs:
1203 gnu = "grep \"^[[:space:]]*AM_GNU_GETTEXT\" %s >/dev/null" % config
1204 if subprocess.call(gnu, shell=True) == 0:
Brad Bishopd89cb5f2019-04-10 09:02:41 -04001205 error_msg = "AM_GNU_GETTEXT used but no inherit gettext"
Brad Bishop19323692019-04-05 15:28:33 -04001206 package_qa_handle_error("configure-gettext", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001207
1208 ###########################################################################
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001209 # Check unrecognised configure options (with a white list)
1210 ###########################################################################
Brad Bishopc342db32019-05-15 21:57:59 -04001211 if bb.data.inherits_class("autotools", d) or bb.data.inherits_class("meson", d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001212 bb.note("Checking configure output for unrecognised options")
1213 try:
Brad Bishopc342db32019-05-15 21:57:59 -04001214 if bb.data.inherits_class("autotools", d):
1215 flag = "WARNING: unrecognized options:"
1216 log = os.path.join(d.getVar('B'), 'config.log')
1217 if bb.data.inherits_class("meson", d):
1218 flag = "WARNING: Unknown options:"
1219 log = os.path.join(d.getVar('T'), 'log.do_configure')
1220 output = subprocess.check_output(['grep', '-F', flag, log]).decode("utf-8").replace(', ', ' ').replace('"', '')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001221 options = set()
1222 for line in output.splitlines():
1223 options |= set(line.partition(flag)[2].split())
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001224 whitelist = set(d.getVar("UNKNOWN_CONFIGURE_WHITELIST").split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001225 options -= whitelist
1226 if options:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001227 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001228 error_msg = pn + ": configure was passed unrecognised options: " + " ".join(options)
1229 package_qa_handle_error("unknown-configure-option", error_msg, d)
1230 except subprocess.CalledProcessError:
1231 pass
1232
1233 # Check invalid PACKAGECONFIG
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001234 pkgconfig = (d.getVar("PACKAGECONFIG") or "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001235 if pkgconfig:
1236 pkgconfigflags = d.getVarFlags("PACKAGECONFIG") or {}
1237 for pconfig in pkgconfig:
1238 if pconfig not in pkgconfigflags:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001239 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001240 error_msg = "%s: invalid PACKAGECONFIG: %s" % (pn, pconfig)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001241 package_qa_handle_error("invalid-packageconfig", error_msg, d)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001242
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001243 qa_sane = d.getVar("QA_SANE")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001244 if not qa_sane:
1245 bb.fatal("Fatal QA errors found, failing task.")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001246}
1247
1248python do_qa_unpack() {
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001249 src_uri = d.getVar('SRC_URI')
1250 s_dir = d.getVar('S')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001251 if src_uri and not os.path.exists(s_dir):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001252 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 -05001253}
1254
1255# The Staging Func, to check all staging
1256#addtask qa_staging after do_populate_sysroot before do_build
1257do_populate_sysroot[postfuncs] += "do_qa_staging "
1258
Brad Bishopd89cb5f2019-04-10 09:02:41 -04001259# Check for patch fuzz
1260do_patch[postfuncs] += "do_qa_patch "
1261
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001262# Check broken config.log files, for packages requiring Gettext which
1263# don't have it in DEPENDS.
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001264#addtask qa_configure after do_configure before do_compile
1265do_configure[postfuncs] += "do_qa_configure "
1266
1267# Check does S exist.
1268do_unpack[postfuncs] += "do_qa_unpack"
1269
1270python () {
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001271 import re
1272
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001273 tests = d.getVar('ALL_QA').split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001274 if "desktop" in tests:
1275 d.appendVar("PACKAGE_DEPENDS", " desktop-file-utils-native")
1276
1277 ###########################################################################
1278 # Check various variables
1279 ###########################################################################
1280
1281 # Checking ${FILESEXTRAPATHS}
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001282 extrapaths = (d.getVar("FILESEXTRAPATHS") or "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001283 if '__default' not in extrapaths.split(":"):
1284 msg = "FILESEXTRAPATHS-variable, must always use _prepend (or _append)\n"
1285 msg += "type of assignment, and don't forget the colon.\n"
1286 msg += "Please assign it with the format of:\n"
1287 msg += " FILESEXTRAPATHS_append := \":${THISDIR}/Your_Files_Path\" or\n"
1288 msg += " FILESEXTRAPATHS_prepend := \"${THISDIR}/Your_Files_Path:\"\n"
1289 msg += "in your bbappend file\n\n"
1290 msg += "Your incorrect assignment is:\n"
1291 msg += "%s\n" % extrapaths
1292 bb.warn(msg)
1293
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001294 overrides = d.getVar('OVERRIDES').split(':')
1295 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001296 if pn in overrides:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001297 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 -05001298 package_qa_handle_error("pn-overrides", msg, d)
Brad Bishop977dc1a2019-02-06 16:01:43 -05001299 prog = re.compile(r'[A-Z]')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001300 if prog.search(pn):
1301 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 -05001302
Brad Bishop08902b02019-08-20 09:16:51 -04001303 # Some people mistakenly use DEPENDS_${PN} instead of DEPENDS and wonder
1304 # why it doesn't work.
1305 if (d.getVar(d.expand('DEPENDS_${PN}'))):
1306 package_qa_handle_error("pkgvarcheck", "recipe uses DEPENDS_${PN}, should use DEPENDS", d)
1307
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001308 issues = []
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001309 if (d.getVar('PACKAGES') or "").split():
1310 for dep in (d.getVar('QADEPENDS') or "").split():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001311 d.appendVarFlag('do_package_qa', 'depends', " %s:do_populate_sysroot" % dep)
1312 for var in 'RDEPENDS', 'RRECOMMENDS', 'RSUGGESTS', 'RCONFLICTS', 'RPROVIDES', 'RREPLACES', 'FILES', 'pkg_preinst', 'pkg_postinst', 'pkg_prerm', 'pkg_postrm', 'ALLOW_EMPTY':
1313 if d.getVar(var, False):
1314 issues.append(var)
1315
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001316 fakeroot_tests = d.getVar('FAKEROOT_QA').split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001317 if set(tests) & set(fakeroot_tests):
1318 d.setVarFlag('do_package_qa', 'fakeroot', '1')
1319 d.appendVarFlag('do_package_qa', 'depends', ' virtual/fakeroot-native:do_populate_sysroot')
1320 else:
1321 d.setVarFlag('do_package_qa', 'rdeptask', '')
1322 for i in issues:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001323 package_qa_handle_error("pkgvarcheck", "%s: Variable %s is set as not being package specific, please fix this." % (d.getVar("FILE"), i), d)
1324 qa_sane = d.getVar("QA_SANE")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001325 if not qa_sane:
1326 bb.fatal("Fatal QA errors found, failing task.")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001327}