blob: 37e10ad850ad97a6f737f4d5ad5d41cd8276344f [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 Geissler615f2f12022-07-15 14:00:58 -050023WARN_QA ?= " libdir xorg-driver-abi buildpaths \
Andrew Geisslerc182c622020-05-15 14:13:32 -050024 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 \
Andrew Geissler9aee5002022-03-30 16:27:02 +000030 license-incompatible license-file-missing obsolete-license \
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 Geissler9aee5002022-03-30 16:27:02 +0000432 if "GNU_HASH" in line or "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 """
Andrew Geissler9aee5002022-03-30 16:27:02 +0000444 Check for build paths inside target files and error if paths are not
445 explicitly ignored.
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500446 """
Andrew Geissler615f2f12022-07-15 14:00:58 -0500447 import stat
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500448
Andrew Geissler615f2f12022-07-15 14:00:58 -0500449 # Ignore symlinks/devs/fifos
450 mode = os.lstat(path).st_mode
451 if stat.S_ISLNK(mode) or stat.S_ISBLK(mode) or stat.S_ISFIFO(mode) or stat.S_ISCHR(mode) or stat.S_ISSOCK(mode):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500452 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 = []
Andrew Geissler9aee5002022-03-30 16:27:02 +0000552 m = hashlib.new('MD5', usedforsecurity=False)
Brad Bishop19323692019-04-05 15:28:33 -0400553 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
Andrew Geissler615f2f12022-07-15 14:00:58 -0500632 skip_shebang_size = False
633 if 'shebang-size' in skip:
634 bb.note("Recipe %s skipping qa checkking: shebang-size" % d.getVar('PN'))
635 skip_shebang_size = True
636
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500637 # find all .la and .pc files
638 # read the content
639 # and check for stuff that looks wrong
640 for root, dirs, files in os.walk(path):
641 for file in files:
642 path = os.path.join(root,file)
Brad Bishop19323692019-04-05 15:28:33 -0400643 if file.endswith(".la") and not skip_la:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500644 with open(path) as f:
645 file_content = f.read()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500646 file_content = file_content.replace(recipesysroot, "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500647 if workdir in file_content:
648 error_msg = "%s failed sanity test (workdir) in path %s" % (file,root)
Andrew Geisslereff27472021-10-29 15:35:00 -0500649 oe.qa.handle_error("la", error_msg, d)
Brad Bishop19323692019-04-05 15:28:33 -0400650 elif file.endswith(".pc") and not skip_pkgconfig:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500651 with open(path) as f:
652 file_content = f.read()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500653 file_content = file_content.replace(recipesysroot, "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500654 if pkgconfigcheck in file_content:
655 error_msg = "%s failed sanity test (tmpdir) in path %s" % (file,root)
Andrew Geisslereff27472021-10-29 15:35:00 -0500656 oe.qa.handle_error("pkgconfig", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500657
Andrew Geissler615f2f12022-07-15 14:00:58 -0500658 if not skip_shebang_size:
659 errors = {}
660 package_qa_check_shebang_size(path, "", d, None, errors)
661 for e in errors:
662 oe.qa.handle_error(e, errors[e], d)
663
664
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500665# Run all package-wide warnfuncs and errorfuncs
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500666def package_qa_package(warnfuncs, errorfuncs, package, d):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500667 warnings = {}
668 errors = {}
669
670 for func in warnfuncs:
671 func(package, d, warnings)
672 for func in errorfuncs:
673 func(package, d, errors)
674
675 for w in warnings:
Andrew Geisslereff27472021-10-29 15:35:00 -0500676 oe.qa.handle_error(w, warnings[w], d)
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500677 for e in errors:
Andrew Geisslereff27472021-10-29 15:35:00 -0500678 oe.qa.handle_error(e, errors[e], d)
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500679
680 return len(errors) == 0
681
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500682# Run all recipe-wide warnfuncs and errorfuncs
683def package_qa_recipe(warnfuncs, errorfuncs, pn, d):
684 warnings = {}
685 errors = {}
686
687 for func in warnfuncs:
688 func(pn, d, warnings)
689 for func in errorfuncs:
690 func(pn, d, errors)
691
692 for w in warnings:
Andrew Geisslereff27472021-10-29 15:35:00 -0500693 oe.qa.handle_error(w, warnings[w], d)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500694 for e in errors:
Andrew Geisslereff27472021-10-29 15:35:00 -0500695 oe.qa.handle_error(e, errors[e], d)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500696
697 return len(errors) == 0
698
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000699def prepopulate_objdump_p(elf, d):
700 output = elf.run_objdump("-p", d)
701 return (elf.name, output)
702
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500703# Walk over all files in a directory and call func
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500704def package_qa_walk(warnfuncs, errorfuncs, package, d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500705 #if this will throw an exception, then fix the dict above
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000706 target_os = d.getVar('HOST_OS')
707 target_arch = d.getVar('HOST_ARCH')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500708
709 warnings = {}
710 errors = {}
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000711 elves = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500712 for path in pkgfiles[package]:
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500713 elf = None
714 if os.path.isfile(path):
715 elf = oe.qa.ELFFile(path)
716 try:
717 elf.open()
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000718 elf.close()
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500719 except oe.qa.NotELFFileError:
720 elf = None
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000721 if elf:
722 elves[path] = elf
723
724 results = oe.utils.multiprocess_launch(prepopulate_objdump_p, elves.values(), d, extraargs=(d,))
725 for item in results:
726 elves[item[0]].set_objdump("-p", item[1])
727
728 for path in pkgfiles[package]:
729 if path in elves:
730 elves[path].open()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500731 for func in warnfuncs:
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000732 func(path, package, d, elves.get(path), warnings)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500733 for func in errorfuncs:
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000734 func(path, package, d, elves.get(path), errors)
735 if path in elves:
736 elves[path].close()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500737
738 for w in warnings:
Andrew Geisslereff27472021-10-29 15:35:00 -0500739 oe.qa.handle_error(w, warnings[w], d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500740 for e in errors:
Andrew Geisslereff27472021-10-29 15:35:00 -0500741 oe.qa.handle_error(e, errors[e], d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500742
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500743def package_qa_check_rdepends(pkg, pkgdest, skip, taskdeps, packages, d):
744 # Don't do this check for kernel/module recipes, there aren't too many debug/development
745 # packages and you can get false positives e.g. on kernel-module-lirc-dev
746 if bb.data.inherits_class("kernel", d) or bb.data.inherits_class("module-base", d):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500747 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500748
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500749 if not "-dbg" in pkg and not "packagegroup-" in pkg and not "-image" in pkg:
750 localdata = bb.data.createCopy(d)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500751 localdata.setVar('OVERRIDES', localdata.getVar('OVERRIDES') + ':' + pkg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500752
753 # Now check the RDEPENDS
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500754 rdepends = bb.utils.explode_deps(localdata.getVar('RDEPENDS') or "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500755
756 # Now do the sanity check!!!
757 if "build-deps" not in skip:
758 for rdepend in rdepends:
759 if "-dbg" in rdepend and "debug-deps" not in skip:
760 error_msg = "%s rdepends on %s" % (pkg,rdepend)
Andrew Geisslereff27472021-10-29 15:35:00 -0500761 oe.qa.handle_error("debug-deps", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500762 if (not "-dev" in pkg and not "-staticdev" in pkg) and rdepend.endswith("-dev") and "dev-deps" not in skip:
763 error_msg = "%s rdepends on %s" % (pkg, rdepend)
Andrew Geisslereff27472021-10-29 15:35:00 -0500764 oe.qa.handle_error("dev-deps", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500765 if rdepend not in packages:
766 rdep_data = oe.packagedata.read_subpkgdata(rdepend, d)
767 if rdep_data and 'PN' in rdep_data and rdep_data['PN'] in taskdeps:
768 continue
769 if not rdep_data or not 'PN' in rdep_data:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500770 pkgdata_dir = d.getVar("PKGDATA_DIR")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500771 try:
772 possibles = os.listdir("%s/runtime-rprovides/%s/" % (pkgdata_dir, rdepend))
773 except OSError:
774 possibles = []
775 for p in possibles:
776 rdep_data = oe.packagedata.read_subpkgdata(p, d)
777 if rdep_data and 'PN' in rdep_data and rdep_data['PN'] in taskdeps:
778 break
779 if rdep_data and 'PN' in rdep_data and rdep_data['PN'] in taskdeps:
780 continue
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500781 if rdep_data and 'PN' in rdep_data:
782 error_msg = "%s rdepends on %s, but it isn't a build dependency, missing %s in DEPENDS or PACKAGECONFIG?" % (pkg, rdepend, rdep_data['PN'])
783 else:
784 error_msg = "%s rdepends on %s, but it isn't a build dependency?" % (pkg, rdepend)
Andrew Geisslereff27472021-10-29 15:35:00 -0500785 oe.qa.handle_error("build-deps", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500786
787 if "file-rdeps" not in skip:
788 ignored_file_rdeps = set(['/bin/sh', '/usr/bin/env', 'rtld(GNU_HASH)'])
789 if bb.data.inherits_class('nativesdk', d):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500790 ignored_file_rdeps |= set(['/bin/bash', '/usr/bin/perl', 'perl'])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500791 # For Saving the FILERDEPENDS
792 filerdepends = {}
793 rdep_data = oe.packagedata.read_subpkgdata(pkg, d)
794 for key in rdep_data:
Andrew Geissler5199d832021-09-24 16:47:35 -0500795 if key.startswith("FILERDEPENDS:"):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500796 for subkey in bb.utils.explode_deps(rdep_data[key]):
797 if subkey not in ignored_file_rdeps and \
798 not subkey.startswith('perl('):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500799 # We already know it starts with FILERDEPENDS_
800 filerdepends[subkey] = key[13:]
801
802 if filerdepends:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500803 done = rdepends[:]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500804 # Add the rprovides of itself
805 if pkg not in done:
806 done.insert(0, pkg)
807
808 # The python is not a package, but python-core provides it, so
809 # skip checking /usr/bin/python if python is in the rdeps, in
Patrick Williams213cb262021-08-07 19:21:33 -0500810 # case there is a RDEPENDS:pkg = "python" in the recipe.
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500811 for py in [ d.getVar('MLPREFIX') + "python", "python" ]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500812 if py in done:
813 filerdepends.pop("/usr/bin/python",None)
814 done.remove(py)
815 for rdep in done:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500816 # The file dependencies may contain package names, e.g.,
817 # perl
818 filerdepends.pop(rdep,None)
819
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500820 # For Saving the FILERPROVIDES, RPROVIDES and FILES_INFO
821 rdep_data = oe.packagedata.read_subpkgdata(rdep, d)
822 for key in rdep_data:
Andrew Geisslerd159c7f2021-09-02 21:05:58 -0500823 if key.startswith("FILERPROVIDES:") or key.startswith("RPROVIDES:"):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500824 for subkey in bb.utils.explode_deps(rdep_data[key]):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500825 filerdepends.pop(subkey,None)
826 # Add the files list to the rprovides
Andrew Geisslerd159c7f2021-09-02 21:05:58 -0500827 if key.startswith("FILES_INFO:"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500828 # Use eval() to make it as a dict
829 for subkey in eval(rdep_data[key]):
830 filerdepends.pop(subkey,None)
831 if not filerdepends:
832 # Break if all the file rdepends are met
833 break
834 if filerdepends:
835 for key in filerdepends:
Patrick Williams213cb262021-08-07 19:21:33 -0500836 error_msg = "%s contained in package %s requires %s, but no providers found in RDEPENDS:%s?" % \
837 (filerdepends[key].replace(":%s" % pkg, "").replace("@underscore@", "_"), pkg, key, pkg)
Andrew Geisslereff27472021-10-29 15:35:00 -0500838 oe.qa.handle_error("file-rdeps", error_msg, d)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500839package_qa_check_rdepends[vardepsexclude] = "OVERRIDES"
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500840
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500841def package_qa_check_deps(pkg, pkgdest, d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500842
843 localdata = bb.data.createCopy(d)
844 localdata.setVar('OVERRIDES', pkg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500845
846 def check_valid_deps(var):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500847 try:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500848 rvar = bb.utils.explode_dep_versions2(localdata.getVar(var) or "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500849 except ValueError as e:
Patrick Williams0ca19cc2021-08-16 14:03:13 -0500850 bb.fatal("%s:%s: %s" % (var, pkg, e))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500851 for dep in rvar:
852 for v in rvar[dep]:
853 if v and not v.startswith(('< ', '= ', '> ', '<= ', '>=')):
Patrick Williams0ca19cc2021-08-16 14:03:13 -0500854 error_msg = "%s:%s is invalid: %s (%s) only comparisons <, =, >, <=, and >= are allowed" % (var, pkg, dep, v)
Andrew Geisslereff27472021-10-29 15:35:00 -0500855 oe.qa.handle_error("dep-cmp", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500856
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500857 check_valid_deps('RDEPENDS')
858 check_valid_deps('RRECOMMENDS')
859 check_valid_deps('RSUGGESTS')
860 check_valid_deps('RPROVIDES')
861 check_valid_deps('RREPLACES')
862 check_valid_deps('RCONFLICTS')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500863
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500864QAPKGTEST[usrmerge] = "package_qa_check_usrmerge"
865def package_qa_check_usrmerge(pkg, d, messages):
Andrew Geisslereff27472021-10-29 15:35:00 -0500866
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500867 pkgdest = d.getVar('PKGDEST')
868 pkg_dir = pkgdest + os.sep + pkg + os.sep
869 merged_dirs = ['bin', 'sbin', 'lib'] + d.getVar('MULTILIB_VARIANTS').split()
870 for f in merged_dirs:
871 if os.path.exists(pkg_dir + f) and not os.path.islink(pkg_dir + f):
872 msg = "%s package is not obeying usrmerge distro feature. /%s should be relocated to /usr." % (pkg, f)
Andrew Geisslereff27472021-10-29 15:35:00 -0500873 oe.qa.add_message(messages, "usrmerge", msg)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500874 return False
875 return True
876
Brad Bishopf3f93bb2019-10-16 14:33:32 -0400877QAPKGTEST[perllocalpod] = "package_qa_check_perllocalpod"
878def package_qa_check_perllocalpod(pkg, d, messages):
879 """
880 Check that the recipe didn't ship a perlocal.pod file, which shouldn't be
881 installed in a distribution package. cpan.bbclass sets NO_PERLLOCAL=1 to
882 handle this for most recipes.
883 """
884 import glob
885 pkgd = oe.path.join(d.getVar('PKGDEST'), pkg)
886 podpath = oe.path.join(pkgd, d.getVar("libdir"), "perl*", "*", "*", "perllocal.pod")
887
888 matches = glob.glob(podpath)
889 if matches:
890 matches = [package_qa_clean_path(path, d, pkg) for path in matches]
891 msg = "%s contains perllocal.pod (%s), should not be installed" % (pkg, " ".join(matches))
Andrew Geisslereff27472021-10-29 15:35:00 -0500892 oe.qa.add_message(messages, "perllocalpod", msg)
Brad Bishopf3f93bb2019-10-16 14:33:32 -0400893
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500894QAPKGTEST[expanded-d] = "package_qa_check_expanded_d"
895def package_qa_check_expanded_d(package, d, messages):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500896 """
897 Check for the expanded D (${D}) value in pkg_* and FILES
898 variables, warn the user to use it correctly.
899 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500900 sane = True
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500901 expanded_d = d.getVar('D')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500902
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500903 for var in 'FILES','pkg_preinst', 'pkg_postinst', 'pkg_prerm', 'pkg_postrm':
Patrick Williams0ca19cc2021-08-16 14:03:13 -0500904 bbvar = d.getVar(var + ":" + package) or ""
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500905 if expanded_d in bbvar:
906 if var == 'FILES':
Andrew Geisslereff27472021-10-29 15:35:00 -0500907 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 -0500908 sane = False
909 else:
Andrew Geisslereff27472021-10-29 15:35:00 -0500910 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 -0500911 sane = False
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500912 return sane
913
Andrew Geissler1e34c2d2020-05-29 16:02:59 -0500914QAPKGTEST[unlisted-pkg-lics] = "package_qa_check_unlisted_pkg_lics"
915def package_qa_check_unlisted_pkg_lics(package, d, messages):
916 """
917 Check that all licenses for a package are among the licenses for the recipe.
918 """
Patrick Williams213cb262021-08-07 19:21:33 -0500919 pkg_lics = d.getVar('LICENSE:' + package)
Andrew Geissler1e34c2d2020-05-29 16:02:59 -0500920 if not pkg_lics:
921 return True
922
923 recipe_lics_set = oe.license.list_licenses(d.getVar('LICENSE'))
Andrew Geissler9aee5002022-03-30 16:27:02 +0000924 package_lics = oe.license.list_licenses(pkg_lics)
925 unlisted = package_lics - recipe_lics_set
926 if unlisted:
927 oe.qa.add_message(messages, "unlisted-pkg-lics",
928 "LICENSE:%s includes licenses (%s) that are not "
929 "listed in LICENSE" % (package, ' '.join(unlisted)))
930 return False
931 obsolete = set(oe.license.obsolete_license_list()) & package_lics - recipe_lics_set
932 if obsolete:
933 oe.qa.add_message(messages, "obsolete-license",
934 "LICENSE:%s includes obsolete licenses %s" % (package, ' '.join(obsolete)))
935 return False
936 return True
Andrew Geissler1e34c2d2020-05-29 16:02:59 -0500937
Andrew Geissler595f6302022-01-24 19:11:47 +0000938QAPKGTEST[empty-dirs] = "package_qa_check_empty_dirs"
939def package_qa_check_empty_dirs(pkg, d, messages):
940 """
941 Check for the existence of files in directories that are expected to be
942 empty.
943 """
944
945 pkgd = oe.path.join(d.getVar('PKGDEST'), pkg)
946 for dir in (d.getVar('QA_EMPTY_DIRS') or "").split():
947 empty_dir = oe.path.join(pkgd, dir)
948 if os.path.exists(empty_dir) and os.listdir(empty_dir):
949 recommendation = (d.getVar('QA_EMPTY_DIRS_RECOMMENDATION:' + dir) or
950 "but it is expected to be empty")
951 msg = "%s installs files in %s, %s" % (pkg, dir, recommendation)
952 oe.qa.add_message(messages, "empty-dirs", msg)
953
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500954def package_qa_check_encoding(keys, encode, d):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600955 def check_encoding(key, enc):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500956 sane = True
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500957 value = d.getVar(key)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500958 if value:
959 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600960 s = value.encode(enc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500961 except UnicodeDecodeError as e:
962 error_msg = "%s has non %s characters" % (key,enc)
963 sane = False
Andrew Geisslereff27472021-10-29 15:35:00 -0500964 oe.qa.handle_error("invalid-chars", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500965 return sane
966
967 for key in keys:
968 sane = check_encoding(key, encode)
969 if not sane:
970 break
971
972HOST_USER_UID := "${@os.getuid()}"
973HOST_USER_GID := "${@os.getgid()}"
974
975QAPATHTEST[host-user-contaminated] = "package_qa_check_host_user"
976def package_qa_check_host_user(path, name, d, elf, messages):
977 """Check for paths outside of /home which are owned by the user running bitbake."""
978
979 if not os.path.lexists(path):
980 return
981
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500982 dest = d.getVar('PKGDEST')
983 pn = d.getVar('PN')
Andrew Geissler615f2f12022-07-15 14:00:58 -0500984 home = os.path.join(dest, name, 'home')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500985 if path == home or path.startswith(home + os.sep):
986 return
987
988 try:
989 stat = os.lstat(path)
990 except OSError as exc:
991 import errno
992 if exc.errno != errno.ENOENT:
993 raise
994 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500995 check_uid = int(d.getVar('HOST_USER_UID'))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500996 if stat.st_uid == check_uid:
Andrew Geisslereff27472021-10-29 15:35:00 -0500997 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 -0500998 return False
999
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001000 check_gid = int(d.getVar('HOST_USER_GID'))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001001 if stat.st_gid == check_gid:
Andrew Geisslereff27472021-10-29 15:35:00 -05001002 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 -05001003 return False
1004 return True
1005
Andrew Geissler5a43b432020-06-13 10:46:56 -05001006QARECIPETEST[unhandled-features-check] = "package_qa_check_unhandled_features_check"
1007def package_qa_check_unhandled_features_check(pn, d, messages):
1008 if not bb.data.inherits_class('features_check', d):
1009 var_set = False
1010 for kind in ['DISTRO', 'MACHINE', 'COMBINED']:
1011 for var in ['ANY_OF_' + kind + '_FEATURES', 'REQUIRED_' + kind + '_FEATURES', 'CONFLICT_' + kind + '_FEATURES']:
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001012 if d.getVar(var) is not None or d.hasOverrides(var):
Andrew Geissler5a43b432020-06-13 10:46:56 -05001013 var_set = True
1014 if var_set:
Andrew Geisslereff27472021-10-29 15:35:00 -05001015 oe.qa.handle_error("unhandled-features-check", "%s: recipe doesn't inherit features_check" % pn, d)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001016
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001017QARECIPETEST[missing-update-alternatives] = "package_qa_check_missing_update_alternatives"
1018def package_qa_check_missing_update_alternatives(pn, d, messages):
1019 # Look at all packages and find out if any of those sets ALTERNATIVE variable
1020 # without inheriting update-alternatives class
1021 for pkg in (d.getVar('PACKAGES') or '').split():
Patrick Williams213cb262021-08-07 19:21:33 -05001022 if d.getVar('ALTERNATIVE:%s' % pkg) and not bb.data.inherits_class('update-alternatives', d):
Andrew Geisslereff27472021-10-29 15:35:00 -05001023 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 -05001024
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001025# The PACKAGE FUNC to scan each package
1026python do_package_qa () {
1027 import subprocess
1028 import oe.packagedata
1029
1030 bb.note("DO PACKAGE QA")
1031
Andrew Geissler9aee5002022-03-30 16:27:02 +00001032 main_lic = d.getVar('LICENSE')
1033
1034 # Check for obsolete license references in main LICENSE (packages are checked below for any changes)
1035 main_licenses = oe.license.list_licenses(d.getVar('LICENSE'))
1036 obsolete = set(oe.license.obsolete_license_list()) & main_licenses
1037 if obsolete:
1038 oe.qa.handle_error("obsolete-license", "Recipe LICENSE includes obsolete licenses %s" % ' '.join(obsolete), d)
1039
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001040 bb.build.exec_func("read_subpackage_metadata", d)
1041
1042 # Check non UTF-8 characters on recipe's metadata
1043 package_qa_check_encoding(['DESCRIPTION', 'SUMMARY', 'LICENSE', 'SECTION'], 'utf-8', d)
1044
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001045 logdir = d.getVar('T')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001046 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001047
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001048 # Scan the packages...
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001049 pkgdest = d.getVar('PKGDEST')
1050 packages = set((d.getVar('PACKAGES') or '').split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001051
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001052 global pkgfiles
1053 pkgfiles = {}
1054 for pkg in packages:
1055 pkgfiles[pkg] = []
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001056 pkgdir = os.path.join(pkgdest, pkg)
1057 for walkroot, dirs, files in os.walk(pkgdir):
1058 # Don't walk into top-level CONTROL or DEBIAN directories as these
1059 # are temporary directories created by do_package.
1060 if walkroot == pkgdir:
1061 for control in ("CONTROL", "DEBIAN"):
1062 if control in dirs:
1063 dirs.remove(control)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001064 for file in files:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001065 pkgfiles[pkg].append(os.path.join(walkroot, file))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001066
1067 # no packages should be scanned
1068 if not packages:
1069 return
1070
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001071 import re
1072 # The package name matches the [a-z0-9.+-]+ regular expression
Brad Bishop977dc1a2019-02-06 16:01:43 -05001073 pkgname_pattern = re.compile(r"^[a-z0-9.+-]+$")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001074
1075 taskdepdata = d.getVar("BB_TASKDEPDATA", False)
1076 taskdeps = set()
1077 for dep in taskdepdata:
1078 taskdeps.add(taskdepdata[dep][0])
1079
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001080 def parse_test_matrix(matrix_name):
1081 testmatrix = d.getVarFlags(matrix_name) or {}
1082 g = globals()
1083 warnchecks = []
1084 for w in (d.getVar("WARN_QA") or "").split():
1085 if w in skip:
1086 continue
1087 if w in testmatrix and testmatrix[w] in g:
1088 warnchecks.append(g[testmatrix[w]])
1089
1090 errorchecks = []
1091 for e in (d.getVar("ERROR_QA") or "").split():
1092 if e in skip:
1093 continue
1094 if e in testmatrix and testmatrix[e] in g:
1095 errorchecks.append(g[testmatrix[e]])
1096 return warnchecks, errorchecks
1097
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001098 for package in packages:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001099 skip = set((d.getVar('INSANE_SKIP') or "").split() +
Patrick Williams213cb262021-08-07 19:21:33 -05001100 (d.getVar('INSANE_SKIP:' + package) or "").split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001101 if skip:
1102 bb.note("Package %s skipping QA tests: %s" % (package, str(skip)))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001103
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001104 bb.note("Checking Package: %s" % package)
1105 # Check package name
1106 if not pkgname_pattern.match(package):
Andrew Geisslereff27472021-10-29 15:35:00 -05001107 oe.qa.handle_error("pkgname",
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001108 "%s doesn't match the [a-z0-9.+-]+ regex" % package, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001109
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001110 warn_checks, error_checks = parse_test_matrix("QAPATHTEST")
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001111 package_qa_walk(warn_checks, error_checks, package, d)
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001112
1113 warn_checks, error_checks = parse_test_matrix("QAPKGTEST")
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001114 package_qa_package(warn_checks, error_checks, package, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001115
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001116 package_qa_check_rdepends(package, pkgdest, skip, taskdeps, packages, d)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001117 package_qa_check_deps(package, pkgdest, d)
1118
1119 warn_checks, error_checks = parse_test_matrix("QARECIPETEST")
1120 package_qa_recipe(warn_checks, error_checks, pn, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001121
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001122 if 'libdir' in d.getVar("ALL_QA").split():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001123 package_qa_check_libdir(d)
1124
Andrew Geisslereff27472021-10-29 15:35:00 -05001125 oe.qa.exit_if_errors(d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001126}
1127
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001128# binutils is used for most checks, so need to set as dependency
1129# POPULATESYSROOTDEPS is defined in staging class.
1130do_package_qa[depends] += "${POPULATESYSROOTDEPS}"
Patrick Williamsde0582f2022-04-08 10:23:27 -05001131do_package_qa[vardeps] = "${@bb.utils.contains('ERROR_QA', 'empty-dirs', 'QA_EMPTY_DIRS', '', d)}"
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001132do_package_qa[vardepsexclude] = "BB_TASKDEPDATA"
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001133do_package_qa[rdeptask] = "do_packagedata"
1134addtask do_package_qa after do_packagedata do_package before do_build
1135
Brad Bishop19323692019-04-05 15:28:33 -04001136# Add the package specific INSANE_SKIPs to the sstate dependencies
1137python() {
1138 pkgs = (d.getVar('PACKAGES') or '').split()
1139 for pkg in pkgs:
Patrick Williams213cb262021-08-07 19:21:33 -05001140 d.appendVarFlag("do_package_qa", "vardeps", " INSANE_SKIP:{}".format(pkg))
Brad Bishop19323692019-04-05 15:28:33 -04001141}
1142
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001143SSTATETASKS += "do_package_qa"
1144do_package_qa[sstate-inputdirs] = ""
1145do_package_qa[sstate-outputdirs] = ""
1146python do_package_qa_setscene () {
1147 sstate_setscene(d)
1148}
1149addtask do_package_qa_setscene
1150
Andrew Geissler615f2f12022-07-15 14:00:58 -05001151python do_qa_sysroot() {
1152 bb.note("QA checking do_populate_sysroot")
1153 sysroot_destdir = d.expand('${SYSROOT_DESTDIR}')
1154 for sysroot_dir in d.expand('${SYSROOT_DIRS}').split():
1155 qa_check_staged(sysroot_destdir + sysroot_dir, d)
1156 oe.qa.exit_with_message_if_errors("do_populate_sysroot for this recipe installed files with QA issues", d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001157}
Andrew Geissler615f2f12022-07-15 14:00:58 -05001158do_populate_sysroot[postfuncs] += "do_qa_sysroot"
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001159
Brad Bishopd89cb5f2019-04-10 09:02:41 -04001160python do_qa_patch() {
1161 import subprocess
1162
1163 ###########################################################################
1164 # Check patch.log for fuzz warnings
1165 #
1166 # Further information on why we check for patch fuzz warnings:
1167 # http://lists.openembedded.org/pipermail/openembedded-core/2018-March/148675.html
1168 # https://bugzilla.yoctoproject.org/show_bug.cgi?id=10450
1169 ###########################################################################
1170
1171 logdir = d.getVar('T')
1172 patchlog = os.path.join(logdir,"log.do_patch")
1173
1174 if os.path.exists(patchlog):
1175 fuzzheader = '--- Patch fuzz start ---'
1176 fuzzfooter = '--- Patch fuzz end ---'
1177 statement = "grep -e '%s' %s > /dev/null" % (fuzzheader, patchlog)
1178 if subprocess.call(statement, shell=True) == 0:
1179 msg = "Fuzz detected:\n\n"
1180 fuzzmsg = ""
1181 inFuzzInfo = False
1182 f = open(patchlog, "r")
1183 for line in f:
1184 if fuzzheader in line:
1185 inFuzzInfo = True
1186 fuzzmsg = ""
1187 elif fuzzfooter in line:
1188 fuzzmsg = fuzzmsg.replace('\n\n', '\n')
1189 msg += fuzzmsg
1190 msg += "\n"
1191 inFuzzInfo = False
1192 elif inFuzzInfo and not 'Now at patch' in line:
1193 fuzzmsg += line
1194 f.close()
1195 msg += "The context lines in the patches can be updated with devtool:\n"
1196 msg += "\n"
1197 msg += " devtool modify %s\n" % d.getVar('PN')
1198 msg += " devtool finish --force-patch-refresh %s <layer_path>\n\n" % d.getVar('PN')
1199 msg += "Don't forget to review changes done by devtool!\n"
Patrick Williamsde0582f2022-04-08 10:23:27 -05001200 if bb.utils.filter('ERROR_QA', 'patch-fuzz', d):
Brad Bishopd89cb5f2019-04-10 09:02:41 -04001201 bb.error(msg)
Patrick Williamsde0582f2022-04-08 10:23:27 -05001202 elif bb.utils.filter('WARN_QA', 'patch-fuzz', d):
Brad Bishopd89cb5f2019-04-10 09:02:41 -04001203 bb.warn(msg)
1204 msg = "Patch log indicates that patches do not apply cleanly."
Andrew Geisslereff27472021-10-29 15:35:00 -05001205 oe.qa.handle_error("patch-fuzz", msg, d)
Andrew Geissler595f6302022-01-24 19:11:47 +00001206
1207 # Check if the patch contains a correctly formatted and spelled Upstream-Status
1208 import re
1209 from oe import patch
1210
1211 for url in patch.src_patches(d):
1212 (_, _, fullpath, _, _, _) = bb.fetch.decodeurl(url)
1213
1214 # skip patches not in oe-core
1215 if '/meta/' not in fullpath:
1216 continue
1217
Andrew Geissler595f6302022-01-24 19:11:47 +00001218 kinda_status_re = re.compile(r"^.*upstream.*status.*$", re.IGNORECASE | re.MULTILINE)
1219 strict_status_re = re.compile(r"^Upstream-Status: (Pending|Submitted|Denied|Accepted|Inappropriate|Backport|Inactive-Upstream)( .+)?$", re.MULTILINE)
Andrew Geissler595f6302022-01-24 19:11:47 +00001220 guidelines = "https://www.openembedded.org/wiki/Commit_Patch_Message_Guidelines#Patch_Header_Recommendations:_Upstream-Status"
1221
Andrew Geissler78b72792022-06-14 06:47:25 -05001222 with open(fullpath, encoding='utf-8', errors='ignore') as f:
1223 file_content = f.read()
1224 match_kinda = kinda_status_re.search(file_content)
1225 match_strict = strict_status_re.search(file_content)
1226
1227 if not match_strict:
1228 if match_kinda:
1229 bb.error("Malformed Upstream-Status in patch\n%s\nPlease correct according to %s :\n%s" % (fullpath, guidelines, match_kinda.group(0)))
1230 else:
1231 bb.error("Missing Upstream-Status in patch\n%s\nPlease add according to %s ." % (fullpath, guidelines))
Brad Bishopd89cb5f2019-04-10 09:02:41 -04001232}
1233
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001234python do_qa_configure() {
1235 import subprocess
1236
1237 ###########################################################################
1238 # Check config.log for cross compile issues
1239 ###########################################################################
1240
1241 configs = []
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001242 workdir = d.getVar('WORKDIR')
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001243
Brad Bishop19323692019-04-05 15:28:33 -04001244 skip = (d.getVar('INSANE_SKIP') or "").split()
1245 skip_configure_unsafe = False
1246 if 'configure-unsafe' in skip:
1247 bb.note("Recipe %s skipping qa checking: configure-unsafe" % d.getVar('PN'))
1248 skip_configure_unsafe = True
1249
1250 if bb.data.inherits_class('autotools', d) and not skip_configure_unsafe:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001251 bb.note("Checking autotools environment for common misconfiguration")
1252 for root, dirs, files in os.walk(workdir):
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001253 statement = "grep -q -F -e 'is unsafe for cross-compilation' %s" % \
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001254 os.path.join(root,"config.log")
1255 if "config.log" in files:
1256 if subprocess.call(statement, shell=True) == 0:
Brad Bishop19323692019-04-05 15:28:33 -04001257 error_msg = """This autoconf log indicates errors, it looked at host include and/or library paths while determining system capabilities.
1258Rerun configure task after fixing this."""
Andrew Geisslereff27472021-10-29 15:35:00 -05001259 oe.qa.handle_error("configure-unsafe", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001260
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001261 if "configure.ac" in files:
1262 configs.append(os.path.join(root,"configure.ac"))
1263 if "configure.in" in files:
1264 configs.append(os.path.join(root, "configure.in"))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001265
1266 ###########################################################################
1267 # Check gettext configuration and dependencies are correct
1268 ###########################################################################
1269
Brad Bishop19323692019-04-05 15:28:33 -04001270 skip_configure_gettext = False
1271 if 'configure-gettext' in skip:
1272 bb.note("Recipe %s skipping qa checking: configure-gettext" % d.getVar('PN'))
1273 skip_configure_gettext = True
1274
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001275 cnf = d.getVar('EXTRA_OECONF') or ""
Brad Bishop19323692019-04-05 15:28:33 -04001276 if not ("gettext" in d.getVar('P') or "gcc-runtime" in d.getVar('P') or \
1277 "--disable-nls" in cnf or skip_configure_gettext):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001278 ml = d.getVar("MLPREFIX") or ""
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001279 if bb.data.inherits_class('cross-canadian', d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001280 gt = "nativesdk-gettext"
1281 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001282 gt = "gettext-native"
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001283 deps = bb.utils.explode_deps(d.getVar('DEPENDS') or "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001284 if gt not in deps:
1285 for config in configs:
1286 gnu = "grep \"^[[:space:]]*AM_GNU_GETTEXT\" %s >/dev/null" % config
1287 if subprocess.call(gnu, shell=True) == 0:
Brad Bishopd89cb5f2019-04-10 09:02:41 -04001288 error_msg = "AM_GNU_GETTEXT used but no inherit gettext"
Andrew Geisslereff27472021-10-29 15:35:00 -05001289 oe.qa.handle_error("configure-gettext", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001290
1291 ###########################################################################
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001292 # Check unrecognised configure options (with a white list)
1293 ###########################################################################
Andrew Geissler595f6302022-01-24 19:11:47 +00001294 if bb.data.inherits_class("autotools", d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001295 bb.note("Checking configure output for unrecognised options")
1296 try:
Brad Bishopc342db32019-05-15 21:57:59 -04001297 if bb.data.inherits_class("autotools", d):
1298 flag = "WARNING: unrecognized options:"
1299 log = os.path.join(d.getVar('B'), 'config.log')
Brad Bishopc342db32019-05-15 21:57:59 -04001300 output = subprocess.check_output(['grep', '-F', flag, log]).decode("utf-8").replace(', ', ' ').replace('"', '')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001301 options = set()
1302 for line in output.splitlines():
1303 options |= set(line.partition(flag)[2].split())
Andrew Geissler9aee5002022-03-30 16:27:02 +00001304 ignore_opts = set(d.getVar("UNKNOWN_CONFIGURE_OPT_IGNORE").split())
1305 options -= ignore_opts
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001306 if options:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001307 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001308 error_msg = pn + ": configure was passed unrecognised options: " + " ".join(options)
Andrew Geisslereff27472021-10-29 15:35:00 -05001309 oe.qa.handle_error("unknown-configure-option", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001310 except subprocess.CalledProcessError:
1311 pass
1312
1313 # Check invalid PACKAGECONFIG
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001314 pkgconfig = (d.getVar("PACKAGECONFIG") or "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001315 if pkgconfig:
1316 pkgconfigflags = d.getVarFlags("PACKAGECONFIG") or {}
1317 for pconfig in pkgconfig:
1318 if pconfig not in pkgconfigflags:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001319 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001320 error_msg = "%s: invalid PACKAGECONFIG: %s" % (pn, pconfig)
Andrew Geisslereff27472021-10-29 15:35:00 -05001321 oe.qa.handle_error("invalid-packageconfig", error_msg, d)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001322
Andrew Geisslereff27472021-10-29 15:35:00 -05001323 oe.qa.exit_if_errors(d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001324}
1325
Andrew Geissler595f6302022-01-24 19:11:47 +00001326def unpack_check_src_uri(pn, d):
1327 import re
1328
1329 skip = (d.getVar('INSANE_SKIP') or "").split()
1330 if 'src-uri-bad' in skip:
1331 bb.note("Recipe %s skipping qa checking: src-uri-bad" % d.getVar('PN'))
1332 return
1333
1334 if "${PN}" in d.getVar("SRC_URI", False):
1335 oe.qa.handle_error("src-uri-bad", "%s: SRC_URI uses PN not BPN" % pn, d)
1336
1337 for url in d.getVar("SRC_URI").split():
1338 if re.search(r"git(hu|la)b\.com/.+/.+/archive/.+", url):
1339 oe.qa.handle_error("src-uri-bad", "%s: SRC_URI uses unstable GitHub/GitLab archives, convert recipe to use git protocol" % pn, d)
1340
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001341python do_qa_unpack() {
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001342 src_uri = d.getVar('SRC_URI')
1343 s_dir = d.getVar('S')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001344 if src_uri and not os.path.exists(s_dir):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001345 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 +00001346
1347 unpack_check_src_uri(d.getVar('PN'), d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001348}
1349
Brad Bishopd89cb5f2019-04-10 09:02:41 -04001350# Check for patch fuzz
1351do_patch[postfuncs] += "do_qa_patch "
1352
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001353# Check broken config.log files, for packages requiring Gettext which
1354# don't have it in DEPENDS.
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001355#addtask qa_configure after do_configure before do_compile
1356do_configure[postfuncs] += "do_qa_configure "
1357
1358# Check does S exist.
1359do_unpack[postfuncs] += "do_qa_unpack"
1360
1361python () {
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001362 import re
1363
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001364 tests = d.getVar('ALL_QA').split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001365 if "desktop" in tests:
1366 d.appendVar("PACKAGE_DEPENDS", " desktop-file-utils-native")
1367
1368 ###########################################################################
1369 # Check various variables
1370 ###########################################################################
1371
1372 # Checking ${FILESEXTRAPATHS}
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001373 extrapaths = (d.getVar("FILESEXTRAPATHS") or "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001374 if '__default' not in extrapaths.split(":"):
Patrick Williams213cb262021-08-07 19:21:33 -05001375 msg = "FILESEXTRAPATHS-variable, must always use :prepend (or :append)\n"
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001376 msg += "type of assignment, and don't forget the colon.\n"
1377 msg += "Please assign it with the format of:\n"
Patrick Williams213cb262021-08-07 19:21:33 -05001378 msg += " FILESEXTRAPATHS:append := \":${THISDIR}/Your_Files_Path\" or\n"
1379 msg += " FILESEXTRAPATHS:prepend := \"${THISDIR}/Your_Files_Path:\"\n"
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001380 msg += "in your bbappend file\n\n"
1381 msg += "Your incorrect assignment is:\n"
1382 msg += "%s\n" % extrapaths
1383 bb.warn(msg)
1384
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001385 overrides = d.getVar('OVERRIDES').split(':')
1386 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001387 if pn in overrides:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001388 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 -05001389 oe.qa.handle_error("pn-overrides", msg, d)
Brad Bishop977dc1a2019-02-06 16:01:43 -05001390 prog = re.compile(r'[A-Z]')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001391 if prog.search(pn):
Andrew Geisslereff27472021-10-29 15:35:00 -05001392 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 -05001393
Patrick Williams0ca19cc2021-08-16 14:03:13 -05001394 # Some people mistakenly use DEPENDS:${PN} instead of DEPENDS and wonder
Brad Bishop08902b02019-08-20 09:16:51 -04001395 # why it doesn't work.
Patrick Williams0ca19cc2021-08-16 14:03:13 -05001396 if (d.getVar(d.expand('DEPENDS:${PN}'))):
Andrew Geisslereff27472021-10-29 15:35:00 -05001397 oe.qa.handle_error("pkgvarcheck", "recipe uses DEPENDS:${PN}, should use DEPENDS", d)
Brad Bishop08902b02019-08-20 09:16:51 -04001398
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001399 issues = []
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001400 if (d.getVar('PACKAGES') or "").split():
1401 for dep in (d.getVar('QADEPENDS') or "").split():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001402 d.appendVarFlag('do_package_qa', 'depends', " %s:do_populate_sysroot" % dep)
1403 for var in 'RDEPENDS', 'RRECOMMENDS', 'RSUGGESTS', 'RCONFLICTS', 'RPROVIDES', 'RREPLACES', 'FILES', 'pkg_preinst', 'pkg_postinst', 'pkg_prerm', 'pkg_postrm', 'ALLOW_EMPTY':
1404 if d.getVar(var, False):
1405 issues.append(var)
1406
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001407 fakeroot_tests = d.getVar('FAKEROOT_QA').split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001408 if set(tests) & set(fakeroot_tests):
1409 d.setVarFlag('do_package_qa', 'fakeroot', '1')
1410 d.appendVarFlag('do_package_qa', 'depends', ' virtual/fakeroot-native:do_populate_sysroot')
1411 else:
1412 d.setVarFlag('do_package_qa', 'rdeptask', '')
1413 for i in issues:
Andrew Geisslereff27472021-10-29 15:35:00 -05001414 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 -06001415
1416 if 'native-last' not in (d.getVar('INSANE_SKIP') or "").split():
1417 for native_class in ['native', 'nativesdk']:
1418 if bb.data.inherits_class(native_class, d):
1419
1420 inherited_classes = d.getVar('__inherit_cache', False) or []
1421 needle = os.path.join('classes', native_class)
1422
1423 bbclassextend = (d.getVar('BBCLASSEXTEND') or '').split()
1424 # BBCLASSEXTEND items are always added in the end
1425 skip_classes = bbclassextend
1426 if bb.data.inherits_class('native', d) or 'native' in bbclassextend:
1427 # native also inherits nopackages and relocatable bbclasses
1428 skip_classes.extend(['nopackages', 'relocatable'])
1429
1430 broken_order = []
1431 for class_item in reversed(inherited_classes):
1432 if needle not in class_item:
1433 for extend_item in skip_classes:
1434 if os.path.join('classes', '%s.bbclass' % extend_item) in class_item:
1435 break
1436 else:
1437 pn = d.getVar('PN')
1438 broken_order.append(os.path.basename(class_item))
1439 else:
1440 break
1441 if broken_order:
Andrew Geisslereff27472021-10-29 15:35:00 -05001442 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 -06001443 "Classes inherited after native/nativesdk: %s" % (pn, " ".join(broken_order)), d)
1444
Andrew Geisslereff27472021-10-29 15:35:00 -05001445 oe.qa.exit_if_errors(d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001446}