当前位置: 首页 > news >正文

CMake进阶:生成器表达式

目录

1.简介

2.基本语法和类型

2.1.基础条件表达式(最常用)

2.2.字符串操作表达式

2.3.目标相关表达式

2.4.输出控制表达式(基础构建块)

3.调试生成器表达式

4.实战示例:跨场景综合应用

5.实用代码片段

6.注意事项

7.总结

相关链接


1.简介

        CMake 生成器表达式(Generator Expressions)是 CMake 3.0+ 引入的高级条件表达式语法,专门用于在生成构建系统(如 Makefile、VS 项目、Xcode 项目)时动态计算值,而非在配置阶段(cmake 命令运行时)。其核心价值是:解决 “多配置构建”(如 VS 同时支持 Debug/Release)、“跨平台适配”、“目标类型区分” 等场景下的动态配置需求,让 CMake 脚本更灵活地适配不同构建环境。

        生成器表达式与普通 CMake 条件判断(如 if(...))的本质区别在于:

  • 普通 if:在配置阶段(运行 cmake .. 时)求值,结果固定(如只能为当前指定的 CMAKE_BUILD_TYPE 生成配置)。
  • 生成器表达式:在生成构建系统时(如生成 VS 项目文件 .vcxproj 时)求值,可根据构建系统的特性(如多配置、目标类型)动态生成不同结果。

        生成器表达式(Generator Expressions)是 CMake 在生成阶段(而非配置阶段)求值的特殊表达式,主要用于:

  1. 多配置构建(如 VS、Xcode)中,为 Debug/Release 配置设置不同的宏定义、编译选项;
  2. 跨平台项目中,为 Windows/Linux/macOS 链接不同的系统库;
  3. 根据目标类型(可执行文件 / 静态库 / 动态库)设置不同的属性;
  4. 动态获取目标文件路径、依赖库路径等(在配置阶段无法预知的路径)。

2.基本语法和类型

基本语法

$<...>  # 生成器表达式的基本格式

内部包含 “条件” 和 “结果”,核心可分为条件表达式字符串操作表达式目标相关表达式三大类。

2.1.基础条件表达式(最常用)

通过判断构建配置、平台、目标类型等条件,返回不同的结果,格式为 $<条件:真结果;假结果>(若省略假结果,条件为假时返回空)。

(1)判断构建配置(CONFIG

用于多配置构建系统(如 VS),区分 Debug/Release/RelWithDebInfo 等配置:

# 格式:$<CONFIG:配置名:真结果> 或 $<CONFIG:配置名,配置名...:真结果>
$<CONFIG:Debug:DEBUG_MODE>       # 若为 Debug 配置,返回 "DEBUG_MODE"
$<CONFIG:Release,NDebug:NDEBUG>  # 若为 Release 或 NDebug 配置,返回 "NDEBUG"

示例:为不同配置添加宏定义

target_compile_definitions(myapp PRIVATE$<CONFIG:Debug>:ENABLE_LOG  # Debug 模式启用日志宏$<CONFIG:Release>:DISABLE_assert  # Release 模式禁用断言宏
)

(2)判断平台(PLATFORM_ID

根据目标平台(Windows/Linux/macOS 等)返回不同结果,PLATFORM_ID 的值与 CMAKE_SYSTEM_NAME 一致:

# 格式:$<PLATFORM_ID:平台名:真结果>
$<PLATFORM_ID:Windows:ws2_32.lib>  # Windows 平台返回 "ws2_32.lib"
$<PLATFORM_ID:Linux:pthread>       # Linux 平台返回 "pthread"

示例:跨平台链接系统库

target_link_libraries(myapp PRIVATE$<PLATFORM_ID:Windows:ws2_32.lib>  # Windows 链接 Winsock2 库$<PLATFORM_ID:Linux:pthread>       # Linux 链接 pthread 库$<PLATFORM_ID:Darwin:CoreFoundation.framework>  # macOS 链接 CoreFoundation
)

(3)判断目标类型(TARGET_TYPE

根据目标是可执行文件、静态库还是动态库返回不同结果:

# 格式:$<TARGET_TYPE:目标类型:真结果>
$<TARGET_TYPE:EXECUTABLE:EXE_OPTION>  # 可执行目标返回 "EXE_OPTION"
$<TARGET_TYPE:SHARED_LIBRARY:SHARED_OPTION>  # 动态库目标返回 "SHARED_OPTION"

示例:为不同类型目标设置编译选项

add_library(mylib SHARED src/mylib.cpp)
target_compile_options(mylib PRIVATE$<TARGET_TYPE:SHARED_LIBRARY:-fPIC>  # 动态库强制添加 -fPIC(Linux)
)

(4)逻辑运算(BOOL/AND/OR/NOT

支持逻辑判断组合,实现复杂条件:

$<BOOL:1>               # 常量 true(返回 1)
$<BOOL:0>               # 常量 false(返回 0)
$<AND:$<CONFIG:Debug>,$<PLATFORM_ID:Windows>>  # Debug 且 Windows 为真
$<OR:$<PLATFORM_ID:Linux>,$<PLATFORM_ID:Darwin>>  # Linux 或 macOS 为真
$<NOT:$<CONFIG:Release>>  # 非 Release 配置为真

示例:Debug 模式且 Windows 平台启用特定宏

target_compile_definitions(myapp PRIVATE$<AND:$<CONFIG:Debug>,$<PLATFORM_ID:Windows>>:WINDOWS_DEBUG
)

2.2.字符串操作表达式

用于字符串转换、拼接、提取等,适合处理路径、名称等字符串。

(1)大小写转换

$<LOWER_CASE:Debug>  # 转为小写:"debug"
$<UPPER_CASE:linux>  # 转为大写:"LINUX"

示例:根据配置生成小写目录名

set_target_properties(myapp PROPERTIESRUNTIME_OUTPUT_DIRECTORY "$<LOWER_CASE:$<CONFIG>>/bin"  # debug/bin 或 release/bin
)

(2)字符串拼接(JOIN

将列表元素用指定分隔符拼接:

$<JOIN:${MY_LIST},;>  # 用分号拼接 MY_LIST 中的元素(CMake 列表默认用分号分隔)

示例:拼接源文件列表为编译选项

set(SRCS "a.cpp;b.cpp")
target_compile_definitions(myapp PRIVATESRC_FILES="$<JOIN:${SRCS},;>"  # 定义 SRC_FILES 宏为 "a.cpp;b.cpp"
)

(3)字符串替换(REPLACE

替换字符串中的子串:

$<REPLACE:abc,ab,x>  # 将 "abc" 中的 "ab" 替换为 "x",结果为 "xc"

示例:修正路径分隔符(Windows 下将 / 转为 \

set(PATH "src/ui")
set(WIN_PATH "$<REPLACE:${PATH},/,\\>")  # Windows 下结果为 "src\ui"

2.3.目标相关表达式

动态获取目标的文件路径、依赖等信息(配置阶段无法预知,需在生成构建系统时确定)。

(1)目标文件路径(TARGET_FILE

   获取目标最终生成的文件路径(如可执行文件、库文件的完整路径):

$<TARGET_FILE:myapp>  # 可执行目标 myapp 的完整路径(如 build/Debug/myapp.exe)

示例:安装时复制目标文件到指定目录

install(FILES $<TARGET_FILE:myapp>DESTINATION bin
)

(2)目标链接的库(TARGET_LINK_LIBRARIES

   获取目标链接的所有库(适合传递依赖):

$<TARGET_LINK_LIBRARIES:myapp>  # 目标 myapp 链接的所有库列表

(3)目标包含目录(TARGET_INCLUDE_DIRECTORIES

   获取目标的包含目录列表:

$<TARGET_INCLUDE_DIRECTORIES:myapp>  # 目标 myapp 的所有包含目录

2.4.输出控制表达式(基础构建块)

最基础的生成器表达式,控制是否输出内容,其他复杂表达式基于此实现:

  • $<0:...>:条件为假,不输出任何内容;
  • $<1:...>:条件为真,输出 ... 的内容;
  • $<IF:条件,真结果,假结果>:CMake 3.8+,条件为真输出真结果,否则输出假结果。

示例:基础条件输出

$<1:DEBUG>  # 总是输出 "DEBUG"
$<0:RELEASE>  # 总是不输出
$<IF:$<CONFIG:Debug>,debug_mode,release_mode>  # Debug 输出 debug_mode,否则 release_mode

3.调试生成器表达式

使用 message() 调试

# 注意:message() 在配置阶段执行,无法直接打印生成器表达式
# 但可以通过 file(GENERATE) 间接调试# 方法1:使用 file(GENERATE)
file(GENERATE OUTPUT debug_info.txtCONTENT "Target file: $<TARGET_FILE:my_app>"
)# 方法2:创建辅助目标
add_custom_target(debug_info ALLCOMMAND ${CMAKE_COMMAND} -E echo "Build type: $<CONFIG>""Compiler: $<CXX_COMPILER_ID>""Target file: $<TARGET_FILE:my_app>"COMMENT "Printing debug information"
)

常见调试技巧

# 检查生成器表达式是否被正确解析
set(debug_output)
if(debug_output)target_compile_definitions(my_app PRIVATE$<$<CONFIG:Debug>:DEBUG_BUILD>)
endif()# 使用变量包装复杂表达式
set(IS_DEBUG $<CONFIG:Debug>)
set(IS_WINDOWS $<PLATFORM_ID:Windows>)
set(DEBUG_ON_WINDOWS $<AND:${IS_DEBUG},${IS_WINDOWS}>)target_compile_definitions(my_app PRIVATE$<${DEBUG_ON_WINDOWS}:WINDOWS_DEBUG>
)

4.实战示例:跨场景综合应用

结合 “VS 工程转 CMake”“跨平台构建” 等场景,演示生成器表达式的实际价值。

示例 1:多配置输出目录

为 Debug/Release 配置设置不同的输出目录(VS 多配置构建):

add_executable(myapp src/main.cpp)# 输出目录:build/Debug/bin 或 build/Release/bin(自动适应配置)
set_target_properties(myapp PROPERTIESRUNTIME_OUTPUT_DIRECTORY "$<CONFIG>/bin"  # 利用 $<CONFIG> 动态获取配置名LIBRARY_OUTPUT_DIRECTORY "$<CONFIG>/lib"
)

示例 2:跨平台编译选项与宏定义

根据平台和配置设置编译选项(如 Windows 禁用安全警告,Linux 启用额外优化):

target_compile_options(myapp PRIVATE# Windows 平台:禁用 CRT 安全警告(_CRT_SECURE_NO_WARNINGS)$<PLATFORM_ID:Windows>:/wd4996# Release 配置:启用 O2 优化$<CONFIG:Release>:-O2
)target_compile_definitions(myapp PRIVATE# Windows 平台定义 PLATFORM_WIN,其他平台定义 PLATFORM_UNIX$<PLATFORM_ID:Windows>:PLATFORM_WIN$<NOT:$<PLATFORM_ID:Windows>>:PLATFORM_UNIX
)

示例 3:根据目标类型设置属性

为静态库和动态库设置不同的版本号规则:

# 静态库
add_library(mystatic STATIC src/lib.cpp)
set_target_properties(mystatic PROPERTIESVERSION $<TARGET_TYPE:STATIC_LIBRARY:1.0-static>  # 静态库版本 1.0-static
)# 动态库
add_library(mydynamic SHARED src/lib.cpp)
set_target_properties(mydynamic PROPERTIESVERSION $<TARGET_TYPE:SHARED_LIBRARY:1.0-shared>  # 动态库版本 1.0-shared
)

5.实用代码片段

跨平台配置模板

# 编译器特定标志
set(GCC_LIKE_FLAGS "-Wall -Wextra -pedantic")
set(MSVC_FLAGS "/W4 /WX")target_compile_options(my_target PRIVATE$<$<CXX_COMPILER_ID:GNU>:${GCC_LIKE_FLAGS}>$<$<CXX_COMPILER_ID:Clang>:${GCC_LIKE_FLAGS}>$<$<CXX_COMPILER_ID:MSVC>:${MSVC_FLAGS}>
)# 配置特定优化
set(DEBUG_OPTIMIZATION "-O0 -g")
set(RELEASE_OPTIMIZATION "-O3 -DNDEBUG")target_compile_options(my_target PRIVATE$<$<CONFIG:Debug>:${DEBUG_OPTIMIZATION}>$<$<CONFIG:Release>:${RELEASE_OPTIMIZATION}>$<$<CONFIG:RelWithDebInfo>:"-O2 -g">$<$<CONFIG:MinSizeRel>:"-Os -DNDEBUG">
)

条件化依赖管理

# 根据特性条件化添加依赖
if(ENABLE_OPENMP)find_package(OpenMP REQUIRED)target_link_libraries(my_app PRIVATE OpenMP::OpenMP_CXX)
endif()# 或者使用生成器表达式(如果依赖项本身支持)
target_compile_definitions(my_app PRIVATE$<$<TARGET_EXISTS:OpenMP::OpenMP_CXX>:HAVE_OPENMP=1>
)# 平台特定的依赖查找
find_package(Threads REQUIRED)
target_link_libraries(my_app PRIVATE Threads::Threads)if(UNIX AND NOT APPLE)find_library(RT_LIBRARY rt)if(RT_LIBRARY)target_link_libraries(my_app PRIVATE ${RT_LIBRARY})endif()
endif()

6.注意事项

1.版本兼容性

部分生成器表达式需要较高 CMake 版本(如 $<IF> 需 3.8+,$<SELECT> 需 3.19+),使用时需在 cmake_minimum_required 中指定足够高的版本(如 cmake_minimum_required(VERSION 3.15))。

2.适用范围

生成器表达式仅能在支持的 CMake 命令中使用,主要包括:

  • 目标属性设置:set_target_properties(如 RUNTIME_OUTPUT_DIRECTORY);
  • 编译 / 链接选项:target_compile_optionstarget_link_librariestarget_compile_definitions
  • 安装命令:install(FILES ...)install(TARGETS ...)。普通变量赋值(如 set(MY_VAR ...))中使用生成器表达式会被视为普通字符串,不会生效。

3.多配置 vs 单配置生成器

  • 多配置生成器(VS、Xcode):支持在一个构建目录中生成多个配置,生成器表达式可正常区分 Debug/Release
  • 单配置生成器(Makefile、Ninja):每次构建只能指定一个配置(通过 CMAKE_BUILD_TYPE),生成器表达式会根据当前配置求值。

4.调试生成器表达式

若表达式结果不符合预期,可通过 message 命令输出表达式本身(而非求值结果,因配置阶段无法求值),或在生成的构建文件(如 VS 的 .vcxproj)中搜索表达式展开后的结果进行排查:

message(STATUS "配置相关表达式:$<CONFIG:Debug:DEBUG>")  # 输出表达式字符串,而非求值结果

7.总结

CMake 生成器表达式是解决 “动态构建配置” 的核心工具,其价值在于:

  • 支持多配置构建系统(如 VS)中为不同配置动态设置属性;
  • 简化跨平台项目的条件判断(无需大量 if(WIN32) 等配置阶段判断);
  • 动态获取目标相关路径(配置阶段无法预知的信息)。

掌握生成器表达式后,可大幅提升 CMake 脚本的灵活性和跨平台兼容性,尤其适合复杂项目(如包含多目标、多配置、多平台需求的工程)。

相关链接

  • CMake 官网 https://cmake.org/
  • CMake 官方文档:https://cmake.org/cmake/help/latest/guide/tutorial/index.html
  • CMake 源码:https://github.com/Kitware/CMake
  • CMake 源码:https://gitlab.kitware.com/cmake/cmakeku
  • 中文版基础介绍: https://www.hahack.com/codes/cmake/
  • wiki: https://gitlab.kitware.com/cmake/community/-/wikis/Home
  • Modern CMake 简体中文版: https://modern-cmake-cn.github.io/Modern-CMake-zh_CN/
http://www.dtcms.com/a/512645.html

相关文章:

  • 从 Vite 到现代构建范式:一个关于“快”的技术哲学
  • 2025世界智能制造大会(南京)将带来那些新技术与新体验?
  • 杭州网站建设杭州上海哪个网站好用
  • 做网站的文案是指网站怎么做才能赚钱吗
  • 完善企业能力等级评价体系 构建高质量发展新标尺
  • Vue2 封装二维码弹窗组件
  • 哪里有做网站较好的公司龙华做网站怎么样
  • 在1688做公司网站前端开发语言的特点是
  • 少儿教育网站建设价格免费看电视剧的网站在线观看
  • (四)从零学 React Props:数据传递 + 实战案例 + 避坑指南
  • 上传自己做的网站吗关键词优化百家号
  • 连云港做网站公司校园网的规划与设计
  • DeepSeek-OCR:视觉压缩的革命性突破——当OCR遇上LLM的“降维打击“
  • 盐城网站开发市场做网站怎么去工信部缴费
  • ps做游戏网站伊宁网站建设优化
  • 【高等数学笔记-极限(7)】函数连续
  • !for_each_process 命令详解
  • 住房与城乡建设网站引流客户的最快方法是什么
  • 江西网站开发的公司wordpress插件看访问者数量
  • 10.21
  • SharedFlow和StateFlow的方案选择-屏幕旋转设计
  • ps做素材下载网站有哪些wordpress用什么框架
  • 网站建设除凡科外还有哪些wordpress主题 添加自定义菜单
  • 怎么做视频网站教程什么是网络营销报价
  • DMA直接存储器访问
  • 电话销售怎么做 网站临沂森拓网络科技有限公司
  • 在网上招标做兼职的网站建设网站装配式建筑楼房
  • 360°客户视图:MDM/CDP如何驱动实时个性化服务?
  • JavaScript 与 React 工程化实践对比
  • 【完整源码+数据集+部署教程】【天线&其他】建筑损毁程度检测评估系统源码&数据集全套:改进yolo11-Parc