They're the correct way to express per-configuration and per-compiler settings that also work in multi-config builds.
Generator expressions look like $<...> and are evaluated during generation, after configuration, separately for each target and each configuration. They're needed because some facts aren't known while CMakeLists.txt runs: in a multi-config build, the configuration isn't chosen until build time, and a target's final file path isn't fixed until generation.
Building blocks:
- Conditions that yield
1or0:$<CONFIG:Debug>,$<CXX_COMPILER_ID:MSVC>,$<PLATFORM_ID:Windows>,$<BOOL:${OPT}>, combined with$<AND:...>,$<OR:...>,$<NOT:...>. $<cond:value>yieldsvalueifcondis1, otherwise nothing;$<IF:cond,a,b>picks one.- Information:
$<TARGET_FILE:app>the built file's path,$<TARGET_FILE_DIR:app>,$<TARGET_PROPERTY:tgt,prop>. $<BUILD_INTERFACE:...>and$<INSTALL_INTERFACE:...>give different values when a target is used from the build tree or after installation.
Many conditions accept lists: $<CONFIG:Debug,RelWithDebInfo> (3.19+) and $<CXX_COMPILER_ID:GNU,Clang>.
Because evaluation happens later, message() prints the literal $<...> text. To inspect results, use file(GENERATE OUTPUT dbg.txt CONTENT "..."). Keep expressions small; deeply nested ones become unreadable fast, and a helper variable holding a part helps.
Example
add_executable(app main.cpp)
target_compile_definitions(app PRIVATE
$<$<CONFIG:Debug>:APP_DEBUG=1> # only in Debug builds
APP_PLATFORM="$<PLATFORM_ID>") # "Linux", "Windows", ...
set(gcc_like "$<CXX_COMPILER_ID:GNU,Clang,AppleClang>")
target_compile_options(app PRIVATE
"$<${gcc_like}:-Wall;-Wextra>" # quoted: no spaces inside a genex
$<$<CXX_COMPILER_ID:MSVC>:/W4>
$<$<AND:${gcc_like},$<CONFIG:Debug>>:-fsanitize=address>)
target_link_options(app PRIVATE
$<$<AND:${gcc_like},$<CONFIG:Debug>>:-fsanitize=address>)
# copy a data file next to the built executable, wherever it ends up
add_custom_command(TARGET app POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${CMAKE_CURRENT_SOURCE_DIR}/config.json $<TARGET_FILE_DIR:app>)
file(GENERATE OUTPUT genex-$<CONFIG>.txt CONTENT "file: $<TARGET_FILE:app>\n")Remember
- $<cond:value> inserts value when cond is true for this target and configuration
- Use $<CONFIG:...> instead of if(CMAKE_BUILD_TYPE) so multi-config works
- message() can't show them; use file(GENERATE) to debug
Watch out for
- Putting a space-separated list inside a genex without quoting, which splits the expression into broken pieces.