blob: 1d73778255a6227c31ba599db291977bc18a95e3 [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
19
20
21# unsafe-references-in-binaries requires prelink-rtld from
22# prelink-native, but we don't want this DEPENDS for -native builds
23QADEPENDS = "prelink-native"
24QADEPENDS_class-native = ""
25QADEPENDS_class-nativesdk = ""
26QA_SANE = "True"
27
28# Elect whether a given type of error is a warning or error, they may
29# have been set by other files.
30WARN_QA ?= "ldflags useless-rpaths rpaths staticdev libdir xorg-driver-abi \
31 textrel already-stripped incompatible-license files-invalid \
32 installed-vs-shipped compile-host-path install-host-path \
33 pn-overrides infodir build-deps file-rdeps \
34 unknown-configure-option symlink-to-sysroot multilib \
Patrick Williamsf1e5d692016-03-30 15:21:19 -050035 invalid-packageconfig host-user-contaminated \
Patrick Williamsc124f4f2015-09-15 14:41:29 -050036 "
37ERROR_QA ?= "dev-so debug-deps dev-deps debug-files arch pkgconfig la \
38 perms dep-cmp pkgvarcheck perm-config perm-line perm-link \
39 split-strip packages-list pkgv-undefined var-undefined \
40 version-going-backwards expanded-d invalid-chars \
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050041 license-checksum dev-elf \
Patrick Williamsc124f4f2015-09-15 14:41:29 -050042 "
43FAKEROOT_QA = "host-user-contaminated"
44FAKEROOT_QA[doc] = "QA tests which need to run under fakeroot. If any \
45enabled tests are listed here, the do_package_qa task will run under fakeroot."
46
47ALL_QA = "${WARN_QA} ${ERROR_QA}"
48
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050049UNKNOWN_CONFIGURE_WHITELIST ?= "--enable-nls --disable-nls --disable-silent-rules --disable-dependency-tracking --with-libtool-sysroot --disable-static"
Patrick Williamsc124f4f2015-09-15 14:41:29 -050050
51#
52# dictionary for elf headers
53#
54# feel free to add and correct.
55#
56# TARGET_OS TARGET_ARCH MACHINE, OSABI, ABIVERSION, Little Endian, 32bit?
Patrick Williamsc0f7c042017-02-23 20:41:17 -060057def package_qa_get_machine_dict(d):
58 machdata = {
Patrick Williamsc124f4f2015-09-15 14:41:29 -050059 "darwin9" : {
60 "arm" : (40, 0, 0, True, 32),
61 },
62 "eabi" : {
63 "arm" : (40, 0, 0, True, 32),
64 },
65 "elf" : {
66 "i586" : (3, 0, 0, True, 32),
67 "x86_64": (62, 0, 0, True, 64),
68 "epiphany": (4643, 0, 0, True, 32),
Patrick Williamsc0f7c042017-02-23 20:41:17 -060069 "mips": ( 8, 0, 0, False, 32),
70 "mipsel": ( 8, 0, 0, True, 32),
Patrick Williamsc124f4f2015-09-15 14:41:29 -050071 },
72 "linux" : {
73 "aarch64" : (183, 0, 0, True, 64),
74 "aarch64_be" :(183, 0, 0, False, 64),
75 "arm" : (40, 97, 0, True, 32),
76 "armeb": (40, 97, 0, False, 32),
77 "powerpc": (20, 0, 0, False, 32),
78 "powerpc64": (21, 0, 0, False, 64),
79 "i386": ( 3, 0, 0, True, 32),
80 "i486": ( 3, 0, 0, True, 32),
81 "i586": ( 3, 0, 0, True, 32),
82 "i686": ( 3, 0, 0, True, 32),
83 "x86_64": (62, 0, 0, True, 64),
84 "ia64": (50, 0, 0, True, 64),
85 "alpha": (36902, 0, 0, True, 64),
86 "hppa": (15, 3, 0, False, 32),
87 "m68k": ( 4, 0, 0, False, 32),
88 "mips": ( 8, 0, 0, False, 32),
89 "mipsel": ( 8, 0, 0, True, 32),
90 "mips64": ( 8, 0, 0, False, 64),
91 "mips64el": ( 8, 0, 0, True, 64),
Patrick Williamsc0f7c042017-02-23 20:41:17 -060092 "mipsisa32r6": ( 8, 0, 0, False, 32),
93 "mipsisa32r6el": ( 8, 0, 0, True, 32),
94 "mipsisa64r6": ( 8, 0, 0, False, 64),
95 "mipsisa64r6el": ( 8, 0, 0, True, 64),
Patrick Williamsf1e5d692016-03-30 15:21:19 -050096 "nios2": (113, 0, 0, True, 32),
Patrick Williamsc124f4f2015-09-15 14:41:29 -050097 "s390": (22, 0, 0, False, 32),
98 "sh4": (42, 0, 0, True, 32),
99 "sparc": ( 2, 0, 0, False, 32),
100 "microblaze": (189, 0, 0, False, 32),
101 "microblazeeb":(189, 0, 0, False, 32),
102 "microblazeel":(189, 0, 0, True, 32),
103 },
104 "linux-uclibc" : {
105 "arm" : ( 40, 97, 0, True, 32),
106 "armeb": ( 40, 97, 0, False, 32),
107 "powerpc": ( 20, 0, 0, False, 32),
108 "i386": ( 3, 0, 0, True, 32),
109 "i486": ( 3, 0, 0, True, 32),
110 "i586": ( 3, 0, 0, True, 32),
111 "i686": ( 3, 0, 0, True, 32),
112 "x86_64": ( 62, 0, 0, True, 64),
113 "mips": ( 8, 0, 0, False, 32),
114 "mipsel": ( 8, 0, 0, True, 32),
115 "mips64": ( 8, 0, 0, False, 64),
116 "mips64el": ( 8, 0, 0, True, 64),
117 "avr32": (6317, 0, 0, False, 32),
118 "sh4": (42, 0, 0, True, 32),
119
120 },
121 "linux-musl" : {
122 "aarch64" : (183, 0, 0, True, 64),
123 "aarch64_be" :(183, 0, 0, False, 64),
124 "arm" : ( 40, 97, 0, True, 32),
125 "armeb": ( 40, 97, 0, False, 32),
126 "powerpc": ( 20, 0, 0, False, 32),
127 "i386": ( 3, 0, 0, True, 32),
128 "i486": ( 3, 0, 0, True, 32),
129 "i586": ( 3, 0, 0, True, 32),
130 "i686": ( 3, 0, 0, True, 32),
131 "x86_64": ( 62, 0, 0, True, 64),
132 "mips": ( 8, 0, 0, False, 32),
133 "mipsel": ( 8, 0, 0, True, 32),
134 "mips64": ( 8, 0, 0, False, 64),
135 "mips64el": ( 8, 0, 0, True, 64),
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500136 "microblaze": (189, 0, 0, False, 32),
137 "microblazeeb":(189, 0, 0, False, 32),
138 "microblazeel":(189, 0, 0, True, 32),
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500139 },
140 "uclinux-uclibc" : {
141 "bfin": ( 106, 0, 0, True, 32),
142 },
143 "linux-gnueabi" : {
144 "arm" : (40, 0, 0, True, 32),
145 "armeb" : (40, 0, 0, False, 32),
146 },
147 "linux-musleabi" : {
148 "arm" : (40, 0, 0, True, 32),
149 "armeb" : (40, 0, 0, False, 32),
150 },
151 "linux-uclibceabi" : {
152 "arm" : (40, 0, 0, True, 32),
153 "armeb" : (40, 0, 0, False, 32),
154 },
155 "linux-gnuspe" : {
156 "powerpc": (20, 0, 0, False, 32),
157 },
158 "linux-muslspe" : {
159 "powerpc": (20, 0, 0, False, 32),
160 },
161 "linux-uclibcspe" : {
162 "powerpc": (20, 0, 0, False, 32),
163 },
164 "linux-gnu" : {
165 "powerpc": (20, 0, 0, False, 32),
166 "sh4": (42, 0, 0, True, 32),
167 },
168 "linux-gnux32" : {
169 "x86_64": (62, 0, 0, True, 32),
170 },
171 "linux-gnun32" : {
172 "mips64": ( 8, 0, 0, False, 32),
173 "mips64el": ( 8, 0, 0, True, 32),
174 },
175 }
176
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600177 # Add in any extra user supplied data which may come from a BSP layer, removing the
178 # need to always change this class directly
179 extra_machdata = (d.getVar("PACKAGEQA_EXTRA_MACHDEFFUNCS", True) or "").split()
180 for m in extra_machdata:
181 call = m + "(machdata, d)"
182 locs = { "machdata" : machdata, "d" : d}
183 machdata = bb.utils.better_eval(call, locs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500184
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600185 return machdata
186
187
188def package_qa_clean_path(path, d, pkg=None):
189 """
190 Remove redundant paths from the path for display. If pkg isn't set then
191 TMPDIR is stripped, otherwise PKGDEST/pkg is stripped.
192 """
193 if pkg:
194 path = path.replace(os.path.join(d.getVar("PKGDEST", True), pkg), "/")
195 return path.replace(d.getVar("TMPDIR", True), "/").replace("//", "/")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500196
197def package_qa_write_error(type, error, d):
198 logfile = d.getVar('QA_LOGFILE', True)
199 if logfile:
200 p = d.getVar('P', True)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600201 with open(logfile, "a+") as f:
202 f.write("%s: %s [%s]\n" % (p, error, type))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500203
204def package_qa_handle_error(error_class, error_msg, d):
205 package_qa_write_error(error_class, error_msg, d)
206 if error_class in (d.getVar("ERROR_QA", True) or "").split():
207 bb.error("QA Issue: %s [%s]" % (error_msg, error_class))
208 d.setVar("QA_SANE", False)
209 return False
210 elif error_class in (d.getVar("WARN_QA", True) or "").split():
211 bb.warn("QA Issue: %s [%s]" % (error_msg, error_class))
212 else:
213 bb.note("QA Issue: %s [%s]" % (error_msg, error_class))
214 return True
215
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500216def package_qa_add_message(messages, section, new_msg):
217 if section not in messages:
218 messages[section] = new_msg
219 else:
220 messages[section] = messages[section] + "\n" + new_msg
221
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500222QAPATHTEST[libexec] = "package_qa_check_libexec"
223def package_qa_check_libexec(path,name, d, elf, messages):
224
225 # Skip the case where the default is explicitly /usr/libexec
226 libexec = d.getVar('libexecdir', True)
227 if libexec == "/usr/libexec":
228 return True
229
230 if 'libexec' in path.split(os.path.sep):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500231 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 -0500232 return False
233
234 return True
235
236QAPATHTEST[rpaths] = "package_qa_check_rpath"
237def package_qa_check_rpath(file,name, d, elf, messages):
238 """
239 Check for dangerous RPATHs
240 """
241 if not elf:
242 return
243
244 if os.path.islink(file):
245 return
246
247 bad_dirs = [d.getVar('BASE_WORKDIR', True), d.getVar('STAGING_DIR_TARGET', True)]
248
249 phdrs = elf.run_objdump("-p", d)
250
251 import re
252 rpath_re = re.compile("\s+RPATH\s+(.*)")
253 for line in phdrs.split("\n"):
254 m = rpath_re.match(line)
255 if m:
256 rpath = m.group(1)
257 for dir in bad_dirs:
258 if dir in rpath:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500259 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 -0500260
261QAPATHTEST[useless-rpaths] = "package_qa_check_useless_rpaths"
262def package_qa_check_useless_rpaths(file, name, d, elf, messages):
263 """
264 Check for RPATHs that are useless but not dangerous
265 """
266 def rpath_eq(a, b):
267 return os.path.normpath(a) == os.path.normpath(b)
268
269 if not elf:
270 return
271
272 if os.path.islink(file):
273 return
274
275 libdir = d.getVar("libdir", True)
276 base_libdir = d.getVar("base_libdir", True)
277
278 phdrs = elf.run_objdump("-p", d)
279
280 import re
281 rpath_re = re.compile("\s+RPATH\s+(.*)")
282 for line in phdrs.split("\n"):
283 m = rpath_re.match(line)
284 if m:
285 rpath = m.group(1)
286 if rpath_eq(rpath, libdir) or rpath_eq(rpath, base_libdir):
287 # The dynamic linker searches both these places anyway. There is no point in
288 # looking there again.
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500289 package_qa_add_message(messages, "useless-rpaths", "%s: %s contains probably-redundant RPATH %s" % (name, package_qa_clean_path(file, d), rpath))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500290
291QAPATHTEST[dev-so] = "package_qa_check_dev"
292def package_qa_check_dev(path, name, d, elf, messages):
293 """
294 Check for ".so" library symlinks in non-dev packages
295 """
296
297 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):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500298 package_qa_add_message(messages, "dev-so", "non -dev/-dbg/nativesdk- package contains symlink .so: %s path '%s'" % \
299 (name, package_qa_clean_path(path,d)))
300
301QAPATHTEST[dev-elf] = "package_qa_check_dev_elf"
302def package_qa_check_dev_elf(path, name, d, elf, messages):
303 """
304 Check that -dev doesn't contain real shared libraries. The test has to
305 check that the file is not a link and is an ELF object as some recipes
306 install link-time .so files that are linker scripts.
307 """
308 if name.endswith("-dev") and path.endswith(".so") and not os.path.islink(path) and elf:
309 package_qa_add_message(messages, "dev-elf", "-dev package contains non-symlink .so: %s path '%s'" % \
310 (name, package_qa_clean_path(path,d)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500311
312QAPATHTEST[staticdev] = "package_qa_check_staticdev"
313def package_qa_check_staticdev(path, name, d, elf, messages):
314 """
315 Check for ".a" library in non-staticdev packages
316 There are a number of exceptions to this rule, -pic packages can contain
317 static libraries, the _nonshared.a belong with their -dev packages and
318 libgcc.a, libgcov.a will be skipped in their packages
319 """
320
321 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"):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500322 package_qa_add_message(messages, "staticdev", "non -staticdev package contains static .a library: %s path '%s'" % \
323 (name, package_qa_clean_path(path,d)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500324
325def package_qa_check_libdir(d):
326 """
327 Check for wrong library installation paths. For instance, catch
328 recipes installing /lib/bar.so when ${base_libdir}="lib32" or
329 installing in /usr/lib64 when ${libdir}="/usr/lib"
330 """
331 import re
332
333 pkgdest = d.getVar('PKGDEST', True)
334 base_libdir = d.getVar("base_libdir",True) + os.sep
335 libdir = d.getVar("libdir", True) + os.sep
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500336 libexecdir = d.getVar("libexecdir", True) + os.sep
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500337 exec_prefix = d.getVar("exec_prefix", True) + os.sep
338
339 messages = []
340
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500341 # The re's are purposely fuzzy, as some there are some .so.x.y.z files
342 # that don't follow the standard naming convention. It checks later
343 # that they are actual ELF files
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500344 lib_re = re.compile("^/lib.+\.so(\..+)?$")
345 exec_re = re.compile("^%s.*/lib.+\.so(\..+)?$" % exec_prefix)
346
347 for root, dirs, files in os.walk(pkgdest):
348 if root == pkgdest:
349 # Skip subdirectories for any packages with libdir in INSANE_SKIP
350 skippackages = []
351 for package in dirs:
352 if 'libdir' in (d.getVar('INSANE_SKIP_' + package, True) or "").split():
353 bb.note("Package %s skipping libdir QA test" % (package))
354 skippackages.append(package)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500355 elif d.getVar('PACKAGE_DEBUG_SPLIT_STYLE', True) == 'debug-file-directory' and package.endswith("-dbg"):
356 bb.note("Package %s skipping libdir QA test for PACKAGE_DEBUG_SPLIT_STYLE equals debug-file-directory" % (package))
357 skippackages.append(package)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500358 for package in skippackages:
359 dirs.remove(package)
360 for file in files:
361 full_path = os.path.join(root, file)
362 rel_path = os.path.relpath(full_path, pkgdest)
363 if os.sep in rel_path:
364 package, rel_path = rel_path.split(os.sep, 1)
365 rel_path = os.sep + rel_path
366 if lib_re.match(rel_path):
367 if base_libdir not in rel_path:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500368 # make sure it's an actual ELF file
369 elf = oe.qa.ELFFile(full_path)
370 try:
371 elf.open()
372 messages.append("%s: found library in wrong location: %s" % (package, rel_path))
373 except (oe.qa.NotELFFileError):
374 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500375 if exec_re.match(rel_path):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500376 if libdir not in rel_path and libexecdir not in rel_path:
377 # make sure it's an actual ELF file
378 elf = oe.qa.ELFFile(full_path)
379 try:
380 elf.open()
381 messages.append("%s: found library in wrong location: %s" % (package, rel_path))
382 except (oe.qa.NotELFFileError):
383 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500384
385 if messages:
386 package_qa_handle_error("libdir", "\n".join(messages), d)
387
388QAPATHTEST[debug-files] = "package_qa_check_dbg"
389def package_qa_check_dbg(path, name, d, elf, messages):
390 """
391 Check for ".debug" files or directories outside of the dbg package
392 """
393
394 if not "-dbg" in name and not "-ptest" in name:
395 if '.debug' in path.split(os.path.sep):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500396 messages("debug-files", "non debug package contains .debug directory: %s path %s" % \
397 (name, package_qa_clean_path(path,d)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500398
399QAPATHTEST[perms] = "package_qa_check_perm"
400def package_qa_check_perm(path,name,d, elf, messages):
401 """
402 Check the permission of files
403 """
404 return
405
406QAPATHTEST[unsafe-references-in-binaries] = "package_qa_check_unsafe_references_in_binaries"
407def package_qa_check_unsafe_references_in_binaries(path, name, d, elf, messages):
408 """
409 Ensure binaries in base_[bindir|sbindir|libdir] do not link to files under exec_prefix
410 """
411 if unsafe_references_skippable(path, name, d):
412 return
413
414 if elf:
415 import subprocess as sub
416 pn = d.getVar('PN', True)
417
418 exec_prefix = d.getVar('exec_prefix', True)
419 sysroot_path = d.getVar('STAGING_DIR_TARGET', True)
420 sysroot_path_usr = sysroot_path + exec_prefix
421
422 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600423 ldd_output = bb.process.Popen(["prelink-rtld", "--root", sysroot_path, path], stdout=sub.PIPE).stdout.read().decode("utf-8")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500424 except bb.process.CmdError:
425 error_msg = pn + ": prelink-rtld aborted when processing %s" % path
426 package_qa_handle_error("unsafe-references-in-binaries", error_msg, d)
427 return False
428
429 if sysroot_path_usr in ldd_output:
430 ldd_output = ldd_output.replace(sysroot_path, "")
431
432 pkgdest = d.getVar('PKGDEST', True)
433 packages = d.getVar('PACKAGES', True)
434
435 for package in packages.split():
436 short_path = path.replace('%s/%s' % (pkgdest, package), "", 1)
437 if (short_path != path):
438 break
439
440 base_err = pn + ": %s, installed in the base_prefix, requires a shared library under exec_prefix (%s)" % (short_path, exec_prefix)
441 for line in ldd_output.split('\n'):
442 if exec_prefix in line:
443 error_msg = "%s: %s" % (base_err, line.strip())
444 package_qa_handle_error("unsafe-references-in-binaries", error_msg, d)
445
446 return False
447
448QAPATHTEST[unsafe-references-in-scripts] = "package_qa_check_unsafe_references_in_scripts"
449def package_qa_check_unsafe_references_in_scripts(path, name, d, elf, messages):
450 """
451 Warn if scripts in base_[bindir|sbindir|libdir] reference files under exec_prefix
452 """
453 if unsafe_references_skippable(path, name, d):
454 return
455
456 if not elf:
457 import stat
458 import subprocess
459 pn = d.getVar('PN', True)
460
461 # Ensure we're checking an executable script
462 statinfo = os.stat(path)
463 if bool(statinfo.st_mode & stat.S_IXUSR):
464 # grep shell scripts for possible references to /exec_prefix/
465 exec_prefix = d.getVar('exec_prefix', True)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500466 statement = "grep -e '%s/[^ :]\{1,\}/[^ :]\{1,\}' %s > /dev/null" % (exec_prefix, path)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500467 if subprocess.call(statement, shell=True) == 0:
468 error_msg = pn + ": Found a reference to %s/ in %s" % (exec_prefix, path)
469 package_qa_handle_error("unsafe-references-in-scripts", error_msg, d)
470 error_msg = "Shell scripts in base_bindir and base_sbindir should not reference anything in exec_prefix"
471 package_qa_handle_error("unsafe-references-in-scripts", error_msg, d)
472
473def unsafe_references_skippable(path, name, d):
474 if bb.data.inherits_class('native', d) or bb.data.inherits_class('nativesdk', d):
475 return True
476
477 if "-dbg" in name or "-dev" in name:
478 return True
479
480 # Other package names to skip:
481 if name.startswith("kernel-module-"):
482 return True
483
484 # Skip symlinks
485 if os.path.islink(path):
486 return True
487
488 # Skip unusual rootfs layouts which make these tests irrelevant
489 exec_prefix = d.getVar('exec_prefix', True)
490 if exec_prefix == "":
491 return True
492
493 pkgdest = d.getVar('PKGDEST', True)
494 pkgdest = pkgdest + "/" + name
495 pkgdest = os.path.abspath(pkgdest)
496 base_bindir = pkgdest + d.getVar('base_bindir', True)
497 base_sbindir = pkgdest + d.getVar('base_sbindir', True)
498 base_libdir = pkgdest + d.getVar('base_libdir', True)
499 bindir = pkgdest + d.getVar('bindir', True)
500 sbindir = pkgdest + d.getVar('sbindir', True)
501 libdir = pkgdest + d.getVar('libdir', True)
502
503 if base_bindir == bindir and base_sbindir == sbindir and base_libdir == libdir:
504 return True
505
506 # Skip files not in base_[bindir|sbindir|libdir]
507 path = os.path.abspath(path)
508 if not (base_bindir in path or base_sbindir in path or base_libdir in path):
509 return True
510
511 return False
512
513QAPATHTEST[arch] = "package_qa_check_arch"
514def package_qa_check_arch(path,name,d, elf, messages):
515 """
516 Check if archs are compatible
517 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600518 import re
519
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500520 if not elf:
521 return
522
523 target_os = d.getVar('TARGET_OS', True)
524 target_arch = d.getVar('TARGET_ARCH', True)
525 provides = d.getVar('PROVIDES', True)
526 bpn = d.getVar('BPN', True)
527
528 if target_arch == "allarch":
529 pn = d.getVar('PN', True)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500530 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 -0500531 return
532
533 # FIXME: Cross package confuse this check, so just skip them
534 for s in ['cross', 'nativesdk', 'cross-canadian']:
535 if bb.data.inherits_class(s, d):
536 return
537
538 # avoid following links to /usr/bin (e.g. on udev builds)
539 # we will check the files pointed to anyway...
540 if os.path.islink(path):
541 return
542
543 #if this will throw an exception, then fix the dict above
544 (machine, osabi, abiversion, littleendian, bits) \
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600545 = package_qa_get_machine_dict(d)[target_os][target_arch]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500546
547 # Check the architecture and endiannes of the binary
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600548 is_32 = (("virtual/kernel" in provides) or bb.data.inherits_class("module", d)) and \
549 (target_os == "linux-gnux32" or re.match('mips64.*32', d.getVar('DEFAULTTUNE', True)))
550 if not ((machine == elf.machine()) or is_32):
551 package_qa_add_message(messages, "arch", "Architecture did not match (%s, expected %s) on %s" % \
552 (oe.qa.elf_machine_to_string(elf.machine()), oe.qa.elf_machine_to_string(machine), package_qa_clean_path(path,d)))
553 elif not ((bits == elf.abiSize()) or is_32):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500554 package_qa_add_message(messages, "arch", "Bit size did not match (%d to %d) %s on %s" % \
555 (bits, elf.abiSize(), bpn, package_qa_clean_path(path,d)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500556 elif not littleendian == elf.isLittleEndian():
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500557 package_qa_add_message(messages, "arch", "Endiannes did not match (%d to %d) on %s" % \
558 (littleendian, elf.isLittleEndian(), package_qa_clean_path(path,d)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500559
560QAPATHTEST[desktop] = "package_qa_check_desktop"
561def package_qa_check_desktop(path, name, d, elf, messages):
562 """
563 Run all desktop files through desktop-file-validate.
564 """
565 if path.endswith(".desktop"):
566 desktop_file_validate = os.path.join(d.getVar('STAGING_BINDIR_NATIVE',True),'desktop-file-validate')
567 output = os.popen("%s %s" % (desktop_file_validate, path))
568 # This only produces output on errors
569 for l in output:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500570 package_qa_add_message(messages, "desktop", "Desktop file issue: " + l.strip())
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500571
572QAPATHTEST[textrel] = "package_qa_textrel"
573def package_qa_textrel(path, name, d, elf, messages):
574 """
575 Check if the binary contains relocations in .text
576 """
577
578 if not elf:
579 return
580
581 if os.path.islink(path):
582 return
583
584 phdrs = elf.run_objdump("-p", d)
585 sane = True
586
587 import re
588 textrel_re = re.compile("\s+TEXTREL\s+")
589 for line in phdrs.split("\n"):
590 if textrel_re.match(line):
591 sane = False
592
593 if not sane:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500594 package_qa_add_message(messages, "textrel", "ELF binary '%s' has relocations in .text" % path)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500595
596QAPATHTEST[ldflags] = "package_qa_hash_style"
597def package_qa_hash_style(path, name, d, elf, messages):
598 """
599 Check if the binary has the right hash style...
600 """
601
602 if not elf:
603 return
604
605 if os.path.islink(path):
606 return
607
608 gnu_hash = "--hash-style=gnu" in d.getVar('LDFLAGS', True)
609 if not gnu_hash:
610 gnu_hash = "--hash-style=both" in d.getVar('LDFLAGS', True)
611 if not gnu_hash:
612 return
613
614 sane = False
615 has_syms = False
616
617 phdrs = elf.run_objdump("-p", d)
618
619 # If this binary has symbols, we expect it to have GNU_HASH too.
620 for line in phdrs.split("\n"):
621 if "SYMTAB" in line:
622 has_syms = True
623 if "GNU_HASH" in line:
624 sane = True
625 if "[mips32]" in line or "[mips64]" in line:
626 sane = True
627
628 if has_syms and not sane:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500629 package_qa_add_message(messages, "ldflags", "No GNU_HASH in the elf binary: '%s'" % path)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500630
631
632QAPATHTEST[buildpaths] = "package_qa_check_buildpaths"
633def package_qa_check_buildpaths(path, name, d, elf, messages):
634 """
635 Check for build paths inside target files and error if not found in the whitelist
636 """
637 # Ignore .debug files, not interesting
638 if path.find(".debug") != -1:
639 return
640
641 # Ignore symlinks
642 if os.path.islink(path):
643 return
644
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500645 # Ignore ipk and deb's CONTROL dir
646 if path.find(name + "/CONTROL/") != -1 or path.find(name + "/DEBIAN/") != -1:
647 return
648
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500649 tmpdir = d.getVar('TMPDIR', True)
650 with open(path) as f:
651 file_content = f.read()
652 if tmpdir in file_content:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500653 package_qa_add_message(messages, "buildpaths", "File %s in package contained reference to tmpdir" % package_qa_clean_path(path,d))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500654
655
656QAPATHTEST[xorg-driver-abi] = "package_qa_check_xorg_driver_abi"
657def package_qa_check_xorg_driver_abi(path, name, d, elf, messages):
658 """
659 Check that all packages containing Xorg drivers have ABI dependencies
660 """
661
662 # Skip dev, dbg or nativesdk packages
663 if name.endswith("-dev") or name.endswith("-dbg") or name.startswith("nativesdk-"):
664 return
665
666 driverdir = d.expand("${libdir}/xorg/modules/drivers/")
667 if driverdir in path and path.endswith(".so"):
668 mlprefix = d.getVar('MLPREFIX', True) or ''
669 for rdep in bb.utils.explode_deps(d.getVar('RDEPENDS_' + name, True) or ""):
670 if rdep.startswith("%sxorg-abi-" % mlprefix):
671 return
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500672 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 -0500673
674QAPATHTEST[infodir] = "package_qa_check_infodir"
675def package_qa_check_infodir(path, name, d, elf, messages):
676 """
677 Check that /usr/share/info/dir isn't shipped in a particular package
678 """
679 infodir = d.expand("${infodir}/dir")
680
681 if infodir in path:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500682 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 -0500683
684QAPATHTEST[symlink-to-sysroot] = "package_qa_check_symlink_to_sysroot"
685def package_qa_check_symlink_to_sysroot(path, name, d, elf, messages):
686 """
687 Check that the package doesn't contain any absolute symlinks to the sysroot.
688 """
689 if os.path.islink(path):
690 target = os.readlink(path)
691 if os.path.isabs(target):
692 tmpdir = d.getVar('TMPDIR', True)
693 if target.startswith(tmpdir):
694 trimmed = path.replace(os.path.join (d.getVar("PKGDEST", True), name), "")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500695 package_qa_add_message(messages, "symlink-to-sysroot", "Symlink %s in %s points to TMPDIR" % (trimmed, name))
696
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600697# Check license variables
698do_populate_lic[postfuncs] += "populate_lic_qa_checksum"
699python populate_lic_qa_checksum() {
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500700 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600701 Check for changes in the license files.
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500702 """
703 import tempfile
704 sane = True
705
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600706 lic_files = d.getVar('LIC_FILES_CHKSUM', True) or ''
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500707 lic = d.getVar('LICENSE', True)
708 pn = d.getVar('PN', True)
709
710 if lic == "CLOSED":
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500711 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500712
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600713 if not lic_files and d.getVar('SRC_URI', True):
714 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 -0500715
716 srcdir = d.getVar('S', True)
717
718 for url in lic_files.split():
719 try:
720 (type, host, path, user, pswd, parm) = bb.fetch.decodeurl(url)
721 except bb.fetch.MalformedUrl:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600722 sane = package_qa_handle_error("license-checksum", pn + ": LIC_FILES_CHKSUM contains an invalid URL: " + url, d)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500723 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500724 srclicfile = os.path.join(srcdir, path)
725 if not os.path.isfile(srclicfile):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500726 package_qa_handle_error("license-checksum", pn + ": LIC_FILES_CHKSUM points to an invalid file: " + srclicfile, d)
727 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500728
729 recipemd5 = parm.get('md5', '')
730 beginline, endline = 0, 0
731 if 'beginline' in parm:
732 beginline = int(parm['beginline'])
733 if 'endline' in parm:
734 endline = int(parm['endline'])
735
736 if (not beginline) and (not endline):
737 md5chksum = bb.utils.md5_file(srclicfile)
738 else:
739 fi = open(srclicfile, 'rb')
740 fo = tempfile.NamedTemporaryFile(mode='wb', prefix='poky.', suffix='.tmp', delete=False)
741 tmplicfile = fo.name;
742 lineno = 0
743 linesout = 0
744 for line in fi:
745 lineno += 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600746 if (lineno >= beginline):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500747 if ((lineno <= endline) or not endline):
748 fo.write(line)
749 linesout += 1
750 else:
751 break
752 fo.flush()
753 fo.close()
754 fi.close()
755 md5chksum = bb.utils.md5_file(tmplicfile)
756 os.unlink(tmplicfile)
757
758 if recipemd5 == md5chksum:
759 bb.note (pn + ": md5 checksum matched for ", url)
760 else:
761 if recipemd5:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500762 msg = pn + ": The LIC_FILES_CHKSUM does not match for " + url
763 msg = msg + "\n" + pn + ": The new md5 checksum is " + md5chksum
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500764 if beginline:
765 if endline:
766 srcfiledesc = "%s (lines %d through to %d)" % (srclicfile, beginline, endline)
767 else:
768 srcfiledesc = "%s (beginning on line %d)" % (srclicfile, beginline)
769 elif endline:
770 srcfiledesc = "%s (ending on line %d)" % (srclicfile, endline)
771 else:
772 srcfiledesc = srclicfile
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500773 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 -0500774
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500775 else:
776 msg = pn + ": LIC_FILES_CHKSUM is not specified for " + url
777 msg = msg + "\n" + pn + ": The md5 checksum is " + md5chksum
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600778 sane = package_qa_handle_error("license-checksum", msg, d)
779
780 if not sane:
781 bb.fatal("Fatal QA errors found, failing task.")
782}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500783
784def package_qa_check_staged(path,d):
785 """
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500786 Check staged la and pc files for common problems like references to the work
787 directory.
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500788
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500789 As this is run after every stage we should be able to find the one
790 responsible for the errors easily even if we look at every .pc and .la file.
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500791 """
792
793 sane = True
794 tmpdir = d.getVar('TMPDIR', True)
795 workdir = os.path.join(tmpdir, "work")
796
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500797 if bb.data.inherits_class("native", d) or bb.data.inherits_class("cross", d):
798 pkgconfigcheck = workdir
799 else:
800 pkgconfigcheck = tmpdir
801
802 # find all .la and .pc files
803 # read the content
804 # and check for stuff that looks wrong
805 for root, dirs, files in os.walk(path):
806 for file in files:
807 path = os.path.join(root,file)
808 if file.endswith(".la"):
809 with open(path) as f:
810 file_content = f.read()
811 if workdir in file_content:
812 error_msg = "%s failed sanity test (workdir) in path %s" % (file,root)
813 sane = package_qa_handle_error("la", error_msg, d)
814 elif file.endswith(".pc"):
815 with open(path) as f:
816 file_content = f.read()
817 if pkgconfigcheck in file_content:
818 error_msg = "%s failed sanity test (tmpdir) in path %s" % (file,root)
819 sane = package_qa_handle_error("pkgconfig", error_msg, d)
820
821 return sane
822
823# Walk over all files in a directory and call func
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500824def package_qa_walk(warnfuncs, errorfuncs, skip, package, d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500825 import oe.qa
826
827 #if this will throw an exception, then fix the dict above
828 target_os = d.getVar('TARGET_OS', True)
829 target_arch = d.getVar('TARGET_ARCH', True)
830
831 warnings = {}
832 errors = {}
833 for path in pkgfiles[package]:
834 elf = oe.qa.ELFFile(path)
835 try:
836 elf.open()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500837 except (IOError, oe.qa.NotELFFileError):
838 # IOError can happen if the packaging control files disappear,
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500839 elf = None
840 for func in warnfuncs:
841 func(path, package, d, elf, warnings)
842 for func in errorfuncs:
843 func(path, package, d, elf, errors)
844
845 for w in warnings:
846 package_qa_handle_error(w, warnings[w], d)
847 for e in errors:
848 package_qa_handle_error(e, errors[e], d)
849
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500850def package_qa_check_rdepends(pkg, pkgdest, skip, taskdeps, packages, d):
851 # Don't do this check for kernel/module recipes, there aren't too many debug/development
852 # packages and you can get false positives e.g. on kernel-module-lirc-dev
853 if bb.data.inherits_class("kernel", d) or bb.data.inherits_class("module-base", d):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500854 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500855
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500856 if not "-dbg" in pkg and not "packagegroup-" in pkg and not "-image" in pkg:
857 localdata = bb.data.createCopy(d)
858 localdata.setVar('OVERRIDES', pkg)
859 bb.data.update_data(localdata)
860
861 # Now check the RDEPENDS
862 rdepends = bb.utils.explode_deps(localdata.getVar('RDEPENDS', True) or "")
863
864 # Now do the sanity check!!!
865 if "build-deps" not in skip:
866 for rdepend in rdepends:
867 if "-dbg" in rdepend and "debug-deps" not in skip:
868 error_msg = "%s rdepends on %s" % (pkg,rdepend)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500869 package_qa_handle_error("debug-deps", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500870 if (not "-dev" in pkg and not "-staticdev" in pkg) and rdepend.endswith("-dev") and "dev-deps" not in skip:
871 error_msg = "%s rdepends on %s" % (pkg, rdepend)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500872 package_qa_handle_error("dev-deps", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500873 if rdepend not in packages:
874 rdep_data = oe.packagedata.read_subpkgdata(rdepend, d)
875 if rdep_data and 'PN' in rdep_data and rdep_data['PN'] in taskdeps:
876 continue
877 if not rdep_data or not 'PN' in rdep_data:
878 pkgdata_dir = d.getVar("PKGDATA_DIR", True)
879 try:
880 possibles = os.listdir("%s/runtime-rprovides/%s/" % (pkgdata_dir, rdepend))
881 except OSError:
882 possibles = []
883 for p in possibles:
884 rdep_data = oe.packagedata.read_subpkgdata(p, d)
885 if rdep_data and 'PN' in rdep_data and rdep_data['PN'] in taskdeps:
886 break
887 if rdep_data and 'PN' in rdep_data and rdep_data['PN'] in taskdeps:
888 continue
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500889 if rdep_data and 'PN' in rdep_data:
890 error_msg = "%s rdepends on %s, but it isn't a build dependency, missing %s in DEPENDS or PACKAGECONFIG?" % (pkg, rdepend, rdep_data['PN'])
891 else:
892 error_msg = "%s rdepends on %s, but it isn't a build dependency?" % (pkg, rdepend)
893 package_qa_handle_error("build-deps", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500894
895 if "file-rdeps" not in skip:
896 ignored_file_rdeps = set(['/bin/sh', '/usr/bin/env', 'rtld(GNU_HASH)'])
897 if bb.data.inherits_class('nativesdk', d):
898 ignored_file_rdeps |= set(['/bin/bash', '/usr/bin/perl'])
899 # For Saving the FILERDEPENDS
900 filerdepends = {}
901 rdep_data = oe.packagedata.read_subpkgdata(pkg, d)
902 for key in rdep_data:
903 if key.startswith("FILERDEPENDS_"):
904 for subkey in rdep_data[key].split():
905 if subkey not in ignored_file_rdeps:
906 # We already know it starts with FILERDEPENDS_
907 filerdepends[subkey] = key[13:]
908
909 if filerdepends:
910 next = rdepends
911 done = rdepends[:]
912 # Find all the rdepends on the dependency chain
913 while next:
914 new = []
915 for rdep in next:
916 rdep_data = oe.packagedata.read_subpkgdata(rdep, d)
917 sub_rdeps = rdep_data.get("RDEPENDS_" + rdep)
918 if not sub_rdeps:
919 continue
920 for sub_rdep in sub_rdeps.split():
921 if sub_rdep in done:
922 continue
923 if not sub_rdep.startswith('(') and \
924 oe.packagedata.has_subpkgdata(sub_rdep, d):
925 # It's a new rdep
926 done.append(sub_rdep)
927 new.append(sub_rdep)
928 next = new
929
930 # Add the rprovides of itself
931 if pkg not in done:
932 done.insert(0, pkg)
933
934 # The python is not a package, but python-core provides it, so
935 # skip checking /usr/bin/python if python is in the rdeps, in
936 # case there is a RDEPENDS_pkg = "python" in the recipe.
937 for py in [ d.getVar('MLPREFIX', True) + "python", "python" ]:
938 if py in done:
939 filerdepends.pop("/usr/bin/python",None)
940 done.remove(py)
941 for rdep in done:
942 # For Saving the FILERPROVIDES, RPROVIDES and FILES_INFO
943 rdep_data = oe.packagedata.read_subpkgdata(rdep, d)
944 for key in rdep_data:
945 if key.startswith("FILERPROVIDES_") or key.startswith("RPROVIDES_"):
946 for subkey in rdep_data[key].split():
947 filerdepends.pop(subkey,None)
948 # Add the files list to the rprovides
949 if key == "FILES_INFO":
950 # Use eval() to make it as a dict
951 for subkey in eval(rdep_data[key]):
952 filerdepends.pop(subkey,None)
953 if not filerdepends:
954 # Break if all the file rdepends are met
955 break
956 if filerdepends:
957 for key in filerdepends:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500958 error_msg = "%s contained in package %s requires %s, but no providers found in RDEPENDS_%s?" % \
959 (filerdepends[key].replace("_%s" % pkg, "").replace("@underscore@", "_"), pkg, key, pkg)
960 package_qa_handle_error("file-rdeps", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500961
962def package_qa_check_deps(pkg, pkgdest, skip, d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500963
964 localdata = bb.data.createCopy(d)
965 localdata.setVar('OVERRIDES', pkg)
966 bb.data.update_data(localdata)
967
968 def check_valid_deps(var):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500969 try:
970 rvar = bb.utils.explode_dep_versions2(localdata.getVar(var, True) or "")
971 except ValueError as e:
972 bb.fatal("%s_%s: %s" % (var, pkg, e))
973 for dep in rvar:
974 for v in rvar[dep]:
975 if v and not v.startswith(('< ', '= ', '> ', '<= ', '>=')):
976 error_msg = "%s_%s is invalid: %s (%s) only comparisons <, =, >, <=, and >= are allowed" % (var, pkg, dep, v)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500977 package_qa_handle_error("dep-cmp", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500978
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500979 check_valid_deps('RDEPENDS')
980 check_valid_deps('RRECOMMENDS')
981 check_valid_deps('RSUGGESTS')
982 check_valid_deps('RPROVIDES')
983 check_valid_deps('RREPLACES')
984 check_valid_deps('RCONFLICTS')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500985
986QAPATHTEST[expanded-d] = "package_qa_check_expanded_d"
987def package_qa_check_expanded_d(path,name,d,elf,messages):
988 """
989 Check for the expanded D (${D}) value in pkg_* and FILES
990 variables, warn the user to use it correctly.
991 """
992
993 sane = True
994 expanded_d = d.getVar('D',True)
995
996 # Get packages for current recipe and iterate
997 packages = d.getVar('PACKAGES', True).split(" ")
998 for pak in packages:
999 # Go through all variables and check if expanded D is found, warn the user accordingly
1000 for var in 'FILES','pkg_preinst', 'pkg_postinst', 'pkg_prerm', 'pkg_postrm':
1001 bbvar = d.getVar(var + "_" + pak, False)
1002 if bbvar:
1003 # Bitbake expands ${D} within bbvar during the previous step, so we check for its expanded value
1004 if expanded_d in bbvar:
1005 if var == 'FILES':
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001006 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" % pak)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001007 sane = False
1008 else:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001009 package_qa_add_message(messages, "expanded-d", "%s in %s recipe contains ${D}, it should be replaced by $D instead" % (var, pak))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001010 sane = False
1011 return sane
1012
1013def package_qa_check_encoding(keys, encode, d):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001014 def check_encoding(key, enc):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001015 sane = True
1016 value = d.getVar(key, True)
1017 if value:
1018 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001019 s = value.encode(enc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001020 except UnicodeDecodeError as e:
1021 error_msg = "%s has non %s characters" % (key,enc)
1022 sane = False
1023 package_qa_handle_error("invalid-chars", error_msg, d)
1024 return sane
1025
1026 for key in keys:
1027 sane = check_encoding(key, encode)
1028 if not sane:
1029 break
1030
1031HOST_USER_UID := "${@os.getuid()}"
1032HOST_USER_GID := "${@os.getgid()}"
1033
1034QAPATHTEST[host-user-contaminated] = "package_qa_check_host_user"
1035def package_qa_check_host_user(path, name, d, elf, messages):
1036 """Check for paths outside of /home which are owned by the user running bitbake."""
1037
1038 if not os.path.lexists(path):
1039 return
1040
1041 dest = d.getVar('PKGDEST', True)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001042 pn = d.getVar('PN', True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001043 home = os.path.join(dest, 'home')
1044 if path == home or path.startswith(home + os.sep):
1045 return
1046
1047 try:
1048 stat = os.lstat(path)
1049 except OSError as exc:
1050 import errno
1051 if exc.errno != errno.ENOENT:
1052 raise
1053 else:
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001054 rootfs_path = path[len(dest):]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001055 check_uid = int(d.getVar('HOST_USER_UID', True))
1056 if stat.st_uid == check_uid:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001057 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, rootfs_path, check_uid))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001058 return False
1059
1060 check_gid = int(d.getVar('HOST_USER_GID', True))
1061 if stat.st_gid == check_gid:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001062 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, rootfs_path, check_gid))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001063 return False
1064 return True
1065
1066# The PACKAGE FUNC to scan each package
1067python do_package_qa () {
1068 import subprocess
1069 import oe.packagedata
1070
1071 bb.note("DO PACKAGE QA")
1072
1073 bb.build.exec_func("read_subpackage_metadata", d)
1074
1075 # Check non UTF-8 characters on recipe's metadata
1076 package_qa_check_encoding(['DESCRIPTION', 'SUMMARY', 'LICENSE', 'SECTION'], 'utf-8', d)
1077
1078 logdir = d.getVar('T', True)
1079 pkg = d.getVar('PN', True)
1080
1081 # Check the compile log for host contamination
1082 compilelog = os.path.join(logdir,"log.do_compile")
1083
1084 if os.path.exists(compilelog):
1085 statement = "grep -e 'CROSS COMPILE Badness:' -e 'is unsafe for cross-compilation' %s > /dev/null" % compilelog
1086 if subprocess.call(statement, shell=True) == 0:
1087 msg = "%s: The compile log indicates that host include and/or library paths were used.\n \
1088 Please check the log '%s' for more information." % (pkg, compilelog)
1089 package_qa_handle_error("compile-host-path", msg, d)
1090
1091 # Check the install log for host contamination
1092 installlog = os.path.join(logdir,"log.do_install")
1093
1094 if os.path.exists(installlog):
1095 statement = "grep -e 'CROSS COMPILE Badness:' -e 'is unsafe for cross-compilation' %s > /dev/null" % installlog
1096 if subprocess.call(statement, shell=True) == 0:
1097 msg = "%s: The install log indicates that host include and/or library paths were used.\n \
1098 Please check the log '%s' for more information." % (pkg, installlog)
1099 package_qa_handle_error("install-host-path", msg, d)
1100
1101 # Scan the packages...
1102 pkgdest = d.getVar('PKGDEST', True)
1103 packages = set((d.getVar('PACKAGES', True) or '').split())
1104
1105 cpath = oe.cachedpath.CachedPath()
1106 global pkgfiles
1107 pkgfiles = {}
1108 for pkg in packages:
1109 pkgfiles[pkg] = []
1110 for walkroot, dirs, files in cpath.walk(pkgdest + "/" + pkg):
1111 for file in files:
1112 pkgfiles[pkg].append(walkroot + os.sep + file)
1113
1114 # no packages should be scanned
1115 if not packages:
1116 return
1117
1118 testmatrix = d.getVarFlags("QAPATHTEST")
1119 import re
1120 # The package name matches the [a-z0-9.+-]+ regular expression
1121 pkgname_pattern = re.compile("^[a-z0-9.+-]+$")
1122
1123 taskdepdata = d.getVar("BB_TASKDEPDATA", False)
1124 taskdeps = set()
1125 for dep in taskdepdata:
1126 taskdeps.add(taskdepdata[dep][0])
1127
1128 g = globals()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001129 for package in packages:
1130 skip = (d.getVar('INSANE_SKIP_' + package, True) or "").split()
1131 if skip:
1132 bb.note("Package %s skipping QA tests: %s" % (package, str(skip)))
1133 warnchecks = []
1134 for w in (d.getVar("WARN_QA", True) or "").split():
1135 if w in skip:
1136 continue
1137 if w in testmatrix and testmatrix[w] in g:
1138 warnchecks.append(g[testmatrix[w]])
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001139 if w == 'unsafe-references-in-binaries':
1140 oe.utils.write_ld_so_conf(d)
1141
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001142 errorchecks = []
1143 for e in (d.getVar("ERROR_QA", True) or "").split():
1144 if e in skip:
1145 continue
1146 if e in testmatrix and testmatrix[e] in g:
1147 errorchecks.append(g[testmatrix[e]])
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001148 if e == 'unsafe-references-in-binaries':
1149 oe.utils.write_ld_so_conf(d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001150
1151 bb.note("Checking Package: %s" % package)
1152 # Check package name
1153 if not pkgname_pattern.match(package):
1154 package_qa_handle_error("pkgname",
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001155 "%s doesn't match the [a-z0-9.+-]+ regex" % package, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001156
1157 path = "%s/%s" % (pkgdest, package)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001158 package_qa_walk(warnchecks, errorchecks, skip, package, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001159
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001160 package_qa_check_rdepends(package, pkgdest, skip, taskdeps, packages, d)
1161 package_qa_check_deps(package, pkgdest, skip, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001162
1163 if 'libdir' in d.getVar("ALL_QA", True).split():
1164 package_qa_check_libdir(d)
1165
1166 qa_sane = d.getVar("QA_SANE", True)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001167 if not qa_sane:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001168 bb.fatal("QA run found fatal errors. Please consider fixing them.")
1169 bb.note("DONE with PACKAGE QA")
1170}
1171
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001172do_package_qa[vardepsexclude] = "BB_TASKDEPDATA"
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001173do_package_qa[rdeptask] = "do_packagedata"
1174addtask do_package_qa after do_packagedata do_package before do_build
1175
1176SSTATETASKS += "do_package_qa"
1177do_package_qa[sstate-inputdirs] = ""
1178do_package_qa[sstate-outputdirs] = ""
1179python do_package_qa_setscene () {
1180 sstate_setscene(d)
1181}
1182addtask do_package_qa_setscene
1183
1184python do_qa_staging() {
1185 bb.note("QA checking staging")
1186
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001187 if not package_qa_check_staged(d.expand('${SYSROOT_DESTDIR}${libdir}'), d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001188 bb.fatal("QA staging was broken by the package built above")
1189}
1190
1191python do_qa_configure() {
1192 import subprocess
1193
1194 ###########################################################################
1195 # Check config.log for cross compile issues
1196 ###########################################################################
1197
1198 configs = []
1199 workdir = d.getVar('WORKDIR', True)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001200
1201 if bb.data.inherits_class('autotools', d):
1202 bb.note("Checking autotools environment for common misconfiguration")
1203 for root, dirs, files in os.walk(workdir):
1204 statement = "grep -q -F -e 'CROSS COMPILE Badness:' -e 'is unsafe for cross-compilation' %s" % \
1205 os.path.join(root,"config.log")
1206 if "config.log" in files:
1207 if subprocess.call(statement, shell=True) == 0:
1208 bb.fatal("""This autoconf log indicates errors, it looked at host include and/or library paths while determining system capabilities.
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001209Rerun configure task after fixing this.""")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001210
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001211 if "configure.ac" in files:
1212 configs.append(os.path.join(root,"configure.ac"))
1213 if "configure.in" in files:
1214 configs.append(os.path.join(root, "configure.in"))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001215
1216 ###########################################################################
1217 # Check gettext configuration and dependencies are correct
1218 ###########################################################################
1219
1220 cnf = d.getVar('EXTRA_OECONF', True) or ""
1221 if "gettext" not in d.getVar('P', True) and "gcc-runtime" not in d.getVar('P', True) and "--disable-nls" not in cnf:
1222 ml = d.getVar("MLPREFIX", True) or ""
1223 if bb.data.inherits_class('native', d) or bb.data.inherits_class('cross', d) or bb.data.inherits_class('crosssdk', d) or bb.data.inherits_class('nativesdk', d):
1224 gt = "gettext-native"
1225 elif bb.data.inherits_class('cross-canadian', d):
1226 gt = "nativesdk-gettext"
1227 else:
1228 gt = "virtual/" + ml + "gettext"
1229 deps = bb.utils.explode_deps(d.getVar('DEPENDS', True) or "")
1230 if gt not in deps:
1231 for config in configs:
1232 gnu = "grep \"^[[:space:]]*AM_GNU_GETTEXT\" %s >/dev/null" % config
1233 if subprocess.call(gnu, shell=True) == 0:
1234 bb.fatal("""%s required but not in DEPENDS for file %s.
1235Missing inherit gettext?""" % (gt, config))
1236
1237 ###########################################################################
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001238 # Check unrecognised configure options (with a white list)
1239 ###########################################################################
1240 if bb.data.inherits_class("autotools", d):
1241 bb.note("Checking configure output for unrecognised options")
1242 try:
1243 flag = "WARNING: unrecognized options:"
1244 log = os.path.join(d.getVar('B', True), 'config.log')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001245 output = subprocess.check_output(['grep', '-F', flag, log]).decode("utf-8").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())
1249 whitelist = set(d.getVar("UNKNOWN_CONFIGURE_WHITELIST", True).split())
1250 options -= whitelist
1251 if options:
1252 pn = d.getVar('PN', True)
1253 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
1259 pkgconfig = (d.getVar("PACKAGECONFIG", True) or "").split()
1260 if pkgconfig:
1261 pkgconfigflags = d.getVarFlags("PACKAGECONFIG") or {}
1262 for pconfig in pkgconfig:
1263 if pconfig not in pkgconfigflags:
1264 pn = d.getVar('PN', True)
1265 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
1268 qa_sane = d.getVar("QA_SANE", True)
1269 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() {
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001274 src_uri = d.getVar('SRC_URI', True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001275 s_dir = d.getVar('S', True)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001276 if src_uri and not os.path.exists(s_dir):
Patrick Williamsc124f4f2015-09-15 14:41:29 -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', True), d.getVar('S', False), s_dir))
1278}
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
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001284# Check broken config.log files, for packages requiring Gettext which
1285# don't have it in DEPENDS.
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001286#addtask qa_configure after do_configure before do_compile
1287do_configure[postfuncs] += "do_qa_configure "
1288
1289# Check does S exist.
1290do_unpack[postfuncs] += "do_qa_unpack"
1291
1292python () {
1293 tests = d.getVar('ALL_QA', True).split()
1294 if "desktop" in tests:
1295 d.appendVar("PACKAGE_DEPENDS", " desktop-file-utils-native")
1296
1297 ###########################################################################
1298 # Check various variables
1299 ###########################################################################
1300
1301 # Checking ${FILESEXTRAPATHS}
1302 extrapaths = (d.getVar("FILESEXTRAPATHS", True) or "")
1303 if '__default' not in extrapaths.split(":"):
1304 msg = "FILESEXTRAPATHS-variable, must always use _prepend (or _append)\n"
1305 msg += "type of assignment, and don't forget the colon.\n"
1306 msg += "Please assign it with the format of:\n"
1307 msg += " FILESEXTRAPATHS_append := \":${THISDIR}/Your_Files_Path\" or\n"
1308 msg += " FILESEXTRAPATHS_prepend := \"${THISDIR}/Your_Files_Path:\"\n"
1309 msg += "in your bbappend file\n\n"
1310 msg += "Your incorrect assignment is:\n"
1311 msg += "%s\n" % extrapaths
1312 bb.warn(msg)
1313
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001314 overrides = d.getVar('OVERRIDES', True).split(':')
1315 pn = d.getVar('PN', True)
1316 if pn in overrides:
1317 msg = 'Recipe %s has PN of "%s" which is in OVERRIDES, this can result in unexpected behaviour.' % (d.getVar("FILE", True), pn)
1318 package_qa_handle_error("pn-overrides", msg, d)
1319
1320 issues = []
1321 if (d.getVar('PACKAGES', True) or "").split():
1322 for dep in (d.getVar('QADEPENDS', True) or "").split():
1323 d.appendVarFlag('do_package_qa', 'depends', " %s:do_populate_sysroot" % dep)
1324 for var in 'RDEPENDS', 'RRECOMMENDS', 'RSUGGESTS', 'RCONFLICTS', 'RPROVIDES', 'RREPLACES', 'FILES', 'pkg_preinst', 'pkg_postinst', 'pkg_prerm', 'pkg_postrm', 'ALLOW_EMPTY':
1325 if d.getVar(var, False):
1326 issues.append(var)
1327
1328 fakeroot_tests = d.getVar('FAKEROOT_QA', True).split()
1329 if set(tests) & set(fakeroot_tests):
1330 d.setVarFlag('do_package_qa', 'fakeroot', '1')
1331 d.appendVarFlag('do_package_qa', 'depends', ' virtual/fakeroot-native:do_populate_sysroot')
1332 else:
1333 d.setVarFlag('do_package_qa', 'rdeptask', '')
1334 for i in issues:
1335 package_qa_handle_error("pkgvarcheck", "%s: Variable %s is set as not being package specific, please fix this." % (d.getVar("FILE", True), i), d)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001336 qa_sane = d.getVar("QA_SANE", True)
1337 if not qa_sane:
1338 bb.fatal("Fatal QA errors found, failing task.")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001339}