summaryrefslogtreecommitdiff
path: root/test/hil/hil_ci_set_matrix.py
blob: bca989bd15fe5743c8a1e54a5184a0036450f6ac (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
import argparse
import json
import os


def _resolve_config_path(config_file):
    if os.path.exists(config_file):
        return config_file

    script_relative = os.path.join(os.path.dirname(__file__), config_file)
    if os.path.exists(script_relative):
        return script_relative

    raise FileNotFoundError(f'Config file not found: {config_file}')


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('config_files', nargs='+', help='Configuration JSON file(s)')
    parser.add_argument('--select', help='hil_select.py JSON; scopes boards when full=false')
    args = parser.parse_args()

    selected = None
    sel = json.loads(args.select) if args.select else None
    if sel and not sel.get('full'):
        selected = set(sel.get('boards', {}))

    # Toolchain buckets must match the toolchains instantiated by the hil-build
    # job in .github/workflows/build.yml. Keep all keys present (even if empty)
    # so `fromJSON(hil_json)[toolchain]` always resolves to a list.
    matrix = {
        'arm-gcc': [],
        'riscv-gcc': [],
        'esp-idf': []
    }

    seen = {toolchain: set() for toolchain in matrix}

    def append_build_arg(toolchain, build_arg):
        if build_arg not in seen[toolchain]:
            seen[toolchain].add(build_arg)
            matrix[toolchain].append(build_arg)

    for config_file in args.config_files:
        with open(_resolve_config_path(config_file)) as f:
            config = json.load(f)

        for board in config['boards']:
            if selected is not None and board['name'] not in selected:
                continue
            name = board['name']
            flasher = board['flasher']
            # esptool boards must build under esp-idf; others default to arm-gcc
            # but may opt into another bucket via an explicit "toolchain" field
            # (e.g. RISC-V boards like ch32v20x need "riscv-gcc").
            if flasher['name'] == 'esptool':
                toolchain = 'esp-idf'
            else:
                toolchain = board.get('toolchain', 'arm-gcc')
            if toolchain not in matrix:
                # a board in no bucket would never be built, and the bare KeyError
                # below would only say so as a traceback from the set-matrix job
                raise SystemExit(
                    f'{name}: toolchain {toolchain!r} is not a build bucket '
                    f'({", ".join(matrix)}); add it here and to the hil-build / '
                    f'hil-build-esp jobs in .github/workflows/build.yml')

            build_board = f'-b {name}'
            if 'build' in board and 'args' in board['build']:
                build_board += ' ' + ' '.join(f'-D{a}' for a in board['build']['args'])

            # Each variant builds into cmake-build-<variant.name> with its own cmake
            # -D defines and raw CFLAGS. No 'variant' -> a single build named after
            # the board.
            variants = board.get('variant') or [{'name': name, 'flags': ''}]
            for v in variants:
                arg = build_board
                if v['name'] != name:
                    arg += f' --build-name {v["name"]}'
                for d in v.get('defines', []):
                    arg += f' -D{d}'
                for tok in v.get('flags', '').split():
                    arg += f' --cflag={tok}'
                append_build_arg(toolchain, arg)

    print(json.dumps(matrix))


if __name__ == '__main__':
    main()