diff options
Diffstat (limited to 'meta/classes/sanity.bbclass')
-rw-r--r-- | meta/classes/sanity.bbclass | 203 |
1 files changed, 124 insertions, 79 deletions
diff --git a/meta/classes/sanity.bbclass b/meta/classes/sanity.bbclass index 2d3f49eb1a..92807dc88e 100644 --- a/meta/classes/sanity.bbclass +++ b/meta/classes/sanity.bbclass @@ -2,7 +2,7 @@ # Sanity check the users setup for common misconfigurations # -SANITY_REQUIRED_UTILITIES ?= "patch diffstat makeinfo git bzip2 tar \ +SANITY_REQUIRED_UTILITIES ?= "patch diffstat git bzip2 tar \ gzip gawk chrpath wget cpio perl file which" def bblayers_conf_file(d): @@ -185,37 +185,6 @@ def raise_sanity_error(msg, d, network_error=False): %s""" % msg) -# Check flags associated with a tuning. -def check_toolchain_tune_args(data, tune, multilib, errs): - found_errors = False - if check_toolchain_args_present(data, tune, multilib, errs, 'CCARGS'): - found_errors = True - if check_toolchain_args_present(data, tune, multilib, errs, 'ASARGS'): - found_errors = True - if check_toolchain_args_present(data, tune, multilib, errs, 'LDARGS'): - found_errors = True - - return found_errors - -def check_toolchain_args_present(data, tune, multilib, tune_errors, which): - args_set = (data.getVar("TUNE_%s" % which) or "").split() - args_wanted = (data.getVar("TUNEABI_REQUIRED_%s_tune-%s" % (which, tune)) or "").split() - args_missing = [] - - # If no args are listed/required, we are done. - if not args_wanted: - return - for arg in args_wanted: - if arg not in args_set: - args_missing.append(arg) - - found_errors = False - if args_missing: - found_errors = True - tune_errors.append("TUNEABI for %s requires '%s' in TUNE_%s (%s)." % - (tune, ' '.join(args_missing), which, ' '.join(args_set))) - return found_errors - # Check a single tune for validity. def check_toolchain_tune(data, tune, multilib): tune_errors = [] @@ -227,7 +196,7 @@ def check_toolchain_tune(data, tune, multilib): overrides = localdata.getVar("OVERRIDES", False) + ":virtclass-multilib-" + multilib localdata.setVar("OVERRIDES", overrides) bb.debug(2, "Sanity-checking tuning '%s' (%s) features:" % (tune, multilib)) - features = (localdata.getVar("TUNE_FEATURES_tune-%s" % tune) or "").split() + features = (localdata.getVar("TUNE_FEATURES:tune-%s" % tune) or "").split() if not features: return "Tuning '%s' has no defined features, and cannot be used." % tune valid_tunes = localdata.getVarFlags('TUNEVALID') or {} @@ -247,17 +216,6 @@ def check_toolchain_tune(data, tune, multilib): bb.debug(2, " %s: %s" % (feature, valid_tunes[feature])) else: tune_errors.append("Feature '%s' is not defined." % feature) - whitelist = localdata.getVar("TUNEABI_WHITELIST") - if whitelist: - tuneabi = localdata.getVar("TUNEABI_tune-%s" % tune) - if not tuneabi: - tuneabi = tune - if True not in [x in whitelist.split() for x in tuneabi.split()]: - tune_errors.append("Tuning '%s' (%s) cannot be used with any supported tuning/ABI." % - (tune, tuneabi)) - else: - if not check_toolchain_tune_args(localdata, tuneabi, multilib, tune_errors): - bb.debug(2, "Sanity check: Compiler args OK for %s." % tune) if tune_errors: return "Tuning '%s' has the following errors:\n" % tune + '\n'.join(tune_errors) @@ -281,7 +239,7 @@ def check_toolchain(data): seen_libs.append(lib) if not lib in global_multilibs: tune_error_set.append("Multilib %s is not present in MULTILIB_GLOBAL_VARIANTS" % lib) - tune = data.getVar("DEFAULTTUNE_virtclass-multilib-%s" % lib) + tune = data.getVar("DEFAULTTUNE:virtclass-multilib-%s" % lib) if tune in seen_tunes: tune_error_set.append("The tuning '%s' appears in more than one multilib." % tune) else: @@ -392,9 +350,12 @@ def check_connectivity(d): msg = data.getVar('CONNECTIVITY_CHECK_MSG') or "" if len(msg) == 0: msg = "%s.\n" % err - msg += " Please ensure your host's network is configured correctly,\n" - msg += " or set BB_NO_NETWORK = \"1\" to disable network access if\n" - msg += " all required sources are on local disk.\n" + msg += " Please ensure your host's network is configured correctly.\n" + msg += " If your ISP or network is blocking the above URL,\n" + msg += " try with another domain name, for example by setting:\n" + msg += " CONNECTIVITY_CHECK_URIS = \"https://www.example.com/\"" + msg += " You could also set BB_NO_NETWORK = \"1\" to disable network\n" + msg += " access if all required sources are on local disk.\n" retval = msg return retval @@ -459,13 +420,12 @@ def check_sanity_validmachine(sanity_data): # Patch before 2.7 can't handle all the features in git-style diffs. Some # patches may incorrectly apply, and others won't apply at all. def check_patch_version(sanity_data): - from distutils.version import LooseVersion import re, subprocess try: result = subprocess.check_output(["patch", "--version"], stderr=subprocess.STDOUT).decode('utf-8') version = re.search(r"[0-9.]+", result.splitlines()[0]).group() - if LooseVersion(version) < LooseVersion("2.7"): + if bb.utils.vercmp_string_op(version, "2.7", "<"): return "Your version of patch is older than 2.7 and has bugs which will break builds. Please install a newer version of patch.\n" else: return None @@ -475,7 +435,6 @@ def check_patch_version(sanity_data): # Unpatched versions of make 3.82 are known to be broken. See GNU Savannah Bug 30612. # Use a modified reproducer from http://savannah.gnu.org/bugs/?30612 to validate. def check_make_version(sanity_data): - from distutils.version import LooseVersion import subprocess try: @@ -483,7 +442,7 @@ def check_make_version(sanity_data): except subprocess.CalledProcessError as e: return "Unable to execute make --version, exit code %d\n%s\n" % (e.returncode, e.output) version = result.split()[2] - if LooseVersion(version) == LooseVersion("3.82"): + if bb.utils.vercmp_string_op(version, "3.82", "=="): # Construct a test file f = open("makefile_test", "w") f.write("makefile_test.a: makefile_test_a.c makefile_test_b.c makefile_test.a( makefile_test_a.c makefile_test_b.c)\n") @@ -511,42 +470,64 @@ def check_make_version(sanity_data): return None -# Check if we're running on WSL (Windows Subsystem for Linux). Its known not to -# work but we should tell the user that upfront. +# Check if we're running on WSL (Windows Subsystem for Linux). +# WSLv1 is known not to work but WSLv2 should work properly as +# long as the VHDX file is optimized often, let the user know +# upfront. +# More information on installing WSLv2 at: +# https://docs.microsoft.com/en-us/windows/wsl/wsl2-install def check_wsl(d): with open("/proc/version", "r") as f: verdata = f.readlines() for l in verdata: if "Microsoft" in l: - return "OpenEmbedded doesn't work under WSL at this time, sorry" + return "OpenEmbedded doesn't work under WSLv1, please upgrade to WSLv2 if you want to run builds on Windows" + elif "microsoft" in l: + bb.warn("You are running bitbake under WSLv2, this works properly but you should optimize your VHDX file eventually to avoid running out of storage space") + return None + +# Require at least gcc version 7.5. +# +# This can be fixed on CentOS-7 with devtoolset-6+ +# https://www.softwarecollections.org/en/scls/rhscl/devtoolset-6/ +# +# A less invasive fix is with scripts/install-buildtools (or with user +# built buildtools-extended-tarball) +# +def check_gcc_version(sanity_data): + import subprocess + + build_cc, version = oe.utils.get_host_compiler_version(sanity_data) + if build_cc.strip() == "gcc": + if bb.utils.vercmp_string_op(version, "7.5", "<"): + return "Your version of gcc is older than 7.5 and will break builds. Please install a newer version of gcc (you could use the project's buildtools-extended-tarball or use scripts/install-buildtools).\n" return None # Tar version 1.24 and onwards handle overwriting symlinks correctly # but earlier versions do not; this needs to work properly for sstate +# Version 1.28 is needed so opkg-build works correctly when reproducibile builds are enabled def check_tar_version(sanity_data): - from distutils.version import LooseVersion import subprocess try: result = subprocess.check_output(["tar", "--version"], stderr=subprocess.STDOUT).decode('utf-8') except subprocess.CalledProcessError as e: return "Unable to execute tar --version, exit code %d\n%s\n" % (e.returncode, e.output) version = result.split()[3] - if LooseVersion(version) < LooseVersion("1.24"): - return "Your version of tar is older than 1.24 and has bugs which will break builds. Please install a newer version of tar.\n" + if bb.utils.vercmp_string_op(version, "1.28", "<"): + return "Your version of tar is older than 1.28 and does not have the support needed to enable reproducible builds. Please install a newer version of tar (you could use the project's buildtools-tarball from our last release or use scripts/install-buildtools).\n" return None # We use git parameters and functionality only found in 1.7.8 or later # The kernel tools assume git >= 1.8.3.1 (verified needed > 1.7.9.5) see #6162 # The git fetcher also had workarounds for git < 1.7.9.2 which we've dropped def check_git_version(sanity_data): - from distutils.version import LooseVersion import subprocess try: result = subprocess.check_output(["git", "--version"], stderr=subprocess.DEVNULL).decode('utf-8') except subprocess.CalledProcessError as e: return "Unable to execute git --version, exit code %d\n%s\n" % (e.returncode, e.output) version = result.split()[2] - if LooseVersion(version) < LooseVersion("1.8.3.1"): + if bb.utils.vercmp_string_op(version, "1.8.3.1", "<"): return "Your version of git is older than 1.8.3.1 and has bugs which will break builds. Please install a newer version of git.\n" return None @@ -578,6 +559,24 @@ def sanity_check_conffiles(d): bb.fatal(str(e)) d.setVar("BB_INVALIDCONF", True) +def drop_v14_cross_builds(d): + import glob + indexes = glob.glob(d.expand("${SSTATE_MANIFESTS}/index-${BUILD_ARCH}_*")) + for i in indexes: + with open(i, "r") as f: + lines = f.readlines() + for l in reversed(lines): + try: + (stamp, manifest, workdir) = l.split() + except ValueError: + bb.fatal("Invalid line '%s' in sstate manifest '%s'" % (l, i)) + for m in glob.glob(manifest + ".*"): + if m.endswith(".postrm"): + continue + sstate_clean_manifest(m, d) + bb.utils.remove(stamp + "*") + bb.utils.remove(workdir, recurse = True) + def sanity_handle_abichanges(status, d): # # Check the 'ABI' of TMPDIR @@ -594,6 +593,12 @@ def sanity_handle_abichanges(status, d): f.write(current_abi) elif int(abi) <= 11 and current_abi == "12": status.addresult("The layout of TMPDIR changed for Recipe Specific Sysroots.\nConversion doesn't make sense and this change will rebuild everything so please delete TMPDIR (%s).\n" % d.getVar("TMPDIR")) + elif int(abi) <= 13 and current_abi == "14": + status.addresult("TMPDIR changed to include path filtering from the pseudo database.\nIt is recommended to use a clean TMPDIR with the new pseudo path filtering so TMPDIR (%s) would need to be removed to continue.\n" % d.getVar("TMPDIR")) + elif int(abi) == 14 and current_abi == "15": + drop_v14_cross_builds(d) + with open(abifile, "w") as f: + f.write(current_abi) elif (abi != current_abi): # Code to convert from one ABI to another could go here if possible. status.addresult("Error, TMPDIR has changed its layout version number (%s to %s) and you need to either rebuild, revert or adjust it at your own risk.\n" % (abi, current_abi)) @@ -622,14 +627,16 @@ def check_sanity_version_change(status, d): # In other words, these tests run once in a given build directory and then # never again until the sanity version or host distrubution id/version changes. - # Check the python install is complete. glib-2.0-natives requries - # xml.parsers.expat + # Check the python install is complete. Examples that are often removed in + # minimal installations: glib-2.0-natives requries # xml.parsers.expat and icu + # requires distutils.sysconfig. try: import xml.parsers.expat - except ImportError: - status.addresult('Your python is not a full install. Please install the module xml.parsers.expat (python-xml on openSUSE and SUSE Linux).\n') - import stat + import distutils.sysconfig + except ImportError as e: + status.addresult('Your Python 3 is not a full install. Please install the module %s (see the Getting Started guide for further information).\n' % e.name) + status.addresult(check_gcc_version(d)) status.addresult(check_make_version(d)) status.addresult(check_patch_version(d)) status.addresult(check_tar_version(d)) @@ -664,6 +671,7 @@ def check_sanity_version_change(status, d): status.addresult('Please use ASSUME_PROVIDED +=, not ASSUME_PROVIDED = in your local.conf\n') # Check that TMPDIR isn't on a filesystem with limited filename length (eg. eCryptFS) + import stat tmpdir = d.getVar('TMPDIR') status.addresult(check_create_long_filename(tmpdir, "TMPDIR")) tmpdirmode = os.stat(tmpdir).st_mode @@ -672,6 +680,23 @@ def check_sanity_version_change(status, d): if (tmpdirmode & stat.S_ISUID): status.addresult("TMPDIR is setuid, please don't build in a setuid directory") + # Check that a user isn't building in a path in PSEUDO_IGNORE_PATHS + pseudoignorepaths = d.getVar('PSEUDO_IGNORE_PATHS', expand=True).split(",") + workdir = d.getVar('WORKDIR', expand=True) + for i in pseudoignorepaths: + if i and workdir.startswith(i): + status.addresult("You are building in a path included in PSEUDO_IGNORE_PATHS " + str(i) + " please locate the build outside this path.\n") + + # Check if PSEUDO_IGNORE_PATHS and and paths under pseudo control overlap + pseudoignorepaths = d.getVar('PSEUDO_IGNORE_PATHS', expand=True).split(",") + pseudo_control_dir = "${D},${PKGD},${PKGDEST},${IMAGEROOTFS},${SDK_OUTPUT}" + pseudocontroldir = d.expand(pseudo_control_dir).split(",") + for i in pseudoignorepaths: + for j in pseudocontroldir: + if i and j: + if j.startswith(i): + status.addresult("A path included in PSEUDO_IGNORE_PATHS " + str(i) + " and the path " + str(j) + " overlap and this will break pseudo permission and ownership tracking. Please set the path " + str(j) + " to a different directory which does not overlap with pseudo controlled directories. \n") + # Some third-party software apparently relies on chmod etc. being suid root (!!) import stat suid_check_bins = "chown chmod mknod".split() @@ -739,15 +764,14 @@ def check_sanity_everybuild(status, d): if 0 == os.getuid(): raise_sanity_error("Do not use Bitbake as root.", d) - # Check the Python version, we now have a minimum of Python 3.4 + # Check the Python version, we now have a minimum of Python 3.6 import sys - if sys.hexversion < 0x03040000: - status.addresult('The system requires at least Python 3.4 to run. Please update your Python interpreter.\n') + if sys.hexversion < 0x030600F0: + status.addresult('The system requires at least Python 3.6 to run. Please update your Python interpreter.\n') # Check the bitbake version meets minimum requirements - from distutils.version import LooseVersion minversion = d.getVar('BB_MIN_VERSION') - if (LooseVersion(bb.__version__) < LooseVersion(minversion)): + if bb.utils.vercmp_string_op(bb.__version__, minversion, "<"): status.addresult('Bitbake version %s is required and version %s was found\n' % (minversion, bb.__version__)) sanity_check_locale(d) @@ -756,6 +780,17 @@ def check_sanity_everybuild(status, d): if "." in paths or "./" in paths or "" in paths: status.addresult("PATH contains '.', './' or '' (empty element), which will break the build, please remove this.\nParsed PATH is " + str(paths) + "\n") + #Check if bitbake is present in PATH environment variable + bb_check = bb.utils.which(d.getVar('PATH'), 'bitbake') + if not bb_check: + bb.warn("bitbake binary is not found in PATH, did you source the script?") + + # Check whether 'inherit' directive is found (used for a class to inherit) + # in conf file it's supposed to be uppercase INHERIT + inherit = d.getVar('inherit') + if inherit: + status.addresult("Please don't use inherit directive in your local.conf. The directive is supposed to be used in classes and recipes only to inherit of bbclasses. Here INHERIT should be used.\n") + # Check that the DISTRO is valid, if set # need to take into account DISTRO renaming DISTRO distro = d.getVar('DISTRO') @@ -798,6 +833,11 @@ def check_sanity_everybuild(status, d): elif d.getVar('SDK_ARCH', False) == "${BUILD_ARCH}": status.addresult('SDKMACHINE is set, but SDK_ARCH has not been changed as a result - SDKMACHINE may have been set too late (e.g. in the distro configuration)\n') + # If SDK_VENDOR looks like "-my-sdk" then the triples are badly formed so fail early + sdkvendor = d.getVar("SDK_VENDOR") + if not (sdkvendor.startswith("-") and sdkvendor.count("-") == 1): + status.addresult("SDK_VENDOR should be of the form '-foosdk' with a single dash; found '%s'\n" % sdkvendor) + check_supported_distro(d) omask = os.umask(0o022) @@ -818,20 +858,25 @@ def check_sanity_everybuild(status, d): except: pass - oeroot = d.getVar('COREBASE') - if oeroot.find('+') != -1: - status.addresult("Error, you have an invalid character (+) in your COREBASE directory path. Please move the installation to a directory which doesn't include any + characters.") - if oeroot.find('@') != -1: - status.addresult("Error, you have an invalid character (@) in your COREBASE directory path. Please move the installation to a directory which doesn't include any @ characters.") - if oeroot.find(' ') != -1: - status.addresult("Error, you have a space in your COREBASE directory path. Please move the installation to a directory which doesn't include a space since autotools doesn't support this.") + for checkdir in ['COREBASE', 'TMPDIR']: + val = d.getVar(checkdir) + if val.find('..') != -1: + status.addresult("Error, you have '..' in your %s directory path. Please ensure the variable contains an absolute path as this can break some recipe builds in obtuse ways." % checkdir) + if val.find('+') != -1: + status.addresult("Error, you have an invalid character (+) in your %s directory path. Please move the installation to a directory which doesn't include any + characters." % checkdir) + if val.find('@') != -1: + status.addresult("Error, you have an invalid character (@) in your %s directory path. Please move the installation to a directory which doesn't include any @ characters." % checkdir) + if val.find(' ') != -1: + status.addresult("Error, you have a space in your %s directory path. Please move the installation to a directory which doesn't include a space since autotools doesn't support this." % checkdir) + if val.find('%') != -1: + status.addresult("Error, you have an invalid character (%) in your %s directory path which causes problems with python string formatting. Please move the installation to a directory which doesn't include any % characters." % checkdir) # Check the format of MIRRORS, PREMIRRORS and SSTATE_MIRRORS import re mirror_vars = ['MIRRORS', 'PREMIRRORS', 'SSTATE_MIRRORS'] protocols = ['http', 'ftp', 'file', 'https', \ 'git', 'gitsm', 'hg', 'osc', 'p4', 'svn', \ - 'bzr', 'cvs', 'npm', 'sftp', 'ssh', 's3' ] + 'bzr', 'cvs', 'npm', 'sftp', 'ssh', 's3', 'az' ] for mirror_var in mirror_vars: mirrors = (d.getVar(mirror_var) or '').replace('\\n', ' ').split() @@ -919,7 +964,7 @@ def check_sanity(sanity_data): last_tmpdir = "" last_sstate_dir = "" last_nativelsbstr = "" - sanityverfile = sanity_data.expand("${TOPDIR}/conf/sanity_info") + sanityverfile = sanity_data.expand("${TOPDIR}/cache/sanity_info") if os.path.exists(sanityverfile): with open(sanityverfile, 'r') as f: for line in f: |