Property Propagation
In legacy C++ build systems (Makefiles, Visual Studio Solutions), build settings like “Include Directories” and “Preprocessor Definitions” were often global or applied loosely to directory Scopes. This led to “Include Hell,” where a consumer relied on a transitive dependency”s headers, Causing build breakages when the dependency graph changed.
Modern CMake (3.0+) resolves this via the Target-Centric Model. Every library or executable is An object (Target) that encapsulates two distinct sets of data:
- Build Requirements: What the target needs to build itself.
- Usage Requirements: What a consumer needs to link against the target.
This module analyzes how these properties propagate through the dependency graph using Visibility Scopes.
The Visibility Scopes
Section titled “The Visibility Scopes”When declaring properties (target_include_directories``target_compile_definitions) or Dependencies (target_link_libraries), CMake requires a scope keyword.
1. PRIVATE (Encapsulation)
Section titled “1. PRIVATE (Encapsulation)”- Semantics: The property is required only to build the target itself. It is not propagated to consumers.
- Mechanism: Populates the target’s
INCLUDE_DIRECTORIES(or relevant property) but not theINTERFACE_variants. - Use Case: Implementation details. If
LibAusesBoost.Asiointernally in its.cppfiles but does not expose Asio headers in its public.hfiles, the dependency isPRIVATE.
2. INTERFACE (Propagation)
Section titled “2. INTERFACE (Propagation)”- Semantics: The property is not required to build the target (or the target has no source), but it is required by consumers.
- Mechanism: Populates the target’s
INTERFACE_properties but not the build properties. - Use Case:
- Header-Only Libraries: There is no binary to compile, so
PRIVATEbuild requirements do not exist. - Forward Declarations:
LibAforward declares a type in its header, but the consumer must include the full definition.
3. PUBLIC (Transitivity)
Section titled “3. PUBLIC (Transitivity)”- Semantics: The property is required for both building the target and by consumers.
- Mechanism: Populates both the build properties and the
INTERFACE_properties. - Use Case: Public API dependencies. If
LibAincludes<fmt/core.h>in its public headerliba.hThen any consumer ofLibAmust also have access tofmt’s include path.
Proof of Property Propagation Rules
Section titled “Proof of Property Propagation Rules”Claim: When target B links against target A with scope SThe INTERFACE_ properties of A propagate to B if and only if S is PUBLIC or INTERFACE.
Proof:
CMake’s property propagation operates on the directed graph where vertices are targets And edges are target_link_libraries relationships. Each edge has a scope label .
Define the propagation function where is a target and is a scope:
- If : sets the build properties of the dependent target but does not modify
INTERFACE_properties. No propagation to downstream consumers. - If : sets only the
INTERFACE_properties of the dependent target. The dependent target’s build properties are unchanged, but downstream consumers will see theseINTERFACE_properties. - If : sets both the build properties and the
INTERFACE_properties. The dependent target gets the property for its own build, and all downstream consumers inherit it.
The INTERFACE_LINK_LIBRARIES of a target is the transitive closure of all PUBLIC and INTERFACE edges reachable from . When consumer links against with scope SCMake Merges T’s INTERFACE_LINK_LIBRARIES into ‘s link list if .
A PRIVATE dependency creates a dead end in the propagation graph. Only PUBLIC and INTERFACE edges allow information flow to downstream consumers.
Corollary: The transitive closure of INTERFACE properties from target to consumer is The set of all targets reachable from via paths consisting entirely of PUBLIC or INTERFACE Edges.
The Propagation Graph
Section titled “The Propagation Graph”Consider a linear dependency chain: App Engine Core.
Scenario A: Strict Encapsulation (Private)
Section titled “Scenario A: Strict Encapsulation (Private)”add_library(Core src/core.cpp)# Core uses OpenSSL internally.target_link_libraries(Core PRIVATE OpenSSL::Crypto)- Core: Links against OpenSSL. Has OpenSSL include paths.
- Engine: Links against Core. Does not inherit OpenSSL include paths.
- App: Links against Engine. Does not see OpenSSL.
Result: Clean build graph. Changing OpenSSL versions only triggers a rebuild of Core.
Scenario B: Transitive Leakage (Public)
Section titled “Scenario B: Transitive Leakage (Public)”add_library(Core src/core.cpp)# Core exposes OpenSSL types in core.htarget_link_libraries(Core PUBLIC OpenSSL::Crypto)
# Engine/CMakeLists.txttarget_link_libraries(Engine PUBLIC Core)- Core: Links OpenSSL.
- Engine: Links Core and inherits OpenSSL requirements automatically.
- App: Links Engine. Inherits Core requirements and OpenSSL requirements.
Result: App can include <openssl/ssl.h> without explicitly asking for it. This indicates a Tightly coupled architecture.
Complete Property Scope Table
Section titled “Complete Property Scope Table”The following table enumerates every CMake property that supports visibility scopes and documents The propagation behavior for each.
| Property | PRIVATE (build only) | INTERFACE (consumers only) | PUBLIC (both) | Target command |
|---|---|---|---|---|
| Include directories | Yes | Yes | Yes | target_include_directories |
| Compile definitions | Yes | Yes | Yes | target_compile_definitions |
| Compile features | Yes | Yes | Yes | target_compile_features |
| Compile options | Yes | Yes | Yes | target_compile_options |
| Link libraries | Yes | Yes | Yes | target_link_libraries |
| Link options | Yes | Yes | Yes | target_link_options |
| Link directories | Yes | Yes | Yes | target_link_directories |
| Precompiled headers | Yes | No | No | target_precompile_headers |
| Sources | Yes | No | No | target_sources |
| Position independent code | Yes | No | No | set_target_properties(PIC) |
| C++ standard level | Yes | No | No | target_compile_features |
| System include directories | Yes | Yes | Yes | target_include_directories(SYSTEM) |
| Autogen include directories | Yes | No | No | target_include_directories (Qt) |
Key Observations
Section titled “Key Observations”- Not all properties propagate.
target_sources``target_precompile_headersAnd position independent code settings are alwaysPRIVATEand cannot be made transitive. - Include directories and compile definitions are the most commonly propagated properties. These are the properties that directly affect whether a consumer can compile against a library.
- Link libraries are the most dangerous property to propagate. Every
PUBLIClink library adds to the transitive closure, increasing compile times and binary size.
Interface Libraries
Section titled “Interface Libraries”An INTERFACE library is a CMake target that produces no binary artifact (.a / .lib). It Functions purely as a container for Usage Requirements. This is an architectural pattern for Standardizing project configuration.
Pattern 1: The Project Options Target
Section titled “Pattern 1: The Project Options Target”Instead of setting global flags (CMAKE_CXX_FLAGS), create a meta-target that defines the standard Compilation environment.
add_library(ProjectOptions INTERFACE)
target_compile_features(ProjectOptions INTERFACE cxx_std_23)
target_compile_options(ProjectOptions INTERFACE $<$<CXX_COMPILER_ID:MSVC>:/W4 /permissive-> $<$<CXX_COMPILER_ID:Clang>:-Wall -Wextra -Wpedantic>)
target_compile_definitions(ProjectOptions INTERFACE PROJECT_ARCH_X64)Usage: Every target in the system links against this interface.
add_library(Physics src/physics.cpp)target_link_libraries(Physics PRIVATE ProjectOptions)Pattern 2: Header-Only Dependencies
Section titled “Pattern 2: Header-Only Dependencies”When using a header-only library (like nlohmann_json or Eigen), the target definition is purely Structural.
add_library(JsonLib INTERFACE)target_include_directories(JsonLib INTERFACE include/)Pattern 3: Interface Alias for Installed Packages
Section titled “Pattern 3: Interface Alias for Installed Packages”When consuming an installed package via find_packageYou can create an interface target to Standardize the consumption:
find_package(fmt REQUIRED)
# Create an alias with our project's preferred settingsadd_library(ProjFmt INTERFACE)target_link_libraries(ProjFmt INTERFACE fmt::fmt)target_compile_definitions(ProjFmt INTERFACE FMT_HEADER_ONLY)Build Interface vs. Install Interface
Section titled “Build Interface vs. Install Interface”A critical nuance in property propagation is the difference between directory layouts during Development (Build Tree) versus after deployment (Install Tree).
- Build Tree: Headers are located in
${CMAKE_CURRENT_SOURCE_DIR}/include. - Install Tree: Headers are located in
${CMAKE_INSTALL_PREFIX}/include.
If you hardcode the include path, installed consumers will fail to find headers. You must use Generator Expressions to distinguish these states.
target_include_directories(CoreSystem PUBLIC # Used when building the project locally $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
# Used by consumers importing the installed package $<INSTALL_INTERFACE:include>)Why This Matters: Formal Treatment
Section titled “Why This Matters: Formal Treatment”During development, the build tree has a different directory layout than the install tree. Consider A library Core with headers in Core/include/. During an in-tree build, the include path is /path/to/project/Core/include. After installation with DESTINATION includeThe path is /usr/local/include.
If you write:
# WRONG: hardcoded source pathtarget_include_directories(Core PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)The installed package will embed an absolute path to the build machine’s source directory. Any Consumer on a different machine will fail to find headers. The $<BUILD_INTERFACE> and $<INSTALL_INTERFACE> generator expressions solve this by conditionally selecting the correct path Based on the context.
Installation with Export
Section titled “Installation with Export”install(TARGETS Core EXPORT CoreTargets LIBRARY DESTINATION lib ARCHIVE DESTINATION lib RUNTIME DESTINATION bin INCLUDES DESTINATION include)
install(EXPORT CoreTargets FILE CoreTargets.cmake NAMESPACE Core:: DESTINATION lib/cmake/Core)Consumers then use:
find_package(Core REQUIRED)target_link_libraries(MyApp PRIVATE Core::Core)System Includes (Warning Suppression)
Section titled “System Includes (Warning Suppression)”When linking third-party dependencies, you often want to inherit their Include Directories but Suppress compiler warnings generated by their code.
CMake provides the SYSTEM attribute for this purpose.
# Using explicit SYSTEM propertytarget_include_directories(CoreSystem SYSTEM PUBLIC ${Boost_INCLUDE_DIRS})When checking targets, dependencies marked as SYSTEM are passed to the compiler via -isystem (Clang/GCC) or /external:I (MSVC). This instructs the compiler to treat them as system headers, Disabling warnings for those specific paths while maintaining visibility.
Architectural Best Practices
Section titled “Architectural Best Practices”- Default to PRIVATE: Start with
PRIVATEvisibility for all dependencies. Only escalate toPUBLICif a type from the dependency appears in your public headers. - Avoid Global State: Never use
include_directories()orlink_libraries(). These apply to the directory scope and break the encapsulation of the dependency graph. - Use Interface Targets: Centralize warning levels, sanitizers, and architecture flags into an
INTERFACEtarget rather than repeating variables across multipleCMakeLists.txtfiles.
Build Property Categories
Section titled “Build Property Categories”CMake classifies build properties into several categories, each with distinct propagation semantics. Understanding these categories is essential for controlling the dependency graph precisely.
Compile Definitions
Section titled “Compile Definitions”Compile definitions (-D flags) propagate through target_compile_definitions with visibility Scopes.
# Core library defines NDEBUG in release buildsadd_library(Core src/core.cpp)target_compile_definitions(Core PUBLIC CORE_VERSION="2.1.0" # Visible to Core and all consumers)target_compile_definitions(Core PRIVATE CORE_INTERNAL_LOGGING # Visible only to Core)Propagation result:
Coresees bothCORE_VERSIONandCORE_INTERNAL_LOGGING.- Any target linking
Core(e.g.,Engine) seesCORE_VERSIONbut notCORE_INTERNAL_LOGGING.
Include Directories
Section titled “Include Directories”Include directories control where the compiler searches for headers (-I / -isystem flags).
add_library(Engine src/engine.cpp)target_include_directories(Engine PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> # Consumers need this $<INSTALL_INTERFACE:include> PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src # Only Engine needs this)Key rule: If a header is #includeD in your public .h file, the directory containing that Header must be a PUBLIC include directory. If a header is only #includeD in .cpp files, it Should be PRIVATE.
Compile Options
Section titled “Compile Options”Compiler flags (-Wall``-O2``/W4) propagate similarly.
add_library(Physics src/physics.cpp)target_compile_options(Physics PRIVATE $<$<CONFIG:Debug>:-fno-omit-frame-pointer> $<$<CONFIG:Release>:-O3 -march=native>)Important: Compiler optimization flags should almost always be PRIVATE. Propagating -march=native to a consumer building on a different CPU would cause illegal instruction errors at Runtime.
Link Libraries
Section titled “Link Libraries”target_link_libraries is the most complex propagation mechanism because it triggers transitive Property propagation from the linked target.
add_library(Network src/network.cpp)target_link_libraries(Network PUBLIC OpenSSL::SSL)target_link_libraries(Network PRIVATE zstd::zstd_static)
add_library(Server src/server.cpp)target_link_libraries(Server PUBLIC Network)Resulting propagation for Server:
- Links against
Network(direct). - Inherently links against
OpenSSL::SSL(transitive, becauseNetworkdeclared itPUBLIC). - Does not link against
zstd::zstd_static(transitive blocked byPRIVATEonNetwork).
How Transitive Dependencies Affect Binary Size
Section titled “How Transitive Dependencies Affect Binary Size”Every PUBLIC or INTERFACE link library adds to the transitive closure of dependencies that the Final executable must link against. This has direct consequences for binary size.
Static Linking Amplification
Section titled “Static Linking Amplification”In a statically linked application, the transitive closure determines which object code is included In the final binary.
App (static) +-- Server | +-- Network (PUBLIC) | | +-- OpenSSL::SSL (PUBLIC) <- ~2MB | | +-- zstd (PRIVATE) <- ~500KB (NOT linked into App) | +-- Logger (PUBLIC) | +-- spdlog (PUBLIC) <- ~200KB +-- Framework +-- Core (PUBLIC) +-- fmt (PUBLIC) <- ~150KBTotal binary size contribution from transitive deps: ~2.35MB (OpenSSL dominates).
If OpenSSL::SSL were changed to PRIVATE on NetworkThe binary would shrink by ~2MB, but Server could no longer expose OpenSSL types in its public headers.
Measuring Transitive Dependency Size
Section titled “Measuring Transitive Dependency Size”Use nm or size to inspect the contribution of each library:
# Show symbol sizes in the final binarynm --size-sort -S build/app | tail -20
# Show section sizessize build/app# text data bss dec hex filename# 1234567 89012 34567 1358146 14bc82 appShared Linking: A Different Trade-off
Section titled “Shared Linking: A Different Trade-off”With shared linking, transitive dependencies do not increase the executable’s size (they are in .so/.dll files). However, they increase the runtime dependency count, which affects Deployment complexity.
# List shared library dependencies of a binaryldd build/app# linux-vdso.so.1# libssl.so.3 => /usr/lib/libssl.so.3# libcrypto.so.3 => /usr/lib/libcrypto.so.3# libstdc++.so.6 => /usr/lib/libstdc++.so.6# libc.so.6 => /usr/lib/libc.so.6Each .so listed is a transitive dependency that must be present on the deployment target.
Example Walkthrough: A Complete Dependency Chain
Section titled “Example Walkthrough: A Complete Dependency Chain”Consider a realistic project with a multi-level dependency chain.
my-app +-- http-client (our library) +-- libcurl (system/vcpkg) | +-- OpenSSL (transitive from libcurl) | +-- zlib (transitive from libcurl) +-- nlohmann-json (direct)Step-by-Step Propagation
Section titled “Step-by-Step Propagation”File: http-client/CMakeLists.txt
add_library(http-client src/http_client.cpp)
target_include_directories(http-client PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> $<INSTALL_INTERFACE:include> PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/detail)
target_link_libraries(http-client PUBLIC nlohmann_json::nlohmann_json # Exposed in http_client.h PRIVATE CURL::libcurl # Used only in .cpp)File: app/CMakeLists.txt
add_executable(my-app src/main.cpp)target_link_libraries(my-app PRIVATE http-client)What my-app Sees
Section titled “What my-app Sees”| Property | Propagated to my-app? | Reason |
|---|---|---|
http-client include dir | Yes | PUBLIC on http-client |
nlohmann_json::nlohmann_json | Yes | PUBLIC on http-client |
nlohmann_json include dir | Yes | Transitive via PUBLIC link |
CURL::libcurl | No | PRIVATE on http-client |
OpenSSL | No | PRIVATE on libcurl (if set correctly by vcpkg) |
zlib | No | PRIVATE on libcurl (if set correctly by vcpkg) |
Result: my-app can include <nlohmann/json.hpp> and <http_client.h> but cannot directly Include <curl/curl.h>. If my-app needs curl directly, it must add CURL::libcurl to its own target_link_libraries.
Changing the Design
Section titled “Changing the Design”If http-client should expose a curl_easy_setopt wrapper in its public header, the design must Change:
target_link_libraries(http-client PUBLIC nlohmann_json::nlohmann_json CURL::libcurl # Now PUBLIC because http_client.h uses curl types)Now my-app inherits CURL::libcurlAnd with it, the transitive OpenSSL and zlib Dependencies. This is the correct behavior: the compiler needs OpenSSL headers to compile code that Includes http_client.h.
Generator Expressions for Conditional Propagation
Section titled “Generator Expressions for Conditional Propagation”CMake generator expressions allow fine-grained control over when properties propagate.
Platform-Specific Definitions
Section titled “Platform-Specific Definitions”add_library(PlatformIO src/platform_io.cpp)target_compile_definitions(PlatformIO PUBLIC $<$<PLATFORM_ID:Windows>:PLATFORM_WINDOWS> $<$<PLATFORM_ID:Linux>:PLATFORM_LINUX> $<$<PLATFORM_ID:Darwin>:PLATFORM_MACOS>)Configuration-Specific Linking
Section titled “Configuration-Specific Linking”target_link_libraries(http-client PUBLIC nlohmann_json::nlohmann_json PRIVATE CURL::libcurl $<$<CONFIG:Debug>:address-sanitizer> # Only linked in debug builds)Compiler-ID Conditional Options
Section titled “Compiler-ID Conditional Options”add_library(Portable target src/portable.cpp)target_compile_options(Portable PUBLIC $<$<CXX_COMPILER_ID:GNU>:-fno-keep-inline-dllexport> $<$<CXX_COMPILER_ID:Clang>:-fvisibility-inlines-hidden> $<$<CXX_COMPILER_ID:MSVC>:/W4>)Feature Test Conditional Definitions
Section titled “Feature Test Conditional Definitions”target_compile_definitions(Portable PUBLIC $<$<BOOL:${USE_FEATURE_X}>:FEATURE_X_ENABLED>)Interface Link Libraries (Head-Only Forwarding)
Section titled “Interface Link Libraries (Head-Only Forwarding)”When a header-only library wraps another library, use INTERFACE_LINK_LIBRARIES to propagate the Dependency without creating a binary:
add_library(json-wrapper INTERFACE)target_include_directories(json-wrapper INTERFACE include/)target_link_libraries(json-wrapper INTERFACE nlohmann_json::nlohmann_json)
# Consumers link json-wrapper and automatically get nlohmann_jsontarget_link_libraries(my-app PRIVATE json-wrapper)Transitive Usage Requirements and INTERFACE_* Properties
Section titled “Transitive Usage Requirements and INTERFACE_* Properties”Every target_link_libraries call with PUBLIC or INTERFACE scope triggers the propagation of All INTERFACE_* properties from the linked target. The complete list of propagated properties:
| Propagated Property | Set by | Effect on consumer |
|---|---|---|
INTERFACE_INCLUDE_DIRECTORIES | target_include_directories(... PUBLIC) | Adds to consumer’s -I paths |
INTERFACE_COMPILE_DEFINITIONS | target_compile_definitions(... PUBLIC) | Adds to consumer’s -D flags |
INTERFACE_COMPILE_OPTIONS | target_compile_options(... PUBLIC) | Adds to consumer’s compiler flags |
INTERFACE_LINK_LIBRARIES | target_link_libraries(... PUBLIC) | Links consumer against transitive deps |
INTERFACE_LINK_OPTIONS | target_link_options(... PUBLIC) | Adds to consumer’s linker flags |
INTERFACE_LINK_DIRECTORIES | target_link_directories(... PUBLIC) | Adds to consumer’s -L paths |
INTERFACE_COMPILE_FEATURES | target_compile_features(... PUBLIC) | Enables C++ features for consumer |
INTERFACE_POSITION_INDEPENDENT_CODE | set_target_properties(PIC) | Enables PIC for consumer |
This is why a single PUBLIC link can cause a cascade of changes: it propagates not just the link Dependency, but all of the linked target’s own transitive usage requirements.
Common Pitfalls
Section titled “Common Pitfalls”- Overusing PUBLIC: The most common mistake is making every dependency
PUBLIC“just in case.” This bloats the transitive closure, increases compile times (more headers to parse), and creates fragile coupling. Start withPRIVATEand escalate only when needed. - Header-only libraries as PRIVATE: A header-only library linked as
PRIVATEmeans its include directories are not propagated. If any header in the target’s public interface includes the header-only library’s headers, consumers will fail to compile. - Mismatched
INTERFACEandPUBLICin installed packages: When exporting a CMake package viainstall(EXPORT ...)ThePUBLICandINTERFACEproperties must be correctly specified. If a dependency isPUBLICin the build tree but not exported in the install tree, installed consumers will get linker errors. SYSTEMinclude directories: UsingSYSTEMon your own project’s headers (not third-party) can mask legitimate warnings. ReserveSYSTEMfor external dependencies only.- Generator expressions in wrong scope: A generator expression like
$<CONFIG:Debug>in aPUBLICcompile option propagates to consumers. If the consumer has a different configuration, the option may be silently ignored or incorrectly applied. UsePRIVATEfor configuration-specific options. - Circular transitive dependencies: If
AlinksBPUBLIC andBlinksAPUBLIC, CMake detects a cycle and errors. This indicates a design flaw: the two libraries should be merged or the dependency should be made PRIVATE on at least one side. - Forgetting that
target_link_librariespropagates more than just linking. When you writetarget_link_libraries(MyLib PUBLIC OtherLib)You are not just adding a linker dependency. You are also propagating all ofOtherLib’s include directories, compile definitions, compile options, and link options to every consumer ofMyLib. This can cause unexpected side effects like inheriting warning suppression flags or optimization flags from transitive dependencies.
Property Propagation Debugging
Section titled “Property Propagation Debugging”When builds fail due to incorrect property propagation, use the following diagnostic techniques.
Inspecting Target Properties
Section titled “Inspecting Target Properties”# Print all properties of a targetget_target_property(INCLUDE_DIRS MyLib INCLUDE_DIRECTORIES)message(STATUS "MyLib INCLUDE_DIRECTORIES: ${INCLUDE_DIRS}")
get_target_property(INTERFACE_LINK_LIBS MyLib INTERFACE_LINK_LIBRARIES)message(STATUS "MyLib INTERFACE_LINK_LIBRARIES: ${INTERFACE_LINK_LIBS}")
get_target_property(COMPILE_DEFS MyLib COMPILE_DEFINITIONS)message(STATUS "MyLib COMPILE_DEFINITIONS: ${COMPILE_DEFS}")Tracing the Transitive Closure
Section titled “Tracing the Transitive Closure”CMake does not provide a built-in command to trace the full transitive closure of properties, but You can use generator expressions to inspect what a target sees:
# Show the effective link libraries (including transitive)target_link_libraries(MyApp PRIVATE $<$<TARGET_EXISTS:MyLib>:MyLib>)
# Show the include directories that would propagate to MyAppadd_custom_target(debug_props COMMAND ${CMAKE_COMMAND} --install-prefix ${CMAKE_BINARY_DIR}/debug -P ${CMAKE_SOURCE_DIR}/cmake/debug_props.cmake)Common Diagnostic Patterns
Section titled “Common Diagnostic Patterns”- “Cannot find -lfoo” at link time: The library is linked with
PUBLICbut the include directory isPRIVATESo the consumer cannot find the headers. Or the library is linked asPRIVATEbut the consumer’s header includes it. - “Undeclared identifier” in consumer: A
PUBLICinclude directory is missing. The dependency is linked but its include paths are not propagated. - Unexpected compile flags in consumer: A dependency sets
target_compile_options(PUBLIC ...)on a third-party library’s target, which propagates unwanted flags. Fix by wrapping the third-party library in anINTERFACEtarget with only the necessary properties.
Generator Expression Quick Reference
Section titled “Generator Expression Quick Reference”The following table lists commonly used generator expressions for property propagation control.
| Expression | Evaluates to | Use case |
|---|---|---|
$<CONFIG:Debug> | 1 in Debug, 0 otherwise | Configuration-specific options |
$<PLATFORM_ID:Linux> | 1 on Linux, 0 otherwise | Platform-specific options |
$<CXX_COMPILER_ID:Clang> | 1 for Clang, 0 otherwise | Compiler-specific options |
$<BUILD_INTERFACE:...> | Value in build tree only | Build-tree-only include paths |
$<INSTALL_INTERFACE:...> | Value in install tree only | Install-tree-only include paths |
$<TARGET_EXISTS:foo> | 1 if target foo exists | Conditional linking |
$<LINK_ONLY:foo> | Link foo but do not add include dirs | Header-only library separation |
$<STREQUAL:${CMAKE_BUILD_TYPE},Release> | 1 in Release builds | Build-type comparison |
$<BOOL:${ENABLE_FEATURE}> | 1 if option is truthy | Option-based conditional |
$<COMPILE_LANGUAGE:CXX> | 1 for C++ sources only | C++-only compile options |
$<COMPILE_LANGUAGE:C> | 1 for C sources only | C-only compile options |
$<AND:$<CONFIG:Debug>,$<PLATFORM_ID:Linux>> | Logical AND | Complex conditions |
$<OR:$<CONFIG:Debug>,$<CONFIG:RelWithDebInfo>> | Logical OR | Multiple config conditions |
See Also
Section titled “See Also”- Dependency Resolution — How the dependency graph is constructed
- vcpkg — How vcpkg manages property propagation for installed packages
- CPM.cmake — How
add_subdirectorymerges dependency properties - Binary Caching — Binary artifacts and their dependency metadata
Summary
Section titled “Summary”This topic covers the essential concepts and techniques related to property propagation, including key principles and practical applications.
Key concepts include:
- core concepts and definitions
- key principles and frameworks
- practical applications
- common techniques and methods
- evaluation and critical analysis
A thorough understanding of these concepts, combined with regular practice and review, is essential for mastery of this topic.
Worked Examples
Section titled “Worked Examples”Worked examples demonstrating the application of key concepts are covered in the detailed sub-pages linked above.