|
cpp_boilerplate 0.1.0
C++23 project template with Conan and CMake
|
A C++23 project template with Conan 2.25+ dependency management, CMake presets, testing, sanitizers, coverage, CI, and IDE setup.
This repository uses:
After creating your own repository from this template, rename the project with scripts/rename.py. The script uses only the Python standard library:
From my-project, the script derives my_project for CMake targets, the C++ namespace, and the include directory. It derives MyProjectConan for the Conan recipe class. It then updates the tracked project files, moves include/cpp_boilerplate/ to include/my_project/, and removes both the script and this section.
Run with --dry-run first to preview every change. Use --title "My Project" to override the README heading; the default is the project name in title case.
The build is layered, and each layer owns one thing:
).
When you change the Conan configuration (versions, options, or profile), rerun make bootstrap or the matching make target and keep using the same public preset names. The preset interface is stable across toolchain changes.
The Conan recipe selects the CMake generator:
This boilerplate supports Linux, macOS, and Windows.
The Makefile is a Unix convenience wrapper. On Windows, drive Conan and the CMake presets directly from any shell; the Visual Studio generator locates MSVC on its own, so no Developer PowerShell or vcvarsall setup is required:
cmake --workflow --preset <name> runs configure, build, and test in one step; it is what the make targets call on Unix too. The sanitize, sanitize-asan, sanitize-ubsan, and coverage presets are Unix-only.
This uses a dedicated sanitizer build tree (Conan names it build/debug-addressundefinedbehavior after the build type and compiler.sanitizer setting) and a Conan sanitize profile so dependencies are rebuilt with matching instrumentation, not linked from their plain (uninstrumented) Debug binaries.
The three modes use Conan profile inheritance. Each mode inherits profiles/sanitize-common, which includes profiles/default, selects Debug, and defines the common instrumentation flags and [runenv]. The mode profile then appends its own -fsanitize flags:
Each make sanitize* target installs the matching instrumented dependency graph, then runs the matching CMake workflow preset. All three modes share conan.lock.
Each mode gets its own package_id and build tree (build/debug-address, build/debug-undefinedbehavior, build/debug-addressundefinedbehavior).
ASan/UBSan runtime options (halt_on_error, print_stacktrace, and related checks) live in profiles/sanitize-common under [runenv] (inherited by every mode). Conan injects them into the generated per-mode test preset, which the public sanitize* test preset inherits, so ctest/cmake --workflow runs the instrumented tests with those options: a single source of truth, no duplication in CMakePresets.json.
That separation relies on a custom compiler.sanitizer setting defined in conan/settings_user.yml. The setting gives instrumented dependency binaries a distinct Conan package_id; the sanitizer flags themselves travel through the profile's tools.build:* conf, which does not affect package_id. Without the setting, --build=missing would silently reuse the uninstrumented Debug binaries.
make bootstrap-sanitize installs that file into your Conan home with conan config install conan/ (the repository's conan/ directory) before resolving dependencies, but only when the home has no settings_user.yml yet. An existing file (for example one your dotfiles manage) is left untouched; it just has to cover the sanitizer values this project uses. A superset (extra compilers, values, or other subsettings) is fine: scripts/check_settings_subset.py verifies this and the build stops with a merge instruction when values are missing. Installing the file changes the global Conan configuration: it adds the compiler.sanitizer subsetting. Its default is null, which is omitted from package_id, so non-sanitize builds do not change.
The default make bootstrap does not install sanitizer-instrumented dependencies. Run make bootstrap-sanitize or the matching make sanitize* target when you need them.
First-party targets are instrumented by the same Conan toolchain (the profile's tools.build:* flags reach the consumer), so the profiles are the single source of sanitizer flags.
Tests are controlled by CMake's built-in BUILD_TESTING option from include(CTest). This project leaves it at the default ON, so make debug, make release, make sanitize*, and make coverage all run the GTest suite.
The main workflow presets are debug, release, sanitize, sanitize-asan, sanitize-ubsan, and coverage. Configure, build, and test presets use the same names. docs is configure/build only because it generates Doxygen HTML instead of compiling and testing the application.
The Conan-generated conan-* presets are internal implementation details and are not the public interface for developers or CI.
conan.lock pins the exact dependency graph for reproducible builds. To update dependencies:
make format and make format-check cover C++ sources (clang-format), CMakeLists.txt (cmake-format, from the cmakelang package), Python scripts (ruff format), and tracked Markdown/JSON/YAML files (prettier; conan.lock is excluded because Conan owns its formatting). make lint runs clang-tidy against the debug compilation database, ruff check on scripts/, and markdownlint on Markdown files; every enabled check is an error. CI pins all lint and format tool versions in .github/ci.env.
Build, test, and generate an HTML coverage report with an enforced line floor:
Both compilers emit GCov-format data (--coverage), reported by a single tool: gcovr (pip install gcovr). It writes coverage-report/index.html and coverage.xml under build/coverage/.
The report fails if line coverage falls below COVERAGE_FAIL_UNDER (default 100; override with make coverage-report COVERAGE_FAIL_UNDER=80).
Generate Doxygen HTML documentation locally:
The output is written to build/docs/html/. GitHub Pages builds the same target and publishes the result from the main branch.
Warnings are errors by default (WARNINGS_AS_ERRORS, default ON) in every preset, locally and in CI. Relax it for a single build tree with cmake --preset <name> -DWARNINGS_AS_ERRORS=OFF.
First-party targets also build with hardening flags by default (ENABLE_HARDENING, default ON; disable with -DENABLE_HARDENING=OFF on any configure preset):
First-party targets build the release preset with link-time optimization (LTO) by default (ENABLE_LTO, default ON; disable with -DENABLE_LTO=OFF). It applies only to the Release configuration (CMake's INTERPROCEDURAL_OPTIMIZATION_RELEASE property), so debug, sanitize*, and coverage builds are unaffected; when the toolchain reports no LTO support, configure logs the reason and continues without it rather than failing.
Sanitizer and coverage builds omit fortification (they build as Debug, which never defines _FORTIFY_SOURCE): fortify conflicts with the ASan interceptors, and coverage builds run at -O0 where glibc fortification warns. The other hardening flags stay on. Like the warning options, hardening covers first-party code only; dependency binaries from the Conan cache are not rebuilt with these flags.
cmake --install installs the application binary to <prefix>/bin. Only the executable is installed; the static library is an internal build artifact and is deliberately not installed or exported.
VS Code with CMake Tools will discover the checked-in public presets automatically after Conan generates ConanPresets.json. Run make bootstrap first.
Then open the folder, accept the recommended extensions, and select the matching public preset. For the checked-in launch configuration, choose the target you want in the CMake Tools sidebar and start Debug: CMake Target. F5 builds the selected debug target and launches it from build/debug.
CLion can use the same public presets. Run make bootstrap first, then in CLion: