Files
mp-units/.github/generate-job-matrix.py
2025-04-21 16:30:33 -04:00

217 lines
6.5 KiB
Python

import argparse
import json
import os
import random
import typing
from types import SimpleNamespace
from job_matrix import CombinationCollector, Compiler, Configuration
def make_gcc_config(version: int) -> Configuration:
return Configuration(
name=f"GCC-{version}",
os="ubuntu-24.04",
compiler=Compiler(
type="GCC",
version=version,
cc=f"gcc-{version}",
cxx=f"g++-{version}",
),
cxx_modules=False,
std_format_support=version >= 13,
)
def make_clang_config(
version: int, platform: typing.Literal["x86-64", "arm64"] = "x86-64"
) -> Configuration:
cfg = SimpleNamespace(
name=f"Clang-{version} ({platform})",
compiler=SimpleNamespace(
type="CLANG",
version=version,
),
lib="libc++",
cxx_modules=version >= 17,
std_format_support=version >= 17,
)
match platform:
case "x86-64":
cfg.os = "ubuntu-22.04" if version < 17 else "ubuntu-24.04"
cfg.compiler.cc = f"clang-{version}"
cfg.compiler.cxx = f"clang++-{version}"
case "arm64":
cfg.os = "macos-14"
pfx = f"/opt/homebrew/opt/llvm@{version}/bin"
cfg.compiler.cc = f"{pfx}/clang"
cfg.compiler.cxx = f"{pfx}/clang++"
case _:
raise KeyError(f"Unsupported platform {platform!r} for Clang")
ret = cfg
ret.compiler = Compiler(**vars(cfg.compiler))
return Configuration(**vars(ret))
def make_apple_clang_config(
os: str, version: str, std_format_support: bool
) -> Configuration:
ret = Configuration(
name=f"Apple Clang {version}",
os=os,
compiler=Compiler(
type="APPLE_CLANG",
version=version,
cc="clang",
cxx="clang++",
),
cxx_modules=False,
std_format_support=std_format_support,
)
return ret
def make_msvc_config(release: str, version: int) -> Configuration:
ret = Configuration(
name=f"MSVC {release}",
os="windows-2022",
compiler=Compiler(
type="MSVC",
version=version,
cc="",
cxx="",
),
cxx_modules=False,
std_format_support=True,
)
return ret
configs = {
c.name: c
for c in [make_gcc_config(ver) for ver in [12, 13, 14]]
+ [
make_clang_config(ver, platform)
for ver in [16, 17, 18, 20]
for platform in ["x86-64", "arm64"]
# arm64 runners are expensive; only consider one version
if ver == 18 or platform != "arm64"
]
+ [
make_apple_clang_config("macos-13", ver, std_format_support=False)
for ver in ["15.2"]
]
# std::format is available in Xcode 16.1 or later
+ [
make_apple_clang_config("macos-14", ver, std_format_support=True)
for ver in ["16.1"]
]
+ [make_msvc_config(release="14.4", version=194)]
}
full_matrix = dict(
config=list(configs.values()),
std=[20, 23],
formatting=["std::format", "fmtlib"],
contracts=["none", "gsl-lite", "ms-gsl"],
build_type=["Release", "Debug"],
)
def main():
parser = argparse.ArgumentParser()
# parser.add_argument("-I","--include",nargs="+",action="append")
# parser.add_argument("-X","--exclude",nargs="+",action="append")
parser.add_argument("--seed", type=int, default=42)
parser.add_argument("--preset", default=None)
parser.add_argument("--debug", nargs="+", default=["combinations"])
parser.add_argument("--suppress-output", default=False, action="store_true")
args = parser.parse_args()
rgen = random.Random(args.seed)
collector = CombinationCollector(
full_matrix,
hard_excludes=lambda e: (
e.formatting == "std::format" and not e.config.std_format_support
),
)
match args.preset:
case None:
pass
case "all":
collector.all_combinations()
case "conan" | "cmake":
collector.all_combinations(
formatting="std::format",
contracts="gsl-lite",
build_type="Debug",
std=20,
)
collector.all_combinations(
filter=lambda me: not me.config.std_format_support,
formatting="fmtlib",
contracts="gsl-lite",
build_type="Debug",
std=20,
)
collector.sample_combinations(rgen=rgen, min_samples_per_value=2)
case "clang-tidy":
collector.all_combinations(config=configs["Clang-18 (x86-64)"])
case "freestanding":
# TODO For some reason Clang-18 Debug with -ffreestanding does not pass CMakeTestCXXCompiler
collector.all_combinations(
filter=lambda e: not (
e.config.name.startswith("Clang-18") and e.build_type == "Debug"
),
config=[configs[c] for c in ["GCC-14", "Clang-18 (x86-64)"]],
contracts="none",
std=23,
)
case _:
raise KeyError(f"Unsupported preset {args.preset!r}")
if not collector.combinations:
raise ValueError("No combination has been produced")
data = sorted(collector.combinations)
json_data = [e.as_json() for e in data]
output_file = os.environ.get("GITHUB_OUTPUT")
if not args.suppress_output:
if output_file:
print(f"Writing outputs to {output_file}")
with open(output_file, "wt") as fh:
fh.write(f"matrix={json.dumps(json_data)}")
else:
print("No output file received!")
for dbg in args.debug:
match dbg:
case "yaml":
import yaml
json_data = json.loads(json.dumps(json_data))
print(yaml.safe_dump(json_data))
case "json":
print(json.dumps(json_data, indent=4))
case "combinations":
for e in data:
print(
f"{e.config!s:17s} c++{e.std:2d} {e.formatting:11s} {e.contracts:8s} {e.build_type:8s}"
)
case "counts":
print(f"Total combinations {len(data)}")
for (k, v), n in sorted(collector.per_value_counts.items()):
print(f" {k}={v}: {n}")
case "none":
pass
case _:
raise KeyError(f"Unknown debug mode {dbg!r}")
if __name__ == "__main__":
main()