Generator Expressions是CMake中用于延迟求值的语法,解决配置阶段无法确定平台/编译器信息的问题;所有条件必须用$<...>包裹,仅支持预定义逻辑函数,不能嵌套普通变量展开。

什么是Generator Expressions,为什么不能直接用if()?
因为CMake的if()在配置阶段(configure time)就求值,而目标平台、编译器这些信息可能要到生成构建系统(generate time)才确定。比如你用cmake -G "Ninja Multi-Config"生成VS解决方案时,MSVC变量在configure阶段是OFF,但实际构建x64平台时又需要启用/EHsc。Generator Expressions就是为解决这种“延迟求值”问题设计的——它不立即计算,而是把表达式原样写进生成的构建文件(如build.ninja或.vcxproj),由构建工具在真正编译时解析。
常用跨平台编译选项的Generator Expressions写法
核心原则:所有条件判断必须包裹在$<...>中,且只接受预定义的逻辑函数(如$<OR:...>、$<AND:...>、$<NOT:...>),不能嵌套普通CMake变量展开。
-
启用C++17标准(所有平台):
target_compile_features(my_target PRIVATE cxx_std_17)或更稳妥地用target_compile_options(my_target PRIVATE $<IF:$<COMPILE_LANGUAGE:CXX>,-std=c++17,/std:c++17>) -
Windows下加
/EHsc,Linux/macOS加-fexceptions:target_compile_options(my_target PRIVATE $<IF:$<STREQUAL:$<TARGET_PROPERTY:PLATFORM_ID>,Windows>,/EHsc,-fexceptions>) -
仅对Clang启用
-Wno-unused-parameter,GCC/MSVC跳过:target_compile_options(my_target PRIVATE $<IF:$<CXX_COMPILER_ID:Clang>,-Wno-unused-parameter,"">) -
Debug模式加
-O0 -g,Release加-O3 -DNDEBUG:target_compile_options(my_target PRIVATE $<CONFIG:Debug>:-O0 -g $<CONFIG:Release>:-O3 -DNDEBUG)
target_compile_options和target_compile_definitions的坑
这两个命令接受Generator Expressions,但有严格限制:
-
target_compile_definitions()里不能直接写$<CONFIG:...>,必须用$<CONFIG:...>:DEF=VALUE语法,例如:target_compile_definitions(my_target PRIVATE $<CONFIG:Debug>:DEBUG=1 $<CONFIG:Release>:NDEBUG=1) - 多个选项拼接时,空格会被截断,推荐每个选项单独调用
target_compile_options(),或用$<SPACE>显式插入空格(极少用) -
$<CXX_COMPILER_ID:...>匹配的是CMAKE_CXX_COMPILER_ID值,常见值为GNU、Clang、AppleClang、MSVC,注意AppleClang≠Clang,需分开写 - 路径相关选项(如
-I)慎用Generator Expressions,容易因相对路径解析失败;优先用target_include_directories()+$<BUILD_INTERFACE:...>
验证Generator Expressions是否生效
最可靠的方式是生成构建文件后直接查看内容,而不是依赖message()——因为message()在configure阶段执行,看不到最终展开结果。
立即学习“C++免费学习笔记(深入)”;
- 对Ninja生成器:
cat build.ninja | grep -A5 'my_target.*cxx'
- 对Visual Studio:
grep -n "/EHsc\|/std" my_target.vcxproj
- 用
cmake --build . --verbose看实际调用的编译命令(部分生成器支持)
最容易被忽略的是PLATFORM_ID和CMAKE_SYSTEM_NAME的区别:PLATFORM_ID用于$<PLATFORM_ID:...>,值为Linux/Windows/Darwin;而CMAKE_SYSTEM_NAME是CMake内部变量,不能在Generator Expressions里直接引用。











