summaryrefslogtreecommitdiffstats
path: root/meta/classes/insane.bbclass
diff options
context:
space:
mode:
Diffstat (limited to 'meta/classes/insane.bbclass')
-rw-r--r--meta/classes/insane.bbclass954
1 files changed, 954 insertions, 0 deletions
diff --git a/meta/classes/insane.bbclass b/meta/classes/insane.bbclass
new file mode 100644
index 0000000000..a784aff3a9
--- /dev/null
+++ b/meta/classes/insane.bbclass
@@ -0,0 +1,954 @@
1# 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# -Ensure that binaries in base_[bindir|sbindir|libdir] do not link
15# into exec_prefix
16# -Check that scripts in base_[bindir|sbindir|libdir] do not reference
17# files under exec_prefix
18
19
20PACKAGE_DEPENDS += "${QADEPENDS}"
21PACKAGEFUNCS += " do_package_qa "
22
23# unsafe-references-in-binaries requires prelink-rtld from
24# prelink-native, but we don't want this DEPENDS for -native builds
25QADEPENDS = "prelink-native"
26QADEPENDS_class-native = ""
27QADEPENDS_class-nativesdk = ""
28QA_SANE = "True"
29
30# Elect whether a given type of error is a warning or error, they may
31# have been set by other files.
32WARN_QA ?= "ldflags useless-rpaths rpaths staticdev libdir xorg-driver-abi \
33 textrel already-stripped incompatible-license files-invalid \
34 installed-vs-shipped compile-host-path install-host-path \
35 pn-overrides infodir \
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 \
41 "
42
43ALL_QA = "${WARN_QA} ${ERROR_QA}"
44
45#
46# dictionary for elf headers
47#
48# feel free to add and correct.
49#
50# TARGET_OS TARGET_ARCH MACHINE, OSABI, ABIVERSION, Little Endian, 32bit?
51def package_qa_get_machine_dict():
52 return {
53 "darwin9" : {
54 "arm" : (40, 0, 0, True, 32),
55 },
56 "linux" : {
57 "aarch64" : (183, 0, 0, True, 64),
58 "arm" : (40, 97, 0, True, 32),
59 "armeb": (40, 97, 0, False, 32),
60 "powerpc": (20, 0, 0, False, 32),
61 "powerpc64": (21, 0, 0, False, 64),
62 "i386": ( 3, 0, 0, True, 32),
63 "i486": ( 3, 0, 0, True, 32),
64 "i586": ( 3, 0, 0, True, 32),
65 "i686": ( 3, 0, 0, True, 32),
66 "x86_64": (62, 0, 0, True, 64),
67 "ia64": (50, 0, 0, True, 64),
68 "alpha": (36902, 0, 0, True, 64),
69 "hppa": (15, 3, 0, False, 32),
70 "m68k": ( 4, 0, 0, False, 32),
71 "mips": ( 8, 0, 0, False, 32),
72 "mipsel": ( 8, 0, 0, True, 32),
73 "mips64": ( 8, 0, 0, False, 64),
74 "mips64el": ( 8, 0, 0, True, 64),
75 "s390": (22, 0, 0, False, 32),
76 "sh4": (42, 0, 0, True, 32),
77 "sparc": ( 2, 0, 0, False, 32),
78 "microblaze": (189, 0, 0, False, 32),
79 "microblazeel":(189, 0, 0, True, 32),
80 },
81 "linux-uclibc" : {
82 "arm" : ( 40, 97, 0, True, 32),
83 "armeb": ( 40, 97, 0, False, 32),
84 "powerpc": ( 20, 0, 0, False, 32),
85 "i386": ( 3, 0, 0, True, 32),
86 "i486": ( 3, 0, 0, True, 32),
87 "i586": ( 3, 0, 0, True, 32),
88 "i686": ( 3, 0, 0, True, 32),
89 "x86_64": ( 62, 0, 0, True, 64),
90 "mips": ( 8, 0, 0, False, 32),
91 "mipsel": ( 8, 0, 0, True, 32),
92 "mips64": ( 8, 0, 0, False, 64),
93 "mips64el": ( 8, 0, 0, True, 64),
94 "avr32": (6317, 0, 0, False, 32),
95 "sh4": (42, 0, 0, True, 32),
96
97 },
98 "uclinux-uclibc" : {
99 "bfin": ( 106, 0, 0, True, 32),
100 },
101 "linux-gnueabi" : {
102 "arm" : (40, 0, 0, True, 32),
103 "armeb" : (40, 0, 0, False, 32),
104 },
105 "linux-uclibceabi" : {
106 "arm" : (40, 0, 0, True, 32),
107 "armeb" : (40, 0, 0, False, 32),
108 },
109 "linux-gnuspe" : {
110 "powerpc": (20, 0, 0, False, 32),
111 },
112 "linux-uclibcspe" : {
113 "powerpc": (20, 0, 0, False, 32),
114 },
115 "linux-gnu" : {
116 "powerpc": (20, 0, 0, False, 32),
117 "sh4": (42, 0, 0, True, 32),
118 },
119 "linux-gnux32" : {
120 "x86_64": (62, 0, 0, True, 32),
121 },
122 "linux-gnun32" : {
123 "mips64": ( 8, 0, 0, False, 32),
124 "mips64el": ( 8, 0, 0, True, 32),
125 },
126 }
127
128
129def package_qa_clean_path(path,d):
130 """ Remove the common prefix from the path. In this case it is the TMPDIR"""
131 return path.replace(d.getVar('TMPDIR',True),"")
132
133def package_qa_write_error(error, d):
134 logfile = d.getVar('QA_LOGFILE', True)
135 if logfile:
136 p = d.getVar('P', True)
137 f = file( logfile, "a+")
138 print >> f, "%s: %s" % (p, error)
139 f.close()
140
141def package_qa_handle_error(error_class, error_msg, d):
142 package_qa_write_error(error_msg, d)
143 if error_class in (d.getVar("ERROR_QA", True) or "").split():
144 bb.error("QA Issue: %s" % error_msg)
145 d.setVar("QA_SANE", False)
146 return False
147 elif error_class in (d.getVar("WARN_QA", True) or "").split():
148 bb.warn("QA Issue: %s" % error_msg)
149 else:
150 bb.note("QA Issue: %s" % error_msg)
151 return True
152
153QAPATHTEST[libexec] = "package_qa_check_libexec"
154def package_qa_check_libexec(path,name, d, elf, messages):
155
156 # Skip the case where the default is explicitly /usr/libexec
157 libexec = d.getVar('libexecdir', True)
158 if libexec == "/usr/libexec":
159 return True
160
161 if 'libexec' in path.split(os.path.sep):
162 messages.append("%s: %s is using libexec please relocate to %s" % (name, package_qa_clean_path(path, d), libexec))
163 return False
164
165 return True
166
167QAPATHTEST[rpaths] = "package_qa_check_rpath"
168def package_qa_check_rpath(file,name, d, elf, messages):
169 """
170 Check for dangerous RPATHs
171 """
172 if not elf:
173 return
174
175 if os.path.islink(file):
176 return
177
178 bad_dirs = [d.getVar('BASE_WORKDIR', True), d.getVar('STAGING_DIR_TARGET', True)]
179
180 phdrs = elf.run_objdump("-p", d)
181
182 import re
183 rpath_re = re.compile("\s+RPATH\s+(.*)")
184 for line in phdrs.split("\n"):
185 m = rpath_re.match(line)
186 if m:
187 rpath = m.group(1)
188 for dir in bad_dirs:
189 if dir in rpath:
190 messages.append("package %s contains bad RPATH %s in file %s" % (name, rpath, file))
191
192QAPATHTEST[useless-rpaths] = "package_qa_check_useless_rpaths"
193def package_qa_check_useless_rpaths(file, name, d, elf, messages):
194 """
195 Check for RPATHs that are useless but not dangerous
196 """
197 def rpath_eq(a, b):
198 return os.path.normpath(a) == os.path.normpath(b)
199
200 if not elf:
201 return
202
203 if os.path.islink(file):
204 return
205
206 libdir = d.getVar("libdir", True)
207 base_libdir = d.getVar("base_libdir", True)
208
209 phdrs = elf.run_objdump("-p", d)
210
211 import re
212 rpath_re = re.compile("\s+RPATH\s+(.*)")
213 for line in phdrs.split("\n"):
214 m = rpath_re.match(line)
215 if m:
216 rpath = m.group(1)
217 if rpath_eq(rpath, libdir) or rpath_eq(rpath, base_libdir):
218 # The dynamic linker searches both these places anyway. There is no point in
219 # looking there again.
220 messages.append("%s: %s contains probably-redundant RPATH %s" % (name, package_qa_clean_path(file, d), rpath))
221
222QAPATHTEST[dev-so] = "package_qa_check_dev"
223def package_qa_check_dev(path, name, d, elf, messages):
224 """
225 Check for ".so" library symlinks in non-dev packages
226 """
227
228 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):
229 messages.append("non -dev/-dbg/-nativesdk package contains symlink .so: %s path '%s'" % \
230 (name, package_qa_clean_path(path,d)))
231
232QAPATHTEST[staticdev] = "package_qa_check_staticdev"
233def package_qa_check_staticdev(path, name, d, elf, messages):
234 """
235 Check for ".a" library in non-staticdev packages
236 There are a number of exceptions to this rule, -pic packages can contain
237 static libraries, the _nonshared.a belong with their -dev packages and
238 libgcc.a, libgcov.a will be skipped in their packages
239 """
240
241 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"):
242 messages.append("non -staticdev package contains static .a library: %s path '%s'" % \
243 (name, package_qa_clean_path(path,d)))
244
245def package_qa_check_libdir(d):
246 """
247 Check for wrong library installation paths. For instance, catch
248 recipes installing /lib/bar.so when ${base_libdir}="lib32" or
249 installing in /usr/lib64 when ${libdir}="/usr/lib"
250 """
251 import re
252
253 pkgdest = d.getVar('PKGDEST', True)
254 base_libdir = d.getVar("base_libdir",True) + os.sep
255 libdir = d.getVar("libdir", True) + os.sep
256 exec_prefix = d.getVar("exec_prefix", True) + os.sep
257
258 messages = []
259
260 lib_re = re.compile("^/lib.+\.so(\..+)?$")
261 exec_re = re.compile("^%s.*/lib.+\.so(\..+)?$" % exec_prefix)
262
263 for root, dirs, files in os.walk(pkgdest):
264 if root == pkgdest:
265 # Skip subdirectories for any packages with libdir in INSANE_SKIP
266 skippackages = []
267 for package in dirs:
268 if 'libdir' in (d.getVar('INSANE_SKIP_' + package, True) or "").split():
269 bb.note("Package %s skipping libdir QA test" % (package))
270 skippackages.append(package)
271 for package in skippackages:
272 dirs.remove(package)
273 for file in files:
274 full_path = os.path.join(root, file)
275 rel_path = os.path.relpath(full_path, pkgdest)
276 if os.sep in rel_path:
277 package, rel_path = rel_path.split(os.sep, 1)
278 rel_path = os.sep + rel_path
279 if lib_re.match(rel_path):
280 if base_libdir not in rel_path:
281 messages.append("%s: found library in wrong location: %s" % (package, rel_path))
282 if exec_re.match(rel_path):
283 if libdir not in rel_path:
284 messages.append("%s: found library in wrong location: %s" % (package, rel_path))
285
286 if messages:
287 package_qa_handle_error("libdir", "\n".join(messages), d)
288
289QAPATHTEST[debug-files] = "package_qa_check_dbg"
290def package_qa_check_dbg(path, name, d, elf, messages):
291 """
292 Check for ".debug" files or directories outside of the dbg package
293 """
294
295 if not "-dbg" in name and not "-ptest" in name:
296 if '.debug' in path.split(os.path.sep):
297 messages.append("non debug package contains .debug directory: %s path %s" % \
298 (name, package_qa_clean_path(path,d)))
299
300QAPATHTEST[perms] = "package_qa_check_perm"
301def package_qa_check_perm(path,name,d, elf, messages):
302 """
303 Check the permission of files
304 """
305 return
306
307QAPATHTEST[unsafe-references-in-binaries] = "package_qa_check_unsafe_references_in_binaries"
308def package_qa_check_unsafe_references_in_binaries(path, name, d, elf, messages):
309 """
310 Ensure binaries in base_[bindir|sbindir|libdir] do not link to files under exec_prefix
311 """
312 if unsafe_references_skippable(path, name, d):
313 return
314
315 if elf:
316 import subprocess as sub
317 pn = d.getVar('PN', True)
318
319 exec_prefix = d.getVar('exec_prefix', True)
320 sysroot_path = d.getVar('STAGING_DIR_TARGET', True)
321 sysroot_path_usr = sysroot_path + exec_prefix
322
323 try:
324 ldd_output = bb.process.Popen(["prelink-rtld", "--root", sysroot_path, path], stdout=sub.PIPE).stdout.read()
325 except bb.process.CmdError:
326 error_msg = pn + ": prelink-rtld aborted when processing %s" % path
327 package_qa_handle_error("unsafe-references-in-binaries", error_msg, d)
328 return False
329
330 if sysroot_path_usr in ldd_output:
331 ldd_output = ldd_output.replace(sysroot_path, "")
332
333 pkgdest = d.getVar('PKGDEST', True)
334 packages = d.getVar('PACKAGES', True)
335
336 for package in packages.split():
337 short_path = path.replace('%s/%s' % (pkgdest, package), "", 1)
338 if (short_path != path):
339 break
340
341 base_err = pn + ": %s, installed in the base_prefix, requires a shared library under exec_prefix (%s)" % (short_path, exec_prefix)
342 for line in ldd_output.split('\n'):
343 if exec_prefix in line:
344 error_msg = "%s: %s" % (base_err, line.strip())
345 package_qa_handle_error("unsafe-references-in-binaries", error_msg, d)
346
347 return False
348
349QAPATHTEST[unsafe-references-in-scripts] = "package_qa_check_unsafe_references_in_scripts"
350def package_qa_check_unsafe_references_in_scripts(path, name, d, elf, messages):
351 """
352 Warn if scripts in base_[bindir|sbindir|libdir] reference files under exec_prefix
353 """
354 if unsafe_references_skippable(path, name, d):
355 return
356
357 if not elf:
358 import stat
359 import subprocess
360 pn = d.getVar('PN', True)
361
362 # Ensure we're checking an executable script
363 statinfo = os.stat(path)
364 if bool(statinfo.st_mode & stat.S_IXUSR):
365 # grep shell scripts for possible references to /exec_prefix/
366 exec_prefix = d.getVar('exec_prefix', True)
367 statement = "grep -e '%s/' %s > /dev/null" % (exec_prefix, path)
368 if subprocess.call(statement, shell=True) == 0:
369 error_msg = pn + ": Found a reference to %s/ in %s" % (exec_prefix, path)
370 package_qa_handle_error("unsafe-references-in-scripts", error_msg, d)
371 error_msg = "Shell scripts in base_bindir and base_sbindir should not reference anything in exec_prefix"
372 package_qa_handle_error("unsafe-references-in-scripts", error_msg, d)
373
374def unsafe_references_skippable(path, name, d):
375 if bb.data.inherits_class('native', d) or bb.data.inherits_class('nativesdk', d):
376 return True
377
378 if "-dbg" in name or "-dev" in name:
379 return True
380
381 # Other package names to skip:
382 if name.startswith("kernel-module-"):
383 return True
384
385 # Skip symlinks
386 if os.path.islink(path):
387 return True
388
389 # Skip unusual rootfs layouts which make these tests irrelevant
390 exec_prefix = d.getVar('exec_prefix', True)
391 if exec_prefix == "":
392 return True
393
394 pkgdest = d.getVar('PKGDEST', True)
395 pkgdest = pkgdest + "/" + name
396 pkgdest = os.path.abspath(pkgdest)
397 base_bindir = pkgdest + d.getVar('base_bindir', True)
398 base_sbindir = pkgdest + d.getVar('base_sbindir', True)
399 base_libdir = pkgdest + d.getVar('base_libdir', True)
400 bindir = pkgdest + d.getVar('bindir', True)
401 sbindir = pkgdest + d.getVar('sbindir', True)
402 libdir = pkgdest + d.getVar('libdir', True)
403
404 if base_bindir == bindir and base_sbindir == sbindir and base_libdir == libdir:
405 return True
406
407 # Skip files not in base_[bindir|sbindir|libdir]
408 path = os.path.abspath(path)
409 if not (base_bindir in path or base_sbindir in path or base_libdir in path):
410 return True
411
412 return False
413
414QAPATHTEST[arch] = "package_qa_check_arch"
415def package_qa_check_arch(path,name,d, elf, messages):
416 """
417 Check if archs are compatible
418 """
419 if not elf:
420 return
421
422 target_os = d.getVar('TARGET_OS', True)
423 target_arch = d.getVar('TARGET_ARCH', True)
424 provides = d.getVar('PROVIDES', True)
425 bpn = d.getVar('BPN', True)
426
427 # FIXME: Cross package confuse this check, so just skip them
428 for s in ['cross', 'nativesdk', 'cross-canadian']:
429 if bb.data.inherits_class(s, d):
430 return
431
432 # avoid following links to /usr/bin (e.g. on udev builds)
433 # we will check the files pointed to anyway...
434 if os.path.islink(path):
435 return
436
437 #if this will throw an exception, then fix the dict above
438 (machine, osabi, abiversion, littleendian, bits) \
439 = package_qa_get_machine_dict()[target_os][target_arch]
440
441 # Check the architecture and endiannes of the binary
442 if not ((machine == elf.machine()) or \
443 ("virtual/kernel" in provides) and (target_os == "linux-gnux32")):
444 messages.append("Architecture did not match (%d to %d) on %s" % \
445 (machine, elf.machine(), package_qa_clean_path(path,d)))
446 elif not ((bits == elf.abiSize()) or \
447 ("virtual/kernel" in provides) and (target_os == "linux-gnux32")):
448 messages.append("Bit size did not match (%d to %d) %s on %s" % \
449 (bits, elf.abiSize(), bpn, package_qa_clean_path(path,d)))
450 elif not littleendian == elf.isLittleEndian():
451 messages.append("Endiannes did not match (%d to %d) on %s" % \
452 (littleendian, elf.isLittleEndian(), package_qa_clean_path(path,d)))
453
454QAPATHTEST[desktop] = "package_qa_check_desktop"
455def package_qa_check_desktop(path, name, d, elf, messages):
456 """
457 Run all desktop files through desktop-file-validate.
458 """
459 if path.endswith(".desktop"):
460 desktop_file_validate = os.path.join(d.getVar('STAGING_BINDIR_NATIVE',True),'desktop-file-validate')
461 output = os.popen("%s %s" % (desktop_file_validate, path))
462 # This only produces output on errors
463 for l in output:
464 messages.append("Desktop file issue: " + l.strip())
465
466QAPATHTEST[textrel] = "package_qa_textrel"
467def package_qa_textrel(path, name, d, elf, messages):
468 """
469 Check if the binary contains relocations in .text
470 """
471
472 if not elf:
473 return
474
475 if os.path.islink(path):
476 return
477
478 phdrs = elf.run_objdump("-p", d)
479 sane = True
480
481 import re
482 textrel_re = re.compile("\s+TEXTREL\s+")
483 for line in phdrs.split("\n"):
484 if textrel_re.match(line):
485 sane = False
486
487 if not sane:
488 messages.append("ELF binary '%s' has relocations in .text" % path)
489
490QAPATHTEST[ldflags] = "package_qa_hash_style"
491def package_qa_hash_style(path, name, d, elf, messages):
492 """
493 Check if the binary has the right hash style...
494 """
495
496 if not elf:
497 return
498
499 if os.path.islink(path):
500 return
501
502 gnu_hash = "--hash-style=gnu" in d.getVar('LDFLAGS', True)
503 if not gnu_hash:
504 gnu_hash = "--hash-style=both" in d.getVar('LDFLAGS', True)
505 if not gnu_hash:
506 return
507
508 sane = False
509 has_syms = False
510
511 phdrs = elf.run_objdump("-p", d)
512
513 # If this binary has symbols, we expect it to have GNU_HASH too.
514 for line in phdrs.split("\n"):
515 if "SYMTAB" in line:
516 has_syms = True
517 if "GNU_HASH" in line:
518 sane = True
519 if "[mips32]" in line or "[mips64]" in line:
520 sane = True
521
522 if has_syms and not sane:
523 messages.append("No GNU_HASH in the elf binary: '%s'" % path)
524
525
526QAPATHTEST[buildpaths] = "package_qa_check_buildpaths"
527def package_qa_check_buildpaths(path, name, d, elf, messages):
528 """
529 Check for build paths inside target files and error if not found in the whitelist
530 """
531 # Ignore .debug files, not interesting
532 if path.find(".debug") != -1:
533 return
534
535 # Ignore symlinks
536 if os.path.islink(path):
537 return
538
539 tmpdir = d.getVar('TMPDIR', True)
540 with open(path) as f:
541 file_content = f.read()
542 if tmpdir in file_content:
543 messages.append("File %s in package contained reference to tmpdir" % package_qa_clean_path(path,d))
544
545
546QAPATHTEST[xorg-driver-abi] = "package_qa_check_xorg_driver_abi"
547def package_qa_check_xorg_driver_abi(path, name, d, elf, messages):
548 """
549 Check that all packages containing Xorg drivers have ABI dependencies
550 """
551
552 # Skip dev, dbg or nativesdk packages
553 if name.endswith("-dev") or name.endswith("-dbg") or name.startswith("nativesdk-"):
554 return
555
556 driverdir = d.expand("${libdir}/xorg/modules/drivers/")
557 if driverdir in path and path.endswith(".so"):
558 for rdep in bb.utils.explode_deps(d.getVar('RDEPENDS_' + name, True) or ""):
559 if rdep.startswith("xorg-abi-"):
560 return
561 messages.append("Package %s contains Xorg driver (%s) but no xorg-abi- dependencies" % (name, os.path.basename(path)))
562
563QAPATHTEST[infodir] = "package_qa_check_infodir"
564def package_qa_check_infodir(path, name, d, elf, messages):
565 """
566 Check that /usr/share/info/dir isn't shipped in a particular package
567 """
568 infodir = d.expand("${infodir}/dir")
569
570 if infodir in path:
571 messages.append("The /usr/share/info/dir file is not meant to be shipped in a particular package.")
572
573def package_qa_check_license(workdir, d):
574 """
575 Check for changes in the license files
576 """
577 import tempfile
578 sane = True
579
580 lic_files = d.getVar('LIC_FILES_CHKSUM', True)
581 lic = d.getVar('LICENSE', True)
582 pn = d.getVar('PN', True)
583
584 if lic == "CLOSED":
585 return True
586
587 if not lic_files:
588 bb.error(pn + ": Recipe file does not have license file information (LIC_FILES_CHKSUM)")
589 return False
590
591 srcdir = d.getVar('S', True)
592
593 for url in lic_files.split():
594 (type, host, path, user, pswd, parm) = bb.fetch.decodeurl(url)
595 srclicfile = os.path.join(srcdir, path)
596 if not os.path.isfile(srclicfile):
597 raise bb.build.FuncFailed( pn + ": LIC_FILES_CHKSUM points to an invalid file: " + srclicfile)
598
599 if 'md5' not in parm:
600 bb.error(pn + ": md5 checksum is not specified for ", url)
601 return False
602 beginline, endline = 0, 0
603 if 'beginline' in parm:
604 beginline = int(parm['beginline'])
605 if 'endline' in parm:
606 endline = int(parm['endline'])
607
608 if (not beginline) and (not endline):
609 md5chksum = bb.utils.md5_file(srclicfile)
610 else:
611 fi = open(srclicfile, 'rb')
612 fo = tempfile.NamedTemporaryFile(mode='wb', prefix='poky.', suffix='.tmp', delete=False)
613 tmplicfile = fo.name;
614 lineno = 0
615 linesout = 0
616 for line in fi:
617 lineno += 1
618 if (lineno >= beginline):
619 if ((lineno <= endline) or not endline):
620 fo.write(line)
621 linesout += 1
622 else:
623 break
624 fo.flush()
625 fo.close()
626 fi.close()
627 md5chksum = bb.utils.md5_file(tmplicfile)
628 os.unlink(tmplicfile)
629
630 if parm['md5'] == md5chksum:
631 bb.note (pn + ": md5 checksum matched for ", url)
632 else:
633 bb.error (pn + ": md5 data is not matching for ", url)
634 bb.error (pn + ": The new md5 checksum is ", md5chksum)
635 bb.error (pn + ": Check if the license information has changed in")
636 sane = False
637
638 return sane
639
640def package_qa_check_staged(path,d):
641 """
642 Check staged la and pc files for sanity
643 -e.g. installed being false
644
645 As this is run after every stage we should be able
646 to find the one responsible for the errors easily even
647 if we look at every .pc and .la file
648 """
649
650 sane = True
651 tmpdir = d.getVar('TMPDIR', True)
652 workdir = os.path.join(tmpdir, "work")
653
654 installed = "installed=yes"
655 if bb.data.inherits_class("native", d) or bb.data.inherits_class("cross", d):
656 pkgconfigcheck = workdir
657 else:
658 pkgconfigcheck = tmpdir
659
660 # find all .la and .pc files
661 # read the content
662 # and check for stuff that looks wrong
663 for root, dirs, files in os.walk(path):
664 for file in files:
665 path = os.path.join(root,file)
666 if file.endswith(".la"):
667 with open(path) as f:
668 file_content = f.read()
669 if workdir in file_content:
670 error_msg = "%s failed sanity test (workdir) in path %s" % (file,root)
671 sane = package_qa_handle_error("la", error_msg, d)
672 elif file.endswith(".pc"):
673 with open(path) as f:
674 file_content = f.read()
675 if pkgconfigcheck in file_content:
676 error_msg = "%s failed sanity test (tmpdir) in path %s" % (file,root)
677 sane = package_qa_handle_error("pkgconfig", error_msg, d)
678
679 return sane
680
681# Walk over all files in a directory and call func
682def package_qa_walk(path, warnfuncs, errorfuncs, skip, package, d):
683 import oe.qa
684
685 #if this will throw an exception, then fix the dict above
686 target_os = d.getVar('TARGET_OS', True)
687 target_arch = d.getVar('TARGET_ARCH', True)
688
689 warnings = []
690 errors = []
691 for path in pkgfiles[package]:
692 elf = oe.qa.ELFFile(path)
693 try:
694 elf.open()
695 except:
696 elf = None
697 for func in warnfuncs:
698 func(path, package, d, elf, warnings)
699 for func in errorfuncs:
700 func(path, package, d, elf, errors)
701
702 for w in warnings:
703 bb.warn("QA Issue: %s" % w)
704 package_qa_write_error(w, d)
705 for e in errors:
706 bb.error("QA Issue: %s" % e)
707 package_qa_write_error(e, d)
708
709 return len(errors) == 0
710
711def package_qa_check_rdepends(pkg, pkgdest, skip, d):
712 # Don't do this check for kernel/module recipes, there aren't too many debug/development
713 # packages and you can get false positives e.g. on kernel-module-lirc-dev
714 if bb.data.inherits_class("kernel", d) or bb.data.inherits_class("module-base", d):
715 return True
716
717 sane = True
718 if not "-dbg" in pkg and not "packagegroup-" in pkg and not "-image" in pkg:
719 localdata = bb.data.createCopy(d)
720 localdata.setVar('OVERRIDES', pkg)
721 bb.data.update_data(localdata)
722
723 # Now check the RDEPENDS
724 rdepends = bb.utils.explode_deps(localdata.getVar('RDEPENDS', True) or "")
725
726 # Now do the sanity check!!!
727 for rdepend in rdepends:
728 if "-dbg" in rdepend and "debug-deps" not in skip:
729 error_msg = "%s rdepends on %s" % (pkg,rdepend)
730 sane = package_qa_handle_error("debug-deps", error_msg, d)
731 if (not "-dev" in pkg and not "-staticdev" in pkg) and rdepend.endswith("-dev") and "dev-deps" not in skip:
732 error_msg = "%s rdepends on %s" % (pkg, rdepend)
733 sane = package_qa_handle_error("dev-deps", error_msg, d)
734
735 return sane
736
737def package_qa_check_deps(pkg, pkgdest, skip, d):
738 sane = True
739
740 localdata = bb.data.createCopy(d)
741 localdata.setVar('OVERRIDES', pkg)
742 bb.data.update_data(localdata)
743
744 def check_valid_deps(var):
745 sane = True
746 try:
747 rvar = bb.utils.explode_dep_versions2(localdata.getVar(var, True) or "")
748 except ValueError as e:
749 bb.fatal("%s_%s: %s" % (var, pkg, e))
750 for dep in rvar:
751 for v in rvar[dep]:
752 if v and not v.startswith(('< ', '= ', '> ', '<= ', '>=')):
753 error_msg = "%s_%s is invalid: %s (%s) only comparisons <, =, >, <=, and >= are allowed" % (var, pkg, dep, v)
754 sane = package_qa_handle_error("dep-cmp", error_msg, d)
755 return sane
756
757 sane = True
758 if not check_valid_deps('RDEPENDS'):
759 sane = False
760 if not check_valid_deps('RRECOMMENDS'):
761 sane = False
762 if not check_valid_deps('RSUGGESTS'):
763 sane = False
764 if not check_valid_deps('RPROVIDES'):
765 sane = False
766 if not check_valid_deps('RREPLACES'):
767 sane = False
768 if not check_valid_deps('RCONFLICTS'):
769 sane = False
770
771 return sane
772
773# The PACKAGE FUNC to scan each package
774python do_package_qa () {
775 import subprocess
776
777 bb.note("DO PACKAGE QA")
778
779 logdir = d.getVar('T', True)
780 pkg = d.getVar('PN', True)
781
782 # Check the compile log for host contamination
783 compilelog = os.path.join(logdir,"log.do_compile")
784
785 if os.path.exists(compilelog):
786 statement = "grep -e 'CROSS COMPILE Badness:' -e 'is unsafe for cross-compilation' %s > /dev/null" % compilelog
787 if subprocess.call(statement, shell=True) == 0:
788 msg = "%s: The compile log indicates that host include and/or library paths were used.\n \
789 Please check the log '%s' for more information." % (pkg, compilelog)
790 package_qa_handle_error("compile-host-path", msg, d)
791
792 # Check the install log for host contamination
793 installlog = os.path.join(logdir,"log.do_install")
794
795 if os.path.exists(installlog):
796 statement = "grep -e 'CROSS COMPILE Badness:' -e 'is unsafe for cross-compilation' %s > /dev/null" % installlog
797 if subprocess.call(statement, shell=True) == 0:
798 msg = "%s: The install log indicates that host include and/or library paths were used.\n \
799 Please check the log '%s' for more information." % (pkg, installlog)
800 package_qa_handle_error("install-host-path", msg, d)
801
802 # Scan the packages...
803 pkgdest = d.getVar('PKGDEST', True)
804 packages = d.getVar('PACKAGES', True)
805
806 # no packages should be scanned
807 if not packages:
808 return
809
810 testmatrix = d.getVarFlags("QAPATHTEST")
811 import re
812 # The package name matches the [a-z0-9.+-]+ regular expression
813 pkgname_pattern = re.compile("^[a-z0-9.+-]+$")
814
815 g = globals()
816 walk_sane = True
817 rdepends_sane = True
818 deps_sane = True
819 for package in packages.split():
820 skip = (d.getVar('INSANE_SKIP_' + package, True) or "").split()
821 if skip:
822 bb.note("Package %s skipping QA tests: %s" % (package, str(skip)))
823 warnchecks = []
824 for w in (d.getVar("WARN_QA", True) or "").split():
825 if w in skip:
826 continue
827 if w in testmatrix and testmatrix[w] in g:
828 warnchecks.append(g[testmatrix[w]])
829 errorchecks = []
830 for e in (d.getVar("ERROR_QA", True) or "").split():
831 if e in skip:
832 continue
833 if e in testmatrix and testmatrix[e] in g:
834 errorchecks.append(g[testmatrix[e]])
835
836 bb.note("Checking Package: %s" % package)
837 # Check package name
838 if not pkgname_pattern.match(package):
839 package_qa_handle_error("pkgname",
840 "%s doesn't match the [a-z0-9.+-]+ regex\n" % package, d)
841
842 path = "%s/%s" % (pkgdest, package)
843 if not package_qa_walk(path, warnchecks, errorchecks, skip, package, d):
844 walk_sane = False
845 if not package_qa_check_rdepends(package, pkgdest, skip, d):
846 rdepends_sane = False
847 if not package_qa_check_deps(package, pkgdest, skip, d):
848 deps_sane = False
849
850
851 if 'libdir' in d.getVar("ALL_QA", True).split():
852 package_qa_check_libdir(d)
853
854 qa_sane = d.getVar("QA_SANE", True)
855 if not walk_sane or not rdepends_sane or not deps_sane or not qa_sane:
856 bb.fatal("QA run found fatal errors. Please consider fixing them.")
857 bb.note("DONE with PACKAGE QA")
858}
859
860
861python do_qa_staging() {
862 bb.note("QA checking staging")
863
864 if not package_qa_check_staged(d.expand('${SYSROOT_DESTDIR}/${STAGING_LIBDIR}'), d):
865 bb.fatal("QA staging was broken by the package built above")
866}
867
868python do_qa_configure() {
869 import subprocess
870
871 ###########################################################################
872 # Check config.log for cross compile issues
873 ###########################################################################
874
875 configs = []
876 workdir = d.getVar('WORKDIR', True)
877 bb.note("Checking autotools environment for common misconfiguration")
878 for root, dirs, files in os.walk(workdir):
879 statement = "grep -e 'CROSS COMPILE Badness:' -e 'is unsafe for cross-compilation' %s > /dev/null" % \
880 os.path.join(root,"config.log")
881 if "config.log" in files:
882 if subprocess.call(statement, shell=True) == 0:
883 bb.fatal("""This autoconf log indicates errors, it looked at host include and/or library paths while determining system capabilities.
884Rerun configure task after fixing this. The path was '%s'""" % root)
885
886 if "configure.ac" in files:
887 configs.append(os.path.join(root,"configure.ac"))
888 if "configure.in" in files:
889 configs.append(os.path.join(root, "configure.in"))
890
891 ###########################################################################
892 # Check gettext configuration and dependencies are correct
893 ###########################################################################
894
895 cnf = d.getVar('EXTRA_OECONF', True) or ""
896 if "gettext" not in d.getVar('P', True) and "gcc-runtime" not in d.getVar('P', True) and "--disable-nls" not in cnf:
897 ml = d.getVar("MLPREFIX", True) or ""
898 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):
899 gt = "gettext-native"
900 elif bb.data.inherits_class('cross-canadian', d):
901 gt = "nativesdk-gettext"
902 else:
903 gt = "virtual/" + ml + "gettext"
904 deps = bb.utils.explode_deps(d.getVar('DEPENDS', True) or "")
905 if gt not in deps:
906 for config in configs:
907 gnu = "grep \"^[[:space:]]*AM_GNU_GETTEXT\" %s >/dev/null" % config
908 if subprocess.call(gnu, shell=True) == 0:
909 bb.fatal("""%s required but not in DEPENDS for file %s.
910Missing inherit gettext?""" % (gt, config))
911
912 ###########################################################################
913 # Check license variables
914 ###########################################################################
915
916 if not package_qa_check_license(workdir, d):
917 bb.fatal("Licensing Error: LIC_FILES_CHKSUM does not match, please fix")
918
919}
920# The Staging Func, to check all staging
921#addtask qa_staging after do_populate_sysroot before do_build
922do_populate_sysroot[postfuncs] += "do_qa_staging "
923
924# Check broken config.log files, for packages requiring Gettext which don't
925# have it in DEPENDS and for correct LIC_FILES_CHKSUM
926#addtask qa_configure after do_configure before do_compile
927do_configure[postfuncs] += "do_qa_configure "
928
929python () {
930 tests = d.getVar('ALL_QA', True).split()
931 if "desktop" in tests:
932 d.appendVar("PACKAGE_DEPENDS", "desktop-file-utils-native")
933
934 ###########################################################################
935 # Check various variables
936 ###########################################################################
937
938 if d.getVar('do_stage', True) is not None:
939 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))
940
941 overrides = d.getVar('OVERRIDES', True).split(':')
942 pn = d.getVar('PN', True)
943 if pn in overrides:
944 msg = 'Recipe %s has PN of "%s" which is in OVERRIDES, this can result in unexpected behaviour.' % (d.getVar("FILE", True), pn)
945 package_qa_handle_error("pn-overrides", msg, d)
946
947 issues = []
948 if (d.getVar('PACKAGES', True) or "").split():
949 for var in 'RDEPENDS', 'RRECOMMENDS', 'RSUGGESTS', 'RCONFLICTS', 'RPROVIDES', 'RREPLACES', 'FILES', 'pkg_preinst', 'pkg_postinst', 'pkg_prerm', 'pkg_postrm', 'ALLOW_EMPTY':
950 if d.getVar(var):
951 issues.append(var)
952 for i in issues:
953 package_qa_handle_error("pkgvarcheck", "%s: Variable %s is set as not being package specific, please fix this." % (d.getVar("FILE", True), i), d)
954}