Prerequisite: complete the README setup for your platform first.
Outcome: first-party code and dependencies built with matching sanitizer instrumentation, followed by the instrumented test suite.
Important
The first sanitizer build may add compiler.sanitizer from
conan/settings_user.yml to the global Conan configuration. If the
Conan home already has settings_user.yml, the build leaves it untouched and checks that it
contains the required values. If the check fails, merge the reported values into the existing
file, then rerun the same command.
Choose one mode:
- ASan + UBSan:
make sanitizeusesprofiles/sanitizeandbuild/debug-addressundefinedbehavior - ASan:
make sanitize-asanusesprofiles/sanitize-asanandbuild/debug-address - UBSan:
make sanitize-ubsanusesprofiles/sanitize-ubsanandbuild/debug-undefinedbehavior
The sanitizer run is complete when the selected target exits successfully after running the tests.
If a sanitizer detects an error, it prints a stack trace, stops at the first finding, and returns a
failure through CTest to the selected make target. Fix the reported source error, then rerun the
same target.
make sanitize is the combined mode used by CI. To install all three instrumented dependency graphs
without running their workflows, use make bootstrap-sanitize. The default make bootstrap does
not install them.
All three profiles inherit profiles/sanitize-common, which includes
profiles/default, selects Debug, and defines the shared instrumentation flags and [runenv].
Each mode appends its own -fsanitize flags and shares conan.lock.
The custom compiler.sanitizer setting gives each instrumented dependency graph a distinct Conan
package_id. Without it, --build=missing could reuse plain Debug dependencies. The setting
defaults to null, which is omitted from package_id, so non-sanitizer builds do not change.
First-party targets receive the same profile flags through the Conan toolchain.
ASan/UBSan runtime options such as halt_on_error and print_stacktrace live under [runenv] in
profiles/sanitize-common. Conan injects them into the generated test presets inherited by the
public sanitize* presets, keeping the runtime configuration out of CMakePresets.json.
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.
Outcome: updated dependency pins and a matching conan.lock for reproducible builds.
Update dependencies in this order:
- Edit version pins in
conanfile.py. - Regenerate the lock file with
make lock. - Run the appropriate
maketarget to verify. - Commit both
conanfile.pyandconan.lock.
The update is complete when the selected build and test workflow passes and both files contain the intended dependency change.
Use make format to rewrite supported files in place:
make formatThen verify formatting and lint findings without modifying files:
make format-check
make lintThe check is complete when both verification targets exit successfully. If make format-check
fails, rerun make format and inspect its changes before checking again. Reported lint findings
require a source fix.
make format and make format-check cover C++ sources (clang-format),
CMakeLists.txt (cmake-format, from the
cmakelang package), scripts/ and conanfile.py
(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, cmake-lint on
CMakeLists.txt, ruff check on scripts/ and conanfile.py, and
markdownlint on Markdown files. Any reported
finding fails the target. CI pins all lint and format tool versions in
.github/ci.env.
Prerequisite: Python 3.10+ with gcovr installed (e.g., pip install gcovr).
Build, test, and generate the coverage report:
make coverage-reportThe run is complete when the tests pass, gcovr reports a line percentage at or above the configured floor, and these files exist:
build/coverage/coverage-report/index.htmlbuild/coverage/coverage.xml
Both supported compilers emit GCov-format data (--coverage), which gcovr reports through one
interface.
The report fails if line coverage falls below COVERAGE_FAIL_UNDER (default 100; override with
make coverage-report COVERAGE_FAIL_UNDER=80).
Prerequisite: Doxygen installed and available on PATH.
Generate the API documentation:
make docsThe build is complete when build/docs/html/index.html exists. GitHub Pages runs 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):
- GCC/Clang/AppleClang:
-fstack-protector-strongand architecture-matched control-flow protection (-fcf-protection=fullon x86-64;-mbranch-protection=standardon 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.
Prerequisite: run make bootstrap so Conan generates ConanPresets.json.
- Open the project folder in VS Code.
- Accept the recommended extensions.
- Select the matching public preset in CMake Tools.
- Select a target in the CMake Tools sidebar.
- Start
Debug: CMake Targetor press F5.
Setup is complete when F5 builds the selected debug target and launches it from build/debug.
Prerequisite: run make bootstrap so Conan generates ConanPresets.json.
In CLion:
- Open the project root
- Select the
debug,release,sanitize*, orcoveragepreset as the active CMake profile - Reload CMake
Setup is complete when CLion finishes reloading the selected public preset without a configure error.
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.