Skip to content

Commit f05d410

Browse files
committed
Streamline ci matrix strategy
1 parent d525775 commit f05d410

File tree

6 files changed

+119
-71
lines changed

6 files changed

+119
-71
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -642,8 +642,6 @@ jobs:
642642
matrix:
643643
compiler: [g++-10, clang++-18]
644644
stdlib: [libstdc++, libc++]
645-
dwarf_version: [4, 5]
646-
split_dwarf: [OFF, ON]
647645
exclude:
648646
- compiler: g++-10
649647
stdlib: libc++
@@ -687,9 +685,7 @@ jobs:
687685
run: |
688686
python3 ci/unittest.py \
689687
--slice=compiler:${{matrix.compiler}} \
690-
--slice=stdlib:${{matrix.stdlib}} \
691-
--slice=dwarf_version:${{matrix.dwarf_version}} \
692-
--slice=split_dwarf:${{matrix.split_dwarf}}
688+
--slice=stdlib:${{matrix.stdlib}}
693689
unittest-linux-bazel:
694690
runs-on: ubuntu-22.04
695691
steps:
@@ -711,8 +707,6 @@ jobs:
711707
matrix:
712708
compiler: [g++-10, clang++-18]
713709
stdlib: [libstdc++, libc++]
714-
dwarf_version: [4, 5]
715-
split_dwarf: [OFF, ON]
716710
exclude:
717711
- compiler: g++-10
718712
stdlib: libc++
@@ -756,9 +750,7 @@ jobs:
756750
run: |
757751
python3 ci/unittest.py \
758752
--slice=compiler:${{matrix.compiler}} \
759-
--slice=stdlib:${{matrix.stdlib}} \
760-
--slice=dwarf_version:${{matrix.dwarf_version}} \
761-
--slice=split_dwarf:${{matrix.split_dwarf}}
753+
--slice=stdlib:${{matrix.stdlib}}
762754
unittest-macos:
763755
runs-on: macos-14
764756
steps:

CMakeLists.txt

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -78,17 +78,19 @@ include(cmake/Autoconfig.cmake)
7878

7979
# =================================================== Library Setup ====================================================
8080

81-
if(NOT CPPTRACE_BUILD_NO_SYMBOLS)
81+
set(
82+
debug
83+
$<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-g>
84+
$<$<CXX_COMPILER_ID:MSVC>:/DEBUG>
85+
)
86+
87+
if(CPPTRACE_BUILD_NO_SYMBOLS)
8288
set(
83-
debug
84-
$<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-g>
85-
$<$<CXX_COMPILER_ID:MSVC>:/DEBUG>
89+
test_debug
90+
$<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-g0>
8691
)
8792
else()
88-
add_compile_options($<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-g0>)
89-
set(
90-
debug
91-
)
93+
set(test_debug ${debug})
9294
endif()
9395

9496
# Target that we can modify (can't modify ALIAS targets)

ci/test-all-configs.py

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ def run_linux_matrix(compilers: list, shared: bool):
349349
matrix = {
350350
"compiler": compilers,
351351
"target": ["Debug"],
352-
"std": ["11", "20"],
352+
"std": ["11"],
353353
"unwind": [
354354
"CPPTRACE_UNWIND_WITH_EXECINFO",
355355
"CPPTRACE_UNWIND_WITH_UNWIND",
@@ -373,12 +373,14 @@ def run_linux_matrix(compilers: list, shared: bool):
373373
exclude = []
374374
).run(build_and_test)
375375

376-
def run_linux_default(compilers: list, shared: bool):
376+
def run_linux_default(compilers: list, shared: bool, stds=None):
377+
if stds is None:
378+
stds = ["11", "20"]
377379
MatrixRunner(
378380
matrix = {
379381
"compiler": compilers,
380382
"target": ["Debug"],
381-
"std": ["11", "20"],
383+
"std": stds,
382384
"config": [""],
383385
"shared": ["On" if shared else "Off"]
384386
},
@@ -390,7 +392,7 @@ def run_macos_matrix(compilers: list, shared: bool):
390392
matrix = {
391393
"compiler": compilers,
392394
"target": ["Debug"],
393-
"std": ["11", "20"],
395+
"std": ["11"],
394396
"unwind": [
395397
"CPPTRACE_UNWIND_WITH_EXECINFO",
396398
"CPPTRACE_UNWIND_WITH_UNWIND",
@@ -413,12 +415,14 @@ def run_macos_matrix(compilers: list, shared: bool):
413415
exclude = []
414416
).run(build_and_test)
415417

416-
def run_macos_default(compilers: list, shared: bool):
418+
def run_macos_default(compilers: list, shared: bool, stds=None):
419+
if stds is None:
420+
stds = ["11", "20"]
417421
MatrixRunner(
418422
matrix = {
419423
"compiler": compilers,
420424
"target": ["Debug"],
421-
"std": ["11", "20"],
425+
"std": stds,
422426
"config": [""],
423427
"shared": ["On" if shared else "Off"]
424428
},
@@ -430,7 +434,7 @@ def run_windows_matrix(compilers: list, shared: bool):
430434
matrix = {
431435
"compiler": compilers,
432436
"target": ["Debug"],
433-
"std": ["11", "20"],
437+
"std": ["11"],
434438
"unwind": [
435439
"CPPTRACE_UNWIND_WITH_WINAPI",
436440
"CPPTRACE_UNWIND_WITH_DBGHELP",
@@ -498,12 +502,14 @@ def run_windows_matrix(compilers: list, shared: bool):
498502
]
499503
).run(build_and_test)
500504

501-
def run_windows_default(compilers: list, shared: bool):
505+
def run_windows_default(compilers: list, shared: bool, stds=None):
506+
if stds is None:
507+
stds = ["11", "20"]
502508
MatrixRunner(
503509
matrix = {
504510
"compiler": compilers,
505511
"target": ["Debug"],
506-
"std": ["11", "20"],
512+
"std": stds,
507513
"config": [""],
508514
"shared": ["On" if shared else "Off"]
509515
},
@@ -551,6 +557,7 @@ def main():
551557
run_linux_default(compilers, args.shared)
552558
else:
553559
run_linux_matrix(compilers, args.shared)
560+
run_linux_default(compilers, args.shared, stds=["20"])
554561
if platform.system() == "Darwin":
555562
compilers = []
556563
if args.clang or args.all:
@@ -561,6 +568,7 @@ def main():
561568
run_macos_default(compilers, args.shared)
562569
else:
563570
run_macos_matrix(compilers, args.shared)
571+
run_macos_default(compilers, args.shared, stds=["20"])
564572
if platform.system() == "Windows":
565573
compilers = []
566574
if args.clang or args.all:
@@ -573,5 +581,6 @@ def main():
573581
run_windows_default(compilers, args.shared)
574582
else:
575583
run_windows_matrix(compilers, args.shared)
584+
run_windows_default(compilers, args.shared, stds=["20"])
576585

577586
main()

ci/unittest.py

Lines changed: 43 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ def build(runner: MatrixRunner):
6969
f"-DCPPTRACE_SANITIZER_BUILD={matrix['sanitizers']}",
7070
f"-DCPPTRACE_BUILD_NO_SYMBOLS={matrix['no_symbols']}",
7171
# f"-DCPPTRACE_BUILD_TESTING_SPLIT_DWARF={matrix['split_dwarf']}",
72-
# f"-DCPPTRACE_BUILD_TESTING_SPLIT_DWARF={matrix['dwarf_version']}",
72+
# f"-DCPPTRACE_BUILD_TESTING_DWARF_VERSION={matrix['dwarf_version']}",
7373
f"-DCPPTRACE_USE_EXTERNAL_LIBDWARF=On",
7474
f"-DCPPTRACE_USE_EXTERNAL_ZSTD=On",
7575
f"-DCPPTRACE_USE_EXTERNAL_GTEST=On",
@@ -121,45 +121,77 @@ def build_and_test(runner: MatrixRunner):
121121
def run_linux_matrix():
122122
MatrixRunner(
123123
matrix = {
124+
# Always fully cross-producted
124125
"compiler": ["g++-10", "clang++-18"],
125126
"stdlib": ["libstdc++", "libc++"],
126-
"sanitizers": ["OFF", "ON"],
127-
"build_type": ["Debug", "RelWithDebInfo"],
128-
"shared": ["OFF", "ON"],
129-
"has_dl_find_object": ["OFF", "ON"],
130127
"split_dwarf": ["OFF", "ON"],
131128
"dwarf_version": ["4", "5"],
132129
"no_symbols": ["On", "Off"],
133130
},
131+
strata = [
132+
# Base: full sanitizer x shared cross-product
133+
{
134+
"sanitizers": ["OFF", "ON"],
135+
"build_type": ["Debug"],
136+
"shared": ["OFF", "ON"],
137+
"has_dl_find_object": ["ON"],
138+
},
139+
# Spot-check: RelWithDebInfo
140+
{
141+
"sanitizers": ["OFF"],
142+
"build_type": ["RelWithDebInfo"],
143+
"shared": ["OFF"],
144+
"has_dl_find_object": ["ON"],
145+
},
146+
# Spot-check: has_dl_find_object=OFF
147+
{
148+
"sanitizers": ["OFF"],
149+
"build_type": ["Debug"],
150+
"shared": ["OFF"],
151+
"has_dl_find_object": ["OFF"],
152+
},
153+
],
134154
exclude = [
135155
{
136156
"compiler": "g++-10",
137157
"stdlib": "libc++",
138158
},
159+
# need to workaround https://github.com/llvm/llvm-project/issues/59432 later
139160
{
140-
# need to workaround https://github.com/llvm/llvm-project/issues/59432 later
141161
"stdlib": "libc++",
142162
"sanitizers": "ON",
143163
},
144-
]
164+
],
145165
).run(build_and_test)
146166

147167
def run_macos_matrix():
148168
MatrixRunner(
149169
matrix = {
170+
# Always fully cross-producted
150171
"compiler": ["g++-12", "clang++"],
151-
"sanitizers": ["OFF", "ON"],
152-
"build_type": ["Debug", "RelWithDebInfo"],
153-
"shared": ["OFF", "ON"],
154172
"dSYM": [True, False],
155173
"no_symbols": ["On", "Off"],
156174
},
175+
strata = [
176+
# Base: full sanitizer x shared cross-product
177+
{
178+
"sanitizers": ["OFF", "ON"],
179+
"build_type": ["Debug"],
180+
"shared": ["OFF", "ON"],
181+
},
182+
# Spot-check: RelWithDebInfo
183+
{
184+
"sanitizers": ["OFF"],
185+
"build_type": ["RelWithDebInfo"],
186+
"shared": ["OFF"],
187+
},
188+
],
157189
exclude = [
158190
{
159191
"compiler": "g++-12",
160192
"sanitizers": "ON",
161193
},
162-
]
194+
],
163195
).run(build_and_test)
164196

165197
def main():

ci/util.py

Lines changed: 42 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -20,29 +20,34 @@
2020
''', re.VERBOSE)
2121

2222
class MatrixRunner:
23-
def __init__(self, matrix, exclude):
23+
def __init__(self, matrix, exclude, strata=None):
2424
self.matrix = matrix
2525
self.exclude = exclude
26-
self.include = self.parse_includes()
27-
self.keys = [*matrix.keys()]
28-
self.values = [*matrix.values()]
26+
self.strata = strata or []
27+
self.slices = self.parse_slices()
28+
strata_keys = list(self.strata[0].keys()) if self.strata else []
29+
self.keys = list(matrix.keys()) + strata_keys
30+
self.values = (
31+
[list(v) for v in matrix.values()]
32+
+ [list(dict.fromkeys(v for s in self.strata for v in s[k])) for k in strata_keys]
33+
)
2934
self.results = {} # insertion-ordered
3035
self.failed = False
3136
self.work = self.get_work()
3237

3338
self.last_matrix_config = None
3439
self.current_matrix_config = None
3540

36-
def parse_includes(self) -> Dict[str, List[str]]:
37-
includes: Dict[str, List[str]] = dict()
41+
def parse_slices(self) -> Dict[str, List[str]]:
42+
slices: Dict[str, List[str]] = dict()
3843
for arg in sys.argv:
3944
if arg.startswith("--slice="):
4045
rest = arg[len("--slice="):]
4146
key, value = rest.split(":")
42-
if key not in includes:
43-
includes[key] = []
44-
includes[key].append(value)
45-
return includes
47+
if key not in slices:
48+
slices[key] = []
49+
slices[key].append(value)
50+
return slices
4651

4752
def run_command(self, *args: List[str], always_output=False, output_matcher=None) -> bool:
4853
self.log(f"{Fore.CYAN}{Style.BRIGHT}Running Command \"{' '.join(args)}\"{Style.RESET_ALL}")
@@ -87,36 +92,44 @@ def last_config(self):
8792
def log(self, *args, **kwargs):
8893
print(*args, **kwargs, flush=True)
8994

90-
def do_exclude(self, matrix_config, exclude):
91-
return all(map(lambda k: matrix_config[k] == exclude[k], exclude.keys()))
95+
def do_exclude(self, config, exclude):
96+
return all(config[k] == exclude[k] for k in exclude)
9297

93-
def do_include(self, matrix_config, include):
94-
if len(include) == 0:
95-
return True
96-
return all(map(lambda k: matrix_config[k] in include[k], include.keys()))
98+
def do_slice(self, config, slices):
99+
return not slices or all(config[k] in slices[k] for k in slices)
97100

98101
def assignment_to_matrix_config(self, assignment):
99-
matrix_config = {}
100-
for k, v in zip(self.matrix.keys(), assignment):
101-
matrix_config[k] = v
102-
return matrix_config
102+
return dict(zip(self.keys, assignment))
103103

104104
def get_work(self):
105105
work = []
106-
for assignment in itertools.product(*self.matrix.values()):
107-
config = self.assignment_to_matrix_config(assignment)
108-
if any(map(lambda ex: self.do_exclude(config, ex), self.exclude)):
109-
continue
110-
if not self.do_include(config, self.include):
111-
continue
112-
work.append(assignment)
106+
if self.strata:
107+
seen = set()
108+
strata_configs = []
109+
strata_keys = list(self.strata[0].keys())
110+
for stratum in self.strata:
111+
for vals in itertools.product(*[stratum[k] for k in strata_keys]):
112+
if vals not in seen:
113+
seen.add(vals)
114+
strata_configs.append(vals)
115+
else:
116+
strata_configs = [()]
117+
for base_vals in itertools.product(*self.matrix.values()):
118+
for strata_vals in strata_configs:
119+
assignment = base_vals + strata_vals
120+
config = self.assignment_to_matrix_config(assignment)
121+
if any(self.do_exclude(config, ex) for ex in self.exclude):
122+
continue
123+
if not self.do_slice(config, self.slices):
124+
continue
125+
work.append(assignment)
113126
return work
114127

115128
def run(self, fn):
116129
for i, assignment in enumerate(self.work):
117130
matrix_config = self.assignment_to_matrix_config(assignment)
118-
config_tuple = tuple(self.values[i].index(p) for i, p in enumerate(assignment))
119-
config_str = ', '.join(map(lambda v: str(v), matrix_config.values()))
131+
config_tuple = tuple(self.values[j].index(p) for j, p in enumerate(assignment))
132+
config_str = ', '.join(str(v) for v in matrix_config.values())
120133
if config_str == "":
121134
self.log(f"{Fore.BLUE}{Style.BRIGHT}{'=' * 10} [{i + 1}/{len(self.work)}] Running with blank config {'=' * 10}{Style.RESET_ALL}")
122135
else:

0 commit comments

Comments
 (0)