mirror of
https://github.com/hathach/tinyusb.git
synced 2026-08-21 20:45:27 +00:00
family_list maps a family to the toolchains that build it, and seven hw/bsp families are in neither: cxd56, efm32, espressif, f1c100s, pic32mz, py32f0, same7x. Scoping to one of them intersected to nothing, so every toolchain key was [], every cmake leg skipped on `if: inputs.build-args != '[]'`, code-metrics took its no-metrics branch, and the PR went green from a build job that ran no compiler. The only signal was a stderr line nothing greps for. Not a coverage regression - master gave the same diff no compile coverage either, since none of the other families compiles same7x's board.h. What is new is that the gap used to be masked by the full matrix and is now the whole answer, and that green now means "ran no compiler" rather than "compiled 64 families". A selection whose families ALL miss is now unusable rather than empty: it prints UNSCOPED, which build.yml and .circleci/config.yml already grep to drop the build extras with it, and emits the full matrix. The two neighbouring cases keep their own answers - an explicit families: [] is still a legitimate nothing-selected, and a partial miss still scopes to the families that do build, noting the rest. The contract test pinned an exact count of fall-open markers, which this would have broken; it now pins the invariant (every message that emits the full matrix carries the marker) and was checked to still fail when a marker is removed. Also corrects the drift guard's note about espressif: hil-build-esp builds its boards by name, but that job is gated on repository_owner, so on a fork an espressif-only PR builds nowhere.
188 lines
7.6 KiB
Python
Executable File
188 lines
7.6 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
import argparse
|
|
import json
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
|
|
# toolchain, url
|
|
toolchain_list = [
|
|
"aarch64-gcc",
|
|
"arm-clang",
|
|
"arm-iar",
|
|
"arm-gcc",
|
|
"esp-idf",
|
|
"ft9xx-gcc",
|
|
"msp430-gcc",
|
|
"riscv-gcc",
|
|
"rx-gcc"
|
|
]
|
|
|
|
# family: [supported toolchain]
|
|
family_list = {
|
|
"apm32f0xx": ["arm-gcc"],
|
|
"at32f402_405": ["arm-gcc"],
|
|
"at32f403a_407": ["arm-gcc"],
|
|
"at32f413": ["arm-gcc"],
|
|
"at32f415": ["arm-gcc"],
|
|
"at32f423": ["arm-gcc"],
|
|
"at32f425": ["arm-gcc"],
|
|
"at32f435_437": ["arm-gcc"],
|
|
"at32f45x": ["arm-gcc"],
|
|
"broadcom_32bit": ["arm-gcc"],
|
|
"broadcom_64bit": ["aarch64-gcc"],
|
|
"ch32f20x": ["arm-gcc"],
|
|
"ch32v10x": ["riscv-gcc"],
|
|
"ch32v20x": ["riscv-gcc"],
|
|
"ch32v30x": ["riscv-gcc"],
|
|
"ch583": ["riscv-gcc"],
|
|
"da1469x": ["arm-gcc"],
|
|
"fomu": ["riscv-gcc"],
|
|
"ft9xx": ["ft9xx-gcc"],
|
|
"gd32vf103": ["riscv-gcc"],
|
|
"hpmicro": ["riscv-gcc"],
|
|
"imxrt": ["arm-gcc", "arm-clang"],
|
|
"kinetis_k": ["arm-gcc"],
|
|
"kinetis_k32l": ["arm-gcc"],
|
|
"kinetis_kl": ["arm-gcc"],
|
|
"lpc11": ["arm-gcc", "arm-clang"],
|
|
"lpc13": ["arm-gcc", "arm-clang"],
|
|
"lpc15": ["arm-gcc", "arm-clang"],
|
|
"lpc17": ["arm-gcc", "arm-clang"],
|
|
"lpc18": ["arm-gcc", "arm-clang"],
|
|
"lpc40": ["arm-gcc", "arm-clang"],
|
|
"lpc43": ["arm-gcc", "arm-clang"],
|
|
"lpc51": ["arm-gcc", "arm-clang"],
|
|
"lpc54": ["arm-gcc", "arm-clang"],
|
|
"lpc55": ["arm-gcc", "arm-clang"],
|
|
"maxim": ["arm-gcc"],
|
|
"mcx": ["arm-gcc"],
|
|
"mm32": ["arm-gcc"],
|
|
"msp430": ["msp430-gcc"],
|
|
"msp432e4": ["arm-gcc"],
|
|
"nrf": ["arm-gcc", "arm-clang"],
|
|
"nuc100_120": ["arm-gcc"],
|
|
"nuc121_125": ["arm-gcc"],
|
|
"nuc126": ["arm-gcc"],
|
|
"nuc505": ["arm-gcc"],
|
|
"ra": ["arm-gcc"],
|
|
"rp2040": ["arm-gcc"],
|
|
"rw61x": ["arm-gcc"],
|
|
"rx": ["rx-gcc"],
|
|
"samd11": ["arm-gcc", "arm-clang"],
|
|
"samd2x_l2x": ["arm-gcc", "arm-clang"],
|
|
"samd5x_e5x": ["arm-gcc", "arm-clang"],
|
|
"samg": ["arm-gcc", "arm-clang"],
|
|
"stm32c0": ["arm-gcc", "arm-clang", "arm-iar"],
|
|
"stm32c5": ["arm-gcc", "arm-clang", "arm-iar"],
|
|
"stm32f0": ["arm-gcc", "arm-clang", "arm-iar"],
|
|
"stm32f1": ["arm-gcc", "arm-clang", "arm-iar"],
|
|
"stm32f2": ["arm-gcc", "arm-clang", "arm-iar"],
|
|
"stm32f3": ["arm-gcc", "arm-clang", "arm-iar"],
|
|
"stm32f4": ["arm-gcc", "arm-clang", "arm-iar"],
|
|
"stm32f7": ["arm-gcc", "arm-clang", "arm-iar"],
|
|
"stm32g0": ["arm-gcc", "arm-clang", "arm-iar"],
|
|
"stm32g4": ["arm-gcc", "arm-clang", "arm-iar"],
|
|
"stm32h5": ["arm-gcc", "arm-clang", "arm-iar"],
|
|
"stm32h7": ["arm-gcc", "arm-clang", "arm-iar"],
|
|
"stm32h7rs": ["arm-gcc", "arm-clang", "arm-iar"],
|
|
"stm32l0": ["arm-gcc", "arm-clang", "arm-iar"],
|
|
"stm32l4": ["arm-gcc", "arm-clang", "arm-iar"],
|
|
"stm32n6": ["arm-gcc"],
|
|
"stm32u0": ["arm-gcc", "arm-clang", "arm-iar"],
|
|
"stm32u5": ["arm-gcc", "arm-clang", "arm-iar"],
|
|
"stm32wb": ["arm-gcc", "arm-clang", "arm-iar"],
|
|
"stm32wba": ["arm-gcc", "arm-clang", "arm-iar"],
|
|
"tm4c": ["arm-gcc"],
|
|
"xmc4000": ["arm-gcc"],
|
|
# S3, P4 will be built by hil test
|
|
# "-bespressif_s3_devkitm": ["esp-idf"],
|
|
# "-bespressif_p4_function_ev": ["esp-idf"],
|
|
}
|
|
|
|
|
|
def set_matrix_json(select=None):
|
|
sel_fams = None
|
|
if select:
|
|
# every shape check is explicit: this runs AFTER main()'s fail-open handler, so
|
|
# an AttributeError on e.g. {"build": ["stm32f4"]} would red the step instead
|
|
# of falling back to the full matrix - the outcome that handler exists to prevent
|
|
b = select.get('build') if isinstance(select, dict) else None
|
|
if not isinstance(b, dict):
|
|
b = {}
|
|
if b.get('full') is False:
|
|
fams = b.get('families')
|
|
if not (isinstance(fams, list) and all(isinstance(f, str) for f in fams)):
|
|
# key ABSENT (or not a list of names) is an unusable selection, not
|
|
# "nothing selected": scoping every toolchain to [] would build zero
|
|
# families and report a vacuous green. An explicit families: [] stays a
|
|
# legitimate nothing-selected.
|
|
print('ci_set_matrix: UNSCOPED - build.full is false but the families '
|
|
'list is unusable, emitting the full matrix', file=sys.stderr)
|
|
else:
|
|
sel_fams = set(fams)
|
|
matrix = {}
|
|
for toolchain in toolchain_list:
|
|
fams = [family for family, tc in family_list.items() if toolchain in tc]
|
|
if sel_fams is not None:
|
|
fams = [f for f in fams if f in sel_fams]
|
|
matrix[toolchain] = fams
|
|
if sel_fams:
|
|
# a family this file does not list builds on no toolchain, so it contributes no
|
|
# leg. hw/bsp holds several CI has never built (efm32, py32f0, same7x, ...) plus
|
|
# espressif, whose boards hil-build-esp builds by name.
|
|
unbuilt = sorted(f for f in sel_fams if f not in family_list)
|
|
if unbuilt and not any(matrix.values()):
|
|
# NONE of the selected families is buildable here, so every leg would skip
|
|
# and the PR would go green from a build job that ran no compiler. That is
|
|
# an unusable selection, not "nothing selected": say UNSCOPED - which
|
|
# build.yml and .circleci/config.yml both grep for - and emit the full
|
|
# matrix. An explicit families: [] is still a legitimate nothing-selected,
|
|
# and a PARTIAL miss still scopes to the families that do build.
|
|
print(f'ci_set_matrix: UNSCOPED - no selected family is built by any '
|
|
f'toolchain here ({", ".join(unbuilt)}), emitting the full matrix',
|
|
file=sys.stderr)
|
|
return set_matrix_json(None)
|
|
if unbuilt:
|
|
print(f'ci_set_matrix: selected families built by no toolchain here: '
|
|
f'{", ".join(unbuilt)}', file=sys.stderr)
|
|
print(json.dumps(matrix))
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser()
|
|
group = parser.add_mutually_exclusive_group()
|
|
group.add_argument('--select', help='tools/ci_select.py JSON; scopes families when build.full is false')
|
|
# a whole selection as one argv/env value can exceed the exec limits on a big
|
|
# diff, which fails the calling step BEFORE it can fall open; callers that
|
|
# already have the selection on disk pass the path instead
|
|
group.add_argument('--select-file', help='file holding the same JSON as --select')
|
|
group.add_argument('--base', help='git ref: run tools/ci_select.py --base REF and scope from it')
|
|
args = parser.parse_args()
|
|
|
|
select = None
|
|
try:
|
|
if args.select:
|
|
select = json.loads(args.select)
|
|
elif args.select_file:
|
|
with open(args.select_file) as f:
|
|
select = json.load(f)
|
|
elif args.base:
|
|
root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
r = subprocess.run([sys.executable, os.path.join(root, 'tools', 'ci_select.py'),
|
|
'--base', args.base],
|
|
capture_output=True, text=True, cwd=root, check=True)
|
|
select = json.loads(r.stdout)
|
|
except Exception as e: # fail-open: an unusable selection must never turn into a red job
|
|
# UNSCOPED is the marker build.yml greps for: it must then drop the build extras
|
|
# (example map, family regex) too, or a full build gets labelled and filtered as
|
|
# a scoped one. Keep the token on every fall-open path.
|
|
print(f'ci_set_matrix: UNSCOPED - selection unusable ({e}), emitting the full '
|
|
f'matrix', file=sys.stderr)
|
|
select = None
|
|
set_matrix_json(select)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|