blob: c57b21735d0c2c6f9afa355f1d18de3ebad7b769 [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?
57def package_qa_get_machine_dict():
58 return {
59 "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),
69 },
70 "linux" : {
71 "aarch64" : (183, 0, 0, True, 64),
72 "aarch64_be" :(183, 0, 0, False, 64),
73 "arm" : (40, 97, 0, True, 32),
74 "armeb": (40, 97, 0, False, 32),
75 "powerpc": (20, 0, 0, False, 32),
76 "powerpc64": (21, 0, 0, False, 64),
77 "i386": ( 3, 0, 0, True, 32),
78 "i486": ( 3, 0, 0, True, 32),
79 "i586": ( 3, 0, 0, True, 32),
80 "i686": ( 3, 0, 0, True, 32),
81 "x86_64": (62, 0, 0, True, 64),
82 "ia64": (50, 0, 0, True, 64),
83 "alpha": (36902, 0, 0, True, 64),
84 "hppa": (15, 3, 0, False, 32),
85 "m68k": ( 4, 0, 0, False, 32),
86 "mips": ( 8, 0, 0, False, 32),
87 "mipsel": ( 8, 0, 0, True, 32),
88 "mips64": ( 8, 0, 0, False, 64),
89 "mips64el": ( 8, 0, 0, True, 64),
Patrick Williamsf1e5d692016-03-30 15:21:19 -050090 "nios2": (113, 0, 0, True, 32),
Patrick Williamsc124f4f2015-09-15 14:41:29 -050091 "s390": (22, 0, 0, False, 32),
92 "sh4": (42, 0, 0, True, 32),
93 "sparc": ( 2, 0, 0, False, 32),
94 "microblaze": (189, 0, 0, False, 32),
95 "microblazeeb":(189, 0, 0, False, 32),
96 "microblazeel":(189, 0, 0, True, 32),
97 },
98 "linux-uclibc" : {
99 "arm" : ( 40, 97, 0, True, 32),
100 "armeb": ( 40, 97, 0, False, 32),
101 "powerpc": ( 20, 0, 0, False, 32),
102 "i386": ( 3, 0, 0, True, 32),
103 "i486": ( 3, 0, 0, True, 32),
104 "i586": ( 3, 0, 0, True, 32),
105 "i686": ( 3, 0, 0, True, 32),
106 "x86_64": ( 62, 0, 0, True, 64),
107 "mips": ( 8, 0, 0, False, 32),
108 "mipsel": ( 8, 0, 0, True, 32),
109 "mips64": ( 8, 0, 0, False, 64),
110 "mips64el": ( 8, 0, 0, True, 64),
111 "avr32": (6317, 0, 0, False, 32),
112 "sh4": (42, 0, 0, True, 32),
113
114 },
115 "linux-musl" : {
116 "aarch64" : (183, 0, 0, True, 64),
117 "aarch64_be" :(183, 0, 0, False, 64),
118 "arm" : ( 40, 97, 0, True, 32),
119 "armeb": ( 40, 97, 0, False, 32),
120 "powerpc": ( 20, 0, 0, False, 32),
121 "i386": ( 3, 0, 0, True, 32),
122 "i486": ( 3, 0, 0, True, 32),
123 "i586": ( 3, 0, 0, True, 32),
124 "i686": ( 3, 0, 0, True, 32),
125 "x86_64": ( 62, 0, 0, True, 64),
126 "mips": ( 8, 0, 0, False, 32),
127 "mipsel": ( 8, 0, 0, True, 32),
128 "mips64": ( 8, 0, 0, False, 64),
129 "mips64el": ( 8, 0, 0, True, 64),
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500130 "microblaze": (189, 0, 0, False, 32),
131 "microblazeeb":(189, 0, 0, False, 32),
132 "microblazeel":(189, 0, 0, True, 32),
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500133 },
134 "uclinux-uclibc" : {
135 "bfin": ( 106, 0, 0, True, 32),
136 },
137 "linux-gnueabi" : {
138 "arm" : (40, 0, 0, True, 32),
139 "armeb" : (40, 0, 0, False, 32),
140 },
141 "linux-musleabi" : {
142 "arm" : (40, 0, 0, True, 32),
143 "armeb" : (40, 0, 0, False, 32),
144 },
145 "linux-uclibceabi" : {
146 "arm" : (40, 0, 0, True, 32),
147 "armeb" : (40, 0, 0, False, 32),
148 },
149 "linux-gnuspe" : {
150 "powerpc": (20, 0, 0, False, 32),
151 },
152 "linux-muslspe" : {
153 "powerpc": (20, 0, 0, False, 32),
154 },
155 "linux-uclibcspe" : {
156 "powerpc": (20, 0, 0, False, 32),
157 },
158 "linux-gnu" : {
159 "powerpc": (20, 0, 0, False, 32),
160 "sh4": (42, 0, 0, True, 32),
161 },
162 "linux-gnux32" : {
163 "x86_64": (62, 0, 0, True, 32),
164 },
165 "linux-gnun32" : {
166 "mips64": ( 8, 0, 0, False, 32),
167 "mips64el": ( 8, 0, 0, True, 32),
168 },
169 }
170
171
172def package_qa_clean_path(path,d):
173 """ Remove the common prefix from the path. In this case it is the TMPDIR"""
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500174 return path.replace(d.getVar("TMPDIR", True) + "/", "")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500175
176def package_qa_write_error(type, error, d):
177 logfile = d.getVar('QA_LOGFILE', True)
178 if logfile:
179 p = d.getVar('P', True)
180 f = file( logfile, "a+")
181 print >> f, "%s: %s [%s]" % (p, error, type)
182 f.close()
183
184def package_qa_handle_error(error_class, error_msg, d):
185 package_qa_write_error(error_class, error_msg, d)
186 if error_class in (d.getVar("ERROR_QA", True) or "").split():
187 bb.error("QA Issue: %s [%s]" % (error_msg, error_class))
188 d.setVar("QA_SANE", False)
189 return False
190 elif error_class in (d.getVar("WARN_QA", True) or "").split():
191 bb.warn("QA Issue: %s [%s]" % (error_msg, error_class))
192 else:
193 bb.note("QA Issue: %s [%s]" % (error_msg, error_class))
194 return True
195
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500196def package_qa_add_message(messages, section, new_msg):
197 if section not in messages:
198 messages[section] = new_msg
199 else:
200 messages[section] = messages[section] + "\n" + new_msg
201
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500202QAPATHTEST[libexec] = "package_qa_check_libexec"
203def package_qa_check_libexec(path,name, d, elf, messages):
204
205 # Skip the case where the default is explicitly /usr/libexec
206 libexec = d.getVar('libexecdir', True)
207 if libexec == "/usr/libexec":
208 return True
209
210 if 'libexec' in path.split(os.path.sep):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500211 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 -0500212 return False
213
214 return True
215
216QAPATHTEST[rpaths] = "package_qa_check_rpath"
217def package_qa_check_rpath(file,name, d, elf, messages):
218 """
219 Check for dangerous RPATHs
220 """
221 if not elf:
222 return
223
224 if os.path.islink(file):
225 return
226
227 bad_dirs = [d.getVar('BASE_WORKDIR', True), d.getVar('STAGING_DIR_TARGET', True)]
228
229 phdrs = elf.run_objdump("-p", d)
230
231 import re
232 rpath_re = re.compile("\s+RPATH\s+(.*)")
233 for line in phdrs.split("\n"):
234 m = rpath_re.match(line)
235 if m:
236 rpath = m.group(1)
237 for dir in bad_dirs:
238 if dir in rpath:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500239 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 -0500240
241QAPATHTEST[useless-rpaths] = "package_qa_check_useless_rpaths"
242def package_qa_check_useless_rpaths(file, name, d, elf, messages):
243 """
244 Check for RPATHs that are useless but not dangerous
245 """
246 def rpath_eq(a, b):
247 return os.path.normpath(a) == os.path.normpath(b)
248
249 if not elf:
250 return
251
252 if os.path.islink(file):
253 return
254
255 libdir = d.getVar("libdir", True)
256 base_libdir = d.getVar("base_libdir", True)
257
258 phdrs = elf.run_objdump("-p", d)
259
260 import re
261 rpath_re = re.compile("\s+RPATH\s+(.*)")
262 for line in phdrs.split("\n"):
263 m = rpath_re.match(line)
264 if m:
265 rpath = m.group(1)
266 if rpath_eq(rpath, libdir) or rpath_eq(rpath, base_libdir):
267 # The dynamic linker searches both these places anyway. There is no point in
268 # looking there again.
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500269 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 -0500270
271QAPATHTEST[dev-so] = "package_qa_check_dev"
272def package_qa_check_dev(path, name, d, elf, messages):
273 """
274 Check for ".so" library symlinks in non-dev packages
275 """
276
277 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 -0500278 package_qa_add_message(messages, "dev-so", "non -dev/-dbg/nativesdk- package contains symlink .so: %s path '%s'" % \
279 (name, package_qa_clean_path(path,d)))
280
281QAPATHTEST[dev-elf] = "package_qa_check_dev_elf"
282def package_qa_check_dev_elf(path, name, d, elf, messages):
283 """
284 Check that -dev doesn't contain real shared libraries. The test has to
285 check that the file is not a link and is an ELF object as some recipes
286 install link-time .so files that are linker scripts.
287 """
288 if name.endswith("-dev") and path.endswith(".so") and not os.path.islink(path) and elf:
289 package_qa_add_message(messages, "dev-elf", "-dev package contains non-symlink .so: %s path '%s'" % \
290 (name, package_qa_clean_path(path,d)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500291
292QAPATHTEST[staticdev] = "package_qa_check_staticdev"
293def package_qa_check_staticdev(path, name, d, elf, messages):
294 """
295 Check for ".a" library in non-staticdev packages
296 There are a number of exceptions to this rule, -pic packages can contain
297 static libraries, the _nonshared.a belong with their -dev packages and
298 libgcc.a, libgcov.a will be skipped in their packages
299 """
300
301 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 -0500302 package_qa_add_message(messages, "staticdev", "non -staticdev package contains static .a library: %s path '%s'" % \
303 (name, package_qa_clean_path(path,d)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500304
305def package_qa_check_libdir(d):
306 """
307 Check for wrong library installation paths. For instance, catch
308 recipes installing /lib/bar.so when ${base_libdir}="lib32" or
309 installing in /usr/lib64 when ${libdir}="/usr/lib"
310 """
311 import re
312
313 pkgdest = d.getVar('PKGDEST', True)
314 base_libdir = d.getVar("base_libdir",True) + os.sep
315 libdir = d.getVar("libdir", True) + os.sep
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500316 libexecdir = d.getVar("libexecdir", True) + os.sep
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500317 exec_prefix = d.getVar("exec_prefix", True) + os.sep
318
319 messages = []
320
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500321 # The re's are purposely fuzzy, as some there are some .so.x.y.z files
322 # that don't follow the standard naming convention. It checks later
323 # that they are actual ELF files
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500324 lib_re = re.compile("^/lib.+\.so(\..+)?$")
325 exec_re = re.compile("^%s.*/lib.+\.so(\..+)?$" % exec_prefix)
326
327 for root, dirs, files in os.walk(pkgdest):
328 if root == pkgdest:
329 # Skip subdirectories for any packages with libdir in INSANE_SKIP
330 skippackages = []
331 for package in dirs:
332 if 'libdir' in (d.getVar('INSANE_SKIP_' + package, True) or "").split():
333 bb.note("Package %s skipping libdir QA test" % (package))
334 skippackages.append(package)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500335 elif d.getVar('PACKAGE_DEBUG_SPLIT_STYLE', True) == 'debug-file-directory' and package.endswith("-dbg"):
336 bb.note("Package %s skipping libdir QA test for PACKAGE_DEBUG_SPLIT_STYLE equals debug-file-directory" % (package))
337 skippackages.append(package)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500338 for package in skippackages:
339 dirs.remove(package)
340 for file in files:
341 full_path = os.path.join(root, file)
342 rel_path = os.path.relpath(full_path, pkgdest)
343 if os.sep in rel_path:
344 package, rel_path = rel_path.split(os.sep, 1)
345 rel_path = os.sep + rel_path
346 if lib_re.match(rel_path):
347 if base_libdir not in rel_path:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500348 # make sure it's an actual ELF file
349 elf = oe.qa.ELFFile(full_path)
350 try:
351 elf.open()
352 messages.append("%s: found library in wrong location: %s" % (package, rel_path))
353 except (oe.qa.NotELFFileError):
354 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500355 if exec_re.match(rel_path):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500356 if libdir not in rel_path and libexecdir not in rel_path:
357 # make sure it's an actual ELF file
358 elf = oe.qa.ELFFile(full_path)
359 try:
360 elf.open()
361 messages.append("%s: found library in wrong location: %s" % (package, rel_path))
362 except (oe.qa.NotELFFileError):
363 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500364
365 if messages:
366 package_qa_handle_error("libdir", "\n".join(messages), d)
367
368QAPATHTEST[debug-files] = "package_qa_check_dbg"
369def package_qa_check_dbg(path, name, d, elf, messages):
370 """
371 Check for ".debug" files or directories outside of the dbg package
372 """
373
374 if not "-dbg" in name and not "-ptest" in name:
375 if '.debug' in path.split(os.path.sep):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500376 messages("debug-files", "non debug package contains .debug directory: %s path %s" % \
377 (name, package_qa_clean_path(path,d)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500378
379QAPATHTEST[perms] = "package_qa_check_perm"
380def package_qa_check_perm(path,name,d, elf, messages):
381 """
382 Check the permission of files
383 """
384 return
385
386QAPATHTEST[unsafe-references-in-binaries] = "package_qa_check_unsafe_references_in_binaries"
387def package_qa_check_unsafe_references_in_binaries(path, name, d, elf, messages):
388 """
389 Ensure binaries in base_[bindir|sbindir|libdir] do not link to files under exec_prefix
390 """
391 if unsafe_references_skippable(path, name, d):
392 return
393
394 if elf:
395 import subprocess as sub
396 pn = d.getVar('PN', True)
397
398 exec_prefix = d.getVar('exec_prefix', True)
399 sysroot_path = d.getVar('STAGING_DIR_TARGET', True)
400 sysroot_path_usr = sysroot_path + exec_prefix
401
402 try:
403 ldd_output = bb.process.Popen(["prelink-rtld", "--root", sysroot_path, path], stdout=sub.PIPE).stdout.read()
404 except bb.process.CmdError:
405 error_msg = pn + ": prelink-rtld aborted when processing %s" % path
406 package_qa_handle_error("unsafe-references-in-binaries", error_msg, d)
407 return False
408
409 if sysroot_path_usr in ldd_output:
410 ldd_output = ldd_output.replace(sysroot_path, "")
411
412 pkgdest = d.getVar('PKGDEST', True)
413 packages = d.getVar('PACKAGES', True)
414
415 for package in packages.split():
416 short_path = path.replace('%s/%s' % (pkgdest, package), "", 1)
417 if (short_path != path):
418 break
419
420 base_err = pn + ": %s, installed in the base_prefix, requires a shared library under exec_prefix (%s)" % (short_path, exec_prefix)
421 for line in ldd_output.split('\n'):
422 if exec_prefix in line:
423 error_msg = "%s: %s" % (base_err, line.strip())
424 package_qa_handle_error("unsafe-references-in-binaries", error_msg, d)
425
426 return False
427
428QAPATHTEST[unsafe-references-in-scripts] = "package_qa_check_unsafe_references_in_scripts"
429def package_qa_check_unsafe_references_in_scripts(path, name, d, elf, messages):
430 """
431 Warn if scripts in base_[bindir|sbindir|libdir] reference files under exec_prefix
432 """
433 if unsafe_references_skippable(path, name, d):
434 return
435
436 if not elf:
437 import stat
438 import subprocess
439 pn = d.getVar('PN', True)
440
441 # Ensure we're checking an executable script
442 statinfo = os.stat(path)
443 if bool(statinfo.st_mode & stat.S_IXUSR):
444 # grep shell scripts for possible references to /exec_prefix/
445 exec_prefix = d.getVar('exec_prefix', True)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500446 statement = "grep -e '%s/[^ :]\{1,\}/[^ :]\{1,\}' %s > /dev/null" % (exec_prefix, path)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500447 if subprocess.call(statement, shell=True) == 0:
448 error_msg = pn + ": Found a reference to %s/ in %s" % (exec_prefix, path)
449 package_qa_handle_error("unsafe-references-in-scripts", error_msg, d)
450 error_msg = "Shell scripts in base_bindir and base_sbindir should not reference anything in exec_prefix"
451 package_qa_handle_error("unsafe-references-in-scripts", error_msg, d)
452
453def unsafe_references_skippable(path, name, d):
454 if bb.data.inherits_class('native', d) or bb.data.inherits_class('nativesdk', d):
455 return True
456
457 if "-dbg" in name or "-dev" in name:
458 return True
459
460 # Other package names to skip:
461 if name.startswith("kernel-module-"):
462 return True
463
464 # Skip symlinks
465 if os.path.islink(path):
466 return True
467
468 # Skip unusual rootfs layouts which make these tests irrelevant
469 exec_prefix = d.getVar('exec_prefix', True)
470 if exec_prefix == "":
471 return True
472
473 pkgdest = d.getVar('PKGDEST', True)
474 pkgdest = pkgdest + "/" + name
475 pkgdest = os.path.abspath(pkgdest)
476 base_bindir = pkgdest + d.getVar('base_bindir', True)
477 base_sbindir = pkgdest + d.getVar('base_sbindir', True)
478 base_libdir = pkgdest + d.getVar('base_libdir', True)
479 bindir = pkgdest + d.getVar('bindir', True)
480 sbindir = pkgdest + d.getVar('sbindir', True)
481 libdir = pkgdest + d.getVar('libdir', True)
482
483 if base_bindir == bindir and base_sbindir == sbindir and base_libdir == libdir:
484 return True
485
486 # Skip files not in base_[bindir|sbindir|libdir]
487 path = os.path.abspath(path)
488 if not (base_bindir in path or base_sbindir in path or base_libdir in path):
489 return True
490
491 return False
492
493QAPATHTEST[arch] = "package_qa_check_arch"
494def package_qa_check_arch(path,name,d, elf, messages):
495 """
496 Check if archs are compatible
497 """
498 if not elf:
499 return
500
501 target_os = d.getVar('TARGET_OS', True)
502 target_arch = d.getVar('TARGET_ARCH', True)
503 provides = d.getVar('PROVIDES', True)
504 bpn = d.getVar('BPN', True)
505
506 if target_arch == "allarch":
507 pn = d.getVar('PN', True)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500508 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 -0500509 return
510
511 # FIXME: Cross package confuse this check, so just skip them
512 for s in ['cross', 'nativesdk', 'cross-canadian']:
513 if bb.data.inherits_class(s, d):
514 return
515
516 # avoid following links to /usr/bin (e.g. on udev builds)
517 # we will check the files pointed to anyway...
518 if os.path.islink(path):
519 return
520
521 #if this will throw an exception, then fix the dict above
522 (machine, osabi, abiversion, littleendian, bits) \
523 = package_qa_get_machine_dict()[target_os][target_arch]
524
525 # Check the architecture and endiannes of the binary
526 if not ((machine == elf.machine()) or \
527 ((("virtual/kernel" in provides) or bb.data.inherits_class("module", d) ) and (target_os == "linux-gnux32" or target_os == "linux-gnun32"))):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500528 package_qa_add_message(messages, "arch", "Architecture did not match (%d to %d) on %s" % \
529 (machine, elf.machine(), package_qa_clean_path(path,d)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500530 elif not ((bits == elf.abiSize()) or \
531 ((("virtual/kernel" in provides) or bb.data.inherits_class("module", d) ) and (target_os == "linux-gnux32" or target_os == "linux-gnun32"))):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500532 package_qa_add_message(messages, "arch", "Bit size did not match (%d to %d) %s on %s" % \
533 (bits, elf.abiSize(), bpn, package_qa_clean_path(path,d)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500534 elif not littleendian == elf.isLittleEndian():
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500535 package_qa_add_message(messages, "arch", "Endiannes did not match (%d to %d) on %s" % \
536 (littleendian, elf.isLittleEndian(), package_qa_clean_path(path,d)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500537
538QAPATHTEST[desktop] = "package_qa_check_desktop"
539def package_qa_check_desktop(path, name, d, elf, messages):
540 """
541 Run all desktop files through desktop-file-validate.
542 """
543 if path.endswith(".desktop"):
544 desktop_file_validate = os.path.join(d.getVar('STAGING_BINDIR_NATIVE',True),'desktop-file-validate')
545 output = os.popen("%s %s" % (desktop_file_validate, path))
546 # This only produces output on errors
547 for l in output:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500548 package_qa_add_message(messages, "desktop", "Desktop file issue: " + l.strip())
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500549
550QAPATHTEST[textrel] = "package_qa_textrel"
551def package_qa_textrel(path, name, d, elf, messages):
552 """
553 Check if the binary contains relocations in .text
554 """
555
556 if not elf:
557 return
558
559 if os.path.islink(path):
560 return
561
562 phdrs = elf.run_objdump("-p", d)
563 sane = True
564
565 import re
566 textrel_re = re.compile("\s+TEXTREL\s+")
567 for line in phdrs.split("\n"):
568 if textrel_re.match(line):
569 sane = False
570
571 if not sane:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500572 package_qa_add_message(messages, "textrel", "ELF binary '%s' has relocations in .text" % path)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500573
574QAPATHTEST[ldflags] = "package_qa_hash_style"
575def package_qa_hash_style(path, name, d, elf, messages):
576 """
577 Check if the binary has the right hash style...
578 """
579
580 if not elf:
581 return
582
583 if os.path.islink(path):
584 return
585
586 gnu_hash = "--hash-style=gnu" in d.getVar('LDFLAGS', True)
587 if not gnu_hash:
588 gnu_hash = "--hash-style=both" in d.getVar('LDFLAGS', True)
589 if not gnu_hash:
590 return
591
592 sane = False
593 has_syms = False
594
595 phdrs = elf.run_objdump("-p", d)
596
597 # If this binary has symbols, we expect it to have GNU_HASH too.
598 for line in phdrs.split("\n"):
599 if "SYMTAB" in line:
600 has_syms = True
601 if "GNU_HASH" in line:
602 sane = True
603 if "[mips32]" in line or "[mips64]" in line:
604 sane = True
605
606 if has_syms and not sane:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500607 package_qa_add_message(messages, "ldflags", "No GNU_HASH in the elf binary: '%s'" % path)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500608
609
610QAPATHTEST[buildpaths] = "package_qa_check_buildpaths"
611def package_qa_check_buildpaths(path, name, d, elf, messages):
612 """
613 Check for build paths inside target files and error if not found in the whitelist
614 """
615 # Ignore .debug files, not interesting
616 if path.find(".debug") != -1:
617 return
618
619 # Ignore symlinks
620 if os.path.islink(path):
621 return
622
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500623 # Ignore ipk and deb's CONTROL dir
624 if path.find(name + "/CONTROL/") != -1 or path.find(name + "/DEBIAN/") != -1:
625 return
626
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500627 tmpdir = d.getVar('TMPDIR', True)
628 with open(path) as f:
629 file_content = f.read()
630 if tmpdir in file_content:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500631 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 -0500632
633
634QAPATHTEST[xorg-driver-abi] = "package_qa_check_xorg_driver_abi"
635def package_qa_check_xorg_driver_abi(path, name, d, elf, messages):
636 """
637 Check that all packages containing Xorg drivers have ABI dependencies
638 """
639
640 # Skip dev, dbg or nativesdk packages
641 if name.endswith("-dev") or name.endswith("-dbg") or name.startswith("nativesdk-"):
642 return
643
644 driverdir = d.expand("${libdir}/xorg/modules/drivers/")
645 if driverdir in path and path.endswith(".so"):
646 mlprefix = d.getVar('MLPREFIX', True) or ''
647 for rdep in bb.utils.explode_deps(d.getVar('RDEPENDS_' + name, True) or ""):
648 if rdep.startswith("%sxorg-abi-" % mlprefix):
649 return
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500650 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 -0500651
652QAPATHTEST[infodir] = "package_qa_check_infodir"
653def package_qa_check_infodir(path, name, d, elf, messages):
654 """
655 Check that /usr/share/info/dir isn't shipped in a particular package
656 """
657 infodir = d.expand("${infodir}/dir")
658
659 if infodir in path:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500660 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 -0500661
662QAPATHTEST[symlink-to-sysroot] = "package_qa_check_symlink_to_sysroot"
663def package_qa_check_symlink_to_sysroot(path, name, d, elf, messages):
664 """
665 Check that the package doesn't contain any absolute symlinks to the sysroot.
666 """
667 if os.path.islink(path):
668 target = os.readlink(path)
669 if os.path.isabs(target):
670 tmpdir = d.getVar('TMPDIR', True)
671 if target.startswith(tmpdir):
672 trimmed = path.replace(os.path.join (d.getVar("PKGDEST", True), name), "")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500673 package_qa_add_message(messages, "symlink-to-sysroot", "Symlink %s in %s points to TMPDIR" % (trimmed, name))
674
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500675def package_qa_check_license(workdir, d):
676 """
677 Check for changes in the license files
678 """
679 import tempfile
680 sane = True
681
682 lic_files = d.getVar('LIC_FILES_CHKSUM', True)
683 lic = d.getVar('LICENSE', True)
684 pn = d.getVar('PN', True)
685
686 if lic == "CLOSED":
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500687 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500688
689 if not lic_files:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500690 package_qa_handle_error("license-checksum", pn + ": Recipe file does not have license file information (LIC_FILES_CHKSUM)", d)
691 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500692
693 srcdir = d.getVar('S', True)
694
695 for url in lic_files.split():
696 try:
697 (type, host, path, user, pswd, parm) = bb.fetch.decodeurl(url)
698 except bb.fetch.MalformedUrl:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500699 package_qa_handle_error("license-checksum", pn + ": LIC_FILES_CHKSUM contains an invalid URL: " + url, d)
700 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500701 srclicfile = os.path.join(srcdir, path)
702 if not os.path.isfile(srclicfile):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500703 package_qa_handle_error("license-checksum", pn + ": LIC_FILES_CHKSUM points to an invalid file: " + srclicfile, d)
704 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500705
706 recipemd5 = parm.get('md5', '')
707 beginline, endline = 0, 0
708 if 'beginline' in parm:
709 beginline = int(parm['beginline'])
710 if 'endline' in parm:
711 endline = int(parm['endline'])
712
713 if (not beginline) and (not endline):
714 md5chksum = bb.utils.md5_file(srclicfile)
715 else:
716 fi = open(srclicfile, 'rb')
717 fo = tempfile.NamedTemporaryFile(mode='wb', prefix='poky.', suffix='.tmp', delete=False)
718 tmplicfile = fo.name;
719 lineno = 0
720 linesout = 0
721 for line in fi:
722 lineno += 1
723 if (lineno >= beginline):
724 if ((lineno <= endline) or not endline):
725 fo.write(line)
726 linesout += 1
727 else:
728 break
729 fo.flush()
730 fo.close()
731 fi.close()
732 md5chksum = bb.utils.md5_file(tmplicfile)
733 os.unlink(tmplicfile)
734
735 if recipemd5 == md5chksum:
736 bb.note (pn + ": md5 checksum matched for ", url)
737 else:
738 if recipemd5:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500739 msg = pn + ": The LIC_FILES_CHKSUM does not match for " + url
740 msg = msg + "\n" + pn + ": The new md5 checksum is " + md5chksum
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500741 if beginline:
742 if endline:
743 srcfiledesc = "%s (lines %d through to %d)" % (srclicfile, beginline, endline)
744 else:
745 srcfiledesc = "%s (beginning on line %d)" % (srclicfile, beginline)
746 elif endline:
747 srcfiledesc = "%s (ending on line %d)" % (srclicfile, endline)
748 else:
749 srcfiledesc = srclicfile
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500750 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 -0500751
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500752 else:
753 msg = pn + ": LIC_FILES_CHKSUM is not specified for " + url
754 msg = msg + "\n" + pn + ": The md5 checksum is " + md5chksum
755 package_qa_handle_error("license-checksum", msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500756
757def package_qa_check_staged(path,d):
758 """
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500759 Check staged la and pc files for common problems like references to the work
760 directory.
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500761
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500762 As this is run after every stage we should be able to find the one
763 responsible for the errors easily even if we look at every .pc and .la file.
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500764 """
765
766 sane = True
767 tmpdir = d.getVar('TMPDIR', True)
768 workdir = os.path.join(tmpdir, "work")
769
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500770 if bb.data.inherits_class("native", d) or bb.data.inherits_class("cross", d):
771 pkgconfigcheck = workdir
772 else:
773 pkgconfigcheck = tmpdir
774
775 # find all .la and .pc files
776 # read the content
777 # and check for stuff that looks wrong
778 for root, dirs, files in os.walk(path):
779 for file in files:
780 path = os.path.join(root,file)
781 if file.endswith(".la"):
782 with open(path) as f:
783 file_content = f.read()
784 if workdir in file_content:
785 error_msg = "%s failed sanity test (workdir) in path %s" % (file,root)
786 sane = package_qa_handle_error("la", error_msg, d)
787 elif file.endswith(".pc"):
788 with open(path) as f:
789 file_content = f.read()
790 if pkgconfigcheck in file_content:
791 error_msg = "%s failed sanity test (tmpdir) in path %s" % (file,root)
792 sane = package_qa_handle_error("pkgconfig", error_msg, d)
793
794 return sane
795
796# Walk over all files in a directory and call func
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500797def package_qa_walk(warnfuncs, errorfuncs, skip, package, d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500798 import oe.qa
799
800 #if this will throw an exception, then fix the dict above
801 target_os = d.getVar('TARGET_OS', True)
802 target_arch = d.getVar('TARGET_ARCH', True)
803
804 warnings = {}
805 errors = {}
806 for path in pkgfiles[package]:
807 elf = oe.qa.ELFFile(path)
808 try:
809 elf.open()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500810 except (IOError, oe.qa.NotELFFileError):
811 # IOError can happen if the packaging control files disappear,
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500812 elf = None
813 for func in warnfuncs:
814 func(path, package, d, elf, warnings)
815 for func in errorfuncs:
816 func(path, package, d, elf, errors)
817
818 for w in warnings:
819 package_qa_handle_error(w, warnings[w], d)
820 for e in errors:
821 package_qa_handle_error(e, errors[e], d)
822
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500823def package_qa_check_rdepends(pkg, pkgdest, skip, taskdeps, packages, d):
824 # Don't do this check for kernel/module recipes, there aren't too many debug/development
825 # packages and you can get false positives e.g. on kernel-module-lirc-dev
826 if bb.data.inherits_class("kernel", d) or bb.data.inherits_class("module-base", d):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500827 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500828
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500829 if not "-dbg" in pkg and not "packagegroup-" in pkg and not "-image" in pkg:
830 localdata = bb.data.createCopy(d)
831 localdata.setVar('OVERRIDES', pkg)
832 bb.data.update_data(localdata)
833
834 # Now check the RDEPENDS
835 rdepends = bb.utils.explode_deps(localdata.getVar('RDEPENDS', True) or "")
836
837 # Now do the sanity check!!!
838 if "build-deps" not in skip:
839 for rdepend in rdepends:
840 if "-dbg" in rdepend and "debug-deps" not in skip:
841 error_msg = "%s rdepends on %s" % (pkg,rdepend)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500842 package_qa_handle_error("debug-deps", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500843 if (not "-dev" in pkg and not "-staticdev" in pkg) and rdepend.endswith("-dev") and "dev-deps" not in skip:
844 error_msg = "%s rdepends on %s" % (pkg, rdepend)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500845 package_qa_handle_error("dev-deps", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500846 if rdepend not in packages:
847 rdep_data = oe.packagedata.read_subpkgdata(rdepend, d)
848 if rdep_data and 'PN' in rdep_data and rdep_data['PN'] in taskdeps:
849 continue
850 if not rdep_data or not 'PN' in rdep_data:
851 pkgdata_dir = d.getVar("PKGDATA_DIR", True)
852 try:
853 possibles = os.listdir("%s/runtime-rprovides/%s/" % (pkgdata_dir, rdepend))
854 except OSError:
855 possibles = []
856 for p in possibles:
857 rdep_data = oe.packagedata.read_subpkgdata(p, d)
858 if rdep_data and 'PN' in rdep_data and rdep_data['PN'] in taskdeps:
859 break
860 if rdep_data and 'PN' in rdep_data and rdep_data['PN'] in taskdeps:
861 continue
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500862 if rdep_data and 'PN' in rdep_data:
863 error_msg = "%s rdepends on %s, but it isn't a build dependency, missing %s in DEPENDS or PACKAGECONFIG?" % (pkg, rdepend, rdep_data['PN'])
864 else:
865 error_msg = "%s rdepends on %s, but it isn't a build dependency?" % (pkg, rdepend)
866 package_qa_handle_error("build-deps", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500867
868 if "file-rdeps" not in skip:
869 ignored_file_rdeps = set(['/bin/sh', '/usr/bin/env', 'rtld(GNU_HASH)'])
870 if bb.data.inherits_class('nativesdk', d):
871 ignored_file_rdeps |= set(['/bin/bash', '/usr/bin/perl'])
872 # For Saving the FILERDEPENDS
873 filerdepends = {}
874 rdep_data = oe.packagedata.read_subpkgdata(pkg, d)
875 for key in rdep_data:
876 if key.startswith("FILERDEPENDS_"):
877 for subkey in rdep_data[key].split():
878 if subkey not in ignored_file_rdeps:
879 # We already know it starts with FILERDEPENDS_
880 filerdepends[subkey] = key[13:]
881
882 if filerdepends:
883 next = rdepends
884 done = rdepends[:]
885 # Find all the rdepends on the dependency chain
886 while next:
887 new = []
888 for rdep in next:
889 rdep_data = oe.packagedata.read_subpkgdata(rdep, d)
890 sub_rdeps = rdep_data.get("RDEPENDS_" + rdep)
891 if not sub_rdeps:
892 continue
893 for sub_rdep in sub_rdeps.split():
894 if sub_rdep in done:
895 continue
896 if not sub_rdep.startswith('(') and \
897 oe.packagedata.has_subpkgdata(sub_rdep, d):
898 # It's a new rdep
899 done.append(sub_rdep)
900 new.append(sub_rdep)
901 next = new
902
903 # Add the rprovides of itself
904 if pkg not in done:
905 done.insert(0, pkg)
906
907 # The python is not a package, but python-core provides it, so
908 # skip checking /usr/bin/python if python is in the rdeps, in
909 # case there is a RDEPENDS_pkg = "python" in the recipe.
910 for py in [ d.getVar('MLPREFIX', True) + "python", "python" ]:
911 if py in done:
912 filerdepends.pop("/usr/bin/python",None)
913 done.remove(py)
914 for rdep in done:
915 # For Saving the FILERPROVIDES, RPROVIDES and FILES_INFO
916 rdep_data = oe.packagedata.read_subpkgdata(rdep, d)
917 for key in rdep_data:
918 if key.startswith("FILERPROVIDES_") or key.startswith("RPROVIDES_"):
919 for subkey in rdep_data[key].split():
920 filerdepends.pop(subkey,None)
921 # Add the files list to the rprovides
922 if key == "FILES_INFO":
923 # Use eval() to make it as a dict
924 for subkey in eval(rdep_data[key]):
925 filerdepends.pop(subkey,None)
926 if not filerdepends:
927 # Break if all the file rdepends are met
928 break
929 if filerdepends:
930 for key in filerdepends:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500931 error_msg = "%s contained in package %s requires %s, but no providers found in RDEPENDS_%s?" % \
932 (filerdepends[key].replace("_%s" % pkg, "").replace("@underscore@", "_"), pkg, key, pkg)
933 package_qa_handle_error("file-rdeps", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500934
935def package_qa_check_deps(pkg, pkgdest, skip, d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500936
937 localdata = bb.data.createCopy(d)
938 localdata.setVar('OVERRIDES', pkg)
939 bb.data.update_data(localdata)
940
941 def check_valid_deps(var):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500942 try:
943 rvar = bb.utils.explode_dep_versions2(localdata.getVar(var, True) or "")
944 except ValueError as e:
945 bb.fatal("%s_%s: %s" % (var, pkg, e))
946 for dep in rvar:
947 for v in rvar[dep]:
948 if v and not v.startswith(('< ', '= ', '> ', '<= ', '>=')):
949 error_msg = "%s_%s is invalid: %s (%s) only comparisons <, =, >, <=, and >= are allowed" % (var, pkg, dep, v)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500950 package_qa_handle_error("dep-cmp", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500951
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500952 check_valid_deps('RDEPENDS')
953 check_valid_deps('RRECOMMENDS')
954 check_valid_deps('RSUGGESTS')
955 check_valid_deps('RPROVIDES')
956 check_valid_deps('RREPLACES')
957 check_valid_deps('RCONFLICTS')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500958
959QAPATHTEST[expanded-d] = "package_qa_check_expanded_d"
960def package_qa_check_expanded_d(path,name,d,elf,messages):
961 """
962 Check for the expanded D (${D}) value in pkg_* and FILES
963 variables, warn the user to use it correctly.
964 """
965
966 sane = True
967 expanded_d = d.getVar('D',True)
968
969 # Get packages for current recipe and iterate
970 packages = d.getVar('PACKAGES', True).split(" ")
971 for pak in packages:
972 # Go through all variables and check if expanded D is found, warn the user accordingly
973 for var in 'FILES','pkg_preinst', 'pkg_postinst', 'pkg_prerm', 'pkg_postrm':
974 bbvar = d.getVar(var + "_" + pak, False)
975 if bbvar:
976 # Bitbake expands ${D} within bbvar during the previous step, so we check for its expanded value
977 if expanded_d in bbvar:
978 if var == 'FILES':
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500979 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 -0500980 sane = False
981 else:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500982 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 -0500983 sane = False
984 return sane
985
986def package_qa_check_encoding(keys, encode, d):
987 def check_encoding(key,enc):
988 sane = True
989 value = d.getVar(key, True)
990 if value:
991 try:
992 s = unicode(value, enc)
993 except UnicodeDecodeError as e:
994 error_msg = "%s has non %s characters" % (key,enc)
995 sane = False
996 package_qa_handle_error("invalid-chars", error_msg, d)
997 return sane
998
999 for key in keys:
1000 sane = check_encoding(key, encode)
1001 if not sane:
1002 break
1003
1004HOST_USER_UID := "${@os.getuid()}"
1005HOST_USER_GID := "${@os.getgid()}"
1006
1007QAPATHTEST[host-user-contaminated] = "package_qa_check_host_user"
1008def package_qa_check_host_user(path, name, d, elf, messages):
1009 """Check for paths outside of /home which are owned by the user running bitbake."""
1010
1011 if not os.path.lexists(path):
1012 return
1013
1014 dest = d.getVar('PKGDEST', True)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001015 pn = d.getVar('PN', True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001016 home = os.path.join(dest, 'home')
1017 if path == home or path.startswith(home + os.sep):
1018 return
1019
1020 try:
1021 stat = os.lstat(path)
1022 except OSError as exc:
1023 import errno
1024 if exc.errno != errno.ENOENT:
1025 raise
1026 else:
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001027 rootfs_path = path[len(dest):]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001028 check_uid = int(d.getVar('HOST_USER_UID', True))
1029 if stat.st_uid == check_uid:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001030 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 -05001031 return False
1032
1033 check_gid = int(d.getVar('HOST_USER_GID', True))
1034 if stat.st_gid == check_gid:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001035 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 -05001036 return False
1037 return True
1038
1039# The PACKAGE FUNC to scan each package
1040python do_package_qa () {
1041 import subprocess
1042 import oe.packagedata
1043
1044 bb.note("DO PACKAGE QA")
1045
1046 bb.build.exec_func("read_subpackage_metadata", d)
1047
1048 # Check non UTF-8 characters on recipe's metadata
1049 package_qa_check_encoding(['DESCRIPTION', 'SUMMARY', 'LICENSE', 'SECTION'], 'utf-8', d)
1050
1051 logdir = d.getVar('T', True)
1052 pkg = d.getVar('PN', True)
1053
1054 # Check the compile log for host contamination
1055 compilelog = os.path.join(logdir,"log.do_compile")
1056
1057 if os.path.exists(compilelog):
1058 statement = "grep -e 'CROSS COMPILE Badness:' -e 'is unsafe for cross-compilation' %s > /dev/null" % compilelog
1059 if subprocess.call(statement, shell=True) == 0:
1060 msg = "%s: The compile log indicates that host include and/or library paths were used.\n \
1061 Please check the log '%s' for more information." % (pkg, compilelog)
1062 package_qa_handle_error("compile-host-path", msg, d)
1063
1064 # Check the install log for host contamination
1065 installlog = os.path.join(logdir,"log.do_install")
1066
1067 if os.path.exists(installlog):
1068 statement = "grep -e 'CROSS COMPILE Badness:' -e 'is unsafe for cross-compilation' %s > /dev/null" % installlog
1069 if subprocess.call(statement, shell=True) == 0:
1070 msg = "%s: The install log indicates that host include and/or library paths were used.\n \
1071 Please check the log '%s' for more information." % (pkg, installlog)
1072 package_qa_handle_error("install-host-path", msg, d)
1073
1074 # Scan the packages...
1075 pkgdest = d.getVar('PKGDEST', True)
1076 packages = set((d.getVar('PACKAGES', True) or '').split())
1077
1078 cpath = oe.cachedpath.CachedPath()
1079 global pkgfiles
1080 pkgfiles = {}
1081 for pkg in packages:
1082 pkgfiles[pkg] = []
1083 for walkroot, dirs, files in cpath.walk(pkgdest + "/" + pkg):
1084 for file in files:
1085 pkgfiles[pkg].append(walkroot + os.sep + file)
1086
1087 # no packages should be scanned
1088 if not packages:
1089 return
1090
1091 testmatrix = d.getVarFlags("QAPATHTEST")
1092 import re
1093 # The package name matches the [a-z0-9.+-]+ regular expression
1094 pkgname_pattern = re.compile("^[a-z0-9.+-]+$")
1095
1096 taskdepdata = d.getVar("BB_TASKDEPDATA", False)
1097 taskdeps = set()
1098 for dep in taskdepdata:
1099 taskdeps.add(taskdepdata[dep][0])
1100
1101 g = globals()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001102 for package in packages:
1103 skip = (d.getVar('INSANE_SKIP_' + package, True) or "").split()
1104 if skip:
1105 bb.note("Package %s skipping QA tests: %s" % (package, str(skip)))
1106 warnchecks = []
1107 for w in (d.getVar("WARN_QA", True) or "").split():
1108 if w in skip:
1109 continue
1110 if w in testmatrix and testmatrix[w] in g:
1111 warnchecks.append(g[testmatrix[w]])
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001112 if w == 'unsafe-references-in-binaries':
1113 oe.utils.write_ld_so_conf(d)
1114
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001115 errorchecks = []
1116 for e in (d.getVar("ERROR_QA", True) or "").split():
1117 if e in skip:
1118 continue
1119 if e in testmatrix and testmatrix[e] in g:
1120 errorchecks.append(g[testmatrix[e]])
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001121 if e == 'unsafe-references-in-binaries':
1122 oe.utils.write_ld_so_conf(d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001123
1124 bb.note("Checking Package: %s" % package)
1125 # Check package name
1126 if not pkgname_pattern.match(package):
1127 package_qa_handle_error("pkgname",
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001128 "%s doesn't match the [a-z0-9.+-]+ regex" % package, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001129
1130 path = "%s/%s" % (pkgdest, package)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001131 package_qa_walk(warnchecks, errorchecks, skip, package, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001132
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001133 package_qa_check_rdepends(package, pkgdest, skip, taskdeps, packages, d)
1134 package_qa_check_deps(package, pkgdest, skip, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001135
1136 if 'libdir' in d.getVar("ALL_QA", True).split():
1137 package_qa_check_libdir(d)
1138
1139 qa_sane = d.getVar("QA_SANE", True)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001140 if not qa_sane:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001141 bb.fatal("QA run found fatal errors. Please consider fixing them.")
1142 bb.note("DONE with PACKAGE QA")
1143}
1144
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001145do_package_qa[vardepsexclude] = "BB_TASKDEPDATA"
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001146do_package_qa[rdeptask] = "do_packagedata"
1147addtask do_package_qa after do_packagedata do_package before do_build
1148
1149SSTATETASKS += "do_package_qa"
1150do_package_qa[sstate-inputdirs] = ""
1151do_package_qa[sstate-outputdirs] = ""
1152python do_package_qa_setscene () {
1153 sstate_setscene(d)
1154}
1155addtask do_package_qa_setscene
1156
1157python do_qa_staging() {
1158 bb.note("QA checking staging")
1159
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001160 if not package_qa_check_staged(d.expand('${SYSROOT_DESTDIR}${libdir}'), d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001161 bb.fatal("QA staging was broken by the package built above")
1162}
1163
1164python do_qa_configure() {
1165 import subprocess
1166
1167 ###########################################################################
1168 # Check config.log for cross compile issues
1169 ###########################################################################
1170
1171 configs = []
1172 workdir = d.getVar('WORKDIR', True)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001173
1174 if bb.data.inherits_class('autotools', d):
1175 bb.note("Checking autotools environment for common misconfiguration")
1176 for root, dirs, files in os.walk(workdir):
1177 statement = "grep -q -F -e 'CROSS COMPILE Badness:' -e 'is unsafe for cross-compilation' %s" % \
1178 os.path.join(root,"config.log")
1179 if "config.log" in files:
1180 if subprocess.call(statement, shell=True) == 0:
1181 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 -05001182Rerun configure task after fixing this.""")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001183
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001184 if "configure.ac" in files:
1185 configs.append(os.path.join(root,"configure.ac"))
1186 if "configure.in" in files:
1187 configs.append(os.path.join(root, "configure.in"))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001188
1189 ###########################################################################
1190 # Check gettext configuration and dependencies are correct
1191 ###########################################################################
1192
1193 cnf = d.getVar('EXTRA_OECONF', True) or ""
1194 if "gettext" not in d.getVar('P', True) and "gcc-runtime" not in d.getVar('P', True) and "--disable-nls" not in cnf:
1195 ml = d.getVar("MLPREFIX", True) or ""
1196 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):
1197 gt = "gettext-native"
1198 elif bb.data.inherits_class('cross-canadian', d):
1199 gt = "nativesdk-gettext"
1200 else:
1201 gt = "virtual/" + ml + "gettext"
1202 deps = bb.utils.explode_deps(d.getVar('DEPENDS', True) or "")
1203 if gt not in deps:
1204 for config in configs:
1205 gnu = "grep \"^[[:space:]]*AM_GNU_GETTEXT\" %s >/dev/null" % config
1206 if subprocess.call(gnu, shell=True) == 0:
1207 bb.fatal("""%s required but not in DEPENDS for file %s.
1208Missing inherit gettext?""" % (gt, config))
1209
1210 ###########################################################################
1211 # Check license variables
1212 ###########################################################################
1213
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001214 package_qa_check_license(workdir, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001215
1216 ###########################################################################
1217 # Check unrecognised configure options (with a white list)
1218 ###########################################################################
1219 if bb.data.inherits_class("autotools", d):
1220 bb.note("Checking configure output for unrecognised options")
1221 try:
1222 flag = "WARNING: unrecognized options:"
1223 log = os.path.join(d.getVar('B', True), 'config.log')
1224 output = subprocess.check_output(['grep', '-F', flag, log]).replace(', ', ' ')
1225 options = set()
1226 for line in output.splitlines():
1227 options |= set(line.partition(flag)[2].split())
1228 whitelist = set(d.getVar("UNKNOWN_CONFIGURE_WHITELIST", True).split())
1229 options -= whitelist
1230 if options:
1231 pn = d.getVar('PN', True)
1232 error_msg = pn + ": configure was passed unrecognised options: " + " ".join(options)
1233 package_qa_handle_error("unknown-configure-option", error_msg, d)
1234 except subprocess.CalledProcessError:
1235 pass
1236
1237 # Check invalid PACKAGECONFIG
1238 pkgconfig = (d.getVar("PACKAGECONFIG", True) or "").split()
1239 if pkgconfig:
1240 pkgconfigflags = d.getVarFlags("PACKAGECONFIG") or {}
1241 for pconfig in pkgconfig:
1242 if pconfig not in pkgconfigflags:
1243 pn = d.getVar('PN', True)
1244 error_msg = "%s: invalid PACKAGECONFIG: %s" % (pn, pconfig)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001245 package_qa_handle_error("invalid-packageconfig", error_msg, d)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001246
1247 qa_sane = d.getVar("QA_SANE", True)
1248 if not qa_sane:
1249 bb.fatal("Fatal QA errors found, failing task.")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001250}
1251
1252python do_qa_unpack() {
1253 bb.note("Checking has ${S} been created")
1254
1255 s_dir = d.getVar('S', True)
1256 if not os.path.exists(s_dir):
1257 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))
1258}
1259
1260# The Staging Func, to check all staging
1261#addtask qa_staging after do_populate_sysroot before do_build
1262do_populate_sysroot[postfuncs] += "do_qa_staging "
1263
1264# Check broken config.log files, for packages requiring Gettext which don't
1265# have it in DEPENDS and for correct LIC_FILES_CHKSUM
1266#addtask qa_configure after do_configure before do_compile
1267do_configure[postfuncs] += "do_qa_configure "
1268
1269# Check does S exist.
1270do_unpack[postfuncs] += "do_qa_unpack"
1271
1272python () {
1273 tests = d.getVar('ALL_QA', True).split()
1274 if "desktop" in tests:
1275 d.appendVar("PACKAGE_DEPENDS", " desktop-file-utils-native")
1276
1277 ###########################################################################
1278 # Check various variables
1279 ###########################################################################
1280
1281 # Checking ${FILESEXTRAPATHS}
1282 extrapaths = (d.getVar("FILESEXTRAPATHS", True) or "")
1283 if '__default' not in extrapaths.split(":"):
1284 msg = "FILESEXTRAPATHS-variable, must always use _prepend (or _append)\n"
1285 msg += "type of assignment, and don't forget the colon.\n"
1286 msg += "Please assign it with the format of:\n"
1287 msg += " FILESEXTRAPATHS_append := \":${THISDIR}/Your_Files_Path\" or\n"
1288 msg += " FILESEXTRAPATHS_prepend := \"${THISDIR}/Your_Files_Path:\"\n"
1289 msg += "in your bbappend file\n\n"
1290 msg += "Your incorrect assignment is:\n"
1291 msg += "%s\n" % extrapaths
1292 bb.warn(msg)
1293
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001294 overrides = d.getVar('OVERRIDES', True).split(':')
1295 pn = d.getVar('PN', True)
1296 if pn in overrides:
1297 msg = 'Recipe %s has PN of "%s" which is in OVERRIDES, this can result in unexpected behaviour.' % (d.getVar("FILE", True), pn)
1298 package_qa_handle_error("pn-overrides", msg, d)
1299
1300 issues = []
1301 if (d.getVar('PACKAGES', True) or "").split():
1302 for dep in (d.getVar('QADEPENDS', True) or "").split():
1303 d.appendVarFlag('do_package_qa', 'depends', " %s:do_populate_sysroot" % dep)
1304 for var in 'RDEPENDS', 'RRECOMMENDS', 'RSUGGESTS', 'RCONFLICTS', 'RPROVIDES', 'RREPLACES', 'FILES', 'pkg_preinst', 'pkg_postinst', 'pkg_prerm', 'pkg_postrm', 'ALLOW_EMPTY':
1305 if d.getVar(var, False):
1306 issues.append(var)
1307
1308 fakeroot_tests = d.getVar('FAKEROOT_QA', True).split()
1309 if set(tests) & set(fakeroot_tests):
1310 d.setVarFlag('do_package_qa', 'fakeroot', '1')
1311 d.appendVarFlag('do_package_qa', 'depends', ' virtual/fakeroot-native:do_populate_sysroot')
1312 else:
1313 d.setVarFlag('do_package_qa', 'rdeptask', '')
1314 for i in issues:
1315 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 -05001316 qa_sane = d.getVar("QA_SANE", True)
1317 if not qa_sane:
1318 bb.fatal("Fatal QA errors found, failing task.")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001319}