cpp_boilerplate 0.1.0
C++23 project template with Conan and CMake
Loading...
Searching...
No Matches
C++ Boilerplate

Build CodeQL Pages C++23 CMake Conan License

Overview

A C++23 project template with Conan 2.25+ dependency management, CMake presets, testing, sanitizers, coverage, CI, and IDE setup.

This repository uses:

  • CMake configure, build, and test presets as the public build interface
  • Conan 2 for dependency management
  • spdlog via Conan as the sample compiled dependency
  • CLI11 via Conan for command-line parsing
  • GoogleTest via Conan
  • Doxygen for generated API documentation

Instantiate this template

After creating your own repository from this template, rename the project with scripts/rename.py. The script uses only the Python standard library:

python3 scripts/rename.py my-project --github-owner your-github-user

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.

Operating model

The build is layered, and each layer owns one thing:

  • Conan owns the dependency graph, the toolchain, the CMake generator, and the ABI-relevant settings. It resolves them from conanfile.py and the [profiles/](profiles) files; conan.lock pins the exact graph for reproducible builds.
  • CMake presets (CMakePresets.json) are the public build interface: the debug, release, sanitize*, and coverage names you configure, build, and test. They are checked in and are the source of truth for how the project is built.
  • Conan writes its toolchain details into a generated ConanPresets.json that CMakePresets.json includes. That file is an implementation detail, not an interface; make bootstrap is the one-time per-clone step that materializes it.
  • The [Makefile](Makefile) is a thin convenience wrapper: each target runs conan install for the right profile, then cmake --workflow --preset <name>. On Windows you run those two commands directly (see WindowsWindows

).

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.

Layout

.
├── include/ public headers
├── src/ application sources
├── tests/ unit tests
├── conanfile.py Conan dependency definition
├── conan/settings_user.yml custom sanitizer setting
├── profiles/ default and sanitizer Conan profiles
└── CMakePresets.json project-owned public presets

Prerequisites

  • CMake 3.29+ (for workflow presets and CMAKE_LINKER_TYPE)
  • Conan 2.25+ (for the CMakeConfigDeps generator)
  • Ninja (optional, Unix only; GNU Make is used when absent)
  • ccache (optional; used automatically for first-party targets when on PATH; not with the Visual Studio generator, which ignores compiler launchers)
  • mold or LLD (optional, Linux only; used automatically to link first-party targets when on PATH, with mold preferred)
  • Doxygen (optional; required only for make docs)
  • A compiler and standard library with working C++23 support

The Conan recipe selects the CMake generator:

  • Ninja on Unix-like systems when it is available
  • Unix Makefiles on Unix-like systems when ninja is not installed
  • The Visual Studio generator matching the detected MSVC on Windows (multi-config; locates MSVC itself, so no extra tool or vcvars environment is needed)

This boilerplate supports Linux, macOS, and Windows.

Configure, build, and test

Quick start

git clone https://github.com/megabyde/cpp-boilerplate.git
cd cpp-boilerplate
make bootstrap # generate ConanPresets.json
make debug
Remarks
Run make help to list local convenience targets.

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:

conan install . -pr=profiles/default -s="build_type=Release" --build=missing --lockfile=conan.lock
cmake --workflow --preset release

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.

Sanitizers (ASAN + UBSAN)

make sanitize
make sanitize-asan
make sanitize-ubsan

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

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.

Public presets

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.

Dependency lock file

conan.lock pins the exact dependency graph for reproducible builds. To update dependencies:

  1. Edit version pins in conanfile.py.
  2. Regenerate the lock file with make lock.
  3. Run the appropriate make target to verify.
  4. Commit both conanfile.py and conan.lock.

Formatting and linting

make format
make format-check
make lint

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.

Coverage

Build, test, and generate an HTML coverage report with an enforced line floor:

make coverage-report

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).

Documentation

Generate Doxygen HTML documentation locally:

make docs

The output is written to build/docs/html/. GitHub Pages builds the same target and publishes the result from the main branch.

Build policy

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):

  • GCC/Clang/AppleClang: -fstack-protector-strong and architecture-matched control-flow protection (-fcf-protection=full on x86-64; -mbranch-protection=standard on AArch64, Linux only, because pac-ret frames break exception unwinding under macOS's compact-unwind format) in every configuration; optimized configurations add -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 (Debug skips fortification because it requires optimization). Executables link as PIE so ASLR covers the program image, and on Linux the linker adds full RELRO (-z relro -z now) and an explicitly non-executable stack.
  • MSVC: /guard:cf (Control Flow Guard) at compile and link; x64 also links with /CETCOMPAT (CET shadow stack).
  • Other compilers build unhardened rather than failing to configure.

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.

Install

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.

$ cmake --workflow --preset release
$ cmake --install build/release --prefix /path/to/prefix
$ /path/to/prefix/bin/cpp_boilerplate
[2026-06-30 20:46:16.431] [info] cpp-boilerplate 0.1.0 starting
[2026-06-30 20:46:16.431] [info] field 0: alpha
[2026-06-30 20:46:16.431] [info] field 1: beta
[2026-06-30 20:46:16.431] [info] field 2: gamma
[2026-06-30 20:46:16.431] [info] done
$ /path/to/prefix/bin/cpp_boilerplate --version
0.1.0

IDE setup

VS Code

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

CLion can use the same public presets. Run make bootstrap first, then in CLion:

  1. Open the project root
  2. Select the debug, release, sanitize*, or coverage preset as the active CMake profile
  3. Reload CMake
Note
No IDE-specific task files are required for the build. The presets are the source of truth. debug, sanitize, and coverage each use their own build tree, so switching between them does not require forcing a fresh reconfigure.