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