summaryrefslogtreecommitdiff
path: root/meta/classes/sanity.bbclass
diff options
context:
space:
mode:
Diffstat (limited to 'meta/classes/sanity.bbclass')
-rw-r--r--meta/classes/sanity.bbclass1187
1 files changed, 908 insertions, 279 deletions
diff --git a/meta/classes/sanity.bbclass b/meta/classes/sanity.bbclass
index fc005aa90d..e8064ac483 100644
--- a/meta/classes/sanity.bbclass
+++ b/meta/classes/sanity.bbclass
@@ -2,362 +2,991 @@
# Sanity check the users setup for common misconfigurations
#
-def raise_sanity_error(msg):
- bb.fatal(""" Poky's config sanity checker detected a potential misconfiguration.
+SANITY_REQUIRED_UTILITIES ?= "patch diffstat makeinfo git bzip2 tar \
+ gzip gawk chrpath wget cpio perl file which"
+
+def bblayers_conf_file(d):
+ return os.path.join(d.getVar('TOPDIR'), 'conf/bblayers.conf')
+
+def sanity_conf_read(fn):
+ with open(fn, 'r') as f:
+ lines = f.readlines()
+ return lines
+
+def sanity_conf_find_line(pattern, lines):
+ import re
+ return next(((index, line)
+ for index, line in enumerate(lines)
+ if re.search(pattern, line)), (None, None))
+
+def sanity_conf_update(fn, lines, version_var_name, new_version):
+ index, line = sanity_conf_find_line(r"^%s" % version_var_name, lines)
+ lines[index] = '%s = "%d"\n' % (version_var_name, new_version)
+ with open(fn, "w") as f:
+ f.write(''.join(lines))
+
+# Functions added to this variable MUST throw a NotImplementedError exception unless
+# they successfully changed the config version in the config file. Exceptions
+# are used since exec_func doesn't handle return values.
+BBLAYERS_CONF_UPDATE_FUNCS += " \
+ conf/bblayers.conf:LCONF_VERSION:LAYER_CONF_VERSION:oecore_update_bblayers \
+ conf/local.conf:CONF_VERSION:LOCALCONF_VERSION:oecore_update_localconf \
+ conf/site.conf:SCONF_VERSION:SITE_CONF_VERSION:oecore_update_siteconf \
+"
+
+SANITY_DIFF_TOOL ?= "meld"
+
+SANITY_LOCALCONF_SAMPLE ?= "${COREBASE}/meta*/conf/local.conf.sample"
+python oecore_update_localconf() {
+ # Check we are using a valid local.conf
+ current_conf = d.getVar('CONF_VERSION')
+ conf_version = d.getVar('LOCALCONF_VERSION')
+
+ failmsg = """Your version of local.conf was generated from an older/newer version of
+local.conf.sample and there have been updates made to this file. Please compare the two
+files and merge any changes before continuing.
+
+Matching the version numbers will remove this message.
+
+\"${SANITY_DIFF_TOOL} conf/local.conf ${SANITY_LOCALCONF_SAMPLE}\"
+
+is a good way to visualise the changes."""
+ failmsg = d.expand(failmsg)
+
+ raise NotImplementedError(failmsg)
+}
+
+SANITY_SITECONF_SAMPLE ?= "${COREBASE}/meta*/conf/site.conf.sample"
+python oecore_update_siteconf() {
+ # If we have a site.conf, check it's valid
+ current_sconf = d.getVar('SCONF_VERSION')
+ sconf_version = d.getVar('SITE_CONF_VERSION')
+
+ failmsg = """Your version of site.conf was generated from an older version of
+site.conf.sample and there have been updates made to this file. Please compare the two
+files and merge any changes before continuing.
+
+Matching the version numbers will remove this message.
+
+\"${SANITY_DIFF_TOOL} conf/site.conf ${SANITY_SITECONF_SAMPLE}\"
+
+is a good way to visualise the changes."""
+ failmsg = d.expand(failmsg)
+
+ raise NotImplementedError(failmsg)
+}
+
+SANITY_BBLAYERCONF_SAMPLE ?= "${COREBASE}/meta*/conf/bblayers.conf.sample"
+python oecore_update_bblayers() {
+ # bblayers.conf is out of date, so see if we can resolve that
+
+ current_lconf = int(d.getVar('LCONF_VERSION'))
+ lconf_version = int(d.getVar('LAYER_CONF_VERSION'))
+
+ failmsg = """Your version of bblayers.conf has the wrong LCONF_VERSION (has ${LCONF_VERSION}, expecting ${LAYER_CONF_VERSION}).
+Please compare your file against bblayers.conf.sample and merge any changes before continuing.
+"${SANITY_DIFF_TOOL} conf/bblayers.conf ${SANITY_BBLAYERCONF_SAMPLE}"
+
+is a good way to visualise the changes."""
+ failmsg = d.expand(failmsg)
+
+ if not current_lconf:
+ raise NotImplementedError(failmsg)
+
+ lines = []
+
+ if current_lconf < 4:
+ raise NotImplementedError(failmsg)
+
+ bblayers_fn = bblayers_conf_file(d)
+ lines = sanity_conf_read(bblayers_fn)
+
+ if current_lconf == 4 and lconf_version > 4:
+ topdir_var = '$' + '{TOPDIR}'
+ index, bbpath_line = sanity_conf_find_line('BBPATH', lines)
+ if bbpath_line:
+ start = bbpath_line.find('"')
+ if start != -1 and (len(bbpath_line) != (start + 1)):
+ if bbpath_line[start + 1] == '"':
+ lines[index] = (bbpath_line[:start + 1] +
+ topdir_var + bbpath_line[start + 1:])
+ else:
+ if not topdir_var in bbpath_line:
+ lines[index] = (bbpath_line[:start + 1] +
+ topdir_var + ':' + bbpath_line[start + 1:])
+ else:
+ raise NotImplementedError(failmsg)
+ else:
+ index, bbfiles_line = sanity_conf_find_line('BBFILES', lines)
+ if bbfiles_line:
+ lines.insert(index, 'BBPATH = "' + topdir_var + '"\n')
+ else:
+ raise NotImplementedError(failmsg)
+
+ current_lconf += 1
+ sanity_conf_update(bblayers_fn, lines, 'LCONF_VERSION', current_lconf)
+ bb.note("Your conf/bblayers.conf has been automatically updated.")
+ return
+
+ elif current_lconf == 5 and lconf_version > 5:
+ # Null update, to avoid issues with people switching between poky and other distros
+ current_lconf = 6
+ sanity_conf_update(bblayers_fn, lines, 'LCONF_VERSION', current_lconf)
+ bb.note("Your conf/bblayers.conf has been automatically updated.")
+ return
+
+ status.addresult()
+
+ elif current_lconf == 6 and lconf_version > 6:
+ # Handle rename of meta-yocto -> meta-poky
+ # This marks the start of separate version numbers but code is needed in OE-Core
+ # for the migration, one last time.
+ layers = d.getVar('BBLAYERS').split()
+ layers = [ os.path.basename(path) for path in layers ]
+ if 'meta-yocto' in layers:
+ found = False
+ while True:
+ index, meta_yocto_line = sanity_conf_find_line(r'.*meta-yocto[\'"\s\n]', lines)
+ if meta_yocto_line:
+ lines[index] = meta_yocto_line.replace('meta-yocto', 'meta-poky')
+ found = True
+ else:
+ break
+ if not found:
+ raise NotImplementedError(failmsg)
+ index, meta_yocto_line = sanity_conf_find_line('LCONF_VERSION.*\n', lines)
+ if meta_yocto_line:
+ lines[index] = 'POKY_BBLAYERS_CONF_VERSION = "1"\n'
+ else:
+ raise NotImplementedError(failmsg)
+ with open(bblayers_fn, "w") as f:
+ f.write(''.join(lines))
+ bb.note("Your conf/bblayers.conf has been automatically updated.")
+ return
+ current_lconf += 1
+ sanity_conf_update(bblayers_fn, lines, 'LCONF_VERSION', current_lconf)
+ bb.note("Your conf/bblayers.conf has been automatically updated.")
+ return
+
+ raise NotImplementedError(failmsg)
+}
+
+def raise_sanity_error(msg, d, network_error=False):
+ if d.getVar("SANITY_USE_EVENTS") == "1":
+ try:
+ bb.event.fire(bb.event.SanityCheckFailed(msg, network_error), d)
+ except TypeError:
+ bb.event.fire(bb.event.SanityCheckFailed(msg), d)
+ return
+
+ bb.fatal(""" OE-core's config sanity checker detected a potential misconfiguration.
Either fix the cause of this error or at your own risk disable the checker (see sanity.conf).
Following is the list of potential problems / advisories:
%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 = []
+ if not tune:
+ return "No tuning found for %s multilib." % multilib
+ localdata = bb.data.createCopy(data)
+ if multilib != "default":
+ # Apply the overrides so we can look at the details.
+ 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()
+ if not features:
+ return "Tuning '%s' has no defined features, and cannot be used." % tune
+ valid_tunes = localdata.getVarFlags('TUNEVALID') or {}
+ conflicts = localdata.getVarFlags('TUNECONFLICTS') or {}
+ # [doc] is the documentation for the variable, not a real feature
+ if 'doc' in valid_tunes:
+ del valid_tunes['doc']
+ if 'doc' in conflicts:
+ del conflicts['doc']
+ for feature in features:
+ if feature in conflicts:
+ for conflict in conflicts[feature].split():
+ if conflict in features:
+ tune_errors.append("Feature '%s' conflicts with '%s'." %
+ (feature, conflict))
+ if feature in valid_tunes:
+ 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)
+
+def check_toolchain(data):
+ tune_error_set = []
+ deftune = data.getVar("DEFAULTTUNE")
+ tune_errors = check_toolchain_tune(data, deftune, 'default')
+ if tune_errors:
+ tune_error_set.append(tune_errors)
+
+ multilibs = (data.getVar("MULTILIB_VARIANTS") or "").split()
+ global_multilibs = (data.getVar("MULTILIB_GLOBAL_VARIANTS") or "").split()
+
+ if multilibs:
+ seen_libs = []
+ seen_tunes = []
+ for lib in multilibs:
+ if lib in seen_libs:
+ tune_error_set.append("The multilib '%s' appears more than once." % lib)
+ else:
+ 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)
+ if tune in seen_tunes:
+ tune_error_set.append("The tuning '%s' appears in more than one multilib." % tune)
+ else:
+ seen_libs.append(tune)
+ if tune == deftune:
+ tune_error_set.append("Multilib '%s' (%s) is also the default tuning." % (lib, deftune))
+ else:
+ tune_errors = check_toolchain_tune(data, tune, lib)
+ if tune_errors:
+ tune_error_set.append(tune_errors)
+ if tune_error_set:
+ return "Toolchain tunings invalid:\n" + '\n'.join(tune_error_set) + "\n"
+
+ return ""
+
def check_conf_exists(fn, data):
bbpath = []
- fn = bb.data.expand(fn, data)
- vbbpath = bb.data.getVar("BBPATH", data)
+ fn = data.expand(fn)
+ vbbpath = data.getVar("BBPATH", False)
if vbbpath:
bbpath += vbbpath.split(":")
for p in bbpath:
- currname = os.path.join(bb.data.expand(p, data), fn)
+ currname = os.path.join(data.expand(p), fn)
if os.access(currname, os.R_OK):
return True
return False
-def check_sanity_sstate_dir_change(sstate_dir):
- # Sanity checks to be done when the value of SSTATE_DIR changes
-
- # Check that SSTATE_DIR isn't on a filesystem with limited filename length (eg. eCryptFS)
- testmsg = ""
- if sstate_dir != "":
- testmsg = check_create_long_filename(sstate_dir, "SSTATE_DIR")
- return testmsg
-
-def check_sanity_tmpdir_change(tmpdir):
- # Sanity checks to be done when the value of TMPDIR changes
-
- # Check that TMPDIR isn't on a filesystem with limited filename length (eg. eCryptFS)
- testmsg = check_create_long_filename(tmpdir, "TMPDIR")
- return testmsg
-
-def check_sanity_version_change():
- # Sanity checks to be done when SANITY_VERSION changes
- return ""
-
-def check_pseudo_wrapper():
- import subprocess as sub
- # Check if bitbake wrapper is being used
- pseudo_build = os.environ.get( 'PSEUDO_BUILD' )
- if not pseudo_build:
- bb.warn("Bitbake has not been run using the bitbake wrapper (scripts/bitbake); this is likely because your PATH has been altered from that normally set up by the oe-init-build-env script. Not using the wrapper may result in failures during package installation, so it is highly recommended that you set your PATH back so that the wrapper script is being executed.")
-
- if (not pseudo_build) or pseudo_build == '2':
- # pseudo ought to be working, let's see if it is...
- p = sub.Popen(['sh', '-c', 'PSEUDO_DISABLED=0 id -u'],stdout=sub.PIPE,stderr=sub.PIPE)
- out, err = p.communicate()
- if out.rstrip() != '0':
- msg = "Pseudo is not functioning correctly, which will cause failures during package installation. Please check your configuration."
- if pseudo_build == '2':
- return msg
- else:
- bb.warn(msg)
- return ""
-
def check_create_long_filename(filepath, pathname):
- testfile = os.path.join(filepath, ''.join([`num`[-1] for num in xrange(1,200)]))
+ import string, random
+ testfile = os.path.join(filepath, ''.join(random.choice(string.ascii_letters) for x in range(200)))
try:
if not os.path.exists(filepath):
bb.utils.mkdirhier(filepath)
- f = file(testfile, "w")
+ f = open(testfile, "w")
f.close()
os.remove(testfile)
- except IOError as (errno, strerror):
- if errno == 36: # ENAMETOOLONG
+ except IOError as e:
+ import errno
+ err, strerror = e.args
+ if err == errno.ENAMETOOLONG:
return "Failed to create a file with a long name in %s. Please use a filesystem that does not unreasonably limit filename length.\n" % pathname
else:
- return "Failed to create a file in %s: %s" % (pathname, strerror)
+ return "Failed to create a file in %s: %s.\n" % (pathname, strerror)
+ except OSError as e:
+ errno, strerror = e.args
+ return "Failed to create %s directory in which to run long name sanity check: %s.\n" % (pathname, strerror)
return ""
-def check_sanity(e):
- from bb import note, error, data, __version__
+def check_path_length(filepath, pathname, limit):
+ if len(filepath) > limit:
+ return "The length of %s is longer than %s, this would cause unexpected errors, please use a shorter path.\n" % (pathname, limit)
+ return ""
- try:
- from distutils.version import LooseVersion
- except ImportError:
- def LooseVersion(v): print "WARNING: sanity.bbclass can't compare versions without python-distutils"; return 1
- import commands
+def get_filesystem_id(path):
+ status, result = oe.utils.getstatusoutput("stat -f -c '%s' %s" % ("%t", path))
+ if status == 0:
+ return result
+ else:
+ bb.warn("Can't get the filesystem id of: %s" % path)
+ return None
+
+# Check that the path isn't located on nfs.
+def check_not_nfs(path, name):
+ # The nfs' filesystem id is 6969
+ if get_filesystem_id(path) == "6969":
+ return "The %s: %s can't be located on nfs.\n" % (name, path)
+ return ""
- # Check the bitbake version meets minimum requirements
- minversion = data.getVar('BB_MIN_VERSION', e.data , True)
- if not minversion:
- # Hack: BB_MIN_VERSION hasn't been parsed yet so return
- # and wait for the next call
- print "Foo %s" % minversion
+# Check that path isn't a broken symlink
+def check_symlink(lnk, data):
+ if os.path.islink(lnk) and not os.path.exists(lnk):
+ raise_sanity_error("%s is a broken symlink." % lnk, data)
+
+def check_connectivity(d):
+ # URI's to check can be set in the CONNECTIVITY_CHECK_URIS variable
+ # using the same syntax as for SRC_URI. If the variable is not set
+ # the check is skipped
+ test_uris = (d.getVar('CONNECTIVITY_CHECK_URIS') or "").split()
+ retval = ""
+
+ bbn = d.getVar('BB_NO_NETWORK')
+ if bbn not in (None, '0', '1'):
+ return 'BB_NO_NETWORK should be "0" or "1", but it is "%s"' % bbn
+
+ # Only check connectivity if network enabled and the
+ # CONNECTIVITY_CHECK_URIS are set
+ network_enabled = not (bbn == '1')
+ check_enabled = len(test_uris)
+ if check_enabled and network_enabled:
+ # Take a copy of the data store and unset MIRRORS and PREMIRRORS
+ data = bb.data.createCopy(d)
+ data.delVar('PREMIRRORS')
+ data.delVar('MIRRORS')
+ try:
+ fetcher = bb.fetch2.Fetch(test_uris, data)
+ fetcher.checkstatus()
+ except Exception as err:
+ # Allow the message to be configured so that users can be
+ # pointed to a support mechanism.
+ 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"
+ retval = msg
+
+ return retval
+
+def check_supported_distro(sanity_data):
+ from fnmatch import fnmatch
+
+ tested_distros = sanity_data.getVar('SANITY_TESTED_DISTROS')
+ if not tested_distros:
return
- if 0 == os.getuid():
- raise_sanity_error("Do not use Bitbake as root.")
-
- messages = ""
+ try:
+ distro = oe.lsb.distro_identifier()
+ except Exception:
+ distro = None
- # Check the Python version, we now use Python 2.6 features in
- # various classes
- import sys
- if sys.hexversion < 0x020600F0:
- messages = messages + 'The system requires at least Python 2.6 to run. Please update your Python interpreter.\n'
+ if not distro:
+ bb.warn('Host distribution could not be determined; you may possibly experience unexpected failures. It is recommended that you use a tested distribution.')
- if (LooseVersion(__version__) < LooseVersion(minversion)):
- messages = messages + 'Bitbake version %s is required and version %s was found\n' % (minversion, __version__)
+ for supported in [x.strip() for x in tested_distros.split('\\n')]:
+ if fnmatch(distro, supported):
+ return
- # Check TARGET_ARCH is set
- if data.getVar('TARGET_ARCH', e.data, True) == 'INVALID':
- messages = messages + 'Please set TARGET_ARCH directly, or choose a MACHINE or DISTRO that does so.\n'
-
- # Check TARGET_OS is set
- if data.getVar('TARGET_OS', e.data, True) == 'INVALID':
- messages = messages + 'Please set TARGET_OS directly, or choose a MACHINE or DISTRO that does so.\n'
+ bb.warn('Host distribution "%s" has not been validated with this version of the build system; you may possibly experience unexpected failures. It is recommended that you use a tested distribution.' % distro)
- # Check we are using a valid lacal.conf
- current_conf = data.getVar('CONF_VERSION', e.data, True)
- conf_version = data.getVar('LOCALCONF_VERSION', e.data, True)
+# Checks we should only make if MACHINE is set correctly
+def check_sanity_validmachine(sanity_data):
+ messages = ""
- if current_conf != conf_version:
- messages = messages + "Your version of local.conf was generated from an older version of local.conf.sample and there have been updates made to this file. Please compare the two files and merge any changes before continuing.\nMatching the version numbers will remove this message.\n\"meld conf/local.conf conf/local.conf.sample\" is a good way to visualise the changes.\n"
+ # Check TUNE_ARCH is set
+ if sanity_data.getVar('TUNE_ARCH') == 'INVALID':
+ messages = messages + 'TUNE_ARCH is unset. Please ensure your MACHINE configuration includes a valid tune configuration file which will set this correctly.\n'
- # Check bblayers.conf is valid
- current_lconf = data.getVar('LCONF_VERSION', e.data, True)
- lconf_version = data.getVar('LAYER_CONF_VERSION', e.data, True)
- if current_lconf != lconf_version:
- messages = messages + "Your version of bblayers.conf was generated from an older version of bblayers.conf.sample and there have been updates made to this file. Please compare the two files and merge any changes before continuing.\nMatching the version numbers will remove this message.\n\"meld conf/bblayers.conf conf/bblayers.conf.sample\" is a good way to visualise the changes.\n"
+ # Check TARGET_OS is set
+ if sanity_data.getVar('TARGET_OS') == 'INVALID':
+ messages = messages + 'Please set TARGET_OS directly, or choose a MACHINE or DISTRO that does so.\n'
- # If we have a site.conf, check it's valid
- if check_conf_exists("conf/site.conf", e.data):
- current_sconf = data.getVar('SCONF_VERSION', e.data, True)
- sconf_version = data.getVar('SITE_CONF_VERSION', e.data, True)
- if current_sconf != sconf_version:
- messages = messages + "Your version of site.conf was generated from an older version of site.conf.sample and there have been updates made to this file. Please compare the two files and merge any changes before continuing.\nMatching the version numbers will remove this message.\n\"meld conf/site.conf conf/site.conf.sample\" is a good way to visualise the changes.\n"
+ # Check that we don't have duplicate entries in PACKAGE_ARCHS & that TUNE_PKGARCH is in PACKAGE_ARCHS
+ pkgarchs = sanity_data.getVar('PACKAGE_ARCHS')
+ tunepkg = sanity_data.getVar('TUNE_PKGARCH')
+ defaulttune = sanity_data.getVar('DEFAULTTUNE')
+ tunefound = False
+ seen = {}
+ dups = []
- assume_provided = data.getVar('ASSUME_PROVIDED', e.data , True).split()
- # Check user doesn't have ASSUME_PROVIDED = instead of += in local.conf
- if "diffstat-native" not in assume_provided:
- messages = messages + 'Please use ASSUME_PROVIDED +=, not ASSUME_PROVIDED = in your local.conf\n'
-
- # Check that the MACHINE is valid, if it is set
- if data.getVar('MACHINE', e.data, True):
- if not check_conf_exists("conf/machine/${MACHINE}.conf", e.data):
- messages = messages + 'Please set a valid MACHINE in your local.conf\n'
+ for pa in pkgarchs.split():
+ if seen.get(pa, 0) == 1:
+ dups.append(pa)
+ else:
+ seen[pa] = 1
+ if pa == tunepkg:
+ tunefound = True
- # Check that DL_DIR is set, exists and is writable. In theory, we should never even hit the check if DL_DIR isn't
- # set, since so much relies on it being set.
- dldir = data.getVar('DL_DIR', e.data, True)
- if not dldir:
- messages = messages + "DL_DIR is not set. Your environment is misconfigured, check that DL_DIR is set, and if the directory exists, that it is writable. \n"
- if os.path.exists(dldir) and not os.access(dldir, os.W_OK):
- messages = messages + "DL_DIR: %s exists but you do not appear to have write access to it. \n" % dldir
-
- # Check that the DISTRO is valid
- # need to take into account DISTRO renaming DISTRO
- #if not ( check_conf_exists("conf/distro/${DISTRO}.conf", e.data) or check_conf_exists("conf/distro/include/${DISTRO}.inc", e.data) ):
- # messages = messages + "DISTRO '%s' not found. Please set a valid DISTRO in your local.conf\n" % data.getVar("DISTRO", e.data, True )
+ if len(dups):
+ messages = messages + "Error, the PACKAGE_ARCHS variable contains duplicates. The following archs are listed more than once: %s" % " ".join(dups)
- missing = ""
+ if tunefound == False:
+ messages = messages + "Error, the PACKAGE_ARCHS variable (%s) for DEFAULTTUNE (%s) does not contain TUNE_PKGARCH (%s)." % (pkgarchs, defaulttune, tunepkg)
- if not check_app_exists("${MAKE}", e.data):
- missing = missing + "GNU make,"
+ return messages
- if not check_app_exists('${BUILD_PREFIX}gcc', e.data):
- missing = missing + "C Compiler (%sgcc)," % data.getVar("BUILD_PREFIX", e.data, True)
+# Checks if necessary to add option march to host gcc
+def check_gcc_march(sanity_data):
+ result = True
+ message = ""
- if not check_app_exists('${BUILD_PREFIX}g++', e.data):
- missing = missing + "C++ Compiler (%sg++)," % data.getVar("BUILD_PREFIX", e.data, True)
+ # Check if -march not in BUILD_CFLAGS
+ if sanity_data.getVar("BUILD_CFLAGS").find("-march") < 0:
+ result = False
- required_utilities = "patch help2man diffstat texi2html makeinfo cvs svn bzip2 tar gzip gawk hg chrpath wget cpio"
+ # Construct a test file
+ f = open("gcc_test.c", "w")
+ f.write("int main (){ volatile int atomic = 2; __sync_bool_compare_and_swap (&atomic, 2, 3); return 0; }\n")
+ f.close()
- # qemu-native needs gcc 3.x
- if "qemu-native" not in assume_provided and "gcc3-native" in assume_provided:
- gcc_version = commands.getoutput("${BUILD_PREFIX}gcc --version | head -n 1 | cut -f 3 -d ' '")
+ # Check if GCC could work without march
+ if not result:
+ status,res = oe.utils.getstatusoutput(sanity_data.expand("${BUILD_CC} gcc_test.c -o gcc_test"))
+ if status == 0:
+ result = True;
+
+ if not result:
+ status,res = oe.utils.getstatusoutput(sanity_data.expand("${BUILD_CC} -march=native gcc_test.c -o gcc_test"))
+ if status == 0:
+ message = "BUILD_CFLAGS_append = \" -march=native\""
+ result = True;
+
+ if not result:
+ build_arch = sanity_data.getVar('BUILD_ARCH')
+ status,res = oe.utils.getstatusoutput(sanity_data.expand("${BUILD_CC} -march=%s gcc_test.c -o gcc_test" % build_arch))
+ if status == 0:
+ message = "BUILD_CFLAGS_append = \" -march=%s\"" % build_arch
+ result = True;
+
+ os.remove("gcc_test.c")
+ if os.path.exists("gcc_test"):
+ os.remove("gcc_test")
+
+ return (result, message)
+
+# 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
+ status, result = oe.utils.getstatusoutput("make --version")
+ if status != 0:
+ return "Unable to execute make --version, exit code %s\n" % status
+ version = result.split()[2]
+ if LooseVersion(version) == LooseVersion("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")
+ f.write("\n")
+ f.write("makefile_test_a.c:\n")
+ f.write(" touch $@\n")
+ f.write("\n")
+ f.write("makefile_test_b.c:\n")
+ f.write(" touch $@\n")
+ f.close()
- if not check_gcc3(e.data) and gcc_version[0] != '3':
- messages = messages + "gcc3-native was in ASSUME_PROVIDED but the gcc-3.x binary can't be found in PATH"
- missing = missing + "gcc-3.x (needed for qemu-native),"
+ # Check if make 3.82 has been patched
+ status,result = oe.utils.getstatusoutput("make -f makefile_test")
+
+ os.remove("makefile_test")
+ if os.path.exists("makefile_test_a.c"):
+ os.remove("makefile_test_a.c")
+ if os.path.exists("makefile_test_b.c"):
+ os.remove("makefile_test_b.c")
+ if os.path.exists("makefile_test.a"):
+ os.remove("makefile_test.a")
+
+ if status != 0:
+ return "Your version of make 3.82 is broken. Please revert to 3.81 or install a patched version.\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
+def check_tar_version(sanity_data):
+ from distutils.version import LooseVersion
+ status, result = oe.utils.getstatusoutput("tar --version")
+ if status != 0:
+ return "Unable to execute tar --version, exit code %s\n" % status
+ 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"
+ 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
+ status, result = oe.utils.getstatusoutput("git --version 2> /dev/null")
+ if status != 0:
+ return "Unable to execute git --version, exit code %s\n" % status
+ version = result.split()[2]
+ if LooseVersion(version) < LooseVersion("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
+
+# Check the required perl modules which may not be installed by default
+def check_perl_modules(sanity_data):
+ ret = ""
+ modules = ( "Text::ParseWords", "Thread::Queue", "Data::Dumper" )
+ errresult = ''
+ for m in modules:
+ status, result = oe.utils.getstatusoutput("perl -e 'use %s'" % m)
+ if status != 0:
+ errresult += result
+ ret += "%s " % m
+ if ret:
+ return "Required perl module(s) not found: %s\n\n%s\n" % (ret, errresult)
+ return None
+
+def sanity_check_conffiles(d):
+ funcs = d.getVar('BBLAYERS_CONF_UPDATE_FUNCS').split()
+ for func in funcs:
+ conffile, current_version, required_version, func = func.split(":")
+ if check_conf_exists(conffile, d) and d.getVar(current_version) is not None and \
+ d.getVar(current_version) != d.getVar(required_version):
+ try:
+ bb.build.exec_func(func, d, pythonexception=True)
+ except NotImplementedError as e:
+ bb.fatal(str(e))
+ d.setVar("BB_INVALIDCONF", True)
+
+def sanity_handle_abichanges(status, d):
+ #
+ # Check the 'ABI' of TMPDIR
+ #
+ import subprocess
- if "qemu-native" in assume_provided:
- if not check_app_exists("qemu-arm", e.data):
- messages = messages + "qemu-native was in ASSUME_PROVIDED but the QEMU binaries (qemu-arm) can't be found in PATH"
+ current_abi = d.getVar('OELAYOUT_ABI')
+ abifile = d.getVar('SANITY_ABIFILE')
+ if os.path.exists(abifile):
+ with open(abifile, "r") as f:
+ abi = f.read().strip()
+ if not abi.isdigit():
+ with open(abifile, "w") as f:
+ 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 (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))
+ else:
+ with open(abifile, "w") as f:
+ f.write(current_abi)
- if "." in data.getVar('PATH', e.data, True).split(":"):
- messages = messages + "PATH contains '.' which will break the build, please remove this"
+def check_sanity_sstate_dir_change(sstate_dir, data):
+ # Sanity checks to be done when the value of SSTATE_DIR changes
- if data.getVar('TARGET_ARCH', e.data, True) == "arm":
- # This path is no longer user-readable in modern (very recent) Linux
+ # Check that SSTATE_DIR isn't on a filesystem with limited filename length (eg. eCryptFS)
+ testmsg = ""
+ if sstate_dir != "":
+ testmsg = check_create_long_filename(sstate_dir, "SSTATE_DIR")
+ # If we don't have permissions to SSTATE_DIR, suggest the user set it as an SSTATE_MIRRORS
try:
- if os.path.exists("/proc/sys/vm/mmap_min_addr"):
- f = open("/proc/sys/vm/mmap_min_addr", "r")
- try:
- if (int(f.read().strip()) > 65536):
- messages = messages + "/proc/sys/vm/mmap_min_addr is not <= 65536. This will cause problems with qemu so please fix the value (as root).\n\nTo fix this in later reboots, set vm.mmap_min_addr = 65536 in /etc/sysctl.conf.\n"
- finally:
- f.close()
- except:
+ err = testmsg.split(': ')[1].strip()
+ if err == "Permission denied.":
+ testmsg = testmsg + "You could try using %s in SSTATE_MIRRORS rather than as an SSTATE_CACHE.\n" % (sstate_dir)
+ except IndexError:
pass
+ return testmsg
+
+def check_sanity_version_change(status, d):
+ # Sanity checks to be done when SANITY_VERSION or NATIVELSBSTRING changes
+ # 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
+ 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
- for util in required_utilities.split():
- if not check_app_exists( util, e.data ):
- missing = missing + "%s," % util
+ status.addresult(check_make_version(d))
+ status.addresult(check_tar_version(d))
+ status.addresult(check_git_version(d))
+ status.addresult(check_perl_modules(d))
- if missing != "":
- missing = missing.rstrip(',')
- messages = messages + "Please install following missing utilities: %s\n" % missing
+ missing = ""
- pseudo_msg = check_pseudo_wrapper()
- if pseudo_msg != "":
- messages = messages + pseudo_msg + '\n'
+ if not check_app_exists("${MAKE}", d):
+ missing = missing + "GNU make,"
- # Check if DISPLAY is set if IMAGETEST is set
- if not data.getVar( 'DISPLAY', e.data, True ) and data.getVar( 'IMAGETEST', e.data, True ) == 'qemu':
- messages = messages + 'qemuimagetest needs a X desktop to start qemu, please set DISPLAY correctly (e.g. DISPLAY=:1.0)\n'
+ if not check_app_exists('${BUILD_CC}', d):
+ missing = missing + "C Compiler (%s)," % d.getVar("BUILD_CC")
- if data.getVar('PATCHRESOLVE', e.data, True) != 'noop':
- # Ensure we have the binary for TERMCMD, as when patch application fails the error is fairly intimidating
- termcmd = data.getVar("TERMCMD", e.data, True)
- term = termcmd.split()[0]
- if not check_app_exists(term, e.data):
- messages = messages + "The console for use in patch error resolution is not available, please install %s or set TERMCMD and TERMCMDRUN (as documented in local.conf).\n" % term
+ if not check_app_exists('${BUILD_CXX}', d):
+ missing = missing + "C++ Compiler (%s)," % d.getVar("BUILD_CXX")
- if os.path.basename(os.readlink('/bin/sh')) == 'dash':
- messages = messages + "Using dash as /bin/sh causes various subtle build problems, please use bash instead (e.g. 'dpkg-reconfigure dash' on an Ubuntu system.\n"
+ required_utilities = d.getVar('SANITY_REQUIRED_UTILITIES')
- omask = os.umask(022)
- if omask & 0755:
- messages = messages + "Please use a umask which allows a+rx and u+rwx\n"
- os.umask(omask)
+ for util in required_utilities.split():
+ if not check_app_exists(util, d):
+ missing = missing + "%s," % util
- oes_bb_conf = data.getVar( 'OES_BITBAKE_CONF', e.data, True )
- if not oes_bb_conf:
- messages = messages + 'You do not include OpenEmbeddeds version of conf/bitbake.conf. This means your environment is misconfigured, in particular check BBPATH.\n'
+ if missing:
+ missing = missing.rstrip(',')
+ status.addresult("Please install the following missing utilities: %s\n" % missing)
- nolibs = data.getVar('NO32LIBS', e.data, True)
+ assume_provided = d.getVar('ASSUME_PROVIDED').split()
+ # Check user doesn't have ASSUME_PROVIDED = instead of += in local.conf
+ if "diffstat-native" not in assume_provided:
+ status.addresult('Please use ASSUME_PROVIDED +=, not ASSUME_PROVIDED = in your local.conf\n')
+
+ if "qemu-native" in assume_provided:
+ if not check_app_exists("qemu-arm", d):
+ status.addresult("qemu-native was in ASSUME_PROVIDED but the QEMU binaries (qemu-arm) can't be found in PATH")
+
+ if "libsdl-native" in assume_provided:
+ if not check_app_exists("sdl-config", d):
+ status.addresult("libsdl-native is set to be ASSUME_PROVIDED but sdl-config can't be found in PATH. Please either install it, or configure qemu not to require sdl.")
+
+ (result, message) = check_gcc_march(d)
+ if result and message:
+ status.addresult("Your gcc version is older than 4.5, please add the following param to local.conf\n \
+ %s\n" % message)
+ if not result:
+ status.addresult("Your gcc version is older than 4.5 or is not working properly. Please verify you can build")
+ status.addresult(" and link something that uses atomic operations, such as: \n")
+ status.addresult(" __sync_bool_compare_and_swap (&atomic, 2, 3);\n")
+
+ # Check that TMPDIR isn't on a filesystem with limited filename length (eg. eCryptFS)
+ tmpdir = d.getVar('TMPDIR')
+ status.addresult(check_create_long_filename(tmpdir, "TMPDIR"))
+ tmpdirmode = os.stat(tmpdir).st_mode
+ if (tmpdirmode & stat.S_ISGID):
+ status.addresult("TMPDIR is setgid, please don't build in a setgid directory")
+ if (tmpdirmode &