summaryrefslogtreecommitdiff
path: root/meta/classes/insane.bbclass
diff options
context:
space:
mode:
Diffstat (limited to 'meta/classes/insane.bbclass')
-rw-r--r--meta/classes/insane.bbclass1366
1 files changed, 1282 insertions, 84 deletions
diff --git a/meta/classes/insane.bbclass b/meta/classes/insane.bbclass
index 4bceaaed82..dc1c2f8d41 100644
--- a/meta/classes/insane.bbclass
+++ b/meta/classes/insane.bbclass
@@ -8,147 +8,1345 @@
# -Check the RUNTIME path for the $TMPDIR
# -Check if .la files wrongly point to workdir
# -Check if .pc files wrongly point to workdir
-# -Check if packages contains .debug directories or .so files where they should be in -dev or -dbg
-#
+# -Check if packages contains .debug directories or .so files
+# where they should be in -dev or -dbg
+# -Check if config.log contains traces to broken autoconf tests
+# -Check invalid characters (non-utf8) on some package metadata
+# -Ensure that binaries in base_[bindir|sbindir|libdir] do not link
+# into exec_prefix
+# -Check that scripts in base_[bindir|sbindir|libdir] do not reference
+# files under exec_prefix
+
+
+# unsafe-references-in-binaries requires prelink-rtld from
+# prelink-native, but we don't want this DEPENDS for -native builds
+QADEPENDS = "prelink-native"
+QADEPENDS_class-native = ""
+QADEPENDS_class-nativesdk = ""
+QA_SANE = "True"
+
+# Elect whether a given type of error is a warning or error, they may
+# have been set by other files.
+WARN_QA ?= "ldflags useless-rpaths rpaths staticdev libdir xorg-driver-abi \
+ textrel already-stripped incompatible-license files-invalid \
+ installed-vs-shipped compile-host-path install-host-path \
+ pn-overrides infodir build-deps \
+ unknown-configure-option symlink-to-sysroot multilib \
+ invalid-packageconfig host-user-contaminated \
+ "
+ERROR_QA ?= "dev-so debug-deps dev-deps debug-files arch pkgconfig la \
+ perms dep-cmp pkgvarcheck perm-config perm-line perm-link \
+ split-strip packages-list pkgv-undefined var-undefined \
+ version-going-backwards expanded-d invalid-chars \
+ license-checksum dev-elf file-rdeps \
+ "
+FAKEROOT_QA = "host-user-contaminated"
+FAKEROOT_QA[doc] = "QA tests which need to run under fakeroot. If any \
+enabled tests are listed here, the do_package_qa task will run under fakeroot."
+ALL_QA = "${WARN_QA} ${ERROR_QA}"
+UNKNOWN_CONFIGURE_WHITELIST ?= "--enable-nls --disable-nls --disable-silent-rules --disable-dependency-tracking --with-libtool-sysroot --disable-static"
+
+#
+# dictionary for elf headers
#
-# We need to have the scanelf utility as soon as
-# possible and this is contained within the pax-utils-native
+# feel free to add and correct.
#
+# TARGET_OS TARGET_ARCH MACHINE, OSABI, ABIVERSION, Little Endian, 32bit?
+def package_qa_get_machine_dict(d):
+ machdata = {
+ "darwin9" : {
+ "arm" : (40, 0, 0, True, 32),
+ },
+ "eabi" : {
+ "arm" : (40, 0, 0, True, 32),
+ },
+ "elf" : {
+ "aarch64" : (183, 0, 0, True, 64),
+ "aarch64_be" :(183, 0, 0, False, 64),
+ "i586" : (3, 0, 0, True, 32),
+ "x86_64": (62, 0, 0, True, 64),
+ "epiphany": (4643, 0, 0, True, 32),
+ "mips": ( 8, 0, 0, False, 32),
+ "mipsel": ( 8, 0, 0, True, 32),
+ },
+ "linux" : {
+ "aarch64" : (183, 0, 0, True, 64),
+ "aarch64_be" :(183, 0, 0, False, 64),
+ "arm" : (40, 97, 0, True, 32),
+ "armeb": (40, 97, 0, False, 32),
+ "powerpc": (20, 0, 0, False, 32),
+ "powerpc64": (21, 0, 0, False, 64),
+ "i386": ( 3, 0, 0, True, 32),
+ "i486": ( 3, 0, 0, True, 32),
+ "i586": ( 3, 0, 0, True, 32),
+ "i686": ( 3, 0, 0, True, 32),
+ "x86_64": (62, 0, 0, True, 64),
+ "ia64": (50, 0, 0, True, 64),
+ "alpha": (36902, 0, 0, True, 64),
+ "hppa": (15, 3, 0, False, 32),
+ "m68k": ( 4, 0, 0, False, 32),
+ "mips": ( 8, 0, 0, False, 32),
+ "mipsel": ( 8, 0, 0, True, 32),
+ "mips64": ( 8, 0, 0, False, 64),
+ "mips64el": ( 8, 0, 0, True, 64),
+ "mipsisa32r6": ( 8, 0, 0, False, 32),
+ "mipsisa32r6el": ( 8, 0, 0, True, 32),
+ "mipsisa64r6": ( 8, 0, 0, False, 64),
+ "mipsisa64r6el": ( 8, 0, 0, True, 64),
+ "nios2": (113, 0, 0, True, 32),
+ "s390": (22, 0, 0, False, 32),
+ "sh4": (42, 0, 0, True, 32),
+ "sparc": ( 2, 0, 0, False, 32),
+ "microblaze": (189, 0, 0, False, 32),
+ "microblazeeb":(189, 0, 0, False, 32),
+ "microblazeel":(189, 0, 0, True, 32),
+ },
+ "linux-uclibc" : {
+ "arm" : ( 40, 97, 0, True, 32),
+ "armeb": ( 40, 97, 0, False, 32),
+ "powerpc": ( 20, 0, 0, False, 32),
+ "i386": ( 3, 0, 0, True, 32),
+ "i486": ( 3, 0, 0, True, 32),
+ "i586": ( 3, 0, 0, True, 32),
+ "i686": ( 3, 0, 0, True, 32),
+ "x86_64": ( 62, 0, 0, True, 64),
+ "mips": ( 8, 0, 0, False, 32),
+ "mipsel": ( 8, 0, 0, True, 32),
+ "mips64": ( 8, 0, 0, False, 64),
+ "mips64el": ( 8, 0, 0, True, 64),
+ "avr32": (6317, 0, 0, False, 32),
+ "sh4": (42, 0, 0, True, 32),
+
+ },
+ "linux-musl" : {
+ "aarch64" : (183, 0, 0, True, 64),
+ "aarch64_be" :(183, 0, 0, False, 64),
+ "arm" : ( 40, 97, 0, True, 32),
+ "armeb": ( 40, 97, 0, False, 32),
+ "powerpc": ( 20, 0, 0, False, 32),
+ "i386": ( 3, 0, 0, True, 32),
+ "i486": ( 3, 0, 0, True, 32),
+ "i586": ( 3, 0, 0, True, 32),
+ "i686": ( 3, 0, 0, True, 32),
+ "x86_64": ( 62, 0, 0, True, 64),
+ "mips": ( 8, 0, 0, False, 32),
+ "mipsel": ( 8, 0, 0, True, 32),
+ "mips64": ( 8, 0, 0, False, 64),
+ "mips64el": ( 8, 0, 0, True, 64),
+ "microblaze": (189, 0, 0, False, 32),
+ "microblazeeb":(189, 0, 0, False, 32),
+ "microblazeel":(189, 0, 0, True, 32),
+ "sh4": ( 42, 0, 0, True, 32),
+ },
+ "uclinux-uclibc" : {
+ "bfin": ( 106, 0, 0, True, 32),
+ },
+ "linux-gnueabi" : {
+ "arm" : (40, 0, 0, True, 32),
+ "armeb" : (40, 0, 0, False, 32),
+ },
+ "linux-musleabi" : {
+ "arm" : (40, 0, 0, True, 32),
+ "armeb" : (40, 0, 0, False, 32),
+ },
+ "linux-uclibceabi" : {
+ "arm" : (40, 0, 0, True, 32),
+ "armeb" : (40, 0, 0, False, 32),
+ },
+ "linux-gnuspe" : {
+ "powerpc": (20, 0, 0, False, 32),
+ },
+ "linux-muslspe" : {
+ "powerpc": (20, 0, 0, False, 32),
+ },
+ "linux-uclibcspe" : {
+ "powerpc": (20, 0, 0, False, 32),
+ },
+ "linux-gnu" : {
+ "powerpc": (20, 0, 0, False, 32),
+ "sh4": (42, 0, 0, True, 32),
+ },
+ "linux-gnux32" : {
+ "x86_64": (62, 0, 0, True, 32),
+ },
+ "linux-gnun32" : {
+ "mips64": ( 8, 0, 0, False, 32),
+ "mips64el": ( 8, 0, 0, True, 32),
+ "mipsisa64r6": ( 8, 0, 0, False, 32),
+ "mipsisa64r6el":( 8, 0, 0, True, 32),
+ },
+ }
+
+ # Add in any extra user supplied data which may come from a BSP layer, removing the
+ # need to always change this class directly
+ extra_machdata = (d.getVar("PACKAGEQA_EXTRA_MACHDEFFUNCS") or "").split()
+ for m in extra_machdata:
+ call = m + "(machdata, d)"
+ locs = { "machdata" : machdata, "d" : d}
+ machdata = bb.utils.better_eval(call, locs)
+ return machdata
-# We play a special package function
-inherit package
-PACKAGE_DEPENDS += "pax-utils-native"
-PACKAGEFUNCS += " do_package_qa "
-def package_qa_check_rpath(file,name,d):
+def package_qa_clean_path(path, d, pkg=None):
+ """
+ Remove redundant paths from the path for display. If pkg isn't set then
+ TMPDIR is stripped, otherwise PKGDEST/pkg is stripped.
+ """
+ if pkg:
+ path = path.replace(os.path.join(d.getVar("PKGDEST"), pkg), "/")
+ return path.replace(d.getVar("TMPDIR"), "/").replace("//", "/")
+
+def package_qa_write_error(type, error, d):
+ logfile = d.getVar('QA_LOGFILE')
+ if logfile:
+ p = d.getVar('P')
+ with open(logfile, "a+") as f:
+ f.write("%s: %s [%s]\n" % (p, error, type))
+
+def package_qa_handle_error(error_class, error_msg, d):
+ package_qa_write_error(error_class, error_msg, d)
+ if error_class in (d.getVar("ERROR_QA") or "").split():
+ bb.error("QA Issue: %s [%s]" % (error_msg, error_class))
+ d.setVar("QA_SANE", False)
+ return False
+ elif error_class in (d.getVar("WARN_QA") or "").split():
+ bb.warn("QA Issue: %s [%s]" % (error_msg, error_class))
+ else:
+ bb.note("QA Issue: %s [%s]" % (error_msg, error_class))
+ return True
+
+def package_qa_add_message(messages, section, new_msg):
+ if section not in messages:
+ messages[section] = new_msg
+ else:
+ messages[section] = messages[section] + "\n" + new_msg
+
+QAPATHTEST[libexec] = "package_qa_check_libexec"
+def package_qa_check_libexec(path,name, d, elf, messages):
+
+ # Skip the case where the default is explicitly /usr/libexec
+ libexec = d.getVar('libexecdir')
+ if libexec == "/usr/libexec":
+ return True
+
+ if 'libexec' in path.split(os.path.sep):
+ package_qa_add_message(messages, "libexec", "%s: %s is using libexec please relocate to %s" % (name, package_qa_clean_path(path, d), libexec))
+ return False
+
+ return True
+
+QAPATHTEST[rpaths] = "package_qa_check_rpath"
+def package_qa_check_rpath(file,name, d, elf, messages):
"""
Check for dangerous RPATHs
"""
- import bb, os
- scanelf = os.path.join(bb.data.getVar('STAGING_BINDIR_NATIVE',d,True),'scanelf')
- bad_dir = bb.data.getVar('TMPDIR', d, True) + "/work"
- if not os.path.exists(scanelf):
- bb.note("Can not check RPATH scanelf not found")
- if not bad_dir in bb.data.getVar('WORKDIR', d, True):
- bb.fatal("This class assumed that WORKDIR is ${TMPDIR}/work... Not doing any check")
+ if not elf:
+ return
+
+ if os.path.islink(file):
+ return
+
+ bad_dirs = [d.getVar('BASE_WORKDIR'), d.getVar('STAGING_DIR_TARGET')]
- output = os.popen("%s -Byr %s" % (scanelf,file))
- txt = output.readline().rsplit()
- if bad_dir in txt:
- bb.fatal("QA Issue package %s contains bad RPATH %s in file %s" % (name, txt, file))
+ phdrs = elf.run_objdump("-p", d)
- pass
+ import re
+ rpath_re = re.compile("\s+RPATH\s+(.*)")
+ for line in phdrs.split("\n"):
+ m = rpath_re.match(line)
+ if m:
+ rpath = m.group(1)
+ for dir in bad_dirs:
+ if dir in rpath:
+ package_qa_add_message(messages, "rpaths", "package %s contains bad RPATH %s in file %s" % (name, rpath, file))
-def package_qa_check_devdbg(path, name,d):
+QAPATHTEST[useless-rpaths] = "package_qa_check_useless_rpaths"
+def package_qa_check_useless_rpaths(file, name, d, elf, messages):
"""
- Check for debug remains inside the binary or
- non dev packages containing
+ Check for RPATHs that are useless but not dangerous
"""
+ def rpath_eq(a, b):
+ return os.path.normpath(a) == os.path.normpath(b)
- import bb, os
- if not "-dev" in name:
- if path[-3:] == ".so" and os.path.islink(path):
- bb.fatal("QA Issue: non -dev package %s contains symlink .so: %s" % (name, os.path.basename (path)))
+ if not elf:
+ return
+
+ if os.path.islink(file):
+ return
+
+ libdir = d.getVar("libdir")
+ base_libdir = d.getVar("base_libdir")
- if not "-dbg" in name:
- if '.debug' in path:
- bb.fatal("QA Issue: non -dbg package contains .debug directory: %s" % name)
+ phdrs = elf.run_objdump("-p", d)
-def package_qa_check_perm(path,name,d):
+ import re
+ rpath_re = re.compile("\s+RPATH\s+(.*)")
+ for line in phdrs.split("\n"):
+ m = rpath_re.match(line)
+ if m:
+ rpath = m.group(1)
+ if rpath_eq(rpath, libdir) or rpath_eq(rpath, base_libdir):
+ # The dynamic linker searches both these places anyway. There is no point in
+ # looking there again.
+ package_qa_add_message(messages, "useless-rpaths", "%s: %s contains probably-redundant RPATH %s" % (name, package_qa_clean_path(file, d), rpath))
+
+QAPATHTEST[dev-so] = "package_qa_check_dev"
+def package_qa_check_dev(path, name, d, elf, messages):
+ """
+ Check for ".so" library symlinks in non-dev packages
+ """
+
+ 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):
+ package_qa_add_message(messages, "dev-so", "non -dev/-dbg/nativesdk- package contains symlink .so: %s path '%s'" % \
+ (name, package_qa_clean_path(path,d)))
+
+QAPATHTEST[dev-elf] = "package_qa_check_dev_elf"
+def package_qa_check_dev_elf(path, name, d, elf, messages):
+ """
+ Check that -dev doesn't contain real shared libraries. The test has to
+ check that the file is not a link and is an ELF object as some recipes
+ install link-time .so files that are linker scripts.
+ """
+ if name.endswith("-dev") and path.endswith(".so") and not os.path.islink(path) and elf:
+ package_qa_add_message(messages, "dev-elf", "-dev package contains non-symlink .so: %s path '%s'" % \
+ (name, package_qa_clean_path(path,d)))
+
+QAPATHTEST[staticdev] = "package_qa_check_staticdev"
+def package_qa_check_staticdev(path, name, d, elf, messages):
+ """
+ Check for ".a" library in non-staticdev packages
+ There are a number of exceptions to this rule, -pic packages can contain
+ static libraries, the _nonshared.a belong with their -dev packages and
+ libgcc.a, libgcov.a will be skipped in their packages
+ """
+
+ 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"):
+ package_qa_add_message(messages, "staticdev", "non -staticdev package contains static .a library: %s path '%s'" % \
+ (name, package_qa_clean_path(path,d)))
+
+def package_qa_check_libdir(d):
+ """
+ Check for wrong library installation paths. For instance, catch
+ recipes installing /lib/bar.so when ${base_libdir}="lib32" or
+ installing in /usr/lib64 when ${libdir}="/usr/lib"
+ """
+ import re
+
+ pkgdest = d.getVar('PKGDEST')
+ base_libdir = d.getVar("base_libdir") + os.sep
+ libdir = d.getVar("libdir") + os.sep
+ libexecdir = d.getVar("libexecdir") + os.sep
+ exec_prefix = d.getVar("exec_prefix") + os.sep
+
+ messages = []
+
+ # The re's are purposely fuzzy, as some there are some .so.x.y.z files
+ # that don't follow the standard naming convention. It checks later
+ # that they are actual ELF files
+ lib_re = re.compile("^/lib.+\.so(\..+)?$")
+ exec_re = re.compile("^%s.*/lib.+\.so(\..+)?$" % exec_prefix)
+
+ for root, dirs, files in os.walk(pkgdest):
+ if root == pkgdest:
+ # Skip subdirectories for any packages with libdir in INSANE_SKIP
+ skippackages = []
+ for package in dirs:
+ if 'libdir' in (d.getVar('INSANE_SKIP_' + package) or "").split():
+ bb.note("Package %s skipping libdir QA test" % (package))
+ skippackages.append(package)
+ elif d.getVar('PACKAGE_DEBUG_SPLIT_STYLE') == 'debug-file-directory' and package.endswith("-dbg"):
+ bb.note("Package %s skipping libdir QA test for PACKAGE_DEBUG_SPLIT_STYLE equals debug-file-directory" % (package))
+ skippackages.append(package)
+ for package in skippackages:
+ dirs.remove(package)
+ for file in files:
+ full_path = os.path.join(root, file)
+ rel_path = os.path.relpath(full_path, pkgdest)
+ if os.sep in rel_path:
+ package, rel_path = rel_path.split(os.sep, 1)
+ rel_path = os.sep + rel_path
+ if lib_re.match(rel_path):
+ if base_libdir not in rel_path:
+ # make sure it's an actual ELF file
+ elf = oe.qa.ELFFile(full_path)
+ try:
+ elf.open()
+ messages.append("%s: found library in wrong location: %s" % (package, rel_path))
+ except (oe.qa.NotELFFileError):
+ pass
+ if exec_re.match(rel_path):
+ if libdir not in rel_path and libexecdir not in rel_path:
+ # make sure it's an actual ELF file
+ elf = oe.qa.ELFFile(full_path)
+ try:
+ elf.open()
+ messages.append("%s: found library in wrong location: %s" % (package, rel_path))
+ except (oe.qa.NotELFFileError):
+ pass
+
+ if messages:
+ package_qa_handle_error("libdir", "\n".join(messages), d)
+
+QAPATHTEST[debug-files] = "package_qa_check_dbg"
+def package_qa_check_dbg(path, name, d, elf, messages):
+ """
+ Check for ".debug" files or directories outside of the dbg package
+ """
+
+ if not "-dbg" in name and not "-ptest" in name:
+ if '.debug' in path.split(os.path.sep):
+ package_qa_add_message(messages, "debug-files", "non debug package contains .debug directory: %s path %s" % \
+ (name, package_qa_clean_path(path,d)))
+
+QAPATHTEST[perms] = "package_qa_check_perm"
+def package_qa_check_perm(path,name,d, elf, messages):
"""
Check the permission of files
"""
- pass
+ return
-def package_qa_check_arch(path,name,d):
+QAPATHTEST[unsafe-references-in-scripts] = "package_qa_check_unsafe_references_in_scripts"
+def package_qa_check_unsafe_references_in_scripts(path, name, d, elf, messages):
+ """
+ Warn if scripts in base_[bindir|sbindir|libdir] reference files under exec_prefix
+ """
+ if unsafe_references_skippable(path, name, d):
+ return
+
+ if not elf:
+ import stat
+ import subprocess
+ pn = d.getVar('PN')
+
+ # Ensure we're checking an executable script
+ statinfo = os.stat(path)
+ if bool(statinfo.st_mode & stat.S_IXUSR):
+ # grep shell scripts for possible references to /exec_prefix/
+ exec_prefix = d.getVar('exec_prefix')
+ statement = "grep -e '%s/[^ :]\{1,\}/[^ :]\{1,\}' %s > /dev/null" % (exec_prefix, path)
+ if subprocess.call(statement, shell=True) == 0:
+ error_msg = pn + ": Found a reference to %s/ in %s" % (exec_prefix, path)
+ package_qa_handle_error("unsafe-references-in-scripts", error_msg, d)
+ error_msg = "Shell scripts in base_bindir and base_sbindir should not reference anything in exec_prefix"
+ package_qa_handle_error("unsafe-references-in-scripts", error_msg, d)
+
+def unsafe_references_skippable(path, name, d):
+ if bb.data.inherits_class('native', d) or bb.data.inherits_class('nativesdk', d):
+ return True
+
+ if "-dbg" in name or "-dev" in name:
+ return True
+
+ # Other package names to skip:
+ if name.startswith("kernel-module-"):
+ return True
+
+ # Skip symlinks
+ if os.path.islink(path):
+ return True
+
+ # Skip unusual rootfs layouts which make these tests irrelevant
+ exec_prefix = d.getVar('exec_prefix')
+ if exec_prefix == "":
+ return True
+
+ pkgdest = d.getVar('PKGDEST')
+ pkgdest = pkgdest + "/" + name
+ pkgdest = os.path.abspath(pkgdest)
+ base_bindir = pkgdest + d.getVar('base_bindir')
+ base_sbindir = pkgdest + d.getVar('base_sbindir')
+ base_libdir = pkgdest + d.getVar('base_libdir')
+ bindir = pkgdest + d.getVar('bindir')
+ sbindir = pkgdest + d.getVar('sbindir')
+ libdir = pkgdest + d.getVar('libdir')
+
+ if base_bindir == bindir and base_sbindir == sbindir and base_libdir == libdir:
+ return True
+
+ # Skip files not in base_[bindir|sbindir|libdir]
+ path = os.path.abspath(path)
+ if not (base_bindir in path or base_sbindir in path or base_libdir in path):
+ return True
+
+ return False
+
+QAPATHTEST[arch] = "package_qa_check_arch"
+def package_qa_check_arch(path,name,d, elf, messages):
"""
Check if archs are compatible
"""
- pass
+ import re
+
+ if not elf:
+ return
+
+ target_os = d.getVar('TARGET_OS')
+ target_arch = d.getVar('TARGET_ARCH')
+ provides = d.getVar('PROVIDES')
+ bpn = d.getVar('BPN')
+
+ if target_arch == "allarch":
+ pn = d.getVar('PN')
+ package_qa_add_message(messages, "arch", pn + ": Recipe inherits the allarch class, but has packaged architecture-specific binaries")
+ return
+
+ # FIXME: Cross package confuse this check, so just skip them
+ for s in ['cross', 'nativesdk', 'cross-canadian']:
+ if bb.data.inherits_class(s, d):
+ return
+
+ # avoid following links to /usr/bin (e.g. on udev builds)
+ # we will check the files pointed to anyway...
+ if os.path.islink(path):
+ return
+
+ #if this will throw an exception, then fix the dict above
+ (machine, osabi, abiversion, littleendian, bits) \
+ = package_qa_get_machine_dict(d)[target_os][target_arch]
+
+ # Check the architecture and endiannes of the binary
+ is_32 = (("virtual/kernel" in provides) or bb.data.inherits_class("module", d)) and \
+ (target_os == "linux-gnux32" or re.match('mips64.*32', d.getVar('DEFAULTTUNE')))
+ if not ((machine == elf.machine()) or is_32):
+ package_qa_add_message(messages, "arch", "Architecture did not match (%s, expected %s) on %s" % \
+ (oe.qa.elf_machine_to_string(elf.machine()), oe.qa.elf_machine_to_string(machine), package_qa_clean_path(path,d)))
+ elif not ((bits == elf.abiSize()) or is_32):
+ package_qa_add_message(messages, "arch", "Bit size did not match (%d to %d) %s on %s" % \
+ (bits, elf.abiSize(), bpn, package_qa_clean_path(path,d)))
+ elif not littleendian == elf.isLittleEndian():
+ package_qa_add_message(messages, "arch", "Endiannes did not match (%d to %d) on %s" % \
+ (littleendian, elf.isLittleEndian(), package_qa_clean_path(path,d)))
+
+QAPATHTEST[desktop] = "package_qa_check_desktop"
+def package_qa_check_desktop(path, name, d, elf, messages):
+ """
+ Run all desktop files through desktop-file-validate.
+ """
+ if path.endswith(".desktop"):
+ desktop_file_validate = os.path.join(d.getVar('STAGING_BINDIR_NATIVE'),'desktop-file-validate')
+ output = os.popen("%s %s" % (desktop_file_validate, path))
+ # This only produces output on errors
+ for l in output:
+ package_qa_add_message(messages, "desktop", "Desktop file issue: " + l.strip())
+
+QAPATHTEST[textrel] = "package_qa_textrel"
+def package_qa_textrel(path, name, d, elf, messages):
+ """
+ Check if the binary contains relocations in .text
+ """
+
+ if not elf:
+ return
+
+ if os.path.islink(path):
+ return
+
+ phdrs = elf.run_objdump("-p", d)
+ sane = True
+
+ import re
+ textrel_re = re.compile("\s+TEXTREL\s+")
+ for line in phdrs.split("\n"):
+ if textrel_re.match(line):
+ sane = False
+
+ if not sane:
+ package_qa_add_message(messages, "textrel", "ELF binary '%s' has relocations in .text" % path)
+
+QAPATHTEST[ldflags] = "package_qa_hash_style"
+def package_qa_hash_style(path, name, d, elf, messages):
+ """
+ Check if the binary has the right hash style...
+ """
+
+ if not elf:
+ return
+
+ if os.path.islink(path):
+ return
+
+ gnu_hash = "--hash-style=gnu" in d.getVar('LDFLAGS')
+ if not gnu_hash:
+ gnu_hash = "--hash-style=both" in d.getVar('LDFLAGS')
+ if not gnu_hash:
+ return
+
+ sane = False
+ has_syms = False
+
+ phdrs = elf.run_objdump("-p", d)
+
+ # If this binary has symbols, we expect it to have GNU_HASH too.
+ for line in phdrs.split("\n"):
+ if "SYMTAB" in line:
+ has_syms = True
+ if "GNU_HASH" in line:
+ sane = True
+ if "[mips32]" in line or "[mips64]" in line:
+ sane = True
+
+ if has_syms and not sane:
+ package_qa_add_message(messages, "ldflags", "No GNU_HASH in the elf binary: '%s'" % path)
+
+
+QAPATHTEST[buildpaths] = "package_qa_check_buildpaths"
+def package_qa_check_buildpaths(path, name, d, elf, messages):
+ """
+ Check for build paths inside target files and error if not found in the whitelist
+ """
+ # Ignore .debug files, not interesting
+ if path.find(".debug") != -1:
+ return
+
+ # Ignore symlinks
+ if os.path.islink(path):
+ return
+
+ # Ignore ipk and deb's CONTROL dir
+ if path.find(name + "/CONTROL/") != -1 or path.find(name + "/DEBIAN/") != -1:
+ return
+
+ tmpdir = d.getVar('TMPDIR')
+ with open(path, 'rb') as f:
+ file_content = f.read().decode('utf-8', errors='ignore')
+ if tmpdir in file_content:
+ package_qa_add_message(messages, "buildpaths", "File %s in package contained reference to tmpdir" % package_qa_clean_path(path,d))
+
+
+QAPATHTEST[xorg-driver-abi] = "package_qa_check_xorg_driver_abi"
+def package_qa_check_xorg_driver_abi(path, name, d, elf, messages):
+ """
+ Check that all packages containing Xorg drivers have ABI dependencies
+ """
+
+ # Skip dev, dbg or nativesdk packages
+ if name.endswith("-dev") or name.endswith("-dbg") or name.startswith("nativesdk-"):
+ return
+
+ driverdir = d.expand("${libdir}/xorg/modules/drivers/")
+ if driverdir in path and path.endswith(".so"):
+ mlprefix = d.getVar('MLPREFIX') or ''
+ for rdep in bb.utils.explode_deps(d.getVar('RDEPENDS_' + name) or ""):
+ if rdep.startswith("%sxorg-abi-" % mlprefix):
+ return
+ package_qa_add_message(messages, "xorg-driver-abi", "Package %s contains Xorg driver (%s) but no xorg-abi- dependencies" % (name, os.path.basename(path)))
-def package_qa_check_pcla(path,name,d):
+QAPATHTEST[infodir] = "package_qa_check_infodir"
+def package_qa_check_infodir(path, name, d, elf, messages):
"""
- .pc and .la files should not point
+ Check that /usr/share/info/dir isn't shipped in a particular package
"""
+ infodir = d.expand("${infodir}/dir")
+
+ if infodir in path:
+ package_qa_add_message(messages, "infodir", "The /usr/share/info/dir file is not meant to be shipped in a particular package.")
+
+QAPATHTEST[symlink-to-sysroot] = "package_qa_check_symlink_to_sysroot"
+def package_qa_check_symlink_to_sysroot(path, name, d, elf, messages):
+ """
+ Check that the package doesn't contain any absolute symlinks to the sysroot.
+ """
+ if os.path.islink(path):
+ target = os.readlink(path)
+ if os.path.isabs(target):
+ tmpdir = d.getVar('TMPDIR')
+ if target.startswith(tmpdir):
+ trimmed = path.replace(os.path.join (d.getVar("PKGDEST"), name), "")
+ package_qa_add_message(messages, "symlink-to-sysroot", "Symlink %s in %s points to TMPDIR" % (trimmed, name))
+
+# Check license variables
+do_populate_lic[postfuncs] += "populate_lic_qa_checksum"
+python populate_lic_qa_checksum() {
+ """
+ Check for changes in the license files.
+ """
+ import tempfile
+ sane = True
+
+ lic_files = d.getVar('LIC_FILES_CHKSUM') or ''
+ lic = d.getVar('LICENSE')
+ pn = d.getVar('PN')
+
+ if lic == "CLOSED":
+ return
+
+ if not lic_files and d.getVar('SRC_URI'):
+ sane = package_qa_handle_error("license-checksum", pn + ": Recipe file fetches files and does not have license file information (LIC_FILES_CHKSUM)", d)
+
+ srcdir = d.getVar('S')
+
+ for url in lic_files.split():
+ try:
+ (type, host, path, user, pswd, parm) = bb.fetch.decodeurl(url)
+ except bb.fetch.MalformedUrl:
+ sane = package_qa_handle_error("license-checksum", pn + ": LIC_FILES_CHKSUM contains an invalid URL: " + url, d)
+ continue
+ srclicfile = os.path.join(srcdir, path)
+ if not os.path.isfile(srclicfile):
+ package_qa_handle_error("license-checksum", pn + ": LIC_FILES_CHKSUM points to an invalid file: " + srclicfile, d)
+ continue
+
+ recipemd5 = parm.get('md5', '')
+ beginline, endline = 0, 0
+ if 'beginline' in parm:
+ beginline = int(parm['beginline'])
+ if 'endline' in parm:
+ endline = int(parm['endline'])
+
+ if (not beginline) and (not endline):
+ md5chksum = bb.utils.md5_file(srclicfile)
+ with open(srclicfile, 'rb') as f:
+ license = f.read()
+ else:
+ fi = open(srclicfile, 'rb')
+ fo = tempfile.NamedTemporaryFile(mode='wb', prefix='poky.', suffix='.tmp', delete=False)
+ tmplicfile = fo.name;
+ lineno = 0
+ linesout = 0
+ license = []
+ for line in fi:
+ lineno += 1
+ if (lineno >= beginline):
+ if ((lineno <= endline) or not endline):
+ fo.write(line)
+ license.append(line)
+ linesout += 1
+ else:
+ break
+ fo.flush()
+ fo.close()
+ fi.close()
+ md5chksum = bb.utils.md5_file(tmplicfile)
+ license = b''.join(license)
+ os.unlink(tmplicfile)
+
+ if recipemd5 == md5chksum:
+ bb.note (pn + ": md5 checksum matched for ", url)
+ else:
+ if recipemd5:
+ msg = pn + ": The LIC_FILES_CHKSUM does not match for " + url
+ msg = msg + "\n" + pn + ": The new md5 checksum is " + md5chksum
+ try:
+ license_lines = license.decode('utf-8').split('\n')
+ except:
+ # License text might not be valid UTF-8, in which
+ # case we don't know how to include it in our output
+ # and have to skip it.
+ pass
+ else:
+ max_lines = int(d.getVar('QA_MAX_LICENSE_LINES') or 20)
+ if not license_lines or license_lines[-1] != '':
+ # Ensure that our license text ends with a line break
+ # (will be added with join() below).
+ license_lines.append('')
+ remove = len(license_lines) - max_lines
+ if remove > 0:
+ start = max_lines // 2
+ end = start + remove - 1
+ del license_lines[start:end]
+ license_lines.insert(start, '...')
+ msg = msg + "\n" + pn + ": Here is the selected license text:" + \
+ "\n" + \
+ "{:v^70}".format(" beginline=%d " % beginline if beginline else "") + \
+ "\n" + "\n".join(license_lines) + \
+ "{:^^70}".format(" endline=%d " % endline if endline else "")
+ if beginline:
+ if endline:
+ srcfiledesc = "%s (lines %d through to %d)" % (srclicfile, beginline, endline)
+ else:
+ srcfiledesc = "%s (beginning on line %d)" % (srclicfile, beginline)
+ elif endline:
+ srcfiledesc = "%s (ending on line %d)" % (srclicfile, endline)
+ else:
+ srcfiledesc = srclicfile
+ msg = msg + "\n" + pn + ": Check if the license information has changed in %s to verify that the LICENSE value \"%s\" remains valid" % (srcfiledesc, lic)
+
+ else:
+ msg = pn + ": LIC_FILES_CHKSUM is not specified for " + url
+ msg = msg + "\n" + pn + ": The md5 checksum is " + md5chksum
+ sane = package_qa_handle_error("license-checksum", msg, d)
+
+ if not sane:
+ bb.fatal("Fatal QA errors found, failing task.")
+}
def package_qa_check_staged(path,d):
"""
- Check staged la and pc files for sanity
- -e.g. installed being false
+ Check staged la and pc files for common problems like references to the work
+ directory.
+
+ As this is run after every stage we should be able to find the one
+ responsible for the errors easily even if we look at every .pc and .la file.
"""
- pass
-# Walk over all files in a directory and call func
-def package_qa_walk(path, funcs, package,d):
- import os
+ sane = True
+ tmpdir = d.getVar('TMPDIR')
+ workdir = os.path.join(tmpdir, "work")
+ recipesysroot = d.getVar("RECIPE_SYSROOT")
+
+ if bb.data.inherits_class("native", d) or bb.data.inherits_class("cross", d):
+ pkgconfigcheck = workdir
+ else:
+ pkgconfigcheck = tmpdir
+
+ # find all .la and .pc files
+ # read the content
+ # and check for stuff that looks wrong
for root, dirs, files in os.walk(path):
for file in files:
path = os.path.join(root,file)
- for func in funcs:
- func(path, package, d)
+ if file.endswith(".la"):
+ with open(path) as f:
+ file_content = f.read()
+ file_content = file_content.replace(recipesysroot, "")
+ if workdir in file_content:
+ error_msg = "%s failed sanity test (workdir) in path %s" % (file,root)
+ sane = package_qa_handle_error("la", error_msg, d)
+ elif file.endswith(".pc"):
+ with open(path) as f:
+ file_content = f.read()
+ file_content = file_content.replace(recipesysroot, "")
+ if pkgconfigcheck in file_content:
+ error_msg = "%s failed sanity test (tmpdir) in path %s" % (file,root)
+ sane = package_qa_handle_error("pkgconfig", error_msg, d)
+ return sane
-def package_qa_check_rdepends(pkg, workdir, d):
- import bb
- if not "-dbg" in pkg and not "task-" in pkg and not "-image" in pkg:
- # Copied from package_ipk.bbclass
- # boiler plate to update the data
- localdata = bb.data.createCopy(d)
- root = "%s/install/%s" % (workdir, pkg)
+# Run all package-wide warnfuncs and errorfuncs
+def package_qa_package(warnfuncs, errorfuncs, skip, package, d):
+ warnings = {}
+ errors = {}
- bb.data.setVar('ROOT', '', localdata)
- bb.data.setVar('ROOT_%s' % pkg, root, localdata)
- pkgname = bb.data.getVar('PKG_%s' % pkg, localdata, True)
- if not pkgname:
- pkgname = pkg
- bb.data.setVar('PKG', pkgname, localdata)
+ for func in warnfuncs:
+ func(package, d, warnings)
+ for func in errorfuncs:
+ func(package, d, errors)
- overrides = bb.data.getVar('OVERRIDES', localdata)
- if not overrides:
- raise bb.build.FuncFailed('OVERRIDES not defined')
- overrides = bb.data