Table of Contents

  1. Project Architecture
  2. Version Management
  3. Dependency Strategy
  4. Library Design
  5. Target Properties Best Practices
  6. Testing Infrastructure
  7. Documentation Pipeline
  8. Static Analysis Integration
  9. Sanitizer Configurations
  10. Installation and Packaging
  11. CI/CD Integration
  12. Preset Workflows
  13. Putting It All Together
  14. Series Complete
Back to CMake Mastery Series

Part 26: Alternative Generators

June 4, 2026 Wasil Zafar 30 min read

The capstone article — combining everything from 32 prior parts into a single, production-grade CMake project with testing, documentation, static analysis, sanitizers, packaging, CI/CD, and preset workflows.

Project Architecture

A professional CMake project separates concerns into distinct directories, each with a focused responsibility. This structure scales from small libraries to large multi-component applications without requiring reorganization as the project grows. The top-level CMakeLists.txt acts as an orchestrator — it defines global settings, finds dependencies, and delegates to subdirectories via add_subdirectory().

Professional Project Directory Structure
        flowchart TD
            ROOT["myproject/"] --> CMAKE["cmake/"]
            ROOT --> SRC["src/"]
            ROOT --> INCLUDE["include/myproject/"]
            ROOT --> TESTS["tests/"]
            ROOT --> DOCS["docs/"]
            ROOT --> EXAMPLES["examples/"]
            ROOT --> PACKAGING["packaging/"]
            ROOT --> PRESETS["CMakePresets.json"]
            ROOT --> TOPLEVEL["CMakeLists.txt"]

            CMAKE --> CM1["FindCustomLib.cmake"]
            CMAKE --> CM2["CompilerWarnings.cmake"]
            CMAKE --> CM3["Sanitizers.cmake"]
            CMAKE --> CM4["Coverage.cmake"]

            SRC --> SRC1["CMakeLists.txt"]
            SRC --> SRC2["core/"]
            SRC --> SRC3["utils/"]
            SRC --> SRC4["app/"]

            INCLUDE --> INC1["core.hpp"]
            INCLUDE --> INC2["utils.hpp"]
            INCLUDE --> INC3["export.hpp"]
            INCLUDE --> INC4["version.hpp.in"]

            TESTS --> T1["CMakeLists.txt"]
            TESTS --> T2["unit/"]
            TESTS --> T3["integration/"]

            DOCS --> D1["CMakeLists.txt"]
            DOCS --> D2["Doxyfile.in"]
            DOCS --> D3["conf.py.in"]

            style ROOT fill:#132440,color:#fff
            style TOPLEVEL fill:#3B9797,color:#fff
            style PRESETS fill:#3B9797,color:#fff
    
Separation of Concerns Principle: Public headers live in include/myproject/ (installed for consumers), private sources live in src/ (never installed), CMake helper modules live in cmake/ (reusable across projects), and tests are fully isolated in tests/ with their own CMakeLists.txt.

Top-Level CMakeLists.txt Design

The root CMakeLists.txt establishes project-wide settings, determines build options, and delegates to subdirectories. It should never contain target definitions directly — those belong in the subdirectory CMakeLists.txt files.

# CMakeLists.txt — Top-level orchestrator
cmake_minimum_required(VERSION 3.25)

project(myproject
    VERSION 2.1.0
    DESCRIPTION "A professional-grade C++ library"
    HOMEPAGE_URL "https://github.com/myorg/myproject"
    LANGUAGES CXX
)

# Prevent in-source builds
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR)
    message(FATAL_ERROR "In-source builds are not allowed. Use: cmake -B build")
endif()

# Standard project-wide settings
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

# Add our cmake/ directory to the module path
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")

# Project options
option(MYPROJECT_BUILD_TESTS "Build unit and integration tests" ON)
option(MYPROJECT_BUILD_DOCS "Build documentation" OFF)
option(MYPROJECT_BUILD_EXAMPLES "Build example programs" ON)
option(MYPROJECT_ENABLE_COVERAGE "Enable code coverage" OFF)
option(MYPROJECT_ENABLE_SANITIZERS "Enable sanitizers (ASan+UBSan)" OFF)
option(MYPROJECT_INSTALL "Generate install target" ON)

# Include helper modules
include(CompilerWarnings)
include(Sanitizers)

# Dependencies
find_package(fmt REQUIRED)
find_package(spdlog REQUIRED)
find_package(nlohmann_json 3.11 REQUIRED)

include(FetchContent)
FetchContent_Declare(
    expected
    GIT_REPOSITORY https://github.com/TartanLlama/expected.git
    GIT_TAG v1.1.0
)
FetchContent_MakeAvailable(expected)

# Source tree
add_subdirectory(src)

# Tests
if(MYPROJECT_BUILD_TESTS)
    enable_testing()
    add_subdirectory(tests)
endif()

# Documentation
if(MYPROJECT_BUILD_DOCS)
    add_subdirectory(docs)
endif()

# Examples
if(MYPROJECT_BUILD_EXAMPLES)
    add_subdirectory(examples)
endif()

# Installation
if(MYPROJECT_INSTALL)
    include(GNUInstallDirs)
    include(CMakePackageConfigHelpers)
    include(cmake/Install.cmake)
endif()
Best Practice: Prefix all project options with your project name (MYPROJECT_) to avoid collisions when your project is consumed as a subdirectory via FetchContent or add_subdirectory(). This namespacing ensures options don't conflict with parent or sibling projects.

Version Management

Professional projects need a single source of truth for their version number. CMake's project(VERSION) provides the canonical version, but production builds should also encode Git metadata for traceability. The standard approach uses configure_file() to generate a header that embeds version information at compile time.

Git-Derived Version

# cmake/GitVersion.cmake — Extract version info from Git
find_package(Git QUIET)

set(MYPROJECT_GIT_HASH "unknown")
set(MYPROJECT_GIT_DESCRIBE "unknown")
set(MYPROJECT_GIT_DIRTY FALSE)

if(GIT_FOUND AND EXISTS "${CMAKE_SOURCE_DIR}/.git")
    execute_process(
        COMMAND ${GIT_EXECUTABLE} rev-parse --short=8 HEAD
        WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
        OUTPUT_VARIABLE MYPROJECT_GIT_HASH
        OUTPUT_STRIP_TRAILING_WHITESPACE
        ERROR_QUIET
    )

    execute_process(
        COMMAND ${GIT_EXECUTABLE} describe --tags --always --dirty
        WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
        OUTPUT_VARIABLE MYPROJECT_GIT_DESCRIBE
        OUTPUT_STRIP_TRAILING_WHITESPACE
        ERROR_QUIET
    )

    execute_process(
        COMMAND ${GIT_EXECUTABLE} diff --quiet HEAD
        WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
        RESULT_VARIABLE GIT_DIFF_RESULT
    )
    if(NOT GIT_DIFF_RESULT EQUAL 0)
        set(MYPROJECT_GIT_DIRTY TRUE)
    endif()
endif()

message(STATUS "Git version: ${MYPROJECT_GIT_DESCRIBE} (${MYPROJECT_GIT_HASH})")
message(STATUS "Git dirty: ${MYPROJECT_GIT_DIRTY}")
// include/myproject/version.hpp.in — Template processed by configure_file()
#pragma once

// Semantic version from project(VERSION)
#define MYPROJECT_VERSION_MAJOR @myproject_VERSION_MAJOR@
#define MYPROJECT_VERSION_MINOR @myproject_VERSION_MINOR@
#define MYPROJECT_VERSION_PATCH @myproject_VERSION_PATCH@
#define MYPROJECT_VERSION_STRING "@myproject_VERSION@"

// Git metadata for build traceability
#define MYPROJECT_GIT_HASH "@MYPROJECT_GIT_HASH@"
#define MYPROJECT_GIT_DESCRIBE "@MYPROJECT_GIT_DESCRIBE@"
#define MYPROJECT_GIT_DIRTY @MYPROJECT_GIT_DIRTY@

// Compile-time build info
#define MYPROJECT_BUILD_TYPE "@CMAKE_BUILD_TYPE@"
#define MYPROJECT_COMPILER_ID "@CMAKE_CXX_COMPILER_ID@"
#define MYPROJECT_COMPILER_VERSION "@CMAKE_CXX_COMPILER_VERSION@"

namespace myproject {
    struct VersionInfo {
        static constexpr int major = MYPROJECT_VERSION_MAJOR;
        static constexpr int minor = MYPROJECT_VERSION_MINOR;
        static constexpr int patch = MYPROJECT_VERSION_PATCH;
        static constexpr const char* string = MYPROJECT_VERSION_STRING;
        static constexpr const char* git_hash = MYPROJECT_GIT_HASH;
        static constexpr const char* git_describe = MYPROJECT_GIT_DESCRIBE;
        static constexpr bool git_dirty = MYPROJECT_GIT_DIRTY;
    };
} // namespace myproject
# src/CMakeLists.txt — Generate version header
include(GitVersion)

configure_file(
    "${CMAKE_SOURCE_DIR}/include/myproject/version.hpp.in"
    "${CMAKE_BINARY_DIR}/generated/myproject/version.hpp"
    @ONLY
)

# Make the generated header available
target_include_directories(myproject_core PUBLIC
    $<BUILD_INTERFACE:${CMAKE_BINARY_DIR}/generated>
)

Dependency Strategy

Professional projects rarely exist in isolation — they depend on external libraries for functionality that would be unreasonable to reimplement. The key decision is how to acquire each dependency. The strategy depends on dependency size, update frequency, and whether your consumers will also need it.

Dependency Resolution Strategy Flowchart
        flowchart TD
            A[Need External Library] --> B{Large/Complex?}
            B -->|Yes| C{System-installable?}
            B -->|No| D{Header-only?}

            C -->|Yes| E[find_package]
            C -->|No| F{Team uses package manager?}

            D -->|Yes| G[FetchContent]
            D -->|No| H{Build takes <30s?}

            F -->|vcpkg| I[vcpkg.json manifest]
            F -->|Conan| J[conanfile.txt]
            F -->|Neither| K[FetchContent + cache]

            H -->|Yes| G
            H -->|No| L[find_package + instructions]

            E --> M[CMake Config Package]
            G --> M
            I --> M
            J --> M
            K --> M
            L --> M

            style A fill:#132440,color:#fff
            style M fill:#3B9797,color:#fff
            style E fill:#16476A,color:#fff
            style G fill:#16476A,color:#fff
    

vcpkg and Conan Manifest Mode

{
    "$schema": "https://raw.githubusercontent.com/microsoft/vcpkg-tool/main/docs/vcpkg.schema.json",
    "name": "myproject",
    "version-semver": "2.1.0",
    "dependencies": [
        "fmt",
        "spdlog",
        "nlohmann-json",
        {
            "name": "catch2",
            "version>=": "3.5.0"
        },
        {
            "name": "boost-asio",
            "platform": "!emscripten"
        }
    ],
    "builtin-baseline": "c9fa965c2a1b1f3f028469f0f103d23c1e40e261",
    "overrides": [
        { "name": "fmt", "version": "11.0.2" }
    ]
}
# Using vcpkg in toolchain mode — CMakePresets.json integration
# No changes needed in CMakeLists.txt! vcpkg integrates transparently.
# The preset sets CMAKE_TOOLCHAIN_FILE to vcpkg's toolchain.

# For Conan 2.x, dependencies appear as normal find_package() calls
# after running: conan install . --output-folder=build --build=missing
find_package(fmt REQUIRED)
find_package(spdlog REQUIRED)
# Same CMakeLists.txt works with vcpkg, Conan, or system packages
Anti-Pattern — Hardcoded Paths: Never use set(FMT_DIR "/usr/local/lib/cmake/fmt") or similar absolute paths. This breaks portability and CI. Always rely on find_package() with proper CMAKE_PREFIX_PATH configuration, or use a package manager manifest that handles paths automatically.

Library Design

Designing a library with CMake means thinking carefully about what you expose to consumers versus what stays internal. The PUBLIC, PRIVATE, and INTERFACE keywords on target_* commands control this boundary precisely. Combined with GenerateExportHeader, your library can present a clean ABI on all platforms.

Header-Only + Compiled Split

# src/CMakeLists.txt — Library with header-only and compiled components

# Compiled library with exported symbols
add_library(myproject_core
    core/engine.cpp
    core/config.cpp
    utils/logging.cpp
    utils/string_utils.cpp
)
add_library(myproject::core ALIAS myproject_core)

# Generate platform-specific export macros
include(GenerateExportHeader)
generate_export_header(myproject_core
    BASE_NAME MYPROJECT
    EXPORT_FILE_NAME "${CMAKE_BINARY_DIR}/generated/myproject/export.hpp"
)

target_include_directories(myproject_core
    PUBLIC
        $<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/include>
        $<BUILD_INTERFACE:${CMAKE_BINARY_DIR}/generated>
        $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
    PRIVATE
        ${CMAKE_CURRENT_SOURCE_DIR}
)

target_link_libraries(myproject_core
    PUBLIC
        nlohmann_json::nlohmann_json   # Appears in our public headers
    PRIVATE
        fmt::fmt                        # Used only in .cpp files
        spdlog::spdlog                  # Used only in .cpp files
        tl::expected                    # Used only in .cpp files
)

target_compile_features(myproject_core PUBLIC cxx_std_20)

# Header-only utilities — INTERFACE library
add_library(myproject_headers INTERFACE)
add_library(myproject::headers ALIAS myproject_headers)

target_include_directories(myproject_headers INTERFACE
    $<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/include>
    $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
)

target_compile_features(myproject_headers INTERFACE cxx_std_20)

# Application executable
add_executable(myproject_app app/main.cpp)
add_executable(myproject::app ALIAS myproject_app)

target_link_libraries(myproject_app PRIVATE myproject::core)
// include/myproject/core.hpp — Public API with export macros
#pragma once

#include "myproject/export.hpp"
#include <nlohmann/json.hpp>
#include <string>
#include <string_view>

namespace myproject {

/// Configuration loaded from JSON files
class MYPROJECT_EXPORT Config {
public:
    explicit Config(std::string_view path);
    ~Config();

    [[nodiscard]] std::string get(std::string_view key) const;
    [[nodiscard]] int get_int(std::string_view key, int default_val = 0) const;
    [[nodiscard]] bool has(std::string_view key) const;

    void reload();

private:
    struct Impl;
    std::unique_ptr<Impl> impl_;
};

/// Core processing engine
class MYPROJECT_EXPORT Engine {
public:
    explicit Engine(const Config& config);
    ~Engine();

    void start();
    void stop();
    [[nodiscard]] bool is_running() const noexcept;

private:
    struct Impl;
    std::unique_ptr<Impl> impl_;
};

} // namespace myproject
Why PIMPL + Export Macros: The Pointer-to-Implementation idiom hides private members from the public header, reducing recompilation cascades. Combined with GenerateExportHeader, your library compiles with proper __declspec(dllexport) on Windows and visibility attributes on Unix — no platform-specific #ifdef needed.

Target Properties Best Practices

Compile Features and ALIAS Targets

Modern CMake expresses requirements through target properties rather than global variables. Every target declares what it needs (compile features, definitions, include paths) and what it provides to consumers. ALIAS targets with namespace prefixes ensure uniform syntax whether a library is consumed via find_package() or add_subdirectory().

# cmake/CompilerWarnings.cmake — Reusable warning configuration
add_library(myproject_warnings INTERFACE)
add_library(myproject::warnings ALIAS myproject_warnings)

target_compile_options(myproject_warnings INTERFACE
    $<$<CXX_COMPILER_ID:MSVC>:
        /W4 /WX /permissive- /utf-8
        /wd4251  # DLL interface warning (expected with PIMPL)
    >
    $<$<NOT:$<CXX_COMPILER_ID:MSVC>>:
        -Wall -Wextra -Wpedantic -Werror
        -Wconversion -Wsign-conversion
        -Wnon-virtual-dtor -Wold-style-cast
        -Wcast-align -Woverloaded-virtual
        -Wshadow -Wformat=2
    >
)

# Apply warnings to our targets (not to dependencies)
target_link_libraries(myproject_core PRIVATE myproject::warnings)
target_link_libraries(myproject_app PRIVATE myproject::warnings)
Namespace Convention: Always create ALIAS targets with your project namespace: add_library(myproject::core ALIAS myproject_core). This means consumers use the same target_link_libraries(... myproject::core) syntax regardless of how they obtain your library. CMake will error if a namespaced target doesn't exist, catching typos early.

Testing Infrastructure

A professional project has tests at multiple levels — unit tests for individual components, integration tests for subsystem interactions, and possibly end-to-end tests for the complete application. CTest provides the orchestration layer, while frameworks like Catch2 or GoogleTest handle assertion logic and test discovery.

CTest Integration with Coverage

# tests/CMakeLists.txt — Test infrastructure
include(CTest)

# Fetch Catch2 for unit testing
FetchContent_Declare(
    Catch2
    GIT_REPOSITORY https://github.com/catchorg/Catch2.git
    GIT_TAG v3.7.1
)
FetchContent_MakeAvailable(Catch2)
list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras)
include(Catch)

# Unit test executable
add_executable(unit_tests
    unit/test_config.cpp
    unit/test_engine.cpp
    unit/test_string_utils.cpp
)

target_link_libraries(unit_tests PRIVATE
    myproject::core
    Catch2::Catch2WithMain
)

# Auto-discover tests from Catch2 TEST_CASE macros
catch_discover_tests(unit_tests
    WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
    PROPERTIES
        LABELS "unit"
        TIMEOUT 30
)

# Integration tests
add_executable(integration_tests
    integration/test_engine_lifecycle.cpp
    integration/test_config_reload.cpp
)

target_link_libraries(integration_tests PRIVATE
    myproject::core
    Catch2::Catch2WithMain
)

catch_discover_tests(integration_tests
    PROPERTIES
        LABELS "integration"
        TIMEOUT 120
)
# cmake/Coverage.cmake — Code coverage with gcov/lcov
option(MYPROJECT_ENABLE_COVERAGE "Enable code coverage" OFF)

if(MYPROJECT_ENABLE_COVERAGE)
    if(NOT CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
        message(WARNING "Coverage requires GCC or Clang")
        return()
    endif()

    message(STATUS "Code coverage enabled")

    # Add coverage flags to the library (not tests)
    target_compile_options(myproject_core PRIVATE --coverage -fprofile-arcs -ftest-coverage)
    target_link_options(myproject_core PRIVATE --coverage)

    # Custom target to generate coverage report
    find_program(LCOV lcov REQUIRED)
    find_program(GENHTML genhtml REQUIRED)

    add_custom_target(coverage
        COMMAND ${LCOV} --capture --directory . --output-file coverage.info
                --rc branch_coverage=1
        COMMAND ${LCOV} --remove coverage.info
                '/usr/*' '*/tests/*' '*/build/*' '*/_deps/*'
                --output-file coverage_filtered.info
                --rc branch_coverage=1
        COMMAND ${GENHTML} coverage_filtered.info
                --output-directory coverage_report
                --branch-coverage --title "myproject Coverage"
        WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
        COMMENT "Generating code coverage report..."
        VERBATIM
    )
endif()
# Run tests with coverage
cmake -B build -DMYPROJECT_ENABLE_COVERAGE=ON -DCMAKE_BUILD_TYPE=Debug
cmake --build build
cd build && ctest --output-on-failure

# Generate HTML coverage report
cmake --build build --target coverage
# Open build/coverage_report/index.html in browser

Documentation Pipeline

Professional C++ projects combine Doxygen (API extraction from source comments) with Sphinx (narrative documentation in reStructuredText) via the Breathe bridge. This produces beautiful, searchable documentation that includes both hand-written guides and auto-generated API reference — all built as part of the CMake workflow.

Build-Time Documentation Generation

# docs/CMakeLists.txt — Documentation build
find_package(Doxygen REQUIRED)
find_program(SPHINX_EXECUTABLE sphinx-build REQUIRED)

# Configure Doxyfile with project version and paths
set(DOXYGEN_INPUT_DIR "${CMAKE_SOURCE_DIR}/include/myproject")
set(DOXYGEN_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/doxygen")

configure_file(
    "${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in"
    "${CMAKE_CURRENT_BINARY_DIR}/Doxyfile"
    @ONLY
)

# Doxygen target — generates XML for Breathe
add_custom_target(doxygen
    COMMAND ${DOXYGEN_EXECUTABLE} "${CMAKE_CURRENT_BINARY_DIR}/Doxyfile"
    WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
    COMMENT "Generating Doxygen XML..."
    VERBATIM
)

# Configure Sphinx conf.py with version and Breathe path
configure_file(
    "${CMAKE_CURRENT_SOURCE_DIR}/conf.py.in"
    "${CMAKE_CURRENT_BINARY_DIR}/conf.py"
    @ONLY
)

# Sphinx target — generates HTML from RST + Doxygen XML
add_custom_target(docs
    COMMAND ${SPHINX_EXECUTABLE}
        -b html
        -c "${CMAKE_CURRENT_BINARY_DIR}"
        "${CMAKE_CURRENT_SOURCE_DIR}/source"
        "${CMAKE_CURRENT_BINARY_DIR}/html"
    DEPENDS doxygen
    COMMENT "Building Sphinx documentation..."
    VERBATIM
)

# Optional: install documentation
install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/html/"
    DESTINATION ${CMAKE_INSTALL_DOCDIR}
    COMPONENT documentation
    OPTIONAL
)
# Build documentation
cmake -B build -DMYPROJECT_BUILD_DOCS=ON
cmake --build build --target docs

# Documentation available at build/docs/html/index.html

Static Analysis Integration

CMake integrates static analysis tools directly into the build process via the CMAKE_<LANG>_CLANG_TIDY, CMAKE_<LANG>_CPPCHECK, and CMAKE_<LANG>_INCLUDE_WHAT_YOU_USE variables. When set, CMake runs the analysis tool on every source file during compilation, catching issues as part of the normal development workflow rather than requiring a separate analysis pass.

clang-tidy, cppcheck, and Include-What-You-Use

# cmake/StaticAnalysis.cmake — Optional static analysis integration
option(MYPROJECT_ENABLE_CLANG_TIDY "Run clang-tidy during build" OFF)
option(MYPROJECT_ENABLE_CPPCHECK "Run cppcheck during build" OFF)
option(MYPROJECT_ENABLE_IWYU "Run include-what-you-use during build" OFF)

if(MYPROJECT_ENABLE_CLANG_TIDY)
    find_program(CLANG_TIDY_EXE NAMES clang-tidy clang-tidy-18 REQUIRED)
    set(CMAKE_CXX_CLANG_TIDY
        ${CLANG_TIDY_EXE}
        --config-file=${CMAKE_SOURCE_DIR}/.clang-tidy
        --header-filter=${CMAKE_SOURCE_DIR}/include/.*
        --warnings-as-errors=*
    )
    message(STATUS "clang-tidy enabled: ${CLANG_TIDY_EXE}")
endif()

if(MYPROJECT_ENABLE_CPPCHECK)
    find_program(CPPCHECK_EXE cppcheck REQUIRED)
    set(CMAKE_CXX_CPPCHECK
        ${CPPCHECK_EXE}
        --enable=warning,performance,portability
        --suppress=missingIncludeSystem
        --inline-suppr
        --error-exitcode=1
        --std=c++20
    )
    message(STATUS "cppcheck enabled: ${CPPCHECK_EXE}")
endif()

if(MYPROJECT_ENABLE_IWYU)
    find_program(IWYU_EXE include-what-you-use REQUIRED)
    set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE
        ${IWYU_EXE}
        -Xiwyu --mapping_file=${CMAKE_SOURCE_DIR}/.iwyu.imp
        -Xiwyu --no_fwd_decls
    )
    message(STATUS "IWYU enabled: ${IWYU_EXE}")
endif()
# .clang-tidy — Project-wide configuration
---
Checks: >
    -*,
    bugprone-*,
    cert-*,
    cppcoreguidelines-*,
    misc-*,
    modernize-*,
    performance-*,
    readability-*,
    -modernize-use-trailing-return-type,
    -readability-identifier-length,
    -cppcoreguidelines-avoid-magic-numbers,
    -readability-magic-numbers

WarningsAsErrors: '*'
HeaderFilterRegex: 'include/myproject/.*'

CheckOptions:
  - key: readability-identifier-naming.ClassCase
    value: CamelCase
  - key: readability-identifier-naming.FunctionCase
    value: lower_case
  - key: readability-identifier-naming.VariableCase
    value: lower_case
  - key: readability-identifier-naming.NamespaceCase
    value: lower_case
  - key: readability-identifier-naming.ConstantCase
    value: UPPER_CASE
  - key: modernize-use-auto.MinTypeNameLength
    value: 5
...
Efficiency Tip: Don't enable all analysis tools simultaneously in development — it dramatically slows compilation. Use CMAKE_CXX_CLANG_TIDY during local development for instant feedback, and run cppcheck + IWYU in CI where build time is less critical. The preset system (covered later) makes toggling these trivial.

Sanitizer Configurations

Runtime sanitizers detect memory errors, data races, and undefined behavior that static analysis cannot catch. Professional projects configure sanitizers as CMake presets so developers can switch to a sanitized build with a single command. Each sanitizer requires a dedicated build because they're generally incompatible with each other.

ASan, TSan, and UBSan

# cmake/Sanitizers.cmake — Sanitizer configuration
option(MYPROJECT_SANITIZER "Enable sanitizer (asan, tsan, ubsan, msan)" "")

if(MYPROJECT_SANITIZER)
    if(MYPROJECT_SANITIZER STREQUAL "asan")
        set(SANITIZER_FLAGS "-fsanitize=address -fno-omit-frame-pointer")
        set(SANITIZER_LINK_FLAGS "-fsanitize=address")
    elseif(MYPROJECT_SANITIZER STREQUAL "tsan")
        set(SANITIZER_FLAGS "-fsanitize=thread")
        set(SANITIZER_LINK_FLAGS "-fsanitize=thread")
    elseif(MYPROJECT_SANITIZER STREQUAL "ubsan")
        set(SANITIZER_FLAGS "-fsanitize=undefined -fno-omit-frame-pointer")
        set(SANITIZER_LINK_FLAGS "-fsanitize=undefined")
    elseif(MYPROJECT_SANITIZER STREQUAL "msan")
        if(NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang")
            message(FATAL_ERROR "MSan requires Clang")
        endif()
        set(SANITIZER_FLAGS "-fsanitize=memory -fno-omit-frame-pointer -fsanitize-memory-track-origins=2")
        set(SANITIZER_LINK_FLAGS "-fsanitize=memory")
    else()
        message(FATAL_ERROR "Unknown sanitizer: ${MYPROJECT_SANITIZER}")
    endif()

    # Apply to all targets in this directory and below
    add_compile_options(${SANITIZER_FLAGS})
    add_link_options(${SANITIZER_LINK_FLAGS})

    message(STATUS "Sanitizer enabled: ${MYPROJECT_SANITIZER}")
endif()
# Build with AddressSanitizer
cmake --preset asan
cmake --build --preset asan
ctest --preset asan

# Build with ThreadSanitizer (for concurrent code)
cmake --preset tsan
cmake --build --preset tsan
ctest --preset tsan

# Build with UndefinedBehaviorSanitizer
cmake --preset ubsan
cmake --build --preset ubsan
ctest --preset ubsan
Anti-Pattern — Mixing Sanitizers: Never combine ASan with TSan in the same build — they're incompatible and produce false positives or crashes. ASan + UBSan can be combined, but TSan and MSan must each run in isolation. Use separate presets for each sanitizer type.

Installation and Packaging

Making your library consumable by other CMake projects requires proper installation rules. The install(EXPORT) command generates CMake config files that allow find_package(myproject) to work seamlessly. CPack then wraps the installed tree into distributable packages (DEB, RPM, NSIS, ZIP).

install(EXPORT) and CPack

# cmake/Install.cmake — Installation and export rules
include(GNUInstallDirs)
include(CMakePackageConfigHelpers)

# Install the library
install(TARGETS myproject_core myproject_headers myproject_warnings
    EXPORT myprojectTargets
    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
    INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)

# Install public headers
install(DIRECTORY "${CMAKE_SOURCE_DIR}/include/myproject"
    DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
    FILES_MATCHING PATTERN "*.hpp" PATTERN "*.h"
)

# Install generated headers (export.hpp, version.hpp)
install(DIRECTORY "${CMAKE_BINARY_DIR}/generated/myproject"
    DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
    FILES_MATCHING PATTERN "*.hpp"
)

# Install the executable
install(TARGETS myproject_app
    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)

# Generate and install CMake package config
install(EXPORT myprojectTargets
    FILE myprojectTargets.cmake
    NAMESPACE myproject::
    DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/myproject
)

# Generate version compatibility file
write_basic_package_version_file(
    "${CMAKE_CURRENT_BINARY_DIR}/myprojectConfigVersion.cmake"
    VERSION ${PROJECT_VERSION}
    COMPATIBILITY SameMajorVersion
)

# Generate config file from template
configure_package_config_file(
    "${CMAKE_SOURCE_DIR}/cmake/myprojectConfig.cmake.in"
    "${CMAKE_CURRENT_BINARY_DIR}/myprojectConfig.cmake"
    INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/myproject
)

install(FILES
    "${CMAKE_CURRENT_BINARY_DIR}/myprojectConfig.cmake"
    "${CMAKE_CURRENT_BINARY_DIR}/myprojectConfigVersion.cmake"
    DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/myproject
)
# cmake/myprojectConfig.cmake.in — Package configuration template
@PACKAGE_INIT@

include(CMakeFindDependencyMacro)

# Re-find our public dependencies so consumers get them automatically
find_dependency(nlohmann_json 3.11)

include("${CMAKE_CURRENT_LIST_DIR}/myprojectTargets.cmake")

check_required_components(myproject)
# packaging/CPack.cmake — Binary package generation
set(CPACK_PACKAGE_NAME "myproject")
set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION})
set(CPACK_PACKAGE_VENDOR "MyOrg")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "A professional-grade C++ library")
set(CPACK_PACKAGE_HOMEPAGE_URL "https://github.com/myorg/myproject")
set(CPACK_PACKAGE_CONTACT "team@myorg.dev")
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE")
set(CPACK_RESOURCE_FILE_README "${CMAKE_SOURCE_DIR}/README.md")

# Component-based installation
set(CPACK_COMPONENTS_ALL runtime development documentation)
set(CPACK_COMPONENT_RUNTIME_DISPLAY_NAME "Runtime Libraries")
set(CPACK_COMPONENT_DEVELOPMENT_DISPLAY_NAME "Development Files")
set(CPACK_COMPONENT_DOCUMENTATION_DISPLAY_NAME "Documentation")

# DEB-specific
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6 (>= 2.31)")
set(CPACK_DEBIAN_PACKAGE_SECTION "libs")
set(CPACK_DEBIAN_FILE_NAME DEB-DEFAULT)

# RPM-specific
set(CPACK_RPM_PACKAGE_LICENSE "MIT")
set(CPACK_RPM_PACKAGE_GROUP "Development/Libraries")
set(CPACK_RPM_FILE_NAME RPM-DEFAULT)

# NSIS (Windows installer)
set(CPACK_NSIS_DISPLAY_NAME "MyProject ${PROJECT_VERSION}")
set(CPACK_NSIS_PACKAGE_NAME "MyProject")
set(CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL ON)

include(CPack)
# Generate packages after building
cmake -B build -DCMAKE_BUILD_TYPE=Release -DMYPROJECT_INSTALL=ON
cmake --build build
cd build

# Generate all configured package types
cpack -G "DEB;RPM;TGZ"

# Generate Windows installer
cpack -G NSIS

# Generate specific component packages
cpack -G DEB -D CPACK_COMPONENTS_ALL=runtime

CI/CD Integration

A professional CMake project runs its full validation suite on every pull request — compilation across multiple platforms, all test levels, static analysis, sanitizer builds, and optionally package generation. CMake Presets make CI configuration dramatically simpler because the CI script only needs to reference preset names rather than repeating all the flags.

CI/CD Pipeline Stages
        flowchart LR
            A[Push/PR] --> B[Configure]
            B --> C[Build]
            C --> D[Test]
            D --> E[Analysis]
            E --> F[Package]
            F --> G[Deploy]

            B --> B1[Linux GCC]
            B --> B2[Linux Clang]
            B --> B3[macOS Clang]
            B --> B4[Windows MSVC]

            D --> D1[Unit Tests]
            D --> D2[Integration Tests]
            D --> D3[Sanitizer Tests]

            E --> E1[clang-tidy]
            E --> E2[Coverage Report]

            F --> F1[DEB/RPM]
            F --> F2[Windows NSIS]
            F --> F3[GitHub Release]

            style A fill:#132440,color:#fff
            style G fill:#BF092F,color:#fff
            style D fill:#3B9797,color:#fff
    
# .github/workflows/ci.yml — GitHub Actions CI
name: CI

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  build-and-test:
    strategy:
      fail-fast: false
      matrix:
        include:
          - os: ubuntu-24.04
            preset: ci-linux-gcc
            compiler: gcc-14
          - os: ubuntu-24.04
            preset: ci-linux-clang
            compiler: clang-18
          - os: macos-14
            preset: ci-macos
            compiler: AppleClang
          - os: windows-latest
            preset: ci-windows
            compiler: MSVC

    runs-on: ${{ matrix.os }}
    name: ${{ matrix.compiler }}

    steps:
      - uses: actions/checkout@v4

      - name: Install dependencies (Linux)
        if: runner.os == 'Linux'
        run: |
          sudo apt-get update
          sudo apt-get install -y ninja-build lcov
          # vcpkg bootstrap
          git clone https://github.com/microsoft/vcpkg.git /opt/vcpkg
          /opt/vcpkg/bootstrap-vcpkg.sh

      - name: Cache vcpkg packages
        uses: actions/cache@v4
        with:
          path: build/vcpkg_installed
          key: vcpkg-${{ matrix.os }}-${{ hashFiles('vcpkg.json') }}
          restore-keys: vcpkg-${{ matrix.os }}-

      - name: Configure
        run: cmake --preset ${{ matrix.preset }}

      - name: Build
        run: cmake --build --preset ${{ matrix.preset }}

      - name: Test
        run: ctest --preset ${{ matrix.preset }}

      - name: Upload test results
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: test-results-${{ matrix.compiler }}
          path: build/*/Testing/

  sanitizers:
    runs-on: ubuntu-24.04
    strategy:
      matrix:
        sanitizer: [asan, tsan, ubsan]
    name: ${{ matrix.sanitizer }}

    steps:
      - uses: actions/checkout@v4

      - name: Configure
        run: cmake --preset ${{ matrix.sanitizer }}

      - name: Build
        run: cmake --build --preset ${{ matrix.sanitizer }}

      - name: Test
        run: ctest --preset ${{ matrix.sanitizer }}

  coverage:
    runs-on: ubuntu-24.04
    needs: build-and-test

    steps:
      - uses: actions/checkout@v4

      - name: Install tools
        run: sudo apt-get install -y ninja-build lcov

      - name: Configure and build
        run: |
          cmake --preset coverage
          cmake --build --preset coverage

      - name: Run tests and generate coverage
        run: |
          ctest --preset coverage
          cmake --build --preset coverage --target coverage

      - name: Upload coverage to Codecov
        uses: codecov/codecov-action@v4
        with:
          file: build/coverage/coverage_filtered.info
          fail_ci_if_error: true

  package:
    runs-on: ubuntu-24.04
    needs: [build-and-test, sanitizers]
    if: github.ref == 'refs/heads/main'

    steps:
      - uses: actions/checkout@v4

      - name: Build release
        run: |
          cmake --preset release
          cmake --build --preset release
          cd build/release && cpack -G "DEB;TGZ"

      - name: Upload packages
        uses: actions/upload-artifact@v4
        with:
          name: packages
          path: build/release/*.deb build/release/*.tar.gz

GitLab CI Configuration

# .gitlab-ci.yml — GitLab CI equivalent
stages:
  - build
  - test
  - analyze
  - package

variables:
  GIT_SUBMODULE_STRATEGY: recursive

.cmake-base:
  image: gcc:14
  before_script:
    - apt-get update && apt-get install -y cmake ninja-build
    - git clone https://github.com/microsoft/vcpkg.git /opt/vcpkg
    - /opt/vcpkg/bootstrap-vcpkg.sh

build:linux:
  extends: .cmake-base
  stage: build
  script:
    - cmake --preset ci-linux-gcc
    - cmake --build --preset ci-linux-gcc
  artifacts:
    paths: [build/]
    expire_in: 1 hour

test:linux:
  extends: .cmake-base
  stage: test
  needs: [build:linux]
  script:
    - ctest --preset ci-linux-gcc --output-on-failure

analyze:clang-tidy:
  stage: analyze
  image: silkeh/clang:18
  script:
    - cmake --preset ci-clang-tidy
    - cmake --build --preset ci-clang-tidy 2>&1 | tee clang-tidy.log
  artifacts:
    paths: [clang-tidy.log]
    when: always

package:release:
  extends: .cmake-base
  stage: package
  only: [tags]
  script:
    - cmake --preset release
    - cmake --build --preset release
    - cd build/release && cpack -G "DEB;RPM;TGZ"
  artifacts:
    paths: [build/release/*.deb, build/release/*.rpm, build/release/*.tar.gz]

Preset Workflows

CMake Presets (CMakePresets.json) encode all build configurations declaratively — eliminating the need to remember or document long command-line invocations. Workflow presets go further by chaining configure → build → test into a single command. This is the definitive way to ensure everyone (developers, CI, new contributors) uses identical settings.

CMakePresets.json

{
    "version": 6,
    "cmakeMinimumRequired": { "major": 3, "minor": 25, "patch": 0 },
    "configurePresets": [
        {
            "name": "base",
            "hidden": true,
            "generator": "Ninja",
            "binaryDir": "${sourceDir}/build/${presetName}",
            "cacheVariables": {
                "CMAKE_EXPORT_COMPILE_COMMANDS": "ON",
                "MYPROJECT_BUILD_TESTS": "ON",
                "MYPROJECT_BUILD_EXAMPLES": "ON"
            },
            "environment": {
                "VCPKG_ROOT": "/opt/vcpkg"
            },
            "toolchainFile": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake"
        },
        {
            "name": "dev",
            "displayName": "Development",
            "inherits": "base",
            "cacheVariables": {
                "CMAKE_BUILD_TYPE": "Debug",
                "MYPROJECT_ENABLE_CLANG_TIDY": "ON"
            }
        },
        {
            "name": "release",
            "displayName": "Release",
            "inherits": "base",
            "cacheVariables": {
                "CMAKE_BUILD_TYPE": "Release",
                "MYPROJECT_BUILD_TESTS": "OFF",
                "MYPROJECT_INSTALL": "ON"
            }
        },
        {
            "name": "asan",
            "displayName": "AddressSanitizer",
            "inherits": "base",
            "cacheVariables": {
                "CMAKE_BUILD_TYPE": "Debug",
                "MYPROJECT_SANITIZER": "asan"
            }
        },
        {
            "name": "tsan",
            "displayName": "ThreadSanitizer",
            "inherits": "base",
            "cacheVariables": {
                "CMAKE_BUILD_TYPE": "Debug",
                "MYPROJECT_SANITIZER": "tsan"
            }
        },
        {
            "name": "ubsan",
            "displayName": "UndefinedBehaviorSanitizer",
            "inherits": "base",
            "cacheVariables": {
                "CMAKE_BUILD_TYPE": "Debug",
                "MYPROJECT_SANITIZER": "ubsan"
            }
        },
        {
            "name": "coverage",
            "displayName": "Code Coverage",
            "inherits": "base",
            "cacheVariables": {
                "CMAKE_BUILD_TYPE": "Debug",
                "MYPROJECT_ENABLE_COVERAGE": "ON"
            }
        },
        {
            "name": "ci-linux-gcc",
            "displayName": "CI Linux GCC",
            "inherits": "base",
            "cacheVariables": {
                "CMAKE_BUILD_TYPE": "RelWithDebInfo",
                "CMAKE_CXX_COMPILER": "g++-14"
            }
        },
        {
            "name": "ci-linux-clang",
            "displayName": "CI Linux Clang",
            "inherits": "base",
            "cacheVariables": {
                "CMAKE_BUILD_TYPE": "RelWithDebInfo",
                "CMAKE_CXX_COMPILER": "clang++-18"
            }
        },
        {
            "name": "ci-clang-tidy",
            "displayName": "CI Static Analysis",
            "inherits": "base",
            "cacheVariables": {
                "CMAKE_BUILD_TYPE": "Debug",
                "CMAKE_CXX_COMPILER": "clang++-18",
                "MYPROJECT_ENABLE_CLANG_TIDY": "ON"
            }
        },
        {
            "name": "ci-macos",
            "displayName": "CI macOS",
            "inherits": "base",
            "cacheVariables": {
                "CMAKE_BUILD_TYPE": "RelWithDebInfo"
            },
            "condition": {
                "type": "equals",
                "lhs": "${hostSystemName}",
                "rhs": "Darwin"
            }
        },
        {
            "name": "ci-windows",
            "displayName": "CI Windows",
            "inherits": "base",
            "generator": "Ninja",
            "cacheVariables": {
                "CMAKE_BUILD_TYPE": "RelWithDebInfo"
            },
            "condition": {
                "type": "equals",
                "lhs": "${hostSystemName}",
                "rhs": "Windows"
            }
        }
    ],
    "buildPresets": [
        { "name": "dev", "configurePreset": "dev" },
        { "name": "release", "configurePreset": "release" },
        { "name": "asan", "configurePreset": "asan" },
        { "name": "tsan", "configurePreset": "tsan" },
        { "name": "ubsan", "configurePreset": "ubsan" },
        { "name": "coverage", "configurePreset": "coverage" },
        { "name": "ci-linux-gcc", "configurePreset": "ci-linux-gcc" },
        { "name": "ci-linux-clang", "configurePreset": "ci-linux-clang" },
        { "name": "ci-clang-tidy", "configurePreset": "ci-clang-tidy" },
        { "name": "ci-macos", "configurePreset": "ci-macos" },
        { "name": "ci-windows", "configurePreset": "ci-windows" }
    ],
    "testPresets": [
        {
            "name": "dev",
            "configurePreset": "dev",
            "output": { "outputOnFailure": true },
            "execution": { "jobs": 0 }
        },
        { "name": "asan", "configurePreset": "asan", "output": { "outputOnFailure": true } },
        { "name": "tsan", "configurePreset": "tsan", "output": { "outputOnFailure": true } },
        { "name": "ubsan", "configurePreset": "ubsan", "output": { "outputOnFailure": true } },
        { "name": "coverage", "configurePreset": "coverage", "output": { "outputOnFailure": true } },
        { "name": "ci-linux-gcc", "configurePreset": "ci-linux-gcc", "output": { "outputOnFailure": true } },
        { "name": "ci-linux-clang", "configurePreset": "ci-linux-clang", "output": { "outputOnFailure": true } },
        { "name": "ci-macos", "configurePreset": "ci-macos", "output": { "outputOnFailure": true } },
        { "name": "ci-windows", "configurePreset": "ci-windows", "output": { "outputOnFailure": true } }
    ],
    "workflowPresets": [
        {
            "name": "dev",
            "displayName": "Development Workflow",
            "steps": [
                { "type": "configure", "name": "dev" },
                { "type": "build", "name": "dev" },
                { "type": "test", "name": "dev" }
            ]
        },
        {
            "name": "ci-full",
            "displayName": "Full CI Workflow",
            "steps": [
                { "type": "configure", "name": "ci-linux-gcc" },
                { "type": "build", "name": "ci-linux-gcc" },
                { "type": "test", "name": "ci-linux-gcc" }
            ]
        }
    ]
}
# Developer daily workflow — single command does everything
cmake --workflow --preset dev

# CI workflow
cmake --workflow --preset ci-full

# List all available presets
cmake --list-presets
cmake --build --list-presets
ctest --list-presets
User Presets: Commit CMakePresets.json to version control (shared configuration). Create CMakeUserPresets.json (gitignored) for personal overrides like custom compiler paths, local vcpkg roots, or IDE-specific settings. User presets inherit from the shared ones.

Putting It All Together

Let's walk through building a complete project from scratch that incorporates every technique from this series. This is a real-world C++ library called nexus — a high-performance event processing engine with a clean public API, comprehensive testing, documentation, CI/CD, and cross-platform packaging.

Complete Project Walkthrough
nexus — Professional Event Processing Library

This walkthrough demonstrates building a production-ready CMake project from the ground up, applying all 32 prior parts' techniques in a cohesive workflow.

capstone production complete-example
# Step 1: Create the project skeleton
mkdir -p nexus/{src/{core,io,utils},include/nexus,tests/{unit,integration}}
mkdir -p nexus/{cmake,docs/source,examples,packaging}
cd nexus
git init

# Step 2: Create vcpkg manifest
cat > vcpkg.json <<'EOF'
{
    "name": "nexus",
    "version-semver": "1.0.0",
    "dependencies": ["fmt", "spdlog", "nlohmann-json", "catch2"]
}
EOF

# Step 3: Initialize CMakePresets.json
# (Use the full preset file from the previous section)

# Step 4: Create .clang-tidy, .clang-format, .gitignore
echo "build/" > .gitignore
echo "CMakeUserPresets.json" >> .gitignore
# nexus/CMakeLists.txt — Complete top-level file
cmake_minimum_required(VERSION 3.25)

project(nexus
    VERSION 1.0.0
    DESCRIPTION "High-performance event processing engine"
    HOMEPAGE_URL "https://github.com/myorg/nexus"
    LANGUAGES CXX
)

# Prevent in-source builds
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR)
    message(FATAL_ERROR "In-source builds not allowed. Use: cmake -B build")
endif()

# Global settings
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

# Module path
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")

# Options
option(NEXUS_BUILD_TESTS "Build tests" ON)
option(NEXUS_BUILD_DOCS "Build documentation" OFF)
option(NEXUS_BUILD_EXAMPLES "Build examples" ON)
option(NEXUS_ENABLE_COVERAGE "Enable coverage" OFF)
option(NEXUS_INSTALL "Generate install target" ON)
set(NEXUS_SANITIZER "" CACHE STRING "Sanitizer: asan, tsan, ubsan, msan")

# Include modules
include(CompilerWarnings)
include(Sanitizers)
if(NEXUS_ENABLE_COVERAGE)
    include(Coverage)
endif()

# Dependencies
find_package(fmt REQUIRED)
find_package(spdlog REQUIRED)
find_package(nlohmann_json 3.11 REQUIRED)

# Source
add_subdirectory(src)

# Tests
if(NEXUS_BUILD_TESTS)
    enable_testing()
    add_subdirectory(tests)
endif()

# Docs
if(NEXUS_BUILD_DOCS)
    add_subdirectory(docs)
endif()

# Examples
if(NEXUS_BUILD_EXAMPLES)
    add_subdirectory(examples)
endif()

# Install
if(NEXUS_INSTALL)
    include(Install)
endif()
# nexus/src/CMakeLists.txt — Library and application targets
include(GenerateExportHeader)
include(GitVersion)

# Core library
add_library(nexus_core
    core/engine.cpp
    core/event_loop.cpp
    core/scheduler.cpp
    io/tcp_listener.cpp
    io/buffer_pool.cpp
    utils/logging.cpp
    utils/thread_pool.cpp
)
add_library(nexus::core ALIAS nexus_core)

generate_export_header(nexus_core
    BASE_NAME NEXUS
    EXPORT_FILE_NAME "${CMAKE_BINARY_DIR}/generated/nexus/export.hpp"
)

# Version header
configure_file(
    "${CMAKE_SOURCE_DIR}/include/nexus/version.hpp.in"
    "${CMAKE_BINARY_DIR}/generated/nexus/version.hpp"
    @ONLY
)

target_include_directories(nexus_core
    PUBLIC
        $<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/include>
        $<BUILD_INTERFACE:${CMAKE_BINARY_DIR}/generated>
        $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
    PRIVATE
        ${CMAKE_CURRENT_SOURCE_DIR}
)

target_link_libraries(nexus_core
    PUBLIC nlohmann_json::nlohmann_json
    PRIVATE fmt::fmt spdlog::spdlog nexus::warnings
)

target_compile_features(nexus_core PUBLIC cxx_std_20)

set_target_properties(nexus_core PROPERTIES
    VERSION ${PROJECT_VERSION}
    SOVERSION ${PROJECT_VERSION_MAJOR}
    OUTPUT_NAME nexus
)

# Application
add_executable(nexus_app app/main.cpp)
add_executable(nexus::app ALIAS nexus_app)
target_link_libraries(nexus_app PRIVATE nexus::core nexus::warnings)
// nexus/include/nexus/core.hpp — Clean public API
#pragma once

#include "nexus/export.hpp"
#include <functional>
#include <memory>
#include <string_view>
#include <chrono>

namespace nexus {

/// Event identifier type
using EventId = std::uint64_t;

/// Event handler callback
using EventHandler = std::function<void(EventId, std::string_view payload)>;

/// Configuration for the event engine
struct NEXUS_EXPORT EngineConfig {
    std::size_t thread_count = std::thread::hardware_concurrency();
    std::size_t max_queue_size = 65536;
    std::chrono::milliseconds idle_timeout{100};
    bool enable_metrics = true;
};

/// High-performance event processing engine
class NEXUS_EXPORT Engine {
public:
    explicit Engine(EngineConfig config = {});
    ~Engine();

    // Non-copyable, movable
    Engine(const Engine&) = delete;
    Engine& operator=(const Engine&) = delete;
    Engine(Engine&&) noexcept;
    Engine& operator=(Engine&&) noexcept;

    /// Register a handler for events matching the given pattern
    void subscribe(std::string_view pattern, EventHandler handler);

    /// Publish an event to all matching subscribers
    void publish(std::string_view topic, std::string_view payload);

    /// Start the event processing loop
    void start();

    /// Stop processing and drain the queue
    void stop();

    /// Check if the engine is currently running
    [[nodiscard]] bool is_running() const noexcept;

    /// Get the number of events processed since start
    [[nodiscard]] std::uint64_t events_processed() const noexcept;

private:
    struct Impl;
    std::unique_ptr<Impl> impl_;
};

} // namespace nexus
// nexus/tests/unit/test_engine.cpp — Catch2 unit tests
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers_string.hpp>
#include <nexus/core.hpp>
#include <thread>
#include <atomic>
#include <latch>

using namespace nexus;
using namespace std::chrono_literals;

TEST_CASE("Engine lifecycle", "[engine]") {
    Engine engine({.thread_count = 2, .max_queue_size = 1024});

    SECTION("starts and stops cleanly") {
        engine.start();
        REQUIRE(engine.is_running());

        engine.stop();
        REQUIRE_FALSE(engine.is_running());
    }

    SECTION("double start is safe") {
        engine.start();
        engine.start();  // Should not throw
        REQUIRE(engine.is_running());
        engine.stop();
    }

    SECTION("stop without start is safe") {
        engine.stop();  // Should not throw
        REQUIRE_FALSE(engine.is_running());
    }
}

TEST_CASE("Event pub/sub", "[engine][events]") {
    Engine engine({.thread_count = 2});
    std::atomic<int> count{0};
    std::latch done{3};

    engine.subscribe("test.*", [&](EventId, std::string_view payload) {
        count.fetch_add(1);
        done.count_down();
    });

    engine.start();

    engine.publish("test.alpha", "payload1");
    engine.publish("test.beta", "payload2");
    engine.publish("test.gamma", "payload3");

    REQUIRE(done.try_wait_for(5s));
    REQUIRE(count.load() == 3);
    REQUIRE(engine.events_processed() >= 3);

    engine.stop();
}

TEST_CASE("Engine config validation", "[engine][config]") {
    SECTION("zero threads uses hardware concurrency") {
        EngineConfig config{.thread_count = 0};
        Engine engine(config);
        // Should not crash — falls back to hardware_concurrency()
    }
}
# Complete development workflow
cd nexus

# Configure + build + test in one command
cmake --workflow --preset dev

# Run sanitizer check
cmake --preset asan
cmake --build --preset asan
ctest --preset asan

# Generate coverage report
cmake --preset coverage
cmake --build --preset coverage
ctest --preset coverage
cmake --build --preset coverage --target coverage
# → Open build/coverage/coverage_report/index.html

# Build release package
cmake --preset release
cmake --build --preset release
cd build/release && cpack -G TGZ

# Verify the installed package works
cmake --install build/release --prefix /tmp/nexus-install
ls /tmp/nexus-install/lib/cmake/nexus/  # Config files present

# Test consumption from another project
mkdir /tmp/consumer && cd /tmp/consumer
cat > CMakeLists.txt <<'EOF'
cmake_minimum_required(VERSION 3.25)
project(consumer LANGUAGES CXX)
find_package(nexus 1.0 REQUIRED)
add_executable(demo main.cpp)
target_link_libraries(demo PRIVATE nexus::core)
EOF

cmake -B build -DCMAKE_PREFIX_PATH=/tmp/nexus-install
cmake --build build
./build/demo  # Works!
The Professional CMake Checklist:
  • ✅ Namespaced ALIAS targets (nexus::core)
  • ✅ Generator expressions for build/install interface split
  • GenerateExportHeader for cross-platform shared library symbols
  • configure_file() for version embedding from Git
  • ✅ Proper PUBLIC/PRIVATE/INTERFACE separation
  • ✅ CMakePresets.json for reproducible configurations
  • ✅ Workflow presets for one-command development
  • ✅ CTest integration with test discovery
  • ✅ Coverage with lcov/gcov
  • ✅ Sanitizer presets (ASan, TSan, UBSan)
  • ✅ Static analysis via CMAKE_CXX_CLANG_TIDY
  • ✅ CPack for binary distribution
  • ✅ Proper install(EXPORT) for downstream consumption
  • ✅ Multi-platform CI with matrix builds
  • ✅ vcpkg manifest mode for dependency management

Series Complete — Congratulations!

🎉 CMake Mastery Complete
You've Completed the Entire CMake Mastery Series

Over 33 articles, you've journeyed from writing your first cmake_minimum_required() to building production-grade projects with cross-platform CI/CD, sanitizer testing, static analysis, and professional packaging. Here's what you've mastered:

  • Parts 1–6: CMake fundamentals — language, targets, libraries, compiler configuration
  • Parts 7–11: Advanced features — generator expressions, environment detection, dependencies, external projects
  • Parts 12–15: Testing and code generation — CTest, framework integration, configure-time operations
  • Parts 16–20: Project organization — structuring, IDEs, C++20 modules, analysis tools, documentation
  • Parts 21–23: Distribution — installation, CPack packaging, CMake Presets
  • Parts 24–29: Specialized topics — mixed languages, cross-compilation, generators, CDash, porting, reproducibility
  • Parts 30–32: Platform mastery — build optimization, Apple platforms, Python extensions
  • Part 33: The capstone — combining everything into a professional project

You now have the knowledge to build, test, analyze, package, and distribute C++ software at a professional level using CMake. The techniques in this series apply equally to personal projects and enterprise-scale codebases with thousands of source files.

Keep building. Keep shipping. Keep mastering your craft.

33 parts complete professional