diff options
author | Koen Kooi <koen.kooi@linaro.org> | 2016-02-18 12:00:19 +0100 |
---|---|---|
committer | Koen Kooi <koen.kooi@linaro.org> | 2016-02-18 12:01:06 +0100 |
commit | 021a8d66e620073de282381b57f6e048e60e8af5 (patch) | |
tree | 262c505fda77130a0b0f224f8edbeca8514af8b2 | |
parent | d7b87f5d93d59c86e8058c41dabefd0fc2ca2ae7 (diff) |
meta-ilp32: sync insane.bbclass with OE-core
Change-Id: I87d5d1018832f9a248cf9a163a1d7925a7a9cc65
Signed-off-by: Koen Kooi <koen.kooi@linaro.org>
-rw-r--r-- | meta-ilp32/classes/insane.bbclass | 361 |
1 files changed, 251 insertions, 110 deletions
diff --git a/meta-ilp32/classes/insane.bbclass b/meta-ilp32/classes/insane.bbclass index ab8896c4..cb0029c7 100644 --- a/meta-ilp32/classes/insane.bbclass +++ b/meta-ilp32/classes/insane.bbclass @@ -11,6 +11,7 @@ # -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 @@ -30,16 +31,22 @@ 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 file-rdeps \ + 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 \ + version-going-backwards expanded-d invalid-chars \ + license-checksum dev-elf \ " +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" +UNKNOWN_CONFIGURE_WHITELIST ?= "--enable-nls --disable-nls --disable-silent-rules --disable-dependency-tracking --with-libtool-sysroot --disable-static" # # dictionary for elf headers @@ -52,6 +59,14 @@ def package_qa_get_machine_dict(): "darwin9" : { "arm" : (40, 0, 0, True, 32), }, + "eabi" : { + "arm" : (40, 0, 0, True, 32), + }, + "elf" : { + "i586" : (3, 0, 0, True, 32), + "x86_64": (62, 0, 0, True, 64), + "epiphany": (4643, 0, 0, True, 32), + }, "linux" : { "aarch64" : (183, 0, 0, True, 64), "aarch64_be" :(183, 0, 0, False, 64), @@ -72,10 +87,12 @@ def package_qa_get_machine_dict(): "mipsel": ( 8, 0, 0, True, 32), "mips64": ( 8, 0, 0, False, 64), "mips64el": ( 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" : { @@ -96,6 +113,8 @@ def package_qa_get_machine_dict(): }, "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), @@ -108,6 +127,9 @@ def package_qa_get_machine_dict(): "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), }, "uclinux-uclibc" : { "bfin": ( 106, 0, 0, True, 32), @@ -152,7 +174,7 @@ def package_qa_get_machine_dict(): 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),"") + return path.replace(d.getVar("TMPDIR", True) + "/", "") def package_qa_write_error(type, error, d): logfile = d.getVar('QA_LOGFILE', True) @@ -174,6 +196,12 @@ def package_qa_handle_error(error_class, error_msg, d): bb.note("QA Issue: %s [%s]" % (error_msg, error_class)) return True +def package_qa_add_message(messages, section, new_msg): + if section not in messages: + messages[section] = new_msg + else: + messages[section] = messages[section] + "\n" + new_msg + QAPATHTEST[libexec] = "package_qa_check_libexec" def package_qa_check_libexec(path,name, d, elf, messages): @@ -183,7 +211,7 @@ def package_qa_check_libexec(path,name, d, elf, messages): return True if 'libexec' in path.split(os.path.sep): - messages["libexec"] = "%s: %s is using libexec please relocate to %s" % (name, package_qa_clean_path(path, d), libexec) + 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 @@ -211,7 +239,7 @@ def package_qa_check_rpath(file,name, d, elf, messages): rpath = m.group(1) for dir in bad_dirs: if dir in rpath: - messages["rpaths"] = "package %s contains bad RPATH %s in file %s" % (name, rpath, file) + 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): @@ -241,7 +269,7 @@ def package_qa_check_useless_rpaths(file, name, d, elf, messages): 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. - messages["useless-rpaths"] = "%s: %s contains probably-redundant RPATH %s" % (name, package_qa_clean_path(file, d), rpath) + 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): @@ -250,8 +278,19 @@ def package_qa_check_dev(path, name, d, elf, messages): """ 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): - messages["dev-so"] = "non -dev/-dbg/-nativesdk package contains symlink .so: %s path '%s'" % \ - (name, package_qa_clean_path(path,d)) + 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): @@ -263,8 +302,8 @@ def package_qa_check_staticdev(path, name, d, elf, messages): """ 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"): - messages["staticdev"] = "non -staticdev package contains static .a library: %s path '%s'" % \ - (name, package_qa_clean_path(path,d)) + 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): """ @@ -277,6 +316,7 @@ def package_qa_check_libdir(d): pkgdest = d.getVar('PKGDEST', True) base_libdir = d.getVar("base_libdir",True) + os.sep libdir = d.getVar("libdir", True) + os.sep + libexecdir = d.getVar("libexecdir", True) + os.sep exec_prefix = d.getVar("exec_prefix", True) + os.sep messages = [] @@ -292,6 +332,9 @@ def package_qa_check_libdir(d): if 'libdir' in (d.getVar('INSANE_SKIP_' + package, True) or "").split(): bb.note("Package %s skipping libdir QA test" % (package)) skippackages.append(package) + elif d.getVar('PACKAGE_DEBUG_SPLIT_STYLE', True) == '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: @@ -304,7 +347,7 @@ def package_qa_check_libdir(d): if base_libdir not in rel_path: messages.append("%s: found library in wrong location: %s" % (package, rel_path)) if exec_re.match(rel_path): - if libdir not in rel_path: + if libdir not in rel_path and libexecdir not in rel_path: messages.append("%s: found library in wrong location: %s" % (package, rel_path)) if messages: @@ -318,8 +361,8 @@ def package_qa_check_dbg(path, name, d, elf, messages): if not "-dbg" in name and not "-ptest" in name: if '.debug' in path.split(os.path.sep): - messages["debug-files"] = "non debug package contains .debug directory: %s path %s" % \ - (name, package_qa_clean_path(path,d)) + messages("debug-files", "non debug package contains .debug directory: %s path %s" % \ + (name, package_qa_clean_path(path,d))) QAPATHTEST[perms] = "package_qa_check_perm" def package_qa_check_perm(path,name,d, elf, messages): @@ -448,6 +491,11 @@ def package_qa_check_arch(path,name,d, elf, messages): provides = d.getVar('PROVIDES', True) bpn = d.getVar('BPN', True) + if target_arch == "allarch": + pn = d.getVar('PN', True) + 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): @@ -465,15 +513,15 @@ def package_qa_check_arch(path,name,d, elf, messages): # Check the architecture and endiannes of the binary if not ((machine == elf.machine()) or \ ((("virtual/kernel" in provides) or bb.data.inherits_class("module", d) ) and (target_os == "linux-gnux32" or target_os == "linux-gnun32"))): - messages["arch"] = "Architecture did not match (%d to %d) on %s" % \ - (machine, elf.machine(), package_qa_clean_path(path,d)) + package_qa_add_message(messages, "arch", "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) or bb.data.inherits_class("module", d) ) and (target_os == "linux-gnux32" or target_os == "linux-gnun32"))): - messages["arch"] = "Bit size did not match (%d to %d) %s on %s" % \ - (bits, elf.abiSize(), bpn, package_qa_clean_path(path,d)) + 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["arch"] = "Endiannes did not match (%d to %d) on %s" % \ - (littleendian, elf.isLittleEndian(), package_qa_clean_path(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))) QAPATHTEST[desktop] = "package_qa_check_desktop" def package_qa_check_desktop(path, name, d, elf, messages): @@ -485,7 +533,7 @@ def package_qa_check_desktop(path, name, d, elf, messages): output = os.popen("%s %s" % (desktop_file_validate, path)) # This only produces output on errors for l in output: - messages["desktop"] = "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): @@ -509,7 +557,7 @@ def package_qa_textrel(path, name, d, elf, messages): sane = False if not sane: - messages["textrel"] = "ELF binary '%s' has relocations in .text" % path + 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): @@ -544,7 +592,7 @@ def package_qa_hash_style(path, name, d, elf, messages): sane = True if has_syms and not sane: - messages["ldflags"] = "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" @@ -560,11 +608,15 @@ def package_qa_check_buildpaths(path, name, d, elf, messages): if os.path.islink(path): return + # Ignore ipk and deb's CONTROL dir + if path.find(name + "/CONTROL/") != -1 or path.find(name + "/DEBIAN/") != -1: + return + tmpdir = d.getVar('TMPDIR', True) with open(path) as f: file_content = f.read() if tmpdir in file_content: - messages["buildpaths"] = "File %s in package contained reference to tmpdir" % package_qa_clean_path(path,d) + 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" @@ -583,7 +635,7 @@ def package_qa_check_xorg_driver_abi(path, name, d, elf, messages): for rdep in bb.utils.explode_deps(d.getVar('RDEPENDS_' + name, True) or ""): if rdep.startswith("%sxorg-abi-" % mlprefix): return - messages["xorg-driver-abi"] = "Package %s contains Xorg driver (%s) but no xorg-abi- dependencies" % (name, os.path.basename(path)) + 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): @@ -593,7 +645,7 @@ def package_qa_check_infodir(path, name, d, elf, messages): infodir = d.expand("${infodir}/dir") if infodir in path: - messages["infodir"] = "The /usr/share/info/dir file is not meant to be shipped in a particular package." + 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): @@ -606,7 +658,7 @@ def package_qa_check_symlink_to_sysroot(path, name, d, elf, messages): tmpdir = d.getVar('TMPDIR', True) if target.startswith(tmpdir): trimmed = path.replace(os.path.join (d.getVar("PKGDEST", True), name), "") - messages["symlink-to-sysroot"] = "Symlink %s in %s points to TMPDIR" % (trimmed, name) + package_qa_add_message(messages, "symlink-to-sysroot", "Symlink %s in %s points to TMPDIR" % (trimmed, name)) def package_qa_check_license(workdir, d): """ @@ -620,11 +672,11 @@ def package_qa_check_license(workdir, d): pn = d.getVar('PN', True) if lic == "CLOSED": - return True + return if not lic_files: - bb.error(pn + ": Recipe file does not have license file information (LIC_FILES_CHKSUM)") - return False + package_qa_handle_error("license-checksum", pn + ": Recipe file does not have license file information (LIC_FILES_CHKSUM)", d) + return srcdir = d.getVar('S', True) @@ -632,10 +684,12 @@ def package_qa_check_license(workdir, d): try: (type, host, path, user, pswd, parm) = bb.fetch.decodeurl(url) except bb.fetch.MalformedUrl: - raise bb.build.FuncFailed( pn + ": LIC_FILES_CHKSUM contains an invalid URL: " + url) + 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 recipemd5 = parm.get('md5', '') beginline, endline = 0, 0 @@ -670,8 +724,8 @@ def package_qa_check_license(workdir, d): bb.note (pn + ": md5 checksum matched for ", url) else: if recipemd5: - bb.error(pn + ": md5 data is not matching for ", url) - bb.error(pn + ": The new md5 checksum is ", md5chksum) + msg = pn + ": The LIC_FILES_CHKSUM does not match for " + url + msg = msg + "\n" + pn + ": The new md5 checksum is " + md5chksum if beginline: if endline: srcfiledesc = "%s (lines %d through to %d)" % (srclicfile, beginline, endline) @@ -681,29 +735,26 @@ def package_qa_check_license(workdir, d): srcfiledesc = "%s (ending on line %d)" % (srclicfile, endline) else: srcfiledesc = srclicfile - bb.error(pn + ": Check if the license information has changed in %s to verify that the LICENSE value \"%s\" remains valid" % (srcfiledesc, lic)) - else: - bb.error(pn + ": md5 checksum is not specified for ", url) - bb.error(pn + ": The md5 checksum is ", md5chksum) - sane = False + msg = msg + "\n" + pn + ": Check if the license information has changed in %s to verify that the LICENSE value \"%s\" remains valid" % (srcfiledesc, lic) - return sane + else: + msg = pn + ": LIC_FILES_CHKSUM is not specified for " + url + msg = msg + "\n" + pn + ": The md5 checksum is " + md5chksum + package_qa_handle_error("license-checksum", msg, d) 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 = d.getVar('TMPDIR', True) workdir = os.path.join(tmpdir, "work") - installed = "installed=yes" if bb.data.inherits_class("native", d) or bb.data.inherits_class("cross", d): pkgconfigcheck = workdir else: @@ -731,7 +782,7 @@ def package_qa_check_staged(path,d): return sane # Walk over all files in a directory and call func -def package_qa_walk(path, warnfuncs, errorfuncs, skip, package, d): +def package_qa_walk(warnfuncs, errorfuncs, skip, package, d): import oe.qa #if this will throw an exception, then fix the dict above @@ -744,7 +795,9 @@ def package_qa_walk(path, warnfuncs, errorfuncs, skip, package, d): elf = oe.qa.ELFFile(path) try: elf.open() - except: + except (IOError, ValueError): + # IOError can happen if the packaging control files disappear, + # ValueError means the file isn't an ELF. elf = None for func in warnfuncs: func(path, package, d, elf, warnings) @@ -756,15 +809,12 @@ def package_qa_walk(path, warnfuncs, errorfuncs, skip, package, d): for e in errors: package_qa_handle_error(e, errors[e], d) - return len(errors) == 0 - def package_qa_check_rdepends(pkg, pkgdest, skip, taskdeps, packages, d): # Don't do this check for kernel/module recipes, there aren't too many debug/development # packages and you can get false positives e.g. on kernel-module-lirc-dev if bb.data.inherits_class("kernel", d) or bb.data.inherits_class("module-base", d): - return True + return - sane = True if not "-dbg" in pkg and not "packagegroup-" in pkg and not "-image" in pkg: localdata = bb.data.createCopy(d) localdata.setVar('OVERRIDES', pkg) @@ -778,10 +828,10 @@ def package_qa_check_rdepends(pkg, pkgdest, skip, taskdeps, packages, d): for rdepend in rdepends: if "-dbg" in rdepend and "debug-deps" not in skip: error_msg = "%s rdepends on %s" % (pkg,rdepend) - sane = package_qa_handle_error("debug-deps", error_msg, d) + package_qa_handle_error("debug-deps", error_msg, d) if (not "-dev" in pkg and not "-staticdev" in pkg) and rdepend.endswith("-dev") and "dev-deps" not in skip: error_msg = "%s rdepends on %s" % (pkg, rdepend) - sane = package_qa_handle_error("dev-deps", error_msg, d) + package_qa_handle_error("dev-deps", error_msg, d) if rdepend not in packages: rdep_data = oe.packagedata.read_subpkgdata(rdepend, d) if rdep_data and 'PN' in rdep_data and rdep_data['PN'] in taskdeps: @@ -799,20 +849,21 @@ def package_qa_check_rdepends(pkg, pkgdest, skip, taskdeps, packages, d): if rdep_data and 'PN' in rdep_data and rdep_data['PN'] in taskdeps: continue error_msg = "%s rdepends on %s, but it isn't a build dependency?" % (pkg, rdepend) - sane = package_qa_handle_error("build-deps", error_msg, d) + package_qa_handle_error("build-deps", error_msg, d) if "file-rdeps" not in skip: ignored_file_rdeps = set(['/bin/sh', '/usr/bin/env', 'rtld(GNU_HASH)']) if bb.data.inherits_class('nativesdk', d): ignored_file_rdeps |= set(['/bin/bash', '/usr/bin/perl']) # For Saving the FILERDEPENDS - filerdepends = set() + filerdepends = {} rdep_data = oe.packagedata.read_subpkgdata(pkg, d) for key in rdep_data: if key.startswith("FILERDEPENDS_"): for subkey in rdep_data[key].split(): - filerdepends.add(subkey) - filerdepends -= ignored_file_rdeps + if subkey not in ignored_file_rdeps: + # We already know it starts with FILERDEPENDS_ + filerdepends[subkey] = key[13:] if filerdepends: next = rdepends @@ -844,44 +895,36 @@ def package_qa_check_rdepends(pkg, pkgdest, skip, taskdeps, packages, d): # case there is a RDEPENDS_pkg = "python" in the recipe. for py in [ d.getVar('MLPREFIX', True) + "python", "python" ]: if py in done: - filerdepends.discard("/usr/bin/python") + filerdepends.pop("/usr/bin/python",None) done.remove(py) for rdep in done: # For Saving the FILERPROVIDES, RPROVIDES and FILES_INFO - rdep_rprovides = set() rdep_data = oe.packagedata.read_subpkgdata(rdep, d) for key in rdep_data: if key.startswith("FILERPROVIDES_") or key.startswith("RPROVIDES_"): for subkey in rdep_data[key].split(): - rdep_rprovides.add(subkey) + filerdepends.pop(subkey,None) # Add the files list to the rprovides if key == "FILES_INFO": # Use eval() to make it as a dict for subkey in eval(rdep_data[key]): - rdep_rprovides.add(subkey) - filerdepends -= rdep_rprovides + filerdepends.pop(subkey,None) if not filerdepends: # Break if all the file rdepends are met break - else: - # Clear it for the next loop - rdep_rprovides.clear() if filerdepends: - error_msg = "%s requires %s, but no providers in its RDEPENDS" % \ - (pkg, ', '.join(str(e) for e in filerdepends)) - sane = package_qa_handle_error("file-rdeps", error_msg, d) - - return sane + for key in filerdepends: + error_msg = "%s contained in package %s requires %s, but no providers found in its RDEPENDS" % \ + (filerdepends[key],pkg, key) + package_qa_handle_error("file-rdeps", error_msg, d) def package_qa_check_deps(pkg, pkgdest, skip, d): - sane = True localdata = bb.data.createCopy(d) localdata.setVar('OVERRIDES', pkg) bb.data.update_data(localdata) def check_valid_deps(var): - sane = True try: rvar = bb.utils.explode_dep_versions2(localdata.getVar(var, True) or "") except ValueError as e: @@ -890,25 +933,95 @@ def package_qa_check_deps(pkg, pkgdest, skip, d): for v in rvar[dep]: if v and not v.startswith(('< ', '= ', '> ', '<= ', '>=')): error_msg = "%s_%s is invalid: %s (%s) only comparisons <, =, >, <=, and >= are allowed" % (var, pkg, dep, v) - sane = package_qa_handle_error("dep-cmp", error_msg, d) - return sane + package_qa_handle_error("dep-cmp", error_msg, d) - sane = True - if not check_valid_deps('RDEPENDS'): - sane = False - if not check_valid_deps('RRECOMMENDS'): - sane = False - if not check_valid_deps('RSUGGESTS'): - sane = False - if not check_valid_deps('RPROVIDES'): - sane = False - if not check_valid_deps('RREPLACES'): - sane = False - if not check_valid_deps('RCONFLICTS'): - sane = False + check_valid_deps('RDEPENDS') + check_valid_deps('RRECOMMENDS') + check_valid_deps('RSUGGESTS') + check_valid_deps('RPROVIDES') + check_valid_deps('RREPLACES') + check_valid_deps('RCONFLICTS') + +QAPATHTEST[expanded-d] = "package_qa_check_expanded_d" +def package_qa_check_expanded_d(path,name,d,elf,messages): + """ + Check for the expanded D (${D}) value in pkg_* and FILES + variables, warn the user to use it correctly. + """ + sane = True + expanded_d = d.getVar('D',True) + + # Get packages for current recipe and iterate + packages = d.getVar('PACKAGES', True).split(" ") + for pak in packages: + # Go through all variables and check if expanded D is found, warn the user accordingly + for var in 'FILES','pkg_preinst', 'pkg_postinst', 'pkg_prerm', 'pkg_postrm': + bbvar = d.getVar(var + "_" + pak, False) + if bbvar: + # Bitbake expands ${D} within bbvar during the previous step, so we check for its expanded value + if expanded_d in bbvar: + if var == 'FILES': + package_qa_add_message(messages, "expanded-d", "FILES in %s recipe should not contain the ${D} variable as it references the local build directory not the target filesystem, best solution is to remove the ${D} reference" % pak) + sane = False + else: + package_qa_add_message(messages, "expanded-d", "%s in %s recipe contains ${D}, it should be replaced by $D instead" % (var, pak)) + sane = False return sane +def package_qa_check_encoding(keys, encode, d): + def check_encoding(key,enc): + sane = True + value = d.getVar(key, True) + if value: + try: + s = unicode(value, enc) + except UnicodeDecodeError as e: + error_msg = "%s has non %s characters" % (key,enc) + sane = False + package_qa_handle_error("invalid-chars", error_msg, d) + return sane + + for key in keys: + sane = check_encoding(key, encode) + if not sane: + break + +HOST_USER_UID := "${@os.getuid()}" +HOST_USER_GID := "${@os.getgid()}" + +QAPATHTEST[host-user-contaminated] = "package_qa_check_host_user" +def package_qa_check_host_user(path, name, d, elf, messages): + """Check for paths outside of /home which are owned by the user running bitbake.""" + + if not os.path.lexists(path): + return + + dest = d.getVar('PKGDEST', True) + pn = d.getVar('PN', True) + home = os.path.join(dest, 'home') + if path == home or path.startswith(home + os.sep): + return + + try: + stat = os.lstat(path) + except OSError as exc: + import errno + if exc.errno != errno.ENOENT: + raise + else: + rootfs_path = path[len(dest):] + check_uid = int(d.getVar('HOST_USER_UID', True)) + if stat.st_uid == check_uid: + package_qa_add_message(messages, "host-user-contaminated", "%s: %s is owned by uid %d, which is the same as the user running bitbake. This may be due to host contamination" % (pn, rootfs_path, check_uid)) + return False + + check_gid = int(d.getVar('HOST_USER_GID', True)) + if stat.st_gid == check_gid: + package_qa_add_message(messages, "host-user-contaminated", "%s: %s is owned by gid %d, which is the same as the user running bitbake. This may be due to host contamination" % (pn, rootfs_path, check_gid)) + return False + return True + # The PACKAGE FUNC to scan each package python do_package_qa () { import subprocess @@ -918,6 +1031,9 @@ python do_package_qa () { bb.build.exec_func("read_subpackage_metadata", d) + # Check non UTF-8 characters on recipe's metadata + package_qa_check_encoding(['DESCRIPTION', 'SUMMARY', 'LICENSE', 'SECTION'], 'utf-8', d) + logdir = d.getVar('T', True) pkg = d.getVar('PN', True) @@ -943,12 +1059,12 @@ python do_package_qa () { # Scan the packages... pkgdest = d.getVar('PKGDEST', True) - packages = d.getVar('PACKAGES', True) + packages = set((d.getVar('PACKAGES', True) or '').split()) cpath = oe.cachedpath.CachedPath() global pkgfiles pkgfiles = {} - for pkg in (packages or "").split(): + for pkg in packages: pkgfiles[pkg] = [] for walkroot, dirs, files in cpath.walk(pkgdest + "/" + pkg): for file in files: @@ -969,10 +1085,7 @@ python do_package_qa () { taskdeps.add(taskdepdata[dep][0]) g = globals() - walk_sane = True - rdepends_sane = True - deps_sane = True - for package in packages.split(): + for package in packages: skip = (d.getVar('INSANE_SKIP_' + package, True) or "").split() if skip: bb.note("Package %s skipping QA tests: %s" % (package, str(skip))) @@ -993,26 +1106,24 @@ python do_package_qa () { # Check package name if not pkgname_pattern.match(package): package_qa_handle_error("pkgname", - "%s doesn't match the [a-z0-9.+-]+ regex\n" % package, d) + "%s doesn't match the [a-z0-9.+-]+ regex" % package, d) path = "%s/%s" % (pkgdest, package) - if not package_qa_walk(path, warnchecks, errorchecks, skip, package, d): - walk_sane = False - if not package_qa_check_rdepends(package, pkgdest, skip, taskdeps, packages, d): - rdepends_sane = False - if not package_qa_check_deps(package, pkgdest, skip, d): - deps_sane = False + package_qa_walk(warnchecks, errorchecks, skip, package, d) + package_qa_check_rdepends(package, pkgdest, skip, taskdeps, packages, d) + package_qa_check_deps(package, pkgdest, skip, d) if 'libdir' in d.getVar("ALL_QA", True).split(): package_qa_check_libdir(d) qa_sane = d.getVar("QA_SANE", True) - if not walk_sane or not rdepends_sane or not deps_sane or not qa_sane: + if not qa_sane: bb.fatal("QA run found fatal errors. Please consider fixing them.") bb.note("DONE with PACKAGE QA") } +do_package_qa[vardepsexclude] = "BB_TASKDEPDATA" do_package_qa[rdeptask] = "do_packagedata" addtask do_package_qa after do_packagedata do_package before do_build @@ -1027,7 +1138,7 @@ addtask do_package_qa_setscene python do_qa_staging() { bb.note("QA checking staging") - if not package_qa_check_staged(d.expand('${SYSROOT_DESTDIR}${STAGING_LIBDIR}'), d): + if not package_qa_check_staged(d.expand('${SYSROOT_DESTDIR}${libdir}'), d): bb.fatal("QA staging was broken by the package built above") } @@ -1047,7 +1158,7 @@ python do_qa_configure() { if "config.log" in files: if subprocess.call(statement, shell=True) == 0: bb.fatal("""This autoconf log indicates errors, it looked at host include and/or library paths while determining system capabilities. -Rerun configure task after fixing this. The path was '%s'""" % root) +Rerun configure task after fixing this.""") if "configure.ac" in files: configs.append(os.path.join(root,"configure.ac")) @@ -1079,8 +1190,7 @@ Missing inherit gettext?""" % (gt, config)) # Check license variables ########################################################################### - if not package_qa_check_license(workdir, d): - bb.fatal("Licensing Error: LIC_FILES_CHKSUM does not match, please fix") + package_qa_check_license(workdir, d) ########################################################################### # Check unrecognised configure options (with a white list) @@ -1102,7 +1212,30 @@ Missing inherit gettext?""" % (gt, config)) package_qa_handle_error("unknown-configure-option", error_msg, d) except subprocess.CalledProcessError: pass + + # Check invalid PACKAGECONFIG + pkgconfig = (d.getVar("PACKAGECONFIG", True) or "").split() + if pkgconfig: + pkgconfigflags = d.getVarFlags("PACKAGECONFIG") or {} + for pconfig in pkgconfig: + if pconfig not in pkgconfigflags: + pn = d.getVar('PN', True) + error_msg = "%s: invalid PACKAGECONFIG: %s" % (pn, pconfig) + package_qa_handle_error("invalid-packageconfig", error_msg, d) + + qa_sane = d.getVar("QA_SANE", True) + if not qa_sane: + bb.fatal("Fatal QA errors found, failing task.") } + +python do_qa_unpack() { + bb.note("Checking has ${S} been created") + + s_dir = d.getVar('S', True) + if not os.path.exists(s_dir): + bb.warn('%s: the directory %s (%s) pointed to by the S variable doesn\'t exist - please set S within the recipe to point to where the source has been unpacked to' % (d.getVar('PN', True), d.getVar('S', False), s_dir)) +} + # The Staging Func, to check all staging #addtask qa_staging after do_populate_sysroot before do_build do_populate_sysroot[postfuncs] += "do_qa_staging " @@ -1112,10 +1245,13 @@ do_populate_sysroot[postfuncs] += "do_qa_staging " #addtask qa_configure after do_configure before do_compile do_configure[postfuncs] += "do_qa_configure " +# Check does S exist. +do_unpack[postfuncs] += "do_qa_unpack" + python () { tests = d.getVar('ALL_QA', True).split() if "desktop" in tests: - d.appendVar("PACKAGE_DEPENDS", "desktop-file-utils-native") + d.appendVar("PACKAGE_DEPENDS", " desktop-file-utils-native") ########################################################################### # Check various variables @@ -1134,9 +1270,6 @@ python () { msg += "%s\n" % extrapaths bb.warn(msg) - if d.getVar('do_stage', True) is not None: - bb.fatal("Legacy staging found for %s as it has a do_stage function. This will need conversion to a do_install or often simply removal to work with OE-core" % d.getVar("FILE", True)) - overrides = d.getVar('OVERRIDES', True).split(':') pn = d.getVar('PN', True) if pn in overrides: @@ -1148,10 +1281,18 @@ python () { for dep in (d.getVar('QADEPENDS', True) or "").split(): d.appendVarFlag('do_package_qa', 'depends', " %s:do_populate_sysroot" % dep) for var in 'RDEPENDS', 'RRECOMMENDS', 'RSUGGESTS', 'RCONFLICTS', 'RPROVIDES', 'RREPLACES', 'FILES', 'pkg_preinst', 'pkg_postinst', 'pkg_prerm', 'pkg_postrm', 'ALLOW_EMPTY': - if d.getVar(var): + if d.getVar(var, False): issues.append(var) + + fakeroot_tests = d.getVar('FAKEROOT_QA', True).split() + if set(tests) & set(fakeroot_tests): + d.setVarFlag('do_package_qa', 'fakeroot', '1') + d.appendVarFlag('do_package_qa', 'depends', ' virtual/fakeroot-native:do_populate_sysroot') else: d.setVarFlag('do_package_qa', 'rdeptask', '') for i in issues: package_qa_handle_error("pkgvarcheck", "%s: Variable %s is set as not being package specific, please fix this." % (d.getVar("FILE", True), i), d) + qa_sane = d.getVar("QA_SANE", True) + if not qa_sane: + bb.fatal("Fatal QA errors found, failing task.") } |