blob: 1e2f1b768ab91d6409596c60396a1626b50d26d6 [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 -050021# Elect whether a given type of error is a warning or error, they may
22# have been set by other files.
Andrew Geisslerc182c622020-05-15 14:13:32 -050023WARN_QA ?= " libdir xorg-driver-abi \
24 textrel incompatible-license files-invalid \
25 infodir build-deps src-uri-bad symlink-to-sysroot multilib \
Brad Bishopd89cb5f2019-04-10 09:02:41 -040026 invalid-packageconfig host-user-contaminated uppercase-pn patch-fuzz \
Andrew Geissler5a43b432020-06-13 10:46:56 -050027 mime mime-xdg unlisted-pkg-lics unhandled-features-check \
Andrew Geisslerd159c7f2021-09-02 21:05:58 -050028 missing-update-alternatives native-last missing-ptest \
Andrew Geisslereff27472021-10-29 15:35:00 -050029 license-exists license-no-generic license-syntax license-format \
30 license-incompatible license-file-missing \
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
Patrick Williams213cb262021-08-07 19:21:33 -050043ERROR_QA:append = "${@bb.utils.contains('DISTRO_FEATURES', 'usrmerge', ' usrmerge', '', d)}"
Brad Bishopd7bf8c12018-02-25 22:55:05 -050044
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
Andrew Geissler82c905d2020-04-13 13:39:40 -050062QAPATHTEST[shebang-size] = "package_qa_check_shebang_size"
63def package_qa_check_shebang_size(path, name, d, elf, messages):
Andrew Geisslerd1e89492021-02-12 15:35:20 -060064 import stat
65 if os.path.islink(path) or stat.S_ISFIFO(os.stat(path).st_mode) or elf:
Andrew Geissler82c905d2020-04-13 13:39:40 -050066 return
67
68 try:
69 with open(path, 'rb') as f:
70 stanza = f.readline(130)
71 except IOError:
72 return
73
74 if stanza.startswith(b'#!'):
75 #Shebang not found
76 try:
77 stanza = stanza.decode("utf-8")
78 except UnicodeDecodeError:
79 #If it is not a text file, it is not a script
80 return
81
82 if len(stanza) > 129:
Andrew Geisslereff27472021-10-29 15:35:00 -050083 oe.qa.add_message(messages, "shebang-size", "%s: %s maximum shebang size exceeded, the maximum size is 128." % (name, package_qa_clean_path(path, d)))
Andrew Geissler82c905d2020-04-13 13:39:40 -050084 return
85
Patrick Williamsc124f4f2015-09-15 14:41:29 -050086QAPATHTEST[libexec] = "package_qa_check_libexec"
87def package_qa_check_libexec(path,name, d, elf, messages):
88
89 # Skip the case where the default is explicitly /usr/libexec
Brad Bishop6e60e8b2018-02-01 10:27:11 -050090 libexec = d.getVar('libexecdir')
Patrick Williamsc124f4f2015-09-15 14:41:29 -050091 if libexec == "/usr/libexec":
92 return True
93
94 if 'libexec' in path.split(os.path.sep):
Andrew Geisslereff27472021-10-29 15:35:00 -050095 oe.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 -050096 return False
97
98 return True
99
100QAPATHTEST[rpaths] = "package_qa_check_rpath"
101def package_qa_check_rpath(file,name, d, elf, messages):
102 """
103 Check for dangerous RPATHs
104 """
105 if not elf:
106 return
107
108 if os.path.islink(file):
109 return
110
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500111 bad_dirs = [d.getVar('BASE_WORKDIR'), d.getVar('STAGING_DIR_TARGET')]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500112
113 phdrs = elf.run_objdump("-p", d)
114
115 import re
Brad Bishop977dc1a2019-02-06 16:01:43 -0500116 rpath_re = re.compile(r"\s+RPATH\s+(.*)")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500117 for line in phdrs.split("\n"):
118 m = rpath_re.match(line)
119 if m:
120 rpath = m.group(1)
121 for dir in bad_dirs:
122 if dir in rpath:
Andrew Geisslereff27472021-10-29 15:35:00 -0500123 oe.qa.add_message(messages, "rpaths", "package %s contains bad RPATH %s in file %s" % (name, rpath, file))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500124
125QAPATHTEST[useless-rpaths] = "package_qa_check_useless_rpaths"
126def package_qa_check_useless_rpaths(file, name, d, elf, messages):
127 """
128 Check for RPATHs that are useless but not dangerous
129 """
130 def rpath_eq(a, b):
131 return os.path.normpath(a) == os.path.normpath(b)
132
133 if not elf:
134 return
135
136 if os.path.islink(file):
137 return
138
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500139 libdir = d.getVar("libdir")
140 base_libdir = d.getVar("base_libdir")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500141
142 phdrs = elf.run_objdump("-p", d)
143
144 import re
Brad Bishop977dc1a2019-02-06 16:01:43 -0500145 rpath_re = re.compile(r"\s+RPATH\s+(.*)")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500146 for line in phdrs.split("\n"):
147 m = rpath_re.match(line)
148 if m:
149 rpath = m.group(1)
150 if rpath_eq(rpath, libdir) or rpath_eq(rpath, base_libdir):
151 # The dynamic linker searches both these places anyway. There is no point in
152 # looking there again.
Andrew Geisslereff27472021-10-29 15:35:00 -0500153 oe.qa.add_message(messages, "useless-rpaths", "%s: %s contains probably-redundant RPATH %s" % (name, package_qa_clean_path(file, d, name), rpath))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500154
155QAPATHTEST[dev-so] = "package_qa_check_dev"
156def package_qa_check_dev(path, name, d, elf, messages):
157 """
158 Check for ".so" library symlinks in non-dev packages
159 """
160
161 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):
Andrew Geisslereff27472021-10-29 15:35:00 -0500162 oe.qa.add_message(messages, "dev-so", "non -dev/-dbg/nativesdk- package %s contains symlink .so '%s'" % \
Andrew Geisslerc926e172021-05-07 16:11:35 -0500163 (name, package_qa_clean_path(path, d, name)))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500164
165QAPATHTEST[dev-elf] = "package_qa_check_dev_elf"
166def package_qa_check_dev_elf(path, name, d, elf, messages):
167 """
168 Check that -dev doesn't contain real shared libraries. The test has to
169 check that the file is not a link and is an ELF object as some recipes
170 install link-time .so files that are linker scripts.
171 """
172 if name.endswith("-dev") and path.endswith(".so") and not os.path.islink(path) and elf:
Andrew Geisslereff27472021-10-29 15:35:00 -0500173 oe.qa.add_message(messages, "dev-elf", "-dev package %s contains non-symlink .so '%s'" % \
Andrew Geisslerc926e172021-05-07 16:11:35 -0500174 (name, package_qa_clean_path(path, d, name)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500175
176QAPATHTEST[staticdev] = "package_qa_check_staticdev"
177def package_qa_check_staticdev(path, name, d, elf, messages):
178 """
179 Check for ".a" library in non-staticdev packages
180 There are a number of exceptions to this rule, -pic packages can contain
181 static libraries, the _nonshared.a belong with their -dev packages and
182 libgcc.a, libgcov.a will be skipped in their packages
183 """
184
Andrew Geissler82c905d2020-04-13 13:39:40 -0500185 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:
Andrew Geisslereff27472021-10-29 15:35:00 -0500186 oe.qa.add_message(messages, "staticdev", "non -staticdev package contains static .a library: %s path '%s'" % \
Andrew Geisslerc926e172021-05-07 16:11:35 -0500187 (name, package_qa_clean_path(path,d, name)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500188
Andrew Geissler82c905d2020-04-13 13:39:40 -0500189QAPATHTEST[mime] = "package_qa_check_mime"
190def package_qa_check_mime(path, name, d, elf, messages):
191 """
192 Check if package installs mime types to /usr/share/mime/packages
193 while no inheriting mime.bbclass
194 """
195
196 if d.getVar("datadir") + "/mime/packages" in path and path.endswith('.xml') and not bb.data.inherits_class("mime", d):
Andrew Geisslereff27472021-10-29 15:35:00 -0500197 oe.qa.add_message(messages, "mime", "package contains mime types but does not inherit mime: %s path '%s'" % \
Andrew Geissler82c905d2020-04-13 13:39:40 -0500198 (name, package_qa_clean_path(path,d)))
199
200QAPATHTEST[mime-xdg] = "package_qa_check_mime_xdg"
201def package_qa_check_mime_xdg(path, name, d, elf, messages):
202 """
203 Check if package installs desktop file containing MimeType and requires
204 mime-types.bbclass to create /usr/share/applications/mimeinfo.cache
205 """
206
207 if d.getVar("datadir") + "/applications" in path and path.endswith('.desktop') and not bb.data.inherits_class("mime-xdg", d):
208 mime_type_found = False
209 try:
210 with open(path, 'r') as f:
211 for line in f.read().split('\n'):
212 if 'MimeType' in line:
213 mime_type_found = True
214 break;
215 except:
216 # At least libreoffice installs symlinks with absolute paths that are dangling here.
217 # We could implement some magic but for few (one) recipes it is not worth the effort so just warn:
218 wstr = "%s cannot open %s - is it a symlink with absolute path?\n" % (name, package_qa_clean_path(path,d))
219 wstr += "Please check if (linked) file contains key 'MimeType'.\n"
220 pkgname = name
221 if name == d.getVar('PN'):
222 pkgname = '${PN}'
Patrick Williams213cb262021-08-07 19:21:33 -0500223 wstr += "If yes: add \'inhert mime-xdg\' and \'MIME_XDG_PACKAGES += \"%s\"\' / if no add \'INSANE_SKIP:%s += \"mime-xdg\"\' to recipe." % (pkgname, pkgname)
Andrew Geisslereff27472021-10-29 15:35:00 -0500224 oe.qa.add_message(messages, "mime-xdg", wstr)
Andrew Geissler82c905d2020-04-13 13:39:40 -0500225 if mime_type_found:
Andrew Geisslereff27472021-10-29 15:35:00 -0500226 oe.qa.add_message(messages, "mime-xdg", "package contains desktop file with key 'MimeType' but does not inhert mime-xdg: %s path '%s'" % \
Andrew Geissler82c905d2020-04-13 13:39:40 -0500227 (name, package_qa_clean_path(path,d)))
228
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500229def package_qa_check_libdir(d):
230 """
231 Check for wrong library installation paths. For instance, catch
232 recipes installing /lib/bar.so when ${base_libdir}="lib32" or
233 installing in /usr/lib64 when ${libdir}="/usr/lib"
234 """
235 import re
236
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500237 pkgdest = d.getVar('PKGDEST')
238 base_libdir = d.getVar("base_libdir") + os.sep
239 libdir = d.getVar("libdir") + os.sep
240 libexecdir = d.getVar("libexecdir") + os.sep
241 exec_prefix = d.getVar("exec_prefix") + os.sep
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500242
243 messages = []
244
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500245 # The re's are purposely fuzzy, as some there are some .so.x.y.z files
246 # that don't follow the standard naming convention. It checks later
247 # that they are actual ELF files
Brad Bishop977dc1a2019-02-06 16:01:43 -0500248 lib_re = re.compile(r"^/lib.+\.so(\..+)?$")
249 exec_re = re.compile(r"^%s.*/lib.+\.so(\..+)?$" % exec_prefix)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500250
251 for root, dirs, files in os.walk(pkgdest):
252 if root == pkgdest:
253 # Skip subdirectories for any packages with libdir in INSANE_SKIP
254 skippackages = []
255 for package in dirs:
Patrick Williams213cb262021-08-07 19:21:33 -0500256 if 'libdir' in (d.getVar('INSANE_SKIP:' + package) or "").split():
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500257 bb.note("Package %s skipping libdir QA test" % (package))
258 skippackages.append(package)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500259 elif d.getVar('PACKAGE_DEBUG_SPLIT_STYLE') == 'debug-file-directory' and package.endswith("-dbg"):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500260 bb.note("Package %s skipping libdir QA test for PACKAGE_DEBUG_SPLIT_STYLE equals debug-file-directory" % (package))
261 skippackages.append(package)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500262 for package in skippackages:
263 dirs.remove(package)
264 for file in files:
265 full_path = os.path.join(root, file)
266 rel_path = os.path.relpath(full_path, pkgdest)
267 if os.sep in rel_path:
268 package, rel_path = rel_path.split(os.sep, 1)
269 rel_path = os.sep + rel_path
270 if lib_re.match(rel_path):
271 if base_libdir not in rel_path:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500272 # make sure it's an actual ELF file
273 elf = oe.qa.ELFFile(full_path)
274 try:
275 elf.open()
276 messages.append("%s: found library in wrong location: %s" % (package, rel_path))
277 except (oe.qa.NotELFFileError):
278 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500279 if exec_re.match(rel_path):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500280 if libdir not in rel_path and libexecdir not in rel_path:
281 # make sure it's an actual ELF file
282 elf = oe.qa.ELFFile(full_path)
283 try:
284 elf.open()
285 messages.append("%s: found library in wrong location: %s" % (package, rel_path))
286 except (oe.qa.NotELFFileError):
287 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500288
289 if messages:
Andrew Geisslereff27472021-10-29 15:35:00 -0500290 oe.qa.handle_error("libdir", "\n".join(messages), d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500291
292QAPATHTEST[debug-files] = "package_qa_check_dbg"
293def package_qa_check_dbg(path, name, d, elf, messages):
294 """
295 Check for ".debug" files or directories outside of the dbg package
296 """
297
298 if not "-dbg" in name and not "-ptest" in name:
299 if '.debug' in path.split(os.path.sep):
Andrew Geisslereff27472021-10-29 15:35:00 -0500300 oe.qa.add_message(messages, "debug-files", "non debug package contains .debug directory: %s path %s" % \
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500301 (name, package_qa_clean_path(path,d)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500302
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500303QAPATHTEST[arch] = "package_qa_check_arch"
304def package_qa_check_arch(path,name,d, elf, messages):
305 """
306 Check if archs are compatible
307 """
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800308 import re, oe.elf
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600309
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500310 if not elf:
311 return
312
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500313 target_os = d.getVar('TARGET_OS')
314 target_arch = d.getVar('TARGET_ARCH')
315 provides = d.getVar('PROVIDES')
316 bpn = d.getVar('BPN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500317
318 if target_arch == "allarch":
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500319 pn = d.getVar('PN')
Andrew Geisslereff27472021-10-29 15:35:00 -0500320 oe.qa.add_message(messages, "arch", pn + ": Recipe inherits the allarch class, but has packaged architecture-specific binaries")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500321 return
322
323 # FIXME: Cross package confuse this check, so just skip them
324 for s in ['cross', 'nativesdk', 'cross-canadian']:
325 if bb.data.inherits_class(s, d):
326 return
327
328 # avoid following links to /usr/bin (e.g. on udev builds)
329 # we will check the files pointed to anyway...
330 if os.path.islink(path):
331 return
332
333 #if this will throw an exception, then fix the dict above
334 (machine, osabi, abiversion, littleendian, bits) \
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800335 = oe.elf.machine_dict(d)[target_os][target_arch]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500336
337 # Check the architecture and endiannes of the binary
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600338 is_32 = (("virtual/kernel" in provides) or bb.data.inherits_class("module", d)) and \
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800339 (target_os == "linux-gnux32" or target_os == "linux-muslx32" or \
Brad Bishop977dc1a2019-02-06 16:01:43 -0500340 target_os == "linux-gnu_ilp32" or re.match(r'mips64.*32', d.getVar('DEFAULTTUNE')))
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800341 is_bpf = (oe.qa.elf_machine_to_string(elf.machine()) == "BPF")
342 if not ((machine == elf.machine()) or is_32 or is_bpf):
Andrew Geisslereff27472021-10-29 15:35:00 -0500343 oe.qa.add_message(messages, "arch", "Architecture did not match (%s, expected %s) in %s" % \
Andrew Geisslerb7d28612020-07-24 16:15:54 -0500344 (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 -0400345 elif not ((bits == elf.abiSize()) or is_32 or is_bpf):
Andrew Geisslereff27472021-10-29 15:35:00 -0500346 oe.qa.add_message(messages, "arch", "Bit size did not match (%d, expected %d) in %s" % \
Andrew Geisslerb7d28612020-07-24 16:15:54 -0500347 (elf.abiSize(), bits, package_qa_clean_path(path, d, name)))
Brad Bishop19323692019-04-05 15:28:33 -0400348 elif not ((littleendian == elf.isLittleEndian()) or is_bpf):
Andrew Geisslereff27472021-10-29 15:35:00 -0500349 oe.qa.add_message(messages, "arch", "Endiannes did not match (%d, expected %d) in %s" % \
Andrew Geisslerb7d28612020-07-24 16:15:54 -0500350 (elf.isLittleEndian(), littleendian, package_qa_clean_path(path,d, name)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500351
352QAPATHTEST[desktop] = "package_qa_check_desktop"
353def package_qa_check_desktop(path, name, d, elf, messages):
354 """
355 Run all desktop files through desktop-file-validate.
356 """
357 if path.endswith(".desktop"):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500358 desktop_file_validate = os.path.join(d.getVar('STAGING_BINDIR_NATIVE'),'desktop-file-validate')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500359 output = os.popen("%s %s" % (desktop_file_validate, path))
360 # This only produces output on errors
361 for l in output:
Andrew Geisslereff27472021-10-29 15:35:00 -0500362 oe.qa.add_message(messages, "desktop", "Desktop file issue: " + l.strip())
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500363
364QAPATHTEST[textrel] = "package_qa_textrel"
365def package_qa_textrel(path, name, d, elf, messages):
366 """
367 Check if the binary contains relocations in .text
368 """
369
370 if not elf:
371 return
372
373 if os.path.islink(path):
374 return
375
376 phdrs = elf.run_objdump("-p", d)
377 sane = True
378
379 import re
Brad Bishop977dc1a2019-02-06 16:01:43 -0500380 textrel_re = re.compile(r"\s+TEXTREL\s+")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500381 for line in phdrs.split("\n"):
382 if textrel_re.match(line):
383 sane = False
Brad Bishop1d80a2e2019-11-15 16:35:03 -0500384 break
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500385
386 if not sane:
Brad Bishop1d80a2e2019-11-15 16:35:03 -0500387 path = package_qa_clean_path(path, d, name)
Andrew Geisslereff27472021-10-29 15:35:00 -0500388 oe.qa.add_message(messages, "textrel", "%s: ELF binary %s has relocations in .text" % (name, path))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500389
390QAPATHTEST[ldflags] = "package_qa_hash_style"
391def package_qa_hash_style(path, name, d, elf, messages):
392 """
393 Check if the binary has the right hash style...
394 """
395
396 if not elf:
397 return
398
399 if os.path.islink(path):
400 return
401
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500402 gnu_hash = "--hash-style=gnu" in d.getVar('LDFLAGS')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500403 if not gnu_hash:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500404 gnu_hash = "--hash-style=both" in d.getVar('LDFLAGS')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500405 if not gnu_hash:
406 return
407
408 sane = False
409 has_syms = False
410
411 phdrs = elf.run_objdump("-p", d)
412
413 # If this binary has symbols, we expect it to have GNU_HASH too.
414 for line in phdrs.split("\n"):
415 if "SYMTAB" in line:
416 has_syms = True
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500417 if "GNU_HASH" in line or "DT_MIPS_XHASH" in line:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500418 sane = True
Andrew Geissler82c905d2020-04-13 13:39:40 -0500419 if ("[mips32]" in line or "[mips64]" in line) and d.getVar('TCLIBC') == "musl":
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500420 sane = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500421 if has_syms and not sane:
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500422 path = package_qa_clean_path(path, d, name)
Andrew Geisslereff27472021-10-29 15:35:00 -0500423 oe.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 -0500424
425
426QAPATHTEST[buildpaths] = "package_qa_check_buildpaths"
427def package_qa_check_buildpaths(path, name, d, elf, messages):
428 """
429 Check for build paths inside target files and error if not found in the whitelist
430 """
431 # Ignore .debug files, not interesting
432 if path.find(".debug") != -1:
433 return
434
435 # Ignore symlinks
436 if os.path.islink(path):
437 return
438
Brad Bishopd5ae7d92018-06-14 09:52:03 -0700439 tmpdir = bytes(d.getVar('TMPDIR'), encoding="utf-8")
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500440 with open(path, 'rb') as f:
Brad Bishopd5ae7d92018-06-14 09:52:03 -0700441 file_content = f.read()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500442 if tmpdir in file_content:
Brad Bishopf3fd2882019-06-21 08:06:37 -0400443 trimmed = path.replace(os.path.join (d.getVar("PKGDEST"), name), "")
Andrew Geisslereff27472021-10-29 15:35:00 -0500444 oe.qa.add_message(messages, "buildpaths", "File %s in package %s contains reference to TMPDIR" % (trimmed, name))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500445
446
447QAPATHTEST[xorg-driver-abi] = "package_qa_check_xorg_driver_abi"
448def package_qa_check_xorg_driver_abi(path, name, d, elf, messages):
449 """
450 Check that all packages containing Xorg drivers have ABI dependencies
451 """
452
453 # Skip dev, dbg or nativesdk packages
454 if name.endswith("-dev") or name.endswith("-dbg") or name.startswith("nativesdk-"):
455 return
456
457 driverdir = d.expand("${libdir}/xorg/modules/drivers/")
458 if driverdir in path and path.endswith(".so"):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500459 mlprefix = d.getVar('MLPREFIX') or ''
Patrick Williams213cb262021-08-07 19:21:33 -0500460 for rdep in bb.utils.explode_deps(d.getVar('RDEPENDS:' + name) or ""):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500461 if rdep.startswith("%sxorg-abi-" % mlprefix):
462 return
Andrew Geisslereff27472021-10-29 15:35:00 -0500463 oe.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 -0500464
465QAPATHTEST[infodir] = "package_qa_check_infodir"
466def package_qa_check_infodir(path, name, d, elf, messages):
467 """
468 Check that /usr/share/info/dir isn't shipped in a particular package
469 """
470 infodir = d.expand("${infodir}/dir")
471
472 if infodir in path:
Andrew Geisslereff27472021-10-29 15:35:00 -0500473 oe.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 -0500474
475QAPATHTEST[symlink-to-sysroot] = "package_qa_check_symlink_to_sysroot"
476def package_qa_check_symlink_to_sysroot(path, name, d, elf, messages):
477 """
478 Check that the package doesn't contain any absolute symlinks to the sysroot.
479 """
480 if os.path.islink(path):
481 target = os.readlink(path)
482 if os.path.isabs(target):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500483 tmpdir = d.getVar('TMPDIR')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500484 if target.startswith(tmpdir):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500485 trimmed = path.replace(os.path.join (d.getVar("PKGDEST"), name), "")
Andrew Geisslereff27472021-10-29 15:35:00 -0500486 oe.qa.add_message(messages, "symlink-to-sysroot", "Symlink %s in %s points to TMPDIR" % (trimmed, name))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500487
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600488# Check license variables
489do_populate_lic[postfuncs] += "populate_lic_qa_checksum"
490python populate_lic_qa_checksum() {
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500491 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600492 Check for changes in the license files.
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500493 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500494
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500495 lic_files = d.getVar('LIC_FILES_CHKSUM') or ''
496 lic = d.getVar('LICENSE')
497 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500498
499 if lic == "CLOSED":
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500500 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500501
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500502 if not lic_files and d.getVar('SRC_URI'):
Andrew Geisslereff27472021-10-29 15:35:00 -0500503 oe.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 -0500504
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500505 srcdir = d.getVar('S')
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500506 corebase_licensefile = d.getVar('COREBASE') + "/LICENSE"
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500507 for url in lic_files.split():
508 try:
509 (type, host, path, user, pswd, parm) = bb.fetch.decodeurl(url)
510 except bb.fetch.MalformedUrl:
Andrew Geisslereff27472021-10-29 15:35:00 -0500511 oe.qa.handle_error("license-checksum", pn + ": LIC_FILES_CHKSUM contains an invalid URL: " + url, d)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500512 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500513 srclicfile = os.path.join(srcdir, path)
514 if not os.path.isfile(srclicfile):
Andrew Geisslereff27472021-10-29 15:35:00 -0500515 oe.qa.handle_error("license-checksum", pn + ": LIC_FILES_CHKSUM points to an invalid file: " + srclicfile, d)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500516 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500517
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500518 if (srclicfile == corebase_licensefile):
519 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")
520
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500521 recipemd5 = parm.get('md5', '')
522 beginline, endline = 0, 0
523 if 'beginline' in parm:
524 beginline = int(parm['beginline'])
525 if 'endline' in parm:
526 endline = int(parm['endline'])
527
528 if (not beginline) and (not endline):
529 md5chksum = bb.utils.md5_file(srclicfile)
Brad Bishop19323692019-04-05 15:28:33 -0400530 with open(srclicfile, 'r', errors='replace') as f:
531 license = f.read().splitlines()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500532 else:
Brad Bishop19323692019-04-05 15:28:33 -0400533 with open(srclicfile, 'rb') as f:
534 import hashlib
535 lineno = 0
536 license = []
537 m = hashlib.md5()
538 for line in f:
539 lineno += 1
540 if (lineno >= beginline):
541 if ((lineno <= endline) or not endline):
542 m.update(line)
543 license.append(line.decode('utf-8', errors='replace').rstrip())
544 else:
545 break
546 md5chksum = m.hexdigest()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500547 if recipemd5 == md5chksum:
548 bb.note (pn + ": md5 checksum matched for ", url)
549 else:
550 if recipemd5:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500551 msg = pn + ": The LIC_FILES_CHKSUM does not match for " + url
552 msg = msg + "\n" + pn + ": The new md5 checksum is " + md5chksum
Brad Bishop19323692019-04-05 15:28:33 -0400553 max_lines = int(d.getVar('QA_MAX_LICENSE_LINES') or 20)
554 if not license or license[-1] != '':
555 # Ensure that our license text ends with a line break
556 # (will be added with join() below).
557 license.append('')
558 remove = len(license) - max_lines
559 if remove > 0:
560 start = max_lines // 2
561 end = start + remove - 1
562 del license[start:end]
563 license.insert(start, '...')
564 msg = msg + "\n" + pn + ": Here is the selected license text:" + \
565 "\n" + \
566 "{:v^70}".format(" beginline=%d " % beginline if beginline else "") + \
567 "\n" + "\n".join(license) + \
568 "{:^^70}".format(" endline=%d " % endline if endline else "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500569 if beginline:
570 if endline:
571 srcfiledesc = "%s (lines %d through to %d)" % (srclicfile, beginline, endline)
572 else:
573 srcfiledesc = "%s (beginning on line %d)" % (srclicfile, beginline)
574 elif endline:
575 srcfiledesc = "%s (ending on line %d)" % (srclicfile, endline)
576 else:
577 srcfiledesc = srclicfile
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500578 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 -0500579
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500580 else:
581 msg = pn + ": LIC_FILES_CHKSUM is not specified for " + url
582 msg = msg + "\n" + pn + ": The md5 checksum is " + md5chksum
Andrew Geisslereff27472021-10-29 15:35:00 -0500583 oe.qa.handle_error("license-checksum", msg, d)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600584
Andrew Geisslereff27472021-10-29 15:35:00 -0500585 oe.qa.exit_if_errors(d)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600586}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500587
Brad Bishop19323692019-04-05 15:28:33 -0400588def qa_check_staged(path,d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500589 """
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500590 Check staged la and pc files for common problems like references to the work
591 directory.
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500592
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500593 As this is run after every stage we should be able to find the one
594 responsible for the errors easily even if we look at every .pc and .la file.
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500595 """
596
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500597 tmpdir = d.getVar('TMPDIR')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500598 workdir = os.path.join(tmpdir, "work")
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500599 recipesysroot = d.getVar("RECIPE_SYSROOT")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500600
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500601 if bb.data.inherits_class("native", d) or bb.data.inherits_class("cross", d):
602 pkgconfigcheck = workdir
603 else:
604 pkgconfigcheck = tmpdir
605
Brad Bishop19323692019-04-05 15:28:33 -0400606 skip = (d.getVar('INSANE_SKIP') or "").split()
607 skip_la = False
608 if 'la' in skip:
609 bb.note("Recipe %s skipping qa checking: la" % d.getVar('PN'))
610 skip_la = True
611
612 skip_pkgconfig = False
613 if 'pkgconfig' in skip:
614 bb.note("Recipe %s skipping qa checking: pkgconfig" % d.getVar('PN'))
615 skip_pkgconfig = True
616
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500617 # find all .la and .pc files
618 # read the content
619 # and check for stuff that looks wrong
620 for root, dirs, files in os.walk(path):
621 for file in files:
622 path = os.path.join(root,file)
Brad Bishop19323692019-04-05 15:28:33 -0400623 if file.endswith(".la") and not skip_la:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500624 with open(path) as f:
625 file_content = f.read()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500626 file_content = file_content.replace(recipesysroot, "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500627 if workdir in file_content:
628 error_msg = "%s failed sanity test (workdir) in path %s" % (file,root)
Andrew Geisslereff27472021-10-29 15:35:00 -0500629 oe.qa.handle_error("la", error_msg, d)
Brad Bishop19323692019-04-05 15:28:33 -0400630 elif file.endswith(".pc") and not skip_pkgconfig:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500631 with open(path) as f:
632 file_content = f.read()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500633 file_content = file_content.replace(recipesysroot, "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500634 if pkgconfigcheck in file_content:
635 error_msg = "%s failed sanity test (tmpdir) in path %s" % (file,root)
Andrew Geisslereff27472021-10-29 15:35:00 -0500636 oe.qa.handle_error("pkgconfig", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500637
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500638# Run all package-wide warnfuncs and errorfuncs
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500639def package_qa_package(warnfuncs, errorfuncs, package, d):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500640 warnings = {}
641 errors = {}
642
643 for func in warnfuncs:
644 func(package, d, warnings)
645 for func in errorfuncs:
646 func(package, d, errors)
647
648 for w in warnings:
Andrew Geisslereff27472021-10-29 15:35:00 -0500649 oe.qa.handle_error(w, warnings[w], d)
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500650 for e in errors:
Andrew Geisslereff27472021-10-29 15:35:00 -0500651 oe.qa.handle_error(e, errors[e], d)
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500652
653 return len(errors) == 0
654
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500655# Run all recipe-wide warnfuncs and errorfuncs
656def package_qa_recipe(warnfuncs, errorfuncs, pn, d):
657 warnings = {}
658 errors = {}
659
660 for func in warnfuncs:
661 func(pn, d, warnings)
662 for func in errorfuncs:
663 func(pn, d, errors)
664
665 for w in warnings:
Andrew Geisslereff27472021-10-29 15:35:00 -0500666 oe.qa.handle_error(w, warnings[w], d)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500667 for e in errors:
Andrew Geisslereff27472021-10-29 15:35:00 -0500668 oe.qa.handle_error(e, errors[e], d)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500669
670 return len(errors) == 0
671
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500672# Walk over all files in a directory and call func
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500673def package_qa_walk(warnfuncs, errorfuncs, package, d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500674 #if this will throw an exception, then fix the dict above
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500675 target_os = d.getVar('TARGET_OS')
676 target_arch = d.getVar('TARGET_ARCH')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500677
678 warnings = {}
679 errors = {}
680 for path in pkgfiles[package]:
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500681 elf = None
682 if os.path.isfile(path):
683 elf = oe.qa.ELFFile(path)
684 try:
685 elf.open()
686 except oe.qa.NotELFFileError:
687 elf = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500688 for func in warnfuncs:
689 func(path, package, d, elf, warnings)
690 for func in errorfuncs:
691 func(path, package, d, elf, errors)
692
693 for w in warnings:
Andrew Geisslereff27472021-10-29 15:35:00 -0500694 oe.qa.handle_error(w, warnings[w], d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500695 for e in errors:
Andrew Geisslereff27472021-10-29 15:35:00 -0500696 oe.qa.handle_error(e, errors[e], d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500697
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500698def package_qa_check_rdepends(pkg, pkgdest, skip, taskdeps, packages, d):
699 # Don't do this check for kernel/module recipes, there aren't too many debug/development
700 # packages and you can get false positives e.g. on kernel-module-lirc-dev
701 if bb.data.inherits_class("kernel", d) or bb.data.inherits_class("module-base", d):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500702 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500703
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500704 if not "-dbg" in pkg and not "packagegroup-" in pkg and not "-image" in pkg:
705 localdata = bb.data.createCopy(d)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500706 localdata.setVar('OVERRIDES', localdata.getVar('OVERRIDES') + ':' + pkg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500707
708 # Now check the RDEPENDS
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500709 rdepends = bb.utils.explode_deps(localdata.getVar('RDEPENDS') or "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500710
711 # Now do the sanity check!!!
712 if "build-deps" not in skip:
713 for rdepend in rdepends:
714 if "-dbg" in rdepend and "debug-deps" not in skip:
715 error_msg = "%s rdepends on %s" % (pkg,rdepend)
Andrew Geisslereff27472021-10-29 15:35:00 -0500716 oe.qa.handle_error("debug-deps", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500717 if (not "-dev" in pkg and not "-staticdev" in pkg) and rdepend.endswith("-dev") and "dev-deps" not in skip:
718 error_msg = "%s rdepends on %s" % (pkg, rdepend)
Andrew Geisslereff27472021-10-29 15:35:00 -0500719 oe.qa.handle_error("dev-deps", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500720 if rdepend not in packages:
721 rdep_data = oe.packagedata.read_subpkgdata(rdepend, d)
722 if rdep_data and 'PN' in rdep_data and rdep_data['PN'] in taskdeps:
723 continue
724 if not rdep_data or not 'PN' in rdep_data:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500725 pkgdata_dir = d.getVar("PKGDATA_DIR")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500726 try:
727 possibles = os.listdir("%s/runtime-rprovides/%s/" % (pkgdata_dir, rdepend))
728 except OSError:
729 possibles = []
730 for p in possibles:
731 rdep_data = oe.packagedata.read_subpkgdata(p, d)
732 if rdep_data and 'PN' in rdep_data and rdep_data['PN'] in taskdeps:
733 break
734 if rdep_data and 'PN' in rdep_data and rdep_data['PN'] in taskdeps:
735 continue
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500736 if rdep_data and 'PN' in rdep_data:
737 error_msg = "%s rdepends on %s, but it isn't a build dependency, missing %s in DEPENDS or PACKAGECONFIG?" % (pkg, rdepend, rdep_data['PN'])
738 else:
739 error_msg = "%s rdepends on %s, but it isn't a build dependency?" % (pkg, rdepend)
Andrew Geisslereff27472021-10-29 15:35:00 -0500740 oe.qa.handle_error("build-deps", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500741
742 if "file-rdeps" not in skip:
743 ignored_file_rdeps = set(['/bin/sh', '/usr/bin/env', 'rtld(GNU_HASH)'])
744 if bb.data.inherits_class('nativesdk', d):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500745 ignored_file_rdeps |= set(['/bin/bash', '/usr/bin/perl', 'perl'])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500746 # For Saving the FILERDEPENDS
747 filerdepends = {}
748 rdep_data = oe.packagedata.read_subpkgdata(pkg, d)
749 for key in rdep_data:
Andrew Geissler5199d832021-09-24 16:47:35 -0500750 if key.startswith("FILERDEPENDS:"):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500751 for subkey in bb.utils.explode_deps(rdep_data[key]):
752 if subkey not in ignored_file_rdeps and \
753 not subkey.startswith('perl('):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500754 # We already know it starts with FILERDEPENDS_
755 filerdepends[subkey] = key[13:]
756
757 if filerdepends:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500758 done = rdepends[:]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500759 # Add the rprovides of itself
760 if pkg not in done:
761 done.insert(0, pkg)
762
763 # The python is not a package, but python-core provides it, so
764 # skip checking /usr/bin/python if python is in the rdeps, in
Patrick Williams213cb262021-08-07 19:21:33 -0500765 # case there is a RDEPENDS:pkg = "python" in the recipe.
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500766 for py in [ d.getVar('MLPREFIX') + "python", "python" ]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500767 if py in done:
768 filerdepends.pop("/usr/bin/python",None)
769 done.remove(py)
770 for rdep in done:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500771 # The file dependencies may contain package names, e.g.,
772 # perl
773 filerdepends.pop(rdep,None)
774
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500775 # For Saving the FILERPROVIDES, RPROVIDES and FILES_INFO
776 rdep_data = oe.packagedata.read_subpkgdata(rdep, d)
777 for key in rdep_data:
Andrew Geisslerd159c7f2021-09-02 21:05:58 -0500778 if key.startswith("FILERPROVIDES:") or key.startswith("RPROVIDES:"):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500779 for subkey in bb.utils.explode_deps(rdep_data[key]):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500780 filerdepends.pop(subkey,None)
781 # Add the files list to the rprovides
Andrew Geisslerd159c7f2021-09-02 21:05:58 -0500782 if key.startswith("FILES_INFO:"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500783 # Use eval() to make it as a dict
784 for subkey in eval(rdep_data[key]):
785 filerdepends.pop(subkey,None)
786 if not filerdepends:
787 # Break if all the file rdepends are met
788 break
789 if filerdepends:
790 for key in filerdepends:
Patrick Williams213cb262021-08-07 19:21:33 -0500791 error_msg = "%s contained in package %s requires %s, but no providers found in RDEPENDS:%s?" % \
792 (filerdepends[key].replace(":%s" % pkg, "").replace("@underscore@", "_"), pkg, key, pkg)
Andrew Geisslereff27472021-10-29 15:35:00 -0500793 oe.qa.handle_error("file-rdeps", error_msg, d)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500794package_qa_check_rdepends[vardepsexclude] = "OVERRIDES"
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500795
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500796def package_qa_check_deps(pkg, pkgdest, d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500797
798 localdata = bb.data.createCopy(d)
799 localdata.setVar('OVERRIDES', pkg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500800
801 def check_valid_deps(var):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500802 try:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500803 rvar = bb.utils.explode_dep_versions2(localdata.getVar(var) or "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500804 except ValueError as e:
Patrick Williams0ca19cc2021-08-16 14:03:13 -0500805 bb.fatal("%s:%s: %s" % (var, pkg, e))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500806 for dep in rvar:
807 for v in rvar[dep]:
808 if v and not v.startswith(('< ', '= ', '> ', '<= ', '>=')):
Patrick Williams0ca19cc2021-08-16 14:03:13 -0500809 error_msg = "%s:%s is invalid: %s (%s) only comparisons <, =, >, <=, and >= are allowed" % (var, pkg, dep, v)
Andrew Geisslereff27472021-10-29 15:35:00 -0500810 oe.qa.handle_error("dep-cmp", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500811
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500812 check_valid_deps('RDEPENDS')
813 check_valid_deps('RRECOMMENDS')
814 check_valid_deps('RSUGGESTS')
815 check_valid_deps('RPROVIDES')
816 check_valid_deps('RREPLACES')
817 check_valid_deps('RCONFLICTS')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500818
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500819QAPKGTEST[usrmerge] = "package_qa_check_usrmerge"
820def package_qa_check_usrmerge(pkg, d, messages):
Andrew Geisslereff27472021-10-29 15:35:00 -0500821
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500822 pkgdest = d.getVar('PKGDEST')
823 pkg_dir = pkgdest + os.sep + pkg + os.sep
824 merged_dirs = ['bin', 'sbin', 'lib'] + d.getVar('MULTILIB_VARIANTS').split()
825 for f in merged_dirs:
826 if os.path.exists(pkg_dir + f) and not os.path.islink(pkg_dir + f):
827 msg = "%s package is not obeying usrmerge distro feature. /%s should be relocated to /usr." % (pkg, f)
Andrew Geisslereff27472021-10-29 15:35:00 -0500828 oe.qa.add_message(messages, "usrmerge", msg)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500829 return False
830 return True
831
Brad Bishopf3f93bb2019-10-16 14:33:32 -0400832QAPKGTEST[perllocalpod] = "package_qa_check_perllocalpod"
833def package_qa_check_perllocalpod(pkg, d, messages):
834 """
835 Check that the recipe didn't ship a perlocal.pod file, which shouldn't be
836 installed in a distribution package. cpan.bbclass sets NO_PERLLOCAL=1 to
837 handle this for most recipes.
838 """
839 import glob
840 pkgd = oe.path.join(d.getVar('PKGDEST'), pkg)
841 podpath = oe.path.join(pkgd, d.getVar("libdir"), "perl*", "*", "*", "perllocal.pod")
842
843 matches = glob.glob(podpath)
844 if matches:
845 matches = [package_qa_clean_path(path, d, pkg) for path in matches]
846 msg = "%s contains perllocal.pod (%s), should not be installed" % (pkg, " ".join(matches))
Andrew Geisslereff27472021-10-29 15:35:00 -0500847 oe.qa.add_message(messages, "perllocalpod", msg)
Brad Bishopf3f93bb2019-10-16 14:33:32 -0400848
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500849QAPKGTEST[expanded-d] = "package_qa_check_expanded_d"
850def package_qa_check_expanded_d(package, d, messages):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500851 """
852 Check for the expanded D (${D}) value in pkg_* and FILES
853 variables, warn the user to use it correctly.
854 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500855 sane = True
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500856 expanded_d = d.getVar('D')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500857
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500858 for var in 'FILES','pkg_preinst', 'pkg_postinst', 'pkg_prerm', 'pkg_postrm':
Patrick Williams0ca19cc2021-08-16 14:03:13 -0500859 bbvar = d.getVar(var + ":" + package) or ""
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500860 if expanded_d in bbvar:
861 if var == 'FILES':
Andrew Geisslereff27472021-10-29 15:35:00 -0500862 oe.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)
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500863 sane = False
864 else:
Andrew Geisslereff27472021-10-29 15:35:00 -0500865 oe.qa.add_message(messages, "expanded-d", "%s in %s recipe contains ${D}, it should be replaced by $D instead" % (var, package))
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500866 sane = False
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500867 return sane
868
Andrew Geissler1e34c2d2020-05-29 16:02:59 -0500869QAPKGTEST[unlisted-pkg-lics] = "package_qa_check_unlisted_pkg_lics"
870def package_qa_check_unlisted_pkg_lics(package, d, messages):
871 """
872 Check that all licenses for a package are among the licenses for the recipe.
873 """
Patrick Williams213cb262021-08-07 19:21:33 -0500874 pkg_lics = d.getVar('LICENSE:' + package)
Andrew Geissler1e34c2d2020-05-29 16:02:59 -0500875 if not pkg_lics:
876 return True
877
878 recipe_lics_set = oe.license.list_licenses(d.getVar('LICENSE'))
879 unlisted = oe.license.list_licenses(pkg_lics) - recipe_lics_set
880 if not unlisted:
881 return True
882
Andrew Geisslereff27472021-10-29 15:35:00 -0500883 oe.qa.add_message(messages, "unlisted-pkg-lics",
Patrick Williams213cb262021-08-07 19:21:33 -0500884 "LICENSE:%s includes licenses (%s) that are not "
Andrew Geissler1e34c2d2020-05-29 16:02:59 -0500885 "listed in LICENSE" % (package, ' '.join(unlisted)))
886 return False
887
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500888def package_qa_check_encoding(keys, encode, d):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600889 def check_encoding(key, enc):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500890 sane = True
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500891 value = d.getVar(key)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500892 if value:
893 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600894 s = value.encode(enc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500895 except UnicodeDecodeError as e:
896 error_msg = "%s has non %s characters" % (key,enc)
897 sane = False
Andrew Geisslereff27472021-10-29 15:35:00 -0500898 oe.qa.handle_error("invalid-chars", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500899 return sane
900
901 for key in keys:
902 sane = check_encoding(key, encode)
903 if not sane:
904 break
905
906HOST_USER_UID := "${@os.getuid()}"
907HOST_USER_GID := "${@os.getgid()}"
908
909QAPATHTEST[host-user-contaminated] = "package_qa_check_host_user"
910def package_qa_check_host_user(path, name, d, elf, messages):
911 """Check for paths outside of /home which are owned by the user running bitbake."""
912
913 if not os.path.lexists(path):
914 return
915
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500916 dest = d.getVar('PKGDEST')
917 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500918 home = os.path.join(dest, 'home')
919 if path == home or path.startswith(home + os.sep):
920 return
921
922 try:
923 stat = os.lstat(path)
924 except OSError as exc:
925 import errno
926 if exc.errno != errno.ENOENT:
927 raise
928 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500929 check_uid = int(d.getVar('HOST_USER_UID'))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500930 if stat.st_uid == check_uid:
Andrew Geisslereff27472021-10-29 15:35:00 -0500931 oe.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 -0500932 return False
933
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500934 check_gid = int(d.getVar('HOST_USER_GID'))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500935 if stat.st_gid == check_gid:
Andrew Geisslereff27472021-10-29 15:35:00 -0500936 oe.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 -0500937 return False
938 return True
939
Brad Bishop15ae2502019-06-18 21:44:24 -0400940QARECIPETEST[src-uri-bad] = "package_qa_check_src_uri"
941def package_qa_check_src_uri(pn, d, messages):
942 import re
943
944 if "${PN}" in d.getVar("SRC_URI", False):
Andrew Geisslereff27472021-10-29 15:35:00 -0500945 oe.qa.handle_error("src-uri-bad", "%s: SRC_URI uses PN not BPN" % pn, d)
Brad Bishop15ae2502019-06-18 21:44:24 -0400946
Andrew Geissler82c905d2020-04-13 13:39:40 -0500947 for url in d.getVar("SRC_URI").split():
Andrew Geissler6ce62a22020-11-30 19:58:47 -0600948 if re.search(r"git(hu|la)b\.com/.+/.+/archive/.+", url):
Andrew Geisslereff27472021-10-29 15:35:00 -0500949 oe.qa.handle_error("src-uri-bad", "%s: SRC_URI uses unstable GitHub/GitLab archives, convert recipe to use git protocol" % pn, d)
Brad Bishop15ae2502019-06-18 21:44:24 -0400950
Andrew Geissler5a43b432020-06-13 10:46:56 -0500951QARECIPETEST[unhandled-features-check] = "package_qa_check_unhandled_features_check"
952def package_qa_check_unhandled_features_check(pn, d, messages):
953 if not bb.data.inherits_class('features_check', d):
954 var_set = False
955 for kind in ['DISTRO', 'MACHINE', 'COMBINED']:
956 for var in ['ANY_OF_' + kind + '_FEATURES', 'REQUIRED_' + kind + '_FEATURES', 'CONFLICT_' + kind + '_FEATURES']:
957 if d.getVar(var) is not None or d.overridedata.get(var) is not None:
958 var_set = True
959 if var_set:
Andrew Geisslereff27472021-10-29 15:35:00 -0500960 oe.qa.handle_error("unhandled-features-check", "%s: recipe doesn't inherit features_check" % pn, d)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500961
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500962QARECIPETEST[missing-update-alternatives] = "package_qa_check_missing_update_alternatives"
963def package_qa_check_missing_update_alternatives(pn, d, messages):
964 # Look at all packages and find out if any of those sets ALTERNATIVE variable
965 # without inheriting update-alternatives class
966 for pkg in (d.getVar('PACKAGES') or '').split():
Patrick Williams213cb262021-08-07 19:21:33 -0500967 if d.getVar('ALTERNATIVE:%s' % pkg) and not bb.data.inherits_class('update-alternatives', d):
Andrew Geisslereff27472021-10-29 15:35:00 -0500968 oe.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)
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500969
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500970# The PACKAGE FUNC to scan each package
971python do_package_qa () {
972 import subprocess
973 import oe.packagedata
974
975 bb.note("DO PACKAGE QA")
976
977 bb.build.exec_func("read_subpackage_metadata", d)
978
979 # Check non UTF-8 characters on recipe's metadata
980 package_qa_check_encoding(['DESCRIPTION', 'SUMMARY', 'LICENSE', 'SECTION'], 'utf-8', d)
981
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500982 logdir = d.getVar('T')
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500983 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500984
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500985 # Scan the packages...
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500986 pkgdest = d.getVar('PKGDEST')
987 packages = set((d.getVar('PACKAGES') or '').split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500988
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500989 global pkgfiles
990 pkgfiles = {}
991 for pkg in packages:
992 pkgfiles[pkg] = []
Andrew Geisslerb7d28612020-07-24 16:15:54 -0500993 pkgdir = os.path.join(pkgdest, pkg)
994 for walkroot, dirs, files in os.walk(pkgdir):
995 # Don't walk into top-level CONTROL or DEBIAN directories as these
996 # are temporary directories created by do_package.
997 if walkroot == pkgdir:
998 for control in ("CONTROL", "DEBIAN"):
999 if control in dirs:
1000 dirs.remove(control)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001001 for file in files:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001002 pkgfiles[pkg].append(os.path.join(walkroot, file))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001003
1004 # no packages should be scanned
1005 if not packages:
1006 return
1007
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001008 import re
1009 # The package name matches the [a-z0-9.+-]+ regular expression
Brad Bishop977dc1a2019-02-06 16:01:43 -05001010 pkgname_pattern = re.compile(r"^[a-z0-9.+-]+$")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001011
1012 taskdepdata = d.getVar("BB_TASKDEPDATA", False)
1013 taskdeps = set()
1014 for dep in taskdepdata:
1015 taskdeps.add(taskdepdata[dep][0])
1016
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001017 def parse_test_matrix(matrix_name):
1018 testmatrix = d.getVarFlags(matrix_name) or {}
1019 g = globals()
1020 warnchecks = []
1021 for w in (d.getVar("WARN_QA") or "").split():
1022 if w in skip:
1023 continue
1024 if w in testmatrix and testmatrix[w] in g:
1025 warnchecks.append(g[testmatrix[w]])
1026
1027 errorchecks = []
1028 for e in (d.getVar("ERROR_QA") or "").split():
1029 if e in skip:
1030 continue
1031 if e in testmatrix and testmatrix[e] in g:
1032 errorchecks.append(g[testmatrix[e]])
1033 return warnchecks, errorchecks
1034
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001035 for package in packages:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001036 skip = set((d.getVar('INSANE_SKIP') or "").split() +
Patrick Williams213cb262021-08-07 19:21:33 -05001037 (d.getVar('INSANE_SKIP:' + package) or "").split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001038 if skip:
1039 bb.note("Package %s skipping QA tests: %s" % (package, str(skip)))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001040
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001041 bb.note("Checking Package: %s" % package)
1042 # Check package name
1043 if not pkgname_pattern.match(package):
Andrew Geisslereff27472021-10-29 15:35:00 -05001044 oe.qa.handle_error("pkgname",
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001045 "%s doesn't match the [a-z0-9.+-]+ regex" % package, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001046
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001047 warn_checks, error_checks = parse_test_matrix("QAPATHTEST")
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001048 package_qa_walk(warn_checks, error_checks, package, d)
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001049
1050 warn_checks, error_checks = parse_test_matrix("QAPKGTEST")
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001051 package_qa_package(warn_checks, error_checks, package, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001052
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001053 package_qa_check_rdepends(package, pkgdest, skip, taskdeps, packages, d)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001054 package_qa_check_deps(package, pkgdest, d)
1055
1056 warn_checks, error_checks = parse_test_matrix("QARECIPETEST")
1057 package_qa_recipe(warn_checks, error_checks, pn, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001058
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001059 if 'libdir' in d.getVar("ALL_QA").split():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001060 package_qa_check_libdir(d)
1061
Andrew Geisslereff27472021-10-29 15:35:00 -05001062 oe.qa.exit_if_errors(d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001063}
1064
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001065# binutils is used for most checks, so need to set as dependency
1066# POPULATESYSROOTDEPS is defined in staging class.
1067do_package_qa[depends] += "${POPULATESYSROOTDEPS}"
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001068do_package_qa[vardepsexclude] = "BB_TASKDEPDATA"
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001069do_package_qa[rdeptask] = "do_packagedata"
1070addtask do_package_qa after do_packagedata do_package before do_build
1071
Brad Bishop19323692019-04-05 15:28:33 -04001072# Add the package specific INSANE_SKIPs to the sstate dependencies
1073python() {
1074 pkgs = (d.getVar('PACKAGES') or '').split()
1075 for pkg in pkgs:
Patrick Williams213cb262021-08-07 19:21:33 -05001076 d.appendVarFlag("do_package_qa", "vardeps", " INSANE_SKIP:{}".format(pkg))
Brad Bishop19323692019-04-05 15:28:33 -04001077}
1078
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001079SSTATETASKS += "do_package_qa"
1080do_package_qa[sstate-inputdirs] = ""
1081do_package_qa[sstate-outputdirs] = ""
1082python do_package_qa_setscene () {
1083 sstate_setscene(d)
1084}
1085addtask do_package_qa_setscene
1086
1087python do_qa_staging() {
1088 bb.note("QA checking staging")
Andrew Geisslereff27472021-10-29 15:35:00 -05001089 qa_check_staged(d.expand('${SYSROOT_DESTDIR}${libdir}'), d)
1090 oe.qa.exit_with_message_if_errors("QA staging was broken by the package built above", d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001091}
1092
Brad Bishopd89cb5f2019-04-10 09:02:41 -04001093python do_qa_patch() {
1094 import subprocess
1095
1096 ###########################################################################
1097 # Check patch.log for fuzz warnings
1098 #
1099 # Further information on why we check for patch fuzz warnings:
1100 # http://lists.openembedded.org/pipermail/openembedded-core/2018-March/148675.html
1101 # https://bugzilla.yoctoproject.org/show_bug.cgi?id=10450
1102 ###########################################################################
1103
1104 logdir = d.getVar('T')
1105 patchlog = os.path.join(logdir,"log.do_patch")
1106
1107 if os.path.exists(patchlog):
1108 fuzzheader = '--- Patch fuzz start ---'
1109 fuzzfooter = '--- Patch fuzz end ---'
1110 statement = "grep -e '%s' %s > /dev/null" % (fuzzheader, patchlog)
1111 if subprocess.call(statement, shell=True) == 0:
1112 msg = "Fuzz detected:\n\n"
1113 fuzzmsg = ""
1114 inFuzzInfo = False
1115 f = open(patchlog, "r")
1116 for line in f:
1117 if fuzzheader in line:
1118 inFuzzInfo = True
1119 fuzzmsg = ""
1120 elif fuzzfooter in line:
1121 fuzzmsg = fuzzmsg.replace('\n\n', '\n')
1122 msg += fuzzmsg
1123 msg += "\n"
1124 inFuzzInfo = False
1125 elif inFuzzInfo and not 'Now at patch' in line:
1126 fuzzmsg += line
1127 f.close()
1128 msg += "The context lines in the patches can be updated with devtool:\n"
1129 msg += "\n"
1130 msg += " devtool modify %s\n" % d.getVar('PN')
1131 msg += " devtool finish --force-patch-refresh %s <layer_path>\n\n" % d.getVar('PN')
1132 msg += "Don't forget to review changes done by devtool!\n"
1133 if 'patch-fuzz' in d.getVar('ERROR_QA'):
1134 bb.error(msg)
1135 elif 'patch-fuzz' in d.getVar('WARN_QA'):
1136 bb.warn(msg)
1137 msg = "Patch log indicates that patches do not apply cleanly."
Andrew Geisslereff27472021-10-29 15:35:00 -05001138 oe.qa.handle_error("patch-fuzz", msg, d)
Brad Bishopd89cb5f2019-04-10 09:02:41 -04001139}
1140
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001141python do_qa_configure() {
1142 import subprocess
1143
1144 ###########################################################################
1145 # Check config.log for cross compile issues
1146 ###########################################################################
1147
1148 configs = []
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001149 workdir = d.getVar('WORKDIR')
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001150
Brad Bishop19323692019-04-05 15:28:33 -04001151 skip = (d.getVar('INSANE_SKIP') or "").split()
1152 skip_configure_unsafe = False
1153 if 'configure-unsafe' in skip:
1154 bb.note("Recipe %s skipping qa checking: configure-unsafe" % d.getVar('PN'))
1155 skip_configure_unsafe = True
1156
1157 if bb.data.inherits_class('autotools', d) and not skip_configure_unsafe:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001158 bb.note("Checking autotools environment for common misconfiguration")
1159 for root, dirs, files in os.walk(workdir):
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001160 statement = "grep -q -F -e 'is unsafe for cross-compilation' %s" % \
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001161 os.path.join(root,"config.log")
1162 if "config.log" in files:
1163 if subprocess.call(statement, shell=True) == 0:
Brad Bishop19323692019-04-05 15:28:33 -04001164 error_msg = """This autoconf log indicates errors, it looked at host include and/or library paths while determining system capabilities.
1165Rerun configure task after fixing this."""
Andrew Geisslereff27472021-10-29 15:35:00 -05001166 oe.qa.handle_error("configure-unsafe", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001167
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001168 if "configure.ac" in files:
1169 configs.append(os.path.join(root,"configure.ac"))
1170 if "configure.in" in files:
1171 configs.append(os.path.join(root, "configure.in"))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001172
1173 ###########################################################################
1174 # Check gettext configuration and dependencies are correct
1175 ###########################################################################
1176
Brad Bishop19323692019-04-05 15:28:33 -04001177 skip_configure_gettext = False
1178 if 'configure-gettext' in skip:
1179 bb.note("Recipe %s skipping qa checking: configure-gettext" % d.getVar('PN'))
1180 skip_configure_gettext = True
1181
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001182 cnf = d.getVar('EXTRA_OECONF') or ""
Brad Bishop19323692019-04-05 15:28:33 -04001183 if not ("gettext" in d.getVar('P') or "gcc-runtime" in d.getVar('P') or \
1184 "--disable-nls" in cnf or skip_configure_gettext):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001185 ml = d.getVar("MLPREFIX") or ""
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001186 if bb.data.inherits_class('cross-canadian', d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001187 gt = "nativesdk-gettext"
1188 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001189 gt = "gettext-native"
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001190 deps = bb.utils.explode_deps(d.getVar('DEPENDS') or "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001191 if gt not in deps:
1192 for config in configs:
1193 gnu = "grep \"^[[:space:]]*AM_GNU_GETTEXT\" %s >/dev/null" % config
1194 if subprocess.call(gnu, shell=True) == 0:
Brad Bishopd89cb5f2019-04-10 09:02:41 -04001195 error_msg = "AM_GNU_GETTEXT used but no inherit gettext"
Andrew Geisslereff27472021-10-29 15:35:00 -05001196 oe.qa.handle_error("configure-gettext", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001197
1198 ###########################################################################
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001199 # Check unrecognised configure options (with a white list)
1200 ###########################################################################
Brad Bishopc342db32019-05-15 21:57:59 -04001201 if bb.data.inherits_class("autotools", d) or bb.data.inherits_class("meson", d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001202 bb.note("Checking configure output for unrecognised options")
1203 try:
Brad Bishopc342db32019-05-15 21:57:59 -04001204 if bb.data.inherits_class("autotools", d):
1205 flag = "WARNING: unrecognized options:"
1206 log = os.path.join(d.getVar('B'), 'config.log')
1207 if bb.data.inherits_class("meson", d):
1208 flag = "WARNING: Unknown options:"
1209 log = os.path.join(d.getVar('T'), 'log.do_configure')
1210 output = subprocess.check_output(['grep', '-F', flag, log]).decode("utf-8").replace(', ', ' ').replace('"', '')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001211 options = set()
1212 for line in output.splitlines():
1213 options |= set(line.partition(flag)[2].split())
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001214 whitelist = set(d.getVar("UNKNOWN_CONFIGURE_WHITELIST").split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001215 options -= whitelist
1216 if options:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001217 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001218 error_msg = pn + ": configure was passed unrecognised options: " + " ".join(options)
Andrew Geisslereff27472021-10-29 15:35:00 -05001219 oe.qa.handle_error("unknown-configure-option", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001220 except subprocess.CalledProcessError:
1221 pass
1222
1223 # Check invalid PACKAGECONFIG
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001224 pkgconfig = (d.getVar("PACKAGECONFIG") or "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001225 if pkgconfig:
1226 pkgconfigflags = d.getVarFlags("PACKAGECONFIG") or {}
1227 for pconfig in pkgconfig:
1228 if pconfig not in pkgconfigflags:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001229 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001230 error_msg = "%s: invalid PACKAGECONFIG: %s" % (pn, pconfig)
Andrew Geisslereff27472021-10-29 15:35:00 -05001231 oe.qa.handle_error("invalid-packageconfig", error_msg, d)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001232
Andrew Geisslereff27472021-10-29 15:35:00 -05001233 oe.qa.exit_if_errors(d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001234}
1235
1236python do_qa_unpack() {
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001237 src_uri = d.getVar('SRC_URI')
1238 s_dir = d.getVar('S')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001239 if src_uri and not os.path.exists(s_dir):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001240 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 -05001241}
1242
1243# The Staging Func, to check all staging
1244#addtask qa_staging after do_populate_sysroot before do_build
1245do_populate_sysroot[postfuncs] += "do_qa_staging "
1246
Brad Bishopd89cb5f2019-04-10 09:02:41 -04001247# Check for patch fuzz
1248do_patch[postfuncs] += "do_qa_patch "
1249
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001250# Check broken config.log files, for packages requiring Gettext which
1251# don't have it in DEPENDS.
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001252#addtask qa_configure after do_configure before do_compile
1253do_configure[postfuncs] += "do_qa_configure "
1254
1255# Check does S exist.
1256do_unpack[postfuncs] += "do_qa_unpack"
1257
1258python () {
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001259 import re
1260
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001261 tests = d.getVar('ALL_QA').split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001262 if "desktop" in tests:
1263 d.appendVar("PACKAGE_DEPENDS", " desktop-file-utils-native")
1264
1265 ###########################################################################
1266 # Check various variables
1267 ###########################################################################
1268
1269 # Checking ${FILESEXTRAPATHS}
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001270 extrapaths = (d.getVar("FILESEXTRAPATHS") or "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001271 if '__default' not in extrapaths.split(":"):
Patrick Williams213cb262021-08-07 19:21:33 -05001272 msg = "FILESEXTRAPATHS-variable, must always use :prepend (or :append)\n"
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001273 msg += "type of assignment, and don't forget the colon.\n"
1274 msg += "Please assign it with the format of:\n"
Patrick Williams213cb262021-08-07 19:21:33 -05001275 msg += " FILESEXTRAPATHS:append := \":${THISDIR}/Your_Files_Path\" or\n"
1276 msg += " FILESEXTRAPATHS:prepend := \"${THISDIR}/Your_Files_Path:\"\n"
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001277 msg += "in your bbappend file\n\n"
1278 msg += "Your incorrect assignment is:\n"
1279 msg += "%s\n" % extrapaths
1280 bb.warn(msg)
1281
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001282 overrides = d.getVar('OVERRIDES').split(':')
1283 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001284 if pn in overrides:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001285 msg = 'Recipe %s has PN of "%s" which is in OVERRIDES, this can result in unexpected behaviour.' % (d.getVar("FILE"), pn)
Andrew Geisslereff27472021-10-29 15:35:00 -05001286 oe.qa.handle_error("pn-overrides", msg, d)
Brad Bishop977dc1a2019-02-06 16:01:43 -05001287 prog = re.compile(r'[A-Z]')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001288 if prog.search(pn):
Andrew Geisslereff27472021-10-29 15:35:00 -05001289 oe.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 -05001290
Patrick Williams0ca19cc2021-08-16 14:03:13 -05001291 # Some people mistakenly use DEPENDS:${PN} instead of DEPENDS and wonder
Brad Bishop08902b02019-08-20 09:16:51 -04001292 # why it doesn't work.
Patrick Williams0ca19cc2021-08-16 14:03:13 -05001293 if (d.getVar(d.expand('DEPENDS:${PN}'))):
Andrew Geisslereff27472021-10-29 15:35:00 -05001294 oe.qa.handle_error("pkgvarcheck", "recipe uses DEPENDS:${PN}, should use DEPENDS", d)
Brad Bishop08902b02019-08-20 09:16:51 -04001295
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001296 issues = []
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001297 if (d.getVar('PACKAGES') or "").split():
1298 for dep in (d.getVar('QADEPENDS') or "").split():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001299 d.appendVarFlag('do_package_qa', 'depends', " %s:do_populate_sysroot" % dep)
1300 for var in 'RDEPENDS', 'RRECOMMENDS', 'RSUGGESTS', 'RCONFLICTS', 'RPROVIDES', 'RREPLACES', 'FILES', 'pkg_preinst', 'pkg_postinst', 'pkg_prerm', 'pkg_postrm', 'ALLOW_EMPTY':
1301 if d.getVar(var, False):
1302 issues.append(var)
1303
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001304 fakeroot_tests = d.getVar('FAKEROOT_QA').split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001305 if set(tests) & set(fakeroot_tests):
1306 d.setVarFlag('do_package_qa', 'fakeroot', '1')
1307 d.appendVarFlag('do_package_qa', 'depends', ' virtual/fakeroot-native:do_populate_sysroot')
1308 else:
1309 d.setVarFlag('do_package_qa', 'rdeptask', '')
1310 for i in issues:
Andrew Geisslereff27472021-10-29 15:35:00 -05001311 oe.qa.handle_error("pkgvarcheck", "%s: Variable %s is set as not being package specific, please fix this." % (d.getVar("FILE"), i), d)
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001312
1313 if 'native-last' not in (d.getVar('INSANE_SKIP') or "").split():
1314 for native_class in ['native', 'nativesdk']:
1315 if bb.data.inherits_class(native_class, d):
1316
1317 inherited_classes = d.getVar('__inherit_cache', False) or []
1318 needle = os.path.join('classes', native_class)
1319
1320 bbclassextend = (d.getVar('BBCLASSEXTEND') or '').split()
1321 # BBCLASSEXTEND items are always added in the end
1322 skip_classes = bbclassextend
1323 if bb.data.inherits_class('native', d) or 'native' in bbclassextend:
1324 # native also inherits nopackages and relocatable bbclasses
1325 skip_classes.extend(['nopackages', 'relocatable'])
1326
1327 broken_order = []
1328 for class_item in reversed(inherited_classes):
1329 if needle not in class_item:
1330 for extend_item in skip_classes:
1331 if os.path.join('classes', '%s.bbclass' % extend_item) in class_item:
1332 break
1333 else:
1334 pn = d.getVar('PN')
1335 broken_order.append(os.path.basename(class_item))
1336 else:
1337 break
1338 if broken_order:
Andrew Geisslereff27472021-10-29 15:35:00 -05001339 oe.qa.handle_error("native-last", "%s: native/nativesdk class is not inherited last, this can result in unexpected behaviour. "
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001340 "Classes inherited after native/nativesdk: %s" % (pn, " ".join(broken_order)), d)
1341
Andrew Geisslereff27472021-10-29 15:35:00 -05001342 oe.qa.exit_if_errors(d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001343}