diff options
Diffstat (limited to 'meta/classes/insane.bbclass')
| -rw-r--r-- | meta/classes/insane.bbclass | 1288 |
1 files changed, 958 insertions, 330 deletions
diff --git a/meta/classes/insane.bbclass b/meta/classes/insane.bbclass index ead6ec1a86..dc1c2f8d41 100644 --- a/meta/classes/insane.bbclass +++ b/meta/classes/insane.bbclass @@ -11,26 +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 +# -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 -# -# 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 ${QADEPENDS}" -PACKAGEFUNCS += " do_package_qa " - # unsafe-references-in-binaries requires prelink-rtld from # prelink-native, but we don't want this DEPENDS for -native builds QADEPENDS = "prelink-native" -QADEPENDS_virtclass-native = "" -QADEPENDS_virtclass-nativesdk = "" +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 @@ -38,12 +54,26 @@ QADEPENDS_virtclass-nativesdk = "" # 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), @@ -59,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), @@ -74,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), }, @@ -85,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) -# Currently not being used by default "desktop" -WARN_QA ?= "ldflags useless-rpaths rpaths unsafe-references-in-binaries unsafe-references-in-scripts staticdev" -ERROR_QA ?= "dev-so debug-deps dev-deps debug-files arch la2 pkgconfig la perms" + 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(d.getVar('TMPDIR',True),"") -def package_qa_write_error(error, d): - logfile = d.getVar('QA_LOGFILE', 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(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): """ @@ -141,46 +246,52 @@ def package_qa_check_rpath(file,name, d, elf, messages): if not elf: return - scanelf = os.path.join(d.getVar('STAGING_BINDIR_NATIVE',True),'scanelf') - bad_dirs = [d.getVar('TMPDIR', True) + "/work", d.getVar('STAGING_DIR_TARGET', True)] - bad_dir_test = d.getVar('TMPDIR', 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 d.getVar('WORKDIR', 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 = d.getVar('OBJDUMP', True) - env_path = d.getVar('PATH', True) + if os.path.islink(file): + return + + libdir = d.getVar("libdir") + base_libdir = d.getVar("base_libdir") - libdir = d.getVar("libdir", True) - base_libdir = d.getVar("base_libdir", 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): @@ -188,8 +299,19 @@ 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("-nativesdk") and path.endswith(".so") and os.path.islink(path): - messages.append("non -dev/-dbg/-nativesdk 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" @@ -201,19 +323,82 @@ def package_qa_check_staticdev(path, name, d, elf, messages): libgcc.a, libgcov.a will be skipped in their packages """ - if not name.endswith("-pic") and not name.endswith("-staticdev") and path.endswith(".a") and not path.endswith("_nonshared.a"): - messages.append("non -staticdev package contains static .a library: %s path '%s'" % \ + 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" @@ -223,111 +408,90 @@ def package_qa_check_perm(path,name,d, elf, messages): """ return -QAPATHTEST[unsafe-references-in-binaries] = "package_qa_check_unsafe_references_in_binaries" -def package_qa_check_unsafe_references_in_binaries(path, name, d, elf, messages): - """ - Ensure binaries in base_[bindir|sbindir|libdir] do not link to files under exec_prefix - """ - if unsafe_references_skippable(path, name, d): - return - - if elf: - import subprocess as sub - pn = d.getVar('PN', True) - - exec_prefix = d.getVar('exec_prefix', True) - sysroot_path = d.getVar('STAGING_DIR_TARGET', True) - sysroot_path_usr = sysroot_path + exec_prefix - - try: - ldd_output = bb.process.Popen(["prelink-rtld", "--root", sysroot_path, path], stdout=sub.PIPE).stdout.read() - except bb.process.CmdError: - error_msg = pn + ": prelink-rtld aborted when processing %s" % path - package_qa_handle_error("unsafe-references-in-binaries", error_msg, d) - return False - - if sysroot_path_usr in ldd_output: - error_msg = pn + ": %s links to something under exec_prefix" % path - package_qa_handle_error("unsafe-references-in-binaries", error_msg, d) - error_msg = "ldd reports: %s" % ldd_output - package_qa_handle_error("unsafe-references-in-binaries", error_msg, d) - return False - 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 - pn = d.getVar('PN', True) - - # 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', True) - statement = "grep -e '%s/' %s > /dev/null" % (exec_prefix, path) - if os.system(statement) == 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) + """ + Warn if scripts in base_[bindir|sbindir|libdir] reference files under exec_prefix + """ + if unsafe_references_skippable(path, name, d): + return -def unsafe_references_skippable(path, name, d): - if bb.data.inherits_class('native', d) or bb.data.inherits_class('nativesdk', d): - return True + 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) - if "-dbg" in name or "-dev" in name: - return True +def unsafe_references_skippable(path, name, d): + if bb.data.inherits_class('native', d) or bb.data.inherits_class('nativesdk', d): + return True - # Other package names to skip: - if name.startswith("kernel-module-"): - return True + if "-dbg" in name or "-dev" in name: + return True - # Skip symlinks - if os.path.islink(path): - return True + # Other package names to skip: + if name.startswith("kernel-module-"): + return True - # Skip unusual rootfs layouts which make these tests irrelevant - exec_prefix = d.getVar('exec_prefix', True) - if exec_prefix == "": - return True + # Skip symlinks + if os.path.islink(path): + return True - pkgdest = d.getVar('PKGDEST', True) - pkgdest = pkgdest + "/" + name - pkgdest = os.path.abspath(pkgdest) - base_bindir = pkgdest + d.getVar('base_bindir', True) - base_sbindir = pkgdest + d.getVar('base_sbindir', True) - base_libdir = pkgdest + d.getVar('base_libdir', True) - bindir = pkgdest + d.getVar('bindir', True) - sbindir = pkgdest + d.getVar('sbindir', True) - libdir = pkgdest + d.getVar('libdir', True) + # Skip unusual rootfs layouts which make these tests irrelevant + exec_prefix = d.getVar('exec_prefix') + if exec_prefix == "": + return True - if base_bindir == bindir and base_sbindir == sbindir and base_libdir == libdir: - 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 + # 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 + 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', True) - target_arch = d.getVar('TARGET_ARCH', True) - provides = d.getVar('PROVIDES', 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']: @@ -341,19 +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()) or \ - ("virtual/kernel" in provides) and (target_os == "linux-gnux32")): - 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()) or \ - ("virtual/kernel" in provides) and (target_os == "linux-gnux32")): - messages.append("Bit size did not match (%d to %d) %s on %s" % \ + 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" @@ -362,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(d.getVar('STAGING_BINDIR_NATIVE',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): @@ -380,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 d.getVar('LDFLAGS', True) + gnu_hash = "--hash-style=gnu" in d.getVar('LDFLAGS') if not gnu_hash: - gnu_hash = "--hash-style=both" in d.getVar('LDFLAGS', True) + gnu_hash = "--hash-style=both" in d.getVar('LDFLAGS') if not gnu_hash: return - objdump = d.getVar('OBJDUMP', True) - env_path = d.getVar('PATH', 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" @@ -418,42 +605,91 @@ def package_qa_check_buildpaths(path, name, d, elf, messages): if os.path.islink(path): return - tmpdir = d.getVar('TMPDIR', 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))) -def package_qa_check_license(workdir, d): +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_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 = d.getVar('LIC_FILES_CHKSUM', True) - lic = d.getVar('LICENSE', True) - pn = d.getVar('PN', 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 = d.getVar('S', 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 an invalid file: " + srclicfile) + 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']) @@ -462,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 @@ -480,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 |
