xref: /OK3568_Linux_fs/yocto/poky/meta/classes/sanity.bbclass (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun#
2*4882a593Smuzhiyun# Sanity check the users setup for common misconfigurations
3*4882a593Smuzhiyun#
4*4882a593Smuzhiyun
5*4882a593SmuzhiyunSANITY_REQUIRED_UTILITIES ?= "patch diffstat git bzip2 tar \
6*4882a593Smuzhiyun    gzip gawk chrpath wget cpio perl file which"
7*4882a593Smuzhiyun
8*4882a593Smuzhiyundef bblayers_conf_file(d):
9*4882a593Smuzhiyun    return os.path.join(d.getVar('TOPDIR'), 'conf/bblayers.conf')
10*4882a593Smuzhiyun
11*4882a593Smuzhiyundef sanity_conf_read(fn):
12*4882a593Smuzhiyun    with open(fn, 'r') as f:
13*4882a593Smuzhiyun        lines = f.readlines()
14*4882a593Smuzhiyun    return lines
15*4882a593Smuzhiyun
16*4882a593Smuzhiyundef sanity_conf_find_line(pattern, lines):
17*4882a593Smuzhiyun    import re
18*4882a593Smuzhiyun    return next(((index, line)
19*4882a593Smuzhiyun        for index, line in enumerate(lines)
20*4882a593Smuzhiyun        if re.search(pattern, line)), (None, None))
21*4882a593Smuzhiyun
22*4882a593Smuzhiyundef sanity_conf_update(fn, lines, version_var_name, new_version):
23*4882a593Smuzhiyun    index, line = sanity_conf_find_line(r"^%s" % version_var_name, lines)
24*4882a593Smuzhiyun    lines[index] = '%s = "%d"\n' % (version_var_name, new_version)
25*4882a593Smuzhiyun    with open(fn, "w") as f:
26*4882a593Smuzhiyun        f.write(''.join(lines))
27*4882a593Smuzhiyun
28*4882a593Smuzhiyun# Functions added to this variable MUST throw a NotImplementedError exception unless
29*4882a593Smuzhiyun# they successfully changed the config version in the config file. Exceptions
30*4882a593Smuzhiyun# are used since exec_func doesn't handle return values.
31*4882a593SmuzhiyunBBLAYERS_CONF_UPDATE_FUNCS += " \
32*4882a593Smuzhiyun    conf/bblayers.conf:LCONF_VERSION:LAYER_CONF_VERSION:oecore_update_bblayers \
33*4882a593Smuzhiyun    conf/local.conf:CONF_VERSION:LOCALCONF_VERSION:oecore_update_localconf \
34*4882a593Smuzhiyun    conf/site.conf:SCONF_VERSION:SITE_CONF_VERSION:oecore_update_siteconf \
35*4882a593Smuzhiyun"
36*4882a593Smuzhiyun
37*4882a593SmuzhiyunSANITY_DIFF_TOOL ?= "meld"
38*4882a593Smuzhiyun
39*4882a593SmuzhiyunSANITY_LOCALCONF_SAMPLE ?= "${COREBASE}/meta*/conf/local.conf.sample"
40*4882a593Smuzhiyunpython oecore_update_localconf() {
41*4882a593Smuzhiyun    # Check we are using a valid local.conf
42*4882a593Smuzhiyun    current_conf  = d.getVar('CONF_VERSION')
43*4882a593Smuzhiyun    conf_version =  d.getVar('LOCALCONF_VERSION')
44*4882a593Smuzhiyun
45*4882a593Smuzhiyun    failmsg = """Your version of local.conf was generated from an older/newer version of
46*4882a593Smuzhiyunlocal.conf.sample and there have been updates made to this file. Please compare the two
47*4882a593Smuzhiyunfiles and merge any changes before continuing.
48*4882a593Smuzhiyun
49*4882a593SmuzhiyunMatching the version numbers will remove this message.
50*4882a593Smuzhiyun
51*4882a593Smuzhiyun\"${SANITY_DIFF_TOOL} conf/local.conf ${SANITY_LOCALCONF_SAMPLE}\"
52*4882a593Smuzhiyun
53*4882a593Smuzhiyunis a good way to visualise the changes."""
54*4882a593Smuzhiyun    failmsg = d.expand(failmsg)
55*4882a593Smuzhiyun
56*4882a593Smuzhiyun    raise NotImplementedError(failmsg)
57*4882a593Smuzhiyun}
58*4882a593Smuzhiyun
59*4882a593SmuzhiyunSANITY_SITECONF_SAMPLE ?= "${COREBASE}/meta*/conf/site.conf.sample"
60*4882a593Smuzhiyunpython oecore_update_siteconf() {
61*4882a593Smuzhiyun    # If we have a site.conf, check it's valid
62*4882a593Smuzhiyun    current_sconf = d.getVar('SCONF_VERSION')
63*4882a593Smuzhiyun    sconf_version = d.getVar('SITE_CONF_VERSION')
64*4882a593Smuzhiyun
65*4882a593Smuzhiyun    failmsg = """Your version of site.conf was generated from an older version of
66*4882a593Smuzhiyunsite.conf.sample and there have been updates made to this file. Please compare the two
67*4882a593Smuzhiyunfiles and merge any changes before continuing.
68*4882a593Smuzhiyun
69*4882a593SmuzhiyunMatching the version numbers will remove this message.
70*4882a593Smuzhiyun
71*4882a593Smuzhiyun\"${SANITY_DIFF_TOOL} conf/site.conf ${SANITY_SITECONF_SAMPLE}\"
72*4882a593Smuzhiyun
73*4882a593Smuzhiyunis a good way to visualise the changes."""
74*4882a593Smuzhiyun    failmsg = d.expand(failmsg)
75*4882a593Smuzhiyun
76*4882a593Smuzhiyun    raise NotImplementedError(failmsg)
77*4882a593Smuzhiyun}
78*4882a593Smuzhiyun
79*4882a593SmuzhiyunSANITY_BBLAYERCONF_SAMPLE ?= "${COREBASE}/meta*/conf/bblayers.conf.sample"
80*4882a593Smuzhiyunpython oecore_update_bblayers() {
81*4882a593Smuzhiyun    # bblayers.conf is out of date, so see if we can resolve that
82*4882a593Smuzhiyun
83*4882a593Smuzhiyun    current_lconf = int(d.getVar('LCONF_VERSION'))
84*4882a593Smuzhiyun    lconf_version = int(d.getVar('LAYER_CONF_VERSION'))
85*4882a593Smuzhiyun
86*4882a593Smuzhiyun    failmsg = """Your version of bblayers.conf has the wrong LCONF_VERSION (has ${LCONF_VERSION}, expecting ${LAYER_CONF_VERSION}).
87*4882a593SmuzhiyunPlease compare your file against bblayers.conf.sample and merge any changes before continuing.
88*4882a593Smuzhiyun"${SANITY_DIFF_TOOL} conf/bblayers.conf ${SANITY_BBLAYERCONF_SAMPLE}"
89*4882a593Smuzhiyun
90*4882a593Smuzhiyunis a good way to visualise the changes."""
91*4882a593Smuzhiyun    failmsg = d.expand(failmsg)
92*4882a593Smuzhiyun
93*4882a593Smuzhiyun    if not current_lconf:
94*4882a593Smuzhiyun        raise NotImplementedError(failmsg)
95*4882a593Smuzhiyun
96*4882a593Smuzhiyun    lines = []
97*4882a593Smuzhiyun
98*4882a593Smuzhiyun    if current_lconf < 4:
99*4882a593Smuzhiyun        raise NotImplementedError(failmsg)
100*4882a593Smuzhiyun
101*4882a593Smuzhiyun    bblayers_fn = bblayers_conf_file(d)
102*4882a593Smuzhiyun    lines = sanity_conf_read(bblayers_fn)
103*4882a593Smuzhiyun
104*4882a593Smuzhiyun    if current_lconf == 4 and lconf_version > 4:
105*4882a593Smuzhiyun        topdir_var = '$' + '{TOPDIR}'
106*4882a593Smuzhiyun        index, bbpath_line = sanity_conf_find_line('BBPATH', lines)
107*4882a593Smuzhiyun        if bbpath_line:
108*4882a593Smuzhiyun            start = bbpath_line.find('"')
109*4882a593Smuzhiyun            if start != -1 and (len(bbpath_line) != (start + 1)):
110*4882a593Smuzhiyun                if bbpath_line[start + 1] == '"':
111*4882a593Smuzhiyun                    lines[index] = (bbpath_line[:start + 1] +
112*4882a593Smuzhiyun                                    topdir_var + bbpath_line[start + 1:])
113*4882a593Smuzhiyun                else:
114*4882a593Smuzhiyun                    if not topdir_var in bbpath_line:
115*4882a593Smuzhiyun                        lines[index] = (bbpath_line[:start + 1] +
116*4882a593Smuzhiyun                                    topdir_var + ':' + bbpath_line[start + 1:])
117*4882a593Smuzhiyun            else:
118*4882a593Smuzhiyun                raise NotImplementedError(failmsg)
119*4882a593Smuzhiyun        else:
120*4882a593Smuzhiyun            index, bbfiles_line = sanity_conf_find_line('BBFILES', lines)
121*4882a593Smuzhiyun            if bbfiles_line:
122*4882a593Smuzhiyun                lines.insert(index, 'BBPATH = "' + topdir_var + '"\n')
123*4882a593Smuzhiyun            else:
124*4882a593Smuzhiyun                raise NotImplementedError(failmsg)
125*4882a593Smuzhiyun
126*4882a593Smuzhiyun        current_lconf += 1
127*4882a593Smuzhiyun        sanity_conf_update(bblayers_fn, lines, 'LCONF_VERSION', current_lconf)
128*4882a593Smuzhiyun        bb.note("Your conf/bblayers.conf has been automatically updated.")
129*4882a593Smuzhiyun        return
130*4882a593Smuzhiyun
131*4882a593Smuzhiyun    elif current_lconf == 5 and lconf_version > 5:
132*4882a593Smuzhiyun        # Null update, to avoid issues with people switching between poky and other distros
133*4882a593Smuzhiyun        current_lconf = 6
134*4882a593Smuzhiyun        sanity_conf_update(bblayers_fn, lines, 'LCONF_VERSION', current_lconf)
135*4882a593Smuzhiyun        bb.note("Your conf/bblayers.conf has been automatically updated.")
136*4882a593Smuzhiyun        return
137*4882a593Smuzhiyun
138*4882a593Smuzhiyun        status.addresult()
139*4882a593Smuzhiyun
140*4882a593Smuzhiyun    elif current_lconf == 6 and lconf_version > 6:
141*4882a593Smuzhiyun        # Handle rename of meta-yocto -> meta-poky
142*4882a593Smuzhiyun        # This marks the start of separate version numbers but code is needed in OE-Core
143*4882a593Smuzhiyun        # for the migration, one last time.
144*4882a593Smuzhiyun        layers = d.getVar('BBLAYERS').split()
145*4882a593Smuzhiyun        layers = [ os.path.basename(path) for path in layers ]
146*4882a593Smuzhiyun        if 'meta-yocto' in layers:
147*4882a593Smuzhiyun            found = False
148*4882a593Smuzhiyun            while True:
149*4882a593Smuzhiyun                index, meta_yocto_line = sanity_conf_find_line(r'.*meta-yocto[\'"\s\n]', lines)
150*4882a593Smuzhiyun                if meta_yocto_line:
151*4882a593Smuzhiyun                    lines[index] = meta_yocto_line.replace('meta-yocto', 'meta-poky')
152*4882a593Smuzhiyun                    found = True
153*4882a593Smuzhiyun                else:
154*4882a593Smuzhiyun                    break
155*4882a593Smuzhiyun            if not found:
156*4882a593Smuzhiyun                raise NotImplementedError(failmsg)
157*4882a593Smuzhiyun            index, meta_yocto_line = sanity_conf_find_line('LCONF_VERSION.*\n', lines)
158*4882a593Smuzhiyun            if meta_yocto_line:
159*4882a593Smuzhiyun                lines[index] = 'POKY_BBLAYERS_CONF_VERSION = "1"\n'
160*4882a593Smuzhiyun            else:
161*4882a593Smuzhiyun                raise NotImplementedError(failmsg)
162*4882a593Smuzhiyun            with open(bblayers_fn, "w") as f:
163*4882a593Smuzhiyun                f.write(''.join(lines))
164*4882a593Smuzhiyun            bb.note("Your conf/bblayers.conf has been automatically updated.")
165*4882a593Smuzhiyun            return
166*4882a593Smuzhiyun        current_lconf += 1
167*4882a593Smuzhiyun        sanity_conf_update(bblayers_fn, lines, 'LCONF_VERSION', current_lconf)
168*4882a593Smuzhiyun        bb.note("Your conf/bblayers.conf has been automatically updated.")
169*4882a593Smuzhiyun        return
170*4882a593Smuzhiyun
171*4882a593Smuzhiyun    raise NotImplementedError(failmsg)
172*4882a593Smuzhiyun}
173*4882a593Smuzhiyun
174*4882a593Smuzhiyundef raise_sanity_error(msg, d, network_error=False):
175*4882a593Smuzhiyun    if d.getVar("SANITY_USE_EVENTS") == "1":
176*4882a593Smuzhiyun        try:
177*4882a593Smuzhiyun            bb.event.fire(bb.event.SanityCheckFailed(msg, network_error), d)
178*4882a593Smuzhiyun        except TypeError:
179*4882a593Smuzhiyun            bb.event.fire(bb.event.SanityCheckFailed(msg), d)
180*4882a593Smuzhiyun        return
181*4882a593Smuzhiyun
182*4882a593Smuzhiyun    bb.fatal(""" OE-core's config sanity checker detected a potential misconfiguration.
183*4882a593Smuzhiyun    Either fix the cause of this error or at your own risk disable the checker (see sanity.conf).
184*4882a593Smuzhiyun    Following is the list of potential problems / advisories:
185*4882a593Smuzhiyun
186*4882a593Smuzhiyun    %s""" % msg)
187*4882a593Smuzhiyun
188*4882a593Smuzhiyun# Check a single tune for validity.
189*4882a593Smuzhiyundef check_toolchain_tune(data, tune, multilib):
190*4882a593Smuzhiyun    tune_errors = []
191*4882a593Smuzhiyun    if not tune:
192*4882a593Smuzhiyun        return "No tuning found for %s multilib." % multilib
193*4882a593Smuzhiyun    localdata = bb.data.createCopy(data)
194*4882a593Smuzhiyun    if multilib != "default":
195*4882a593Smuzhiyun        # Apply the overrides so we can look at the details.
196*4882a593Smuzhiyun        overrides = localdata.getVar("OVERRIDES", False) + ":virtclass-multilib-" + multilib
197*4882a593Smuzhiyun        localdata.setVar("OVERRIDES", overrides)
198*4882a593Smuzhiyun    bb.debug(2, "Sanity-checking tuning '%s' (%s) features:" % (tune, multilib))
199*4882a593Smuzhiyun    features = (localdata.getVar("TUNE_FEATURES:tune-%s" % tune) or "").split()
200*4882a593Smuzhiyun    if not features:
201*4882a593Smuzhiyun        return "Tuning '%s' has no defined features, and cannot be used." % tune
202*4882a593Smuzhiyun    valid_tunes = localdata.getVarFlags('TUNEVALID') or {}
203*4882a593Smuzhiyun    conflicts = localdata.getVarFlags('TUNECONFLICTS') or {}
204*4882a593Smuzhiyun    # [doc] is the documentation for the variable, not a real feature
205*4882a593Smuzhiyun    if 'doc' in valid_tunes:
206*4882a593Smuzhiyun        del valid_tunes['doc']
207*4882a593Smuzhiyun    if 'doc' in conflicts:
208*4882a593Smuzhiyun        del conflicts['doc']
209*4882a593Smuzhiyun    for feature in features:
210*4882a593Smuzhiyun        if feature in conflicts:
211*4882a593Smuzhiyun            for conflict in conflicts[feature].split():
212*4882a593Smuzhiyun                if conflict in features:
213*4882a593Smuzhiyun                    tune_errors.append("Feature '%s' conflicts with '%s'." %
214*4882a593Smuzhiyun                        (feature, conflict))
215*4882a593Smuzhiyun        if feature in valid_tunes:
216*4882a593Smuzhiyun            bb.debug(2, "  %s: %s" % (feature, valid_tunes[feature]))
217*4882a593Smuzhiyun        else:
218*4882a593Smuzhiyun            tune_errors.append("Feature '%s' is not defined." % feature)
219*4882a593Smuzhiyun    if tune_errors:
220*4882a593Smuzhiyun        return "Tuning '%s' has the following errors:\n" % tune + '\n'.join(tune_errors)
221*4882a593Smuzhiyun
222*4882a593Smuzhiyundef check_toolchain(data):
223*4882a593Smuzhiyun    tune_error_set = []
224*4882a593Smuzhiyun    deftune = data.getVar("DEFAULTTUNE")
225*4882a593Smuzhiyun    tune_errors = check_toolchain_tune(data, deftune, 'default')
226*4882a593Smuzhiyun    if tune_errors:
227*4882a593Smuzhiyun        tune_error_set.append(tune_errors)
228*4882a593Smuzhiyun
229*4882a593Smuzhiyun    multilibs = (data.getVar("MULTILIB_VARIANTS") or "").split()
230*4882a593Smuzhiyun    global_multilibs = (data.getVar("MULTILIB_GLOBAL_VARIANTS") or "").split()
231*4882a593Smuzhiyun
232*4882a593Smuzhiyun    if multilibs:
233*4882a593Smuzhiyun        seen_libs = []
234*4882a593Smuzhiyun        seen_tunes = []
235*4882a593Smuzhiyun        for lib in multilibs:
236*4882a593Smuzhiyun            if lib in seen_libs:
237*4882a593Smuzhiyun                tune_error_set.append("The multilib '%s' appears more than once." % lib)
238*4882a593Smuzhiyun            else:
239*4882a593Smuzhiyun                seen_libs.append(lib)
240*4882a593Smuzhiyun            if not lib in global_multilibs:
241*4882a593Smuzhiyun                tune_error_set.append("Multilib %s is not present in MULTILIB_GLOBAL_VARIANTS" % lib)
242*4882a593Smuzhiyun            tune = data.getVar("DEFAULTTUNE:virtclass-multilib-%s" % lib)
243*4882a593Smuzhiyun            if tune in seen_tunes:
244*4882a593Smuzhiyun                tune_error_set.append("The tuning '%s' appears in more than one multilib." % tune)
245*4882a593Smuzhiyun            else:
246*4882a593Smuzhiyun                seen_libs.append(tune)
247*4882a593Smuzhiyun            if tune == deftune:
248*4882a593Smuzhiyun                tune_error_set.append("Multilib '%s' (%s) is also the default tuning." % (lib, deftune))
249*4882a593Smuzhiyun            else:
250*4882a593Smuzhiyun                tune_errors = check_toolchain_tune(data, tune, lib)
251*4882a593Smuzhiyun            if tune_errors:
252*4882a593Smuzhiyun                tune_error_set.append(tune_errors)
253*4882a593Smuzhiyun    if tune_error_set:
254*4882a593Smuzhiyun        return "Toolchain tunings invalid:\n" + '\n'.join(tune_error_set) + "\n"
255*4882a593Smuzhiyun
256*4882a593Smuzhiyun    return ""
257*4882a593Smuzhiyun
258*4882a593Smuzhiyundef check_conf_exists(fn, data):
259*4882a593Smuzhiyun    bbpath = []
260*4882a593Smuzhiyun    fn = data.expand(fn)
261*4882a593Smuzhiyun    vbbpath = data.getVar("BBPATH", False)
262*4882a593Smuzhiyun    if vbbpath:
263*4882a593Smuzhiyun        bbpath += vbbpath.split(":")
264*4882a593Smuzhiyun    for p in bbpath:
265*4882a593Smuzhiyun        currname = os.path.join(data.expand(p), fn)
266*4882a593Smuzhiyun        if os.access(currname, os.R_OK):
267*4882a593Smuzhiyun            return True
268*4882a593Smuzhiyun    return False
269*4882a593Smuzhiyun
270*4882a593Smuzhiyundef check_create_long_filename(filepath, pathname):
271*4882a593Smuzhiyun    import string, random
272*4882a593Smuzhiyun    testfile = os.path.join(filepath, ''.join(random.choice(string.ascii_letters) for x in range(200)))
273*4882a593Smuzhiyun    try:
274*4882a593Smuzhiyun        if not os.path.exists(filepath):
275*4882a593Smuzhiyun            bb.utils.mkdirhier(filepath)
276*4882a593Smuzhiyun        f = open(testfile, "w")
277*4882a593Smuzhiyun        f.close()
278*4882a593Smuzhiyun        os.remove(testfile)
279*4882a593Smuzhiyun    except IOError as e:
280*4882a593Smuzhiyun        import errno
281*4882a593Smuzhiyun        err, strerror = e.args
282*4882a593Smuzhiyun        if err == errno.ENAMETOOLONG:
283*4882a593Smuzhiyun            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
284*4882a593Smuzhiyun        else:
285*4882a593Smuzhiyun            return "Failed to create a file in %s: %s.\n" % (pathname, strerror)
286*4882a593Smuzhiyun    except OSError as e:
287*4882a593Smuzhiyun        errno, strerror = e.args
288*4882a593Smuzhiyun        return "Failed to create %s directory in which to run long name sanity check: %s.\n" % (pathname, strerror)
289*4882a593Smuzhiyun    return ""
290*4882a593Smuzhiyun
291*4882a593Smuzhiyundef check_path_length(filepath, pathname, limit):
292*4882a593Smuzhiyun    if len(filepath) > limit:
293*4882a593Smuzhiyun        return "The length of %s is longer than %s, this would cause unexpected errors, please use a shorter path.\n" % (pathname, limit)
294*4882a593Smuzhiyun    return ""
295*4882a593Smuzhiyun
296*4882a593Smuzhiyundef get_filesystem_id(path):
297*4882a593Smuzhiyun    import subprocess
298*4882a593Smuzhiyun    try:
299*4882a593Smuzhiyun        return subprocess.check_output(["stat", "-f", "-c", "%t", path]).decode('utf-8').strip()
300*4882a593Smuzhiyun    except subprocess.CalledProcessError:
301*4882a593Smuzhiyun        bb.warn("Can't get filesystem id of: %s" % path)
302*4882a593Smuzhiyun        return None
303*4882a593Smuzhiyun
304*4882a593Smuzhiyun# Check that the path isn't located on nfs.
305*4882a593Smuzhiyundef check_not_nfs(path, name):
306*4882a593Smuzhiyun    # The nfs' filesystem id is 6969
307*4882a593Smuzhiyun    if get_filesystem_id(path) == "6969":
308*4882a593Smuzhiyun        return "The %s: %s can't be located on nfs.\n" % (name, path)
309*4882a593Smuzhiyun    return ""
310*4882a593Smuzhiyun
311*4882a593Smuzhiyun# Check that the path is on a case-sensitive file system
312*4882a593Smuzhiyundef check_case_sensitive(path, name):
313*4882a593Smuzhiyun    import tempfile
314*4882a593Smuzhiyun    with tempfile.NamedTemporaryFile(prefix='TmP', dir=path) as tmp_file:
315*4882a593Smuzhiyun        if os.path.exists(tmp_file.name.lower()):
316*4882a593Smuzhiyun            return "The %s (%s) can't be on a case-insensitive file system.\n" % (name, path)
317*4882a593Smuzhiyun        return ""
318*4882a593Smuzhiyun
319*4882a593Smuzhiyun# Check that path isn't a broken symlink
320*4882a593Smuzhiyundef check_symlink(lnk, data):
321*4882a593Smuzhiyun    if os.path.islink(lnk) and not os.path.exists(lnk):
322*4882a593Smuzhiyun        raise_sanity_error("%s is a broken symlink." % lnk, data)
323*4882a593Smuzhiyun
324*4882a593Smuzhiyundef check_connectivity(d):
325*4882a593Smuzhiyun    # URI's to check can be set in the CONNECTIVITY_CHECK_URIS variable
326*4882a593Smuzhiyun    # using the same syntax as for SRC_URI. If the variable is not set
327*4882a593Smuzhiyun    # the check is skipped
328*4882a593Smuzhiyun    test_uris = (d.getVar('CONNECTIVITY_CHECK_URIS') or "").split()
329*4882a593Smuzhiyun    retval = ""
330*4882a593Smuzhiyun
331*4882a593Smuzhiyun    bbn = d.getVar('BB_NO_NETWORK')
332*4882a593Smuzhiyun    if bbn not in (None, '0', '1'):
333*4882a593Smuzhiyun        return 'BB_NO_NETWORK should be "0" or "1", but it is "%s"' % bbn
334*4882a593Smuzhiyun
335*4882a593Smuzhiyun    # Only check connectivity if network enabled and the
336*4882a593Smuzhiyun    # CONNECTIVITY_CHECK_URIS are set
337*4882a593Smuzhiyun    network_enabled = not (bbn == '1')
338*4882a593Smuzhiyun    check_enabled = len(test_uris)
339*4882a593Smuzhiyun    if check_enabled and network_enabled:
340*4882a593Smuzhiyun        # Take a copy of the data store and unset MIRRORS and PREMIRRORS
341*4882a593Smuzhiyun        data = bb.data.createCopy(d)
342*4882a593Smuzhiyun        data.delVar('PREMIRRORS')
343*4882a593Smuzhiyun        data.delVar('MIRRORS')
344*4882a593Smuzhiyun        try:
345*4882a593Smuzhiyun            fetcher = bb.fetch2.Fetch(test_uris, data)
346*4882a593Smuzhiyun            fetcher.checkstatus()
347*4882a593Smuzhiyun        except Exception as err:
348*4882a593Smuzhiyun            # Allow the message to be configured so that users can be
349*4882a593Smuzhiyun            # pointed to a support mechanism.
350*4882a593Smuzhiyun            msg = data.getVar('CONNECTIVITY_CHECK_MSG') or ""
351*4882a593Smuzhiyun            if len(msg) == 0:
352*4882a593Smuzhiyun                msg = "%s.\n" % err
353*4882a593Smuzhiyun                msg += "    Please ensure your host's network is configured correctly.\n"
354*4882a593Smuzhiyun                msg += "    Please ensure CONNECTIVITY_CHECK_URIS is correct and specified URIs are available.\n"
355*4882a593Smuzhiyun                msg += "    If your ISP or network is blocking the above URL,\n"
356*4882a593Smuzhiyun                msg += "    try with another domain name, for example by setting:\n"
357*4882a593Smuzhiyun                msg += "    CONNECTIVITY_CHECK_URIS = \"https://www.example.com/\""
358*4882a593Smuzhiyun                msg += "    You could also set BB_NO_NETWORK = \"1\" to disable network\n"
359*4882a593Smuzhiyun                msg += "    access if all required sources are on local disk.\n"
360*4882a593Smuzhiyun            retval = msg
361*4882a593Smuzhiyun
362*4882a593Smuzhiyun    return retval
363*4882a593Smuzhiyun
364*4882a593Smuzhiyundef check_supported_distro(sanity_data):
365*4882a593Smuzhiyun    from fnmatch import fnmatch
366*4882a593Smuzhiyun
367*4882a593Smuzhiyun    tested_distros = sanity_data.getVar('SANITY_TESTED_DISTROS')
368*4882a593Smuzhiyun    if not tested_distros:
369*4882a593Smuzhiyun        return
370*4882a593Smuzhiyun
371*4882a593Smuzhiyun    try:
372*4882a593Smuzhiyun        distro = oe.lsb.distro_identifier()
373*4882a593Smuzhiyun    except Exception:
374*4882a593Smuzhiyun        distro = None
375*4882a593Smuzhiyun
376*4882a593Smuzhiyun    if not distro:
377*4882a593Smuzhiyun        bb.warn('Host distribution could not be determined; you may possibly experience unexpected failures. It is recommended that you use a tested distribution.')
378*4882a593Smuzhiyun
379*4882a593Smuzhiyun    for supported in [x.strip() for x in tested_distros.split('\\n')]:
380*4882a593Smuzhiyun        if fnmatch(distro, supported):
381*4882a593Smuzhiyun            return
382*4882a593Smuzhiyun
383*4882a593Smuzhiyun    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)
384*4882a593Smuzhiyun
385*4882a593Smuzhiyun# Checks we should only make if MACHINE is set correctly
386*4882a593Smuzhiyundef check_sanity_validmachine(sanity_data):
387*4882a593Smuzhiyun    messages = ""
388*4882a593Smuzhiyun
389*4882a593Smuzhiyun    # Check TUNE_ARCH is set
390*4882a593Smuzhiyun    if sanity_data.getVar('TUNE_ARCH') == 'INVALID':
391*4882a593Smuzhiyun        messages = messages + 'TUNE_ARCH is unset. Please ensure your MACHINE configuration includes a valid tune configuration file which will set this correctly.\n'
392*4882a593Smuzhiyun
393*4882a593Smuzhiyun    # Check TARGET_OS is set
394*4882a593Smuzhiyun    if sanity_data.getVar('TARGET_OS') == 'INVALID':
395*4882a593Smuzhiyun        messages = messages + 'Please set TARGET_OS directly, or choose a MACHINE or DISTRO that does so.\n'
396*4882a593Smuzhiyun
397*4882a593Smuzhiyun    # Check that we don't have duplicate entries in PACKAGE_ARCHS & that TUNE_PKGARCH is in PACKAGE_ARCHS
398*4882a593Smuzhiyun    pkgarchs = sanity_data.getVar('PACKAGE_ARCHS')
399*4882a593Smuzhiyun    tunepkg = sanity_data.getVar('TUNE_PKGARCH')
400*4882a593Smuzhiyun    defaulttune = sanity_data.getVar('DEFAULTTUNE')
401*4882a593Smuzhiyun    tunefound = False
402*4882a593Smuzhiyun    seen = {}
403*4882a593Smuzhiyun    dups = []
404*4882a593Smuzhiyun
405*4882a593Smuzhiyun    for pa in pkgarchs.split():
406*4882a593Smuzhiyun        if seen.get(pa, 0) == 1:
407*4882a593Smuzhiyun            dups.append(pa)
408*4882a593Smuzhiyun        else:
409*4882a593Smuzhiyun            seen[pa] = 1
410*4882a593Smuzhiyun        if pa == tunepkg:
411*4882a593Smuzhiyun            tunefound = True
412*4882a593Smuzhiyun
413*4882a593Smuzhiyun    if len(dups):
414*4882a593Smuzhiyun        messages = messages + "Error, the PACKAGE_ARCHS variable contains duplicates. The following archs are listed more than once: %s" % " ".join(dups)
415*4882a593Smuzhiyun
416*4882a593Smuzhiyun    if tunefound == False:
417*4882a593Smuzhiyun        messages = messages + "Error, the PACKAGE_ARCHS variable (%s) for DEFAULTTUNE (%s) does not contain TUNE_PKGARCH (%s)." % (pkgarchs, defaulttune, tunepkg)
418*4882a593Smuzhiyun
419*4882a593Smuzhiyun    return messages
420*4882a593Smuzhiyun
421*4882a593Smuzhiyun# Patch before 2.7 can't handle all the features in git-style diffs.  Some
422*4882a593Smuzhiyun# patches may incorrectly apply, and others won't apply at all.
423*4882a593Smuzhiyundef check_patch_version(sanity_data):
424*4882a593Smuzhiyun    import re, subprocess
425*4882a593Smuzhiyun
426*4882a593Smuzhiyun    try:
427*4882a593Smuzhiyun        result = subprocess.check_output(["patch", "--version"], stderr=subprocess.STDOUT).decode('utf-8')
428*4882a593Smuzhiyun        version = re.search(r"[0-9.]+", result.splitlines()[0]).group()
429*4882a593Smuzhiyun        if bb.utils.vercmp_string_op(version, "2.7", "<"):
430*4882a593Smuzhiyun            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"
431*4882a593Smuzhiyun        else:
432*4882a593Smuzhiyun            return None
433*4882a593Smuzhiyun    except subprocess.CalledProcessError as e:
434*4882a593Smuzhiyun        return "Unable to execute patch --version, exit code %d:\n%s\n" % (e.returncode, e.output)
435*4882a593Smuzhiyun
436*4882a593Smuzhiyun# Glibc needs make 4.0 or later, we may as well match at this point
437*4882a593Smuzhiyundef check_make_version(sanity_data):
438*4882a593Smuzhiyun    import subprocess
439*4882a593Smuzhiyun
440*4882a593Smuzhiyun    try:
441*4882a593Smuzhiyun        result = subprocess.check_output(['make', '--version'], stderr=subprocess.STDOUT).decode('utf-8')
442*4882a593Smuzhiyun    except subprocess.CalledProcessError as e:
443*4882a593Smuzhiyun        return "Unable to execute make --version, exit code %d\n%s\n" % (e.returncode, e.output)
444*4882a593Smuzhiyun    version = result.split()[2]
445*4882a593Smuzhiyun    if bb.utils.vercmp_string_op(version, "4.0", "<"):
446*4882a593Smuzhiyun        return "Please install a make version of 4.0 or later.\n"
447*4882a593Smuzhiyun
448*4882a593Smuzhiyun    if bb.utils.vercmp_string_op(version, "4.2.1", "=="):
449*4882a593Smuzhiyun        distro = oe.lsb.distro_identifier()
450*4882a593Smuzhiyun        if "ubuntu" in distro or "debian" in distro or "linuxmint" in distro:
451*4882a593Smuzhiyun            return None
452*4882a593Smuzhiyun        return "make version 4.2.1 is known to have issues on Centos/OpenSUSE and other non-Ubuntu systems. Please use a buildtools-make-tarball or a newer version of make.\n"
453*4882a593Smuzhiyun    return None
454*4882a593Smuzhiyun
455*4882a593Smuzhiyun
456*4882a593Smuzhiyun# Check if we're running on WSL (Windows Subsystem for Linux).
457*4882a593Smuzhiyun# WSLv1 is known not to work but WSLv2 should work properly as
458*4882a593Smuzhiyun# long as the VHDX file is optimized often, let the user know
459*4882a593Smuzhiyun# upfront.
460*4882a593Smuzhiyun# More information on installing WSLv2 at:
461*4882a593Smuzhiyun# https://docs.microsoft.com/en-us/windows/wsl/wsl2-install
462*4882a593Smuzhiyundef check_wsl(d):
463*4882a593Smuzhiyun    with open("/proc/version", "r") as f:
464*4882a593Smuzhiyun        verdata = f.readlines()
465*4882a593Smuzhiyun    for l in verdata:
466*4882a593Smuzhiyun        if "Microsoft" in l:
467*4882a593Smuzhiyun            return "OpenEmbedded doesn't work under WSLv1, please upgrade to WSLv2 if you want to run builds on Windows"
468*4882a593Smuzhiyun        elif "microsoft" in l:
469*4882a593Smuzhiyun            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")
470*4882a593Smuzhiyun    return None
471*4882a593Smuzhiyun
472*4882a593Smuzhiyun# Require at least gcc version 7.5.
473*4882a593Smuzhiyun#
474*4882a593Smuzhiyun# This can be fixed on CentOS-7 with devtoolset-6+
475*4882a593Smuzhiyun# https://www.softwarecollections.org/en/scls/rhscl/devtoolset-6/
476*4882a593Smuzhiyun#
477*4882a593Smuzhiyun# A less invasive fix is with scripts/install-buildtools (or with user
478*4882a593Smuzhiyun# built buildtools-extended-tarball)
479*4882a593Smuzhiyun#
480*4882a593Smuzhiyundef check_gcc_version(sanity_data):
481*4882a593Smuzhiyun    import subprocess
482*4882a593Smuzhiyun
483*4882a593Smuzhiyun    build_cc, version = oe.utils.get_host_compiler_version(sanity_data)
484*4882a593Smuzhiyun    if build_cc.strip() == "gcc":
485*4882a593Smuzhiyun        if bb.utils.vercmp_string_op(version, "7.5", "<"):
486*4882a593Smuzhiyun            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"
487*4882a593Smuzhiyun    return None
488*4882a593Smuzhiyun
489*4882a593Smuzhiyun# Tar version 1.24 and onwards handle overwriting symlinks correctly
490*4882a593Smuzhiyun# but earlier versions do not; this needs to work properly for sstate
491*4882a593Smuzhiyun# Version 1.28 is needed so opkg-build works correctly when reproducibile builds are enabled
492*4882a593Smuzhiyundef check_tar_version(sanity_data):
493*4882a593Smuzhiyun    import subprocess
494*4882a593Smuzhiyun    try:
495*4882a593Smuzhiyun        result = subprocess.check_output(["tar", "--version"], stderr=subprocess.STDOUT).decode('utf-8')
496*4882a593Smuzhiyun    except subprocess.CalledProcessError as e:
497*4882a593Smuzhiyun        return "Unable to execute tar --version, exit code %d\n%s\n" % (e.returncode, e.output)
498*4882a593Smuzhiyun    version = result.split()[3]
499*4882a593Smuzhiyun    if bb.utils.vercmp_string_op(version, "1.28", "<"):
500*4882a593Smuzhiyun        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"
501*4882a593Smuzhiyun
502*4882a593Smuzhiyun    try:
503*4882a593Smuzhiyun        result = subprocess.check_output(["tar", "--help"], stderr=subprocess.STDOUT).decode('utf-8')
504*4882a593Smuzhiyun        if "--xattrs" not in result:
505*4882a593Smuzhiyun            return "Your tar doesn't support --xattrs, please use GNU tar.\n"
506*4882a593Smuzhiyun    except subprocess.CalledProcessError as e:
507*4882a593Smuzhiyun        return "Unable to execute tar --help, exit code %d\n%s\n" % (e.returncode, e.output)
508*4882a593Smuzhiyun
509*4882a593Smuzhiyun    return None
510*4882a593Smuzhiyun
511*4882a593Smuzhiyun# We use git parameters and functionality only found in 1.7.8 or later
512*4882a593Smuzhiyun# The kernel tools assume git >= 1.8.3.1 (verified needed > 1.7.9.5) see #6162
513*4882a593Smuzhiyun# The git fetcher also had workarounds for git < 1.7.9.2 which we've dropped
514*4882a593Smuzhiyundef check_git_version(sanity_data):
515*4882a593Smuzhiyun    import subprocess
516*4882a593Smuzhiyun    try:
517*4882a593Smuzhiyun        result = subprocess.check_output(["git", "--version"], stderr=subprocess.DEVNULL).decode('utf-8')
518*4882a593Smuzhiyun    except subprocess.CalledProcessError as e:
519*4882a593Smuzhiyun        return "Unable to execute git --version, exit code %d\n%s\n" % (e.returncode, e.output)
520*4882a593Smuzhiyun    version = result.split()[2]
521*4882a593Smuzhiyun    if bb.utils.vercmp_string_op(version, "1.8.3.1", "<"):
522*4882a593Smuzhiyun        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"
523*4882a593Smuzhiyun    return None
524*4882a593Smuzhiyun
525*4882a593Smuzhiyun# Check the required perl modules which may not be installed by default
526*4882a593Smuzhiyundef check_perl_modules(sanity_data):
527*4882a593Smuzhiyun    import subprocess
528*4882a593Smuzhiyun    ret = ""
529*4882a593Smuzhiyun    modules = ( "Text::ParseWords", "Thread::Queue", "Data::Dumper" )
530*4882a593Smuzhiyun    errresult = ''
531*4882a593Smuzhiyun    for m in modules:
532*4882a593Smuzhiyun        try:
533*4882a593Smuzhiyun            subprocess.check_output(["perl", "-e", "use %s" % m])
534*4882a593Smuzhiyun        except subprocess.CalledProcessError as e:
535*4882a593Smuzhiyun            errresult += bytes.decode(e.output)
536*4882a593Smuzhiyun            ret += "%s " % m
537*4882a593Smuzhiyun    if ret:
538*4882a593Smuzhiyun        return "Required perl module(s) not found: %s\n\n%s\n" % (ret, errresult)
539*4882a593Smuzhiyun    return None
540*4882a593Smuzhiyun
541*4882a593Smuzhiyundef sanity_check_conffiles(d):
542*4882a593Smuzhiyun    funcs = d.getVar('BBLAYERS_CONF_UPDATE_FUNCS').split()
543*4882a593Smuzhiyun    for func in funcs:
544*4882a593Smuzhiyun        conffile, current_version, required_version, func = func.split(":")
545*4882a593Smuzhiyun        if check_conf_exists(conffile, d) and d.getVar(current_version) is not None and \
546*4882a593Smuzhiyun                d.getVar(current_version) != d.getVar(required_version):
547*4882a593Smuzhiyun            try:
548*4882a593Smuzhiyun                bb.build.exec_func(func, d)
549*4882a593Smuzhiyun            except NotImplementedError as e:
550*4882a593Smuzhiyun                bb.fatal(str(e))
551*4882a593Smuzhiyun            d.setVar("BB_INVALIDCONF", True)
552*4882a593Smuzhiyun
553*4882a593Smuzhiyundef drop_v14_cross_builds(d):
554*4882a593Smuzhiyun    import glob
555*4882a593Smuzhiyun    indexes = glob.glob(d.expand("${SSTATE_MANIFESTS}/index-${BUILD_ARCH}_*"))
556*4882a593Smuzhiyun    for i in indexes:
557*4882a593Smuzhiyun        with open(i, "r") as f:
558*4882a593Smuzhiyun            lines = f.readlines()
559*4882a593Smuzhiyun            for l in reversed(lines):
560*4882a593Smuzhiyun                try:
561*4882a593Smuzhiyun                    (stamp, manifest, workdir) = l.split()
562*4882a593Smuzhiyun                except ValueError:
563*4882a593Smuzhiyun                    bb.fatal("Invalid line '%s' in sstate manifest '%s'" % (l, i))
564*4882a593Smuzhiyun                for m in glob.glob(manifest + ".*"):
565*4882a593Smuzhiyun                    if m.endswith(".postrm"):
566*4882a593Smuzhiyun                        continue
567*4882a593Smuzhiyun                    sstate_clean_manifest(m, d)
568*4882a593Smuzhiyun                bb.utils.remove(stamp + "*")
569*4882a593Smuzhiyun                bb.utils.remove(workdir, recurse = True)
570*4882a593Smuzhiyun
571*4882a593Smuzhiyundef sanity_handle_abichanges(status, d):
572*4882a593Smuzhiyun    #
573*4882a593Smuzhiyun    # Check the 'ABI' of TMPDIR
574*4882a593Smuzhiyun    #
575*4882a593Smuzhiyun    import subprocess
576*4882a593Smuzhiyun
577*4882a593Smuzhiyun    current_abi = d.getVar('OELAYOUT_ABI')
578*4882a593Smuzhiyun    abifile = d.getVar('SANITY_ABIFILE')
579*4882a593Smuzhiyun    if os.path.exists(abifile):
580*4882a593Smuzhiyun        with open(abifile, "r") as f:
581*4882a593Smuzhiyun            abi = f.read().strip()
582*4882a593Smuzhiyun        if not abi.isdigit():
583*4882a593Smuzhiyun            with open(abifile, "w") as f:
584*4882a593Smuzhiyun                f.write(current_abi)
585*4882a593Smuzhiyun        elif int(abi) <= 11 and current_abi == "12":
586*4882a593Smuzhiyun            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"))
587*4882a593Smuzhiyun        elif int(abi) <= 13 and current_abi == "14":
588*4882a593Smuzhiyun            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"))
589*4882a593Smuzhiyun        elif int(abi) == 14 and current_abi == "15":
590*4882a593Smuzhiyun            drop_v14_cross_builds(d)
591*4882a593Smuzhiyun            with open(abifile, "w") as f:
592*4882a593Smuzhiyun                f.write(current_abi)
593*4882a593Smuzhiyun        elif (abi != current_abi):
594*4882a593Smuzhiyun            # Code to convert from one ABI to another could go here if possible.
595*4882a593Smuzhiyun            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))
596*4882a593Smuzhiyun    else:
597*4882a593Smuzhiyun        with open(abifile, "w") as f:
598*4882a593Smuzhiyun            f.write(current_abi)
599*4882a593Smuzhiyun
600*4882a593Smuzhiyundef check_sanity_sstate_dir_change(sstate_dir, data):
601*4882a593Smuzhiyun    # Sanity checks to be done when the value of SSTATE_DIR changes
602*4882a593Smuzhiyun
603*4882a593Smuzhiyun    # Check that SSTATE_DIR isn't on a filesystem with limited filename length (eg. eCryptFS)
604*4882a593Smuzhiyun    testmsg = ""
605*4882a593Smuzhiyun    if sstate_dir != "":
606*4882a593Smuzhiyun        testmsg = check_create_long_filename(sstate_dir, "SSTATE_DIR")
607*4882a593Smuzhiyun        # If we don't have permissions to SSTATE_DIR, suggest the user set it as an SSTATE_MIRRORS
608*4882a593Smuzhiyun        try:
609*4882a593Smuzhiyun            err = testmsg.split(': ')[1].strip()
610*4882a593Smuzhiyun            if err == "Permission denied.":
611*4882a593Smuzhiyun                testmsg = testmsg + "You could try using %s in SSTATE_MIRRORS rather than as an SSTATE_CACHE.\n" % (sstate_dir)
612*4882a593Smuzhiyun        except IndexError:
613*4882a593Smuzhiyun            pass
614*4882a593Smuzhiyun    return testmsg
615*4882a593Smuzhiyun
616*4882a593Smuzhiyundef check_sanity_version_change(status, d):
617*4882a593Smuzhiyun    # Sanity checks to be done when SANITY_VERSION or NATIVELSBSTRING changes
618*4882a593Smuzhiyun    # In other words, these tests run once in a given build directory and then
619*4882a593Smuzhiyun    # never again until the sanity version or host distrubution id/version changes.
620*4882a593Smuzhiyun
621*4882a593Smuzhiyun    # Check the python install is complete. Examples that are often removed in
622*4882a593Smuzhiyun    # minimal installations: glib-2.0-natives requries # xml.parsers.expat and icu
623*4882a593Smuzhiyun    # requires distutils.sysconfig.
624*4882a593Smuzhiyun    try:
625*4882a593Smuzhiyun        import xml.parsers.expat
626*4882a593Smuzhiyun        import distutils.sysconfig
627*4882a593Smuzhiyun    except ImportError as e:
628*4882a593Smuzhiyun        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)
629*4882a593Smuzhiyun
630*4882a593Smuzhiyun    status.addresult(check_gcc_version(d))
631*4882a593Smuzhiyun    status.addresult(check_make_version(d))
632*4882a593Smuzhiyun    status.addresult(check_patch_version(d))
633*4882a593Smuzhiyun    status.addresult(check_tar_version(d))
634*4882a593Smuzhiyun    status.addresult(check_git_version(d))
635*4882a593Smuzhiyun    status.addresult(check_perl_modules(d))
636*4882a593Smuzhiyun    status.addresult(check_wsl(d))
637*4882a593Smuzhiyun
638*4882a593Smuzhiyun    missing = ""
639*4882a593Smuzhiyun
640*4882a593Smuzhiyun    if not check_app_exists("${MAKE}", d):
641*4882a593Smuzhiyun        missing = missing + "GNU make,"
642*4882a593Smuzhiyun
643*4882a593Smuzhiyun    if not check_app_exists('${BUILD_CC}', d):
644*4882a593Smuzhiyun        missing = missing + "C Compiler (%s)," % d.getVar("BUILD_CC")
645*4882a593Smuzhiyun
646*4882a593Smuzhiyun    if not check_app_exists('${BUILD_CXX}', d):
647*4882a593Smuzhiyun        missing = missing + "C++ Compiler (%s)," % d.getVar("BUILD_CXX")
648*4882a593Smuzhiyun
649*4882a593Smuzhiyun    required_utilities = d.getVar('SANITY_REQUIRED_UTILITIES')
650*4882a593Smuzhiyun
651*4882a593Smuzhiyun    for util in required_utilities.split():
652*4882a593Smuzhiyun        if not check_app_exists(util, d):
653*4882a593Smuzhiyun            missing = missing + "%s," % util
654*4882a593Smuzhiyun
655*4882a593Smuzhiyun    if missing:
656*4882a593Smuzhiyun        missing = missing.rstrip(',')
657*4882a593Smuzhiyun        status.addresult("Please install the following missing utilities: %s\n" % missing)
658*4882a593Smuzhiyun
659*4882a593Smuzhiyun    assume_provided = d.getVar('ASSUME_PROVIDED').split()
660*4882a593Smuzhiyun    # Check user doesn't have ASSUME_PROVIDED = instead of += in local.conf
661*4882a593Smuzhiyun    if "diffstat-native" not in assume_provided:
662*4882a593Smuzhiyun        status.addresult('Please use ASSUME_PROVIDED +=, not ASSUME_PROVIDED = in your local.conf\n')
663*4882a593Smuzhiyun
664*4882a593Smuzhiyun    # Check that TMPDIR isn't on a filesystem with limited filename length (eg. eCryptFS)
665*4882a593Smuzhiyun    import stat
666*4882a593Smuzhiyun    tmpdir = d.getVar('TMPDIR')
667*4882a593Smuzhiyun    status.addresult(check_create_long_filename(tmpdir, "TMPDIR"))
668*4882a593Smuzhiyun    tmpdirmode = os.stat(tmpdir).st_mode
669*4882a593Smuzhiyun    if (tmpdirmode & stat.S_ISGID):
670*4882a593Smuzhiyun        status.addresult("TMPDIR is setgid, please don't build in a setgid directory")
671*4882a593Smuzhiyun    if (tmpdirmode & stat.S_ISUID):
672*4882a593Smuzhiyun        status.addresult("TMPDIR is setuid, please don't build in a setuid directory")
673*4882a593Smuzhiyun
674*4882a593Smuzhiyun    # Check that a user isn't building in a path in PSEUDO_IGNORE_PATHS
675*4882a593Smuzhiyun    pseudoignorepaths = d.getVar('PSEUDO_IGNORE_PATHS', expand=True).split(",")
676*4882a593Smuzhiyun    workdir = d.getVar('WORKDIR', expand=True)
677*4882a593Smuzhiyun    for i in pseudoignorepaths:
678*4882a593Smuzhiyun        if i and workdir.startswith(i):
679*4882a593Smuzhiyun            status.addresult("You are building in a path included in PSEUDO_IGNORE_PATHS " + str(i) + " please locate the build outside this path.\n")
680*4882a593Smuzhiyun
681*4882a593Smuzhiyun    # Check if PSEUDO_IGNORE_PATHS and and paths under pseudo control overlap
682*4882a593Smuzhiyun    pseudoignorepaths = d.getVar('PSEUDO_IGNORE_PATHS', expand=True).split(",")
683*4882a593Smuzhiyun    pseudo_control_dir = "${D},${PKGD},${PKGDEST},${IMAGEROOTFS},${SDK_OUTPUT}"
684*4882a593Smuzhiyun    pseudocontroldir = d.expand(pseudo_control_dir).split(",")
685*4882a593Smuzhiyun    for i in pseudoignorepaths:
686*4882a593Smuzhiyun        for j in pseudocontroldir:
687*4882a593Smuzhiyun            if i and j:
688*4882a593Smuzhiyun                if j.startswith(i):
689*4882a593Smuzhiyun                    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")
690*4882a593Smuzhiyun
691*4882a593Smuzhiyun    # Some third-party software apparently relies on chmod etc. being suid root (!!)
692*4882a593Smuzhiyun    import stat
693*4882a593Smuzhiyun    suid_check_bins = "chown chmod mknod".split()
694*4882a593Smuzhiyun    for bin_cmd in suid_check_bins:
695*4882a593Smuzhiyun        bin_path = bb.utils.which(os.environ["PATH"], bin_cmd)
696*4882a593Smuzhiyun        if bin_path:
697*4882a593Smuzhiyun            bin_stat = os.stat(bin_path)
698*4882a593Smuzhiyun            if bin_stat.st_uid == 0 and bin_stat.st_mode & stat.S_ISUID:
699*4882a593Smuzhiyun                status.addresult('%s has the setuid bit set. This interferes with pseudo and may cause other issues that break the build process.\n' % bin_path)
700*4882a593Smuzhiyun
701*4882a593Smuzhiyun    # Check that we can fetch from various network transports
702*4882a593Smuzhiyun    netcheck = check_connectivity(d)
703*4882a593Smuzhiyun    status.addresult(netcheck)
704*4882a593Smuzhiyun    if netcheck:
705*4882a593Smuzhiyun        status.network_error = True
706*4882a593Smuzhiyun
707*4882a593Smuzhiyun    nolibs = d.getVar('NO32LIBS')
708*4882a593Smuzhiyun    if not nolibs:
709*4882a593Smuzhiyun        lib32path = '/lib'
710*4882a593Smuzhiyun        if os.path.exists('/lib64') and ( os.path.islink('/lib64') or os.path.islink('/lib') ):
711*4882a593Smuzhiyun           lib32path = '/lib32'
712*4882a593Smuzhiyun
713*4882a593Smuzhiyun        if os.path.exists('%s/libc.so.6' % lib32path) and not os.path.exists('/usr/include/gnu/stubs-32.h'):
714*4882a593Smuzhiyun            status.addresult("You have a 32-bit libc, but no 32-bit headers.  You must install the 32-bit libc headers.\n")
715*4882a593Smuzhiyun
716*4882a593Smuzhiyun    bbpaths = d.getVar('BBPATH').split(":")
717*4882a593Smuzhiyun    if ("." in bbpaths or "./" in bbpaths or "" in bbpaths):
718*4882a593Smuzhiyun        status.addresult("BBPATH references the current directory, either through "    \
719*4882a593Smuzhiyun                "an empty entry, a './' or a '.'.\n\t This is unsafe and means your "\
720*4882a593Smuzhiyun                "layer configuration is adding empty elements to BBPATH.\n\t "\
721*4882a593Smuzhiyun                "Please check your layer.conf files and other BBPATH "        \
722*4882a593Smuzhiyun                "settings to remove the current working directory "           \
723*4882a593Smuzhiyun                "references.\n" \
724*4882a593Smuzhiyun                "Parsed BBPATH is" + str(bbpaths));
725*4882a593Smuzhiyun
726*4882a593Smuzhiyun    oes_bb_conf = d.getVar( 'OES_BITBAKE_CONF')
727*4882a593Smuzhiyun    if not oes_bb_conf:
728*4882a593Smuzhiyun        status.addresult('You are not using the OpenEmbedded version of conf/bitbake.conf. This means your environment is misconfigured, in particular check BBPATH.\n')
729*4882a593Smuzhiyun
730*4882a593Smuzhiyun    # The length of TMPDIR can't be longer than 410
731*4882a593Smuzhiyun    status.addresult(check_path_length(tmpdir, "TMPDIR", 410))
732*4882a593Smuzhiyun
733*4882a593Smuzhiyun    # Check that TMPDIR isn't located on nfs
734*4882a593Smuzhiyun    status.addresult(check_not_nfs(tmpdir, "TMPDIR"))
735*4882a593Smuzhiyun
736*4882a593Smuzhiyun    # Check for case-insensitive file systems (such as Linux in Docker on
737*4882a593Smuzhiyun    # macOS with default HFS+ file system)
738*4882a593Smuzhiyun    status.addresult(check_case_sensitive(tmpdir, "TMPDIR"))
739*4882a593Smuzhiyun
740*4882a593Smuzhiyundef sanity_check_locale(d):
741*4882a593Smuzhiyun    """
742*4882a593Smuzhiyun    Currently bitbake switches locale to en_US.UTF-8 so check that this locale actually exists.
743*4882a593Smuzhiyun    """
744*4882a593Smuzhiyun    import locale
745*4882a593Smuzhiyun    try:
746*4882a593Smuzhiyun        locale.setlocale(locale.LC_ALL, "en_US.UTF-8")
747*4882a593Smuzhiyun    except locale.Error:
748*4882a593Smuzhiyun        raise_sanity_error("Your system needs to support the en_US.UTF-8 locale.", d)
749*4882a593Smuzhiyun
750*4882a593Smuzhiyundef check_sanity_everybuild(status, d):
751*4882a593Smuzhiyun    import os, stat
752*4882a593Smuzhiyun    # Sanity tests which test the users environment so need to run at each build (or are so cheap
753*4882a593Smuzhiyun    # it makes sense to always run them.
754*4882a593Smuzhiyun
755*4882a593Smuzhiyun    if 0 == os.getuid():
756*4882a593Smuzhiyun        raise_sanity_error("Do not use Bitbake as root.", d)
757*4882a593Smuzhiyun
758*4882a593Smuzhiyun    # Check the Python version, we now have a minimum of Python 3.6
759*4882a593Smuzhiyun    import sys
760*4882a593Smuzhiyun    if sys.hexversion < 0x030600F0:
761*4882a593Smuzhiyun        status.addresult('The system requires at least Python 3.6 to run. Please update your Python interpreter.\n')
762*4882a593Smuzhiyun
763*4882a593Smuzhiyun    # Check the bitbake version meets minimum requirements
764*4882a593Smuzhiyun    minversion = d.getVar('BB_MIN_VERSION')
765*4882a593Smuzhiyun    if bb.utils.vercmp_string_op(bb.__version__, minversion, "<"):
766*4882a593Smuzhiyun        status.addresult('Bitbake version %s is required and version %s was found\n' % (minversion, bb.__version__))
767*4882a593Smuzhiyun
768*4882a593Smuzhiyun    sanity_check_locale(d)
769*4882a593Smuzhiyun
770*4882a593Smuzhiyun    paths = d.getVar('PATH').split(":")
771*4882a593Smuzhiyun    if "." in paths or "./" in paths or "" in paths:
772*4882a593Smuzhiyun        status.addresult("PATH contains '.', './' or '' (empty element), which will break the build, please remove this.\nParsed PATH is " + str(paths) + "\n")
773*4882a593Smuzhiyun
774*4882a593Smuzhiyun    #Check if bitbake is present in PATH environment variable
775*4882a593Smuzhiyun    bb_check = bb.utils.which(d.getVar('PATH'), 'bitbake')
776*4882a593Smuzhiyun    if not bb_check:
777*4882a593Smuzhiyun        bb.warn("bitbake binary is not found in PATH, did you source the script?")
778*4882a593Smuzhiyun
779*4882a593Smuzhiyun    # Check whether 'inherit' directive is found (used for a class to inherit)
780*4882a593Smuzhiyun    # in conf file it's supposed to be uppercase INHERIT
781*4882a593Smuzhiyun    inherit = d.getVar('inherit')
782*4882a593Smuzhiyun    if inherit:
783*4882a593Smuzhiyun        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")
784*4882a593Smuzhiyun
785*4882a593Smuzhiyun    # Check that the DISTRO is valid, if set
786*4882a593Smuzhiyun    # need to take into account DISTRO renaming DISTRO
787*4882a593Smuzhiyun    distro = d.getVar('DISTRO')
788*4882a593Smuzhiyun    if distro and distro != "nodistro":
789*4882a593Smuzhiyun        if not ( check_conf_exists("conf/distro/${DISTRO}.conf", d) or check_conf_exists("conf/distro/include/${DISTRO}.inc", d) ):
790*4882a593Smuzhiyun            status.addresult("DISTRO '%s' not found. Please set a valid DISTRO in your local.conf\n" % d.getVar("DISTRO"))
791*4882a593Smuzhiyun
792*4882a593Smuzhiyun    # Check that these variables don't use tilde-expansion as we don't do that
793*4882a593Smuzhiyun    for v in ("TMPDIR", "DL_DIR", "SSTATE_DIR"):
794*4882a593Smuzhiyun        if d.getVar(v).startswith("~"):
795*4882a593Smuzhiyun            status.addresult("%s uses ~ but Bitbake will not expand this, use an absolute path or variables." % v)
796*4882a593Smuzhiyun
797*4882a593Smuzhiyun    # Check that DL_DIR is set, exists and is writable. In theory, we should never even hit the check if DL_DIR isn't
798*4882a593Smuzhiyun    # set, since so much relies on it being set.
799*4882a593Smuzhiyun    dldir = d.getVar('DL_DIR')
800*4882a593Smuzhiyun    if not dldir:
801*4882a593Smuzhiyun        status.addresult("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")
802*4882a593Smuzhiyun    if os.path.exists(dldir) and not os.access(dldir, os.W_OK):
803*4882a593Smuzhiyun        status.addresult("DL_DIR: %s exists but you do not appear to have write access to it. \n" % dldir)
804*4882a593Smuzhiyun    check_symlink(dldir, d)
805*4882a593Smuzhiyun
806*4882a593Smuzhiyun    # Check that the MACHINE is valid, if it is set
807*4882a593Smuzhiyun    machinevalid = True
808*4882a593Smuzhiyun    if d.getVar('MACHINE'):
809*4882a593Smuzhiyun        if not check_conf_exists("conf/machine/${MACHINE}.conf", d):
810*4882a593Smuzhiyun            status.addresult('MACHINE=%s is invalid. Please set a valid MACHINE in your local.conf, environment or other configuration file.\n' % (d.getVar('MACHINE')))
811*4882a593Smuzhiyun            machinevalid = False
812*4882a593Smuzhiyun        else:
813*4882a593Smuzhiyun            status.addresult(check_sanity_validmachine(d))
814*4882a593Smuzhiyun    else:
815*4882a593Smuzhiyun        status.addresult('Please set a MACHINE in your local.conf or environment\n')
816*4882a593Smuzhiyun        machinevalid = False
817*4882a593Smuzhiyun    if machinevalid:
818*4882a593Smuzhiyun        status.addresult(check_toolchain(d))
819*4882a593Smuzhiyun
820*4882a593Smuzhiyun    # Check that the SDKMACHINE is valid, if it is set
821*4882a593Smuzhiyun    if d.getVar('SDKMACHINE'):
822*4882a593Smuzhiyun        if not check_conf_exists("conf/machine-sdk/${SDKMACHINE}.conf", d):
823*4882a593Smuzhiyun            status.addresult('Specified SDKMACHINE value is not valid\n')
824*4882a593Smuzhiyun        elif d.getVar('SDK_ARCH', False) == "${BUILD_ARCH}":
825*4882a593Smuzhiyun            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')
826*4882a593Smuzhiyun
827*4882a593Smuzhiyun    # If SDK_VENDOR looks like "-my-sdk" then the triples are badly formed so fail early
828*4882a593Smuzhiyun    sdkvendor = d.getVar("SDK_VENDOR")
829*4882a593Smuzhiyun    if not (sdkvendor.startswith("-") and sdkvendor.count("-") == 1):
830*4882a593Smuzhiyun        status.addresult("SDK_VENDOR should be of the form '-foosdk' with a single dash; found '%s'\n" % sdkvendor)
831*4882a593Smuzhiyun
832*4882a593Smuzhiyun    check_supported_distro(d)
833*4882a593Smuzhiyun
834*4882a593Smuzhiyun    omask = os.umask(0o022)
835*4882a593Smuzhiyun    if omask & 0o755:
836*4882a593Smuzhiyun        status.addresult("Please use a umask which allows a+rx and u+rwx\n")
837*4882a593Smuzhiyun    os.umask(omask)
838*4882a593Smuzhiyun
839*4882a593Smuzhiyun    if d.getVar('TARGET_ARCH') == "arm":
840*4882a593Smuzhiyun        # This path is no longer user-readable in modern (very recent) Linux
841*4882a593Smuzhiyun        try:
842*4882a593Smuzhiyun            if os.path.exists("/proc/sys/vm/mmap_min_addr"):
843*4882a593Smuzhiyun                f = open("/proc/sys/vm/mmap_min_addr", "r")
844*4882a593Smuzhiyun                try:
845*4882a593Smuzhiyun                    if (int(f.read().strip()) > 65536):
846*4882a593Smuzhiyun                        status.addresult("/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")
847*4882a593Smuzhiyun                finally:
848*4882a593Smuzhiyun                    f.close()
849*4882a593Smuzhiyun        except:
850*4882a593Smuzhiyun            pass
851*4882a593Smuzhiyun
852*4882a593Smuzhiyun    for checkdir in ['COREBASE', 'TMPDIR']:
853*4882a593Smuzhiyun        val = d.getVar(checkdir)
854*4882a593Smuzhiyun        if val.find('..') != -1:
855*4882a593Smuzhiyun            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)
856*4882a593Smuzhiyun        if val.find('+') != -1:
857*4882a593Smuzhiyun            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)
858*4882a593Smuzhiyun        if val.find('@') != -1:
859*4882a593Smuzhiyun            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)
860*4882a593Smuzhiyun        if val.find(' ') != -1:
861*4882a593Smuzhiyun            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)
862*4882a593Smuzhiyun        if val.find('%') != -1:
863*4882a593Smuzhiyun            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)
864*4882a593Smuzhiyun
865*4882a593Smuzhiyun    # Check the format of MIRRORS, PREMIRRORS and SSTATE_MIRRORS
866*4882a593Smuzhiyun    import re
867*4882a593Smuzhiyun    mirror_vars = ['MIRRORS', 'PREMIRRORS', 'SSTATE_MIRRORS']
868*4882a593Smuzhiyun    protocols = ['http', 'ftp', 'file', 'https', \
869*4882a593Smuzhiyun                 'git', 'gitsm', 'hg', 'osc', 'p4', 'svn', \
870*4882a593Smuzhiyun                 'bzr', 'cvs', 'npm', 'sftp', 'ssh', 's3', 'az', 'ftps', 'crate']
871*4882a593Smuzhiyun    for mirror_var in mirror_vars:
872*4882a593Smuzhiyun        mirrors = (d.getVar(mirror_var) or '').replace('\\n', ' ').split()
873*4882a593Smuzhiyun
874*4882a593Smuzhiyun        # Split into pairs
875*4882a593Smuzhiyun        if len(mirrors) % 2 != 0:
876*4882a593Smuzhiyun            bb.warn('Invalid mirror variable value for %s: %s, should contain paired members.' % (mirror_var, str(mirrors)))
877*4882a593Smuzhiyun            continue
878*4882a593Smuzhiyun        mirrors = list(zip(*[iter(mirrors)]*2))
879*4882a593Smuzhiyun
880*4882a593Smuzhiyun        for mirror_entry in mirrors:
881*4882a593Smuzhiyun            pattern, mirror = mirror_entry
882*4882a593Smuzhiyun
883*4882a593Smuzhiyun            decoded = bb.fetch2.decodeurl(pattern)
884*4882a593Smuzhiyun            try:
885*4882a593Smuzhiyun                pattern_scheme = re.compile(decoded[0])
886*4882a593Smuzhiyun            except re.error as exc:
887*4882a593Smuzhiyun                bb.warn('Invalid scheme regex (%s) in %s; %s' % (pattern, mirror_var, mirror_entry))
888*4882a593Smuzhiyun                continue
889*4882a593Smuzhiyun
890*4882a593Smuzhiyun            if not any(pattern_scheme.match(protocol) for protocol in protocols):
891*4882a593Smuzhiyun                bb.warn('Invalid protocol (%s) in %s: %s' % (decoded[0], mirror_var, mirror_entry))
892*4882a593Smuzhiyun                continue
893*4882a593Smuzhiyun
894*4882a593Smuzhiyun            if not any(mirror.startswith(protocol + '://') for protocol in protocols):
895*4882a593Smuzhiyun                bb.warn('Invalid protocol in %s: %s' % (mirror_var, mirror_entry))
896*4882a593Smuzhiyun                continue
897*4882a593Smuzhiyun
898*4882a593Smuzhiyun            if mirror.startswith('file://'):
899*4882a593Smuzhiyun                import urllib
900*4882a593Smuzhiyun                check_symlink(urllib.parse.urlparse(mirror).path, d)
901*4882a593Smuzhiyun                # SSTATE_MIRROR ends with a /PATH string
902*4882a593Smuzhiyun                if mirror.endswith('/PATH'):
903*4882a593Smuzhiyun                    # remove /PATH$ from SSTATE_MIRROR to get a working
904*4882a593Smuzhiyun                    # base directory path
905*4882a593Smuzhiyun                    mirror_base = urllib.parse.urlparse(mirror[:-1*len('/PATH')]).path
906*4882a593Smuzhiyun                    check_symlink(mirror_base, d)
907*4882a593Smuzhiyun
908*4882a593Smuzhiyun    # Check sstate mirrors aren't being used with a local hash server and no remote
909*4882a593Smuzhiyun    hashserv = d.getVar("BB_HASHSERVE")
910*4882a593Smuzhiyun    if d.getVar("SSTATE_MIRRORS") and hashserv and hashserv.startswith("unix://") and not d.getVar("BB_HASHSERVE_UPSTREAM"):
911*4882a593Smuzhiyun        bb.warn("You are using a local hash equivalence server but have configured an sstate mirror. This will likely mean no sstate will match from the mirror. You may wish to disable the hash equivalence use (BB_HASHSERVE), or use a hash equivalence server alongside the sstate mirror.")
912*4882a593Smuzhiyun
913*4882a593Smuzhiyun    # Check that TMPDIR hasn't changed location since the last time we were run
914*4882a593Smuzhiyun    tmpdir = d.getVar('TMPDIR')
915*4882a593Smuzhiyun    checkfile = os.path.join(tmpdir, "saved_tmpdir")
916*4882a593Smuzhiyun    if os.path.exists(checkfile):
917*4882a593Smuzhiyun        with open(checkfile, "r") as f:
918*4882a593Smuzhiyun            saved_tmpdir = f.read().strip()
919*4882a593Smuzhiyun            if (saved_tmpdir != tmpdir):
920*4882a593Smuzhiyun                status.addresult("Error, TMPDIR has changed location. You need to either move it back to %s or delete it and rebuild\n" % saved_tmpdir)
921*4882a593Smuzhiyun    else:
922*4882a593Smuzhiyun        bb.utils.mkdirhier(tmpdir)
923*4882a593Smuzhiyun        # Remove setuid, setgid and sticky bits from TMPDIR
924*4882a593Smuzhiyun        try:
925*4882a593Smuzhiyun            os.chmod(tmpdir, os.stat(tmpdir).st_mode & ~ stat.S_ISUID)
926*4882a593Smuzhiyun            os.chmod(tmpdir, os.stat(tmpdir).st_mode & ~ stat.S_ISGID)
927*4882a593Smuzhiyun            os.chmod(tmpdir, os.stat(tmpdir).st_mode & ~ stat.S_ISVTX)
928*4882a593Smuzhiyun        except OSError as exc:
929*4882a593Smuzhiyun            bb.warn("Unable to chmod TMPDIR: %s" % exc)
930*4882a593Smuzhiyun        with open(checkfile, "w") as f:
931*4882a593Smuzhiyun            f.write(tmpdir)
932*4882a593Smuzhiyun
933*4882a593Smuzhiyun    # If /bin/sh is a symlink, check that it points to dash or bash
934*4882a593Smuzhiyun    if os.path.islink('/bin/sh'):
935*4882a593Smuzhiyun        real_sh = os.path.realpath('/bin/sh')
936*4882a593Smuzhiyun        # Due to update-alternatives, the shell name may take various
937*4882a593Smuzhiyun        # forms, such as /bin/dash, bin/bash, /bin/bash.bash ...
938*4882a593Smuzhiyun        if '/dash' not in real_sh and '/bash' not in real_sh:
939*4882a593Smuzhiyun            status.addresult("Error, /bin/sh links to %s, must be dash or bash\n" % real_sh)
940*4882a593Smuzhiyun
941*4882a593Smuzhiyundef check_sanity(sanity_data):
942*4882a593Smuzhiyun    class SanityStatus(object):
943*4882a593Smuzhiyun        def __init__(self):
944*4882a593Smuzhiyun            self.messages = ""
945*4882a593Smuzhiyun            self.network_error = False
946*4882a593Smuzhiyun
947*4882a593Smuzhiyun        def addresult(self, message):
948*4882a593Smuzhiyun            if message:
949*4882a593Smuzhiyun                self.messages = self.messages + message
950*4882a593Smuzhiyun
951*4882a593Smuzhiyun    status = SanityStatus()
952*4882a593Smuzhiyun
953*4882a593Smuzhiyun    tmpdir = sanity_data.getVar('TMPDIR')
954*4882a593Smuzhiyun    sstate_dir = sanity_data.getVar('SSTATE_DIR')
955*4882a593Smuzhiyun
956*4882a593Smuzhiyun    check_symlink(sstate_dir, sanity_data)
957*4882a593Smuzhiyun
958*4882a593Smuzhiyun    # Check saved sanity info
959*4882a593Smuzhiyun    last_sanity_version = 0
960*4882a593Smuzhiyun    last_tmpdir = ""
961*4882a593Smuzhiyun    last_sstate_dir = ""
962*4882a593Smuzhiyun    last_nativelsbstr = ""
963*4882a593Smuzhiyun    sanityverfile = sanity_data.expand("${TOPDIR}/cache/sanity_info")
964*4882a593Smuzhiyun    if os.path.exists(sanityverfile):
965*4882a593Smuzhiyun        with open(sanityverfile, 'r') as f:
966*4882a593Smuzhiyun            for line in f:
967*4882a593Smuzhiyun                if line.startswith('SANITY_VERSION'):
968*4882a593Smuzhiyun                    last_sanity_version = int(line.split()[1])
969*4882a593Smuzhiyun                if line.startswith('TMPDIR'):
970*4882a593Smuzhiyun                    last_tmpdir = line.split()[1]
971*4882a593Smuzhiyun                if line.startswith('SSTATE_DIR'):
972*4882a593Smuzhiyun                    last_sstate_dir = line.split()[1]
973*4882a593Smuzhiyun                if line.startswith('NATIVELSBSTRING'):
974*4882a593Smuzhiyun                    last_nativelsbstr = line.split()[1]
975*4882a593Smuzhiyun
976*4882a593Smuzhiyun    check_sanity_everybuild(status, sanity_data)
977*4882a593Smuzhiyun
978*4882a593Smuzhiyun    sanity_version = int(sanity_data.getVar('SANITY_VERSION') or 1)
979*4882a593Smuzhiyun    network_error = False
980*4882a593Smuzhiyun    # NATIVELSBSTRING var may have been overridden with "universal", so
981*4882a593Smuzhiyun    # get actual host distribution id and version
982*4882a593Smuzhiyun    nativelsbstr = lsb_distro_identifier(sanity_data)
983*4882a593Smuzhiyun    if last_sanity_version < sanity_version or last_nativelsbstr != nativelsbstr:
984*4882a593Smuzhiyun        check_sanity_version_change(status, sanity_data)
985*4882a593Smuzhiyun        status.addresult(check_sanity_sstate_dir_change(sstate_dir, sanity_data))
986*4882a593Smuzhiyun    else:
987*4882a593Smuzhiyun        if last_sstate_dir != sstate_dir:
988*4882a593Smuzhiyun            status.addresult(check_sanity_sstate_dir_change(sstate_dir, sanity_data))
989*4882a593Smuzhiyun
990*4882a593Smuzhiyun    if os.path.exists(os.path.dirname(sanityverfile)) and not status.messages:
991*4882a593Smuzhiyun        with open(sanityverfile, 'w') as f:
992*4882a593Smuzhiyun            f.write("SANITY_VERSION %s\n" % sanity_version)
993*4882a593Smuzhiyun            f.write("TMPDIR %s\n" % tmpdir)
994*4882a593Smuzhiyun            f.write("SSTATE_DIR %s\n" % sstate_dir)
995*4882a593Smuzhiyun            f.write("NATIVELSBSTRING %s\n" % nativelsbstr)
996*4882a593Smuzhiyun
997*4882a593Smuzhiyun    sanity_handle_abichanges(status, sanity_data)
998*4882a593Smuzhiyun
999*4882a593Smuzhiyun    if status.messages != "":
1000*4882a593Smuzhiyun        raise_sanity_error(sanity_data.expand(status.messages), sanity_data, status.network_error)
1001*4882a593Smuzhiyun
1002*4882a593Smuzhiyunaddhandler config_reparse_eventhandler
1003*4882a593Smuzhiyunconfig_reparse_eventhandler[eventmask] = "bb.event.ConfigParsed"
1004*4882a593Smuzhiyunpython config_reparse_eventhandler() {
1005*4882a593Smuzhiyun    sanity_check_conffiles(e.data)
1006*4882a593Smuzhiyun}
1007*4882a593Smuzhiyun
1008*4882a593Smuzhiyunaddhandler check_sanity_eventhandler
1009*4882a593Smuzhiyuncheck_sanity_eventhandler[eventmask] = "bb.event.SanityCheck bb.event.NetworkTest"
1010*4882a593Smuzhiyunpython check_sanity_eventhandler() {
1011*4882a593Smuzhiyun    if bb.event.getName(e) == "SanityCheck":
1012*4882a593Smuzhiyun        sanity_data = bb.data.createCopy(e.data)
1013*4882a593Smuzhiyun        check_sanity(sanity_data)
1014*4882a593Smuzhiyun        if e.generateevents:
1015*4882a593Smuzhiyun            sanity_data.setVar("SANITY_USE_EVENTS", "1")
1016*4882a593Smuzhiyun        bb.event.fire(bb.event.SanityCheckPassed(), e.data)
1017*4882a593Smuzhiyun    elif bb.event.getName(e) == "NetworkTest":
1018*4882a593Smuzhiyun        sanity_data = bb.data.createCopy(e.data)
1019*4882a593Smuzhiyun        if e.generateevents:
1020*4882a593Smuzhiyun            sanity_data.setVar("SANITY_USE_EVENTS", "1")
1021*4882a593Smuzhiyun        bb.event.fire(bb.event.NetworkTestFailed() if check_connectivity(sanity_data) else bb.event.NetworkTestPassed(), e.data)
1022*4882a593Smuzhiyun
1023*4882a593Smuzhiyun    return
1024*4882a593Smuzhiyun}
1025