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