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.bbclass1235
1 files changed, 991 insertions, 244 deletions
diff --git a/meta/classes/insane.bbclass b/meta/classes/insane.bbclass
index a660a06976..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,12 +54,26 @@ 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, 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),
@@ -50,9 +89,19 @@ def package_qa_get_machine_dict():
"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),
@@ -65,10 +114,32 @@ def package_qa_get_machine_dict():
"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),
+ "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),
},
@@ -76,54 +147,97 @@ def package_qa_get_machine_dict():
"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-gnu" : {
+ "linux-gnuspe" : {
"powerpc": (20, 0, 0, False, 32),
},
- "linux-gnuspe" : {
+ "linux-muslspe" : {
"powerpc": (20, 0, 0, False, 32),
},
"linux-uclibcspe" : {
"powerpc": (20, 0, 0, False, 32),
},
"linux-gnu" : {
- "microblaze": (47787, 0, 0, False, 32),
- "microblazeel": (47787, 0, 0, True, 32),
+ "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
-WARN_QA ?= "dev-so rpaths debug-deps dev-deps debug-files arch la2 pkgconfig desktop la ldflags perms useless-rpaths"
-ERROR_QA ?= ""
-#ERROR_QA ?= "rpaths debug-deps dev-deps debug-files arch pkgconfig perms"
-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_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(error, d):
- logfile = d.getVar('QA_LOGFILE', True)
+def package_qa_write_error(type, error, d):
+ logfile = d.getVar('QA_LOGFILE')
if logfile:
- p = d.getVar('P', True)
- f = file( logfile, "a+")
- print >> f, "%s: %s" % (p, error)
- f.close()
+ 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_msg, d)
- if error_class in (d.getVar("ERROR_QA", True) or "").split():
- bb.error("QA Issue: %s" % error_msg)
+ 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)
+ 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):
"""
@@ -132,46 +246,52 @@ def package_qa_check_rpath(file,name, d, elf, messages):
if not elf:
return
- 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 os.path.islink(file):
+ return
- 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")
+ bad_dirs = [d.getVar('BASE_WORKDIR'), d.getVar('STAGING_DIR_TARGET')]
- 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:
- messages.append("package %s contains bad RPATH %s in file %s" % (name, line, file))
+ 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))
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
- objdump = bb.data.getVar('OBJDUMP', d, True)
- env_path = bb.data.getVar('PATH', d, True)
+ if os.path.islink(file):
+ return
+
+ libdir = d.getVar("libdir")
+ base_libdir = d.getVar("base_libdir")
- libdir = bb.data.getVar("libdir", d, True)
- base_libdir = bb.data.getVar("base_libdir", d, True)
+ phdrs = elf.run_objdump("-p", d)
import re
rpath_re = re.compile("\s+RPATH\s+(.*)")
- for line in os.popen("LC_ALL=C PATH=%s %s -p '%s' 2> /dev/null" % (env_path, objdump, file), "r"):
- m = rpath_re.match(line)
- if m:
- rpath = m.group(1)
- if rpath == libdir or rpath == base_libdir:
- # The dynamic linker searches both these places anyway. There is no point in
- # looking there again.
- messages.append("%s: %s contains probably-redundant RPATH %s" % (name, package_qa_clean_path(file, d), rpath))
+ 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):
@@ -179,19 +299,106 @@ 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 path.endswith(".so") and os.path.islink(path):
- messages.append("non -dev/-dbg package contains symlink .so: %s path '%s'" % \
+ 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:
+ if not "-dbg" in name and not "-ptest" in name:
if '.debug' in path.split(os.path.sep):
- messages.append("non debug package contains .debug directory: %s path %s" % \
+ 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"
@@ -201,16 +408,90 @@ def package_qa_check_perm(path,name,d, elf, messages):
"""
return
+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
"""
+ import re
+
if not elf:
return
- target_os = bb.data.getVar('TARGET_OS', d, True)
- target_arch = bb.data.getVar('TARGET_ARCH', d, True)
+ 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']:
@@ -224,17 +505,19 @@ def package_qa_check_arch(path,name,d, elf, messages):
#if this will throw an exception, then fix the dict above
(machine, osabi, abiversion, littleendian, bits) \
- = package_qa_get_machine_dict()[target_os][target_arch]
+ = package_qa_get_machine_dict(d)[target_os][target_arch]
# Check the architecture and endiannes of the binary
- if not machine == elf.machine():
- messages.append("Architecture did not match (%d to %d) on %s" % \
- (machine, elf.machine(), package_qa_clean_path(path,d)))
- elif not bits == elf.abiSize():
- messages.append("Bit size did not match (%d to %d) on %s" % \
- (bits, elf.abiSize(), package_qa_clean_path(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():
- messages.append("Endiannes did not match (%d to %d) on %s" % \
+ 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"
@@ -243,11 +526,35 @@ 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(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:
- messages.append("Desktop file issue: " + l.strip())
+ 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):
@@ -261,29 +568,28 @@ def package_qa_hash_style(path, name, d, elf, messages):
if os.path.islink(path):
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
- objdump = bb.data.getVar('OBJDUMP', d, True)
- env_path = bb.data.getVar('PATH', d, True)
-
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 os.popen("LC_ALL=C PATH=%s %s -p '%s' 2> /dev/null" % (env_path, objdump, path), "r"):
+ 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 has_syms and not sane:
- messages.append("No GNU_HASH in the elf binary: '%s'" % path)
+ package_qa_add_message(messages, "ldflags", "No GNU_HASH in the elf binary: '%s'" % path)
QAPATHTEST[buildpaths] = "package_qa_check_buildpaths"
@@ -299,42 +605,91 @@ def package_qa_check_buildpaths(path, name, d, elf, messages):
if os.path.islink(path):
return
- tmpdir = bb.data.getVar('TMPDIR', d, True)
- file_content = open(path).read()
- if tmpdir in file_content:
- messages.append("File %s in package contained reference to tmpdir" % package_qa_clean_path(path,d))
+ # 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)))
+
+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.")
-def package_qa_check_license(workdir, d):
+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
+ Check for changes in the license files.
"""
import tempfile
sane = True
- lic_files = bb.data.getVar('LIC_FILES_CHKSUM', d, True)
- lic = bb.data.getVar('LICENSE', d, True)
- pn = bb.data.getVar('PN', d, True)
+ lic_files = d.getVar('LIC_FILES_CHKSUM') or ''
+ lic = d.getVar('LICENSE')
+ pn = d.getVar('PN')
if lic == "CLOSED":
- return True
+ return
- if not lic_files:
- # just throw a warning now. Once licensing data in entered for enough of the recipes,
- # this will be converted into error and False will be returned.
- bb.error(pn + ": Recipe file does not have license file information (LIC_FILES_CHKSUM)")
- return False
+ 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 = bb.data.getVar('S', d, True)
+ srcdir = d.getVar('S')
for url in lic_files.split():
- (type, host, path, user, pswd, parm) = bb.decodeurl(url)
+ 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):
- raise bb.build.FuncFailed( pn + ": LIC_FILES_CHKSUM points to invalid file: " + path)
+ package_qa_handle_error("license-checksum", pn + ": LIC_FILES_CHKSUM points to an invalid file: " + srclicfile, d)
+ continue
- if 'md5' not in parm:
- bb.error(pn + ": md5 checksum is not specified for ", url)
- return False
+ recipemd5 = parm.get('md5', '')
beginline, endline = 0, 0
if 'beginline' in parm:
beginline = int(parm['beginline'])
@@ -343,17 +698,21 @@ def package_qa_check_license(workdir, d):
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, 'r')
+ 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 >= beginline):
if ((lineno <= endline) or not endline):
fo.write(line)
+ license.append(line)
linesout += 1
else:
break
@@ -361,33 +720,73 @@ def package_qa_check_license(workdir, d):
fo.close()
fi.close()
md5chksum = bb.utils.md5_file(tmplicfile)
+ license = b''.join(license)
os.unlink(tmplicfile)
- if parm['md5'] == md5chksum:
+ if recipemd5 == md5chksum:
bb.note (pn + ": md5 checksum matched for ", url)
else:
- bb.error (pn + ": md5 data is not matching for ", url)
- bb.error (pn + ": The new md5 checksum is ", md5chksum)
- bb.error (pn + ": Check if the license information has changed in")
- sane = False
-
- return sane
+ 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
+ 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.
"""
sane = True
- tmpdir = bb.data.getVar('TMPDIR', d, True)
+ tmpdir = d.getVar('TMPDIR')
workdir = os.path.join(tmpdir, "work")
+ r