Skip to content
Closed
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
e019166
ensure that we have an import_std configuration in the test-set; also…
burnpanck Nov 13, 2024
96e17f9
Merge branch 'master' of https://github.com/mpusz/mp-units into featu…
burnpanck Nov 13, 2024
318c4a0
Merge remote-tracking branch 'upstream/master' into feature/faster-CI
burnpanck Nov 17, 2024
bc440cc
ci: expose all conan-configurable features as such to the matrix gene…
burnpanck Nov 17, 2024
3a50086
Merge branch 'master' into feature/faster-CI
burnpanck Nov 17, 2024
22ae08d
Merge remote-tracking branch 'upstream/master' into feature/faster-CI
burnpanck Feb 15, 2025
0683ba1
Fix `import_std` version for clang in job matrix
mpusz Feb 15, 2025
2aa4628
Merge branch 'master' into feature/faster-CI
mpusz Jul 10, 2025
7aaf187
Merge branch 'master' into feature/faster-CI
mpusz Jul 10, 2025
d9f4162
Merge branch 'master' into feature/faster-CI
burnpanck Nov 12, 2025
10687b9
added the option to run the full, dense matrix (careful; more than 10…
burnpanck Nov 12, 2025
085acc1
Merge branch 'master' into feature/faster-CI
burnpanck Nov 12, 2025
dcdb7b6
correctly implement feature support matrix around no_crtp/explicit_this
burnpanck Nov 14, 2025
d8d6994
consolidated configuration summary string generation for CI, try fix …
burnpanck Nov 14, 2025
a386a18
added 'release-test' matrix preset for workflow_dispatch
burnpanck Nov 14, 2025
cc2a1c3
rename `matrix.config` to `matrix.toolchain` - because the config inc…
burnpanck Nov 14, 2025
be293c2
trying to fix import_std CI with a different hash for CMAKE_EXPERIMEN…
burnpanck Nov 14, 2025
ee065c0
try to detect CMake version, so we can set CMAKE_EXPERIMENTAL_CXX_IMP…
burnpanck Nov 19, 2025
5a4cf9c
Merge branch 'master' into feature/faster-CI
burnpanck Dec 12, 2025
0edb7a9
Merge branch 'master' into feature/faster-CI
mpusz Dec 18, 2025
49155c2
Remove std_format_support from macOS 14 config
mpusz Dec 18, 2025
1971c67
Merge branch 'master' into feature/faster-CI
mpusz Dec 18, 2025
49c7c35
Merge branch 'master' into feature/faster-CI
mpusz Dec 18, 2025
071da8a
feat: we just need the latest value of `CMAKE_EXPERIMENTAL_CXX_IMPORT…
mpusz Dec 18, 2025
55df0ad
feat: we just need the latest value of `CMAKE_EXPERIMENTAL_CXX_IMPORT…
mpusz Dec 18, 2025
14e1d7c
Merge branch 'master' into feature/faster-CI
mpusz Dec 18, 2025
48cf248
Remove macOS 13 configuration from job matrix
mpusz Dec 19, 2025
ff0a824
Merge branch 'master' into feature/faster-CI
mpusz Dec 19, 2025
02813e2
Merge branch 'master' into feature/faster-CI
mpusz Dec 19, 2025
098751c
Merge branch 'master' into feature/faster-CI
mpusz Dec 19, 2025
21ba7fe
Merge branch 'master' into feature/faster-CI
mpusz Dec 19, 2025
ea70c70
Merge branch 'master' into feature/faster-CI
mpusz Dec 19, 2025
b8b4b35
Merge branch 'master' into feature/faster-CI
mpusz Dec 19, 2025
f803aaf
ci: update CMake configuration conditions
mpusz Dec 19, 2025
d46808e
Merge branch 'master' into feature/faster-CI
mpusz Dec 28, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .flake8
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,3 @@ ignore =
E712,
# line break before binary operator
W503
per-file-ignores =
# flake8 is just plain wrong here, contradicting black
.github/generate-job-matrix.py:E225,E231
240 changes: 188 additions & 52 deletions .github/generate-job-matrix.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,95 @@
import argparse
import dataclasses
import json
import os
import random
import typing
from dataclasses import dataclass
from types import SimpleNamespace

from job_matrix import CombinationCollector, Compiler, Configuration
from job_matrix_builder import CombinationCollector


def make_gcc_config(version: int) -> Configuration:
return Configuration(
@dataclass(frozen=True, order=True, kw_only=True)
class Compiler:
type: typing.Literal["GCC", "CLANG", "APPLE_CLANG", "MSVC"]
version: str | int
cc: str
cxx: str


@dataclass(frozen=True, order=True, kw_only=True)
class Features:
cxx_modules: bool = False
std_format: bool = False
import_std: bool = False
freestanding: bool = False


@dataclass(frozen=True, order=True, kw_only=True)
class Platform:
"""This is really mainly the compiler."""

name: str
os: str
compiler: Compiler
lib: typing.Literal["libc++", "libstdc++"] | None = None
feature_support: Features

def __str__(self):
return self.name

def for_github(self):
ret = dataclasses.asdict(self)
del ret["feature_support"]
return ret


@dataclass(frozen=True, order=True, kw_only=True)
class Configuration(Features):
platform: Platform
std: typing.Literal[20, 23]
contracts: typing.Literal["none", "gsl-lite", "ms-gsl"]
build_type: typing.Literal["Release", "Debug"]

@property
def is_supported(self) -> bool:
# check if selected features are supported by the platform
s = self.platform.feature_support
for field in dataclasses.fields(Features):
if getattr(self, field.name) and not getattr(s, field.name):
return False
# additional checks for import_std
if self.import_std:
if self.std < 23:
return False
if not self.cxx_modules:
return False
if not self.std_format:
return False
if self.contracts != "none":
return False
return True

def for_github(self):
features = {
field.name: str(getattr(self, field.name))
for field in dataclasses.fields(Features)
}
ret = {
field.name: getattr(self, field.name)
for field in dataclasses.fields(self)
if field.name not in features
}
ret["platform"] = self.platform.for_github()
ret["formatting"] = "std::format" if self.std_format else "fmtlib"
features["contracts"] = self.contracts
ret["conan-config"] = " ".join(f"-o '&:{k}={v}'" for k, v in features.items())
return ret


def make_gcc_platform(version: int) -> Platform:
return Platform(
name=f"GCC-{version}",
os="ubuntu-24.04",
compiler=Compiler(
Expand All @@ -18,25 +98,31 @@ def make_gcc_config(version: int) -> Configuration:
cc=f"gcc-{version}",
cxx=f"g++-{version}",
),
cxx_modules=False,
std_format_support=version >= 13,
feature_support=Features(
std_format=version >= 13,
freestanding=True,
),
)


def make_clang_config(
version: int, platform: typing.Literal["x86-64", "arm64"] = "x86-64"
) -> Configuration:
def make_clang_platform(
version: int, architecture: typing.Literal["x86-64", "arm64"] = "x86-64"
) -> Platform:
cfg = SimpleNamespace(
name=f"Clang-{version} ({platform})",
name=f"Clang-{version} ({architecture})",
compiler=SimpleNamespace(
type="CLANG",
version=version,
),
lib="libc++",
cxx_modules=version >= 17,
std_format_support=version >= 17,
feature_support=Features(
cxx_modules=version >= 17,
std_format=version >= 17,
import_std=version >= 18,
freestanding=True,
),
)
match platform:
match architecture:
case "x86-64":
cfg.os = "ubuntu-22.04" if version < 17 else "ubuntu-24.04"
cfg.compiler.cc = f"clang-{version}"
Expand All @@ -47,16 +133,16 @@ def make_clang_config(
cfg.compiler.cc = f"{pfx}/clang"
cfg.compiler.cxx = f"{pfx}/clang++"
case _:
raise KeyError(f"Unsupported platform {platform!r} for Clang")
raise KeyError(f"Unsupported architecture {architecture!r} for Clang")
ret = cfg
ret.compiler = Compiler(**vars(cfg.compiler))
return Configuration(**vars(ret))
return Platform(**vars(ret))


def make_apple_clang_config(
def make_apple_clang_platform(
os: str, version: str, std_format_support: bool
) -> Configuration:
ret = Configuration(
) -> Platform:
ret = Platform(
name=f"Apple Clang {version}",
os=os,
compiler=Compiler(
Expand All @@ -65,14 +151,13 @@ def make_apple_clang_config(
cc="clang",
cxx="clang++",
),
cxx_modules=False,
std_format_support=std_format_support,
feature_support=Features(std_format=std_format_support),
)
return ret


def make_msvc_config(release: str, version: int) -> Configuration:
ret = Configuration(
def make_msvc_platform(release: str, version: int) -> Platform:
ret = Platform(
name=f"MSVC {release}",
os="windows-2022",
compiler=Compiler(
Expand All @@ -81,38 +166,42 @@ def make_msvc_config(release: str, version: int) -> Configuration:
cc="",
cxx="",
),
cxx_modules=False,
std_format_support=True,
feature_support=Features(
std_format=True,
),
)
return ret


configs = {
c.name: c
for c in [make_gcc_config(ver) for ver in [12, 13, 14]]
platforms = {
p.name: p
for p in [make_gcc_platform(ver) for ver in [12, 13, 14]]
+ [
make_clang_config(ver, platform)
make_clang_platform(ver, arch)
for ver in [16, 17, 18, 20]
for platform in ["x86-64", "arm64"]
for arch in ["x86-64", "arm64"]
# arm64 runners are expensive; only consider one version
if ver == 18 or platform != "arm64"
if ver == 18 or arch != "arm64"
]
+ [
make_apple_clang_config("macos-13", ver, std_format_support=False)
make_apple_clang_platform("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)
make_apple_clang_platform("macos-14", ver, std_format_support=True)
for ver in ["16.1"]
]
+ [make_msvc_config(release="14.4", version=194)]
+ [make_msvc_platform(release="14.4", version=194)]
}

full_matrix = dict(
config=list(configs.values()),
platform=list(platforms.values()),
std=[20, 23],
formatting=["std::format", "fmtlib"],
std_format=[False, True],
import_std=[False, True],
cxx_modules=[False, True],
freestanding=[False, True],
contracts=["none", "gsl-lite", "ms-gsl"],
build_type=["Release", "Debug"],
)
Expand All @@ -122,47 +211,91 @@ 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("--seed", type=int, default=None)
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()

if not args.seed:
args.seed = random.randint(0, (1 << 32) - 1)

print(f"Random-seed for this matrix is {args.seed}")

rgen = random.Random(args.seed)

collector = CombinationCollector(
full_matrix,
hard_excludes=lambda e: (
e.formatting == "std::format" and not e.config.std_format_support
full_matrix=full_matrix,
configuration_element_type=Configuration,
hard_excludes=lambda c: (not c.is_supported)
or (
# TODO For some reason Clang-18 Debug with -ffreestanding does not pass CMakeTestCXXCompiler
c.freestanding
and c.platform.name.startswith("Clang-18")
and c.build_type == "Debug"
),
)
if args.preset:
# whatever the preset; we always want to have a test that does import_std;
# that requires a very specific configuration
collector.sample_combinations(
rgen=rgen,
min_samples=1,
std_format=True,
import_std=True,
cxx_modules=True,
freestanding=args.preset == "freestanding",
std=23,
contracts="none",
platform=platforms["Clang-18 (x86-64)"],
)
match args.preset:
case None:
pass
case "all":
collector.all_combinations()
case "conan" | "cmake":
collector.all_combinations(
formatting="std::format",
config = dict(
contracts="gsl-lite",
build_type="Debug",
std=20,
freestanding=False,
)
collector.all_combinations(
filter=lambda me: not me.config.std_format_support,
formatting="fmtlib",
contracts="gsl-lite",
build_type="Debug",
std=20,
std_format=True,
**config,
)
# fmtlib for those platforms where we don't support std_format
collector.all_combinations(
filter=lambda me: not me.platform.feature_support.std_format,
std_format=False,
**config,
)
collector.sample_combinations(
rgen=rgen,
min_samples_per_value=1,
freestanding=False,
)
# add more coverage to import_std=False configurations;
collector.sample_combinations(
rgen=rgen,
min_samples_per_value=2,
import_std=False,
freestanding=False,
)
collector.sample_combinations(rgen=rgen, min_samples_per_value=2)
case "clang-tidy":
collector.all_combinations(config=configs["Clang-18 (x86-64)"])
collector.sample_combinations(
rgen=rgen,
min_samples_per_value=1,
platform=platforms["Clang-18 (x86-64)"],
freestanding=False,
)
case "freestanding":
collector.all_combinations(
config=[configs[c] for c in ["GCC-14", "Clang-20 (x86-64)"]],
platform=[platforms[c] for c in ["GCC-14", "Clang-20 (x86-64)"]],
contracts="none",
freestanding=True,
std=23,
)
case _:
Expand All @@ -173,7 +306,7 @@ def main():

data = sorted(collector.combinations)

json_data = [e.as_json() for e in data]
json_data = [e.for_github() for e in data]

output_file = os.environ.get("GITHUB_OUTPUT")
if not args.suppress_output:
Expand All @@ -195,13 +328,16 @@ def main():
print(json.dumps(json_data, indent=4))
case "combinations":
for e in data:
std_format = "yes" if e.std_format else "no "
cxx_modules = "yes" if e.cxx_modules else "no "
import_std = "yes" if e.import_std else "no "
print(
f"{e.config!s:17s} c++{e.std:2d} {e.formatting:11s} {e.contracts:8s} {e.build_type:8s}"
f"{e.platform!s:17s} c++{e.std:2d} "
f"{std_format=:3s} {cxx_modules=:3s} {import_std=:3s} "
f"{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 _:
Expand Down
Loading
Loading