Files
cpp-project-template/docs/cmake-api.md
T
tqcq 05eabe7cd4
cpp-template / format (push) Failing after 3m38s
cpp-template / debug (push) Failing after 5m18s
cpp-template / clang-tidy (push) Failing after 1m26s
cpp-template / release (push) Failing after 1m42s
cpp-template / cxx11-smoke (push) Failing after 1m43s
cpp-template / install-consumer (push) Failing after 1m37s
cpp-template / asan (push) Failing after 1m23s
cpp-template / tsan (push) Failing after 1m26s
cpp-template / fuzz-smoke (push) Failing after 1m15s
cpp-template / no-network-negative (push) Failing after 7m7s
docs: document template workflows
2026-05-18 09:41:16 +08:00

14 KiB

CMake helper API manual

This manual documents the project helper API implemented under cmake/. The examples are intentionally close to the positive helper fixtures and current root usage, but paths and target names are illustrative unless a section explicitly says otherwise.

Global conventions

  • Call native project(<name> LANGUAGES C CXX) before cc_project(); the helper rejects CMake's implicit project(Project) default.
  • The primary project standard is C++14. Fuzz lane targets require the fuzz preset or equivalent Clang + C++17 configuration.
  • INCS, DEPS, and DEFINES use explicit visibility groups. Every item must follow PUBLIC, PRIVATE, or INTERFACE; unqualified items are rejected.
  • Executable-like targets (cc_executable, cc_test, cc_benchmark, and cc_fuzz) do not support INTERFACE visibility because they are not consumed as link interfaces. Use PUBLIC or PRIVATE only.
  • cc_library(TYPE INTERFACE) supports only INTERFACE visibility for INCS, DEPS, and DEFINES.
  • Helpers do not acquire external dependencies. Define or load targets such as GTest::gtest, benchmark::benchmark, and fuzztest::fuzztest before calling the corresponding helper.
  • There are aggregate targets run_tests and run_benchmarks, but no per-target run_test_<name>, run_benchmark_<name>, or run_fuzz_<name> wrappers.

cc_project()

Signature:

cc_project()

Use in the root CMakeLists.txt after native project() and after any cache variables that should influence helper options.

cmake_minimum_required(VERSION 3.19)
project(my_package LANGUAGES C CXX)

include(cmake/cc_project.cmake)
cc_project()

Behavior:

  • Sets C++ defaults: C++14 by default, required standard, extensions off.
  • Enables CMAKE_EXPORT_COMPILE_COMMANDS unless the caller already set it.
  • Enables position-independent code.
  • Defines helper options:
    • CC_ENABLE_INSTALL: ON by default for the top-level project, OFF for subprojects.
    • CC_ENABLE_TESTING: ON by default.
    • CC_ENABLE_BENCHMARKS: ON by default.
    • CC_ENABLE_FUZZTEST: OFF by default; requires the fuzz lane when enabled.
    • CC_WARNINGS_AS_ERRORS: OFF by default.
  • Bridges existing lane options into helper options when present: CPP_TEMPLATE_ENABLE_TESTS, CPP_TEMPLATE_ENABLE_BENCHMARKS, and CPP_TEMPLATE_FUZZ_LANE.
  • Creates top-level aggregate targets when enabled:
    • run_tests: runs ctest --output-on-failure.
    • run_benchmarks: aggregate target populated by cc_benchmark().
  • Defers cc_finalize_install() at generate time when the install module is included.

cc_library()

Signature:

cc_library(<target>
    TYPE STATIC|SHARED|INTERFACE
    [SRCS <src>...]
    [HDRS <hdr>...]
    [INCS [PUBLIC|PRIVATE|INTERFACE <dir>...]...]
    [DEPS [PUBLIC|PRIVATE|INTERFACE <dep>...]...]
    [DEFINES [PUBLIC|PRIVATE|INTERFACE <define>...]...]
    [OPTIONS <option>...]
    [FEATURES <feature>...]
    [ALIAS <alias>]
    [INSTALL]
)

Required contract:

  • TYPE is mandatory and must be STATIC, SHARED, or INTERFACE.
  • OBJECT libraries are not supported by this helper.
  • TYPE INTERFACE must not provide SRCS or HDRS.
  • INCS, DEPS, and DEFINES require explicit visibility for every item.
  • TYPE INTERFACE only accepts INTERFACE visibility in visibility-grouped arguments.
  • ALIAS creates a real build-tree library alias with add_library(<alias> ALIAS <target>) and also stores install/export metadata.
  • INSTALL only marks the target. Install rules are produced later by cc_finalize_install() when install is enabled.

Static library example, mirroring the positive helper fixture pattern:

include("${CC_PROJECT_ROOT}/cmake/cc_targets.cmake")

cc_library(my_static_lib
    TYPE STATIC
    SRCS lib.cpp
    HDRS lib.h
    INCS PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}
    DEPS PUBLIC m
    DEFINES PUBLIC MY_STATIC_BUILD
    OPTIONS -Wall
    FEATURES cxx_std_14
    ALIAS my_static_lib::my_static_lib
    INSTALL
)

Interface library example:

cc_library(my_headers
    TYPE INTERFACE
    INCS INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include
    DEFINES INTERFACE MY_HEADERS_ONLY=1
    FEATURES cxx_std_14
)

cc_executable()

Signature:

cc_executable(<target>
    [SRCS <src>...]
    [HDRS <hdr>...]
    [INCS [PUBLIC|PRIVATE <dir>...]...]
    [DEPS [PUBLIC|PRIVATE <dep>...]...]
    [DEFINES [PUBLIC|PRIVATE <define>...]...]
    [OPTIONS <option>...]
    [FEATURES <feature>...]
    [ALIAS <alias>]
    [INSTALL]
)

Behavior:

  • Creates an executable target. SRCS may be omitted, producing an initially empty executable target.
  • Applies OPTIONS and FEATURES privately.
  • Rejects INTERFACE visibility in INCS, DEPS, and DEFINES.
  • ALIAS is install/export metadata only. CMake does not support add_executable(<alias> ALIAS ...), so no build-tree executable alias is created.
  • INSTALL marks the target for later install/export finalization.

Example:

include("${CC_PROJECT_ROOT}/cmake/cc_targets.cmake")

cc_executable(hello_exe
    SRCS main.cpp
    DEPS PRIVATE my_static_lib
    OPTIONS -Wall
    FEATURES cxx_std_14
)

Installable executable example:

cc_executable(my_tool
    SRCS main.cpp
    ALIAS my_ns::my_tool
    INSTALL
)

cc_test()

Signature:

cc_test(<target>
    [NO_MAIN]
    SRCS <src>...
    [HDRS <hdr>...]
    [INCS [PUBLIC|PRIVATE <dir>...]...]
    [DEPS [PUBLIC|PRIVATE <dep>...]...]
    [DEFINES [PUBLIC|PRIVATE <define>...]...]
    [OPTIONS <option>...]
    [FEATURES <feature>...]
    [ARGS <arg>...]
)

Behavior:

  • Skips target creation when CC_ENABLE_TESTING is OFF.
  • Requires GTest::gtest; if unavailable, warns and skips target creation.
  • Requires non-empty SRCS.
  • Registers add_test(NAME <target> COMMAND <target> [<ARGS>...]).
  • Adds the test executable as a dependency of run_tests when that aggregate target exists.
  • Without NO_MAIN, links GTest::gtest_main, which implies GTest::gtest.
  • With NO_MAIN, links GTest::gtest only; use this when the test source or another dependency supplies main().
  • GTest::gmock may be listed in DEPS with or without NO_MAIN.
  • GTest::gmock_main requires NO_MAIN; the helper rejects it otherwise because it conflicts with default GTest::gtest_main.

Basic example:

include("${CC_PROJECT_ROOT}/cmake/cc_targets.cmake")
include("${CC_PROJECT_ROOT}/cmake/cc_testing.cmake")

enable_testing()

cc_test(calc_test
    SRCS calc_test.cpp
    DEPS PRIVATE calc
    ARGS --gtest_filter=CalcTest.*
)

GMock custom-main example, mirroring the positive helper fixture pattern:

cc_test(mock_test
    SRCS test_mock.cpp
    NO_MAIN
    DEPS PRIVATE GTest::gmock
)

cc_benchmark()

Signature:

cc_benchmark(<target>
    [NO_MAIN]
    SRCS <src>...
    [HDRS <hdr>...]
    [INCS [PUBLIC|PRIVATE <dir>...]...]
    [DEPS [PUBLIC|PRIVATE <dep>...]...]
    [DEFINES [PUBLIC|PRIVATE <define>...]...]
    [OPTIONS <option>...]
    [FEATURES <feature>...]
    [ARGS <arg>...]
)

Behavior:

  • Skips target creation when CC_ENABLE_BENCHMARKS is OFF.
  • Requires benchmark::benchmark; if unavailable, warns and skips target creation.
  • Without NO_MAIN, also requires and links benchmark::benchmark_main.
  • With NO_MAIN, links benchmark::benchmark only; use this when the benchmark source provides main().
  • Requires non-empty SRCS.
  • Does not register the benchmark with default CTest.
  • Adds the benchmark target as a dependency of run_benchmarks when that aggregate target exists.
  • Adds a POST_BUILD command to run_benchmarks that directly executes the benchmark binary with ARGS.
  • ARGS are native Google Benchmark runtime arguments. There is no helper-specific OUTPUT keyword; use native flags such as --benchmark_out=<path> and --benchmark_out_format=json.

Example with native benchmark arguments, mirroring the positive helper fixture pattern:

include("${CC_PROJECT_ROOT}/cmake/cc_targets.cmake")
include("${CC_PROJECT_ROOT}/cmake/cc_benchmark.cmake")

add_custom_target(run_benchmarks
    COMMENT "Running all benchmarks"
    VERBATIM
)

cc_benchmark(args_bench
    SRCS bench_args.cc
    ARGS --benchmark_out=${CMAKE_BINARY_DIR}/bench_output.json
         --benchmark_out_format=json
)

Custom-main example:

cc_benchmark(no_main_bench
    SRCS bench_no_main.cc
    NO_MAIN
    DEPS PRIVATE calc
)

cc_fuzz()

Signature:

cc_fuzz(<target>
    [REQUIRED]
    SRCS <src>...
    [HDRS <hdr>...]
    [INCS [PUBLIC|PRIVATE <dir>...]...]
    [DEPS [PUBLIC|PRIVATE <dep>...]...]
    [DEFINES [PUBLIC|PRIVATE <define>...]...]
    [OPTIONS <option>...]
    [FEATURES <feature>...]
    [SMOKE_ARGS <arg>...]
    [SMOKE_ENV <key=value>...]
    [SMOKE_TIMEOUT <seconds>]
    [ARGS <arg>...]
)

Behavior:

  • Skips target creation when CC_ENABLE_FUZZTEST is OFF.
  • Requires non-empty SRCS.
  • Links fuzztest::fuzztest when available, otherwise fuzztest.
  • Links the available FuzzTest GTest main provider when present.
  • If FuzzTest is unavailable, warns and skips by default. With REQUIRED, configure fails with guidance to use the fuzz lane.
  • Does not materialize FuzzTest dependencies and does not change compiler standard itself.
  • Fuzz targets are intended for the fuzz lane: Clang and C++17 via cmake --preset fuzz or equivalent CPP_TEMPLATE_FUZZ_LANE=ON setup.
  • Registers only a deterministic CTest smoke test named <target>_smoke with label fuzz_smoke.
  • SMOKE_ARGS and SMOKE_ENV are for fast deterministic CI smoke execution; SMOKE_TIMEOUT defaults to 30 seconds.
  • ARGS are stored as _CC_FUZZ_ARGS metadata for long-running direct executable invocation outside CTest.
  • There are no run_fuzz_<name> wrapper targets.

Example based on current root usage:

include(cmake/cc_targets.cmake)
include(cmake/cc_fuzz.cmake)

cc_fuzz(calc_fuzz
    SRCS src/calc_fuzz.cc
    DEPS PRIVATE calc
    SMOKE_ENV FUZZTEST_PRNG_SEED=42
)

Example with explicit smoke and long-run arguments:

cc_fuzz(parser_fuzz
    REQUIRED
    SRCS parser_fuzz.cc
    DEPS PRIVATE parser
    FEATURES cxx_std_17
    SMOKE_ARGS --fuzz_for=1s
    SMOKE_ENV FUZZTEST_PRNG_SEED=42
    SMOKE_TIMEOUT 10
    ARGS --fuzz_for=600s
)

cc_install()

Signature:

cc_install(<target> [DEPS <logical-name>...])

Behavior:

  • Marks an existing target for install/export by setting _CC_INSTALL TRUE.
  • Fails if the target does not exist.
  • DEPS lists install-time dependency logical names registered with cc_register_dependency().
  • DEPS is not a link dependency list. Link dependencies belong in cc_library(... DEPS ...) or cc_executable(... DEPS ...); install-time DEPS control generated find_dependency() calls and static private dependency validation.
  • Installable targets must have explicit namespaced ALIAS metadata before finalization. The helpers do not auto-create aliases.

Example using explicit install marking instead of an INSTALL keyword:

cc_library(core
    TYPE STATIC
    SRCS core.cpp
    INCS PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include
    DEPS PRIVATE ZLIB::ZLIB
    ALIAS mypkg::core
)

cc_register_dependency(zlib
    PACKAGE ZLIB
    TARGETS ZLIB::ZLIB
)

cc_install(core DEPS zlib)

cc_register_dependency()

Signature:

cc_register_dependency(<logical-name>
    PACKAGE <PackageName>
    [VERSION <version-or-range>]
    [EXACT]
    [COMPONENTS <component>...]
    [OPTIONAL_COMPONENTS <component>...]
    [TARGETS <target>...]
)

Behavior:

  • Registers package metadata for generated package config files.
  • PACKAGE is required and becomes the find_dependency(<PackageName> ...) package name.
  • VERSION, EXACT, COMPONENTS, and OPTIONAL_COMPONENTS are forwarded to find_dependency().
  • TARGETS records the imported targets provided by the package. It is metadata used by install/export validation.
  • Duplicate logical names are allowed only when all metadata matches exactly; conflicting re-registration fails at configure time.
  • Reference registered logical names from cc_install(<target> DEPS <logical-name>...).

Example:

cc_register_dependency(fmt
    PACKAGE fmt
    VERSION 10.0
    TARGETS fmt::fmt
)

cc_library(formatting
    TYPE STATIC
    SRCS formatting.cpp
    DEPS PUBLIC fmt::fmt
    ALIAS mypkg::formatting
    INSTALL
)

cc_install(formatting DEPS fmt)

cc_finalize_install()

Signature:

cc_finalize_install()

Behavior:

  • Idempotent; repeated calls after the first do nothing.
  • Returns without producing install rules when CC_ENABLE_INSTALL is OFF.
  • Uses only targets marked by INSTALL or cc_install().
  • Validates that every install target has an explicit namespaced alias of the form namespace::name.
  • Requires all installed targets to use the same namespace.
  • Sets EXPORT_NAME from the alias leaf and installs targets into a single export set named <project>Targets.
  • Generates and installs <project>Config.cmake and <project>ConfigVersion.cmake.
  • Emits find_dependency() calls for logical dependencies declared with cc_install(... DEPS ...).
  • Installs public headers from include/<project>/ when that directory exists.
  • cc_project() schedules this function with cmake_language(DEFER) for top-level projects, so most projects should not need to call it manually.

Manual finalization example for a narrow fixture or custom project layout:

include(cmake/cc_project.cmake)
include(cmake/cc_targets.cmake)
include(cmake/cc_install.cmake)

cc_project()

cc_library(core
    TYPE STATIC
    SRCS core.cpp
    ALIAS mypkg::core
    INSTALL
)

cc_finalize_install()

Unsupported helper patterns

Do not use these patterns through the helper API:

# Unsupported: OBJECT library type.
cc_library(obj TYPE OBJECT SRCS obj.cpp)

# Unsupported: missing visibility before dependency.
cc_library(core TYPE STATIC SRCS core.cpp DEPS fmt::fmt)

# Unsupported: executable build-tree aliases.
add_executable(mypkg::tool ALIAS tool)

# Unsupported: helper-specific benchmark output keyword.
cc_benchmark(core_bench SRCS bench.cc OUTPUT bench.json)

# Unsupported: expected per-target run wrappers; use run_fuzz_tests or ctest labels instead.
# Do not run: cmake --build build --target run_fuzz_core_fuzz