Skip to content

Commit 55efc7f

Browse files
committed
Expand CI build coverage and CMake conformance (#197)
CI changes: - Add dedicated superproject CMake build with find_package and subdirectory integration tests (sparse checkout disabled only for this entry) - Add clang-tidy step with configure-only CMake (no full build), checking both src/ and include/ files - Add multi-platform coverage infrastructure (Linux lcov, macOS gcovr+llvm-cov, Windows gcovr) with codecov-action@v5 - Add FlameGraph/time-trace variant for Clang - Generate platform booleans (linux/windows/macos) in matrix - Gate standalone CMake builds via build-cmake flag from generate-matrix.py (is_latest/is_earliest) - Skip B2 for coverage, time-trace, superproject-cmake, and clang-tidy entries - Add is_earliest flags to GCC 13, Clang 17, MSVC 14.34, Apple-Clang macos-15 - Add Set Path/LD_LIBRARY_PATH steps for shared lib integration tests CMake changes: - Use include(CTest) pattern for standalone detection - Simplify BUILD_TESTS option to use ${BUILD_TESTING} directly - Use generator expression for shared/static compile definitions - Update cmake_test to use boost-root subdirectory pattern with BOOST_INCLUDE_LIBRARIES and EXCLUDE_FROM_ALL - Add target_compile_features(main PRIVATE cxx_std_20) to cmake_test - Bump cmake_test minimum version to 3.13...3.31
1 parent 5cb0a02 commit 55efc7f

5 files changed

Lines changed: 308 additions & 80 deletions

File tree

.github/compilers.json

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
"cxx": "g++-13",
99
"cc": "gcc-13",
1010
"b2_toolset": "gcc",
11-
"arm": true
11+
"arm": true,
12+
"is_earliest": true
1213
},
1314
{
1415
"version": "14",
@@ -41,7 +42,8 @@
4142
"cxx": "clang++-17",
4243
"cc": "clang-17",
4344
"b2_toolset": "clang",
44-
"arm": true
45+
"arm": true,
46+
"is_earliest": true
4547
},
4648
{
4749
"version": "18",
@@ -73,7 +75,8 @@
7375
"cc": "clang-20",
7476
"b2_toolset": "clang",
7577
"arm": true,
76-
"is_latest": true
78+
"is_latest": true,
79+
"clang_tidy": true
7780
}
7881
],
7982
"msvc": [
@@ -83,7 +86,8 @@
8386
"latest_cxxstd": "20",
8487
"runs_on": "windows-2022",
8588
"b2_toolset": "msvc-14.3",
86-
"generator": "Visual Studio 17 2022"
89+
"generator": "Visual Studio 17 2022",
90+
"is_earliest": true
8791
},
8892
{
8993
"version": "14.42",
@@ -131,7 +135,8 @@
131135
"cxx": "clang++",
132136
"cc": "clang",
133137
"b2_toolset": "clang",
134-
"cxxflags": "-fvisibility=hidden -fvisibility-inlines-hidden"
138+
"cxxflags": "-fvisibility=hidden -fvisibility-inlines-hidden",
139+
"is_earliest": true
135140
},
136141
{
137142
"version": "*",

.github/generate-matrix.py

Lines changed: 100 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,15 @@ def load_compilers(path=None):
3131
return json.load(f)
3232

3333

34+
def platform_for_family(compiler_family):
35+
"""Return the platform name for a compiler family."""
36+
if compiler_family in ("msvc", "clang-cl", "mingw"):
37+
return "windows"
38+
elif compiler_family == "apple-clang":
39+
return "macos"
40+
return "linux"
41+
42+
3443
def make_entry(compiler_family, spec, **overrides):
3544
"""Build a matrix entry dict from a compiler spec and optional overrides."""
3645
entry = {
@@ -42,7 +51,7 @@ def make_entry(compiler_family, spec, **overrides):
4251
"b2-toolset": spec["b2_toolset"],
4352
"shared": True,
4453
"build-type": "Release",
45-
"build-cmake": True,
54+
platform_for_family(compiler_family): True,
4655
}
4756

4857
if spec.get("container"):
@@ -57,8 +66,18 @@ def make_entry(compiler_family, spec, **overrides):
5766
entry["generator-toolset"] = spec["generator_toolset"]
5867
if spec.get("is_latest"):
5968
entry["is-latest"] = True
69+
if spec.get("is_earliest"):
70+
entry["is-earliest"] = True
71+
if "shared" in spec:
72+
entry["shared"] = spec["shared"]
73+
if spec.get("vcpkg_triplet"):
74+
entry["vcpkg-triplet"] = spec["vcpkg_triplet"]
75+
76+
# CMake builds only on earliest/latest compilers, unless explicitly disabled
6077
if spec.get("build_cmake") is False:
6178
entry["build-cmake"] = False
79+
elif spec.get("is_latest") or spec.get("is_earliest"):
80+
entry["build-cmake"] = True
6281
if spec.get("cmake_cxxstd"):
6382
entry["cmake-cxxstd"] = spec["cmake_cxxstd"]
6483
if spec.get("cxxflags"):
@@ -113,6 +132,15 @@ def generate_name(compiler_family, entry):
113132
if entry.get("x86"):
114133
modifiers.append("x86")
115134

135+
if entry.get("clang-tidy"):
136+
modifiers.append("clang-tidy")
137+
138+
if entry.get("time-trace"):
139+
modifiers.append("time-trace")
140+
141+
if entry.get("superproject-cmake"):
142+
modifiers.append("superproject CMake")
143+
116144
if entry.get("shared") is False:
117145
modifiers.append("static")
118146

@@ -158,15 +186,36 @@ def generate_tsan_variant(compiler_family, spec):
158186

159187

160188
def generate_coverage_variant(compiler_family, spec):
161-
"""Generate coverage variant (GCC only)."""
162-
return make_entry(compiler_family, spec, **{
189+
"""Generate coverage variant with platform-specific flags.
190+
191+
Linux/Windows: full gcov flags with atomic profile updates.
192+
macOS: --coverage only (Apple-Clang uses llvm-cov).
193+
"""
194+
platform = platform_for_family(compiler_family)
195+
196+
if platform == "macos":
197+
cov_flags = "--coverage"
198+
else:
199+
cov_flags = ("--coverage -fprofile-arcs -ftest-coverage"
200+
" -fprofile-update=atomic")
201+
202+
overrides = {
163203
"coverage": True,
204+
"coverage-flag": platform,
164205
"shared": False,
165206
"build-type": "Debug",
166-
"cxxflags": "--coverage -fprofile-arcs -ftest-coverage",
167-
"ccflags": "--coverage -fprofile-arcs -ftest-coverage",
168-
"install": "lcov wget unzip",
169-
})
207+
"build-cmake": False,
208+
"cxxflags": cov_flags,
209+
"ccflags": cov_flags,
210+
}
211+
212+
if platform == "linux":
213+
overrides["install"] = "lcov wget unzip"
214+
215+
entry = make_entry(compiler_family, spec, **overrides)
216+
entry.pop("is-latest", None)
217+
entry.pop("is-earliest", None)
218+
return entry
170219

171220

172221
def generate_x86_variant(compiler_family, spec):
@@ -186,32 +235,73 @@ def generate_arm_entry(compiler_family, spec):
186235
return make_entry(compiler_family, arm_spec)
187236

188237

238+
def generate_time_trace_variant(compiler_family, spec):
239+
"""Generate time-trace variant for compile-time profiling (Clang only)."""
240+
return make_entry(compiler_family, spec, **{
241+
"time-trace": True,
242+
"build-cmake": True,
243+
"cxxflags": "-ftime-trace",
244+
})
245+
246+
247+
def generate_superproject_cmake_variant(compiler_family, spec):
248+
"""Generate a single superproject CMake build to verify integration."""
249+
entry = make_entry(compiler_family, spec, **{
250+
"superproject-cmake": True,
251+
"build-cmake": False,
252+
})
253+
entry.pop("is-latest", None)
254+
entry.pop("is-earliest", None)
255+
return entry
256+
257+
258+
def apply_clang_tidy(entry, spec):
259+
"""Add clang-tidy flag and install package to an entry."""
260+
entry["clang-tidy"] = True
261+
entry["build-cmake"] = False
262+
version = spec["version"]
263+
existing_install = entry.get("install", "")
264+
tidy_pkg = f"clang-tidy-{version}"
265+
entry["install"] = f"{existing_install} {tidy_pkg}".strip()
266+
entry["name"] = generate_name(entry["compiler"], entry)
267+
return entry
268+
269+
189270
def main():
190271
compilers = load_compilers()
191272
matrix = []
192273

193274
for family, specs in compilers.items():
194275
for spec in specs:
195276
# Base entry (x86_64 / default arch)
196-
matrix.append(make_entry(family, spec))
277+
base = make_entry(family, spec)
278+
if spec.get("clang_tidy"):
279+
apply_clang_tidy(base, spec)
280+
matrix.append(base)
197281

198282
# ARM entry if supported
199283
if spec.get("arm"):
200284
matrix.append(generate_arm_entry(family, spec))
201285

202286
# Variants for the latest compiler in each family
203287
if spec.get("is_latest"):
204-
matrix.append(generate_sanitizer_variant(family, spec))
288+
if family != "mingw":
289+
matrix.append(generate_sanitizer_variant(family, spec))
205290

206291
# TSan is incompatible with ASan; separate variant for Linux
207292
if family in ("gcc", "clang", "apple-clang"):
208293
matrix.append(generate_tsan_variant(family, spec))
209294

210-
if family == "gcc":
295+
# GCC always gets coverage; other families opt in via spec flag
296+
if family == "gcc" or spec.get("coverage"):
211297
matrix.append(generate_coverage_variant(family, spec))
212298

299+
if family == "gcc":
300+
matrix.append(generate_superproject_cmake_variant(family, spec))
301+
213302
if family == "clang":
214303
matrix.append(generate_x86_variant(family, spec))
304+
matrix.append(generate_time_trace_variant(family, spec))
215305

216306
json.dump(matrix, sys.stdout)
217307

0 commit comments

Comments
 (0)