aboutsummaryrefslogtreecommitdiffstats
path: root/classes/sanity-external-toolchain.bbclass
blob: 863bfd814f072adc791a1fc2ac30857773e203e7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
TOOLCHAIN_SANITY_VERSION = "1"

def check_toolchain_sanity(d, generate_events=False):
    import shlex
    import tempfile

    if not d.getVar('TCMODE', True).startswith('external'):
        return

    extpath = d.getVar('EXTERNAL_TOOLCHAIN', True)

    # Test 1: EXTERNAL_TOOLCHAIN exists
    if not os.path.exists(extpath):
        raise_exttc_sanity_error('EXTERNAL_TOOLCHAIN path `%s` does not exist' % extpath, d, generate_events)
    extpath = os.path.realpath(extpath)

    sanity_file = d.expand('${TOPDIR}/conf/exttc_sanity_info')
    version = d.getVar('TOOLCHAIN_SANITY_VERSION', True)
    check, config = should_run(sanity_file, {'version': version, 'path': extpath})
    if not check:
        return
    cfgdata = config['DEFAULT']

    # Test 2: EXTERNAL_TARGET_SYS is set correctly
    if d.getVar('EXTERNAL_TARGET_SYS', True) == 'UNKNOWN':
        raise_exttc_sanity_error('Unable to locate prefixed gcc binary for %s in EXTERNAL_TOOLCHAIN_BIN (%s)' % (d.getVar('TARGET_SYS', True), d.getVar('EXTERNAL_TOOLCHAIN_BIN', True)), d, generate_events)

    # Test 3: gcc binary exists
    gcc = d.expand('${EXTERNAL_TOOLCHAIN_BIN}/${EXTERNAL_TARGET_SYS}-gcc')
    if not os.path.exists(gcc):
        raise_exttc_sanity_error('Compiler path `%s` does not exist' % gcc, d, generate_events)

    # Test 4: we can run it to get the version
    cmd = d.expand('${EXTERNAL_TOOLCHAIN_BIN}/${EXTERNAL_CC} -dumpversion')
    sourcery_version = exttc_sanity_run(shlex.split(cmd), d, generate_events)
    if cfgdata.get('sourcery_version') == sourcery_version:
        return

    # Test 5: we can compile an empty test app
    with tempfile.TemporaryDirectory() as tmpdir:
        with open(os.path.join(tmpdir, 'test.c'), 'w') as f:
            f.write('int main() {}')

        # The external toolchain recipes haven't necessarily been built, so we
        # need to drop --sysroot= and --no-sysroot-suffix and use the bits in
        # the external toolchain sysroots for this test
        l = d.createCopy()
        l.setVar('TOOLCHAIN_OPTIONS', '')
        l.setVar('TARGET_PREFIX', '${EXTERNAL_TARGET_SYS}-')
        l.setVar('HOST_CC_ARCH:remove', '--no-sysroot-suffix')
        cmd = l.expand('${EXTERNAL_TOOLCHAIN_BIN}/${EXTERNAL_CC} ${HOST_CC_ARCH} ${CFLAGS} ${LDFLAGS} test.c -o test')
        exttc_sanity_run(shlex.split(cmd), d, generate_events, tmpdir)

    with open(sanity_file, 'w') as f:
        config.write(f)

def should_run(cfgfile, expected):
    import configparser

    config = configparser.ConfigParser()
    readfiles = config.read(cfgfile)
    cfgdata = config['DEFAULT']

    if cfgfile in readfiles and cfgdata == expected:
        return False, None

    cfgdata.update(expected)
    return True, config

def raise_exttc_sanity_error(msg, d, generate_events):
    msg = 'Sanity check of the external toolchain failed: ' + msg
    if generate_events:
        try:
            bb.event.fire(bb.event.SanityCheckFailed(msg, None), d)
        except TypeError:
            bb.event.fire(bb.event.SanityCheckFailed(msg), d)
    else:
        bb.fatal(msg)

def exttc_sanity_run(cmd, d, generate_events, cwd='/'):
    import subprocess
    try:
        return subprocess.check_output(cmd, stderr=subprocess.STDOUT, cwd=cwd)
    except FileNotFoundError:
        raise_exttc_sanity_error('\n  Command: %s\n  Exit Code: 127\n  Output: no such file or directory' % cmd, d, generate_events)
    except subprocess.CalledProcessError as exc:
        if not isinstance(cmd, str):
            cmd = subprocess.list2cmdline(cmd)
        output = exc.output.decode()
        output_indented = ''.join('    ' + l for l in output.splitlines(keepends=True))
        raise_exttc_sanity_error('\n  Command: %s\n  Exit code: %s\n  Output:\n%s' % (cmd, exc.returncode, output_indented), d, generate_events)

python toolchain_sanity_eventhandler() {
    check_toolchain_sanity(d, e.generateevents)
}
toolchain_sanity_eventhandler[eventmask] = "bb.event.SanityCheck"
addhandler toolchain_sanity_eventhandler