1 简介与全局配置
官方网址 CMake 是一个用于管理源代码构建的工具。最初,CMake 被设计为各种 Makefile 变体的生成器。如今 CMake 广泛应用于 C 和 C++ 语言,但它也可以用于构建其他语言的源代码。
- 核心逻辑:CMakeLists.txt -> 生成器 (Ninja/Makefile/VS) -> 构建系统。建议使用 3.22...3.26 语法指定版本范围,这能让新版本 CMake 启用新特性,同时保持旧版本兼容。
- 版本管理:cmake_minimum_required(VERSION 3.22) —— 必须置于文件首行。
- 项目属性:project(MyAwesomeProject VERSION 1.0 LANGUAGES C CXX) —— 定义名称、版本和语言。
- 语言标准精细控制:
- CMAKE_CXX_STANDARD:可选 11, 14, 17, 20, 23。
- CMAKE_CXX_EXTENSIONS:建议设为 OFF(禁用 GNU 扩展以确保标准跨平台)。
- CMAKE_CXX_STANDARD_REQUIRED:建议设为 ON(强制编译器支持,不支持则报错)。
2 语法核心与底层逻辑
CMake 不是简单的配置,它是具有变量作用域的脚本语言。
2.1 变量系统
- 普通变量 (Normal Variable):
- 作用域:在当前文件夹及其子文件夹中有效。
- 设置:set(MY_VAR "a" "b") 实际上是 set(MY_VAR "a;b")。
- 缓存变量 (Cache Variable):
- 命令:set(VAR VAL CACHE STRING "doc")。
- 粘性:即便修改 CMakeLists.txt,只要 CMakeCache.txt 还在,它就不会变,除非手动 -D 修改。
- -D 逻辑:命令行参数具有最高优先级,会直接写入缓存。
- 内置变量 (关键路径):
- CMAKE_SOURCE_DIR:工程根目录。
- CMAKE_BINARY_DIR:构建输出根目录。
- CMAKE_CURRENT_SOURCE_DIR:当前处理的 CMakeLists.txt 所在目录。
- CMAKE_CURRENT_BINARY_DIR:当前对应的构建输出目录。
2.2 列表操作——伪数组
列表本质是带分号的字符串。使用 list() 命令操作:
- list(LENGTH <out_var>):获取元素数量。
- list(APPEND <elements...>):追加元素。
- list(INSERT ):在指定位置插入。
- list(REMOVE_ITEM ):根据值删除。
- list(REVERSE ):反转列表。
- 循环遍历:
foreach(item IN LISTS my_list)
message(STATUS "Processing: ${item}")
endforeach()
2.3 流程控制与条件判定 (if)
if(...) 会经历两阶段流程:
- 常量检查:匹配 ON, TRUE, YES, 1 (真) 或 OFF, FALSE, NO, 0, 空字符串, 以-NOTFOUND结尾 (假)。
- 变量解引用:若非逻辑常量,则假设它是变量名并自动读取其值进行判断。
常用指令:
- STREQUAL:字符串全等比较。
- DEFINED:变量是否存在(不关心值)。
- MATCHES:正则匹配。
- VERSION_LESS / VERSION_EQUAL:专门用于版本号对齐。
- NOT / AND / OR:组合逻辑。
2.4 函数与宏
- function(name [arg1...]):创建独立作用域。
- 返回值:新版本使用 return(PROPAGATE var);旧版本使用 set(var val PARENT_SCOPE)。
- macro(name [arg1...]):文本替换,不创建作用域。内部命令直接在调用处执行。
- 可变参数:使用 ARGN 获取所有未命名的参数。
2.5 日志系统
使用message方法进,格式message(level_key content),日志关键字如下:
- FATAL_ERROR:致命错误,立即停止配置。
- SEND_ERROR:配置错误,继续脚本运行但不生成构建系统。
- WARNING / AUTHOR_WARNING:警告信息。
- STATUS:带 -- 前缀的常规信息。
- VERBOSE:详细模式(需开启特定开关才显示)。
3 构建与生成
3.1 命令行参数
- -S :指定源码目录。
- -B :指定构建目录。
- --build :触发实际编译。
- --config :多配置生成器(如 VS)指定 Debug 或 Release。
- --clean-first:构建前清理旧产物。
- --install :执行安装逻辑。
- -- :递参数给底层的 Ninja 或 Make(如开启 8 核并行):-- -j8
3.2 预设
通过 CMakePresets.json 将复杂的命令行参数持久化,属性以及意义如下
- version:建议 3 以上。
- configurePresets:预定义 generator, binaryDir, cacheVariables。
- buildPresets:预定义 targets, configuration (Debug/Release)。
- CMakeUserPresets.json:本地个人配置,不应提交版本控制。
3.3 脚本模式
cmake -P script.cmake:不执行配置生成流程,仅顺序执行 CMake 逻辑。不支持 project() 和 Target 相关的操作。
4 目标化构建指令
这是现代 CMake 的精髓,避免使用全局命令。
4.1 定义目标 (Add Target
- add_executable(name sources...):可执行程序。
- add_library(name [TYPE] sources...):
- STATIC:静态库(包含在目标中)。
- SHARED:共享库(运行时加载)。
- MODULE:插件库(不支持链接,仅支持动态加载)。
- OBJECT:对象库(仅编译 .o,不打包,用 $<TARGET_OBJECTS:name> 引用)。
- INTERFACE:接口库(无源码,仅传递配置)。
- IMPORTED:导入已有的外部库文件。
4.2 配置目标属性
这些指令支持作用域
- PRIVATE:编译本目标时使用,不传递给依赖者。用于 .cpp 引用的私有头文件。
- INTERFACE:编译本目标时不使用,仅传递给依赖者。用于 Header-only 库。
- PUBLIC:双向有效。用于 .h 中暴露给外部调用的接口定义。
指令有:
- target_sources():关联源文件。
- target_include_directories():头文件搜索路径。
- target_link_libraries():链接依赖。
- target_compile_definitions():定义宏(如 -DDEBUG)。
- target_compile_options():设置特定编译器参数(如警告等级)。
- target_link_options():设置链接器参数。
- target_precompile_headers():优化构建速度,预编译头文件。
- 高级控制:set_target_properties() 设置 OUTPUT_NAME 或版本号。
4.3 FILE_SET (CMake 3.23+):取代了繁琐的 PUBLIC_HEADER 属性。 参数:
- BASE_DIRS:指定相对路径的基准(如 include)。
- FILES:列出所有头文件。
- 优势:安装时会自动保持目录结构,并自动设置 target_include_directories。
5 安装
install() 命令将构建产物移动到标准化目录。产物主要有:
- ARCHIVE:静态库/导入库(.a / .lib),建议目录${CMAKE_INSTALL_LIBDIR}
- LIBRARY:共享库(.so / .dylib),建议目录 ${CMAKE_INSTALL_LIBDIR}
- RUNTIME:可执行程序 / DLL(app / .dll),建议路径${CMAKE_INSTALL_BINDIR}
- PUBLIC_HEADER:公开头文件(.h)建议路径${CMAKE_INSTALL_INCLUDEDIR}
GNU 规范路径: include(GNUInstallDirs) 后,可使用 ${CMAKE_INSTALL_BINDIR} 等变量,自动适配 /usr/bin 或 /usr/local/bin。
6 依赖查找
- find_file() / find_path() / find_library() / find_program():查找具体的个体。
- find_package(Name [version] [REQUIRED] [COMPONENTS...]):
- 结果:成功后设置变量 <Name>_FOUND。
- REQUIRED:找不到则终止配置。
- QUIET:不显示非致命信息。
7 生成器表达式
格式:($<...>),在配置阶段之后、构建阶段之前求值。
- 条件选择:$<IF:condition,true_val,false_val>。
- 配置检查:$<CONFIG:Debug>。
- 接口区分:
- $<BUILD_INTERFACE:...>:仅在源码目录下构建时生效。
- $<INSTALL_INTERFACE:...>:仅在安装到目标环境后生效。
- 路径获取:$<TARGET_FILE:target>。
8 自动化与代码生成
分为两步走,不会主动运行,必须被目标依赖;
- add_custom_command:
- OUTPUT:必须明确生成的文件名。
- DEPENDS:文件变化时才会重新触发。
- COMMAND:调用 python, sh 或 ${CMAKE_COMMAND} -E。
- add_custom_target:一个“伪目标”,总是被认为已过期。常用于跑单元测试、生成文档或触发代码混淆。
CMake 内部模式 使用 ${CMAKE_COMMAND} -E 调用内置跨平台命令: copy, make_directory, echo, touch, remove。保证了 Windows 和 Linux 下构建脚本的通用性。
如果在此文章中您有所收获,请给作者一个鼓励,点个赞,谢谢支持
技术变化都很快,但基础技术、理论知识永远都是那些;作者希望在余后的生活中,对常用技术点进行基础知识分享;如果你觉得文章写的不错,请给予关注和点赞;如果文章存在错误,也请多多指教!