blob: 7bbe8b63a2aa3219cfdfb7e6e51f0b3db32a4000 [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" : {
Brad Bishop37a0e4d2017-12-04 01:01:44 -050066 "aarch64" : (183, 0, 0, True, 64),
67 "aarch64_be" :(183, 0, 0, False, 64),
Patrick Williamsc124f4f2015-09-15 14:41:29 -050068 "i586" : (3, 0, 0, True, 32),
69 "x86_64": (62, 0, 0, True, 64),
70 "epiphany": (4643, 0, 0, True, 32),
Patrick Williamsc0f7c042017-02-23 20:41:17 -060071 "mips": ( 8, 0, 0, False, 32),
72 "mipsel": ( 8, 0, 0, True, 32),
Patrick Williamsc124f4f2015-09-15 14:41:29 -050073 },
74 "linux" : {
75 "aarch64" : (183, 0, 0, True, 64),
76 "aarch64_be" :(183, 0, 0, False, 64),
77 "arm" : (40, 97, 0, True, 32),
78 "armeb": (40, 97, 0, False, 32),
79 "powerpc": (20, 0, 0, False, 32),
80 "powerpc64": (21, 0, 0, False, 64),
81 "i386": ( 3, 0, 0, True, 32),
82 "i486": ( 3, 0, 0, True, 32),
83 "i586": ( 3, 0, 0, True, 32),
84 "i686": ( 3, 0, 0, True, 32),
85 "x86_64": (62, 0, 0, True, 64),
86 "ia64": (50, 0, 0, True, 64),
87 "alpha": (36902, 0, 0, True, 64),
88 "hppa": (15, 3, 0, False, 32),
89 "m68k": ( 4, 0, 0, False, 32),
90 "mips": ( 8, 0, 0, False, 32),
91 "mipsel": ( 8, 0, 0, True, 32),
92 "mips64": ( 8, 0, 0, False, 64),
93 "mips64el": ( 8, 0, 0, True, 64),
Patrick Williamsc0f7c042017-02-23 20:41:17 -060094 "mipsisa32r6": ( 8, 0, 0, False, 32),
95 "mipsisa32r6el": ( 8, 0, 0, True, 32),
96 "mipsisa64r6": ( 8, 0, 0, False, 64),
97 "mipsisa64r6el": ( 8, 0, 0, True, 64),
Patrick Williamsf1e5d692016-03-30 15:21:19 -050098 "nios2": (113, 0, 0, True, 32),
Patrick Williamsc124f4f2015-09-15 14:41:29 -050099 "s390": (22, 0, 0, False, 32),
100 "sh4": (42, 0, 0, True, 32),
101 "sparc": ( 2, 0, 0, False, 32),
102 "microblaze": (189, 0, 0, False, 32),
103 "microblazeeb":(189, 0, 0, False, 32),
104 "microblazeel":(189, 0, 0, True, 32),
105 },
106 "linux-uclibc" : {
107 "arm" : ( 40, 97, 0, True, 32),
108 "armeb": ( 40, 97, 0, False, 32),
109 "powerpc": ( 20, 0, 0, False, 32),
110 "i386": ( 3, 0, 0, True, 32),
111 "i486": ( 3, 0, 0, True, 32),
112 "i586": ( 3, 0, 0, True, 32),
113 "i686": ( 3, 0, 0, True, 32),
114 "x86_64": ( 62, 0, 0, True, 64),
115 "mips": ( 8, 0, 0, False, 32),
116 "mipsel": ( 8, 0, 0, True, 32),
117 "mips64": ( 8, 0, 0, False, 64),
118 "mips64el": ( 8, 0, 0, True, 64),
119 "avr32": (6317, 0, 0, False, 32),
120 "sh4": (42, 0, 0, True, 32),
121
122 },
123 "linux-musl" : {
124 "aarch64" : (183, 0, 0, True, 64),
125 "aarch64_be" :(183, 0, 0, False, 64),
126 "arm" : ( 40, 97, 0, True, 32),
127 "armeb": ( 40, 97, 0, False, 32),
128 "powerpc": ( 20, 0, 0, False, 32),
129 "i386": ( 3, 0, 0, True, 32),
130 "i486": ( 3, 0, 0, True, 32),
131 "i586": ( 3, 0, 0, True, 32),
132 "i686": ( 3, 0, 0, True, 32),
133 "x86_64": ( 62, 0, 0, True, 64),
134 "mips": ( 8, 0, 0, False, 32),
135 "mipsel": ( 8, 0, 0, True, 32),
136 "mips64": ( 8, 0, 0, False, 64),
137 "mips64el": ( 8, 0, 0, True, 64),
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500138 "microblaze": (189, 0, 0, False, 32),
139 "microblazeeb":(189, 0, 0, False, 32),
140 "microblazeel":(189, 0, 0, True, 32),
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500141 },
142 "uclinux-uclibc" : {
143 "bfin": ( 106, 0, 0, True, 32),
144 },
145 "linux-gnueabi" : {
146 "arm" : (40, 0, 0, True, 32),
147 "armeb" : (40, 0, 0, False, 32),
148 },
149 "linux-musleabi" : {
150 "arm" : (40, 0, 0, True, 32),
151 "armeb" : (40, 0, 0, False, 32),
152 },
153 "linux-uclibceabi" : {
154 "arm" : (40, 0, 0, True, 32),
155 "armeb" : (40, 0, 0, False, 32),
156 },
157 "linux-gnuspe" : {
158 "powerpc": (20, 0, 0, False, 32),
159 },
160 "linux-muslspe" : {
161 "powerpc": (20, 0, 0, False, 32),
162 },
163 "linux-uclibcspe" : {
164 "powerpc": (20, 0, 0, False, 32),
165 },
166 "linux-gnu" : {
167 "powerpc": (20, 0, 0, False, 32),
168 "sh4": (42, 0, 0, True, 32),
169 },
170 "linux-gnux32" : {
171 "x86_64": (62, 0, 0, True, 32),
172 },
173 "linux-gnun32" : {
174 "mips64": ( 8, 0, 0, False, 32),
175 "mips64el": ( 8, 0, 0, True, 32),
176 },
177 }
178
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600179 # Add in any extra user supplied data which may come from a BSP layer, removing the
180 # need to always change this class directly
181 extra_machdata = (d.getVar("PACKAGEQA_EXTRA_MACHDEFFUNCS", True) or "").split()
182 for m in extra_machdata:
183 call = m + "(machdata, d)"
184 locs = { "machdata" : machdata, "d" : d}
185 machdata = bb.utils.better_eval(call, locs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500186
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600187 return machdata
188
189
190def package_qa_clean_path(path, d, pkg=None):
191 """
192 Remove redundant paths from the path for display. If pkg isn't set then
193 TMPDIR is stripped, otherwise PKGDEST/pkg is stripped.
194 """
195 if pkg:
196 path = path.replace(os.path.join(d.getVar("PKGDEST", True), pkg), "/")
197 return path.replace(d.getVar("TMPDIR", True), "/").replace("//", "/")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500198
199def package_qa_write_error(type, error, d):
200 logfile = d.getVar('QA_LOGFILE', True)
201 if logfile:
202 p = d.getVar('P', True)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600203 with open(logfile, "a+") as f:
204 f.write("%s: %s [%s]\n" % (p, error, type))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500205
206def package_qa_handle_error(error_class, error_msg, d):
207 package_qa_write_error(error_class, error_msg, d)
208 if error_class in (d.getVar("ERROR_QA", True) or "").split():
209 bb.error("QA Issue: %s [%s]" % (error_msg, error_class))
210 d.setVar("QA_SANE", False)
211 return False
212 elif error_class in (d.getVar("WARN_QA", True) or "").split():
213 bb.warn("QA Issue: %s [%s]" % (error_msg, error_class))
214 else:
215 bb.note("QA Issue: %s [%s]" % (error_msg, error_class))
216 return True
217
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500218def package_qa_add_message(messages, section, new_msg):
219 if section not in messages:
220 messages[section] = new_msg
221 else:
222 messages[section] = messages[section] + "\n" + new_msg
223
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500224QAPATHTEST[libexec] = "package_qa_check_libexec"
225def package_qa_check_libexec(path,name, d, elf, messages):
226
227 # Skip the case where the default is explicitly /usr/libexec
228 libexec = d.getVar('libexecdir', True)
229 if libexec == "/usr/libexec":
230 return True
231
232 if 'libexec' in path.split(os.path.sep):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500233 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 -0500234 return False
235
236 return True
237
238QAPATHTEST[rpaths] = "package_qa_check_rpath"
239def package_qa_check_rpath(file,name, d, elf, messages):
240 """
241 Check for dangerous RPATHs
242 """
243 if not elf:
244 return
245
246 if os.path.islink(file):
247 return
248
249 bad_dirs = [d.getVar('BASE_WORKDIR', True), d.getVar('STAGING_DIR_TARGET', True)]
250
251 phdrs = elf.run_objdump("-p", d)
252
253 import re
254 rpath_re = re.compile("\s+RPATH\s+(.*)")
255 for line in phdrs.split("\n"):
256 m = rpath_re.match(line)
257 if m:
258 rpath = m.group(1)
259 for dir in bad_dirs:
260 if dir in rpath:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500261 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 -0500262
263QAPATHTEST[useless-rpaths] = "package_qa_check_useless_rpaths"
264def package_qa_check_useless_rpaths(file, name, d, elf, messages):
265 """
266 Check for RPATHs that are useless but not dangerous
267 """
268 def rpath_eq(a, b):
269 return os.path.normpath(a) == os.path.normpath(b)
270
271 if not elf:
272 return
273
274 if os.path.islink(file):
275 return
276
277 libdir = d.getVar("libdir", True)
278 base_libdir = d.getVar("base_libdir", True)
279
280 phdrs = elf.run_objdump("-p", d)
281
282 import re
283 rpath_re = re.compile("\s+RPATH\s+(.*)")
284 for line in phdrs.split("\n"):
285 m = rpath_re.match(line)
286 if m:
287 rpath = m.group(1)
288 if rpath_eq(rpath, libdir) or rpath_eq(rpath, base_libdir):
289 # The dynamic linker searches both these places anyway. There is no point in
290 # looking there again.
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500291 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 -0500292
293QAPATHTEST[dev-so] = "package_qa_check_dev"
294def package_qa_check_dev(path, name, d, elf, messages):
295 """
296 Check for ".so" library symlinks in non-dev packages
297 """
298
299 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 -0500300 package_qa_add_message(messages, "dev-so", "non -dev/-dbg/nativesdk- package contains symlink .so: %s path '%s'" % \
301 (name, package_qa_clean_path(path,d)))
302
303QAPATHTEST[dev-elf] = "package_qa_check_dev_elf"
304def package_qa_check_dev_elf(path, name, d, elf, messages):
305 """
306 Check that -dev doesn't contain real shared libraries. The test has to
307 check that the file is not a link and is an ELF object as some recipes
308 install link-time .so files that are linker scripts.
309 """
310 if name.endswith("-dev") and path.endswith(".so") and not os.path.islink(path) and elf:
311 package_qa_add_message(messages, "dev-elf", "-dev package contains non-symlink .so: %s path '%s'" % \
312 (name, package_qa_clean_path(path,d)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500313
314QAPATHTEST[staticdev] = "package_qa_check_staticdev"
315def package_qa_check_staticdev(path, name, d, elf, messages):
316 """
317 Check for ".a" library in non-staticdev packages
318 There are a number of exceptions to this rule, -pic packages can contain
319 static libraries, the _nonshared.a belong with their -dev packages and
320 libgcc.a, libgcov.a will be skipped in their packages
321 """
322
323 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 -0500324 package_qa_add_message(messages, "staticdev", "non -staticdev package contains static .a library: %s path '%s'" % \
325 (name, package_qa_clean_path(path,d)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500326
327def package_qa_check_libdir(d):
328 """
329 Check for wrong library installation paths. For instance, catch
330 recipes installing /lib/bar.so when ${base_libdir}="lib32" or
331 installing in /usr/lib64 when ${libdir}="/usr/lib"
332 """
333 import re
334
335 pkgdest = d.getVar('PKGDEST', True)
336 base_libdir = d.getVar("base_libdir",True) + os.sep
337 libdir = d.getVar("libdir", True) + os.sep
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500338 libexecdir = d.getVar("libexecdir", True) + os.sep
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500339 exec_prefix = d.getVar("exec_prefix", True) + os.sep
340
341 messages = []
342
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500343 # The re's are purposely fuzzy, as some there are some .so.x.y.z files
344 # that don't follow the standard naming convention. It checks later
345 # that they are actual ELF files
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500346 lib_re = re.compile("^/lib.+\.so(\..+)?$")
347 exec_re = re.compile("^%s.*/lib.+\.so(\..+)?$" % exec_prefix)
348
349 for root, dirs, files in os.walk(pkgdest):
350 if root == pkgdest:
351 # Skip subdirectories for any packages with libdir in INSANE_SKIP
352 skippackages = []
353 for package in dirs:
354 if 'libdir' in (d.getVar('INSANE_SKIP_' + package, True) or "").split():
355 bb.note("Package %s skipping libdir QA test" % (package))
356 skippackages.append(package)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500357 elif d.getVar('PACKAGE_DEBUG_SPLIT_STYLE', True) == 'debug-file-directory' and package.endswith("-dbg"):
358 bb.note("Package %s skipping libdir QA test for PACKAGE_DEBUG_SPLIT_STYLE equals debug-file-directory" % (package))
359 skippackages.append(package)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500360 for package in skippackages:
361 dirs.remove(package)
362 for file in files:
363 full_path = os.path.join(root, file)
364 rel_path = os.path.relpath(full_path, pkgdest)
365 if os.sep in rel_path:
366 package, rel_path = rel_path.split(os.sep, 1)
367 rel_path = os.sep + rel_path
368 if lib_re.match(rel_path):
369 if base_libdir not in rel_path:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500370 # make sure it's an actual ELF file
371 elf = oe.qa.ELFFile(full_path)
372 try:
373 elf.open()
374 messages.append("%s: found library in wrong location: %s" % (package, rel_path))
375 except (oe.qa.NotELFFileError):
376 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500377 if exec_re.match(rel_path):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500378 if libdir not in rel_path and libexecdir not in rel_path:
379 # make sure it's an actual ELF file
380 elf = oe.qa.ELFFile(full_path)
381 try:
382 elf.open()
383 messages.append("%s: found library in wrong location: %s" % (package, rel_path))
384 except (oe.qa.NotELFFileError):
385 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500386
387 if messages:
388 package_qa_handle_error("libdir", "\n".join(messages), d)
389
390QAPATHTEST[debug-files] = "package_qa_check_dbg"
391def package_qa_check_dbg(path, name, d, elf, messages):
392 """
393 Check for ".debug" files or directories outside of the dbg package
394 """
395
396 if not "-dbg" in name and not "-ptest" in name:
397 if '.debug' in path.split(os.path.sep):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500398 messages("debug-files", "non debug package contains .debug directory: %s path %s" % \
399 (name, package_qa_clean_path(path,d)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500400
401QAPATHTEST[perms] = "package_qa_check_perm"
402def package_qa_check_perm(path,name,d, elf, messages):
403 """
404 Check the permission of files
405 """
406 return
407
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500408
409QAPATHTEST[unsafe-references-in-scripts] = "package_qa_check_unsafe_references_in_scripts"
410def package_qa_check_unsafe_references_in_scripts(path, name, d, elf, messages):
411 """
412 Warn if scripts in base_[bindir|sbindir|libdir] reference files under exec_prefix
413 """
414 if unsafe_references_skippable(path, name, d):
415 return
416
417 if not elf:
418 import stat
419 import subprocess
420 pn = d.getVar('PN', True)
421
422 # Ensure we're checking an executable script
423 statinfo = os.stat(path)
424 if bool(statinfo.st_mode & stat.S_IXUSR):
425 # grep shell scripts for possible references to /exec_prefix/
426 exec_prefix = d.getVar('exec_prefix', True)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500427 statement = "grep -e '%s/[^ :]\{1,\}/[^ :]\{1,\}' %s > /dev/null" % (exec_prefix, path)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500428 if subprocess.call(statement, shell=True) == 0:
429 error_msg = pn + ": Found a reference to %s/ in %s" % (exec_prefix, path)
430 package_qa_handle_error("unsafe-references-in-scripts", error_msg, d)
431 error_msg = "Shell scripts in base_bindir and base_sbindir should not reference anything in exec_prefix"
432 package_qa_handle_error("unsafe-references-in-scripts", error_msg, d)
433
434def unsafe_references_skippable(path, name, d):
435 if bb.data.inherits_class('native', d) or bb.data.inherits_class('nativesdk', d):
436 return True
437
438 if "-dbg" in name or "-dev" in name:
439 return True
440
441 # Other package names to skip:
442 if name.startswith("kernel-module-"):
443 return True
444
445 # Skip symlinks
446 if os.path.islink(path):
447 return True
448
449 # Skip unusual rootfs layouts which make these tests irrelevant
450 exec_prefix = d.getVar('exec_prefix', True)
451 if exec_prefix == "":
452 return True
453
454 pkgdest = d.getVar('PKGDEST', True)
455 pkgdest = pkgdest + "/" + name
456 pkgdest = os.path.abspath(pkgdest)
457 base_bindir = pkgdest + d.getVar('base_bindir', True)
458 base_sbindir = pkgdest + d.getVar('base_sbindir', True)
459 base_libdir = pkgdest + d.getVar('base_libdir', True)
460 bindir = pkgdest + d.getVar('bindir', True)
461 sbindir = pkgdest + d.getVar('sbindir', True)
462 libdir = pkgdest + d.getVar('libdir', True)
463
464 if base_bindir == bindir and base_sbindir == sbindir and base_libdir == libdir:
465 return True
466
467 # Skip files not in base_[bindir|sbindir|libdir]
468 path = os.path.abspath(path)
469 if not (base_bindir in path or base_sbindir in path or base_libdir in path):
470 return True
471
472 return False
473
474QAPATHTEST[arch] = "package_qa_check_arch"
475def package_qa_check_arch(path,name,d, elf, messages):
476 """
477 Check if archs are compatible
478 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600479 import re
480
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500481 if not elf:
482 return
483
484 target_os = d.getVar('TARGET_OS', True)
485 target_arch = d.getVar('TARGET_ARCH', True)
486 provides = d.getVar('PROVIDES', True)
487 bpn = d.getVar('BPN', True)
488
489 if target_arch == "allarch":
490 pn = d.getVar('PN', True)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500491 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 -0500492 return
493
494 # FIXME: Cross package confuse this check, so just skip them
495 for s in ['cross', 'nativesdk', 'cross-canadian']:
496 if bb.data.inherits_class(s, d):
497 return
498
499 # avoid following links to /usr/bin (e.g. on udev builds)
500 # we will check the files pointed to anyway...
501 if os.path.islink(path):
502 return
503
504 #if this will throw an exception, then fix the dict above
505 (machine, osabi, abiversion, littleendian, bits) \
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600506 = package_qa_get_machine_dict(d)[target_os][target_arch]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500507
508 # Check the architecture and endiannes of the binary
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600509 is_32 = (("virtual/kernel" in provides) or bb.data.inherits_class("module", d)) and \
510 (target_os == "linux-gnux32" or re.match('mips64.*32', d.getVar('DEFAULTTUNE', True)))
511 if not ((machine == elf.machine()) or is_32):
512 package_qa_add_message(messages, "arch", "Architecture did not match (%s, expected %s) on %s" % \
513 (oe.qa.elf_machine_to_string(elf.machine()), oe.qa.elf_machine_to_string(machine), package_qa_clean_path(path,d)))
514 elif not ((bits == elf.abiSize()) or is_32):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500515 package_qa_add_message(messages, "arch", "Bit size did not match (%d to %d) %s on %s" % \
516 (bits, elf.abiSize(), bpn, package_qa_clean_path(path,d)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500517 elif not littleendian == elf.isLittleEndian():
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500518 package_qa_add_message(messages, "arch", "Endiannes did not match (%d to %d) on %s" % \
519 (littleendian, elf.isLittleEndian(), package_qa_clean_path(path,d)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500520
521QAPATHTEST[desktop] = "package_qa_check_desktop"
522def package_qa_check_desktop(path, name, d, elf, messages):
523 """
524 Run all desktop files through desktop-file-validate.
525 """
526 if path.endswith(".desktop"):
527 desktop_file_validate = os.path.join(d.getVar('STAGING_BINDIR_NATIVE',True),'desktop-file-validate')
528 output = os.popen("%s %s" % (desktop_file_validate, path))
529 # This only produces output on errors
530 for l in output:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500531 package_qa_add_message(messages, "desktop", "Desktop file issue: " + l.strip())
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500532
533QAPATHTEST[textrel] = "package_qa_textrel"
534def package_qa_textrel(path, name, d, elf, messages):
535 """
536 Check if the binary contains relocations in .text
537 """
538
539 if not elf:
540 return
541
542 if os.path.islink(path):
543 return
544
545 phdrs = elf.run_objdump("-p", d)
546 sane = True
547
548 import re
549 textrel_re = re.compile("\s+TEXTREL\s+")
550 for line in phdrs.split("\n"):
551 if textrel_re.match(line):
552 sane = False
553
554 if not sane:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500555 package_qa_add_message(messages, "textrel", "ELF binary '%s' has relocations in .text" % path)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500556
557QAPATHTEST[ldflags] = "package_qa_hash_style"
558def package_qa_hash_style(path, name, d, elf, messages):
559 """
560 Check if the binary has the right hash style...
561 """
562
563 if not elf:
564 return
565
566 if os.path.islink(path):
567 return
568
569 gnu_hash = "--hash-style=gnu" in d.getVar('LDFLAGS', True)
570 if not gnu_hash:
571 gnu_hash = "--hash-style=both" in d.getVar('LDFLAGS', True)
572 if not gnu_hash:
573 return
574
575 sane = False
576 has_syms = False
577
578 phdrs = elf.run_objdump("-p", d)
579
580 # If this binary has symbols, we expect it to have GNU_HASH too.
581 for line in phdrs.split("\n"):
582 if "SYMTAB" in line:
583 has_syms = True
584 if "GNU_HASH" in line:
585 sane = True
586 if "[mips32]" in line or "[mips64]" in line:
587 sane = True
588
589 if has_syms and not sane:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500590 package_qa_add_message(messages, "ldflags", "No GNU_HASH in the elf binary: '%s'" % path)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500591
592
593QAPATHTEST[buildpaths] = "package_qa_check_buildpaths"
594def package_qa_check_buildpaths(path, name, d, elf, messages):
595 """
596 Check for build paths inside target files and error if not found in the whitelist
597 """
598 # Ignore .debug files, not interesting
599 if path.find(".debug") != -1:
600 return
601
602 # Ignore symlinks
603 if os.path.islink(path):
604 return
605
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500606 # Ignore ipk and deb's CONTROL dir
607 if path.find(name + "/CONTROL/") != -1 or path.find(name + "/DEBIAN/") != -1:
608 return
609
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500610 tmpdir = d.getVar('TMPDIR', True)
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500611 with open(path, 'rb') as f:
612 file_content = f.read().decode('utf-8', errors='ignore')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500613 if tmpdir in file_content:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500614 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 -0500615
616
617QAPATHTEST[xorg-driver-abi] = "package_qa_check_xorg_driver_abi"
618def package_qa_check_xorg_driver_abi(path, name, d, elf, messages):
619 """
620 Check that all packages containing Xorg drivers have ABI dependencies
621 """
622
623 # Skip dev, dbg or nativesdk packages
624 if name.endswith("-dev") or name.endswith("-dbg") or name.startswith("nativesdk-"):
625 return
626
627 driverdir = d.expand("${libdir}/xorg/modules/drivers/")
628 if driverdir in path and path.endswith(".so"):
629 mlprefix = d.getVar('MLPREFIX', True) or ''
630 for rdep in bb.utils.explode_deps(d.getVar('RDEPENDS_' + name, True) or ""):
631 if rdep.startswith("%sxorg-abi-" % mlprefix):
632 return
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500633 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 -0500634
635QAPATHTEST[infodir] = "package_qa_check_infodir"
636def package_qa_check_infodir(path, name, d, elf, messages):
637 """
638 Check that /usr/share/info/dir isn't shipped in a particular package
639 """
640 infodir = d.expand("${infodir}/dir")
641
642 if infodir in path:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500643 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 -0500644
645QAPATHTEST[symlink-to-sysroot] = "package_qa_check_symlink_to_sysroot"
646def package_qa_check_symlink_to_sysroot(path, name, d, elf, messages):
647 """
648 Check that the package doesn't contain any absolute symlinks to the sysroot.
649 """
650 if os.path.islink(path):
651 target = os.readlink(path)
652 if os.path.isabs(target):
653 tmpdir = d.getVar('TMPDIR', True)
654 if target.startswith(tmpdir):
655 trimmed = path.replace(os.path.join (d.getVar("PKGDEST", True), name), "")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500656 package_qa_add_message(messages, "symlink-to-sysroot", "Symlink %s in %s points to TMPDIR" % (trimmed, name))
657
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600658# Check license variables
659do_populate_lic[postfuncs] += "populate_lic_qa_checksum"
660python populate_lic_qa_checksum() {
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500661 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600662 Check for changes in the license files.
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500663 """
664 import tempfile
665 sane = True
666
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600667 lic_files = d.getVar('LIC_FILES_CHKSUM', True) or ''
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500668 lic = d.getVar('LICENSE', True)
669 pn = d.getVar('PN', True)
670
671 if lic == "CLOSED":
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500672 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500673
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600674 if not lic_files and d.getVar('SRC_URI', True):
675 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 -0500676
677 srcdir = d.getVar('S', True)
678
679 for url in lic_files.split():
680 try:
681 (type, host, path, user, pswd, parm) = bb.fetch.decodeurl(url)
682 except bb.fetch.MalformedUrl:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600683 sane = package_qa_handle_error("license-checksum", pn + ": LIC_FILES_CHKSUM contains an invalid URL: " + url, d)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500684 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500685 srclicfile = os.path.join(srcdir, path)
686 if not os.path.isfile(srclicfile):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500687 package_qa_handle_error("license-checksum", pn + ": LIC_FILES_CHKSUM points to an invalid file: " + srclicfile, d)
688 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500689
690 recipemd5 = parm.get('md5', '')
691 beginline, endline = 0, 0
692 if 'beginline' in parm:
693 beginline = int(parm['beginline'])
694 if 'endline' in parm:
695 endline = int(parm['endline'])
696
697 if (not beginline) and (not endline):
698 md5chksum = bb.utils.md5_file(srclicfile)
699 else:
700 fi = open(srclicfile, 'rb')
701 fo = tempfile.NamedTemporaryFile(mode='wb', prefix='poky.', suffix='.tmp', delete=False)
702 tmplicfile = fo.name;
703 lineno = 0
704 linesout = 0
705 for line in fi:
706 lineno += 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600707 if (lineno >= beginline):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500708 if ((lineno <= endline) or not endline):
709 fo.write(line)
710 linesout += 1
711 else:
712 break
713 fo.flush()
714 fo.close()
715 fi.close()
716 md5chksum = bb.utils.md5_file(tmplicfile)
717 os.unlink(tmplicfile)
718
719 if recipemd5 == md5chksum:
720 bb.note (pn + ": md5 checksum matched for ", url)
721 else:
722 if recipemd5:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500723 msg = pn + ": The LIC_FILES_CHKSUM does not match for " + url
724 msg = msg + "\n" + pn + ": The new md5 checksum is " + md5chksum
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500725 if beginline:
726 if endline:
727 srcfiledesc = "%s (lines %d through to %d)" % (srclicfile, beginline, endline)
728 else:
729 srcfiledesc = "%s (beginning on line %d)" % (srclicfile, beginline)
730 elif endline:
731 srcfiledesc = "%s (ending on line %d)" % (srclicfile, endline)
732 else:
733 srcfiledesc = srclicfile
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500734 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 -0500735
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500736 else:
737 msg = pn + ": LIC_FILES_CHKSUM is not specified for " + url
738 msg = msg + "\n" + pn + ": The md5 checksum is " + md5chksum
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600739 sane = package_qa_handle_error("license-checksum", msg, d)
740
741 if not sane:
742 bb.fatal("Fatal QA errors found, failing task.")
743}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500744
745def package_qa_check_staged(path,d):
746 """
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500747 Check staged la and pc files for common problems like references to the work
748 directory.
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500749
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500750 As this is run after every stage we should be able to find the one
751 responsible for the errors easily even if we look at every .pc and .la file.
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500752 """
753
754 sane = True
755 tmpdir = d.getVar('TMPDIR', True)
756 workdir = os.path.join(tmpdir, "work")
757
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500758 if bb.data.inherits_class("native", d) or bb.data.inherits_class("cross", d):
759 pkgconfigcheck = workdir
760 else:
761 pkgconfigcheck = tmpdir
762
763 # find all .la and .pc files
764 # read the content
765 # and check for stuff that looks wrong
766 for root, dirs, files in os.walk(path):
767 for file in files:
768 path = os.path.join(root,file)
769 if file.endswith(".la"):
770 with open(path) as f:
771 file_content = f.read()
772 if workdir in file_content:
773 error_msg = "%s failed sanity test (workdir) in path %s" % (file,root)
774 sane = package_qa_handle_error("la", error_msg, d)
775 elif file.endswith(".pc"):
776 with open(path) as f:
777 file_content = f.read()
778 if pkgconfigcheck in file_content:
779 error_msg = "%s failed sanity test (tmpdir) in path %s" % (file,root)
780 sane = package_qa_handle_error("pkgconfig", error_msg, d)
781
782 return sane
783
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500784# Run all package-wide warnfuncs and errorfuncs
785def package_qa_package(warnfuncs, errorfuncs, skip, package, d):
786 warnings = {}
787 errors = {}
788
789 for func in warnfuncs:
790 func(package, d, warnings)
791 for func in errorfuncs:
792 func(package, d, errors)
793
794 for w in warnings:
795 package_qa_handle_error(w, warnings[w], d)
796 for e in errors:
797 package_qa_handle_error(e, errors[e], d)
798
799 return len(errors) == 0
800
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500801# Walk over all files in a directory and call func
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500802def package_qa_walk(warnfuncs, errorfuncs, skip, package, d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500803 import oe.qa
804
805 #if this will throw an exception, then fix the dict above
806 target_os = d.getVar('TARGET_OS', True)
807 target_arch = d.getVar('TARGET_ARCH', True)
808
809 warnings = {}
810 errors = {}
811 for path in pkgfiles[package]:
812 elf = oe.qa.ELFFile(path)
813 try:
814 elf.open()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500815 except (IOError, oe.qa.NotELFFileError):
816 # IOError can happen if the packaging control files disappear,
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500817 elf = None
818 for func in warnfuncs:
819 func(path, package, d, elf, warnings)
820 for func in errorfuncs:
821 func(path, package, d, elf, errors)
822
823 for w in warnings:
824 package_qa_handle_error(w, warnings[w], d)
825 for e in errors:
826 package_qa_handle_error(e, errors[e], d)
827
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500828def package_qa_check_rdepends(pkg, pkgdest, skip, taskdeps, packages, d):
829 # Don't do this check for kernel/module recipes, there aren't too many debug/development
830 # packages and you can get false positives e.g. on kernel-module-lirc-dev
831 if bb.data.inherits_class("kernel", d) or bb.data.inherits_class("module-base", d):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500832 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500833
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500834 if not "-dbg" in pkg and not "packagegroup-" in pkg and not "-image" in pkg:
835 localdata = bb.data.createCopy(d)
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500836 localdata.setVar('OVERRIDES', localdata.getVar('OVERRIDES', True) + ':' + pkg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500837 bb.data.update_data(localdata)
838
839 # Now check the RDEPENDS
840 rdepends = bb.utils.explode_deps(localdata.getVar('RDEPENDS', True) or "")
841
842 # Now do the sanity check!!!
843 if "build-deps" not in skip:
844 for rdepend in rdepends:
845 if "-dbg" in rdepend and "debug-deps" not in skip:
846 error_msg = "%s rdepends on %s" % (pkg,rdepend)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500847 package_qa_handle_error("debug-deps", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500848 if (not "-dev" in pkg and not "-staticdev" in pkg) and rdepend.endswith("-dev") and "dev-deps" not in skip:
849 error_msg = "%s rdepends on %s" % (pkg, rdepend)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500850 package_qa_handle_error("dev-deps", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500851 if rdepend not in packages:
852 rdep_data = oe.packagedata.read_subpkgdata(rdepend, d)
853 if rdep_data and 'PN' in rdep_data and rdep_data['PN'] in taskdeps:
854 continue
855 if not rdep_data or not 'PN' in rdep_data:
856 pkgdata_dir = d.getVar("PKGDATA_DIR", True)
857 try:
858 possibles = os.listdir("%s/runtime-rprovides/%s/" % (pkgdata_dir, rdepend))
859 except OSError:
860 possibles = []
861 for p in possibles:
862 rdep_data = oe.packagedata.read_subpkgdata(p, d)
863 if rdep_data and 'PN' in rdep_data and rdep_data['PN'] in taskdeps:
864 break
865 if rdep_data and 'PN' in rdep_data and rdep_data['PN'] in taskdeps:
866 continue
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500867 if rdep_data and 'PN' in rdep_data:
868 error_msg = "%s rdepends on %s, but it isn't a build dependency, missing %s in DEPENDS or PACKAGECONFIG?" % (pkg, rdepend, rdep_data['PN'])
869 else:
870 error_msg = "%s rdepends on %s, but it isn't a build dependency?" % (pkg, rdepend)
871 package_qa_handle_error("build-deps", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500872
873 if "file-rdeps" not in skip:
874 ignored_file_rdeps = set(['/bin/sh', '/usr/bin/env', 'rtld(GNU_HASH)'])
875 if bb.data.inherits_class('nativesdk', d):
876 ignored_file_rdeps |= set(['/bin/bash', '/usr/bin/perl'])
877 # For Saving the FILERDEPENDS
878 filerdepends = {}
879 rdep_data = oe.packagedata.read_subpkgdata(pkg, d)
880 for key in rdep_data:
881 if key.startswith("FILERDEPENDS_"):
882 for subkey in rdep_data[key].split():
883 if subkey not in ignored_file_rdeps:
884 # We already know it starts with FILERDEPENDS_
885 filerdepends[subkey] = key[13:]
886
887 if filerdepends:
888 next = rdepends
889 done = rdepends[:]
890 # Find all the rdepends on the dependency chain
891 while next:
892 new = []
893 for rdep in next:
894 rdep_data = oe.packagedata.read_subpkgdata(rdep, d)
895 sub_rdeps = rdep_data.get("RDEPENDS_" + rdep)
896 if not sub_rdeps:
897 continue
898 for sub_rdep in sub_rdeps.split():
899 if sub_rdep in done:
900 continue
901 if not sub_rdep.startswith('(') and \
902 oe.packagedata.has_subpkgdata(sub_rdep, d):
903 # It's a new rdep
904 done.append(sub_rdep)
905 new.append(sub_rdep)
906 next = new
907
908 # Add the rprovides of itself
909 if pkg not in done:
910 done.insert(0, pkg)
911
912 # The python is not a package, but python-core provides it, so
913 # skip checking /usr/bin/python if python is in the rdeps, in
914 # case there is a RDEPENDS_pkg = "python" in the recipe.
915 for py in [ d.getVar('MLPREFIX', True) + "python", "python" ]:
916 if py in done:
917 filerdepends.pop("/usr/bin/python",None)
918 done.remove(py)
919 for rdep in done:
920 # For Saving the FILERPROVIDES, RPROVIDES and FILES_INFO
921 rdep_data = oe.packagedata.read_subpkgdata(rdep, d)
922 for key in rdep_data:
923 if key.startswith("FILERPROVIDES_") or key.startswith("RPROVIDES_"):
924 for subkey in rdep_data[key].split():
925 filerdepends.pop(subkey,None)
926 # Add the files list to the rprovides
927 if key == "FILES_INFO":
928 # Use eval() to make it as a dict
929 for subkey in eval(rdep_data[key]):
930 filerdepends.pop(subkey,None)
931 if not filerdepends:
932 # Break if all the file rdepends are met
933 break
934 if filerdepends:
935 for key in filerdepends:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500936 error_msg = "%s contained in package %s requires %s, but no providers found in RDEPENDS_%s?" % \
937 (filerdepends[key].replace("_%s" % pkg, "").replace("@underscore@", "_"), pkg, key, pkg)
938 package_qa_handle_error("file-rdeps", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500939
940def package_qa_check_deps(pkg, pkgdest, skip, d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500941
942 localdata = bb.data.createCopy(d)
943 localdata.setVar('OVERRIDES', pkg)
944 bb.data.update_data(localdata)
945
946 def check_valid_deps(var):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500947 try:
948 rvar = bb.utils.explode_dep_versions2(localdata.getVar(var, True) or "")
949 except ValueError as e:
950 bb.fatal("%s_%s: %s" % (var, pkg, e))
951 for dep in rvar:
952 for v in rvar[dep]:
953 if v and not v.startswith(('< ', '= ', '> ', '<= ', '>=')):
954 error_msg = "%s_%s is invalid: %s (%s) only comparisons <, =, >, <=, and >= are allowed" % (var, pkg, dep, v)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500955 package_qa_handle_error("dep-cmp", error_msg, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500956
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500957 check_valid_deps('RDEPENDS')
958 check_valid_deps('RRECOMMENDS')
959 check_valid_deps('RSUGGESTS')
960 check_valid_deps('RPROVIDES')
961 check_valid_deps('RREPLACES')
962 check_valid_deps('RCONFLICTS')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500963
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500964QAPKGTEST[expanded-d] = "package_qa_check_expanded_d"
965def package_qa_check_expanded_d(package, d, messages):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500966 """
967 Check for the expanded D (${D}) value in pkg_* and FILES
968 variables, warn the user to use it correctly.
969 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500970 sane = True
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500971 expanded_d = d.getVar('D', True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500972
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500973 for var in 'FILES','pkg_preinst', 'pkg_postinst', 'pkg_prerm', 'pkg_postrm':
974 bbvar = d.getVar(var + "_" + package, True) or ""
975 if expanded_d in bbvar:
976 if var == 'FILES':
977 package_qa_add_message(messages, "expanded-d", "FILES in %s recipe should not contain the ${D} variable as it references the local build directory not the target filesystem, best solution is to remove the ${D} reference" % package)
978 sane = False
979 else:
980 package_qa_add_message(messages, "expanded-d", "%s in %s recipe contains ${D}, it should be replaced by $D instead" % (var, package))
981 sane = False
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500982 return sane
983
984def package_qa_check_encoding(keys, encode, d):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600985 def check_encoding(key, enc):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500986 sane = True
987 value = d.getVar(key, True)
988 if value:
989 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600990 s = value.encode(enc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500991 except UnicodeDecodeError as e:
992 error_msg = "%s has non %s characters" % (key,enc)
993 sane = False
994 package_qa_handle_error("invalid-chars", error_msg, d)
995 return sane
996
997 for key in keys:
998 sane = check_encoding(key, encode)
999 if not sane:
1000 break
1001
1002HOST_USER_UID := "${@os.getuid()}"
1003HOST_USER_GID := "${@os.getgid()}"
1004
1005QAPATHTEST[host-user-contaminated] = "package_qa_check_host_user"
1006def package_qa_check_host_user(path, name, d, elf, messages):
1007 """Check for paths outside of /home which are owned by the user running bitbake."""
1008
1009 if not os.path.lexists(path):
1010 return
1011
1012 dest = d.getVar('PKGDEST', True)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001013 pn = d.getVar('PN', True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001014 home = os.path.join(dest, 'home')
1015 if path == home or path.startswith(home + os.sep):
1016 return
1017
1018 try:
1019 stat = os.lstat(path)
1020 except OSError as exc:
1021 import errno
1022 if exc.errno != errno.ENOENT:
1023 raise
1024 else:
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001025 rootfs_path = path[len(dest):]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001026 check_uid = int(d.getVar('HOST_USER_UID', True))
1027 if stat.st_uid == check_uid:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001028 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 -05001029 return False
1030
1031 check_gid = int(d.getVar('HOST_USER_GID', True))
1032 if stat.st_gid == check_gid:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001033 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 -05001034 return False
1035 return True
1036
1037# The PACKAGE FUNC to scan each package
1038python do_package_qa () {
1039 import subprocess
1040 import oe.packagedata
1041
1042 bb.note("DO PACKAGE QA")
1043
1044 bb.build.exec_func("read_subpackage_metadata", d)
1045
1046 # Check non UTF-8 characters on recipe's metadata
1047 package_qa_check_encoding(['DESCRIPTION', 'SUMMARY', 'LICENSE', 'SECTION'], 'utf-8', d)
1048
1049 logdir = d.getVar('T', True)
1050 pkg = d.getVar('PN', True)
1051
1052 # Check the compile log for host contamination
1053 compilelog = os.path.join(logdir,"log.do_compile")
1054
1055 if os.path.exists(compilelog):
1056 statement = "grep -e 'CROSS COMPILE Badness:' -e 'is unsafe for cross-compilation' %s > /dev/null" % compilelog
1057 if subprocess.call(statement, shell=True) == 0:
1058 msg = "%s: The compile log indicates that host include and/or library paths were used.\n \
1059 Please check the log '%s' for more information." % (pkg, compilelog)
1060 package_qa_handle_error("compile-host-path", msg, d)
1061
1062 # Check the install log for host contamination
1063 installlog = os.path.join(logdir,"log.do_install")
1064
1065 if os.path.exists(installlog):
1066 statement = "grep -e 'CROSS COMPILE Badness:' -e 'is unsafe for cross-compilation' %s > /dev/null" % installlog
1067 if subprocess.call(statement, shell=True) == 0:
1068 msg = "%s: The install log indicates that host include and/or library paths were used.\n \
1069 Please check the log '%s' for more information." % (pkg, installlog)
1070 package_qa_handle_error("install-host-path", msg, d)
1071
1072 # Scan the packages...
1073 pkgdest = d.getVar('PKGDEST', True)
1074 packages = set((d.getVar('PACKAGES', True) or '').split())
1075
1076 cpath = oe.cachedpath.CachedPath()
1077 global pkgfiles
1078 pkgfiles = {}
1079 for pkg in packages:
1080 pkgfiles[pkg] = []
1081 for walkroot, dirs, files in cpath.walk(pkgdest + "/" + pkg):
1082 for file in files:
1083 pkgfiles[pkg].append(walkroot + os.sep + file)
1084
1085 # no packages should be scanned
1086 if not packages:
1087 return
1088
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001089 import re
1090 # The package name matches the [a-z0-9.+-]+ regular expression
1091 pkgname_pattern = re.compile("^[a-z0-9.+-]+$")
1092
1093 taskdepdata = d.getVar("BB_TASKDEPDATA", False)
1094 taskdeps = set()
1095 for dep in taskdepdata:
1096 taskdeps.add(taskdepdata[dep][0])
1097
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001098 for package in packages:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001099 def parse_test_matrix(matrix_name):
1100 testmatrix = d.getVarFlags(matrix_name) or {}
1101 g = globals()
1102 warnchecks = []
1103 for w in (d.getVar("WARN_QA", True) or "").split():
1104 if w in skip:
1105 continue
1106 if w in testmatrix and testmatrix[w] in g:
1107 warnchecks.append(g[testmatrix[w]])
1108 if w == 'unsafe-references-in-binaries':
1109 oe.utils.write_ld_so_conf(d)
1110
1111 errorchecks = []
1112 for e in (d.getVar("ERROR_QA", True) or "").split():
1113 if e in skip:
1114 continue
1115 if e in testmatrix and testmatrix[e] in g:
1116 errorchecks.append(g[testmatrix[e]])
1117 if e == 'unsafe-references-in-binaries':
1118 oe.utils.write_ld_so_conf(d)
1119 return warnchecks, errorchecks
1120
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001121 skip = (d.getVar('INSANE_SKIP_' + package, True) or "").split()
1122 if skip:
1123 bb.note("Package %s skipping QA tests: %s" % (package, str(skip)))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001124
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001125
1126 bb.note("Checking Package: %s" % package)
1127 # Check package name
1128 if not pkgname_pattern.match(package):
1129 package_qa_handle_error("pkgname",
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001130 "%s doesn't match the [a-z0-9.+-]+ regex" % package, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001131
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001132 warn_checks, error_checks = parse_test_matrix("QAPATHTEST")
1133 package_qa_walk(warn_checks, error_checks, skip, package, d)
1134
1135 warn_checks, error_checks = parse_test_matrix("QAPKGTEST")
1136 package_qa_package(warn_checks, error_checks, skip, package, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001137
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001138 package_qa_check_rdepends(package, pkgdest, skip, taskdeps, packages, d)
1139 package_qa_check_deps(package, pkgdest, skip, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001140
1141 if 'libdir' in d.getVar("ALL_QA", True).split():
1142 package_qa_check_libdir(d)
1143
1144 qa_sane = d.getVar("QA_SANE", True)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001145 if not qa_sane:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001146 bb.fatal("QA run found fatal errors. Please consider fixing them.")
1147 bb.note("DONE with PACKAGE QA")
1148}
1149
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001150do_package_qa[vardepsexclude] = "BB_TASKDEPDATA"
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001151do_package_qa[rdeptask] = "do_packagedata"
1152addtask do_package_qa after do_packagedata do_package before do_build
1153
1154SSTATETASKS += "do_package_qa"
1155do_package_qa[sstate-inputdirs] = ""
1156do_package_qa[sstate-outputdirs] = ""
1157python do_package_qa_setscene () {
1158 sstate_setscene(d)
1159}
1160addtask do_package_qa_setscene
1161
1162python do_qa_staging() {
1163 bb.note("QA checking staging")
1164
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001165 if not package_qa_check_staged(d.expand('${SYSROOT_DESTDIR}${libdir}'), d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001166 bb.fatal("QA staging was broken by the package built above")
1167}
1168
1169python do_qa_configure() {
1170 import subprocess
1171
1172 ###########################################################################
1173 # Check config.log for cross compile issues
1174 ###########################################################################
1175
1176 configs = []
1177 workdir = d.getVar('WORKDIR', True)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001178
1179 if bb.data.inherits_class('autotools', d):
1180 bb.note("Checking autotools environment for common misconfiguration")
1181 for root, dirs, files in os.walk(workdir):
1182 statement = "grep -q -F -e 'CROSS COMPILE Badness:' -e 'is unsafe for cross-compilation' %s" % \
1183 os.path.join(root,"config.log")
1184 if "config.log" in files:
1185 if subprocess.call(statement, shell=True) == 0:
1186 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 -05001187Rerun configure task after fixing this.""")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001188
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001189 if "configure.ac" in files:
1190 configs.append(os.path.join(root,"configure.ac"))
1191 if "configure.in" in files:
1192 configs.append(os.path.join(root, "configure.in"))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001193
1194 ###########################################################################
1195 # Check gettext configuration and dependencies are correct
1196 ###########################################################################
1197
1198 cnf = d.getVar('EXTRA_OECONF', True) or ""
1199 if "gettext" not in d.getVar('P', True) and "gcc-runtime" not in d.getVar('P', True) and "--disable-nls" not in cnf:
1200 ml = d.getVar("MLPREFIX", True) or ""
1201 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):
1202 gt = "gettext-native"
1203 elif bb.data.inherits_class('cross-canadian', d):
1204 gt = "nativesdk-gettext"
1205 else:
1206 gt = "virtual/" + ml + "gettext"
1207 deps = bb.utils.explode_deps(d.getVar('DEPENDS', True) or "")
1208 if gt not in deps:
1209 for config in configs:
1210 gnu = "grep \"^[[:space:]]*AM_GNU_GETTEXT\" %s >/dev/null" % config
1211 if subprocess.call(gnu, shell=True) == 0:
1212 bb.fatal("""%s required but not in DEPENDS for file %s.
1213Missing inherit gettext?""" % (gt, config))
1214
1215 ###########################################################################
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001216 # Check unrecognised configure options (with a white list)
1217 ###########################################################################
1218 if bb.data.inherits_class("autotools", d):
1219 bb.note("Checking configure output for unrecognised options")
1220 try:
1221 flag = "WARNING: unrecognized options:"
1222 log = os.path.join(d.getVar('B', True), 'config.log')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001223 output = subprocess.check_output(['grep', '-F', flag, log]).decode("utf-8").replace(', ', ' ')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001224 options = set()
1225 for line in output.splitlines():
1226 options |= set(line.partition(flag)[2].split())
1227 whitelist = set(d.getVar("UNKNOWN_CONFIGURE_WHITELIST", True).split())
1228 options -= whitelist
1229 if options:
1230 pn = d.getVar('PN', True)
1231 error_msg = pn + ": configure was passed unrecognised options: " + " ".join(options)
1232 package_qa_handle_error("unknown-configure-option", error_msg, d)
1233 except subprocess.CalledProcessError:
1234 pass
1235
1236 # Check invalid PACKAGECONFIG
1237 pkgconfig = (d.getVar("PACKAGECONFIG", True) or "").split()
1238 if pkgconfig:
1239 pkgconfigflags = d.getVarFlags("PACKAGECONFIG") or {}
1240 for pconfig in pkgconfig:
1241 if pconfig not in pkgconfigflags:
1242 pn = d.getVar('PN', True)
1243 error_msg = "%s: invalid PACKAGECONFIG: %s" % (pn, pconfig)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001244 package_qa_handle_error("invalid-packageconfig", error_msg, d)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001245
1246 qa_sane = d.getVar("QA_SANE", True)
1247 if not qa_sane:
1248 bb.fatal("Fatal QA errors found, failing task.")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001249}
1250
1251python do_qa_unpack() {
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001252 src_uri = d.getVar('SRC_URI', True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001253 s_dir = d.getVar('S', True)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001254 if src_uri and not os.path.exists(s_dir):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001255 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))
1256}
1257
1258# The Staging Func, to check all staging
1259#addtask qa_staging after do_populate_sysroot before do_build
1260do_populate_sysroot[postfuncs] += "do_qa_staging "
1261
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001262# Check broken config.log files, for packages requiring Gettext which
1263# don't have it in DEPENDS.
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001264#addtask qa_configure after do_configure before do_compile
1265do_configure[postfuncs] += "do_qa_configure "
1266
1267# Check does S exist.
1268do_unpack[postfuncs] += "do_qa_unpack"
1269
1270python () {
1271 tests = d.getVar('ALL_QA', True).split()
1272 if "desktop" in tests:
1273 d.appendVar("PACKAGE_DEPENDS", " desktop-file-utils-native")
1274
1275 ###########################################################################
1276 # Check various variables
1277 ###########################################################################
1278
1279 # Checking ${FILESEXTRAPATHS}
1280 extrapaths = (d.getVar("FILESEXTRAPATHS", True) or "")
1281 if '__default' not in extrapaths.split(":"):
1282 msg = "FILESEXTRAPATHS-variable, must always use _prepend (or _append)\n"
1283 msg += "type of assignment, and don't forget the colon.\n"
1284 msg += "Please assign it with the format of:\n"
1285 msg += " FILESEXTRAPATHS_append := \":${THISDIR}/Your_Files_Path\" or\n"
1286 msg += " FILESEXTRAPATHS_prepend := \"${THISDIR}/Your_Files_Path:\"\n"
1287 msg += "in your bbappend file\n\n"
1288 msg += "Your incorrect assignment is:\n"
1289 msg += "%s\n" % extrapaths
1290 bb.warn(msg)
1291
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001292 overrides = d.getVar('OVERRIDES', True).split(':')
1293 pn = d.getVar('PN', True)
1294 if pn in overrides:
1295 msg = 'Recipe %s has PN of "%s" which is in OVERRIDES, this can result in unexpected behaviour.' % (d.getVar("FILE", True), pn)
1296 package_qa_handle_error("pn-overrides", msg, d)
1297
1298 issues = []
1299 if (d.getVar('PACKAGES', True) or "").split():
1300 for dep in (d.getVar('QADEPENDS', True) or "").split():
1301 d.appendVarFlag('do_package_qa', 'depends', " %s:do_populate_sysroot" % dep)
1302 for var in 'RDEPENDS', 'RRECOMMENDS', 'RSUGGESTS', 'RCONFLICTS', 'RPROVIDES', 'RREPLACES', 'FILES', 'pkg_preinst', 'pkg_postinst', 'pkg_prerm', 'pkg_postrm', 'ALLOW_EMPTY':
1303 if d.getVar(var, False):
1304 issues.append(var)
1305
1306 fakeroot_tests = d.getVar('FAKEROOT_QA', True).split()
1307 if set(tests) & set(fakeroot_tests):
1308 d.setVarFlag('do_package_qa', 'fakeroot', '1')
1309 d.appendVarFlag('do_package_qa', 'depends', ' virtual/fakeroot-native:do_populate_sysroot')
1310 else:
1311 d.setVarFlag('do_package_qa', 'rdeptask', '')
1312 for i in issues:
1313 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 -05001314 qa_sane = d.getVar("QA_SANE", True)
1315 if not qa_sane:
1316 bb.fatal("Fatal QA errors found, failing task.")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001317}