blob: c6dff9659ceea62269b3c8e06df0bd2ffbffbd9f [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 \
Andrew Geisslerc9f78652020-09-18 14:11:35 -050030 missing-update-alternatives \
Patrick Williamsc124f4f2015-09-15 14:41:29 -050031 "
32ERROR_QA ?= "dev-so debug-deps dev-deps debug-files arch pkgconfig la \
33 perms dep-cmp pkgvarcheck perm-config perm-line perm-link \
34 split-strip packages-list pkgv-undefined var-undefined \
35 version-going-backwards expanded-d invalid-chars \
Brad Bishop19323692019-04-05 15:28:33 -040036 license-checksum dev-elf file-rdeps configure-unsafe \
Andrew Geissler82c905d2020-04-13 13:39:40 -050037 configure-gettext perllocalpod shebang-size \
Andrew Geisslerc182c622020-05-15 14:13:32 -050038 already-stripped installed-vs-shipped ldflags compile-host-path \
39 install-host-path pn-overrides unknown-configure-option \
40 useless-rpaths rpaths staticdev \
Patrick Williamsc124f4f2015-09-15 14:41:29 -050041 "
Brad Bishopd7bf8c12018-02-25 22:55:05 -050042# Add usrmerge QA check based on distro feature
43ERROR_QA_append = "${@bb.utils.contains('DISTRO_FEATURES', 'usrmerge', ' usrmerge', '', d)}"
44
Patrick Williamsc124f4f2015-09-15 14:41:29 -050045FAKEROOT_QA = "host-user-contaminated"
46FAKEROOT_QA[doc] = "QA tests which need to run under fakeroot. If any \
47enabled tests are listed here, the do_package_qa task will run under fakeroot."
48
49ALL_QA = "${WARN_QA} ${ERROR_QA}"
50
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050051UNKNOWN_CONFIGURE_WHITELIST ?= "--enable-nls --disable-nls --disable-silent-rules --disable-dependency-tracking --with-libtool-sysroot --disable-static"
Patrick Williamsc124f4f2015-09-15 14:41:29 -050052
Patrick Williamsc0f7c042017-02-23 20:41:17 -060053def package_qa_clean_path(path, d, pkg=None):
54 """
55 Remove redundant paths from the path for display. If pkg isn't set then
56 TMPDIR is stripped, otherwise PKGDEST/pkg is stripped.
57 """
58 if pkg:
Brad Bishop6e60e8b2018-02-01 10:27:11 -050059 path = path.replace(os.path.join(d.getVar("PKGDEST"), pkg), "/")
60 return path.replace(d.getVar("TMPDIR"), "/").replace("//", "/")
Patrick Williamsc124f4f2015-09-15 14:41:29 -050061
62def package_qa_write_error(type, error, d):
Brad Bishop6e60e8b2018-02-01 10:27:11 -050063 logfile = d.getVar('QA_LOGFILE')
Patrick Williamsc124f4f2015-09-15 14:41:29 -050064 if logfile:
Brad Bishop6e60e8b2018-02-01 10:27:11 -050065 p = d.getVar('P')
Patrick Williamsc0f7c042017-02-23 20:41:17 -060066 with open(logfile, "a+") as f:
67 f.write("%s: %s [%s]\n" % (p, error, type))
Patrick Williamsc124f4f2015-09-15 14:41:29 -050068
69def package_qa_handle_error(error_class, error_msg, d):
Brad Bishop6e60e8b2018-02-01 10:27:11 -050070 if error_class in (d.getVar("ERROR_QA") or "").split():
Brad Bishopd7bf8c12018-02-25 22:55:05 -050071 package_qa_write_error(error_class, error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050072 bb.error("QA Issue: %s [%s]" % (error_msg, error_class))
73 d.setVar("QA_SANE", False)
74 return False
Brad Bishop6e60e8b2018-02-01 10:27:11 -050075 elif error_class in (d.getVar("WARN_QA") or "").split():
Brad Bishopd7bf8c12018-02-25 22:55:05 -050076 package_qa_write_error(error_class, error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050077 bb.warn("QA Issue: %s [%s]" % (error_msg, error_class))
78 else:
79 bb.note("QA Issue: %s [%s]" % (error_msg, error_class))
80 return True
81
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050082def package_qa_add_message(messages, section, new_msg):
83 if section not in messages:
84 messages[section] = new_msg
85 else:
86 messages[section] = messages[section] + "\n" + new_msg
87
Andrew Geissler82c905d2020-04-13 13:39:40 -050088QAPATHTEST[shebang-size] = "package_qa_check_shebang_size"
89def package_qa_check_shebang_size(path, name, d, elf, messages):
90 if os.path.islink(path) or elf:
91 return
92
93 try:
94 with open(path, 'rb') as f:
95 stanza = f.readline(130)
96 except IOError:
97 return
98
99 if stanza.startswith(b'#!'):
100 #Shebang not found
101 try:
102 stanza = stanza.decode("utf-8")
103 except UnicodeDecodeError:
104 #If it is not a text file, it is not a script
105 return
106
107 if len(stanza) > 129:
108 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)))
109 return
110
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500111QAPATHTEST[libexec] = "package_qa_check_libexec"
112def package_qa_check_libexec(path,name, d, elf, messages):
113
114 # Skip the case where the default is explicitly /usr/libexec
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500115 libexec = d.getVar('libexecdir')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500116 if libexec == "/usr/libexec":
117 return True
118
119 if 'libexec' in path.split(os.path.sep):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500120 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 -0500121 return False
122
123 return True
124
125QAPATHTEST[rpaths] = "package_qa_check_rpath"
126def package_qa_check_rpath(file,name, d, elf, messages):
127 """
128 Check for dangerous RPATHs
129 """
130 if not elf:
131 return
132
133 if os.path.islink(file):
134 return
135
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500136 bad_dirs = [d.getVar('BASE_WORKDIR'), d.getVar('STAGING_DIR_TARGET')]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500137
138 phdrs = elf.run_objdump("-p", d)
139
140 import re
Brad Bishop977dc1a2019-02-06 16:01:43 -0500141 rpath_re = re.compile(r"\s+RPATH\s+(.*)")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500142 for line in phdrs.split("\n"):
143 m = rpath_re.match(line)
144 if m:
145 rpath = m.group(1)
146 for dir in bad_dirs:
147 if dir in rpath:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500148 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 -0500149
150QAPATHTEST[useless-rpaths] = "package_qa_check_useless_rpaths"
151def package_qa_check_useless_rpaths(file, name, d, elf, messages):
152 """
153 Check for RPATHs that are useless but not dangerous
154 """
155 def rpath_eq(a, b):
156 return os.path.normpath(a) == os.path.normpath(b)
157
158 if not elf:
159 return
160
161 if os.path.islink(file):
162 return
163
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500164 libdir = d.getVar("libdir")
165 base_libdir = d.getVar("base_libdir")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500166
167 phdrs = elf.run_objdump("-p", d)
168
169 import re
Brad Bishop977dc1a2019-02-06 16:01:43 -0500170 rpath_re = re.compile(r"\s+RPATH\s+(.*)")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500171 for line in phdrs.split("\n"):
172 m = rpath_re.match(line)
173 if m:
174 rpath = m.group(1)
175 if rpath_eq(rpath, libdir) or rpath_eq(rpath, base_libdir):
176 # The dynamic linker searches both these places anyway. There is no point in
177 # looking there again.
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500178 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 -0500179
180QAPATHTEST[dev-so] = "package_qa_check_dev"
181def package_qa_check_dev(path, name, d, elf, messages):
182 """
183 Check for ".so" library symlinks in non-dev packages
184 """
185
186 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 -0500187 package_qa_add_message(messages, "dev-so", "non -dev/-dbg/nativesdk- package contains symlink .so: %s path '%s'" % \
188 (name, package_qa_clean_path(path,d)))
189
190QAPATHTEST[dev-elf] = "package_qa_check_dev_elf"
191def package_qa_check_dev_elf(path, name, d, elf, messages):
192 """
193 Check that -dev doesn't contain real shared libraries. The test has to
194 check that the file is not a link and is an ELF object as some recipes
195 install link-time .so files that are linker scripts.
196 """
197 if name.endswith("-dev") and path.endswith(".so") and not os.path.islink(path) and elf:
198 package_qa_add_message(messages, "dev-elf", "-dev package contains non-symlink .so: %s path '%s'" % \
199 (name, package_qa_clean_path(path,d)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500200
201QAPATHTEST[staticdev] = "package_qa_check_staticdev"
202def package_qa_check_staticdev(path, name, d, elf, messages):
203 """
204 Check for ".a" library in non-staticdev packages
205 There are a number of exceptions to this rule, -pic packages can contain
206 static libraries, the _nonshared.a belong with their -dev packages and
207 libgcc.a, libgcov.a will be skipped in their packages
208 """
209
Andrew Geissler82c905d2020-04-13 13:39:40 -0500210 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 -0500211 package_qa_add_message(messages, "staticdev", "non -staticdev package contains static .a library: %s path '%s'" % \
212 (name, package_qa_clean_path(path,d)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500213
Andrew Geissler82c905d2020-04-13 13:39:40 -0500214QAPATHTEST[mime] = "package_qa_check_mime"
215def package_qa_check_mime(path, name, d, elf, messages):
216 """
217 Check if package installs mime types to /usr/share/mime/packages
218 while no inheriting mime.bbclass
219 """
220
221 if d.getVar("datadir") + "/mime/packages" in path and path.endswith('.xml') and not bb.data.inherits_class("mime", d):
222 package_qa_add_message(messages, "mime", "package contains mime types but does not inherit mime: %s path '%s'" % \
223 (name, package_qa_clean_path(path,d)))
224
225QAPATHTEST[mime-xdg] = "package_qa_check_mime_xdg"
226def package_qa_check_mime_xdg(path, name, d, elf, messages):
227 """
228 Check if package installs desktop file containing MimeType and requires
229 mime-types.bbclass to create /usr/share/applications/mimeinfo.cache
230 """
231
232 if d.getVar("datadir") + "/applications" in path and path.endswith('.desktop') and not bb.data.inherits_class("mime-xdg", d):
233 mime_type_found = False
234 try:
235 with open(path, 'r') as f:
236 for line in f.read().split('\n'):
237 if 'MimeType' in line:
238 mime_type_found = True
239 break;
240 except:
241 # At least libreoffice installs symlinks with absolute paths that are dangling here.
242 # We could implement some magic but for few (one) recipes it is not worth the effort so just warn:
243 wstr = "%s cannot open %s - is it a symlink with absolute path?\n" % (name, package_qa_clean_path(path,d))
244 wstr += "Please check if (linked) file contains key 'MimeType'.\n"
245 pkgname = name
246 if name == d.getVar('PN'):
247 pkgname = '${PN}'
248 wstr += "If yes: add \'inhert mime-xdg\' and \'MIME_XDG_PACKAGES += \"%s\"\' / if no add \'INSANE_SKIP_%s += \"mime-xdg\"\' to recipe." % (pkgname, pkgname)
249 package_qa_add_message(messages, "mime-xdg", wstr)
250 if mime_type_found:
251 package_qa_add_message(messages, "mime-xdg", "package contains desktop file with key 'MimeType' but does not inhert mime-xdg: %s path '%s'" % \
252 (name, package_qa_clean_path(path,d)))
253
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500254def package_qa_check_libdir(d):
255 """
256 Check for wrong library installation paths. For instance, catch
257 recipes installing /lib/bar.so when ${base_libdir}="lib32" or
258 installing in /usr/lib64 when ${libdir}="/usr/lib"
259 """
260 import re
261
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500262 pkgdest = d.getVar('PKGDEST')
263 base_libdir = d.getVar("base_libdir") + os.sep
264 libdir = d.getVar("libdir") + os.sep
265 libexecdir = d.getVar("libexecdir") + os.sep
266 exec_prefix = d.getVar("exec_prefix") + os.sep
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500267
268 messages = []
269
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500270 # The re's are purposely fuzzy, as some there are some .so.x.y.z files
271 # that don't follow the standard naming convention. It checks later
272 # that they are actual ELF files
Brad Bishop977dc1a2019-02-06 16:01:43 -0500273 lib_re = re.compile(r"^/lib.+\.so(\..+)?$")
274 exec_re = re.compile(r"^%s.*/lib.+\.so(\..+)?$" % exec_prefix)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500275
276 for root, dirs, files in os.walk(pkgdest):
277 if root == pkgdest:
278 # Skip subdirectories for any packages with libdir in INSANE_SKIP
279 skippackages = []
280 for package in dirs:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500281 if 'libdir' in (d.getVar('INSANE_SKIP_' + package) or "").split():
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500282 bb.note("Package %s skipping libdir QA test" % (package))
283 skippackages.append(package)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500284 elif d.getVar('PACKAGE_DEBUG_SPLIT_STYLE') == 'debug-file-directory' and package.endswith("-dbg"):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500285 bb.note("Package %s skipping libdir QA test for PACKAGE_DEBUG_SPLIT_STYLE equals debug-file-directory" % (package))
286 skippackages.append(package)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500287 for package in skippackages:
288 dirs.remove(package)
289 for file in files:
290 full_path = os.path.join(root, file)
291 rel_path = os.path.relpath(full_path, pkgdest)
292 if os.sep in rel_path:
293 package, rel_path = rel_path.split(os.sep, 1)
294 rel_path = os.sep + rel_path
295 if lib_re.match(rel_path):
296 if base_libdir not in rel_path:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500297 # make sure it's an actual ELF file
298 elf = oe.qa.ELFFile(full_path)
299 try:
300 elf.open()
301 messages.append("%s: found library in wrong location: %s" % (package, rel_path))
302 except (oe.qa.NotELFFileError):
303 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500304 if exec_re.match(rel_path):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500305 if libdir not in rel_path and libexecdir not in rel_path:
306 # make sure it's an actual ELF file
307 elf = oe.qa.ELFFile(full_path)
308 try:
309 elf.open()
310 messages.append("%s: found library in wrong location: %s" % (package, rel_path))
311 except (oe.qa.NotELFFileError):
312 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500313
314 if messages:
315 package_qa_handle_error("libdir", "\n".join(messages), d)
316
317QAPATHTEST[debug-files] = "package_qa_check_dbg"
318def package_qa_check_dbg(path, name, d, elf, messages):
319 """
320 Check for ".debug" files or directories outside of the dbg package
321 """
322
323 if not "-dbg" in name and not "-ptest" in name:
324 if '.debug' in path.split(os.path.sep):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500325 package_qa_add_message(messages, "debug-files", "non debug package contains .debug directory: %s path %s" % \
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500326 (name, package_qa_clean_path(path,d)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500327
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500328QAPATHTEST[arch] = "package_qa_check_arch"
329def package_qa_check_arch(path,name,d, elf, messages):
330 """
331 Check if archs are compatible
332 """
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800333 import re, oe.elf
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600334
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500335 if not elf:
336 return
337
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500338 target_os = d.getVar('TARGET_OS')
339 target_arch = d.getVar('TARGET_ARCH')
340 provides = d.getVar('PROVIDES')
341 bpn = d.getVar('BPN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500342
343 if target_arch == "allarch":
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500344 pn = d.getVar('PN')
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500345 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 -0500346 return
347
348 # FIXME: Cross package confuse this check, so just skip them
349 for s in ['cross', 'nativesdk', 'cross-canadian']:
350 if bb.data.inherits_class(s, d):
351 return
352
353 # avoid following links to /usr/bin (e.g. on udev builds)
354 # we will check the files pointed to anyway...
355 if os.path.islink(path):
356 return
357
358 #if this will throw an exception, then fix the dict above
359 (machine, osabi, abiversion, littleendian, bits) \
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800360 = oe.elf.machine_dict(d)[target_os][target_arch]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500361
362 # Check the architecture and endiannes of the binary
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600363 is_32 = (("virtual/kernel" in provides) or bb.data.inherits_class("module", d)) and \
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800364 (target_os == "linux-gnux32" or target_os == "linux-muslx32" or \
Brad Bishop977dc1a2019-02-06 16:01:43 -0500365 target_os == "linux-gnu_ilp32" or re.match(r'mips64.*32', d.getVar('DEFAULTTUNE')))
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800366 is_bpf = (oe.qa.elf_machine_to_string(elf.machine()) == "BPF")
367 if not ((machine == elf.machine()) or is_32 or is_bpf):
Andrew Geisslerb7d28612020-07-24 16:15:54 -0500368 package_qa_add_message(messages, "arch", "Architecture did not match (%s, expected %s) in %s" % \
369 (oe.qa.elf_machine_to_string(elf.machine()), oe.qa.elf_machine_to_string(machine), package_qa_clean_path(path, d, name)))
Brad Bishop19323692019-04-05 15:28:33 -0400370 elif not ((bits == elf.abiSize()) or is_32 or is_bpf):
Andrew Geisslerb7d28612020-07-24 16:15:54 -0500371 package_qa_add_message(messages, "arch", "Bit size did not match (%d, expected %d) in %s" % \
372 (elf.abiSize(), bits, package_qa_clean_path(path, d, name)))
Brad Bishop19323692019-04-05 15:28:33 -0400373 elif not ((littleendian == elf.isLittleEndian()) or is_bpf):
Andrew Geisslerb7d28612020-07-24 16:15:54 -0500374 package_qa_add_message(messages, "arch", "Endiannes did not match (%d, expected %d) in %s" % \
375 (elf.isLittleEndian(), littleendian, package_qa_clean_path(path,d, name)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500376
377QAPATHTEST[desktop] = "package_qa_check_desktop"
378def package_qa_check_desktop(path, name, d, elf, messages):
379 """
380 Run all desktop files through desktop-file-validate.
381 """
382 if path.endswith(".desktop"):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500383 desktop_file_validate = os.path.join(d.getVar('STAGING_BINDIR_NATIVE'),'desktop-file-validate')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500384 output = os.popen("%s %s" % (desktop_file_validate, path))
385 # This only produces output on errors
386 for l in output:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500387 package_qa_add_message(messages, "desktop", "Desktop file issue: " + l.strip())
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500388
389QAPATHTEST[textrel] = "package_qa_textrel"
390def package_qa_textrel(path, name, d, elf, messages):
391 """
392 Check if the binary contains relocations in .text
393 """
394
395 if not elf:
396 return
397
398 if os.path.islink(path):
399 return
400
401 phdrs = elf.run_objdump("-p", d)
402 sane = True
403
404 import re
Brad Bishop977dc1a2019-02-06 16:01:43 -0500405 textrel_re = re.compile(r"\s+TEXTREL\s+")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500406 for line in phdrs.split("\n"):
407 if textrel_re.match(line):
408 sane = False
Brad Bishop1d80a2e2019-11-15 16:35:03 -0500409 break
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500410
411 if not sane:
Brad Bishop1d80a2e2019-11-15 16:35:03 -0500412 path = package_qa_clean_path(path, d, name)
413 package_qa_add_message(messages, "textrel", "%s: ELF binary %s has relocations in .text" % (name, path))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500414
415QAPATHTEST[ldflags] = "package_qa_hash_style"
416def package_qa_hash_style(path, name, d, elf, messages):
417 """
418 Check if the binary has the right hash style...
419 """
420
421 if not elf:
422 return
423
424 if os.path.islink(path):
425 return
426
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500427 gnu_hash = "--hash-style=gnu" in d.getVar('LDFLAGS')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500428 if not gnu_hash:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500429 gnu_hash = "--hash-style=both" in d.getVar('LDFLAGS')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500430 if not gnu_hash:
431 return
432
433 sane = False
434 has_syms = False
435
436 phdrs = elf.run_objdump("-p", d)
437
438 # If this binary has symbols, we expect it to have GNU_HASH too.
439 for line in phdrs.split("\n"):
440 if "SYMTAB" in line:
441 has_syms = True
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500442 if "GNU_HASH" in line or "DT_MIPS_XHASH" in line:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500443 sane = True
Andrew Geissler82c905d2020-04-13 13:39:40 -0500444 if ("[mips32]" in line or "[mips64]" in line) and d.getVar('TCLIBC') == "musl":
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500445 sane = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500446 if has_syms and not sane:
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500447 path = package_qa_clean_path(path, d, name)
448 package_qa_add_message(messages, "ldflags", "File %s in package %s doesn't have GNU_HASH (didn't pass LDFLAGS?)" % (path, name))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500449
450
451QAPATHTEST[buildpaths] = "package_qa_check_buildpaths"
452def package_qa_check_buildpaths(path, name, d, elf, messages):
453 """
454 Check for build paths inside target files and error if not found in the whitelist
455 """
456 # Ignore .debug files, not interesting
457 if path.find(".debug") != -1:
458 return
459
460 # Ignore symlinks
461 if os.path.islink(path):
462 return
463
Brad Bishopd5ae7d92018-06-14 09:52:03 -0700464 tmpdir = bytes(d.getVar('TMPDIR'), encoding="utf-8")
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500465 with open(path, 'rb') as f:
Brad Bishopd5ae7d92018-06-14 09:52:03 -0700466 file_content = f.read()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500467 if tmpdir in file_content:
Brad Bishopf3fd2882019-06-21 08:06:37 -0400468 trimmed = path.replace(os.path.join (d.getVar("PKGDEST"), name), "")
469 package_qa_add_message(messages, "buildpaths", "File %s in package %s contains reference to TMPDIR" % (trimmed, name))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500470
471
472QAPATHTEST[xorg-driver-abi] = "package_qa_check_xorg_driver_abi"
473def package_qa_check_xorg_driver_abi(path, name, d, elf, messages):
474 """
475 Check that all packages containing Xorg drivers have ABI dependencies
476 """
477
478 # Skip dev, dbg or nativesdk packages
479 if name.endswith("-dev") or name.endswith("-dbg") or name.startswith("nativesdk-"):
480 return
481
482 driverdir = d.expand("${libdir}/xorg/modules/drivers/")
483 if driverdir in path and path.endswith(".so"):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500484 mlprefix = d.getVar('MLPREFIX') or ''
485 for rdep in bb.utils.explode_deps(d.getVar('RDEPENDS_' + name) or ""):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500486 if rdep.startswith("%sxorg-abi-" % mlprefix):
487 return
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500488 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 -0500489
490QAPATHTEST[infodir] = "package_qa_check_infodir"
491def package_qa_check_infodir(path, name, d, elf, messages):
492 """
493 Check that /usr/share/info/dir isn't shipped in a particular package
494 """
495 infodir = d.expand("${infodir}/dir")
496
497 if infodir in path:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500498 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 -0500499
500QAPATHTEST[symlink-to-sysroot] = "package_qa_check_symlink_to_sysroot"
501def package_qa_check_symlink_to_sysroot(path, name, d, elf, messages):
502 """
503 Check that the package doesn't contain any absolute symlinks to the sysroot.
504 """
505 if os.path.islink(path):
506 target = os.readlink(path)
507 if os.path.isabs(target):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500508 tmpdir = d.getVar('TMPDIR')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500509 if target.startswith(tmpdir):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500510 trimmed = path.replace(os.path.join (d.getVar("PKGDEST"), name), "")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500511 package_qa_add_message(messages, "symlink-to-sysroot", "Symlink %s in %s points to TMPDIR" % (trimmed, name))
512
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600513# Check license variables
514do_populate_lic[postfuncs] += "populate_lic_qa_checksum"
515python populate_lic_qa_checksum() {
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500516 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600517 Check for changes in the license files.
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500518 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500519 sane = True
520
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500521 lic_files = d.getVar('LIC_FILES_CHKSUM') or ''
522 lic = d.getVar('LICENSE')
523 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500524
525 if lic == "CLOSED":
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500526 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500527
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500528 if not lic_files and d.getVar('SRC_URI'):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800529 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 -0500530
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500531 srcdir = d.getVar('S')
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500532 corebase_licensefile = d.getVar('COREBASE') + "/LICENSE"
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500533 for url in lic_files.split():
534 try:
535 (type, host, path, user, pswd, parm) = bb.fetch.decodeurl(url)
536 except bb.fetch.MalformedUrl:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800537 sane &= package_qa_handle_error("license-checksum", pn + ": LIC_FILES_CHKSUM contains an invalid URL: " + url, d)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500538 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500539 srclicfile = os.path.join(srcdir, path)
540 if not os.path.isfile(srclicfile):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800541 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 -0500542 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500543
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500544 if (srclicfile == corebase_licensefile):
545 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")
546
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500547 recipemd5 = parm.get('md5', '')
548 beginline, endline = 0, 0
549 if 'beginline' in parm:
550 beginline = int(parm['beginline'])
551 if 'endline' in parm:
552 endline = int(parm['endline'])
553
554 if (not beginline) and (not endline):
555 md5chksum = bb.utils.md5_file(srclicfile)
Brad Bishop19323692019-04-05 15:28:33 -0400556 with open(srclicfile, 'r', errors='replace') as f:
557 license = f.read().splitlines()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500558 else:
Brad Bishop19323692019-04-05 15:28:33 -0400559 with open(srclicfile, 'rb') as f:
560 import hashlib
561 lineno = 0
562 license = []
563 m = hashlib.md5()
564 for line in f:
565 lineno += 1
566 if (lineno >= beginline):
567 if ((lineno <= endline) or not endline):
568 m.update(line)
569 license.append(line.decode('utf-8', errors='replace').rstrip())
570 else:
571 break
572 md5chksum = m.hexdigest()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500573 if recipemd5 == md5chksum:
574 bb.note (pn + ": md5 checksum matched for ", url)
575 else:
576 if recipemd5:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500577 msg = pn + ": The LIC_FILES_CHKSUM does not match for " + url
578 msg = msg + "\n" + pn + ": The new md5 checksum is " + md5chksum
Brad Bishop19323692019-04-05 15:28:33 -0400579 max_lines = int(d.getVar('QA_MAX_LICENSE_LINES') or 20)
580 if not license or license[-1] != '':
581 # Ensure that our license text ends with a line break
582 # (will be added with join() below).
583 license.append('')
584 remove = len(license) - max_lines
585 if remove > 0:
586 start = max_lines // 2
587 end = start + remove - 1
588 del license[start:end]
589 license.insert(start, '...')
590 msg = msg + "\n" + pn + ": Here is the selected license text:" + \
591 "\n" + \
592 "{:v^70}".format(" beginline=%d " % beginline if beginline else "") + \
593 "\n" + "\n".join(license) + \
594 "{:^^70}".format(" endline=%d " % endline if endline else "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500595 if beginline:
596 if endline:
597 srcfiledesc = "%s (lines %d through to %d)" % (srclicfile, beginline, endline)
598 else:
599 srcfiledesc = "%s (beginning on line %d)" % (srclicfile, beginline)
600 elif endline:
601 srcfiledesc = "%s (ending on line %d)" % (srclicfile, endline)
602 else:
603 srcfiledesc = srclicfile
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500604 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 -0500605
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500606 else:
607 msg = pn + ": LIC_FILES_CHKSUM is not specified for " + url
608 msg = msg + "\n" + pn + ": The md5 checksum is " + md5chksum
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800609 sane &= package_qa_handle_error("license-checksum", msg, d)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600610
611 if not sane:
612 bb.fatal("Fatal QA errors found, failing task.")
613}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500614
Brad Bishop19323692019-04-05 15:28:33 -0400615def qa_check_staged(path,d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500616 """
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500617 Check staged la and pc files for common problems like references to the work
618 directory.
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500619
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500620 As this is run after every stage we should be able to find the one
621 responsible for the errors easily even if we look at every .pc and .la file.
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500622 """
623
624 sane = True
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500625 tmpdir = d.getVar('TMPDIR')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500626 workdir = os.path.join(tmpdir, "work")
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500627 recipesysroot = d.getVar("RECIPE_SYSROOT")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500628
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500629 if bb.data.inherits_class("native", d) or bb.data.inherits_class("cross", d):
630 pkgconfigcheck = workdir
631 else:
632 pkgconfigcheck = tmpdir
633
Brad Bishop19323692019-04-05 15:28:33 -0400634 skip = (d.getVar('INSANE_SKIP') or "").split()
635 skip_la = False
636 if 'la' in skip:
637 bb.note("Recipe %s skipping qa checking: la" % d.getVar('PN'))
638 skip_la = True
639
640 skip_pkgconfig = False
641 if 'pkgconfig' in skip:
642 bb.note("Recipe %s skipping qa checking: pkgconfig" % d.getVar('PN'))
643 skip_pkgconfig = True
644
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500645 # find all .la and .pc files
646 # read the content
647 # and check for stuff that looks wrong
648 for root, dirs, files in os.walk(path):
649 for file in files:
650 path = os.path.join(root,file)
Brad Bishop19323692019-04-05 15:28:33 -0400651 if file.endswith(".la") and not skip_la:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500652 with open(path) as f:
653 file_content = f.read()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500654 file_content = file_content.replace(recipesysroot, "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500655 if workdir in file_content:
656 error_msg = "%s failed sanity test (workdir) in path %s" % (file,root)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800657 sane &= package_qa_handle_error("la", error_msg, d)
Brad Bishop19323692019-04-05 15:28:33 -0400658 elif file.endswith(".pc") and not skip_pkgconfig:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500659 with open(path) as f:
660 file_content = f.read()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500661 file_content = file_content.replace(recipesysroot, "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500662 if pkgconfigcheck in file_content:
663 error_msg = "%s failed sanity test (tmpdir) in path %s" % (file,root)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800664 sane &= package_qa_handle_error("pkgconfig", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500665
666 return sane
667
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500668# Run all package-wide warnfuncs and errorfuncs
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500669def package_qa_package(warnfuncs, errorfuncs, package, d):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500670 warnings = {}
671 errors = {}
672
673 for func in warnfuncs:
674 func(package, d, warnings)
675 for func in errorfuncs:
676 func(package, d, errors)
677
678 for w in warnings:
679 package_qa_handle_error(w, warnings[w], d)
680 for e in errors:
681 package_qa_handle_error(e, errors[e], d)
682
683 return len(errors) == 0
684
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500685# Run all recipe-wide warnfuncs and errorfuncs
686def package_qa_recipe(warnfuncs, errorfuncs, pn, d):
687 warnings = {}
688 errors = {}
689
690 for func in warnfuncs:
691 func(pn, d, warnings)
692 for func in errorfuncs:
693 func(pn, d, errors)
694
695 for w in warnings:
696 package_qa_handle_error(w, warnings[w], d)
697 for e in errors:
698 package_qa_handle_error(e, errors[e], d)
699
700 return len(errors) == 0
701
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500702# Walk over all files in a directory and call func
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500703def package_qa_walk(warnfuncs, errorfuncs, package, d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500704 import oe.qa
705
706 #if this will throw an exception, then fix the dict above
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500707 target_os = d.getVar('TARGET_OS')
708 target_arch = d.getVar('TARGET_ARCH')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500709
710 warnings = {}
711 errors = {}
712 for path in pkgfiles[package]:
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500713 elf = None
714 if os.path.isfile(path):
715 elf = oe.qa.ELFFile(path)
716 try:
717 elf.open()
718 except oe.qa.NotELFFileError:
719 elf = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500720 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
Andrew Geissler1e34c2d2020-05-29 16:02:59 -0500900QAPKGTEST[unlisted-pkg-lics] = "package_qa_check_unlisted_pkg_lics"
901def package_qa_check_unlisted_pkg_lics(package, d, messages):
902 """
903 Check that all licenses for a package are among the licenses for the recipe.
904 """
905 pkg_lics = d.getVar('LICENSE_' + package)
906 if not pkg_lics:
907 return True
908
909 recipe_lics_set = oe.license.list_licenses(d.getVar('LICENSE'))
910 unlisted = oe.license.list_licenses(pkg_lics) - recipe_lics_set
911 if not unlisted:
912 return True
913
914 package_qa_add_message(messages, "unlisted-pkg-lics",
915 "LICENSE_%s includes licenses (%s) that are not "
916 "listed in LICENSE" % (package, ' '.join(unlisted)))
917 return False
918
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500919def package_qa_check_encoding(keys, encode, d):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600920 def check_encoding(key, enc):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500921 sane = True
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500922 value = d.getVar(key)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500923 if value:
924 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600925 s = value.encode(enc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500926 except UnicodeDecodeError as e:
927 error_msg = "%s has non %s characters" % (key,enc)
928 sane = False
929 package_qa_handle_error("invalid-chars", error_msg, d)
930 return sane
931
932 for key in keys:
933 sane = check_encoding(key, encode)
934 if not sane:
935 break
936
937HOST_USER_UID := "${@os.getuid()}"
938HOST_USER_GID := "${@os.getgid()}"
939
940QAPATHTEST[host-user-contaminated] = "package_qa_check_host_user"
941def package_qa_check_host_user(path, name, d, elf, messages):
942 """Check for paths outside of /home which are owned by the user running bitbake."""
943
944 if not os.path.lexists(path):
945 return
946
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500947 dest = d.getVar('PKGDEST')
948 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500949 home = os.path.join(dest, 'home')
950 if path == home or path.startswith(home + os.sep):
951 return
952
953 try:
954 stat = os.lstat(path)
955 except OSError as exc:
956 import errno
957 if exc.errno != errno.ENOENT:
958 raise
959 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500960 check_uid = int(d.getVar('HOST_USER_UID'))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500961 if stat.st_uid == check_uid:
Brad Bishop96ff1982019-08-19 13:50:42 -0400962 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 -0500963 return False
964
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500965 check_gid = int(d.getVar('HOST_USER_GID'))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500966 if stat.st_gid == check_gid:
Brad Bishop96ff1982019-08-19 13:50:42 -0400967 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 -0500968 return False
969 return True
970
Brad Bishop15ae2502019-06-18 21:44:24 -0400971QARECIPETEST[src-uri-bad] = "package_qa_check_src_uri"
972def package_qa_check_src_uri(pn, d, messages):
973 import re
974
975 if "${PN}" in d.getVar("SRC_URI", False):
976 package_qa_handle_error("src-uri-bad", "%s: SRC_URI uses PN not BPN" % pn, d)
977
Andrew Geissler82c905d2020-04-13 13:39:40 -0500978 for url in d.getVar("SRC_URI").split():
979 if re.search(r"github\.com/.+/.+/archive/.+", url):
980 package_qa_handle_error("src-uri-bad", "%s: SRC_URI uses unstable GitHub archives" % pn, d)
Brad Bishop15ae2502019-06-18 21:44:24 -0400981
Andrew Geissler5a43b432020-06-13 10:46:56 -0500982QARECIPETEST[unhandled-features-check] = "package_qa_check_unhandled_features_check"
983def package_qa_check_unhandled_features_check(pn, d, messages):
984 if not bb.data.inherits_class('features_check', d):
985 var_set = False
986 for kind in ['DISTRO', 'MACHINE', 'COMBINED']:
987 for var in ['ANY_OF_' + kind + '_FEATURES', 'REQUIRED_' + kind + '_FEATURES', 'CONFLICT_' + kind + '_FEATURES']:
988 if d.getVar(var) is not None or d.overridedata.get(var) is not None:
989 var_set = True
990 if var_set:
991 package_qa_handle_error("unhandled-features-check", "%s: recipe doesn't inherit features_check" % pn, d)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500992
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500993QARECIPETEST[missing-update-alternatives] = "package_qa_check_missing_update_alternatives"
994def package_qa_check_missing_update_alternatives(pn, d, messages):
995 # Look at all packages and find out if any of those sets ALTERNATIVE variable
996 # without inheriting update-alternatives class
997 for pkg in (d.getVar('PACKAGES') or '').split():
998 if d.getVar('ALTERNATIVE_%s' % pkg) and not bb.data.inherits_class('update-alternatives', d):
999 package_qa_handle_error("missing-update-alternatives", "%s: recipe defines ALTERNATIVE_%s but doesn't inherit update-alternatives. This might fail during do_rootfs later!" % (pn, pkg), d)
1000
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001001# The PACKAGE FUNC to scan each package
1002python do_package_qa () {
1003 import subprocess
1004 import oe.packagedata
1005
1006 bb.note("DO PACKAGE QA")
1007
1008 bb.build.exec_func("read_subpackage_metadata", d)
1009
1010 # Check non UTF-8 characters on recipe's metadata
1011 package_qa_check_encoding(['DESCRIPTION', 'SUMMARY', 'LICENSE', 'SECTION'], 'utf-8', d)
1012
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001013 logdir = d.getVar('T')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001014 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001015
1016 # Check the compile log for host contamination
1017 compilelog = os.path.join(logdir,"log.do_compile")
1018
1019 if os.path.exists(compilelog):
1020 statement = "grep -e 'CROSS COMPILE Badness:' -e 'is unsafe for cross-compilation' %s > /dev/null" % compilelog
1021 if subprocess.call(statement, shell=True) == 0:
1022 msg = "%s: The compile log indicates that host include and/or library paths were used.\n \
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001023 Please check the log '%s' for more information." % (pn, compilelog)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001024 package_qa_handle_error("compile-host-path", msg, d)
1025
1026 # Check the install log for host contamination
1027 installlog = os.path.join(logdir,"log.do_install")
1028
1029 if os.path.exists(installlog):
1030 statement = "grep -e 'CROSS COMPILE Badness:' -e 'is unsafe for cross-compilation' %s > /dev/null" % installlog
1031 if subprocess.call(statement, shell=True) == 0:
1032 msg = "%s: The install log indicates that host include and/or library paths were used.\n \
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001033 Please check the log '%s' for more information." % (pn, installlog)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001034 package_qa_handle_error("install-host-path", msg, d)
1035
1036 # Scan the packages...
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001037 pkgdest = d.getVar('PKGDEST')
1038 packages = set((d.getVar('PACKAGES') or '').split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001039
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001040 global pkgfiles
1041 pkgfiles = {}
1042 for pkg in packages:
1043 pkgfiles[pkg] = []
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001044 pkgdir = os.path.join(pkgdest, pkg)
1045 for walkroot, dirs, files in os.walk(pkgdir):
1046 # Don't walk into top-level CONTROL or DEBIAN directories as these
1047 # are temporary directories created by do_package.
1048 if walkroot == pkgdir:
1049 for control in ("CONTROL", "DEBIAN"):
1050 if control in dirs:
1051 dirs.remove(control)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001052 for file in files:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001053 pkgfiles[pkg].append(os.path.join(walkroot, file))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001054
1055 # no packages should be scanned
1056 if not packages:
1057 return
1058
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001059 import re
1060 # The package name matches the [a-z0-9.+-]+ regular expression
Brad Bishop977dc1a2019-02-06 16:01:43 -05001061 pkgname_pattern = re.compile(r"^[a-z0-9.+-]+$")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001062
1063 taskdepdata = d.getVar("BB_TASKDEPDATA", False)
1064 taskdeps = set()
1065 for dep in taskdepdata:
1066 taskdeps.add(taskdepdata[dep][0])
1067
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001068 def parse_test_matrix(matrix_name):
1069 testmatrix = d.getVarFlags(matrix_name) or {}
1070 g = globals()
1071 warnchecks = []
1072 for w in (d.getVar("WARN_QA") or "").split():
1073 if w in skip:
1074 continue
1075 if w in testmatrix and testmatrix[w] in g:
1076 warnchecks.append(g[testmatrix[w]])
1077
1078 errorchecks = []
1079 for e in (d.getVar("ERROR_QA") or "").split():
1080 if e in skip:
1081 continue
1082 if e in testmatrix and testmatrix[e] in g:
1083 errorchecks.append(g[testmatrix[e]])
1084 return warnchecks, errorchecks
1085
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001086 for package in packages:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001087 skip = set((d.getVar('INSANE_SKIP') or "").split() +
1088 (d.getVar('INSANE_SKIP_' + package) or "").split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001089 if skip:
1090 bb.note("Package %s skipping QA tests: %s" % (package, str(skip)))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001091
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001092 bb.note("Checking Package: %s" % package)
1093 # Check package name
1094 if not pkgname_pattern.match(package):
1095 package_qa_handle_error("pkgname",
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001096 "%s doesn't match the [a-z0-9.+-]+ regex" % package, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001097
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001098 warn_checks, error_checks = parse_test_matrix("QAPATHTEST")
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001099 package_qa_walk(warn_checks, error_checks, package, d)
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001100
1101 warn_checks, error_checks = parse_test_matrix("QAPKGTEST")
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001102 package_qa_package(warn_checks, error_checks, package, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001103
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001104 package_qa_check_rdepends(package, pkgdest, skip, taskdeps, packages, d)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001105 package_qa_check_deps(package, pkgdest, d)
1106
1107 warn_checks, error_checks = parse_test_matrix("QARECIPETEST")
1108 package_qa_recipe(warn_checks, error_checks, pn, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001109
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001110 if 'libdir' in d.getVar("ALL_QA").split():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001111 package_qa_check_libdir(d)
1112
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001113 qa_sane = d.getVar("QA_SANE")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001114 if not qa_sane:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001115 bb.fatal("QA run found fatal errors. Please consider fixing them.")
1116 bb.note("DONE with PACKAGE QA")
1117}
1118
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001119# binutils is used for most checks, so need to set as dependency
1120# POPULATESYSROOTDEPS is defined in staging class.
1121do_package_qa[depends] += "${POPULATESYSROOTDEPS}"
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001122do_package_qa[vardepsexclude] = "BB_TASKDEPDATA"
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001123do_package_qa[rdeptask] = "do_packagedata"
1124addtask do_package_qa after do_packagedata do_package before do_build
1125
Brad Bishop19323692019-04-05 15:28:33 -04001126# Add the package specific INSANE_SKIPs to the sstate dependencies
1127python() {
1128 pkgs = (d.getVar('PACKAGES') or '').split()
1129 for pkg in pkgs:
1130 d.appendVarFlag("do_package_qa", "vardeps", " INSANE_SKIP_{}".format(pkg))
1131}
1132
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001133SSTATETASKS += "do_package_qa"
1134do_package_qa[sstate-inputdirs] = ""
1135do_package_qa[sstate-outputdirs] = ""
1136python do_package_qa_setscene () {
1137 sstate_setscene(d)
1138}
1139addtask do_package_qa_setscene
1140
1141python do_qa_staging() {
1142 bb.note("QA checking staging")
Brad Bishop19323692019-04-05 15:28:33 -04001143 if not qa_check_staged(d.expand('${SYSROOT_DESTDIR}${libdir}'), d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001144 bb.fatal("QA staging was broken by the package built above")
1145}
1146
Brad Bishopd89cb5f2019-04-10 09:02:41 -04001147python do_qa_patch() {
1148 import subprocess
1149
1150 ###########################################################################
1151 # Check patch.log for fuzz warnings
1152 #
1153 # Further information on why we check for patch fuzz warnings:
1154 # http://lists.openembedded.org/pipermail/openembedded-core/2018-March/148675.html
1155 # https://bugzilla.yoctoproject.org/show_bug.cgi?id=10450
1156 ###########################################################################
1157
1158 logdir = d.getVar('T')
1159 patchlog = os.path.join(logdir,"log.do_patch")
1160
1161 if os.path.exists(patchlog):
1162 fuzzheader = '--- Patch fuzz start ---'
1163 fuzzfooter = '--- Patch fuzz end ---'
1164 statement = "grep -e '%s' %s > /dev/null" % (fuzzheader, patchlog)
1165 if subprocess.call(statement, shell=True) == 0:
1166 msg = "Fuzz detected:\n\n"
1167 fuzzmsg = ""
1168 inFuzzInfo = False
1169 f = open(patchlog, "r")
1170 for line in f:
1171 if fuzzheader in line:
1172 inFuzzInfo = True
1173 fuzzmsg = ""
1174 elif fuzzfooter in line:
1175 fuzzmsg = fuzzmsg.replace('\n\n', '\n')
1176 msg += fuzzmsg
1177 msg += "\n"
1178 inFuzzInfo = False
1179 elif inFuzzInfo and not 'Now at patch' in line:
1180 fuzzmsg += line
1181 f.close()
1182 msg += "The context lines in the patches can be updated with devtool:\n"
1183 msg += "\n"
1184 msg += " devtool modify %s\n" % d.getVar('PN')
1185 msg += " devtool finish --force-patch-refresh %s <layer_path>\n\n" % d.getVar('PN')
1186 msg += "Don't forget to review changes done by devtool!\n"
1187 if 'patch-fuzz' in d.getVar('ERROR_QA'):
1188 bb.error(msg)
1189 elif 'patch-fuzz' in d.getVar('WARN_QA'):
1190 bb.warn(msg)
1191 msg = "Patch log indicates that patches do not apply cleanly."
1192 package_qa_handle_error("patch-fuzz", msg, d)
1193}
1194
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001195python do_qa_configure() {
1196 import subprocess
1197
1198 ###########################################################################
1199 # Check config.log for cross compile issues
1200 ###########################################################################
1201
1202 configs = []
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001203 workdir = d.getVar('WORKDIR')
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001204
Brad Bishop19323692019-04-05 15:28:33 -04001205 skip = (d.getVar('INSANE_SKIP') or "").split()
1206 skip_configure_unsafe = False
1207 if 'configure-unsafe' in skip:
1208 bb.note("Recipe %s skipping qa checking: configure-unsafe" % d.getVar('PN'))
1209 skip_configure_unsafe = True
1210
1211 if bb.data.inherits_class('autotools', d) and not skip_configure_unsafe:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001212 bb.note("Checking autotools environment for common misconfiguration")
1213 for root, dirs, files in os.walk(workdir):
1214 statement = "grep -q -F -e 'CROSS COMPILE Badness:' -e 'is unsafe for cross-compilation' %s" % \
1215 os.path.join(root,"config.log")
1216 if "config.log" in files:
1217 if subprocess.call(statement, shell=True) == 0:
Brad Bishop19323692019-04-05 15:28:33 -04001218 error_msg = """This autoconf log indicates errors, it looked at host include and/or library paths while determining system capabilities.
1219Rerun configure task after fixing this."""
1220 package_qa_handle_error("configure-unsafe", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001221
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001222 if "configure.ac" in files:
1223 configs.append(os.path.join(root,"configure.ac"))
1224 if "configure.in" in files:
1225 configs.append(os.path.join(root, "configure.in"))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001226
1227 ###########################################################################
1228 # Check gettext configuration and dependencies are correct
1229 ###########################################################################
1230
Brad Bishop19323692019-04-05 15:28:33 -04001231 skip_configure_gettext = False
1232 if 'configure-gettext' in skip:
1233 bb.note("Recipe %s skipping qa checking: configure-gettext" % d.getVar('PN'))
1234 skip_configure_gettext = True
1235
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001236 cnf = d.getVar('EXTRA_OECONF') or ""
Brad Bishop19323692019-04-05 15:28:33 -04001237 if not ("gettext" in d.getVar('P') or "gcc-runtime" in d.getVar('P') or \
1238 "--disable-nls" in cnf or skip_configure_gettext):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001239 ml = d.getVar("MLPREFIX") or ""
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001240 if bb.data.inherits_class('cross-canadian', d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001241 gt = "nativesdk-gettext"
1242 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001243 gt = "gettext-native"
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001244 deps = bb.utils.explode_deps(d.getVar('DEPENDS') or "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001245 if gt not in deps:
1246 for config in configs:
1247 gnu = "grep \"^[[:space:]]*AM_GNU_GETTEXT\" %s >/dev/null" % config
1248 if subprocess.call(gnu, shell=True) == 0:
Brad Bishopd89cb5f2019-04-10 09:02:41 -04001249 error_msg = "AM_GNU_GETTEXT used but no inherit gettext"
Brad Bishop19323692019-04-05 15:28:33 -04001250 package_qa_handle_error("configure-gettext", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001251
1252 ###########################################################################
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001253 # Check unrecognised configure options (with a white list)
1254 ###########################################################################
Brad Bishopc342db32019-05-15 21:57:59 -04001255 if bb.data.inherits_class("autotools", d) or bb.data.inherits_class("meson", d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001256 bb.note("Checking configure output for unrecognised options")
1257 try:
Brad Bishopc342db32019-05-15 21:57:59 -04001258 if bb.data.inherits_class("autotools", d):
1259 flag = "WARNING: unrecognized options:"
1260 log = os.path.join(d.getVar('B'), 'config.log')
1261 if bb.data.inherits_class("meson", d):
1262 flag = "WARNING: Unknown options:"
1263 log = os.path.join(d.getVar('T'), 'log.do_configure')
1264 output = subprocess.check_output(['grep', '-F', flag, log]).decode("utf-8").replace(', ', ' ').replace('"', '')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001265 options = set()
1266 for line in output.splitlines():
1267 options |= set(line.partition(flag)[2].split())
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001268 whitelist = set(d.getVar("UNKNOWN_CONFIGURE_WHITELIST").split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001269 options -= whitelist
1270 if options:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001271 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001272 error_msg = pn + ": configure was passed unrecognised options: " + " ".join(options)
1273 package_qa_handle_error("unknown-configure-option", error_msg, d)
1274 except subprocess.CalledProcessError:
1275 pass
1276
1277 # Check invalid PACKAGECONFIG
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001278 pkgconfig = (d.getVar("PACKAGECONFIG") or "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001279 if pkgconfig:
1280 pkgconfigflags = d.getVarFlags("PACKAGECONFIG") or {}
1281 for pconfig in pkgconfig:
1282 if pconfig not in pkgconfigflags:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001283 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001284 error_msg = "%s: invalid PACKAGECONFIG: %s" % (pn, pconfig)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001285 package_qa_handle_error("invalid-packageconfig", error_msg, d)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001286
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001287 qa_sane = d.getVar("QA_SANE")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001288 if not qa_sane:
1289 bb.fatal("Fatal QA errors found, failing task.")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001290}
1291
1292python do_qa_unpack() {
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001293 src_uri = d.getVar('SRC_URI')
1294 s_dir = d.getVar('S')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001295 if src_uri and not os.path.exists(s_dir):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001296 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 -05001297}
1298
1299# The Staging Func, to check all staging
1300#addtask qa_staging after do_populate_sysroot before do_build
1301do_populate_sysroot[postfuncs] += "do_qa_staging "
1302
Brad Bishopd89cb5f2019-04-10 09:02:41 -04001303# Check for patch fuzz
1304do_patch[postfuncs] += "do_qa_patch "
1305
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001306# Check broken config.log files, for packages requiring Gettext which
1307# don't have it in DEPENDS.
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001308#addtask qa_configure after do_configure before do_compile
1309do_configure[postfuncs] += "do_qa_configure "
1310
1311# Check does S exist.
1312do_unpack[postfuncs] += "do_qa_unpack"
1313
1314python () {
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001315 import re
1316
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001317 tests = d.getVar('ALL_QA').split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001318 if "desktop" in tests:
1319 d.appendVar("PACKAGE_DEPENDS", " desktop-file-utils-native")
1320
1321 ###########################################################################
1322 # Check various variables
1323 ###########################################################################
1324
1325 # Checking ${FILESEXTRAPATHS}
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001326 extrapaths = (d.getVar("FILESEXTRAPATHS") or "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001327 if '__default' not in extrapaths.split(":"):
1328 msg = "FILESEXTRAPATHS-variable, must always use _prepend (or _append)\n"
1329 msg += "type of assignment, and don't forget the colon.\n"
1330 msg += "Please assign it with the format of:\n"
1331 msg += " FILESEXTRAPATHS_append := \":${THISDIR}/Your_Files_Path\" or\n"
1332 msg += " FILESEXTRAPATHS_prepend := \"${THISDIR}/Your_Files_Path:\"\n"
1333 msg += "in your bbappend file\n\n"
1334 msg += "Your incorrect assignment is:\n"
1335 msg += "%s\n" % extrapaths
1336 bb.warn(msg)
1337
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001338 overrides = d.getVar('OVERRIDES').split(':')
1339 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001340 if pn in overrides:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001341 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 -05001342 package_qa_handle_error("pn-overrides", msg, d)
Brad Bishop977dc1a2019-02-06 16:01:43 -05001343 prog = re.compile(r'[A-Z]')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001344 if prog.search(pn):
1345 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 -05001346
Brad Bishop08902b02019-08-20 09:16:51 -04001347 # Some people mistakenly use DEPENDS_${PN} instead of DEPENDS and wonder
1348 # why it doesn't work.
1349 if (d.getVar(d.expand('DEPENDS_${PN}'))):
1350 package_qa_handle_error("pkgvarcheck", "recipe uses DEPENDS_${PN}, should use DEPENDS", d)
1351
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001352 issues = []
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001353 if (d.getVar('PACKAGES') or "").split():
1354 for dep in (d.getVar('QADEPENDS') or "").split():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001355 d.appendVarFlag('do_package_qa', 'depends', " %s:do_populate_sysroot" % dep)
1356 for var in 'RDEPENDS', 'RRECOMMENDS', 'RSUGGESTS', 'RCONFLICTS', 'RPROVIDES', 'RREPLACES', 'FILES', 'pkg_preinst', 'pkg_postinst', 'pkg_prerm', 'pkg_postrm', 'ALLOW_EMPTY':
1357 if d.getVar(var, False):
1358 issues.append(var)
1359
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001360 fakeroot_tests = d.getVar('FAKEROOT_QA').split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001361 if set(tests) & set(fakeroot_tests):
1362 d.setVarFlag('do_package_qa', 'fakeroot', '1')
1363 d.appendVarFlag('do_package_qa', 'depends', ' virtual/fakeroot-native:do_populate_sysroot')
1364 else:
1365 d.setVarFlag('do_package_qa', 'rdeptask', '')
1366 for i in issues:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001367 package_qa_handle_error("pkgvarcheck", "%s: Variable %s is set as not being package specific, please fix this." % (d.getVar("FILE"), i), d)
1368 qa_sane = d.getVar("QA_SANE")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001369 if not qa_sane:
1370 bb.fatal("Fatal QA errors found, failing task.")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001371}