11import argparse
2+ import dataclasses
23import json
34import os
45import random
56import typing
7+ from dataclasses import dataclass
68from types import SimpleNamespace
79
8- from job_matrix import CombinationCollector , Compiler , Configuration
10+ from job_matrix_builder import CombinationCollector
911
1012
11- def make_gcc_config (version : int ) -> Configuration :
12- return Configuration (
13+ @dataclass (frozen = True , order = True , kw_only = True )
14+ class Compiler :
15+ type : typing .Literal ["GCC" , "CLANG" , "APPLE_CLANG" , "MSVC" ]
16+ version : str | int
17+ cc : str
18+ cxx : str
19+
20+
21+ @dataclass (frozen = True , order = True , kw_only = True )
22+ class Features :
23+ cxx_modules : bool = False
24+ std_format : bool = False
25+ import_std : bool = False
26+ freestanding : bool = False
27+
28+
29+ @dataclass (frozen = True , order = True , kw_only = True )
30+ class Platform :
31+ """This is really mainly the compiler."""
32+
33+ name : str
34+ os : str
35+ compiler : Compiler
36+ lib : typing .Literal ["libc++" , "libstdc++" ] | None = None
37+ feature_support : Features
38+
39+ def __str__ (self ):
40+ return self .name
41+
42+ def for_github (self ):
43+ ret = dataclasses .asdict (self )
44+ del ret ["feature_support" ]
45+ return ret
46+
47+
48+ @dataclass (frozen = True , order = True , kw_only = True )
49+ class Configuration (Features ):
50+ platform : Platform
51+ std : typing .Literal [20 , 23 ]
52+ contracts : typing .Literal ["none" , "gsl-lite" , "ms-gsl" ]
53+ build_type : typing .Literal ["Release" , "Debug" ]
54+
55+ @property
56+ def is_supported (self ) -> bool :
57+ # check if selected features are supported by the platform
58+ s = self .platform .feature_support
59+ for field in dataclasses .fields (Features ):
60+ if getattr (self , field .name ) and not getattr (s , field .name ):
61+ return False
62+ # additional checks for import_std
63+ if self .import_std :
64+ if self .std < 23 :
65+ return False
66+ if not self .cxx_modules :
67+ return False
68+ if not self .std_format :
69+ return False
70+ if self .contracts != "none" :
71+ return False
72+ return True
73+
74+ def for_github (self ):
75+ features = {
76+ field .name : str (getattr (self , field .name ))
77+ for field in dataclasses .fields (Features )
78+ }
79+ ret = {
80+ field .name : getattr (self , field .name )
81+ for field in dataclasses .fields (self )
82+ if field .name not in features
83+ }
84+ ret ["platform" ] = self .platform .for_github ()
85+ ret ["formatting" ] = "std::format" if self .std_format else "fmtlib"
86+ features ["contracts" ] = self .contracts
87+ ret ["conan-config" ] = " " .join (f"-o '&:{ k } ={ v } '" for k , v in features .items ())
88+ return ret
89+
90+
91+ def make_gcc_config (version : int ) -> Platform :
92+ return Platform (
1393 name = f"GCC-{ version } " ,
1494 os = "ubuntu-24.04" ,
1595 compiler = Compiler (
@@ -18,25 +98,31 @@ def make_gcc_config(version: int) -> Configuration:
1898 cc = f"gcc-{ version } " ,
1999 cxx = f"g++-{ version } " ,
20100 ),
21- cxx_modules = False ,
22- std_format_support = version >= 13 ,
101+ feature_support = Features (
102+ std_format = version >= 13 ,
103+ freestanding = True ,
104+ ),
23105 )
24106
25107
26108def make_clang_config (
27- version : int , platform : typing .Literal ["x86-64" , "arm64" ] = "x86-64"
28- ) -> Configuration :
109+ version : int , architecture : typing .Literal ["x86-64" , "arm64" ] = "x86-64"
110+ ) -> Platform :
29111 cfg = SimpleNamespace (
30- name = f"Clang-{ version } ({ platform } )" ,
112+ name = f"Clang-{ version } ({ architecture } )" ,
31113 compiler = SimpleNamespace (
32114 type = "CLANG" ,
33115 version = version ,
34116 ),
35117 lib = "libc++" ,
36- cxx_modules = version >= 17 ,
37- std_format_support = version >= 17 ,
118+ feature_support = Features (
119+ cxx_modules = version >= 17 ,
120+ std_format = version >= 17 ,
121+ import_std = version >= 17 ,
122+ freestanding = True ,
123+ ),
38124 )
39- match platform :
125+ match architecture :
40126 case "x86-64" :
41127 cfg .os = "ubuntu-22.04" if version < 17 else "ubuntu-24.04"
42128 cfg .compiler .cc = f"clang-{ version } "
@@ -47,14 +133,14 @@ def make_clang_config(
47133 cfg .compiler .cc = f"{ pfx } /clang"
48134 cfg .compiler .cxx = f"{ pfx } /clang++"
49135 case _:
50- raise KeyError (f"Unsupported platform { platform !r} for Clang" )
136+ raise KeyError (f"Unsupported architecture { architecture !r} for Clang" )
51137 ret = cfg
52138 ret .compiler = Compiler (** vars (cfg .compiler ))
53- return Configuration (** vars (ret ))
139+ return Platform (** vars (ret ))
54140
55141
56- def make_apple_clang_config (version : int ) -> Configuration :
57- ret = Configuration (
142+ def make_apple_clang_config (version : int ) -> Platform :
143+ ret = Platform (
58144 name = f"Apple Clang { version } " ,
59145 os = "macos-13" ,
60146 compiler = Compiler (
@@ -63,14 +149,13 @@ def make_apple_clang_config(version: int) -> Configuration:
63149 cc = "clang" ,
64150 cxx = "clang++" ,
65151 ),
66- cxx_modules = False ,
67- std_format_support = False ,
152+ feature_support = Features (),
68153 )
69154 return ret
70155
71156
72- def make_msvc_config (release : str , version : int ) -> Configuration :
73- ret = Configuration (
157+ def make_msvc_config (release : str , version : int ) -> Platform :
158+ ret = Platform (
74159 name = f"MSVC { release } " ,
75160 os = "windows-2022" ,
76161 compiler = Compiler (
@@ -79,30 +164,34 @@ def make_msvc_config(release: str, version: int) -> Configuration:
79164 cc = "" ,
80165 cxx = "" ,
81166 ),
82- cxx_modules = False ,
83- std_format_support = True ,
167+ feature_support = Features (
168+ std_format = True ,
169+ ),
84170 )
85171 return ret
86172
87173
88- configs = {
89- c .name : c
90- for c in [make_gcc_config (ver ) for ver in [12 , 13 , 14 ]]
174+ platforms = {
175+ p .name : p
176+ for p in [make_gcc_config (ver ) for ver in [12 , 13 , 14 ]]
91177 + [
92- make_clang_config (ver , platform )
178+ make_clang_config (ver , arch )
93179 for ver in [16 , 17 , 18 ]
94- for platform in ["x86-64" , "arm64" ]
180+ for arch in ["x86-64" , "arm64" ]
95181 # arm64 runners are expensive; only consider one version
96- if ver == 18 or platform != "arm64"
182+ if ver == 18 or arch != "arm64"
97183 ]
98184 + [make_apple_clang_config (ver ) for ver in [15 ]]
99185 + [make_msvc_config (release = "14.4" , version = 194 )]
100186}
101187
102188full_matrix = dict (
103- config = list (configs .values ()),
189+ platform = list (platforms .values ()),
104190 std = [20 , 23 ],
105- formatting = ["std::format" , "fmtlib" ],
191+ std_format = [False , True ],
192+ import_std = [False , True ],
193+ cxx_modules = [False , True ],
194+ freestanding = [False , True ],
106195 contracts = ["none" , "gsl-lite" , "ms-gsl" ],
107196 build_type = ["Release" , "Debug" ],
108197)
@@ -112,61 +201,83 @@ def main():
112201 parser = argparse .ArgumentParser ()
113202 # parser.add_argument("-I","--include",nargs="+",action="append")
114203 # parser.add_argument("-X","--exclude",nargs="+",action="append")
115- parser .add_argument ("--seed" , type = int , default = 42 )
204+ parser .add_argument ("--seed" , type = int , default = None )
116205 parser .add_argument ("--preset" , default = None )
117206 parser .add_argument ("--debug" , nargs = "+" , default = ["combinations" ])
118207 parser .add_argument ("--suppress-output" , default = False , action = "store_true" )
119208
120209 args = parser .parse_args ()
121210
211+ if not args .seed :
212+ args .seed = random .randint (0 , (1 << 32 ) - 1 )
213+
214+ print (f"Random-seed for this matrix is { args .seed } " )
215+
122216 rgen = random .Random (args .seed )
123217
124218 collector = CombinationCollector (
125- full_matrix ,
126- hard_excludes = lambda e : (
127- e .formatting == "std::format" and not e .config .std_format_support
219+ full_matrix = full_matrix ,
220+ configuration_element_type = Configuration ,
221+ hard_excludes = lambda c : (not c .is_supported )
222+ or (
223+ # TODO For some reason Clang-18 Debug with -ffreestanding does not pass CMakeTestCXXCompiler
224+ c .freestanding
225+ and c .platform .name .startswith ("Clang-18" )
226+ and c .build_type == "Debug"
128227 ),
129228 )
130229 if args .preset :
131230 # whatever the preset; we always want to have a test that does import_std;
132231 # that requires a very specific configuration
133232 collector .sample_combinations (
134233 rgen = rgen ,
135- min_samples_per_value = 1 ,
136- formatting = "std::format" ,
234+ min_samples = 1 ,
235+ std_format = True ,
236+ import_std = True ,
237+ cxx_modules = True ,
238+ freestanding = args .preset == "freestanding" ,
239+ std = 23 ,
137240 contracts = "none" ,
138- config = configs ["Clang-18 (x86-64)" ],
241+ platform = platforms ["Clang-18 (x86-64)" ],
139242 )
140243 match args .preset :
141244 case None :
142245 pass
143246 case "all" :
144247 collector .all_combinations ()
145248 case "conan" | "cmake" :
146- collector .all_combinations (
147- formatting = "std::format" ,
249+ config = dict (
148250 contracts = "gsl-lite" ,
149251 build_type = "Debug" ,
150252 std = 20 ,
253+ freestanding = False ,
151254 )
152255 collector .all_combinations (
153- filter = lambda me : not me .config .std_format_support ,
154- formatting = "fmtlib" ,
155- contracts = "gsl-lite" ,
156- build_type = "Debug" ,
157- std = 20 ,
256+ std_format = True ,
257+ ** config ,
258+ )
259+ # fmtlib for those platforms where we don't support std_format
260+ collector .all_combinations (
261+ filter = lambda me : not me .platform .feature_support .std_format ,
262+ std_format = False ,
263+ ** config ,
264+ )
265+ collector .sample_combinations (
266+ rgen = rgen ,
267+ # there is just a single acceptable import_std configuration; so we cannot request more than one here
268+ min_samples_per_value = 1 ,
269+ freestanding = False ,
158270 )
159- collector .sample_combinations (rgen = rgen , min_samples_per_value = 2 )
160271 case "clang-tidy" :
161- collector .all_combinations (config = configs ["Clang-18 (x86-64)" ])
272+ collector .all_combinations (
273+ platform = platforms ["Clang-18 (x86-64)" ],
274+ freestanding = False ,
275+ )
162276 case "freestanding" :
163- # TODO For some reason Clang-18 Debug with -ffreestanding does not pass CMakeTestCXXCompiler
164277 collector .all_combinations (
165- filter = lambda e : not (
166- e .config .name .startswith ("Clang-18" ) and e .build_type == "Debug"
167- ),
168- config = [configs [c ] for c in ["GCC-14" , "Clang-18 (x86-64)" ]],
278+ platform = [platforms [c ] for c in ["GCC-14" , "Clang-18 (x86-64)" ]],
169279 contracts = "none" ,
280+ freestanding = True ,
170281 std = 23 ,
171282 )
172283 case _:
@@ -177,7 +288,7 @@ def main():
177288
178289 data = sorted (collector .combinations )
179290
180- json_data = [e .as_json () for e in data ]
291+ json_data = [e .for_github () for e in data ]
181292
182293 output_file = os .environ .get ("GITHUB_OUTPUT" )
183294 if not args .suppress_output :
@@ -199,8 +310,13 @@ def main():
199310 print (json .dumps (json_data , indent = 4 ))
200311 case "combinations" :
201312 for e in data :
313+ std_format = "yes" if e .std_format else "no "
314+ cxx_modules = "yes" if e .cxx_modules else "no "
315+ import_std = "yes" if e .import_std else "no "
202316 print (
203- f"{ e .config !s:17s} c++{ e .std :2d} { e .formatting :11s} { e .contracts :8s} { e .build_type :8s} "
317+ f"{ e .platform !s:17s} c++{ e .std :2d} "
318+ f"{ std_format = :3s} { cxx_modules = :3s} { import_std = :3s} "
319+ f"{ e .contracts :8s} { e .build_type :8s} "
204320 )
205321 case "counts" :
206322 print (f"Total combinations { len (data )} " )
0 commit comments