blob: be5ec60146f242493b08feaa6de93e51734a8601 [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001# BB Class inspired by ebuild.sh
2#
3# This class will test files after installation for certain
4# security issues and other kind of issues.
5#
6# Checks we do:
7# -Check the ownership and permissions
8# -Check the RUNTIME path for the $TMPDIR
9# -Check if .la files wrongly point to workdir
10# -Check if .pc files wrongly point to workdir
11# -Check if packages contains .debug directories or .so files
12# where they should be in -dev or -dbg
13# -Check if config.log contains traces to broken autoconf tests
14# -Check invalid characters (non-utf8) on some package metadata
15# -Ensure that binaries in base_[bindir|sbindir|libdir] do not link
16# into exec_prefix
17# -Check that scripts in base_[bindir|sbindir|libdir] do not reference
18# files under exec_prefix
Brad Bishopd7bf8c12018-02-25 22:55:05 -050019# -Check if the package name is upper case
Patrick Williamsc124f4f2015-09-15 14:41:29 -050020
Patrick Williamsc124f4f2015-09-15 14:41:29 -050021QA_SANE = "True"
22
23# Elect whether a given type of error is a warning or error, they may
24# have been set by other files.
Andrew Geisslerc182c622020-05-15 14:13:32 -050025WARN_QA ?= " libdir xorg-driver-abi \
26 textrel incompatible-license files-invalid \
27 infodir build-deps src-uri-bad symlink-to-sysroot multilib \
Brad Bishopd89cb5f2019-04-10 09:02:41 -040028 invalid-packageconfig host-user-contaminated uppercase-pn patch-fuzz \
Andrew Geissler5a43b432020-06-13 10:46:56 -050029 mime mime-xdg unlisted-pkg-lics unhandled-features-check \
Andrew Geisslerd1e89492021-02-12 15:35:20 -060030 missing-update-alternatives native-last \
Patrick Williamsc124f4f2015-09-15 14:41:29 -050031 "
32ERROR_QA ?= "dev-so debug-deps dev-deps debug-files arch pkgconfig la \
33 perms dep-cmp pkgvarcheck perm-config perm-line perm-link \
34 split-strip packages-list pkgv-undefined var-undefined \
35 version-going-backwards expanded-d invalid-chars \
Brad Bishop19323692019-04-05 15:28:33 -040036 license-checksum dev-elf file-rdeps configure-unsafe \
Andrew Geissler82c905d2020-04-13 13:39:40 -050037 configure-gettext perllocalpod shebang-size \
Andrew Geisslerc182c622020-05-15 14:13:32 -050038 already-stripped installed-vs-shipped ldflags compile-host-path \
39 install-host-path pn-overrides unknown-configure-option \
40 useless-rpaths rpaths staticdev \
Patrick Williamsc124f4f2015-09-15 14:41:29 -050041 "
Brad Bishopd7bf8c12018-02-25 22:55:05 -050042# Add usrmerge QA check based on distro feature
Patrick Williams213cb262021-08-07 19:21:33 -050043ERROR_QA:append = "${@bb.utils.contains('DISTRO_FEATURES', 'usrmerge', ' usrmerge', '', d)}"
Brad Bishopd7bf8c12018-02-25 22:55:05 -050044
Patrick Williamsc124f4f2015-09-15 14:41:29 -050045FAKEROOT_QA = "host-user-contaminated"
46FAKEROOT_QA[doc] = "QA tests which need to run under fakeroot. If any \
47enabled tests are listed here, the do_package_qa task will run under fakeroot."
48
49ALL_QA = "${WARN_QA} ${ERROR_QA}"
50
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050051UNKNOWN_CONFIGURE_WHITELIST ?= "--enable-nls --disable-nls --disable-silent-rules --disable-dependency-tracking --with-libtool-sysroot --disable-static"
Patrick Williamsc124f4f2015-09-15 14:41:29 -050052
Patrick Williamsc0f7c042017-02-23 20:41:17 -060053def package_qa_clean_path(path, d, pkg=None):
54 """
55 Remove redundant paths from the path for display. If pkg isn't set then
56 TMPDIR is stripped, otherwise PKGDEST/pkg is stripped.
57 """
58 if pkg:
Brad Bishop6e60e8b2018-02-01 10:27:11 -050059 path = path.replace(os.path.join(d.getVar("PKGDEST"), pkg), "/")
60 return path.replace(d.getVar("TMPDIR"), "/").replace("//", "/")
Patrick Williamsc124f4f2015-09-15 14:41:29 -050061
62def package_qa_write_error(type, error, d):
Brad Bishop6e60e8b2018-02-01 10:27:11 -050063 logfile = d.getVar('QA_LOGFILE')
Patrick Williamsc124f4f2015-09-15 14:41:29 -050064 if logfile:
Brad Bishop6e60e8b2018-02-01 10:27:11 -050065 p = d.getVar('P')
Patrick Williamsc0f7c042017-02-23 20:41:17 -060066 with open(logfile, "a+") as f:
67 f.write("%s: %s [%s]\n" % (p, error, type))
Patrick Williamsc124f4f2015-09-15 14:41:29 -050068
69def package_qa_handle_error(error_class, error_msg, d):
Brad Bishop6e60e8b2018-02-01 10:27:11 -050070 if error_class in (d.getVar("ERROR_QA") or "").split():
Brad Bishopd7bf8c12018-02-25 22:55:05 -050071 package_qa_write_error(error_class, error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050072 bb.error("QA Issue: %s [%s]" % (error_msg, error_class))
73 d.setVar("QA_SANE", False)
74 return False
Brad Bishop6e60e8b2018-02-01 10:27:11 -050075 elif error_class in (d.getVar("WARN_QA") or "").split():
Brad Bishopd7bf8c12018-02-25 22:55:05 -050076 package_qa_write_error(error_class, error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050077 bb.warn("QA Issue: %s [%s]" % (error_msg, error_class))
78 else:
79 bb.note("QA Issue: %s [%s]" % (error_msg, error_class))
80 return True
81
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050082def package_qa_add_message(messages, section, new_msg):
83 if section not in messages:
84 messages[section] = new_msg
85 else:
86 messages[section] = messages[section] + "\n" + new_msg
87
Andrew Geissler82c905d2020-04-13 13:39:40 -050088QAPATHTEST[shebang-size] = "package_qa_check_shebang_size"
89def package_qa_check_shebang_size(path, name, d, elf, messages):
Andrew Geisslerd1e89492021-02-12 15:35:20 -060090 import stat
91 if os.path.islink(path) or stat.S_ISFIFO(os.stat(path).st_mode) or elf:
Andrew Geissler82c905d2020-04-13 13:39:40 -050092 return
93
94 try:
95 with open(path, 'rb') as f:
96 stanza = f.readline(130)
97 except IOError:
98 return
99
100 if stanza.startswith(b'#!'):
101 #Shebang not found
102 try:
103 stanza = stanza.decode("utf-8")
104 except UnicodeDecodeError:
105 #If it is not a text file, it is not a script
106 return
107
108 if len(stanza) > 129:
109 package_qa_add_message(messages, "shebang-size", "%s: %s maximum shebang size exceeded, the maximum size is 128." % (name, package_qa_clean_path(path, d)))
110 return
111
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500112QAPATHTEST[libexec] = "package_qa_check_libexec"
113def package_qa_check_libexec(path,name, d, elf, messages):
114
115 # Skip the case where the default is explicitly /usr/libexec
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500116 libexec = d.getVar('libexecdir')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500117 if libexec == "/usr/libexec":
118 return True
119
120 if 'libexec' in path.split(os.path.sep):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500121 package_qa_add_message(messages, "libexec", "%s: %s is using libexec please relocate to %s" % (name, package_qa_clean_path(path, d), libexec))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500122 return False
123
124 return True
125
126QAPATHTEST[rpaths] = "package_qa_check_rpath"
127def package_qa_check_rpath(file,name, d, elf, messages):
128 """
129 Check for dangerous RPATHs
130 """
131 if not elf:
132 return
133
134 if os.path.islink(file):
135 return
136
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500137 bad_dirs = [d.getVar('BASE_WORKDIR'), d.getVar('STAGING_DIR_TARGET')]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500138
139 phdrs = elf.run_objdump("-p", d)
140
141 import re
Brad Bishop977dc1a2019-02-06 16:01:43 -0500142 rpath_re = re.compile(r"\s+RPATH\s+(.*)")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500143 for line in phdrs.split("\n"):
144 m = rpath_re.match(line)
145 if m:
146 rpath = m.group(1)
147 for dir in bad_dirs:
148 if dir in rpath:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500149 package_qa_add_message(messages, "rpaths", "package %s contains bad RPATH %s in file %s" % (name, rpath, file))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500150
151QAPATHTEST[useless-rpaths] = "package_qa_check_useless_rpaths"
152def package_qa_check_useless_rpaths(file, name, d, elf, messages):
153 """
154 Check for RPATHs that are useless but not dangerous
155 """
156 def rpath_eq(a, b):
157 return os.path.normpath(a) == os.path.normpath(b)
158
159 if not elf:
160 return
161
162 if os.path.islink(file):
163 return
164
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500165 libdir = d.getVar("libdir")
166 base_libdir = d.getVar("base_libdir")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500167
168 phdrs = elf.run_objdump("-p", d)
169
170 import re
Brad Bishop977dc1a2019-02-06 16:01:43 -0500171 rpath_re = re.compile(r"\s+RPATH\s+(.*)")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500172 for line in phdrs.split("\n"):
173 m = rpath_re.match(line)
174 if m:
175 rpath = m.group(1)
176 if rpath_eq(rpath, libdir) or rpath_eq(rpath, base_libdir):
177 # The dynamic linker searches both these places anyway. There is no point in
178 # looking there again.
Andrew Geisslerc926e172021-05-07 16:11:35 -0500179 package_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 -0500180
181QAPATHTEST[dev-so] = "package_qa_check_dev"
182def package_qa_check_dev(path, name, d, elf, messages):
183 """
184 Check for ".so" library symlinks in non-dev packages
185 """
186
187 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 Geisslerc926e172021-05-07 16:11:35 -0500188 package_qa_add_message(messages, "dev-so", "non -dev/-dbg/nativesdk- package %s contains symlink .so '%s'" % \
189 (name, package_qa_clean_path(path, d, name)))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500190
191QAPATHTEST[dev-elf] = "package_qa_check_dev_elf"
192def package_qa_check_dev_elf(path, name, d, elf, messages):
193 """
194 Check that -dev doesn't contain real shared libraries. The test has to
195 check that the file is not a link and is an ELF object as some recipes
196 install link-time .so files that are linker scripts.
197 """
198 if name.endswith("-dev") and path.endswith(".so") and not os.path.islink(path) and elf:
Andrew Geisslerc926e172021-05-07 16:11:35 -0500199 package_qa_add_message(messages, "dev-elf", "-dev package %s contains non-symlink .so '%s'" % \
200 (name, package_qa_clean_path(path, d, name)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500201
202QAPATHTEST[staticdev] = "package_qa_check_staticdev"
203def package_qa_check_staticdev(path, name, d, elf, messages):
204 """
205 Check for ".a" library in non-staticdev packages
206 There are a number of exceptions to this rule, -pic packages can contain
207 static libraries, the _nonshared.a belong with their -dev packages and
208 libgcc.a, libgcov.a will be skipped in their packages
209 """
210
Andrew Geissler82c905d2020-04-13 13:39:40 -0500211 if not name.endswith("-pic") and not name.endswith("-staticdev") and not name.endswith("-ptest") and path.endswith(".a") and not path.endswith("_nonshared.a") and not '/usr/lib/debug-static/' in path and not '/.debug-static/' in path:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500212 package_qa_add_message(messages, "staticdev", "non -staticdev package contains static .a library: %s path '%s'" % \
Andrew Geisslerc926e172021-05-07 16:11:35 -0500213 (name, package_qa_clean_path(path,d, name)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500214
Andrew Geissler82c905d2020-04-13 13:39:40 -0500215QAPATHTEST[mime] = "package_qa_check_mime"
216def package_qa_check_mime(path, name, d, elf, messages):
217 """
218 Check if package installs mime types to /usr/share/mime/packages
219 while no inheriting mime.bbclass
220 """
221
222 if d.getVar("datadir") + "/mime/packages" in path and path.endswith('.xml') and not bb.data.inherits_class("mime", d):
223 package_qa_add_message(messages, "mime", "package contains mime types but does not inherit mime: %s path '%s'" % \
224 (name, package_qa_clean_path(path,d)))
225
226QAPATHTEST[mime-xdg] = "package_qa_check_mime_xdg"
227def package_qa_check_mime_xdg(path, name, d, elf, messages):
228 """
229 Check if package installs desktop file containing MimeType and requires
230 mime-types.bbclass to create /usr/share/applications/mimeinfo.cache
231 """
232
233 if d.getVar("datadir") + "/applications" in path and path.endswith('.desktop') and not bb.data.inherits_class("mime-xdg", d):
234 mime_type_found = False
235 try:
236 with open(path, 'r') as f:
237 for line in f.read().split('\n'):
238 if 'MimeType' in line:
239 mime_type_found = True
240 break;
241 except:
242 # At least libreoffice installs symlinks with absolute paths that are dangling here.
243 # We could implement some magic but for few (one) recipes it is not worth the effort so just warn:
244 wstr = "%s cannot open %s - is it a symlink with absolute path?\n" % (name, package_qa_clean_path(path,d))
245 wstr += "Please check if (linked) file contains key 'MimeType'.\n"
246 pkgname = name
247 if name == d.getVar('PN'):
248 pkgname = '${PN}'
Patrick Williams213cb262021-08-07 19:21:33 -0500249 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 Geissler82c905d2020-04-13 13:39:40 -0500250 package_qa_add_message(messages, "mime-xdg", wstr)
251 if mime_type_found:
252 package_qa_add_message(messages, "mime-xdg", "package contains desktop file with key 'MimeType' but does not inhert mime-xdg: %s path '%s'" % \
253 (name, package_qa_clean_path(path,d)))
254
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500255def package_qa_check_libdir(d):
256 """
257 Check for wrong library installation paths. For instance, catch
258 recipes installing /lib/bar.so when ${base_libdir}="lib32" or
259 installing in /usr/lib64 when ${libdir}="/usr/lib"
260 """
261 import re
262
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500263 pkgdest = d.getVar('PKGDEST')
264 base_libdir = d.getVar("base_libdir") + os.sep
265 libdir = d.getVar("libdir") + os.sep
266 libexecdir = d.getVar("libexecdir") + os.sep
267 exec_prefix = d.getVar("exec_prefix") + os.sep
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500268
269 messages = []
270
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500271 # The re's are purposely fuzzy, as some there are some .so.x.y.z files
272 # that don't follow the standard naming convention. It checks later
273 # that they are actual ELF files
Brad Bishop977dc1a2019-02-06 16:01:43 -0500274 lib_re = re.compile(r"^/lib.+\.so(\..+)?$")
275 exec_re = re.compile(r"^%s.*/lib.+\.so(\..+)?$" % exec_prefix)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500276
277 for root, dirs, files in os.walk(pkgdest):
278 if root == pkgdest:
279 # Skip subdirectories for any packages with libdir in INSANE_SKIP
280 skippackages = []
281 for package in dirs:
Patrick Williams213cb262021-08-07 19:21:33 -0500282 if 'libdir' in (d.getVar('INSANE_SKIP:' + package) or "").split():
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500283 bb.note("Package %s skipping libdir QA test" % (package))
284 skippackages.append(package)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500285 elif d.getVar('PACKAGE_DEBUG_SPLIT_STYLE') == 'debug-file-directory' and package.endswith("-dbg"):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500286 bb.note("Package %s skipping libdir QA test for PACKAGE_DEBUG_SPLIT_STYLE equals debug-file-directory" % (package))
287 skippackages.append(package)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500288 for package in skippackages:
289 dirs.remove(package)
290 for file in files:
291 full_path = os.path.join(root, file)
292 rel_path = os.path.relpath(full_path, pkgdest)
293 if os.sep in rel_path:
294 package, rel_path = rel_path.split(os.sep, 1)
295 rel_path = os.sep + rel_path
296 if lib_re.match(rel_path):
297 if base_libdir not in rel_path:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500298 # make sure it's an actual ELF file
299 elf = oe.qa.ELFFile(full_path)
300 try:
301 elf.open()
302 messages.append("%s: found library in wrong location: %s" % (package, rel_path))
303 except (oe.qa.NotELFFileError):
304 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500305 if exec_re.match(rel_path):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500306 if libdir not in rel_path and libexecdir not in rel_path:
307 # make sure it's an actual ELF file
308 elf = oe.qa.ELFFile(full_path)
309 try:
310 elf.open()
311 messages.append("%s: found library in wrong location: %s" % (package, rel_path))
312 except (oe.qa.NotELFFileError):
313 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500314
315 if messages:
316 package_qa_handle_error("libdir", "\n".join(messages), d)
317
318QAPATHTEST[debug-files] = "package_qa_check_dbg"
319def package_qa_check_dbg(path, name, d, elf, messages):
320 """
321 Check for ".debug" files or directories outside of the dbg package
322 """
323
324 if not "-dbg" in name and not "-ptest" in name:
325 if '.debug' in path.split(os.path.sep):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500326 package_qa_add_message(messages, "debug-files", "non debug package contains .debug directory: %s path %s" % \
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500327 (name, package_qa_clean_path(path,d)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500328
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500329QAPATHTEST[arch] = "package_qa_check_arch"
330def package_qa_check_arch(path,name,d, elf, messages):
331 """
332 Check if archs are compatible
333 """
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800334 import re, oe.elf
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600335
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500336 if not elf:
337 return
338
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500339 target_os = d.getVar('TARGET_OS')
340 target_arch = d.getVar('TARGET_ARCH')
341 provides = d.getVar('PROVIDES')
342 bpn = d.getVar('BPN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500343
344 if target_arch == "allarch":
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500345 pn = d.getVar('PN')
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500346 package_qa_add_message(messages, "arch", pn + ": Recipe inherits the allarch class, but has packaged architecture-specific binaries")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500347 return
348
349 # FIXME: Cross package confuse this check, so just skip them
350 for s in ['cross', 'nativesdk', 'cross-canadian']:
351 if bb.data.inherits_class(s, d):
352 return
353
354 # avoid following links to /usr/bin (e.g. on udev builds)
355 # we will check the files pointed to anyway...
356 if os.path.islink(path):
357 return
358
359 #if this will throw an exception, then fix the dict above
360 (machine, osabi, abiversion, littleendian, bits) \
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800361 = oe.elf.machine_dict(d)[target_os][target_arch]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500362
363 # Check the architecture and endiannes of the binary
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600364 is_32 = (("virtual/kernel" in provides) or bb.data.inherits_class("module", d)) and \
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800365 (target_os == "linux-gnux32" or target_os == "linux-muslx32" or \
Brad Bishop977dc1a2019-02-06 16:01:43 -0500366 target_os == "linux-gnu_ilp32" or re.match(r'mips64.*32', d.getVar('DEFAULTTUNE')))
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800367 is_bpf = (oe.qa.elf_machine_to_string(elf.machine()) == "BPF")
368 if not ((machine == elf.machine()) or is_32 or is_bpf):
Andrew Geisslerb7d28612020-07-24 16:15:54 -0500369 package_qa_add_message(messages, "arch", "Architecture did not match (%s, expected %s) in %s" % \
370 (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 -0400371 elif not ((bits == elf.abiSize()) or is_32 or is_bpf):
Andrew Geisslerb7d28612020-07-24 16:15:54 -0500372 package_qa_add_message(messages, "arch", "Bit size did not match (%d, expected %d) in %s" % \
373 (elf.abiSize(), bits, package_qa_clean_path(path, d, name)))
Brad Bishop19323692019-04-05 15:28:33 -0400374 elif not ((littleendian == elf.isLittleEndian()) or is_bpf):
Andrew Geisslerb7d28612020-07-24 16:15:54 -0500375 package_qa_add_message(messages, "arch", "Endiannes did not match (%d, expected %d) in %s" % \
376 (elf.isLittleEndian(), littleendian, package_qa_clean_path(path,d, name)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500377
378QAPATHTEST[desktop] = "package_qa_check_desktop"
379def package_qa_check_desktop(path, name, d, elf, messages):
380 """
381 Run all desktop files through desktop-file-validate.
382 """
383 if path.endswith(".desktop"):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500384 desktop_file_validate = os.path.join(d.getVar('STAGING_BINDIR_NATIVE'),'desktop-file-validate')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500385 output = os.popen("%s %s" % (desktop_file_validate, path))
386 # This only produces output on errors
387 for l in output:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500388 package_qa_add_message(messages, "desktop", "Desktop file issue: " + l.strip())
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500389
390QAPATHTEST[textrel] = "package_qa_textrel"
391def package_qa_textrel(path, name, d, elf, messages):
392 """
393 Check if the binary contains relocations in .text
394 """
395
396 if not elf:
397 return
398
399 if os.path.islink(path):
400 return
401
402 phdrs = elf.run_objdump("-p", d)
403 sane = True
404
405 import re
Brad Bishop977dc1a2019-02-06 16:01:43 -0500406 textrel_re = re.compile(r"\s+TEXTREL\s+")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500407 for line in phdrs.split("\n"):
408 if textrel_re.match(line):
409 sane = False
Brad Bishop1d80a2e2019-11-15 16:35:03 -0500410 break
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500411
412 if not sane:
Brad Bishop1d80a2e2019-11-15 16:35:03 -0500413 path = package_qa_clean_path(path, d, name)
414 package_qa_add_message(messages, "textrel", "%s: ELF binary %s has relocations in .text" % (name, path))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500415
416QAPATHTEST[ldflags] = "package_qa_hash_style"
417def package_qa_hash_style(path, name, d, elf, messages):
418 """
419 Check if the binary has the right hash style...
420 """
421
422 if not elf:
423 return
424
425 if os.path.islink(path):
426 return
427
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500428 gnu_hash = "--hash-style=gnu" in d.getVar('LDFLAGS')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500429 if not gnu_hash:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500430 gnu_hash = "--hash-style=both" in d.getVar('LDFLAGS')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500431 if not gnu_hash:
432 return
433
434 sane = False
435 has_syms = False
436
437 phdrs = elf.run_objdump("-p", d)
438
439 # If this binary has symbols, we expect it to have GNU_HASH too.
440 for line in phdrs.split("\n"):
441 if "SYMTAB" in line:
442 has_syms = True
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500443 if "GNU_HASH" in line or "DT_MIPS_XHASH" in line:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500444 sane = True
Andrew Geissler82c905d2020-04-13 13:39:40 -0500445 if ("[mips32]" in line or "[mips64]" in line) and d.getVar('TCLIBC') == "musl":
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500446 sane = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500447 if has_syms and not sane:
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500448 path = package_qa_clean_path(path, d, name)
449 package_qa_add_message(messages, "ldflags", "File %s in package %s doesn't have GNU_HASH (didn't pass LDFLAGS?)" % (path, name))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500450
451
452QAPATHTEST[buildpaths] = "package_qa_check_buildpaths"
453def package_qa_check_buildpaths(path, name, d, elf, messages):
454 """
455 Check for build paths inside target files and error if not found in the whitelist
456 """
457 # Ignore .debug files, not interesting
458 if path.find(".debug") != -1:
459 return
460
461 # Ignore symlinks
462 if os.path.islink(path):
463 return
464
Brad Bishopd5ae7d92018-06-14 09:52:03 -0700465 tmpdir = bytes(d.getVar('TMPDIR'), encoding="utf-8")
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500466 with open(path, 'rb') as f:
Brad Bishopd5ae7d92018-06-14 09:52:03 -0700467 file_content = f.read()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500468 if tmpdir in file_content:
Brad Bishopf3fd2882019-06-21 08:06:37 -0400469 trimmed = path.replace(os.path.join (d.getVar("PKGDEST"), name), "")
470 package_qa_add_message(messages, "buildpaths", "File %s in package %s contains reference to TMPDIR" % (trimmed, name))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500471
472
473QAPATHTEST[xorg-driver-abi] = "package_qa_check_xorg_driver_abi"
474def package_qa_check_xorg_driver_abi(path, name, d, elf, messages):
475 """
476 Check that all packages containing Xorg drivers have ABI dependencies
477 """
478
479 # Skip dev, dbg or nativesdk packages
480 if name.endswith("-dev") or name.endswith("-dbg") or name.startswith("nativesdk-"):
481 return
482
483 driverdir = d.expand("${libdir}/xorg/modules/drivers/")
484 if driverdir in path and path.endswith(".so"):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500485 mlprefix = d.getVar('MLPREFIX') or ''
Patrick Williams213cb262021-08-07 19:21:33 -0500486 for rdep in bb.utils.explode_deps(d.getVar('RDEPENDS:' + name) or ""):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500487 if rdep.startswith("%sxorg-abi-" % mlprefix):
488 return
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500489 package_qa_add_message(messages, "xorg-driver-abi", "Package %s contains Xorg driver (%s) but no xorg-abi- dependencies" % (name, os.path.basename(path)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500490
491QAPATHTEST[infodir] = "package_qa_check_infodir"
492def package_qa_check_infodir(path, name, d, elf, messages):
493 """
494 Check that /usr/share/info/dir isn't shipped in a particular package
495 """
496 infodir = d.expand("${infodir}/dir")
497
498 if infodir in path:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500499 package_qa_add_message(messages, "infodir", "The /usr/share/info/dir file is not meant to be shipped in a particular package.")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500500
501QAPATHTEST[symlink-to-sysroot] = "package_qa_check_symlink_to_sysroot"
502def package_qa_check_symlink_to_sysroot(path, name, d, elf, messages):
503 """
504 Check that the package doesn't contain any absolute symlinks to the sysroot.
505 """
506 if os.path.islink(path):
507 target = os.readlink(path)
508 if os.path.isabs(target):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500509 tmpdir = d.getVar('TMPDIR')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500510 if target.startswith(tmpdir):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500511 trimmed = path.replace(os.path.join (d.getVar("PKGDEST"), name), "")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500512 package_qa_add_message(messages, "symlink-to-sysroot", "Symlink %s in %s points to TMPDIR" % (trimmed, name))
513
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600514# Check license variables
515do_populate_lic[postfuncs] += "populate_lic_qa_checksum"
516python populate_lic_qa_checksum() {
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500517 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600518 Check for changes in the license files.
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500519 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500520 sane = True
521
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500522 lic_files = d.getVar('LIC_FILES_CHKSUM') or ''
523 lic = d.getVar('LICENSE')
524 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500525
526 if lic == "CLOSED":
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500527 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500528
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500529 if not lic_files and d.getVar('SRC_URI'):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800530 sane &= package_qa_handle_error("license-checksum", pn + ": Recipe file fetches files and does not have license file information (LIC_FILES_CHKSUM)", d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500531
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500532 srcdir = d.getVar('S')
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500533 corebase_licensefile = d.getVar('COREBASE') + "/LICENSE"
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500534 for url in lic_files.split():
535 try:
536 (type, host, path, user, pswd, parm) = bb.fetch.decodeurl(url)
537 except bb.fetch.MalformedUrl:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800538 sane &= package_qa_handle_error("license-checksum", pn + ": LIC_FILES_CHKSUM contains an invalid URL: " + url, d)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500539 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500540 srclicfile = os.path.join(srcdir, path)
541 if not os.path.isfile(srclicfile):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800542 sane &= package_qa_handle_error("license-checksum", pn + ": LIC_FILES_CHKSUM points to an invalid file: " + srclicfile, d)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500543 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500544
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500545 if (srclicfile == corebase_licensefile):
546 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")
547
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500548 recipemd5 = parm.get('md5', '')
549 beginline, endline = 0, 0
550 if 'beginline' in parm:
551 beginline = int(parm['beginline'])
552 if 'endline' in parm:
553 endline = int(parm['endline'])
554
555 if (not beginline) and (not endline):
556 md5chksum = bb.utils.md5_file(srclicfile)
Brad Bishop19323692019-04-05 15:28:33 -0400557 with open(srclicfile, 'r', errors='replace') as f:
558 license = f.read().splitlines()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500559 else:
Brad Bishop19323692019-04-05 15:28:33 -0400560 with open(srclicfile, 'rb') as f:
561 import hashlib
562 lineno = 0
563 license = []
564 m = hashlib.md5()
565 for line in f:
566 lineno += 1
567 if (lineno >= beginline):
568 if ((lineno <= endline) or not endline):
569 m.update(line)
570 license.append(line.decode('utf-8', errors='replace').rstrip())
571 else:
572 break
573 md5chksum = m.hexdigest()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500574 if recipemd5 == md5chksum:
575 bb.note (pn + ": md5 checksum matched for ", url)
576 else:
577 if recipemd5:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500578 msg = pn + ": The LIC_FILES_CHKSUM does not match for " + url
579 msg = msg + "\n" + pn + ": The new md5 checksum is " + md5chksum
Brad Bishop19323692019-04-05 15:28:33 -0400580 max_lines = int(d.getVar('QA_MAX_LICENSE_LINES') or 20)
581 if not license or license[-1] != '':
582 # Ensure that our license text ends with a line break
583 # (will be added with join() below).
584 license.append('')
585 remove = len(license) - max_lines
586 if remove > 0:
587 start = max_lines // 2
588 end = start + remove - 1
589 del license[start:end]
590 license.insert(start, '...')
591 msg = msg + "\n" + pn + ": Here is the selected license text:" + \
592 "\n" + \
593 "{:v^70}".format(" beginline=%d " % beginline if beginline else "") + \
594 "\n" + "\n".join(license) + \
595 "{:^^70}".format(" endline=%d " % endline if endline else "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500596 if beginline:
597 if endline:
598 srcfiledesc = "%s (lines %d through to %d)" % (srclicfile, beginline, endline)
599 else:
600 srcfiledesc = "%s (beginning on line %d)" % (srclicfile, beginline)
601 elif endline:
602 srcfiledesc = "%s (ending on line %d)" % (srclicfile, endline)
603 else:
604 srcfiledesc = srclicfile
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500605 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 -0500606
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500607 else:
608 msg = pn + ": LIC_FILES_CHKSUM is not specified for " + url
609 msg = msg + "\n" + pn + ": The md5 checksum is " + md5chksum
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800610 sane &= package_qa_handle_error("license-checksum", msg, d)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600611
612 if not sane:
613 bb.fatal("Fatal QA errors found, failing task.")
614}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500615
Brad Bishop19323692019-04-05 15:28:33 -0400616def qa_check_staged(path,d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500617 """
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500618 Check staged la and pc files for common problems like references to the work
619 directory.
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500620
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500621 As this is run after every stage we should be able to find the one
622 responsible for the errors easily even if we look at every .pc and .la file.
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500623 """
624
625 sane = True
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500626 tmpdir = d.getVar('TMPDIR')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500627 workdir = os.path.join(tmpdir, "work")
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500628 recipesysroot = d.getVar("RECIPE_SYSROOT")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500629
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500630 if bb.data.inherits_class("native", d) or bb.data.inherits_class("cross", d):
631 pkgconfigcheck = workdir
632 else:
633 pkgconfigcheck = tmpdir
634
Brad Bishop19323692019-04-05 15:28:33 -0400635 skip = (d.getVar('INSANE_SKIP') or "").split()
636 skip_la = False
637 if 'la' in skip:
638 bb.note("Recipe %s skipping qa checking: la" % d.getVar('PN'))
639 skip_la = True
640
641 skip_pkgconfig = False
642 if 'pkgconfig' in skip:
643 bb.note("Recipe %s skipping qa checking: pkgconfig" % d.getVar('PN'))
644 skip_pkgconfig = True
645
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500646 # find all .la and .pc files
647 # read the content
648 # and check for stuff that looks wrong
649 for root, dirs, files in os.walk(path):
650 for file in files:
651 path = os.path.join(root,file)
Brad Bishop19323692019-04-05 15:28:33 -0400652 if file.endswith(".la") and not skip_la:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500653 with open(path) as f:
654 file_content = f.read()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500655 file_content = file_content.replace(recipesysroot, "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500656 if workdir in file_content:
657 error_msg = "%s failed sanity test (workdir) in path %s" % (file,root)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800658 sane &= package_qa_handle_error("la", error_msg, d)
Brad Bishop19323692019-04-05 15:28:33 -0400659 elif file.endswith(".pc") and not skip_pkgconfig:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500660 with open(path) as f:
661 file_content = f.read()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500662 file_content = file_content.replace(recipesysroot, "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500663 if pkgconfigcheck in file_content:
664 error_msg = "%s failed sanity test (tmpdir) in path %s" % (file,root)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800665 sane &= package_qa_handle_error("pkgconfig", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500666
667 return sane
668
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500669# Run all package-wide warnfuncs and errorfuncs
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500670def package_qa_package(warnfuncs, errorfuncs, package, d):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500671 warnings = {}
672 errors = {}
673
674 for func in warnfuncs:
675 func(package, d, warnings)
676 for func in errorfuncs:
677 func(package, d, errors)
678
679 for w in warnings:
680 package_qa_handle_error(w, warnings[w], d)
681 for e in errors:
682 package_qa_handle_error(e, errors[e], d)
683
684 return len(errors) == 0
685
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500686# Run all recipe-wide warnfuncs and errorfuncs
687def package_qa_recipe(warnfuncs, errorfuncs, pn, d):
688 warnings = {}
689 errors = {}
690
691 for func in warnfuncs:
692 func(pn, d, warnings)
693 for func in errorfuncs:
694 func(pn, d, errors)
695
696 for w in warnings:
697 package_qa_handle_error(w, warnings[w], d)
698 for e in errors:
699 package_qa_handle_error(e, errors[e], d)
700
701 return len(errors) == 0
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 import oe.qa
706
707 #if this will throw an exception, then fix the dict above
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500708 target_os = d.getVar('TARGET_OS')
709 target_arch = d.getVar('TARGET_ARCH')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500710
711 warnings = {}
712 errors = {}
713 for path in pkgfiles[package]:
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500714 elf = None
715 if os.path.isfile(path):
716 elf = oe.qa.ELFFile(path)
717 try:
718 elf.open()
719 except oe.qa.NotELFFileError:
720 elf = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500721 for func in warnfuncs:
722 func(path, package, d, elf, warnings)
723 for func in errorfuncs:
724 func(path, package, d, elf, errors)
725
726 for w in warnings:
727 package_qa_handle_error(w, warnings[w], d)
728 for e in errors:
729 package_qa_handle_error(e, errors[e], d)
730
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500731def package_qa_check_rdepends(pkg, pkgdest, skip, taskdeps, packages, d):
732 # Don't do this check for kernel/module recipes, there aren't too many debug/development
733 # packages and you can get false positives e.g. on kernel-module-lirc-dev
734 if bb.data.inherits_class("kernel", d) or bb.data.inherits_class("module-base", d):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500735 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500736
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500737 if not "-dbg" in pkg and not "packagegroup-" in pkg and not "-image" in pkg:
738 localdata = bb.data.createCopy(d)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500739 localdata.setVar('OVERRIDES', localdata.getVar('OVERRIDES') + ':' + pkg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500740
741 # Now check the RDEPENDS
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500742 rdepends = bb.utils.explode_deps(localdata.getVar('RDEPENDS') or "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500743
744 # Now do the sanity check!!!
745 if "build-deps" not in skip:
746 for rdepend in rdepends:
747 if "-dbg" in rdepend and "debug-deps" not in skip:
748 error_msg = "%s rdepends on %s" % (pkg,rdepend)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500749 package_qa_handle_error("debug-deps", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500750 if (not "-dev" in pkg and not "-staticdev" in pkg) and rdepend.endswith("-dev") and "dev-deps" not in skip:
751 error_msg = "%s rdepends on %s" % (pkg, rdepend)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500752 package_qa_handle_error("dev-deps", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500753 if rdepend not in packages:
754 rdep_data = oe.packagedata.read_subpkgdata(rdepend, d)
755 if rdep_data and 'PN' in rdep_data and rdep_data['PN'] in taskdeps:
756 continue
757 if not rdep_data or not 'PN' in rdep_data:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500758 pkgdata_dir = d.getVar("PKGDATA_DIR")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500759 try:
760 possibles = os.listdir("%s/runtime-rprovides/%s/" % (pkgdata_dir, rdepend))
761 except OSError:
762 possibles = []
763 for p in possibles:
764 rdep_data = oe.packagedata.read_subpkgdata(p, d)
765 if rdep_data and 'PN' in rdep_data and rdep_data['PN'] in taskdeps:
766 break
767 if rdep_data and 'PN' in rdep_data and rdep_data['PN'] in taskdeps:
768 continue
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500769 if rdep_data and 'PN' in rdep_data:
770 error_msg = "%s rdepends on %s, but it isn't a build dependency, missing %s in DEPENDS or PACKAGECONFIG?" % (pkg, rdepend, rdep_data['PN'])
771 else:
772 error_msg = "%s rdepends on %s, but it isn't a build dependency?" % (pkg, rdepend)
773 package_qa_handle_error("build-deps", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500774
775 if "file-rdeps" not in skip:
776 ignored_file_rdeps = set(['/bin/sh', '/usr/bin/env', 'rtld(GNU_HASH)'])
777 if bb.data.inherits_class('nativesdk', d):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500778 ignored_file_rdeps |= set(['/bin/bash', '/usr/bin/perl', 'perl'])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500779 # For Saving the FILERDEPENDS
780 filerdepends = {}
781 rdep_data = oe.packagedata.read_subpkgdata(pkg, d)
782 for key in rdep_data:
783 if key.startswith("FILERDEPENDS_"):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500784 for subkey in bb.utils.explode_deps(rdep_data[key]):
785 if subkey not in ignored_file_rdeps and \
786 not subkey.startswith('perl('):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500787 # We already know it starts with FILERDEPENDS_
788 filerdepends[subkey] = key[13:]
789
790 if filerdepends:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500791 done = rdepends[:]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500792 # Add the rprovides of itself
793 if pkg not in done:
794 done.insert(0, pkg)
795
796 # The python is not a package, but python-core provides it, so
797 # skip checking /usr/bin/python if python is in the rdeps, in
Patrick Williams213cb262021-08-07 19:21:33 -0500798 # case there is a RDEPENDS:pkg = "python" in the recipe.
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500799 for py in [ d.getVar('MLPREFIX') + "python", "python" ]:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500800 if py in done:
801 filerdepends.pop("/usr/bin/python",None)
802 done.remove(py)
803 for rdep in done:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500804 # The file dependencies may contain package names, e.g.,
805 # perl
806 filerdepends.pop(rdep,None)
807
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500808 # For Saving the FILERPROVIDES, RPROVIDES and FILES_INFO
809 rdep_data = oe.packagedata.read_subpkgdata(rdep, d)
810 for key in rdep_data:
Patrick Williams213cb262021-08-07 19:21:33 -0500811 if key.startswith("FILERPROVIDES_") or key.startswith("RPROVIDES:"):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500812 for subkey in bb.utils.explode_deps(rdep_data[key]):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500813 filerdepends.pop(subkey,None)
814 # Add the files list to the rprovides
815 if key == "FILES_INFO":
816 # Use eval() to make it as a dict
817 for subkey in eval(rdep_data[key]):
818 filerdepends.pop(subkey,None)
819 if not filerdepends:
820 # Break if all the file rdepends are met
821 break
822 if filerdepends:
823 for key in filerdepends:
Patrick Williams213cb262021-08-07 19:21:33 -0500824 error_msg = "%s contained in package %s requires %s, but no providers found in RDEPENDS:%s?" % \
825 (filerdepends[key].replace(":%s" % pkg, "").replace("@underscore@", "_"), pkg, key, pkg)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500826 package_qa_handle_error("file-rdeps", error_msg, d)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500827package_qa_check_rdepends[vardepsexclude] = "OVERRIDES"
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500828
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500829def package_qa_check_deps(pkg, pkgdest, d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500830
831 localdata = bb.data.createCopy(d)
832 localdata.setVar('OVERRIDES', pkg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500833
834 def check_valid_deps(var):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500835 try:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500836 rvar = bb.utils.explode_dep_versions2(localdata.getVar(var) or "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500837 except ValueError as e:
838 bb.fatal("%s_%s: %s" % (var, pkg, e))
839 for dep in rvar:
840 for v in rvar[dep]:
841 if v and not v.startswith(('< ', '= ', '> ', '<= ', '>=')):
842 error_msg = "%s_%s is invalid: %s (%s) only comparisons <, =, >, <=, and >= are allowed" % (var, pkg, dep, v)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500843 package_qa_handle_error("dep-cmp", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500844
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500845 check_valid_deps('RDEPENDS')
846 check_valid_deps('RRECOMMENDS')
847 check_valid_deps('RSUGGESTS')
848 check_valid_deps('RPROVIDES')
849 check_valid_deps('RREPLACES')
850 check_valid_deps('RCONFLICTS')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500851
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500852QAPKGTEST[usrmerge] = "package_qa_check_usrmerge"
853def package_qa_check_usrmerge(pkg, d, messages):
854 pkgdest = d.getVar('PKGDEST')
855 pkg_dir = pkgdest + os.sep + pkg + os.sep
856 merged_dirs = ['bin', 'sbin', 'lib'] + d.getVar('MULTILIB_VARIANTS').split()
857 for f in merged_dirs:
858 if os.path.exists(pkg_dir + f) and not os.path.islink(pkg_dir + f):
859 msg = "%s package is not obeying usrmerge distro feature. /%s should be relocated to /usr." % (pkg, f)
860 package_qa_add_message(messages, "usrmerge", msg)
861 return False
862 return True
863
Brad Bishopf3f93bb2019-10-16 14:33:32 -0400864QAPKGTEST[perllocalpod] = "package_qa_check_perllocalpod"
865def package_qa_check_perllocalpod(pkg, d, messages):
866 """
867 Check that the recipe didn't ship a perlocal.pod file, which shouldn't be
868 installed in a distribution package. cpan.bbclass sets NO_PERLLOCAL=1 to
869 handle this for most recipes.
870 """
871 import glob
872 pkgd = oe.path.join(d.getVar('PKGDEST'), pkg)
873 podpath = oe.path.join(pkgd, d.getVar("libdir"), "perl*", "*", "*", "perllocal.pod")
874
875 matches = glob.glob(podpath)
876 if matches:
877 matches = [package_qa_clean_path(path, d, pkg) for path in matches]
878 msg = "%s contains perllocal.pod (%s), should not be installed" % (pkg, " ".join(matches))
879 package_qa_add_message(messages, "perllocalpod", msg)
880
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500881QAPKGTEST[expanded-d] = "package_qa_check_expanded_d"
882def package_qa_check_expanded_d(package, d, messages):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500883 """
884 Check for the expanded D (${D}) value in pkg_* and FILES
885 variables, warn the user to use it correctly.
886 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500887 sane = True
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500888 expanded_d = d.getVar('D')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500889
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500890 for var in 'FILES','pkg_preinst', 'pkg_postinst', 'pkg_prerm', 'pkg_postrm':
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500891 bbvar = d.getVar(var + "_" + package) or ""
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500892 if expanded_d in bbvar:
893 if var == 'FILES':
894 package_qa_add_message(messages, "expanded-d", "FILES in %s recipe should not contain the ${D} variable as it references the local build directory not the target filesystem, best solution is to remove the ${D} reference" % package)
895 sane = False
896 else:
897 package_qa_add_message(messages, "expanded-d", "%s in %s recipe contains ${D}, it should be replaced by $D instead" % (var, package))
898 sane = False
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500899 return sane
900
Andrew Geissler1e34c2d2020-05-29 16:02:59 -0500901QAPKGTEST[unlisted-pkg-lics] = "package_qa_check_unlisted_pkg_lics"
902def package_qa_check_unlisted_pkg_lics(package, d, messages):
903 """
904 Check that all licenses for a package are among the licenses for the recipe.
905 """
Patrick Williams213cb262021-08-07 19:21:33 -0500906 pkg_lics = d.getVar('LICENSE:' + package)
Andrew Geissler1e34c2d2020-05-29 16:02:59 -0500907 if not pkg_lics:
908 return True
909
910 recipe_lics_set = oe.license.list_licenses(d.getVar('LICENSE'))
911 unlisted = oe.license.list_licenses(pkg_lics) - recipe_lics_set
912 if not unlisted:
913 return True
914
915 package_qa_add_message(messages, "unlisted-pkg-lics",
Patrick Williams213cb262021-08-07 19:21:33 -0500916 "LICENSE:%s includes licenses (%s) that are not "
Andrew Geissler1e34c2d2020-05-29 16:02:59 -0500917 "listed in LICENSE" % (package, ' '.join(unlisted)))
918 return False
919
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500920def package_qa_check_encoding(keys, encode, d):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600921 def check_encoding(key, enc):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500922 sane = True
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500923 value = d.getVar(key)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500924 if value:
925 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600926 s = value.encode(enc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500927 except UnicodeDecodeError as e:
928 error_msg = "%s has non %s characters" % (key,enc)
929 sane = False
930 package_qa_handle_error("invalid-chars", error_msg, d)
931 return sane
932
933 for key in keys:
934 sane = check_encoding(key, encode)
935 if not sane:
936 break
937
938HOST_USER_UID := "${@os.getuid()}"
939HOST_USER_GID := "${@os.getgid()}"
940
941QAPATHTEST[host-user-contaminated] = "package_qa_check_host_user"
942def package_qa_check_host_user(path, name, d, elf, messages):
943 """Check for paths outside of /home which are owned by the user running bitbake."""
944
945 if not os.path.lexists(path):
946 return
947
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500948 dest = d.getVar('PKGDEST')
949 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500950 home = os.path.join(dest, 'home')
951 if path == home or path.startswith(home + os.sep):
952 return
953
954 try:
955 stat = os.lstat(path)
956 except OSError as exc:
957 import errno
958 if exc.errno != errno.ENOENT:
959 raise
960 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500961 check_uid = int(d.getVar('HOST_USER_UID'))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500962 if stat.st_uid == check_uid:
Brad Bishop96ff1982019-08-19 13:50:42 -0400963 package_qa_add_message(messages, "host-user-contaminated", "%s: %s is owned by uid %d, which is the same as the user running bitbake. This may be due to host contamination" % (pn, package_qa_clean_path(path, d, name), check_uid))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500964 return False
965
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500966 check_gid = int(d.getVar('HOST_USER_GID'))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500967 if stat.st_gid == check_gid:
Brad Bishop96ff1982019-08-19 13:50:42 -0400968 package_qa_add_message(messages, "host-user-contaminated", "%s: %s is owned by gid %d, which is the same as the user running bitbake. This may be due to host contamination" % (pn, package_qa_clean_path(path, d, name), check_gid))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500969 return False
970 return True
971
Brad Bishop15ae2502019-06-18 21:44:24 -0400972QARECIPETEST[src-uri-bad] = "package_qa_check_src_uri"
973def package_qa_check_src_uri(pn, d, messages):
974 import re
975
976 if "${PN}" in d.getVar("SRC_URI", False):
977 package_qa_handle_error("src-uri-bad", "%s: SRC_URI uses PN not BPN" % pn, d)
978
Andrew Geissler82c905d2020-04-13 13:39:40 -0500979 for url in d.getVar("SRC_URI").split():
Andrew Geissler6ce62a22020-11-30 19:58:47 -0600980 if re.search(r"git(hu|la)b\.com/.+/.+/archive/.+", url):
981 package_qa_handle_error("src-uri-bad", "%s: SRC_URI uses unstable GitHub/GitLab archives, convert recipe to use git protocol" % pn, d)
Brad Bishop15ae2502019-06-18 21:44:24 -0400982
Andrew Geissler5a43b432020-06-13 10:46:56 -0500983QARECIPETEST[unhandled-features-check] = "package_qa_check_unhandled_features_check"
984def package_qa_check_unhandled_features_check(pn, d, messages):
985 if not bb.data.inherits_class('features_check', d):
986 var_set = False
987 for kind in ['DISTRO', 'MACHINE', 'COMBINED']:
988 for var in ['ANY_OF_' + kind + '_FEATURES', 'REQUIRED_' + kind + '_FEATURES', 'CONFLICT_' + kind + '_FEATURES']:
989 if d.getVar(var) is not None or d.overridedata.get(var) is not None:
990 var_set = True
991 if var_set:
992 package_qa_handle_error("unhandled-features-check", "%s: recipe doesn't inherit features_check" % pn, d)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500993
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500994QARECIPETEST[missing-update-alternatives] = "package_qa_check_missing_update_alternatives"
995def package_qa_check_missing_update_alternatives(pn, d, messages):
996 # Look at all packages and find out if any of those sets ALTERNATIVE variable
997 # without inheriting update-alternatives class
998 for pkg in (d.getVar('PACKAGES') or '').split():
Patrick Williams213cb262021-08-07 19:21:33 -0500999 if d.getVar('ALTERNATIVE:%s' % pkg) and not bb.data.inherits_class('update-alternatives', d):
1000 package_qa_handle_error("missing-update-alternatives", "%s: recipe defines ALTERNATIVE:%s but doesn't inherit update-alternatives. This might fail during do_rootfs later!" % (pn, pkg), d)
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001001
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001002# The PACKAGE FUNC to scan each package
1003python do_package_qa () {
1004 import subprocess
1005 import oe.packagedata
1006
1007 bb.note("DO PACKAGE QA")
1008
1009 bb.build.exec_func("read_subpackage_metadata", d)
1010
1011 # Check non UTF-8 characters on recipe's metadata
1012 package_qa_check_encoding(['DESCRIPTION', 'SUMMARY', 'LICENSE', 'SECTION'], 'utf-8', d)
1013
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001014 logdir = d.getVar('T')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001015 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001016
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001017 # Scan the packages...
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001018 pkgdest = d.getVar('PKGDEST')
1019 packages = set((d.getVar('PACKAGES') or '').split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001020
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001021 global pkgfiles
1022 pkgfiles = {}
1023 for pkg in packages:
1024 pkgfiles[pkg] = []
Andrew Geisslerb7d28612020-07-24 16:15:54 -05001025 pkgdir = os.path.join(pkgdest, pkg)
1026 for walkroot, dirs, files in os.walk(pkgdir):
1027 # Don't walk into top-level CONTROL or DEBIAN directories as these
1028 # are temporary directories created by do_package.
1029 if walkroot == pkgdir:
1030 for control in ("CONTROL", "DEBIAN"):
1031 if control in dirs:
1032 dirs.remove(control)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001033 for file in files:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001034 pkgfiles[pkg].append(os.path.join(walkroot, file))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001035
1036 # no packages should be scanned
1037 if not packages:
1038 return
1039
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001040 import re
1041 # The package name matches the [a-z0-9.+-]+ regular expression
Brad Bishop977dc1a2019-02-06 16:01:43 -05001042 pkgname_pattern = re.compile(r"^[a-z0-9.+-]+$")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001043
1044 taskdepdata = d.getVar("BB_TASKDEPDATA", False)
1045 taskdeps = set()
1046 for dep in taskdepdata:
1047 taskdeps.add(taskdepdata[dep][0])
1048
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001049 def parse_test_matrix(matrix_name):
1050 testmatrix = d.getVarFlags(matrix_name) or {}
1051 g = globals()
1052 warnchecks = []
1053 for w in (d.getVar("WARN_QA") or "").split():
1054 if w in skip:
1055 continue
1056 if w in testmatrix and testmatrix[w] in g:
1057 warnchecks.append(g[testmatrix[w]])
1058
1059 errorchecks = []
1060 for e in (d.getVar("ERROR_QA") or "").split():
1061 if e in skip:
1062 continue
1063 if e in testmatrix and testmatrix[e] in g:
1064 errorchecks.append(g[testmatrix[e]])
1065 return warnchecks, errorchecks
1066
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001067 for package in packages:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001068 skip = set((d.getVar('INSANE_SKIP') or "").split() +
Patrick Williams213cb262021-08-07 19:21:33 -05001069 (d.getVar('INSANE_SKIP:' + package) or "").split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001070 if skip:
1071 bb.note("Package %s skipping QA tests: %s" % (package, str(skip)))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001072
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001073 bb.note("Checking Package: %s" % package)
1074 # Check package name
1075 if not pkgname_pattern.match(package):
1076 package_qa_handle_error("pkgname",
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001077 "%s doesn't match the [a-z0-9.+-]+ regex" % package, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001078
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001079 warn_checks, error_checks = parse_test_matrix("QAPATHTEST")
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001080 package_qa_walk(warn_checks, error_checks, package, d)
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001081
1082 warn_checks, error_checks = parse_test_matrix("QAPKGTEST")
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001083 package_qa_package(warn_checks, error_checks, package, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001084
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001085 package_qa_check_rdepends(package, pkgdest, skip, taskdeps, packages, d)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001086 package_qa_check_deps(package, pkgdest, d)
1087
1088 warn_checks, error_checks = parse_test_matrix("QARECIPETEST")
1089 package_qa_recipe(warn_checks, error_checks, pn, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001090
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001091 if 'libdir' in d.getVar("ALL_QA").split():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001092 package_qa_check_libdir(d)
1093
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001094 qa_sane = d.getVar("QA_SANE")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001095 if not qa_sane:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001096 bb.fatal("QA run found fatal errors. Please consider fixing them.")
1097 bb.note("DONE with PACKAGE QA")
1098}
1099
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001100# binutils is used for most checks, so need to set as dependency
1101# POPULATESYSROOTDEPS is defined in staging class.
1102do_package_qa[depends] += "${POPULATESYSROOTDEPS}"
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001103do_package_qa[vardepsexclude] = "BB_TASKDEPDATA"
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001104do_package_qa[rdeptask] = "do_packagedata"
1105addtask do_package_qa after do_packagedata do_package before do_build
1106
Brad Bishop19323692019-04-05 15:28:33 -04001107# Add the package specific INSANE_SKIPs to the sstate dependencies
1108python() {
1109 pkgs = (d.getVar('PACKAGES') or '').split()
1110 for pkg in pkgs:
Patrick Williams213cb262021-08-07 19:21:33 -05001111 d.appendVarFlag("do_package_qa", "vardeps", " INSANE_SKIP:{}".format(pkg))
Brad Bishop19323692019-04-05 15:28:33 -04001112}
1113
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001114SSTATETASKS += "do_package_qa"
1115do_package_qa[sstate-inputdirs] = ""
1116do_package_qa[sstate-outputdirs] = ""
1117python do_package_qa_setscene () {
1118 sstate_setscene(d)
1119}
1120addtask do_package_qa_setscene
1121
1122python do_qa_staging() {
1123 bb.note("QA checking staging")
Brad Bishop19323692019-04-05 15:28:33 -04001124 if not qa_check_staged(d.expand('${SYSROOT_DESTDIR}${libdir}'), d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001125 bb.fatal("QA staging was broken by the package built above")
1126}
1127
Brad Bishopd89cb5f2019-04-10 09:02:41 -04001128python do_qa_patch() {
1129 import subprocess
1130
1131 ###########################################################################
1132 # Check patch.log for fuzz warnings
1133 #
1134 # Further information on why we check for patch fuzz warnings:
1135 # http://lists.openembedded.org/pipermail/openembedded-core/2018-March/148675.html
1136 # https://bugzilla.yoctoproject.org/show_bug.cgi?id=10450
1137 ###########################################################################
1138
1139 logdir = d.getVar('T')
1140 patchlog = os.path.join(logdir,"log.do_patch")
1141
1142 if os.path.exists(patchlog):
1143 fuzzheader = '--- Patch fuzz start ---'
1144 fuzzfooter = '--- Patch fuzz end ---'
1145 statement = "grep -e '%s' %s > /dev/null" % (fuzzheader, patchlog)
1146 if subprocess.call(statement, shell=True) == 0:
1147 msg = "Fuzz detected:\n\n"
1148 fuzzmsg = ""
1149 inFuzzInfo = False
1150 f = open(patchlog, "r")
1151 for line in f:
1152 if fuzzheader in line:
1153 inFuzzInfo = True
1154 fuzzmsg = ""
1155 elif fuzzfooter in line:
1156 fuzzmsg = fuzzmsg.replace('\n\n', '\n')
1157 msg += fuzzmsg
1158 msg += "\n"
1159 inFuzzInfo = False
1160 elif inFuzzInfo and not 'Now at patch' in line:
1161 fuzzmsg += line
1162 f.close()
1163 msg += "The context lines in the patches can be updated with devtool:\n"
1164 msg += "\n"
1165 msg += " devtool modify %s\n" % d.getVar('PN')
1166 msg += " devtool finish --force-patch-refresh %s <layer_path>\n\n" % d.getVar('PN')
1167 msg += "Don't forget to review changes done by devtool!\n"
1168 if 'patch-fuzz' in d.getVar('ERROR_QA'):
1169 bb.error(msg)
1170 elif 'patch-fuzz' in d.getVar('WARN_QA'):
1171 bb.warn(msg)
1172 msg = "Patch log indicates that patches do not apply cleanly."
1173 package_qa_handle_error("patch-fuzz", msg, d)
1174}
1175
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001176python do_qa_configure() {
1177 import subprocess
1178
1179 ###########################################################################
1180 # Check config.log for cross compile issues
1181 ###########################################################################
1182
1183 configs = []
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001184 workdir = d.getVar('WORKDIR')
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001185
Brad Bishop19323692019-04-05 15:28:33 -04001186 skip = (d.getVar('INSANE_SKIP') or "").split()
1187 skip_configure_unsafe = False
1188 if 'configure-unsafe' in skip:
1189 bb.note("Recipe %s skipping qa checking: configure-unsafe" % d.getVar('PN'))
1190 skip_configure_unsafe = True
1191
1192 if bb.data.inherits_class('autotools', d) and not skip_configure_unsafe:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001193 bb.note("Checking autotools environment for common misconfiguration")
1194 for root, dirs, files in os.walk(workdir):
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001195 statement = "grep -q -F -e 'is unsafe for cross-compilation' %s" % \
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001196 os.path.join(root,"config.log")
1197 if "config.log" in files:
1198 if subprocess.call(statement, shell=True) == 0:
Brad Bishop19323692019-04-05 15:28:33 -04001199 error_msg = """This autoconf log indicates errors, it looked at host include and/or library paths while determining system capabilities.
1200Rerun configure task after fixing this."""
1201 package_qa_handle_error("configure-unsafe", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001202
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001203 if "configure.ac" in files:
1204 configs.append(os.path.join(root,"configure.ac"))
1205 if "configure.in" in files:
1206 configs.append(os.path.join(root, "configure.in"))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001207
1208 ###########################################################################
1209 # Check gettext configuration and dependencies are correct
1210 ###########################################################################
1211
Brad Bishop19323692019-04-05 15:28:33 -04001212 skip_configure_gettext = False
1213 if 'configure-gettext' in skip:
1214 bb.note("Recipe %s skipping qa checking: configure-gettext" % d.getVar('PN'))
1215 skip_configure_gettext = True
1216
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001217 cnf = d.getVar('EXTRA_OECONF') or ""
Brad Bishop19323692019-04-05 15:28:33 -04001218 if not ("gettext" in d.getVar('P') or "gcc-runtime" in d.getVar('P') or \
1219 "--disable-nls" in cnf or skip_configure_gettext):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001220 ml = d.getVar("MLPREFIX") or ""
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001221 if bb.data.inherits_class('cross-canadian', d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001222 gt = "nativesdk-gettext"
1223 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001224 gt = "gettext-native"
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001225 deps = bb.utils.explode_deps(d.getVar('DEPENDS') or "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001226 if gt not in deps:
1227 for config in configs:
1228 gnu = "grep \"^[[:space:]]*AM_GNU_GETTEXT\" %s >/dev/null" % config
1229 if subprocess.call(gnu, shell=True) == 0:
Brad Bishopd89cb5f2019-04-10 09:02:41 -04001230 error_msg = "AM_GNU_GETTEXT used but no inherit gettext"
Brad Bishop19323692019-04-05 15:28:33 -04001231 package_qa_handle_error("configure-gettext", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001232
1233 ###########################################################################
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001234 # Check unrecognised configure options (with a white list)
1235 ###########################################################################
Brad Bishopc342db32019-05-15 21:57:59 -04001236 if bb.data.inherits_class("autotools", d) or bb.data.inherits_class("meson", d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001237 bb.note("Checking configure output for unrecognised options")
1238 try:
Brad Bishopc342db32019-05-15 21:57:59 -04001239 if bb.data.inherits_class("autotools", d):
1240 flag = "WARNING: unrecognized options:"
1241 log = os.path.join(d.getVar('B'), 'config.log')
1242 if bb.data.inherits_class("meson", d):
1243 flag = "WARNING: Unknown options:"
1244 log = os.path.join(d.getVar('T'), 'log.do_configure')
1245 output = subprocess.check_output(['grep', '-F', flag, log]).decode("utf-8").replace(', ', ' ').replace('"', '')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001246 options = set()
1247 for line in output.splitlines():
1248 options |= set(line.partition(flag)[2].split())
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001249 whitelist = set(d.getVar("UNKNOWN_CONFIGURE_WHITELIST").split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001250 options -= whitelist
1251 if options:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001252 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001253 error_msg = pn + ": configure was passed unrecognised options: " + " ".join(options)
1254 package_qa_handle_error("unknown-configure-option", error_msg, d)
1255 except subprocess.CalledProcessError:
1256 pass
1257
1258 # Check invalid PACKAGECONFIG
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001259 pkgconfig = (d.getVar("PACKAGECONFIG") or "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001260 if pkgconfig:
1261 pkgconfigflags = d.getVarFlags("PACKAGECONFIG") or {}
1262 for pconfig in pkgconfig:
1263 if pconfig not in pkgconfigflags:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001264 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001265 error_msg = "%s: invalid PACKAGECONFIG: %s" % (pn, pconfig)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001266 package_qa_handle_error("invalid-packageconfig", error_msg, d)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001267
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001268 qa_sane = d.getVar("QA_SANE")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001269 if not qa_sane:
1270 bb.fatal("Fatal QA errors found, failing task.")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001271}
1272
1273python do_qa_unpack() {
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001274 src_uri = d.getVar('SRC_URI')
1275 s_dir = d.getVar('S')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001276 if src_uri and not os.path.exists(s_dir):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001277 bb.warn('%s: the directory %s (%s) pointed to by the S variable doesn\'t exist - please set S within the recipe to point to where the source has been unpacked to' % (d.getVar('PN'), d.getVar('S', False), s_dir))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001278}
1279
1280# The Staging Func, to check all staging
1281#addtask qa_staging after do_populate_sysroot before do_build
1282do_populate_sysroot[postfuncs] += "do_qa_staging "
1283
Brad Bishopd89cb5f2019-04-10 09:02:41 -04001284# Check for patch fuzz
1285do_patch[postfuncs] += "do_qa_patch "
1286
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001287# Check broken config.log files, for packages requiring Gettext which
1288# don't have it in DEPENDS.
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001289#addtask qa_configure after do_configure before do_compile
1290do_configure[postfuncs] += "do_qa_configure "
1291
1292# Check does S exist.
1293do_unpack[postfuncs] += "do_qa_unpack"
1294
1295python () {
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001296 import re
1297
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001298 tests = d.getVar('ALL_QA').split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001299 if "desktop" in tests:
1300 d.appendVar("PACKAGE_DEPENDS", " desktop-file-utils-native")
1301
1302 ###########################################################################
1303 # Check various variables
1304 ###########################################################################
1305
1306 # Checking ${FILESEXTRAPATHS}
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001307 extrapaths = (d.getVar("FILESEXTRAPATHS") or "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001308 if '__default' not in extrapaths.split(":"):
Patrick Williams213cb262021-08-07 19:21:33 -05001309 msg = "FILESEXTRAPATHS-variable, must always use :prepend (or :append)\n"
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001310 msg += "type of assignment, and don't forget the colon.\n"
1311 msg += "Please assign it with the format of:\n"
Patrick Williams213cb262021-08-07 19:21:33 -05001312 msg += " FILESEXTRAPATHS:append := \":${THISDIR}/Your_Files_Path\" or\n"
1313 msg += " FILESEXTRAPATHS:prepend := \"${THISDIR}/Your_Files_Path:\"\n"
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001314 msg += "in your bbappend file\n\n"
1315 msg += "Your incorrect assignment is:\n"
1316 msg += "%s\n" % extrapaths
1317 bb.warn(msg)
1318
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001319 overrides = d.getVar('OVERRIDES').split(':')
1320 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001321 if pn in overrides:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001322 msg = 'Recipe %s has PN of "%s" which is in OVERRIDES, this can result in unexpected behaviour.' % (d.getVar("FILE"), pn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001323 package_qa_handle_error("pn-overrides", msg, d)
Brad Bishop977dc1a2019-02-06 16:01:43 -05001324 prog = re.compile(r'[A-Z]')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001325 if prog.search(pn):
1326 package_qa_handle_error("uppercase-pn", 'PN: %s is upper case, this can result in unexpected behavior.' % pn, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001327
Brad Bishop08902b02019-08-20 09:16:51 -04001328 # Some people mistakenly use DEPENDS_${PN} instead of DEPENDS and wonder
1329 # why it doesn't work.
1330 if (d.getVar(d.expand('DEPENDS_${PN}'))):
1331 package_qa_handle_error("pkgvarcheck", "recipe uses DEPENDS_${PN}, should use DEPENDS", d)
1332
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001333 issues = []
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001334 if (d.getVar('PACKAGES') or "").split():
1335 for dep in (d.getVar('QADEPENDS') or "").split():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001336 d.appendVarFlag('do_package_qa', 'depends', " %s:do_populate_sysroot" % dep)
1337 for var in 'RDEPENDS', 'RRECOMMENDS', 'RSUGGESTS', 'RCONFLICTS', 'RPROVIDES', 'RREPLACES', 'FILES', 'pkg_preinst', 'pkg_postinst', 'pkg_prerm', 'pkg_postrm', 'ALLOW_EMPTY':
1338 if d.getVar(var, False):
1339 issues.append(var)
1340
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001341 fakeroot_tests = d.getVar('FAKEROOT_QA').split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001342 if set(tests) & set(fakeroot_tests):
1343 d.setVarFlag('do_package_qa', 'fakeroot', '1')
1344 d.appendVarFlag('do_package_qa', 'depends', ' virtual/fakeroot-native:do_populate_sysroot')
1345 else:
1346 d.setVarFlag('do_package_qa', 'rdeptask', '')
1347 for i in issues:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001348 package_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 -06001349
1350 if 'native-last' not in (d.getVar('INSANE_SKIP') or "").split():
1351 for native_class in ['native', 'nativesdk']:
1352 if bb.data.inherits_class(native_class, d):
1353
1354 inherited_classes = d.getVar('__inherit_cache', False) or []
1355 needle = os.path.join('classes', native_class)
1356
1357 bbclassextend = (d.getVar('BBCLASSEXTEND') or '').split()
1358 # BBCLASSEXTEND items are always added in the end
1359 skip_classes = bbclassextend
1360 if bb.data.inherits_class('native', d) or 'native' in bbclassextend:
1361 # native also inherits nopackages and relocatable bbclasses
1362 skip_classes.extend(['nopackages', 'relocatable'])
1363
1364 broken_order = []
1365 for class_item in reversed(inherited_classes):
1366 if needle not in class_item:
1367 for extend_item in skip_classes:
1368 if os.path.join('classes', '%s.bbclass' % extend_item) in class_item:
1369 break
1370 else:
1371 pn = d.getVar('PN')
1372 broken_order.append(os.path.basename(class_item))
1373 else:
1374 break
1375 if broken_order:
1376 package_qa_handle_error("native-last", "%s: native/nativesdk class is not inherited last, this can result in unexpected behaviour. "
1377 "Classes inherited after native/nativesdk: %s" % (pn, " ".join(broken_order)), d)
1378
1379
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001380 qa_sane = d.getVar("QA_SANE")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001381 if not qa_sane:
1382 bb.fatal("Fatal QA errors found, failing task.")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001383}