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.bbclass1470
1 files changed, 1107 insertions, 363 deletions
diff --git a/meta/classes/insane.bbclass b/meta/classes/insane.bbclass
index 3ab4c2ce3a..dc1c2f8d41 100644
--- a/meta/classes/insane.bbclass
+++ b/meta/classes/insane.bbclass
@@ -11,17 +11,42 @@
# -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
-
-
-#
-# We need to have the scanelf utility as soon as
-# possible and this is contained within the pax-utils-native.
-# The package.bbclass can help us here.
-#
-inherit package
-PACKAGE_DEPENDS += "pax-utils-native desktop-file-utils-native"
-PACKAGEFUNCS += " do_package_qa "
-
+# -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
@@ -29,338 +54,642 @@ PACKAGEFUNCS += " do_package_qa "
# feel free to add and correct.
#
# TARGET_OS TARGET_ARCH MACHINE, OSABI, ABIVERSION, Little Endian, 32bit?
-def package_qa_get_machine_dict():
- return {
+def package_qa_get_machine_dict(d):
+ machdata = {
"darwin9" : {
- "arm" : (40, 0, 0, True, True),
+ "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" : {
- "arm" : (40, 97, 0, True, True),
- "armeb": (40, 97, 0, False, True),
- "powerpc": (20, 0, 0, False, True),
- "i386": ( 3, 0, 0, True, True),
- "i486": ( 3, 0, 0, True, True),
- "i586": ( 3, 0, 0, True, True),
- "i686": ( 3, 0, 0, True, True),
- "x86_64": (62, 0, 0, True, False),
- "ia64": (50, 0, 0, True, False),
- "alpha": (36902, 0, 0, True, False),
- "hppa": (15, 3, 0, False, True),
- "m68k": ( 4, 0, 0, False, True),
- "mips": ( 8, 0, 0, False, True),
- "mipsel": ( 8, 0, 0, True, True),
- "s390": (22, 0, 0, False, True),
- "sh4": (42, 0, 0, True, True),
- "sparc": ( 2, 0, 0, False, True),
+ "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, True),
- "armeb": ( 40, 97, 0, False, True),
- "powerpc": ( 20, 0, 0, False, True),
- "i386": ( 3, 0, 0, True, True),
- "i486": ( 3, 0, 0, True, True),
- "i586": ( 3, 0, 0, True, True),
- "i686": ( 3, 0, 0, True, True),
- "x86_64": ( 62, 0, 0, True, False),
- "mips": ( 8, 0, 0, False, True),
- "mipsel": ( 8, 0, 0, True, True),
- "avr32": (6317, 0, 0, False, True),
- "sh4": (42, 0, 0, True, True),
+ "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, True),
+ "bfin": ( 106, 0, 0, True, 32),
},
"linux-gnueabi" : {
- "arm" : (40, 0, 0, True, True),
- "armeb" : (40, 0, 0, False, True),
+ "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-uclibcgnueabi" : {
- "arm" : (40, 0, 0, True, True),
- "armeb" : (40, 0, 0, False, True),
+ "linux-uclibceabi" : {
+ "arm" : (40, 0, 0, True, 32),
+ "armeb" : (40, 0, 0, False, 32),
},
"linux-gnuspe" : {
- "powerpc": (20, 0, 0, False, True),
+ "powerpc": (20, 0, 0, False, 32),
+ },
+ "linux-muslspe" : {
+ "powerpc": (20, 0, 0, False, 32),
},
"linux-uclibcspe" : {
- "powerpc": (20, 0, 0, False, True),
+ "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)
-# Known Error classes
-# 0 - non dev contains .so
-# 1 - package contains a dangerous RPATH
-# 2 - package depends on debug package
-# 3 - non dbg contains .so
-# 4 - wrong architecture
-# 5 - .la contains installed=yes or reference to the workdir
-# 6 - .pc contains reference to /usr/include or workdir
-# 7 - the desktop file is not valid
-# 8 - .la contains reference to the workdir
-# 9 - LDFLAGS ignored
-# 10 - Build paths in binaries
+ return machdata
-def package_qa_clean_path(path,d):
- """ Remove the common prefix from the path. In this case it is the TMPDIR"""
- return path.replace(bb.data.getVar('TMPDIR',d,True),"")
-def package_qa_make_fatal_error(error_class, name, path,d):
+def package_qa_clean_path(path, d, pkg=None):
"""
- decide if an error is fatal
-
- TODO: Load a whitelist of known errors
+ Remove redundant paths from the path for display. If pkg isn't set then
+ TMPDIR is stripped, otherwise PKGDEST/pkg is stripped.
"""
- return False
- return not error_class in [0, 5, 7, 8, 9]
-
-def package_qa_write_error(error_class, name, path, d):
- """
- Log the error
- """
-
- ERROR_NAMES =[
- "non dev contains .so",
- "package contains RPATH",
- "package depends on debug package",
- "non dbg contains .debug",
- "wrong architecture",
- "evil hides inside the .la",
- "evil hides inside the .pc",
- "the desktop file is not valid",
- ".la contains reference to the workdir",
- "LDFLAGS ignored",
- "package contains reference to tmpdir paths",
- ]
-
- log_path = os.path.join( bb.data.getVar('T', d, True), "log.qa_package" )
- f = file( log_path, "a+")
- print >> f, "%s, %s, %s" % \
- (ERROR_NAMES[error_class], name, package_qa_clean_path(path,d))
- f.close()
-
- logfile = bb.data.getVar('QA_LOGFILE', d, True)
+ 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 = bb.data.getVar('P', d, True)
- f = file( logfile, "a+")
- print >> f, "%s, %s, %s, %s" % \
- (p, ERROR_NAMES[error_class], name, package_qa_clean_path(path,d))
- f.close()
-
-def package_qa_handle_error(error_class, error_msg, name, path, d):
- fatal = package_qa_make_fatal_error(error_class, name, path, d)
- if fatal:
- bb.error("QA Issue: %s" % error_msg)
+ 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:
- bb.warn("QA Issue: %s" % error_msg)
- package_qa_write_error(error_class, name, path, d)
+ messages[section] = messages[section] + "\n" + new_msg
- return not fatal
+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
-def package_qa_check_rpath(file,name,d, elf):
+QAPATHTEST[rpaths] = "package_qa_check_rpath"
+def package_qa_check_rpath(file,name, d, elf, messages):
"""
Check for dangerous RPATHs
"""
if not elf:
- return True
+ return
- sane = True
- scanelf = os.path.join(bb.data.getVar('STAGING_BINDIR_NATIVE',d,True),'scanelf')
- bad_dirs = [bb.data.getVar('TMPDIR', d, True) + "/work", bb.data.getVar('STAGING_DIR_TARGET', d, True)]
- bad_dir_test = bb.data.getVar('TMPDIR', d, True)
- if not os.path.exists(scanelf):
- bb.fatal("Can not check RPATH, scanelf (part of pax-utils-native) not found")
-
- if not bad_dirs[0] in bb.data.getVar('WORKDIR', d, True):
- bb.fatal("This class assumed that WORKDIR is ${TMPDIR}/work... Not doing any check")
-
- output = os.popen("%s -B -F%%r#F '%s'" % (scanelf,file))
- txt = output.readline().split()
- for line in txt:
- for dir in bad_dirs:
- if dir in line:
- error_msg = "package %s contains bad RPATH %s in file %s" % (name, line, file)
- sane = sane + package_qa_handle_error(1, error_msg, name, file, d)
+ if os.path.islink(file):
+ return
- return sane
+ bad_dirs = [d.getVar('BASE_WORKDIR'), d.getVar('STAGING_DIR_TARGET')]
+
+ phdrs = elf.run_objdump("-p", 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)
+ 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_dev(path, name,d, elf):
+QAPATHTEST[useless-rpaths] = "package_qa_check_useless_rpaths"
+def package_qa_check_useless_rpaths(file, name, d, elf, messages):
+ """
+ Check for RPATHs that are useless but not dangerous
+ """
+ def rpath_eq(a, b):
+ return os.path.normpath(a) == os.path.normpath(b)
+
+ if not elf:
+ return
+
+ if os.path.islink(file):
+ return
+
+ libdir = d.getVar("libdir")
+ base_libdir = d.getVar("base_libdir")
+
+ phdrs = elf.run_objdump("-p", 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
"""
- sane = True
+ 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)))
- if not name.endswith("-dev") and path.endswith(".so") and os.path.islink(path):
- error_msg = "non -dev package contains symlink .so: %s path '%s'" % \
- (name, package_qa_clean_path(path,d))
- sane = package_qa_handle_error(0, error_msg, name, 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)))
- return sane
+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
+ """
-def package_qa_check_dbg(path, name,d, elf):
+ 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
"""
- sane = True
-
- if not "-dbg" in name:
+ if not "-dbg" in name and not "-ptest" in name:
if '.debug' in path.split(os.path.sep):
- error_msg = "non debug package contains .debug directory: %s path %s" % \
- (name, package_qa_clean_path(path,d))
- sane = package_qa_handle_error(3, error_msg, name, path, d)
+ package_qa_add_message(messages, "debug-files", "non debug package contains .debug directory: %s path %s" % \
+ (name, package_qa_clean_path(path,d)))
- return sane
-
-def package_qa_check_perm(path,name,d, elf):
+QAPATHTEST[perms] = "package_qa_check_perm"
+def package_qa_check_perm(path,name,d, elf, messages):
"""
Check the permission of files
"""
- sane = True
- return sane
+ return
-def package_qa_check_arch(path,name,d, elf):
+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):
"""
- Check if archs are compatible
+ 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
- sane = True
- target_os = bb.data.getVar('TARGET_OS', d, True)
- target_arch = bb.data.getVar('TARGET_ARCH', d, 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
+ """
+ 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 True
+ 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 True
+ return
#if this will throw an exception, then fix the dict above
- (machine, osabi, abiversion, littleendian, bits32) \
- = package_qa_get_machine_dict()[target_os][target_arch]
+ (machine, osabi, abiversion, littleendian, bits) \
+ = package_qa_get_machine_dict(d)[target_os][target_arch]
# Check the architecture and endiannes of the binary
- if not machine == elf.machine():
- error_msg = "Architecture did not match (%d to %d) on %s" % \
- (machine, elf.machine(), package_qa_clean_path(path,d))
- sane = package_qa_handle_error(4, error_msg, name, path, d)
+ 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():
- error_msg = "Endiannes did not match (%d to %d) on %s" % \
- (littleendian, elf.isLittleEndian(), package_qa_clean_path(path,d))
- sane = package_qa_handle_error(4, error_msg, name, path, d)
+ package_qa_add_message(messages, "arch", "Endiannes did not match (%d to %d) on %s" % \
+ (littleendian, elf.isLittleEndian(), package_qa_clean_path(path,d)))
- return sane
-
-def package_qa_check_desktop(path, name, d, elf):
+QAPATHTEST[desktop] = "package_qa_check_desktop"
+def package_qa_check_desktop(path, name, d, elf, messages):
"""
Run all desktop files through desktop-file-validate.
"""
- sane = True
if path.endswith(".desktop"):
- desktop_file_validate = os.path.join(bb.data.getVar('STAGING_BINDIR_NATIVE',d,True),'desktop-file-validate')
+ 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:
- sane = package_qa_handle_error(7, l.strip(), name, path, d)
+ package_qa_add_message(messages, "desktop", "Desktop file issue: " + l.strip())
- return sane
+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
-def package_qa_hash_style(path, name, d, elf):
+ 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 True
+ return
if os.path.islink(path):
- return True
+ return
- gnu_hash = "--hash-style=gnu" in bb.data.getVar('LDFLAGS', d, True)
+ gnu_hash = "--hash-style=gnu" in d.getVar('LDFLAGS')
if not gnu_hash:
- gnu_hash = "--hash-style=both" in bb.data.getVar('LDFLAGS', d, True)
+ gnu_hash = "--hash-style=both" in d.getVar('LDFLAGS')
if not gnu_hash:
- return True
+ return
- objdump = bb.data.getVar('OBJDUMP', d, True)
- env_path = bb.data.getVar('PATH', d, True)
+ sane = False
+ has_syms = False
- sane = True
- elf = False
- # A bit hacky. We do not know if path is an elf binary or not
- # we will search for 'NEEDED' or 'INIT' as this should be printed...
- # and come before the HASH section (guess!!!) and works on split out
- # debug symbols too
- for line in os.popen("LC_ALL=C PATH=%s %s -p '%s' 2> /dev/null" % (env_path, objdump, path), "r"):
- if "NEEDED" in line or "INIT" in line:
- sane = False
- elf = True
+ 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
+ sane = True
- if elf and not sane:
- error_msg = "No GNU_HASH in the elf binary: '%s'" % path
- return package_qa_handle_error(9, error_msg, name, path, d)
+ if has_syms and not sane:
+ package_qa_add_message(messages, "ldflags", "No GNU_HASH in the elf binary: '%s'" % path)
- return True
-def package_qa_check_buildpaths(path, name, d, elf):
+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
"""
- sane = True
-
# Ignore .debug files, not interesting
if path.find(".debug") != -1:
- return True
+ return
# Ignore symlinks
if os.path.islink(path):
- return True
+ return
- tmpdir = bb.data.getVar('TMPDIR', d, True)
- file_content = open(path).read()
- if tmpdir in file_content:
- error_msg = "File %s in package contained reference to tmpdir" % package_qa_clean_path(path,d)
- sane = package_qa_handle_error(10, error_msg, name, path, d)
- return sane
+ # 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
-def package_qa_check_license(workdir, d):
+ 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)))
+
+QAPATHTEST[infodir] = "package_qa_check_infodir"
+def package_qa_check_infodir(path, name, d, elf, messages):
+ """
+ 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_popula