blob: 11532ecd08fa9e33c409fd56b4da304cfb7f07e5 [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
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
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
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500328 target_os = d.getVar('TARGET_OS')
329 target_arch = d.getVar('TARGET_ARCH')
330 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
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500687# Walk over all files in a directory and call func
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500688def package_qa_walk(warnfuncs, errorfuncs, package, d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500689 #if this will throw an exception, then fix the dict above
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500690 target_os = d.getVar('TARGET_OS')
691 target_arch = d.getVar('TARGET_ARCH')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500692
693 warnings = {}
694 errors = {}
695 for path in pkgfiles[package]:
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500696 elf = None
697 if os.path.isfile(path):
698 elf = oe.qa.ELFFile(path)
699 try:
700 elf.open()
701 except oe.qa.NotELFFileError:
702 elf = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500703 for func in warnfuncs:
704 func(path, package, d, elf, warnings)
705 for func in errorfuncs:
706 func(path, package, d, elf, errors)
707
708 for w in warnings:
Andrew Geisslereff27472021-10-29 15:35:00 -0500709 oe.qa.handle_error(w, warnings[w], d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500710 for e in errors:
Andrew Geisslereff27472021-10-29 15:35:00 -0500711 oe.qa.handle_error(e, errors[e], d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500712
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500713def package_qa_check_rdepends(pkg, pkgdest, skip, taskdeps, packages, d):
714 # Don't do this check for kernel/module recipes, there aren't too many debug/development
715 # packages and you can get false positives e.g. on kernel-module-lirc-dev
716 if bb.data.inherits_class("kernel", d) or bb.data.inherits_class("module-base", d):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500717 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500718
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500719 if not "-dbg" in pkg and not "packagegroup-" in pkg and not "-image" in pkg:
720 localdata = bb.data.createCopy(d)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500721 localdata.setVar('OVERRIDES', localdata.getVar('OVERRIDES') + ':' + pkg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500722
723 # Now check the RDEPENDS
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500724 rdepends = bb.utils.explode_deps(localdata.getVar('RDEPENDS') or "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500725
726 # Now do the sanity check!!!
727 if "build-deps" not in skip:
728 for rdepend in rdepends:
729 if "-dbg" in rdepend and "debug-deps" not in skip:
730 error_msg = "%s rdepends on %s" % (pkg,rdepend)
Andrew Geisslereff27472021-10-29 15:35:00 -0500731 oe.qa.handle_error("debug-deps", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500732 if (not "-dev" in pkg and not "-staticdev" in pkg) and rdepend.endswith("-dev") and "dev-deps" not in skip:
733 error_msg = "%s rdepends on %s" % (pkg, rdepend)
Andrew Geisslereff27472021-10-29 15:35:00 -0500734 oe.qa.handle_error("dev-deps", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500735 if rdepend not in packages:
736 rdep_data = oe.packagedata.read_subpkgdata(rdepend, d)
737 if rdep_data and 'PN' in rdep_data and rdep_data['PN'] in taskdeps:
738 continue
739 if not rdep_data or not 'PN' in rdep_data:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500740 pkgdata_dir = d.getVar("PKGDATA_DIR")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500741 try:
742 possibles = os.listdir("%s/runtime-rprovides/%s/" % (pkgdata_dir, rdepend))
743 except OSError:
744 possibles = []
745 for p in possibles:
746 rdep_data = oe.packagedata.read_subpkgdata(p, d)
747 if rdep_data and 'PN' in rdep_data and rdep_data['PN'] in taskdeps:
748 break
749 if rdep_data and 'PN' in rdep_data and rdep_data['PN'] in taskdeps:
750 continue
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500751 if rdep_data and 'PN' in rdep_data:
752 error_msg = "%s rdepends on %s, but it isn't a build dependency, missing %s in DEPENDS or PACKAGECONFIG?" % (pkg, rdepend, rdep_data['PN'])
753 else:
754 error_msg = "%s rdepends on %s, but it isn't a build dependency?" % (pkg, rdepend)
Andrew Geisslereff27472021-10-29 15:35:00 -0500755 oe.qa.handle_error("build-deps", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500756
757 if "file-rdeps" not in skip:
758 ignored_file_rdeps = set(['/bin/sh', '/usr/bin/env', 'rtld(GNU_HASH)'])
759 if bb.data.inherits_class('nativesdk', d):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500760 ignored_file_rdeps |= set(['/bin/bash', '/usr/bin/perl', 'perl'])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500761 # For Saving the FILERDEPENDS
762 filerdepends = {}
763 rdep_data = oe.packagedata.read_subpkgdata(pkg, d)
764 for key in rdep_data:
Andrew Geissler5199d832021-09-24 16:47:35 -0500765 if key.startswith("FILERDEPENDS:"):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500766 for subkey in bb.utils.explode_deps(rdep_data[key]):
767 if subkey not in ignored_file_rdeps and \
768 not subkey.startswith('perl('):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500769 # We already know it starts with FILERDEPENDS_
770 filerdepends[subkey] = key[13:]
771
772 if filerdepends:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500773 done = rdepends[:]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500774 # Add the rprovides of itself
775 if pkg not in done:
776 done.insert(0, pkg)
777
778 # The python is not a package, but python-core provides it, so
779 # skip checking /usr/bin/python if python is in the rdeps, in
Patrick Williams213cb262021-08-07 19:21:33 -0500780 # case there is a RDEPENDS:pkg = "python" in the recipe.
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500781 for py in [ d.getVar('MLPREFIX') + "python", "python" ]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500782 if py in done:
783 filerdepends.pop("/usr/bin/python",None)
784 done.remove(py)
785 for rdep in done:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500786 # The file dependencies may contain package names, e.g.,
787 # perl
788 filerdepends.pop(rdep,None)
789
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500790 # For Saving the FILERPROVIDES, RPROVIDES and FILES_INFO
791 rdep_data = oe.packagedata.read_subpkgdata(rdep, d)
792 for key in rdep_data:
Andrew Geisslerd159c7f2021-09-02 21:05:58 -0500793 if key.startswith("FILERPROVIDES:") or key.startswith("RPROVIDES:"):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500794 for subkey in bb.utils.explode_deps(rdep_data[key]):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500795 filerdepends.pop(subkey,None)
796 # Add the files list to the rprovides
Andrew Geisslerd159c7f2021-09-02 21:05:58 -0500797 if key.startswith("FILES_INFO:"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500798 # Use eval() to make it as a dict
799 for subkey in eval(rdep_data[key]):
800 filerdepends.pop(subkey,None)
801 if not filerdepends:
802 # Break if all the file rdepends are met
803 break
804 if filerdepends:
805 for key in filerdepends:
Patrick Williams213cb262021-08-07 19:21:33 -0500806 error_msg = "%s contained in package %s requires %s, but no providers found in RDEPENDS:%s?" % \
807 (filerdepends[key].replace(":%s" % pkg, "").replace("@underscore@", "_"), pkg, key, pkg)
Andrew Geisslereff27472021-10-29 15:35:00 -0500808 oe.qa.handle_error("file-rdeps", error_msg, d)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500809package_qa_check_rdepends[vardepsexclude] = "OVERRIDES"
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500810
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500811def package_qa_check_deps(pkg, pkgdest, d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500812
813 localdata = bb.data.createCopy(d)
814 localdata.setVar('OVERRIDES', pkg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500815
816 def check_valid_deps(var):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500817 try:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500818 rvar = bb.utils.explode_dep_versions2(localdata.getVar(var) or "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500819 except ValueError as e:
Patrick Williams0ca19cc2021-08-16 14:03:13 -0500820 bb.fatal("%s:%s: %s" % (var, pkg, e))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500821 for dep in rvar:
822 for v in rvar[dep]:
823 if v and not v.startswith(('< ', '= ', '> ', '<= ', '>=')):
Patrick Williams0ca19cc2021-08-16 14:03:13 -0500824 error_msg = "%s:%s is invalid: %s (%s) only comparisons <, =, >, <=, and >= are allowed" % (var, pkg, dep, v)
Andrew Geisslereff27472021-10-29 15:35:00 -0500825 oe.qa.handle_error("dep-cmp", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500826
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500827 check_valid_deps('RDEPENDS')
828 check_valid_deps('RRECOMMENDS')
829 check_valid_deps('RSUGGESTS')
830 check_valid_deps('RPROVIDES')
831 check_valid_deps('RREPLACES')
832 check_valid_deps('RCONFLICTS')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500833
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500834QAPKGTEST[usrmerge] = "package_qa_check_usrmerge"
835def package_qa_check_usrmerge(pkg, d, messages):
Andrew Geisslereff27472021-10-29 15:35:00 -0500836
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500837 pkgdest = d.getVar('PKGDEST')
838 pkg_dir = pkgdest + os.sep + pkg + os.sep
839 merged_dirs = ['bin', 'sbin', 'lib'] + d.getVar('MULTILIB_VARIANTS').split()
840 for f in merged_dirs:
841 if os.path.exists(pkg_dir + f) and not os.path.islink(pkg_dir + f):
842 msg = "%s package is not obeying usrmerge distro feature. /%s should be relocated to /usr." % (pkg, f)
Andrew Geisslereff27472021-10-29 15:35:00 -0500843 oe.qa.add_message(messages, "usrmerge", msg)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500844 return False
845 return True
846
Brad Bishopf3f93bb2019-10-16 14:33:32 -0400847QAPKGTEST[perllocalpod] = "package_qa_check_perllocalpod"
848def package_qa_check_perllocalpod(pkg, d, messages):
849 """
850 Check that the recipe didn't ship a perlocal.pod file, which shouldn't be
851 installed in a distribution package. cpan.bbclass sets NO_PERLLOCAL=1 to
852 handle this for most recipes.
853 """
854 import glob
855 pkgd = oe.path.join(d.getVar('PKGDEST'), pkg)
856 podpath = oe.path.join(pkgd, d.getVar("libdir"), "perl*", "*", "*", "perllocal.pod")
857
858 matches = glob.glob(podpath)
859 if matches:
860 matches = [package_qa_clean_path(path, d, pkg) for path in matches]
861 msg = "%s contains perllocal.pod (%s), should not be installed" % (pkg, " ".join(matches))
Andrew Geisslereff27472021-10-29 15:35:00 -0500862 oe.qa.add_message(messages, "perllocalpod", msg)
Brad Bishopf3f93bb2019-10-16 14:33:32 -0400863
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500864QAPKGTEST[expanded-d] = "package_qa_check_expanded_d"
865def package_qa_check_expanded_d(package, d, messages):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500866 """
867 Check for the expanded D (${D}) value in pkg_* and FILES
868 variables, warn the user to use it correctly.
869 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500870 sane = True
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500871 expanded_d = d.getVar('D')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500872
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500873 for var in 'FILES','pkg_preinst', 'pkg_postinst', 'pkg_prerm', 'pkg_postrm':
Patrick Williams0ca19cc2021-08-16 14:03:13 -0500874 bbvar = d.getVar(var + ":" + package) or ""
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500875 if expanded_d in bbvar:
876 if var == 'FILES':
Andrew Geisslereff27472021-10-29 15:35:00 -0500877 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 -0500878 sane = False
879 else:
Andrew Geisslereff27472021-10-29 15:35:00 -0500880 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 -0500881 sane = False
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500882 return sane
883
Andrew Geissler1e34c2d2020-05-29 16:02:59 -0500884QAPKGTEST[unlisted-pkg-lics] = "package_qa_check_unlisted_pkg_lics"
885def package_qa_check_unlisted_pkg_lics(package, d, messages):
886 """
887 Check that all licenses for a package are among the licenses for the recipe.
888 """
Patrick Williams213cb262021-08-07 19:21:33 -0500889 pkg_lics = d.getVar('LICENSE:' + package)
Andrew Geissler1e34c2d2020-05-29 16:02:59 -0500890 if not pkg_lics:
891 return True
892
893 recipe_lics_set = oe.license.list_licenses(d.getVar('LICENSE'))
894 unlisted = oe.license.list_licenses(pkg_lics) - recipe_lics_set
895 if not unlisted:
896 return True
897
Andrew Geisslereff27472021-10-29 15:35:00 -0500898 oe.qa.add_message(messages, "unlisted-pkg-lics",
Patrick Williams213cb262021-08-07 19:21:33 -0500899 "LICENSE:%s includes licenses (%s) that are not "
Andrew Geissler1e34c2d2020-05-29 16:02:59 -0500900 "listed in LICENSE" % (package, ' '.join(unlisted)))
901 return False
902
Andrew Geissler595f6302022-01-24 19:11:47 +0000903QAPKGTEST[empty-dirs] = "package_qa_check_empty_dirs"
904def package_qa_check_empty_dirs(pkg, d, messages):
905 """
906 Check for the existence of files in directories that are expected to be
907 empty.
908 """
909
910 pkgd = oe.path.join(d.getVar('PKGDEST'), pkg)
911 for dir in (d.getVar('QA_EMPTY_DIRS') or "").split():
912 empty_dir = oe.path.join(pkgd, dir)
913 if os.path.exists(empty_dir) and os.listdir(empty_dir):
914 recommendation = (d.getVar('QA_EMPTY_DIRS_RECOMMENDATION:' + dir) or
915 "but it is expected to be empty")
916 msg = "%s installs files in %s, %s" % (pkg, dir, recommendation)
917 oe.qa.add_message(messages, "empty-dirs", msg)
918
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500919def package_qa_check_encoding(keys, encode, d):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600920 def check_encoding(key, enc):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500921 sane = True
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500922 value = d.getVar(key)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500923 if value:
924 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600925 s = value.encode(enc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500926 except UnicodeDecodeError as e:
927 error_msg = "%s has non %s characters" % (key,enc)
928 sane = False
Andrew Geisslereff27472021-10-29 15:35:00 -0500929 oe.qa.handle_error("invalid-chars", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500930 return sane
931
932 for key in keys:
933 sane = check_encoding(key, encode)
934 if not sane:
935 break
936
937HOST_USER_UID := "${@os.getuid()}"
938HOST_USER_GID := "${@os.getgid()}"
939
940QAPATHTEST[host-user-contaminated] = "package_qa_check_host_user"
941def package_qa_check_host_user(path, name, d, elf, messages):
942 """Check for paths outside of /home which are owned by the user running bitbake."""
943
944 if not os.path.lexists(path):
945 return
946
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500947 dest = d.getVar('PKGDEST')
948 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500949 home = os.path.join(dest, 'home')
950 if path == home or path.startswith(home + os.sep):
951 return
952
953 try:
954 stat = os.lstat(path)
955 except OSError as exc:
956 import errno
957 if exc.errno != errno.ENOENT:
958 raise
959 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500960 check_uid = int(d.getVar('HOST_USER_UID'))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500961 if stat.st_uid == check_uid:
Andrew Geisslereff27472021-10-29 15:35:00 -0500962 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 -0500963 return False
964
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500965 check_gid = int(d.getVar('HOST_USER_GID'))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500966 if stat.st_gid == check_gid:
Andrew Geisslereff27472021-10-29 15:35:00 -0500967 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 -0500968 return False
969 return True
970
Andrew Geissler5a43b432020-06-13 10:46:56 -0500971QARECIPETEST[unhandled-features-check] = "package_qa_check_unhandled_features_check"
972def package_qa_check_unhandled_features_check(pn, d, messages):
973 if not bb.data.inherits_class('features_check', d):
974 var_set = False
975 for kind in ['DISTRO', 'MACHINE', 'COMBINED']:
976 for var in ['ANY_OF_' + kind + '_FEATURES', 'REQUIRED_' + kind + '_FEATURES', 'CONFLICT_' + kind + '_FEATURES']:
977 if d.getVar(var) is not None or d.overridedata.get(var) is not None:
978 var_set = True
979 if var_set:
Andrew Geisslereff27472021-10-29 15:35:00 -0500980 oe.qa.handle_error("unhandled-features-check", "%s: recipe doesn't inherit features_check" % pn, d)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500981
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500982QARECIPETEST[missing-update-alternatives] = "package_qa_check_missing_update_alternatives"
983def package_qa_check_missing_update_alternatives(pn, d, messages):
984 # Look at all packages and find out if any of those sets ALTERNATIVE variable
985 # without inheriting update-alternatives class
986 for pkg in (d.getVar('PACKAGES') or '').split():
Patrick Williams213cb262021-08-07 19:21:33 -0500987 if d.getVar('ALTERNATIVE:%s' % pkg) and not bb.data.inherits_class('update-alternatives', d):
Andrew Geisslereff27472021-10-29 15:35:00 -0500988 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 -0500989
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500990# The PACKAGE FUNC to scan each package
991python do_package_qa () {
992 import subprocess
993 import oe.packagedata
994
995 bb.note("DO PACKAGE QA")
996
997 bb.build.exec_func("read_subpackage_metadata", d)
998
999 # Check non UTF-8 characters on recipe's metadata
1000 package_qa_check_encoding(['DESCRIPTION', 'SUMMARY', 'LICENSE', 'SECTION'], 'utf-8', d)
1001
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001002 logdir = d.getVar('T')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001003 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001004
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001005 # Scan the packages...
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001006 pkgdest = d.getVar('PKGDEST')
1007 packages = set((d.getVar('PACKAGES') or '').split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001008
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001009 global pkgfiles
1010 pkgfiles = {}
1011 for pkg in packages:
1012 pkgfiles[pkg] = []
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001013 pkgdir = os.path.join(pkgdest, pkg)
1014 for walkroot, dirs, files in os.walk(pkgdir):
1015 # Don't walk into top-level CONTROL or DEBIAN directories as these
1016 # are temporary directories created by do_package.
1017 if walkroot == pkgdir:
1018 for control in ("CONTROL", "DEBIAN"):
1019 if control in dirs:
1020 dirs.remove(control)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001021 for file in files:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001022 pkgfiles[pkg].append(os.path.join(walkroot, file))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001023
1024 # no packages should be scanned
1025 if not packages:
1026 return
1027
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001028 import re
1029 # The package name matches the [a-z0-9.+-]+ regular expression
Brad Bishop977dc1a2019-02-06 16:01:43 -05001030 pkgname_pattern = re.compile(r"^[a-z0-9.+-]+$")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001031
1032 taskdepdata = d.getVar("BB_TASKDEPDATA", False)
1033 taskdeps = set()
1034 for dep in taskdepdata:
1035 taskdeps.add(taskdepdata[dep][0])
1036
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001037 def parse_test_matrix(matrix_name):
1038 testmatrix = d.getVarFlags(matrix_name) or {}
1039 g = globals()
1040 warnchecks = []
1041 for w in (d.getVar("WARN_QA") or "").split():
1042 if w in skip:
1043 continue
1044 if w in testmatrix and testmatrix[w] in g:
1045 warnchecks.append(g[testmatrix[w]])
1046
1047 errorchecks = []
1048 for e in (d.getVar("ERROR_QA") or "").split():
1049 if e in skip:
1050 continue
1051 if e in testmatrix and testmatrix[e] in g:
1052 errorchecks.append(g[testmatrix[e]])
1053 return warnchecks, errorchecks
1054
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001055 for package in packages:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001056 skip = set((d.getVar('INSANE_SKIP') or "").split() +
Patrick Williams213cb262021-08-07 19:21:33 -05001057 (d.getVar('INSANE_SKIP:' + package) or "").split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001058 if skip:
1059 bb.note("Package %s skipping QA tests: %s" % (package, str(skip)))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001060
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001061 bb.note("Checking Package: %s" % package)
1062 # Check package name
1063 if not pkgname_pattern.match(package):
Andrew Geisslereff27472021-10-29 15:35:00 -05001064 oe.qa.handle_error("pkgname",
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001065 "%s doesn't match the [a-z0-9.+-]+ regex" % package, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001066
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001067 warn_checks, error_checks = parse_test_matrix("QAPATHTEST")
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001068 package_qa_walk(warn_checks, error_checks, package, d)
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001069
1070 warn_checks, error_checks = parse_test_matrix("QAPKGTEST")
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001071 package_qa_package(warn_checks, error_checks, package, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001072
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001073 package_qa_check_rdepends(package, pkgdest, skip, taskdeps, packages, d)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001074 package_qa_check_deps(package, pkgdest, d)
1075
1076 warn_checks, error_checks = parse_test_matrix("QARECIPETEST")
1077 package_qa_recipe(warn_checks, error_checks, pn, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001078
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001079 if 'libdir' in d.getVar("ALL_QA").split():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001080 package_qa_check_libdir(d)
1081
Andrew Geisslereff27472021-10-29 15:35:00 -05001082 oe.qa.exit_if_errors(d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001083}
1084
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001085# binutils is used for most checks, so need to set as dependency
1086# POPULATESYSROOTDEPS is defined in staging class.
1087do_package_qa[depends] += "${POPULATESYSROOTDEPS}"
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001088do_package_qa[vardepsexclude] = "BB_TASKDEPDATA"
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001089do_package_qa[rdeptask] = "do_packagedata"
1090addtask do_package_qa after do_packagedata do_package before do_build
1091
Brad Bishop19323692019-04-05 15:28:33 -04001092# Add the package specific INSANE_SKIPs to the sstate dependencies
1093python() {
1094 pkgs = (d.getVar('PACKAGES') or '').split()
1095 for pkg in pkgs:
Patrick Williams213cb262021-08-07 19:21:33 -05001096 d.appendVarFlag("do_package_qa", "vardeps", " INSANE_SKIP:{}".format(pkg))
Brad Bishop19323692019-04-05 15:28:33 -04001097}
1098
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001099SSTATETASKS += "do_package_qa"
1100do_package_qa[sstate-inputdirs] = ""
1101do_package_qa[sstate-outputdirs] = ""
1102python do_package_qa_setscene () {
1103 sstate_setscene(d)
1104}
1105addtask do_package_qa_setscene
1106
1107python do_qa_staging() {
1108 bb.note("QA checking staging")
Andrew Geisslereff27472021-10-29 15:35:00 -05001109 qa_check_staged(d.expand('${SYSROOT_DESTDIR}${libdir}'), d)
1110 oe.qa.exit_with_message_if_errors("QA staging was broken by the package built above", d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001111}
1112
Brad Bishopd89cb5f2019-04-10 09:02:41 -04001113python do_qa_patch() {
1114 import subprocess
1115
1116 ###########################################################################
1117 # Check patch.log for fuzz warnings
1118 #
1119 # Further information on why we check for patch fuzz warnings:
1120 # http://lists.openembedded.org/pipermail/openembedded-core/2018-March/148675.html
1121 # https://bugzilla.yoctoproject.org/show_bug.cgi?id=10450
1122 ###########################################################################
1123
1124 logdir = d.getVar('T')
1125 patchlog = os.path.join(logdir,"log.do_patch")
1126
1127 if os.path.exists(patchlog):
1128 fuzzheader = '--- Patch fuzz start ---'
1129 fuzzfooter = '--- Patch fuzz end ---'
1130 statement = "grep -e '%s' %s > /dev/null" % (fuzzheader, patchlog)
1131 if subprocess.call(statement, shell=True) == 0:
1132 msg = "Fuzz detected:\n\n"
1133 fuzzmsg = ""
1134 inFuzzInfo = False
1135 f = open(patchlog, "r")
1136 for line in f:
1137 if fuzzheader in line:
1138 inFuzzInfo = True
1139 fuzzmsg = ""
1140 elif fuzzfooter in line:
1141 fuzzmsg = fuzzmsg.replace('\n\n', '\n')
1142 msg += fuzzmsg
1143 msg += "\n"
1144 inFuzzInfo = False
1145 elif inFuzzInfo and not 'Now at patch' in line:
1146 fuzzmsg += line
1147 f.close()
1148 msg += "The context lines in the patches can be updated with devtool:\n"
1149 msg += "\n"
1150 msg += " devtool modify %s\n" % d.getVar('PN')
1151 msg += " devtool finish --force-patch-refresh %s <layer_path>\n\n" % d.getVar('PN')
1152 msg += "Don't forget to review changes done by devtool!\n"
1153 if 'patch-fuzz' in d.getVar('ERROR_QA'):
1154 bb.error(msg)
1155 elif 'patch-fuzz' in d.getVar('WARN_QA'):
1156 bb.warn(msg)
1157 msg = "Patch log indicates that patches do not apply cleanly."
Andrew Geisslereff27472021-10-29 15:35:00 -05001158 oe.qa.handle_error("patch-fuzz", msg, d)
Andrew Geissler595f6302022-01-24 19:11:47 +00001159
1160 # Check if the patch contains a correctly formatted and spelled Upstream-Status
1161 import re
1162 from oe import patch
1163
1164 for url in patch.src_patches(d):
1165 (_, _, fullpath, _, _, _) = bb.fetch.decodeurl(url)
1166
1167 # skip patches not in oe-core
1168 if '/meta/' not in fullpath:
1169 continue
1170
1171 content = open(fullpath, encoding='utf-8', errors='ignore').read()
1172 kinda_status_re = re.compile(r"^.*upstream.*status.*$", re.IGNORECASE | re.MULTILINE)
1173 strict_status_re = re.compile(r"^Upstream-Status: (Pending|Submitted|Denied|Accepted|Inappropriate|Backport|Inactive-Upstream)( .+)?$", re.MULTILINE)
1174 match_kinda = kinda_status_re.search(content)
1175 match_strict = strict_status_re.search(content)
1176 guidelines = "https://www.openembedded.org/wiki/Commit_Patch_Message_Guidelines#Patch_Header_Recommendations:_Upstream-Status"
1177
1178 if not match_strict:
1179 if match_kinda:
1180 bb.error("Malformed Upstream-Status in patch\n%s\nPlease correct according to %s :\n%s" % (fullpath, guidelines, match_kinda.group(0)))
1181 else:
1182 bb.error("Missing Upstream-Status in patch\n%s\nPlease add according to %s ." % (fullpath, guidelines))
Brad Bishopd89cb5f2019-04-10 09:02:41 -04001183}
1184
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001185python do_qa_configure() {
1186 import subprocess
1187
1188 ###########################################################################
1189 # Check config.log for cross compile issues
1190 ###########################################################################
1191
1192 configs = []
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001193 workdir = d.getVar('WORKDIR')
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001194
Brad Bishop19323692019-04-05 15:28:33 -04001195 skip = (d.getVar('INSANE_SKIP') or "").split()
1196 skip_configure_unsafe = False
1197 if 'configure-unsafe' in skip:
1198 bb.note("Recipe %s skipping qa checking: configure-unsafe" % d.getVar('PN'))
1199 skip_configure_unsafe = True
1200
1201 if bb.data.inherits_class('autotools', d) and not skip_configure_unsafe:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001202 bb.note("Checking autotools environment for common misconfiguration")
1203 for root, dirs, files in os.walk(workdir):
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001204 statement = "grep -q -F -e 'is unsafe for cross-compilation' %s" % \
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001205 os.path.join(root,"config.log")
1206 if "config.log" in files:
1207 if subprocess.call(statement, shell=True) == 0:
Brad Bishop19323692019-04-05 15:28:33 -04001208 error_msg = """This autoconf log indicates errors, it looked at host include and/or library paths while determining system capabilities.
1209Rerun configure task after fixing this."""
Andrew Geisslereff27472021-10-29 15:35:00 -05001210 oe.qa.handle_error("configure-unsafe", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001211
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001212 if "configure.ac" in files:
1213 configs.append(os.path.join(root,"configure.ac"))
1214 if "configure.in" in files:
1215 configs.append(os.path.join(root, "configure.in"))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001216
1217 ###########################################################################
1218 # Check gettext configuration and dependencies are correct
1219 ###########################################################################
1220
Brad Bishop19323692019-04-05 15:28:33 -04001221 skip_configure_gettext = False
1222 if 'configure-gettext' in skip:
1223 bb.note("Recipe %s skipping qa checking: configure-gettext" % d.getVar('PN'))
1224 skip_configure_gettext = True
1225
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001226 cnf = d.getVar('EXTRA_OECONF') or ""
Brad Bishop19323692019-04-05 15:28:33 -04001227 if not ("gettext" in d.getVar('P') or "gcc-runtime" in d.getVar('P') or \
1228 "--disable-nls" in cnf or skip_configure_gettext):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001229 ml = d.getVar("MLPREFIX") or ""
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001230 if bb.data.inherits_class('cross-canadian', d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001231 gt = "nativesdk-gettext"
1232 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001233 gt = "gettext-native"
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001234 deps = bb.utils.explode_deps(d.getVar('DEPENDS') or "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001235 if gt not in deps:
1236 for config in configs:
1237 gnu = "grep \"^[[:space:]]*AM_GNU_GETTEXT\" %s >/dev/null" % config
1238 if subprocess.call(gnu, shell=True) == 0:
Brad Bishopd89cb5f2019-04-10 09:02:41 -04001239 error_msg = "AM_GNU_GETTEXT used but no inherit gettext"
Andrew Geisslereff27472021-10-29 15:35:00 -05001240 oe.qa.handle_error("configure-gettext", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001241
1242 ###########################################################################
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001243 # Check unrecognised configure options (with a white list)
1244 ###########################################################################
Andrew Geissler595f6302022-01-24 19:11:47 +00001245 if bb.data.inherits_class("autotools", d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001246 bb.note("Checking configure output for unrecognised options")
1247 try:
Brad Bishopc342db32019-05-15 21:57:59 -04001248 if bb.data.inherits_class("autotools", d):
1249 flag = "WARNING: unrecognized options:"
1250 log = os.path.join(d.getVar('B'), 'config.log')
Brad Bishopc342db32019-05-15 21:57:59 -04001251 output = subprocess.check_output(['grep', '-F', flag, log]).decode("utf-8").replace(', ', ' ').replace('"', '')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001252 options = set()
1253 for line in output.splitlines():
1254 options |= set(line.partition(flag)[2].split())
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001255 whitelist = set(d.getVar("UNKNOWN_CONFIGURE_WHITELIST").split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001256 options -= whitelist
1257 if options:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001258 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001259 error_msg = pn + ": configure was passed unrecognised options: " + " ".join(options)
Andrew Geisslereff27472021-10-29 15:35:00 -05001260 oe.qa.handle_error("unknown-configure-option", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001261 except subprocess.CalledProcessError:
1262 pass
1263
1264 # Check invalid PACKAGECONFIG
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001265 pkgconfig = (d.getVar("PACKAGECONFIG") or "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001266 if pkgconfig:
1267 pkgconfigflags = d.getVarFlags("PACKAGECONFIG") or {}
1268 for pconfig in pkgconfig:
1269 if pconfig not in pkgconfigflags:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001270 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001271 error_msg = "%s: invalid PACKAGECONFIG: %s" % (pn, pconfig)
Andrew Geisslereff27472021-10-29 15:35:00 -05001272 oe.qa.handle_error("invalid-packageconfig", error_msg, d)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001273
Andrew Geisslereff27472021-10-29 15:35:00 -05001274 oe.qa.exit_if_errors(d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001275}
1276
Andrew Geissler595f6302022-01-24 19:11:47 +00001277def unpack_check_src_uri(pn, d):
1278 import re
1279
1280 skip = (d.getVar('INSANE_SKIP') or "").split()
1281 if 'src-uri-bad' in skip:
1282 bb.note("Recipe %s skipping qa checking: src-uri-bad" % d.getVar('PN'))
1283 return
1284
1285 if "${PN}" in d.getVar("SRC_URI", False):
1286 oe.qa.handle_error("src-uri-bad", "%s: SRC_URI uses PN not BPN" % pn, d)
1287
1288 for url in d.getVar("SRC_URI").split():
1289 if re.search(r"git(hu|la)b\.com/.+/.+/archive/.+", url):
1290 oe.qa.handle_error("src-uri-bad", "%s: SRC_URI uses unstable GitHub/GitLab archives, convert recipe to use git protocol" % pn, d)
1291
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001292python do_qa_unpack() {
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001293 src_uri = d.getVar('SRC_URI')
1294 s_dir = d.getVar('S')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001295 if src_uri and not os.path.exists(s_dir):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001296 bb.warn('%s: the directory %s (%s) pointed to by the S variable doesn\'t exist - please set S within the recipe to point to where the source has been unpacked to' % (d.getVar('PN'), d.getVar('S', False), s_dir))
Andrew Geissler595f6302022-01-24 19:11:47 +00001297
1298 unpack_check_src_uri(d.getVar('PN'), d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001299}
1300
1301# The Staging Func, to check all staging
1302#addtask qa_staging after do_populate_sysroot before do_build
1303do_populate_sysroot[postfuncs] += "do_qa_staging "
1304
Brad Bishopd89cb5f2019-04-10 09:02:41 -04001305# Check for patch fuzz
1306do_patch[postfuncs] += "do_qa_patch "
1307
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001308# Check broken config.log files, for packages requiring Gettext which
1309# don't have it in DEPENDS.
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001310#addtask qa_configure after do_configure before do_compile
1311do_configure[postfuncs] += "do_qa_configure "
1312
1313# Check does S exist.
1314do_unpack[postfuncs] += "do_qa_unpack"
1315
1316python () {
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001317 import re
1318
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001319 tests = d.getVar('ALL_QA').split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001320 if "desktop" in tests:
1321 d.appendVar("PACKAGE_DEPENDS", " desktop-file-utils-native")
1322
1323 ###########################################################################
1324 # Check various variables
1325 ###########################################################################
1326
1327 # Checking ${FILESEXTRAPATHS}
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001328 extrapaths = (d.getVar("FILESEXTRAPATHS") or "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001329 if '__default' not in extrapaths.split(":"):
Patrick Williams213cb262021-08-07 19:21:33 -05001330 msg = "FILESEXTRAPATHS-variable, must always use :prepend (or :append)\n"
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001331 msg += "type of assignment, and don't forget the colon.\n"
1332 msg += "Please assign it with the format of:\n"
Patrick Williams213cb262021-08-07 19:21:33 -05001333 msg += " FILESEXTRAPATHS:append := \":${THISDIR}/Your_Files_Path\" or\n"
1334 msg += " FILESEXTRAPATHS:prepend := \"${THISDIR}/Your_Files_Path:\"\n"
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001335 msg += "in your bbappend file\n\n"
1336 msg += "Your incorrect assignment is:\n"
1337 msg += "%s\n" % extrapaths
1338 bb.warn(msg)
1339
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001340 overrides = d.getVar('OVERRIDES').split(':')
1341 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001342 if pn in overrides:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001343 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 -05001344 oe.qa.handle_error("pn-overrides", msg, d)
Brad Bishop977dc1a2019-02-06 16:01:43 -05001345 prog = re.compile(r'[A-Z]')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001346 if prog.search(pn):
Andrew Geisslereff27472021-10-29 15:35:00 -05001347 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 -05001348
Patrick Williams0ca19cc2021-08-16 14:03:13 -05001349 # Some people mistakenly use DEPENDS:${PN} instead of DEPENDS and wonder
Brad Bishop08902b02019-08-20 09:16:51 -04001350 # why it doesn't work.
Patrick Williams0ca19cc2021-08-16 14:03:13 -05001351 if (d.getVar(d.expand('DEPENDS:${PN}'))):
Andrew Geisslereff27472021-10-29 15:35:00 -05001352 oe.qa.handle_error("pkgvarcheck", "recipe uses DEPENDS:${PN}, should use DEPENDS", d)
Brad Bishop08902b02019-08-20 09:16:51 -04001353
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001354 issues = []
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001355 if (d.getVar('PACKAGES') or "").split():
1356 for dep in (d.getVar('QADEPENDS') or "").split():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001357 d.appendVarFlag('do_package_qa', 'depends', " %s:do_populate_sysroot" % dep)
1358 for var in 'RDEPENDS', 'RRECOMMENDS', 'RSUGGESTS', 'RCONFLICTS', 'RPROVIDES', 'RREPLACES', 'FILES', 'pkg_preinst', 'pkg_postinst', 'pkg_prerm', 'pkg_postrm', 'ALLOW_EMPTY':
1359 if d.getVar(var, False):
1360 issues.append(var)
1361
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001362 fakeroot_tests = d.getVar('FAKEROOT_QA').split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001363 if set(tests) & set(fakeroot_tests):
1364 d.setVarFlag('do_package_qa', 'fakeroot', '1')
1365 d.appendVarFlag('do_package_qa', 'depends', ' virtual/fakeroot-native:do_populate_sysroot')
1366 else:
1367 d.setVarFlag('do_package_qa', 'rdeptask', '')
1368 for i in issues:
Andrew Geisslereff27472021-10-29 15:35:00 -05001369 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 -06001370
1371 if 'native-last' not in (d.getVar('INSANE_SKIP') or "").split():
1372 for native_class in ['native', 'nativesdk']:
1373 if bb.data.inherits_class(native_class, d):
1374
1375 inherited_classes = d.getVar('__inherit_cache', False) or []
1376 needle = os.path.join('classes', native_class)
1377
1378 bbclassextend = (d.getVar('BBCLASSEXTEND') or '').split()
1379 # BBCLASSEXTEND items are always added in the end
1380 skip_classes = bbclassextend
1381 if bb.data.inherits_class('native', d) or 'native' in bbclassextend:
1382 # native also inherits nopackages and relocatable bbclasses
1383 skip_classes.extend(['nopackages', 'relocatable'])
1384
1385 broken_order = []
1386 for class_item in reversed(inherited_classes):
1387 if needle not in class_item:
1388 for extend_item in skip_classes:
1389 if os.path.join('classes', '%s.bbclass' % extend_item) in class_item:
1390 break
1391 else:
1392 pn = d.getVar('PN')
1393 broken_order.append(os.path.basename(class_item))
1394 else:
1395 break
1396 if broken_order:
Andrew Geisslereff27472021-10-29 15:35:00 -05001397 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 -06001398 "Classes inherited after native/nativesdk: %s" % (pn, " ".join(broken_order)), d)
1399
Andrew Geisslereff27472021-10-29 15:35:00 -05001400 oe.qa.exit_if_errors(d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001401}