1
1
import argparse
2
+ import dataclasses
2
3
import json
3
4
import os
4
5
import random
5
6
import typing
7
+ from dataclasses import dataclass
6
8
from types import SimpleNamespace
7
9
8
- from job_matrix import CombinationCollector , Compiler , Configuration
10
+ from job_matrix_builder import CombinationCollector
9
11
10
12
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 (
13
93
name = f"GCC-{ version } " ,
14
94
os = "ubuntu-24.04" ,
15
95
compiler = Compiler (
@@ -18,25 +98,29 @@ def make_gcc_config(version: int) -> Configuration:
18
98
cc = f"gcc-{ version } " ,
19
99
cxx = f"g++-{ version } " ,
20
100
),
21
- cxx_modules = False ,
22
- std_format_support = version >= 13 ,
101
+ feature_support = Features (
102
+ std_format = version >= 13 ,
103
+ ),
23
104
)
24
105
25
106
26
107
def make_clang_config (
27
- version : int , platform : typing .Literal ["x86-64" , "arm64" ] = "x86-64"
28
- ) -> Configuration :
108
+ version : int , architecture : typing .Literal ["x86-64" , "arm64" ] = "x86-64"
109
+ ) -> Platform :
29
110
cfg = SimpleNamespace (
30
- name = f"Clang-{ version } ({ platform } )" ,
111
+ name = f"Clang-{ version } ({ architecture } )" ,
31
112
compiler = SimpleNamespace (
32
113
type = "CLANG" ,
33
114
version = version ,
34
115
),
35
116
lib = "libc++" ,
36
- cxx_modules = version >= 17 ,
37
- std_format_support = version >= 17 ,
117
+ feature_support = Features (
118
+ cxx_modules = version >= 17 ,
119
+ std_format = version >= 17 ,
120
+ import_std = version >= 17 ,
121
+ ),
38
122
)
39
- match platform :
123
+ match architecture :
40
124
case "x86-64" :
41
125
cfg .os = "ubuntu-22.04" if version < 17 else "ubuntu-24.04"
42
126
cfg .compiler .cc = f"clang-{ version } "
@@ -47,14 +131,14 @@ def make_clang_config(
47
131
cfg .compiler .cc = f"{ pfx } /clang"
48
132
cfg .compiler .cxx = f"{ pfx } /clang++"
49
133
case _:
50
- raise KeyError (f"Unsupported platform { platform !r} for Clang" )
134
+ raise KeyError (f"Unsupported architecture { architecture !r} for Clang" )
51
135
ret = cfg
52
136
ret .compiler = Compiler (** vars (cfg .compiler ))
53
- return Configuration (** vars (ret ))
137
+ return Platform (** vars (ret ))
54
138
55
139
56
- def make_apple_clang_config (version : int ) -> Configuration :
57
- ret = Configuration (
140
+ def make_apple_clang_config (version : int ) -> Platform :
141
+ ret = Platform (
58
142
name = f"Apple Clang { version } " ,
59
143
os = "macos-13" ,
60
144
compiler = Compiler (
@@ -63,14 +147,13 @@ def make_apple_clang_config(version: int) -> Configuration:
63
147
cc = "clang" ,
64
148
cxx = "clang++" ,
65
149
),
66
- cxx_modules = False ,
67
- std_format_support = False ,
150
+ feature_support = Features (),
68
151
)
69
152
return ret
70
153
71
154
72
- def make_msvc_config (release : str , version : int ) -> Configuration :
73
- ret = Configuration (
155
+ def make_msvc_config (release : str , version : int ) -> Platform :
156
+ ret = Platform (
74
157
name = f"MSVC { release } " ,
75
158
os = "windows-2022" ,
76
159
compiler = Compiler (
@@ -79,30 +162,34 @@ def make_msvc_config(release: str, version: int) -> Configuration:
79
162
cc = "" ,
80
163
cxx = "" ,
81
164
),
82
- cxx_modules = False ,
83
- std_format_support = True ,
165
+ feature_support = Features (
166
+ std_format = True ,
167
+ ),
84
168
)
85
169
return ret
86
170
87
171
88
- configs = {
89
- c .name : c
90
- for c in [make_gcc_config (ver ) for ver in [12 , 13 , 14 ]]
172
+ platforms = {
173
+ p .name : p
174
+ for p in [make_gcc_config (ver ) for ver in [12 , 13 , 14 ]]
91
175
+ [
92
- make_clang_config (ver , platform )
176
+ make_clang_config (ver , arch )
93
177
for ver in [16 , 17 , 18 ]
94
- for platform in ["x86-64" , "arm64" ]
178
+ for arch in ["x86-64" , "arm64" ]
95
179
# arm64 runners are expensive; only consider one version
96
- if ver == 18 or platform != "arm64"
180
+ if ver == 18 or arch != "arm64"
97
181
]
98
182
+ [make_apple_clang_config (ver ) for ver in [15 ]]
99
183
+ [make_msvc_config (release = "14.4" , version = 194 )]
100
184
}
101
185
102
186
full_matrix = dict (
103
- config = list (configs .values ()),
187
+ platform = list (platforms .values ()),
104
188
std = [20 , 23 ],
105
- formatting = ["std::format" , "fmtlib" ],
189
+ std_format = [False , True ],
190
+ import_std = [False , True ],
191
+ cxx_modules = [False , True ],
192
+ freestanding = [False , True ],
106
193
contracts = ["none" , "gsl-lite" , "ms-gsl" ],
107
194
build_type = ["Release" , "Debug" ],
108
195
)
@@ -112,61 +199,83 @@ def main():
112
199
parser = argparse .ArgumentParser ()
113
200
# parser.add_argument("-I","--include",nargs="+",action="append")
114
201
# parser.add_argument("-X","--exclude",nargs="+",action="append")
115
- parser .add_argument ("--seed" , type = int , default = 42 )
202
+ parser .add_argument ("--seed" , type = int , default = None )
116
203
parser .add_argument ("--preset" , default = None )
117
204
parser .add_argument ("--debug" , nargs = "+" , default = ["combinations" ])
118
205
parser .add_argument ("--suppress-output" , default = False , action = "store_true" )
119
206
120
207
args = parser .parse_args ()
121
208
209
+ if not args .seed :
210
+ args .seed = random .randint (0 , (1 << 32 ) - 1 )
211
+
212
+ print (f"Random-seed for this matrix is { args .seed } " )
213
+
122
214
rgen = random .Random (args .seed )
123
215
124
216
collector = CombinationCollector (
125
- full_matrix ,
126
- hard_excludes = lambda e : (
127
- e .formatting == "std::format" and not e .config .std_format_support
217
+ full_matrix = full_matrix ,
218
+ configuration_element_type = Configuration ,
219
+ hard_excludes = lambda c : (not c .is_supported )
220
+ or (
221
+ # TODO For some reason Clang-18 Debug with -ffreestanding does not pass CMakeTestCXXCompiler
222
+ c .freestanding
223
+ and c .platform .name .startswith ("Clang-18" )
224
+ and c .build_type == "Debug"
128
225
),
129
226
)
130
227
if args .preset :
131
228
# whatever the preset; we always want to have a test that does import_std;
132
229
# that requires a very specific configuration
133
230
collector .sample_combinations (
134
231
rgen = rgen ,
135
- min_samples_per_value = 1 ,
136
- formatting = "std::format" ,
232
+ min_samples = 1 ,
233
+ std_format = True ,
234
+ import_std = True ,
235
+ cxx_modules = True ,
236
+ freestanding = args .preset == "freestanding" ,
237
+ std = 23 ,
137
238
contracts = "none" ,
138
- config = configs ["Clang-18 (x86-64)" ],
239
+ platform = platforms ["Clang-18 (x86-64)" ],
139
240
)
140
241
match args .preset :
141
242
case None :
142
243
pass
143
244
case "all" :
144
245
collector .all_combinations ()
145
246
case "conan" | "cmake" :
146
- collector .all_combinations (
147
- formatting = "std::format" ,
247
+ config = dict (
148
248
contracts = "gsl-lite" ,
149
249
build_type = "Debug" ,
150
250
std = 20 ,
251
+ freestanding = False ,
151
252
)
152
253
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 ,
254
+ std_format = True ,
255
+ ** config ,
256
+ )
257
+ # fmtlib for those platforms where we don't support std_format
258
+ collector .all_combinations (
259
+ filter = lambda me : not me .platform .feature_support .std_format ,
260
+ std_format = False ,
261
+ ** config ,
262
+ )
263
+ collector .sample_combinations (
264
+ rgen = rgen ,
265
+ # there is just a single acceptable import_std configuration; so we cannot request more than one here
266
+ min_samples_per_value = 1 ,
267
+ freestanding = False ,
158
268
)
159
- collector .sample_combinations (rgen = rgen , min_samples_per_value = 2 )
160
269
case "clang-tidy" :
161
- collector .all_combinations (config = configs ["Clang-18 (x86-64)" ])
270
+ collector .all_combinations (
271
+ platform = platforms ["Clang-18 (x86-64)" ],
272
+ freestanding = False ,
273
+ )
162
274
case "freestanding" :
163
- # TODO For some reason Clang-18 Debug with -ffreestanding does not pass CMakeTestCXXCompiler
164
275
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)" ]],
276
+ platform = [platforms [c ] for c in ["GCC-14" , "Clang-18 (x86-64)" ]],
169
277
contracts = "none" ,
278
+ freestanding = True ,
170
279
std = 23 ,
171
280
)
172
281
case _:
@@ -177,7 +286,7 @@ def main():
177
286
178
287
data = sorted (collector .combinations )
179
288
180
- json_data = [e .as_json () for e in data ]
289
+ json_data = [e .for_github () for e in data ]
181
290
182
291
output_file = os .environ .get ("GITHUB_OUTPUT" )
183
292
if not args .suppress_output :
@@ -199,8 +308,13 @@ def main():
199
308
print (json .dumps (json_data , indent = 4 ))
200
309
case "combinations" :
201
310
for e in data :
311
+ std_format = "yes" if e .std_format else "no "
312
+ cxx_modules = "yes" if e .cxx_modules else "no "
313
+ import_std = "yes" if e .import_std else "no "
202
314
print (
203
- f"{ e .config !s:17s} c++{ e .std :2d} { e .formatting :11s} { e .contracts :8s} { e .build_type :8s} "
315
+ f"{ e .platform !s:17s} c++{ e .std :2d} "
316
+ f"{ std_format = :3s} { cxx_modules = :3s} { import_std = :3s} "
317
+ f"{ e .contracts :8s} { e .build_type :8s} "
204
318
)
205
319
case "counts" :
206
320
print (f"Total combinations { len (data )} " )
0 commit comments