blob: 890e865a8f379a182ab3f73d72af5a838e993aa5 [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 \
Andrew Geissler595f6302022-01-24 19:11:47 +000040 useless-rpaths rpaths staticdev empty-dirs \
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
Andrew Geissler7e0e3c02022-02-25 20:34:39 +000051UNKNOWN_CONFIGURE_OPT_IGNORE ?= "--enable-nls --disable-nls --disable-silent-rules --disable-dependency-tracking --with-libtool-sysroot --disable-static"
Patrick Williamsc124f4f2015-09-15 14:41:29 -050052
Andrew Geissler595f6302022-01-24 19:11:47 +000053# This is a list of directories that are expected to be empty.
54QA_EMPTY_DIRS ?= " \
55 /dev/pts \
56 /media \
57 /proc \
58 /run \
59 /tmp \
60 ${localstatedir}/run \
61 ${localstatedir}/volatile \
62"
63# It is possible to specify why a directory is expected to be empty by defining
64# QA_EMPTY_DIRS_RECOMMENDATION:<path>, which will then be included in the error
65# message if the directory is not empty. If it is not specified for a directory,
66# then "but it is expected to be empty" will be used.
67
Patrick Williamsc0f7c042017-02-23 20:41:17 -060068def package_qa_clean_path(path, d, pkg=None):
69 """
70 Remove redundant paths from the path for display. If pkg isn't set then
71 TMPDIR is stripped, otherwise PKGDEST/pkg is stripped.
72 """
73 if pkg:
Brad Bishop6e60e8b2018-02-01 10:27:11 -050074 path = path.replace(os.path.join(d.getVar("PKGDEST"), pkg), "/")
75 return path.replace(d.getVar("TMPDIR"), "/").replace("//", "/")
Patrick Williamsc124f4f2015-09-15 14:41:29 -050076
Andrew Geissler82c905d2020-04-13 13:39:40 -050077QAPATHTEST[shebang-size] = "package_qa_check_shebang_size"
78def package_qa_check_shebang_size(path, name, d, elf, messages):
Andrew Geisslerd1e89492021-02-12 15:35:20 -060079 import stat
80 if os.path.islink(path) or stat.S_ISFIFO(os.stat(path).st_mode) or elf:
Andrew Geissler82c905d2020-04-13 13:39:40 -050081 return
82
83 try:
84 with open(path, 'rb') as f:
85 stanza = f.readline(130)
86 except IOError:
87 return
88
89 if stanza.startswith(b'#!'):
90 #Shebang not found
91 try:
92 stanza = stanza.decode("utf-8")
93 except UnicodeDecodeError:
94 #If it is not a text file, it is not a script
95 return
96
97 if len(stanza) > 129:
Andrew Geisslereff27472021-10-29 15:35:00 -050098 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 -050099 return
100
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500101QAPATHTEST[libexec] = "package_qa_check_libexec"
102def package_qa_check_libexec(path,name, d, elf, messages):
103
104 # Skip the case where the default is explicitly /usr/libexec
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500105 libexec = d.getVar('libexecdir')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500106 if libexec == "/usr/libexec":
107 return True
108
109 if 'libexec' in path.split(os.path.sep):
Andrew Geisslereff27472021-10-29 15:35:00 -0500110 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 -0500111 return False
112
113 return True
114
115QAPATHTEST[rpaths] = "package_qa_check_rpath"
116def package_qa_check_rpath(file,name, d, elf, messages):
117 """
118 Check for dangerous RPATHs
119 """
120 if not elf:
121 return
122
123 if os.path.islink(file):
124 return
125
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500126 bad_dirs = [d.getVar('BASE_WORKDIR'), d.getVar('STAGING_DIR_TARGET')]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500127
128 phdrs = elf.run_objdump("-p", d)
129
130 import re
Brad Bishop977dc1a2019-02-06 16:01:43 -0500131 rpath_re = re.compile(r"\s+RPATH\s+(.*)")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500132 for line in phdrs.split("\n"):
133 m = rpath_re.match(line)
134 if m:
135 rpath = m.group(1)
136 for dir in bad_dirs:
137 if dir in rpath:
Andrew Geisslereff27472021-10-29 15:35:00 -0500138 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 -0500139
140QAPATHTEST[useless-rpaths] = "package_qa_check_useless_rpaths"
141def package_qa_check_useless_rpaths(file, name, d, elf, messages):
142 """
143 Check for RPATHs that are useless but not dangerous
144 """
145 def rpath_eq(a, b):
146 return os.path.normpath(a) == os.path.normpath(b)
147
148 if not elf:
149 return
150
151 if os.path.islink(file):
152 return
153
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500154 libdir = d.getVar("libdir")
155 base_libdir = d.getVar("base_libdir")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500156
157 phdrs = elf.run_objdump("-p", d)
158
159 import re
Brad Bishop977dc1a2019-02-06 16:01:43 -0500160 rpath_re = re.compile(r"\s+RPATH\s+(.*)")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500161 for line in phdrs.split("\n"):
162 m = rpath_re.match(line)
163 if m:
164 rpath = m.group(1)
165 if rpath_eq(rpath, libdir) or rpath_eq(rpath, base_libdir):
166 # The dynamic linker searches both these places anyway. There is no point in
167 # looking there again.
Andrew Geisslereff27472021-10-29 15:35:00 -0500168 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 -0500169
170QAPATHTEST[dev-so] = "package_qa_check_dev"
171def package_qa_check_dev(path, name, d, elf, messages):
172 """
173 Check for ".so" library symlinks in non-dev packages
174 """
175
176 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 -0500177 oe.qa.add_message(messages, "dev-so", "non -dev/-dbg/nativesdk- package %s contains symlink .so '%s'" % \
Andrew Geisslerc926e172021-05-07 16:11:35 -0500178 (name, package_qa_clean_path(path, d, name)))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500179
180QAPATHTEST[dev-elf] = "package_qa_check_dev_elf"
181def package_qa_check_dev_elf(path, name, d, elf, messages):
182 """
183 Check that -dev doesn't contain real shared libraries. The test has to
184 check that the file is not a link and is an ELF object as some recipes
185 install link-time .so files that are linker scripts.
186 """
187 if name.endswith("-dev") and path.endswith(".so") and not os.path.islink(path) and elf:
Andrew Geisslereff27472021-10-29 15:35:00 -0500188 oe.qa.add_message(messages, "dev-elf", "-dev package %s contains non-symlink .so '%s'" % \
Andrew Geisslerc926e172021-05-07 16:11:35 -0500189 (name, package_qa_clean_path(path, d, name)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500190
191QAPATHTEST[staticdev] = "package_qa_check_staticdev"
192def package_qa_check_staticdev(path, name, d, elf, messages):
193 """
194 Check for ".a" library in non-staticdev packages
195 There are a number of exceptions to this rule, -pic packages can contain
196 static libraries, the _nonshared.a belong with their -dev packages and
197 libgcc.a, libgcov.a will be skipped in their packages
198 """
199
Andrew Geissler82c905d2020-04-13 13:39:40 -0500200 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 -0500201 oe.qa.add_message(messages, "staticdev", "non -staticdev package contains static .a library: %s path '%s'" % \
Andrew Geisslerc926e172021-05-07 16:11:35 -0500202 (name, package_qa_clean_path(path,d, name)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500203
Andrew Geissler82c905d2020-04-13 13:39:40 -0500204QAPATHTEST[mime] = "package_qa_check_mime"
205def package_qa_check_mime(path, name, d, elf, messages):
206 """
207 Check if package installs mime types to /usr/share/mime/packages
208 while no inheriting mime.bbclass
209 """
210
211 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 -0500212 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 -0500213 (name, package_qa_clean_path(path,d)))
214
215QAPATHTEST[mime-xdg] = "package_qa_check_mime_xdg"
216def package_qa_check_mime_xdg(path, name, d, elf, messages):
217 """
218 Check if package installs desktop file containing MimeType and requires
219 mime-types.bbclass to create /usr/share/applications/mimeinfo.cache
220 """
221
222 if d.getVar("datadir") + "/applications" in path and path.endswith('.desktop') and not bb.data.inherits_class("mime-xdg", d):
223 mime_type_found = False
224 try:
225 with open(path, 'r') as f:
226 for line in f.read().split('\n'):
227 if 'MimeType' in line:
228 mime_type_found = True
229 break;
230 except:
231 # At least libreoffice installs symlinks with absolute paths that are dangling here.
232 # We could implement some magic but for few (one) recipes it is not worth the effort so just warn:
233 wstr = "%s cannot open %s - is it a symlink with absolute path?\n" % (name, package_qa_clean_path(path,d))
234 wstr += "Please check if (linked) file contains key 'MimeType'.\n"
235 pkgname = name
236 if name == d.getVar('PN'):
237 pkgname = '${PN}'
Patrick Williams213cb262021-08-07 19:21:33 -0500238 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 -0500239 oe.qa.add_message(messages, "mime-xdg", wstr)
Andrew Geissler82c905d2020-04-13 13:39:40 -0500240 if mime_type_found:
Andrew Geisslereff27472021-10-29 15:35:00 -0500241 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 -0500242 (name, package_qa_clean_path(path,d)))
243
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500244def package_qa_check_libdir(d):
245 """
246 Check for wrong library installation paths. For instance, catch
247 recipes installing /lib/bar.so when ${base_libdir}="lib32" or
248 installing in /usr/lib64 when ${libdir}="/usr/lib"
249 """
250 import re
251
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500252 pkgdest = d.getVar('PKGDEST')
253 base_libdir = d.getVar("base_libdir") + os.sep
254 libdir = d.getVar("libdir") + os.sep
255 libexecdir = d.getVar("libexecdir") + os.sep
256 exec_prefix = d.getVar("exec_prefix") + os.sep
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500257
258 messages = []
259
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500260 # The re's are purposely fuzzy, as some there are some .so.x.y.z files
261 # that don't follow the standard naming convention. It checks later
262 # that they are actual ELF files
Brad Bishop977dc1a2019-02-06 16:01:43 -0500263 lib_re = re.compile(r"^/lib.+\.so(\..+)?$")
264 exec_re = re.compile(r"^%s.*/lib.+\.so(\..+)?$" % exec_prefix)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500265
266 for root, dirs, files in os.walk(pkgdest):
267 if root == pkgdest:
268 # Skip subdirectories for any packages with libdir in INSANE_SKIP
269 skippackages = []
270 for package in dirs:
Patrick Williams213cb262021-08-07 19:21:33 -0500271 if 'libdir' in (d.getVar('INSANE_SKIP:' + package) or "").split():
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500272 bb.note("Package %s skipping libdir QA test" % (package))
273 skippackages.append(package)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500274 elif d.getVar('PACKAGE_DEBUG_SPLIT_STYLE') == 'debug-file-directory' and package.endswith("-dbg"):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500275 bb.note("Package %s skipping libdir QA test for PACKAGE_DEBUG_SPLIT_STYLE equals debug-file-directory" % (package))
276 skippackages.append(package)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500277 for package in skippackages:
278 dirs.remove(package)
279 for file in files:
280 full_path = os.path.join(root, file)
281 rel_path = os.path.relpath(full_path, pkgdest)
282 if os.sep in rel_path:
283 package, rel_path = rel_path.split(os.sep, 1)
284 rel_path = os.sep + rel_path
285 if lib_re.match(rel_path):
286 if base_libdir not in rel_path:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500287 # make sure it's an actual ELF file
288 elf = oe.qa.ELFFile(full_path)
289 try:
290 elf.open()
291 messages.append("%s: found library in wrong location: %s" % (package, rel_path))
292 except (oe.qa.NotELFFileError):
293 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500294 if exec_re.match(rel_path):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500295 if libdir not in rel_path and libexecdir not in rel_path:
296 # make sure it's an actual ELF file
297 elf = oe.qa.ELFFile(full_path)
298 try:
299 elf.open()
300 messages.append("%s: found library in wrong location: %s" % (package, rel_path))
301 except (oe.qa.NotELFFileError):
302 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500303
304 if messages:
Andrew Geisslereff27472021-10-29 15:35:00 -0500305 oe.qa.handle_error("libdir", "\n".join(messages), d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500306
307QAPATHTEST[debug-files] = "package_qa_check_dbg"
308def package_qa_check_dbg(path, name, d, elf, messages):
309 """
310 Check for ".debug" files or directories outside of the dbg package
311 """
312
313 if not "-dbg" in name and not "-ptest" in name:
314 if '.debug' in path.split(os.path.sep):
Andrew Geisslereff27472021-10-29 15:35:00 -0500315 oe.qa.add_message(messages, "debug-files", "non debug package contains .debug directory: %s path %s" % \
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500316 (name, package_qa_clean_path(path,d)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500317
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500318QAPATHTEST[arch] = "package_qa_check_arch"
319def package_qa_check_arch(path,name,d, elf, messages):
320 """
321 Check if archs are compatible
322 """
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800323 import re, oe.elf
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600324
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500325 if not elf:
326 return
327
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000328 target_os = d.getVar('HOST_OS')
329 target_arch = d.getVar('HOST_ARCH')
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500330 provides = d.getVar('PROVIDES')
331 bpn = d.getVar('BPN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500332
333 if target_arch == "allarch":
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500334 pn = d.getVar('PN')
Andrew Geisslereff27472021-10-29 15:35:00 -0500335 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 -0500336 return
337
338 # FIXME: Cross package confuse this check, so just skip them
339 for s in ['cross', 'nativesdk', 'cross-canadian']:
340 if bb.data.inherits_class(s, d):
341 return
342
343 # avoid following links to /usr/bin (e.g. on udev builds)
344 # we will check the files pointed to anyway...
345 if os.path.islink(path):
346 return
347
348 #if this will throw an exception, then fix the dict above
349 (machine, osabi, abiversion, littleendian, bits) \
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800350 = oe.elf.machine_dict(d)[target_os][target_arch]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500351
352 # Check the architecture and endiannes of the binary
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600353 is_32 = (("virtual/kernel" in provides) or bb.data.inherits_class("module", d)) and \
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800354 (target_os == "linux-gnux32" or target_os == "linux-muslx32" or \
Brad Bishop977dc1a2019-02-06 16:01:43 -0500355 target_os == "linux-gnu_ilp32" or re.match(r'mips64.*32', d.getVar('DEFAULTTUNE')))
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800356 is_bpf = (oe.qa.elf_machine_to_string(elf.machine()) == "BPF")
357 if not ((machine == elf.machine()) or is_32 or is_bpf):
Andrew Geisslereff27472021-10-29 15:35:00 -0500358 oe.qa.add_message(messages, "arch", "Architecture did not match (%s, expected %s) in %s" % \
Andrew Geisslerb7d28612020-07-24 16:15:54 -0500359 (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 -0400360 elif not ((bits == elf.abiSize()) or is_32 or is_bpf):
Andrew Geisslereff27472021-10-29 15:35:00 -0500361 oe.qa.add_message(messages, "arch", "Bit size did not match (%d, expected %d) in %s" % \
Andrew Geisslerb7d28612020-07-24 16:15:54 -0500362 (elf.abiSize(), bits, package_qa_clean_path(path, d, name)))
Brad Bishop19323692019-04-05 15:28:33 -0400363 elif not ((littleendian == elf.isLittleEndian()) or is_bpf):
Andrew Geisslereff27472021-10-29 15:35:00 -0500364 oe.qa.add_message(messages, "arch", "Endiannes did not match (%d, expected %d) in %s" % \
Andrew Geisslerb7d28612020-07-24 16:15:54 -0500365 (elf.isLittleEndian(), littleendian, package_qa_clean_path(path,d, name)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500366
367QAPATHTEST[desktop] = "package_qa_check_desktop"
368def package_qa_check_desktop(path, name, d, elf, messages):
369 """
370 Run all desktop files through desktop-file-validate.
371 """
372 if path.endswith(".desktop"):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500373 desktop_file_validate = os.path.join(d.getVar('STAGING_BINDIR_NATIVE'),'desktop-file-validate')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500374 output = os.popen("%s %s" % (desktop_file_validate, path))
375 # This only produces output on errors
376 for l in output:
Andrew Geisslereff27472021-10-29 15:35:00 -0500377 oe.qa.add_message(messages, "desktop", "Desktop file issue: " + l.strip())
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500378
379QAPATHTEST[textrel] = "package_qa_textrel"
380def package_qa_textrel(path, name, d, elf, messages):
381 """
382 Check if the binary contains relocations in .text
383 """
384
385 if not elf:
386 return
387
388 if os.path.islink(path):
389 return
390
391 phdrs = elf.run_objdump("-p", d)
392 sane = True
393
394 import re
Brad Bishop977dc1a2019-02-06 16:01:43 -0500395 textrel_re = re.compile(r"\s+TEXTREL\s+")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500396 for line in phdrs.split("\n"):
397 if textrel_re.match(line):
398 sane = False
Brad Bishop1d80a2e2019-11-15 16:35:03 -0500399 break
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500400
401 if not sane:
Brad Bishop1d80a2e2019-11-15 16:35:03 -0500402 path = package_qa_clean_path(path, d, name)
Andrew Geisslereff27472021-10-29 15:35:00 -0500403 oe.qa.add_message(messages, "textrel", "%s: ELF binary %s has relocations in .text" % (name, path))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500404
405QAPATHTEST[ldflags] = "package_qa_hash_style"
406def package_qa_hash_style(path, name, d, elf, messages):
407 """
408 Check if the binary has the right hash style...
409 """
410
411 if not elf:
412 return
413
414 if os.path.islink(path):
415 return
416
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500417 gnu_hash = "--hash-style=gnu" in d.getVar('LDFLAGS')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500418 if not gnu_hash:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500419 gnu_hash = "--hash-style=both" in d.getVar('LDFLAGS')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500420 if not gnu_hash:
421 return
422
423 sane = False
424 has_syms = False
425
426 phdrs = elf.run_objdump("-p", d)
427
428 # If this binary has symbols, we expect it to have GNU_HASH too.
429 for line in phdrs.split("\n"):
430 if "SYMTAB" in line:
431 has_syms = True
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500432 if "GNU_HASH" in line or "DT_MIPS_XHASH" in line:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500433 sane = True
Andrew Geissler82c905d2020-04-13 13:39:40 -0500434 if ("[mips32]" in line or "[mips64]" in line) and d.getVar('TCLIBC') == "musl":
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500435 sane = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500436 if has_syms and not sane:
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500437 path = package_qa_clean_path(path, d, name)
Andrew Geisslereff27472021-10-29 15:35:00 -0500438 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 -0500439
440
441QAPATHTEST[buildpaths] = "package_qa_check_buildpaths"
442def package_qa_check_buildpaths(path, name, d, elf, messages):
443 """
444 Check for build paths inside target files and error if not found in the whitelist
445 """
446 # Ignore .debug files, not interesting
447 if path.find(".debug") != -1:
448 return
449
450 # Ignore symlinks
451 if os.path.islink(path):
452 return
453
Brad Bishopd5ae7d92018-06-14 09:52:03 -0700454 tmpdir = bytes(d.getVar('TMPDIR'), encoding="utf-8")
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500455 with open(path, 'rb') as f:
Brad Bishopd5ae7d92018-06-14 09:52:03 -0700456 file_content = f.read()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500457 if tmpdir in file_content:
Brad Bishopf3fd2882019-06-21 08:06:37 -0400458 trimmed = path.replace(os.path.join (d.getVar("PKGDEST"), name), "")
Andrew Geisslereff27472021-10-29 15:35:00 -0500459 oe.qa.add_message(messages, "buildpaths", "File %s in package %s contains reference to TMPDIR" % (trimmed, name))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500460
461
462QAPATHTEST[xorg-driver-abi] = "package_qa_check_xorg_driver_abi"
463def package_qa_check_xorg_driver_abi(path, name, d, elf, messages):
464 """
465 Check that all packages containing Xorg drivers have ABI dependencies
466 """
467
468 # Skip dev, dbg or nativesdk packages
469 if name.endswith("-dev") or name.endswith("-dbg") or name.startswith("nativesdk-"):
470 return
471
472 driverdir = d.expand("${libdir}/xorg/modules/drivers/")
473 if driverdir in path and path.endswith(".so"):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500474 mlprefix = d.getVar('MLPREFIX') or ''
Patrick Williams213cb262021-08-07 19:21:33 -0500475 for rdep in bb.utils.explode_deps(d.getVar('RDEPENDS:' + name) or ""):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500476 if rdep.startswith("%sxorg-abi-" % mlprefix):
477 return
Andrew Geisslereff27472021-10-29 15:35:00 -0500478 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 -0500479
480QAPATHTEST[infodir] = "package_qa_check_infodir"
481def package_qa_check_infodir(path, name, d, elf, messages):
482 """
483 Check that /usr/share/info/dir isn't shipped in a particular package
484 """
485 infodir = d.expand("${infodir}/dir")
486
487 if infodir in path:
Andrew Geisslereff27472021-10-29 15:35:00 -0500488 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 -0500489
490QAPATHTEST[symlink-to-sysroot] = "package_qa_check_symlink_to_sysroot"
491def package_qa_check_symlink_to_sysroot(path, name, d, elf, messages):
492 """
493 Check that the package doesn't contain any absolute symlinks to the sysroot.
494 """
495 if os.path.islink(path):
496 target = os.readlink(path)
497 if os.path.isabs(target):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500498 tmpdir = d.getVar('TMPDIR')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500499 if target.startswith(tmpdir):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500500 trimmed = path.replace(os.path.join (d.getVar("PKGDEST"), name), "")
Andrew Geisslereff27472021-10-29 15:35:00 -0500501 oe.qa.add_message(messages, "symlink-to-sysroot", "Symlink %s in %s points to TMPDIR" % (trimmed, name))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500502
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600503# Check license variables
504do_populate_lic[postfuncs] += "populate_lic_qa_checksum"
505python populate_lic_qa_checksum() {
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500506 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600507 Check for changes in the license files.
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500508 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500509
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500510 lic_files = d.getVar('LIC_FILES_CHKSUM') or ''
511 lic = d.getVar('LICENSE')
512 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500513
514 if lic == "CLOSED":
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500515 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500516
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500517 if not lic_files and d.getVar('SRC_URI'):
Andrew Geisslereff27472021-10-29 15:35:00 -0500518 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 -0500519
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500520 srcdir = d.getVar('S')
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500521 corebase_licensefile = d.getVar('COREBASE') + "/LICENSE"
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500522 for url in lic_files.split():
523 try:
524 (type, host, path, user, pswd, parm) = bb.fetch.decodeurl(url)
525 except bb.fetch.MalformedUrl:
Andrew Geisslereff27472021-10-29 15:35:00 -0500526 oe.qa.handle_error("license-checksum", pn + ": LIC_FILES_CHKSUM contains an invalid URL: " + url, d)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500527 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500528 srclicfile = os.path.join(srcdir, path)
529 if not os.path.isfile(srclicfile):
Andrew Geisslereff27472021-10-29 15:35:00 -0500530 oe.qa.handle_error("license-checksum", pn + ": LIC_FILES_CHKSUM points to an invalid file: " + srclicfile, d)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500531 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500532
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500533 if (srclicfile == corebase_licensefile):
534 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")
535
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500536 recipemd5 = parm.get('md5', '')
537 beginline, endline = 0, 0
538 if 'beginline' in parm:
539 beginline = int(parm['beginline'])
540 if 'endline' in parm:
541 endline = int(parm['endline'])
542
543 if (not beginline) and (not endline):
544 md5chksum = bb.utils.md5_file(srclicfile)
Brad Bishop19323692019-04-05 15:28:33 -0400545 with open(srclicfile, 'r', errors='replace') as f:
546 license = f.read().splitlines()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500547 else:
Brad Bishop19323692019-04-05 15:28:33 -0400548 with open(srclicfile, 'rb') as f:
549 import hashlib
550 lineno = 0
551 license = []
552 m = hashlib.md5()
553 for line in f:
554 lineno += 1
555 if (lineno >= beginline):
556 if ((lineno <= endline) or not endline):
557 m.update(line)
558 license.append(line.decode('utf-8', errors='replace').rstrip())
559 else:
560 break
561 md5chksum = m.hexdigest()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500562 if recipemd5 == md5chksum:
563 bb.note (pn + ": md5 checksum matched for ", url)
564 else:
565 if recipemd5:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500566 msg = pn + ": The LIC_FILES_CHKSUM does not match for " + url
567 msg = msg + "\n" + pn + ": The new md5 checksum is " + md5chksum
Brad Bishop19323692019-04-05 15:28:33 -0400568 max_lines = int(d.getVar('QA_MAX_LICENSE_LINES') or 20)
569 if not license or license[-1] != '':
570 # Ensure that our license text ends with a line break
571 # (will be added with join() below).
572 license.append('')
573 remove = len(license) - max_lines
574 if remove > 0:
575 start = max_lines // 2
576 end = start + remove - 1
577 del license[start:end]
578 license.insert(start, '...')
579 msg = msg + "\n" + pn + ": Here is the selected license text:" + \
580 "\n" + \
581 "{:v^70}".format(" beginline=%d " % beginline if beginline else "") + \
582 "\n" + "\n".join(license) + \
583 "{:^^70}".format(" endline=%d " % endline if endline else "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500584 if beginline:
585 if endline:
586 srcfiledesc = "%s (lines %d through to %d)" % (srclicfile, beginline, endline)
587 else:
588 srcfiledesc = "%s (beginning on line %d)" % (srclicfile, beginline)
589 elif endline:
590 srcfiledesc = "%s (ending on line %d)" % (srclicfile, endline)
591 else:
592 srcfiledesc = srclicfile
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500593 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 -0500594
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500595 else:
596 msg = pn + ": LIC_FILES_CHKSUM is not specified for " + url
597 msg = msg + "\n" + pn + ": The md5 checksum is " + md5chksum
Andrew Geisslereff27472021-10-29 15:35:00 -0500598 oe.qa.handle_error("license-checksum", msg, d)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600599
Andrew Geisslereff27472021-10-29 15:35:00 -0500600 oe.qa.exit_if_errors(d)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600601}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500602
Brad Bishop19323692019-04-05 15:28:33 -0400603def qa_check_staged(path,d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500604 """
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500605 Check staged la and pc files for common problems like references to the work
606 directory.
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500607
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500608 As this is run after every stage we should be able to find the one
609 responsible for the errors easily even if we look at every .pc and .la file.
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500610 """
611
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500612 tmpdir = d.getVar('TMPDIR')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500613 workdir = os.path.join(tmpdir, "work")
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500614 recipesysroot = d.getVar("RECIPE_SYSROOT")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500615
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500616 if bb.data.inherits_class("native", d) or bb.data.inherits_class("cross", d):
617 pkgconfigcheck = workdir
618 else:
619 pkgconfigcheck = tmpdir
620
Brad Bishop19323692019-04-05 15:28:33 -0400621 skip = (d.getVar('INSANE_SKIP') or "").split()
622 skip_la = False
623 if 'la' in skip:
624 bb.note("Recipe %s skipping qa checking: la" % d.getVar('PN'))
625 skip_la = True
626
627 skip_pkgconfig = False
628 if 'pkgconfig' in skip:
629 bb.note("Recipe %s skipping qa checking: pkgconfig" % d.getVar('PN'))
630 skip_pkgconfig = True
631
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500632 # find all .la and .pc files
633 # read the content
634 # and check for stuff that looks wrong
635 for root, dirs, files in os.walk(path):
636 for file in files:
637 path = os.path.join(root,file)
Brad Bishop19323692019-04-05 15:28:33 -0400638 if file.endswith(".la") and not skip_la:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500639 with open(path) as f:
640 file_content = f.read()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500641 file_content = file_content.replace(recipesysroot, "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500642 if workdir in file_content:
643 error_msg = "%s failed sanity test (workdir) in path %s" % (file,root)
Andrew Geisslereff27472021-10-29 15:35:00 -0500644 oe.qa.handle_error("la", error_msg, d)
Brad Bishop19323692019-04-05 15:28:33 -0400645 elif file.endswith(".pc") and not skip_pkgconfig:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500646 with open(path) as f:
647 file_content = f.read()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500648 file_content = file_content.replace(recipesysroot, "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500649 if pkgconfigcheck in file_content:
650 error_msg = "%s failed sanity test (tmpdir) in path %s" % (file,root)
Andrew Geisslereff27472021-10-29 15:35:00 -0500651 oe.qa.handle_error("pkgconfig", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500652
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500653# Run all package-wide warnfuncs and errorfuncs
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500654def package_qa_package(warnfuncs, errorfuncs, package, d):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500655 warnings = {}
656 errors = {}
657
658 for func in warnfuncs:
659 func(package, d, warnings)
660 for func in errorfuncs:
661 func(package, d, errors)
662
663 for w in warnings:
Andrew Geisslereff27472021-10-29 15:35:00 -0500664 oe.qa.handle_error(w, warnings[w], d)
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500665 for e in errors:
Andrew Geisslereff27472021-10-29 15:35:00 -0500666 oe.qa.handle_error(e, errors[e], d)
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500667
668 return len(errors) == 0
669
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500670# Run all recipe-wide warnfuncs and errorfuncs
671def package_qa_recipe(warnfuncs, errorfuncs, pn, d):
672 warnings = {}
673 errors = {}
674
675 for func in warnfuncs:
676 func(pn, d, warnings)
677 for func in errorfuncs:
678 func(pn, d, errors)
679
680 for w in warnings:
Andrew Geisslereff27472021-10-29 15:35:00 -0500681 oe.qa.handle_error(w, warnings[w], d)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500682 for e in errors:
Andrew Geisslereff27472021-10-29 15:35:00 -0500683 oe.qa.handle_error(e, errors[e], d)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500684
685 return len(errors) == 0
686
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000687def prepopulate_objdump_p(elf, d):
688 output = elf.run_objdump("-p", d)
689 return (elf.name, output)
690
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500691# Walk over all files in a directory and call func
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500692def package_qa_walk(warnfuncs, errorfuncs, package, d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500693 #if this will throw an exception, then fix the dict above
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000694 target_os = d.getVar('HOST_OS')
695 target_arch = d.getVar('HOST_ARCH')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500696
697 warnings = {}
698 errors = {}
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000699 elves = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500700 for path in pkgfiles[package]:
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500701 elf = None
702 if os.path.isfile(path):
703 elf = oe.qa.ELFFile(path)
704 try:
705 elf.open()
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000706 elf.close()
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500707 except oe.qa.NotELFFileError:
708 elf = None
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000709 if elf:
710 elves[path] = elf
711
712 results = oe.utils.multiprocess_launch(prepopulate_objdump_p, elves.values(), d, extraargs=(d,))
713 for item in results:
714 elves[item[0]].set_objdump("-p", item[1])
715
716 for path in pkgfiles[package]:
717 if path in elves:
718 elves[path].open()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500719 for func in warnfuncs:
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000720 func(path, package, d, elves.get(path), warnings)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500721 for func in errorfuncs:
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000722 func(path, package, d, elves.get(path), errors)
723 if path in elves:
724 elves[path].close()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500725
726 for w in warnings:
Andrew Geisslereff27472021-10-29 15:35:00 -0500727 oe.qa.handle_error(w, warnings[w], d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500728 for e in errors:
Andrew Geisslereff27472021-10-29 15:35:00 -0500729 oe.qa.handle_error(e, errors[e], d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500730
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500731def package_qa_check_rdepends(pkg, pkgdest, skip, taskdeps, packages, d):
732 # Don't do this check for kernel/module recipes, there aren't too many debug/development
733 # packages and you can get false positives e.g. on kernel-module-lirc-dev
734 if bb.data.inherits_class("kernel", d) or bb.data.inherits_class("module-base", d):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500735 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500736
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500737 if not "-dbg" in pkg and not "packagegroup-" in pkg and not "-image" in pkg:
738 localdata = bb.data.createCopy(d)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500739 localdata.setVar('OVERRIDES', localdata.getVar('OVERRIDES') + ':' + pkg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500740
741 # Now check the RDEPENDS
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500742 rdepends = bb.utils.explode_deps(localdata.getVar('RDEPENDS') or "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500743
744 # Now do the sanity check!!!
745 if "build-deps" not in skip:
746 for rdepend in rdepends:
747 if "-dbg" in rdepend and "debug-deps" not in skip:
748 error_msg = "%s rdepends on %s" % (pkg,rdepend)
Andrew Geisslereff27472021-10-29 15:35:00 -0500749 oe.qa.handle_error("debug-deps", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500750 if (not "-dev" in pkg and not "-staticdev" in pkg) and rdepend.endswith("-dev") and "dev-deps" not in skip:
751 error_msg = "%s rdepends on %s" % (pkg, rdepend)
Andrew Geisslereff27472021-10-29 15:35:00 -0500752 oe.qa.handle_error("dev-deps", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500753 if rdepend not in packages:
754 rdep_data = oe.packagedata.read_subpkgdata(rdepend, d)
755 if rdep_data and 'PN' in rdep_data and rdep_data['PN'] in taskdeps:
756 continue
757 if not rdep_data or not 'PN' in rdep_data:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500758 pkgdata_dir = d.getVar("PKGDATA_DIR")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500759 try:
760 possibles = os.listdir("%s/runtime-rprovides/%s/" % (pkgdata_dir, rdepend))
761 except OSError:
762 possibles = []
763 for p in possibles:
764 rdep_data = oe.packagedata.read_subpkgdata(p, d)
765 if rdep_data and 'PN' in rdep_data and rdep_data['PN'] in taskdeps:
766 break
767 if rdep_data and 'PN' in rdep_data and rdep_data['PN'] in taskdeps:
768 continue
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500769 if rdep_data and 'PN' in rdep_data:
770 error_msg = "%s rdepends on %s, but it isn't a build dependency, missing %s in DEPENDS or PACKAGECONFIG?" % (pkg, rdepend, rdep_data['PN'])
771 else:
772 error_msg = "%s rdepends on %s, but it isn't a build dependency?" % (pkg, rdepend)
Andrew Geisslereff27472021-10-29 15:35:00 -0500773 oe.qa.handle_error("build-deps", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500774
775 if "file-rdeps" not in skip:
776 ignored_file_rdeps = set(['/bin/sh', '/usr/bin/env', 'rtld(GNU_HASH)'])
777 if bb.data.inherits_class('nativesdk', d):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500778 ignored_file_rdeps |= set(['/bin/bash', '/usr/bin/perl', 'perl'])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500779 # For Saving the FILERDEPENDS
780 filerdepends = {}
781 rdep_data = oe.packagedata.read_subpkgdata(pkg, d)
782 for key in rdep_data:
Andrew Geissler5199d832021-09-24 16:47:35 -0500783 if key.startswith("FILERDEPENDS:"):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500784 for subkey in bb.utils.explode_deps(rdep_data[key]):
785 if subkey not in ignored_file_rdeps and \
786 not subkey.startswith('perl('):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500787 # We already know it starts with FILERDEPENDS_
788 filerdepends[subkey] = key[13:]
789
790 if filerdepends:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500791 done = rdepends[:]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500792 # Add the rprovides of itself
793 if pkg not in done:
794 done.insert(0, pkg)
795
796 # The python is not a package, but python-core provides it, so
797 # skip checking /usr/bin/python if python is in the rdeps, in
Patrick Williams213cb262021-08-07 19:21:33 -0500798 # case there is a RDEPENDS:pkg = "python" in the recipe.
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500799 for py in [ d.getVar('MLPREFIX') + "python", "python" ]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500800 if py in done:
801 filerdepends.pop("/usr/bin/python",None)
802 done.remove(py)
803 for rdep in done:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500804 # The file dependencies may contain package names, e.g.,
805 # perl
806 filerdepends.pop(rdep,None)
807
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500808 # For Saving the FILERPROVIDES, RPROVIDES and FILES_INFO
809 rdep_data = oe.packagedata.read_subpkgdata(rdep, d)
810 for key in rdep_data:
Andrew Geisslerd159c7f2021-09-02 21:05:58 -0500811 if key.startswith("FILERPROVIDES:") or key.startswith("RPROVIDES:"):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500812 for subkey in bb.utils.explode_deps(rdep_data[key]):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500813 filerdepends.pop(subkey,None)
814 # Add the files list to the rprovides
Andrew Geisslerd159c7f2021-09-02 21:05:58 -0500815 if key.startswith("FILES_INFO:"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500816 # Use eval() to make it as a dict
817 for subkey in eval(rdep_data[key]):
818 filerdepends.pop(subkey,None)
819 if not filerdepends:
820 # Break if all the file rdepends are met
821 break
822 if filerdepends:
823 for key in filerdepends:
Patrick Williams213cb262021-08-07 19:21:33 -0500824 error_msg = "%s contained in package %s requires %s, but no providers found in RDEPENDS:%s?" % \
825 (filerdepends[key].replace(":%s" % pkg, "").replace("@underscore@", "_"), pkg, key, pkg)
Andrew Geisslereff27472021-10-29 15:35:00 -0500826 oe.qa.handle_error("file-rdeps", error_msg, d)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500827package_qa_check_rdepends[vardepsexclude] = "OVERRIDES"
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500828
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500829def package_qa_check_deps(pkg, pkgdest, d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500830
831 localdata = bb.data.createCopy(d)
832 localdata.setVar('OVERRIDES', pkg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500833
834 def check_valid_deps(var):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500835 try:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500836 rvar = bb.utils.explode_dep_versions2(localdata.getVar(var) or "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500837 except ValueError as e:
Patrick Williams0ca19cc2021-08-16 14:03:13 -0500838 bb.fatal("%s:%s: %s" % (var, pkg, e))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500839 for dep in rvar:
840 for v in rvar[dep]:
841 if v and not v.startswith(('< ', '= ', '> ', '<= ', '>=')):
Patrick Williams0ca19cc2021-08-16 14:03:13 -0500842 error_msg = "%s:%s is invalid: %s (%s) only comparisons <, =, >, <=, and >= are allowed" % (var, pkg, dep, v)
Andrew Geisslereff27472021-10-29 15:35:00 -0500843 oe.qa.handle_error("dep-cmp", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500844
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500845 check_valid_deps('RDEPENDS')
846 check_valid_deps('RRECOMMENDS')
847 check_valid_deps('RSUGGESTS')
848 check_valid_deps('RPROVIDES')
849 check_valid_deps('RREPLACES')
850 check_valid_deps('RCONFLICTS')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500851
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500852QAPKGTEST[usrmerge] = "package_qa_check_usrmerge"
853def package_qa_check_usrmerge(pkg, d, messages):
Andrew Geisslereff27472021-10-29 15:35:00 -0500854
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500855 pkgdest = d.getVar('PKGDEST')
856 pkg_dir = pkgdest + os.sep + pkg + os.sep
857 merged_dirs = ['bin', 'sbin', 'lib'] + d.getVar('MULTILIB_VARIANTS').split()
858 for f in merged_dirs:
859 if os.path.exists(pkg_dir + f) and not os.path.islink(pkg_dir + f):
860 msg = "%s package is not obeying usrmerge distro feature. /%s should be relocated to /usr." % (pkg, f)
Andrew Geisslereff27472021-10-29 15:35:00 -0500861 oe.qa.add_message(messages, "usrmerge", msg)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500862 return False
863 return True
864
Brad Bishopf3f93bb2019-10-16 14:33:32 -0400865QAPKGTEST[perllocalpod] = "package_qa_check_perllocalpod"
866def package_qa_check_perllocalpod(pkg, d, messages):
867 """
868 Check that the recipe didn't ship a perlocal.pod file, which shouldn't be
869 installed in a distribution package. cpan.bbclass sets NO_PERLLOCAL=1 to
870 handle this for most recipes.
871 """
872 import glob
873 pkgd = oe.path.join(d.getVar('PKGDEST'), pkg)
874 podpath = oe.path.join(pkgd, d.getVar("libdir"), "perl*", "*", "*", "perllocal.pod")
875
876 matches = glob.glob(podpath)
877 if matches:
878 matches = [package_qa_clean_path(path, d, pkg) for path in matches]
879 msg = "%s contains perllocal.pod (%s), should not be installed" % (pkg, " ".join(matches))
Andrew Geisslereff27472021-10-29 15:35:00 -0500880 oe.qa.add_message(messages, "perllocalpod", msg)
Brad Bishopf3f93bb2019-10-16 14:33:32 -0400881
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500882QAPKGTEST[expanded-d] = "package_qa_check_expanded_d"
883def package_qa_check_expanded_d(package, d, messages):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500884 """
885 Check for the expanded D (${D}) value in pkg_* and FILES
886 variables, warn the user to use it correctly.
887 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500888 sane = True
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500889 expanded_d = d.getVar('D')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500890
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500891 for var in 'FILES','pkg_preinst', 'pkg_postinst', 'pkg_prerm', 'pkg_postrm':
Patrick Williams0ca19cc2021-08-16 14:03:13 -0500892 bbvar = d.getVar(var + ":" + package) or ""
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500893 if expanded_d in bbvar:
894 if var == 'FILES':
Andrew Geisslereff27472021-10-29 15:35:00 -0500895 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 -0500896 sane = False
897 else:
Andrew Geisslereff27472021-10-29 15:35:00 -0500898 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 -0500899 sane = False
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500900 return sane
901
Andrew Geissler1e34c2d2020-05-29 16:02:59 -0500902QAPKGTEST[unlisted-pkg-lics] = "package_qa_check_unlisted_pkg_lics"
903def package_qa_check_unlisted_pkg_lics(package, d, messages):
904 """
905 Check that all licenses for a package are among the licenses for the recipe.
906 """
Patrick Williams213cb262021-08-07 19:21:33 -0500907 pkg_lics = d.getVar('LICENSE:' + package)
Andrew Geissler1e34c2d2020-05-29 16:02:59 -0500908 if not pkg_lics:
909 return True
910
911 recipe_lics_set = oe.license.list_licenses(d.getVar('LICENSE'))
912 unlisted = oe.license.list_licenses(pkg_lics) - recipe_lics_set
913 if not unlisted:
914 return True
915
Andrew Geisslereff27472021-10-29 15:35:00 -0500916 oe.qa.add_message(messages, "unlisted-pkg-lics",
Patrick Williams213cb262021-08-07 19:21:33 -0500917 "LICENSE:%s includes licenses (%s) that are not "
Andrew Geissler1e34c2d2020-05-29 16:02:59 -0500918 "listed in LICENSE" % (package, ' '.join(unlisted)))
919 return False
920
Andrew Geissler595f6302022-01-24 19:11:47 +0000921QAPKGTEST[empty-dirs] = "package_qa_check_empty_dirs"
922def package_qa_check_empty_dirs(pkg, d, messages):
923 """
924 Check for the existence of files in directories that are expected to be
925 empty.
926 """
927
928 pkgd = oe.path.join(d.getVar('PKGDEST'), pkg)
929 for dir in (d.getVar('QA_EMPTY_DIRS') or "").split():
930 empty_dir = oe.path.join(pkgd, dir)
931 if os.path.exists(empty_dir) and os.listdir(empty_dir):
932 recommendation = (d.getVar('QA_EMPTY_DIRS_RECOMMENDATION:' + dir) or
933 "but it is expected to be empty")
934 msg = "%s installs files in %s, %s" % (pkg, dir, recommendation)
935 oe.qa.add_message(messages, "empty-dirs", msg)
936
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500937def package_qa_check_encoding(keys, encode, d):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600938 def check_encoding(key, enc):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500939 sane = True
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500940 value = d.getVar(key)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500941 if value:
942 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600943 s = value.encode(enc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500944 except UnicodeDecodeError as e:
945 error_msg = "%s has non %s characters" % (key,enc)
946 sane = False
Andrew Geisslereff27472021-10-29 15:35:00 -0500947 oe.qa.handle_error("invalid-chars", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500948 return sane
949
950 for key in keys:
951 sane = check_encoding(key, encode)
952 if not sane:
953 break
954
955HOST_USER_UID := "${@os.getuid()}"
956HOST_USER_GID := "${@os.getgid()}"
957
958QAPATHTEST[host-user-contaminated] = "package_qa_check_host_user"
959def package_qa_check_host_user(path, name, d, elf, messages):
960 """Check for paths outside of /home which are owned by the user running bitbake."""
961
962 if not os.path.lexists(path):
963 return
964
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500965 dest = d.getVar('PKGDEST')
966 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500967 home = os.path.join(dest, 'home')
968 if path == home or path.startswith(home + os.sep):
969 return
970
971 try:
972 stat = os.lstat(path)
973 except OSError as exc:
974 import errno
975 if exc.errno != errno.ENOENT:
976 raise
977 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500978 check_uid = int(d.getVar('HOST_USER_UID'))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500979 if stat.st_uid == check_uid:
Andrew Geisslereff27472021-10-29 15:35:00 -0500980 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 -0500981 return False
982
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500983 check_gid = int(d.getVar('HOST_USER_GID'))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500984 if stat.st_gid == check_gid:
Andrew Geisslereff27472021-10-29 15:35:00 -0500985 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 -0500986 return False
987 return True
988
Andrew Geissler5a43b432020-06-13 10:46:56 -0500989QARECIPETEST[unhandled-features-check] = "package_qa_check_unhandled_features_check"
990def package_qa_check_unhandled_features_check(pn, d, messages):
991 if not bb.data.inherits_class('features_check', d):
992 var_set = False
993 for kind in ['DISTRO', 'MACHINE', 'COMBINED']:
994 for var in ['ANY_OF_' + kind + '_FEATURES', 'REQUIRED_' + kind + '_FEATURES', 'CONFLICT_' + kind + '_FEATURES']:
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000995 if d.getVar(var) is not None or d.hasOverrides(var):
Andrew Geissler5a43b432020-06-13 10:46:56 -0500996 var_set = True
997 if var_set:
Andrew Geisslereff27472021-10-29 15:35:00 -0500998 oe.qa.handle_error("unhandled-features-check", "%s: recipe doesn't inherit features_check" % pn, d)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500999
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001000QARECIPETEST[missing-update-alternatives] = "package_qa_check_missing_update_alternatives"
1001def package_qa_check_missing_update_alternatives(pn, d, messages):
1002 # Look at all packages and find out if any of those sets ALTERNATIVE variable
1003 # without inheriting update-alternatives class
1004 for pkg in (d.getVar('PACKAGES') or '').split():
Patrick Williams213cb262021-08-07 19:21:33 -05001005 if d.getVar('ALTERNATIVE:%s' % pkg) and not bb.data.inherits_class('update-alternatives', d):
Andrew Geisslereff27472021-10-29 15:35:00 -05001006 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 -05001007
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001008# The PACKAGE FUNC to scan each package
1009python do_package_qa () {
1010 import subprocess
1011 import oe.packagedata
1012
1013 bb.note("DO PACKAGE QA")
1014
1015 bb.build.exec_func("read_subpackage_metadata", d)
1016
1017 # Check non UTF-8 characters on recipe's metadata
1018 package_qa_check_encoding(['DESCRIPTION', 'SUMMARY', 'LICENSE', 'SECTION'], 'utf-8', d)
1019
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001020 logdir = d.getVar('T')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001021 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001022
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001023 # Scan the packages...
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001024 pkgdest = d.getVar('PKGDEST')
1025 packages = set((d.getVar('PACKAGES') or '').split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001026
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001027 global pkgfiles
1028 pkgfiles = {}
1029 for pkg in packages:
1030 pkgfiles[pkg] = []
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001031 pkgdir = os.path.join(pkgdest, pkg)
1032 for walkroot, dirs, files in os.walk(pkgdir):
1033 # Don't walk into top-level CONTROL or DEBIAN directories as these
1034 # are temporary directories created by do_package.
1035 if walkroot == pkgdir:
1036 for control in ("CONTROL", "DEBIAN"):
1037 if control in dirs:
1038 dirs.remove(control)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001039 for file in files:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001040 pkgfiles[pkg].append(os.path.join(walkroot, file))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001041
1042 # no packages should be scanned
1043 if not packages:
1044 return
1045
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001046 import re
1047 # The package name matches the [a-z0-9.+-]+ regular expression
Brad Bishop977dc1a2019-02-06 16:01:43 -05001048 pkgname_pattern = re.compile(r"^[a-z0-9.+-]+$")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001049
1050 taskdepdata = d.getVar("BB_TASKDEPDATA", False)
1051 taskdeps = set()
1052 for dep in taskdepdata:
1053 taskdeps.add(taskdepdata[dep][0])
1054
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001055 def parse_test_matrix(matrix_name):
1056 testmatrix = d.getVarFlags(matrix_name) or {}
1057 g = globals()
1058 warnchecks = []
1059 for w in (d.getVar("WARN_QA") or "").split():
1060 if w in skip:
1061 continue
1062 if w in testmatrix and testmatrix[w] in g:
1063 warnchecks.append(g[testmatrix[w]])
1064
1065 errorchecks = []
1066 for e in (d.getVar("ERROR_QA") or "").split():
1067 if e in skip:
1068 continue
1069 if e in testmatrix and testmatrix[e] in g:
1070 errorchecks.append(g[testmatrix[e]])
1071 return warnchecks, errorchecks
1072
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001073 for package in packages:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001074 skip = set((d.getVar('INSANE_SKIP') or "").split() +
Patrick Williams213cb262021-08-07 19:21:33 -05001075 (d.getVar('INSANE_SKIP:' + package) or "").split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001076 if skip:
1077 bb.note("Package %s skipping QA tests: %s" % (package, str(skip)))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001078
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001079 bb.note("Checking Package: %s" % package)
1080 # Check package name
1081 if not pkgname_pattern.match(package):
Andrew Geisslereff27472021-10-29 15:35:00 -05001082 oe.qa.handle_error("pkgname",
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001083 "%s doesn't match the [a-z0-9.+-]+ regex" % package, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001084
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001085 warn_checks, error_checks = parse_test_matrix("QAPATHTEST")
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001086 package_qa_walk(warn_checks, error_checks, package, d)
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001087
1088 warn_checks, error_checks = parse_test_matrix("QAPKGTEST")
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001089 package_qa_package(warn_checks, error_checks, package, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001090
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001091 package_qa_check_rdepends(package, pkgdest, skip, taskdeps, packages, d)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001092 package_qa_check_deps(package, pkgdest, d)
1093
1094 warn_checks, error_checks = parse_test_matrix("QARECIPETEST")
1095 package_qa_recipe(warn_checks, error_checks, pn, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001096
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001097 if 'libdir' in d.getVar("ALL_QA").split():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001098 package_qa_check_libdir(d)
1099
Andrew Geisslereff27472021-10-29 15:35:00 -05001100 oe.qa.exit_if_errors(d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001101}
1102
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001103# binutils is used for most checks, so need to set as dependency
1104# POPULATESYSROOTDEPS is defined in staging class.
1105do_package_qa[depends] += "${POPULATESYSROOTDEPS}"
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001106do_package_qa[vardepsexclude] = "BB_TASKDEPDATA"
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001107do_package_qa[rdeptask] = "do_packagedata"
1108addtask do_package_qa after do_packagedata do_package before do_build
1109
Brad Bishop19323692019-04-05 15:28:33 -04001110# Add the package specific INSANE_SKIPs to the sstate dependencies
1111python() {
1112 pkgs = (d.getVar('PACKAGES') or '').split()
1113 for pkg in pkgs:
Patrick Williams213cb262021-08-07 19:21:33 -05001114 d.appendVarFlag("do_package_qa", "vardeps", " INSANE_SKIP:{}".format(pkg))
Brad Bishop19323692019-04-05 15:28:33 -04001115}
1116
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001117SSTATETASKS += "do_package_qa"
1118do_package_qa[sstate-inputdirs] = ""
1119do_package_qa[sstate-outputdirs] = ""
1120python do_package_qa_setscene () {
1121 sstate_setscene(d)
1122}
1123addtask do_package_qa_setscene
1124
1125python do_qa_staging() {
1126 bb.note("QA checking staging")
Andrew Geisslereff27472021-10-29 15:35:00 -05001127 qa_check_staged(d.expand('${SYSROOT_DESTDIR}${libdir}'), d)
1128 oe.qa.exit_with_message_if_errors("QA staging was broken by the package built above", d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001129}
1130
Brad Bishopd89cb5f2019-04-10 09:02:41 -04001131python do_qa_patch() {
1132 import subprocess
1133
1134 ###########################################################################
1135 # Check patch.log for fuzz warnings
1136 #
1137 # Further information on why we check for patch fuzz warnings:
1138 # http://lists.openembedded.org/pipermail/openembedded-core/2018-March/148675.html
1139 # https://bugzilla.yoctoproject.org/show_bug.cgi?id=10450
1140 ###########################################################################
1141
1142 logdir = d.getVar('T')
1143 patchlog = os.path.join(logdir,"log.do_patch")
1144
1145 if os.path.exists(patchlog):
1146 fuzzheader = '--- Patch fuzz start ---'
1147 fuzzfooter = '--- Patch fuzz end ---'
1148 statement = "grep -e '%s' %s > /dev/null" % (fuzzheader, patchlog)
1149 if subprocess.call(statement, shell=True) == 0:
1150 msg = "Fuzz detected:\n\n"
1151 fuzzmsg = ""
1152 inFuzzInfo = False
1153 f = open(patchlog, "r")
1154 for line in f:
1155 if fuzzheader in line:
1156 inFuzzInfo = True
1157 fuzzmsg = ""
1158 elif fuzzfooter in line:
1159 fuzzmsg = fuzzmsg.replace('\n\n', '\n')
1160 msg += fuzzmsg
1161 msg += "\n"
1162 inFuzzInfo = False
1163 elif inFuzzInfo and not 'Now at patch' in line:
1164 fuzzmsg += line
1165 f.close()
1166 msg += "The context lines in the patches can be updated with devtool:\n"
1167 msg += "\n"
1168 msg += " devtool modify %s\n" % d.getVar('PN')
1169 msg += " devtool finish --force-patch-refresh %s <layer_path>\n\n" % d.getVar('PN')
1170 msg += "Don't forget to review changes done by devtool!\n"
1171 if 'patch-fuzz' in d.getVar('ERROR_QA'):
1172 bb.error(msg)
1173 elif 'patch-fuzz' in d.getVar('WARN_QA'):
1174 bb.warn(msg)
1175 msg = "Patch log indicates that patches do not apply cleanly."
Andrew Geisslereff27472021-10-29 15:35:00 -05001176 oe.qa.handle_error("patch-fuzz", msg, d)
Andrew Geissler595f6302022-01-24 19:11:47 +00001177
1178 # Check if the patch contains a correctly formatted and spelled Upstream-Status
1179 import re
1180 from oe import patch
1181
1182 for url in patch.src_patches(d):
1183 (_, _, fullpath, _, _, _) = bb.fetch.decodeurl(url)
1184
1185 # skip patches not in oe-core
1186 if '/meta/' not in fullpath:
1187 continue
1188
1189 content = open(fullpath, encoding='utf-8', errors='ignore').read()
1190 kinda_status_re = re.compile(r"^.*upstream.*status.*$", re.IGNORECASE | re.MULTILINE)
1191 strict_status_re = re.compile(r"^Upstream-Status: (Pending|Submitted|Denied|Accepted|Inappropriate|Backport|Inactive-Upstream)( .+)?$", re.MULTILINE)
1192 match_kinda = kinda_status_re.search(content)
1193 match_strict = strict_status_re.search(content)
1194 guidelines = "https://www.openembedded.org/wiki/Commit_Patch_Message_Guidelines#Patch_Header_Recommendations:_Upstream-Status"
1195
1196 if not match_strict:
1197 if match_kinda:
1198 bb.error("Malformed Upstream-Status in patch\n%s\nPlease correct according to %s :\n%s" % (fullpath, guidelines, match_kinda.group(0)))
1199 else:
1200 bb.error("Missing Upstream-Status in patch\n%s\nPlease add according to %s ." % (fullpath, guidelines))
Brad Bishopd89cb5f2019-04-10 09:02:41 -04001201}
1202
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001203python do_qa_configure() {
1204 import subprocess
1205
1206 ###########################################################################
1207 # Check config.log for cross compile issues
1208 ###########################################################################
1209
1210 configs = []
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001211 workdir = d.getVar('WORKDIR')
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001212
Brad Bishop19323692019-04-05 15:28:33 -04001213 skip = (d.getVar('INSANE_SKIP') or "").split()
1214 skip_configure_unsafe = False
1215 if 'configure-unsafe' in skip:
1216 bb.note("Recipe %s skipping qa checking: configure-unsafe" % d.getVar('PN'))
1217 skip_configure_unsafe = True
1218
1219 if bb.data.inherits_class('autotools', d) and not skip_configure_unsafe:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001220 bb.note("Checking autotools environment for common misconfiguration")
1221 for root, dirs, files in os.walk(workdir):
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001222 statement = "grep -q -F -e 'is unsafe for cross-compilation' %s" % \
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001223 os.path.join(root,"config.log")
1224 if "config.log" in files:
1225 if subprocess.call(statement, shell=True) == 0:
Brad Bishop19323692019-04-05 15:28:33 -04001226 error_msg = """This autoconf log indicates errors, it looked at host include and/or library paths while determining system capabilities.
1227Rerun configure task after fixing this."""
Andrew Geisslereff27472021-10-29 15:35:00 -05001228 oe.qa.handle_error("configure-unsafe", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001229
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001230 if "configure.ac" in files:
1231 configs.append(os.path.join(root,"configure.ac"))
1232 if "configure.in" in files:
1233 configs.append(os.path.join(root, "configure.in"))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001234
1235 ###########################################################################
1236 # Check gettext configuration and dependencies are correct
1237 ###########################################################################
1238
Brad Bishop19323692019-04-05 15:28:33 -04001239 skip_configure_gettext = False
1240 if 'configure-gettext' in skip:
1241 bb.note("Recipe %s skipping qa checking: configure-gettext" % d.getVar('PN'))
1242 skip_configure_gettext = True
1243
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001244 cnf = d.getVar('EXTRA_OECONF') or ""
Brad Bishop19323692019-04-05 15:28:33 -04001245 if not ("gettext" in d.getVar('P') or "gcc-runtime" in d.getVar('P') or \
1246 "--disable-nls" in cnf or skip_configure_gettext):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001247 ml = d.getVar("MLPREFIX") or ""
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001248 if bb.data.inherits_class('cross-canadian', d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001249 gt = "nativesdk-gettext"
1250 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001251 gt = "gettext-native"
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001252 deps = bb.utils.explode_deps(d.getVar('DEPENDS') or "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001253 if gt not in deps:
1254 for config in configs:
1255 gnu = "grep \"^[[:space:]]*AM_GNU_GETTEXT\" %s >/dev/null" % config
1256 if subprocess.call(gnu, shell=True) == 0:
Brad Bishopd89cb5f2019-04-10 09:02:41 -04001257 error_msg = "AM_GNU_GETTEXT used but no inherit gettext"
Andrew Geisslereff27472021-10-29 15:35:00 -05001258 oe.qa.handle_error("configure-gettext", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001259
1260 ###########################################################################
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001261 # Check unrecognised configure options (with a white list)
1262 ###########################################################################
Andrew Geissler595f6302022-01-24 19:11:47 +00001263 if bb.data.inherits_class("autotools", d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001264 bb.note("Checking configure output for unrecognised options")
1265 try:
Brad Bishopc342db32019-05-15 21:57:59 -04001266 if bb.data.inherits_class("autotools", d):
1267 flag = "WARNING: unrecognized options:"
1268 log = os.path.join(d.getVar('B'), 'config.log')
Brad Bishopc342db32019-05-15 21:57:59 -04001269 output = subprocess.check_output(['grep', '-F', flag, log]).decode("utf-8").replace(', ', ' ').replace('"', '')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001270 options = set()
1271 for line in output.splitlines():
1272 options |= set(line.partition(flag)[2].split())
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001273 whitelist = set(d.getVar("UNKNOWN_CONFIGURE_OPT_IGNORE").split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001274 options -= whitelist
1275 if options:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001276 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001277 error_msg = pn + ": configure was passed unrecognised options: " + " ".join(options)
Andrew Geisslereff27472021-10-29 15:35:00 -05001278 oe.qa.handle_error("unknown-configure-option", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001279 except subprocess.CalledProcessError:
1280 pass
1281
1282 # Check invalid PACKAGECONFIG
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001283 pkgconfig = (d.getVar("PACKAGECONFIG") or "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001284 if pkgconfig:
1285 pkgconfigflags = d.getVarFlags("PACKAGECONFIG") or {}
1286 for pconfig in pkgconfig:
1287 if pconfig not in pkgconfigflags:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001288 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001289 error_msg = "%s: invalid PACKAGECONFIG: %s" % (pn, pconfig)
Andrew Geisslereff27472021-10-29 15:35:00 -05001290 oe.qa.handle_error("invalid-packageconfig", error_msg, d)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001291
Andrew Geisslereff27472021-10-29 15:35:00 -05001292 oe.qa.exit_if_errors(d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001293}
1294
Andrew Geissler595f6302022-01-24 19:11:47 +00001295def unpack_check_src_uri(pn, d):
1296 import re
1297
1298 skip = (d.getVar('INSANE_SKIP') or "").split()
1299 if 'src-uri-bad' in skip:
1300 bb.note("Recipe %s skipping qa checking: src-uri-bad" % d.getVar('PN'))
1301 return
1302
1303 if "${PN}" in d.getVar("SRC_URI", False):
1304 oe.qa.handle_error("src-uri-bad", "%s: SRC_URI uses PN not BPN" % pn, d)
1305
1306 for url in d.getVar("SRC_URI").split():
1307 if re.search(r"git(hu|la)b\.com/.+/.+/archive/.+", url):
1308 oe.qa.handle_error("src-uri-bad", "%s: SRC_URI uses unstable GitHub/GitLab archives, convert recipe to use git protocol" % pn, d)
1309
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001310python do_qa_unpack() {
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001311 src_uri = d.getVar('SRC_URI')
1312 s_dir = d.getVar('S')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001313 if src_uri and not os.path.exists(s_dir):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001314 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))
Andrew Geissler595f6302022-01-24 19:11:47 +00001315
1316 unpack_check_src_uri(d.getVar('PN'), d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001317}
1318
1319# The Staging Func, to check all staging
1320#addtask qa_staging after do_populate_sysroot before do_build
1321do_populate_sysroot[postfuncs] += "do_qa_staging "
1322
Brad Bishopd89cb5f2019-04-10 09:02:41 -04001323# Check for patch fuzz
1324do_patch[postfuncs] += "do_qa_patch "
1325
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001326# Check broken config.log files, for packages requiring Gettext which
1327# don't have it in DEPENDS.
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001328#addtask qa_configure after do_configure before do_compile
1329do_configure[postfuncs] += "do_qa_configure "
1330
1331# Check does S exist.
1332do_unpack[postfuncs] += "do_qa_unpack"
1333
1334python () {
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001335 import re
1336
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001337 tests = d.getVar('ALL_QA').split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001338 if "desktop" in tests:
1339 d.appendVar("PACKAGE_DEPENDS", " desktop-file-utils-native")
1340
1341 ###########################################################################
1342 # Check various variables
1343 ###########################################################################
1344
1345 # Checking ${FILESEXTRAPATHS}
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001346 extrapaths = (d.getVar("FILESEXTRAPATHS") or "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001347 if '__default' not in extrapaths.split(":"):
Patrick Williams213cb262021-08-07 19:21:33 -05001348 msg = "FILESEXTRAPATHS-variable, must always use :prepend (or :append)\n"
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001349 msg += "type of assignment, and don't forget the colon.\n"
1350 msg += "Please assign it with the format of:\n"
Patrick Williams213cb262021-08-07 19:21:33 -05001351 msg += " FILESEXTRAPATHS:append := \":${THISDIR}/Your_Files_Path\" or\n"
1352 msg += " FILESEXTRAPATHS:prepend := \"${THISDIR}/Your_Files_Path:\"\n"
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001353 msg += "in your bbappend file\n\n"
1354 msg += "Your incorrect assignment is:\n"
1355 msg += "%s\n" % extrapaths
1356 bb.warn(msg)
1357
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001358 overrides = d.getVar('OVERRIDES').split(':')
1359 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001360 if pn in overrides:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001361 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 -05001362 oe.qa.handle_error("pn-overrides", msg, d)
Brad Bishop977dc1a2019-02-06 16:01:43 -05001363 prog = re.compile(r'[A-Z]')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001364 if prog.search(pn):
Andrew Geisslereff27472021-10-29 15:35:00 -05001365 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 -05001366
Patrick Williams0ca19cc2021-08-16 14:03:13 -05001367 # Some people mistakenly use DEPENDS:${PN} instead of DEPENDS and wonder
Brad Bishop08902b02019-08-20 09:16:51 -04001368 # why it doesn't work.
Patrick Williams0ca19cc2021-08-16 14:03:13 -05001369 if (d.getVar(d.expand('DEPENDS:${PN}'))):
Andrew Geisslereff27472021-10-29 15:35:00 -05001370 oe.qa.handle_error("pkgvarcheck", "recipe uses DEPENDS:${PN}, should use DEPENDS", d)
Brad Bishop08902b02019-08-20 09:16:51 -04001371
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001372 issues = []
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001373 if (d.getVar('PACKAGES') or "").split():
1374 for dep in (d.getVar('QADEPENDS') or "").split():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001375 d.appendVarFlag('do_package_qa', 'depends', " %s:do_populate_sysroot" % dep)
1376 for var in 'RDEPENDS', 'RRECOMMENDS', 'RSUGGESTS', 'RCONFLICTS', 'RPROVIDES', 'RREPLACES', 'FILES', 'pkg_preinst', 'pkg_postinst', 'pkg_prerm', 'pkg_postrm', 'ALLOW_EMPTY':
1377 if d.getVar(var, False):
1378 issues.append(var)
1379
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001380 fakeroot_tests = d.getVar('FAKEROOT_QA').split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001381 if set(tests) & set(fakeroot_tests):
1382 d.setVarFlag('do_package_qa', 'fakeroot', '1')
1383 d.appendVarFlag('do_package_qa', 'depends', ' virtual/fakeroot-native:do_populate_sysroot')
1384 else:
1385 d.setVarFlag('do_package_qa', 'rdeptask', '')
1386 for i in issues:
Andrew Geisslereff27472021-10-29 15:35:00 -05001387 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 -06001388
1389 if 'native-last' not in (d.getVar('INSANE_SKIP') or "").split():
1390 for native_class in ['native', 'nativesdk']:
1391 if bb.data.inherits_class(native_class, d):
1392
1393 inherited_classes = d.getVar('__inherit_cache', False) or []
1394 needle = os.path.join('classes', native_class)
1395
1396 bbclassextend = (d.getVar('BBCLASSEXTEND') or '').split()
1397 # BBCLASSEXTEND items are always added in the end
1398 skip_classes = bbclassextend
1399 if bb.data.inherits_class('native', d) or 'native' in bbclassextend:
1400 # native also inherits nopackages and relocatable bbclasses
1401 skip_classes.extend(['nopackages', 'relocatable'])
1402
1403 broken_order = []
1404 for class_item in reversed(inherited_classes):
1405 if needle not in class_item:
1406 for extend_item in skip_classes:
1407 if os.path.join('classes', '%s.bbclass' % extend_item) in class_item:
1408 break
1409 else:
1410 pn = d.getVar('PN')
1411 broken_order.append(os.path.basename(class_item))
1412 else:
1413 break
1414 if broken_order:
Andrew Geisslereff27472021-10-29 15:35:00 -05001415 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 -06001416 "Classes inherited after native/nativesdk: %s" % (pn, " ".join(broken_order)), d)
1417
Andrew Geisslereff27472021-10-29 15:35:00 -05001418 oe.qa.exit_if_errors(d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001419}