rhalbersma / rhalbersma/cpp-ci

Design a shared cpp-build for header-only Boost.Test projects

Open
#1 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
No language data
Stars
1
Forks
0
Avg merge
2h 13m
Merged PRs (30d)
45

Description

cpp-ci removes the drift between four copies of the workflows. The same four repositories also carry four copies of the build system, and it drifts for the same reason: there is nowhere else for it to live. This issue is the note to plan that, not to build it yet.

Working name cpp-build rather than cpp-cmake: it names the job rather than the implementation, pairs with cpp-ci, and leaves room for the pieces that are not .cmake files — CMakePresets.json, a toolchain file, the consumer scaffolding.

Scope: header-only C++ libraries tested with Boost.Test through a vcpkg manifest — xstd, tabula, bit_set, and dctl once it has a vcpkg.json.

What is actually shareable

Three tiers, judged against xstd as the seed.

Tier 1 — pure boilerplate, identical everywhere. The install and export rules are the clearest case: install(TARGETS ... FILE_SET HEADERS), install(EXPORT ... NAMESPACE), configure_package_config_file, write_basic_package_version_file(COMPATIBILITY SameMinorVersion ARCH_INDEPENDENT), and the matching install(FILES ...). That is ~35 lines of CMakeLists.txt plus a cmake/<name>Config.cmake.in, and every line of it is a function of the project name. It is also the part most likely to be subtly wrong in the repo nobody has touched in a while.

Also here: cmake_minimum_required, include(GNUInstallDirs), set(CMAKE_CXX_EXTENSIONS OFF), the PROJECT_IS_TOP_LEVEL + BUILD_TESTING guard around add_subdirectory(test), and CMakePresets.json.

Tier 2 — real logic worth writing once. Two functions carry actual thought:

  • Header self-sufficiency (test/CMakeLists.txt) — generates one TU per public header, compiles it alone, and registers it as a test. Includes the detail that the generated main uses a trailing return type because clang-tidy runs over the generated sources and modernize-use-trailing-return-type would flag int main(). That comment is exactly the kind of hard-won knowledge that does not survive being copy-pasted three times.
  • One test target per source — the GLOB_RECURSE over test/src/*.cpp with the dotted target id derived from the path.

Plus a <project>_test_options interface target for the shared warning flags.

Tier 3 — must stay per-repo. The version, the explicit header list in target_sources(FILE_SET HEADERS FILES ...), the C++ standard in target_compile_features, and anything naming the library's own types.

The constraint that shapes the whole design

The installed package must not depend on cpp-build. xstdConfig.cmake is installed and read by downstream consumers of xstd. If a shared macro leaks into it, every consumer of every one of these libraries needs cpp-build on disk to call find_package. So cpp-build is strictly build-time: it may generate the config file, but nothing it provides may appear in the generated output.

The consumption.yml tests already in cpp-ci are the right guard for this — find_package, add_subdirectory, and FetchContent all exercise the consumer side, and a leak would show up there.

First decision: how does a repo get cpp-build?

Option Cost
git submodule at cmake/cpp-build Pinned by SHA, matching the pinning convention already in use, and needs no network at configure time. Costs contributors a --recursive clone.
FetchContent at configure time No submodule friction, but puts a network fetch in the configure step — which breaks offline builds and adds a failure mode to the very add_subdirectory/FetchContent consumption tests above.
Installed CMake package Cleanest semantics, worst ergonomics: a bootstrap install before any build.
Copy with a sync bot Keeps repos self-contained, but is the current situation with extra machinery.

Leaning submodule, for the same reason cpp-ci pins by SHA: the build should say exactly which version it used, and it should not need the network to find out.

Relationship to cpp-ci

Separate repo, separate lifecycle — same argument as the one for not putting the workflows in xstd. But note the coupling: cpp-ci's consumption.yml names test/consumer/{find_package,add_subdirectory,fetch_content} by convention, and those consumer projects are the natural thing for cpp-build to scaffold.

Sequencing

After the cpp-ci migration is complete. Two centralisations at once would mean a failure could come from either, and this one has the harder blast radius — a broken CI workflow reddens a badge, a broken install rule reaches people who depend on the library.

dctl is the natural first consumer once it has a vcpkg.json, since it has neither CI nor a modern CMake setup to preserve.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by comparing xstd's CMakeLists.txt, test/CMakeLists.txt, cmake/Config.cmake.in, and CMakePresets.json, then review cpp-ci's consumption.yml and the four consumer projects named there. Produce an agreed design for cpp-build's scope, acquisition method, and sequencing; done means the plan preserves per-repository settings and keeps installed packages independent of cpp-build.

Written by the indexing model from the issue text.

Assessment

Tech stack
cmake, cpp
Domain
build-system
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.