5 分钟搞懂 CMake 变量:零基础也能学会的构建技巧
一、还在手撸 CMake?Out 啦!
还在手动修改 CMakeLists.txt
? 每次改个路径、换个编译器都要改半天? 那你就 Out 啦!
CMake 变量:让你解放双手,只需要修改一个变量,就能轻松切换编译选项、调整目标平台,甚至是更换编译器!就像给你的代码加上了“遥控器”,想怎么改就怎么改,而且还不容易出错!
5 分钟,从小白到入门: 这篇教程就是你的 CMake 变量入门指南,保证你 5 分钟就能上手,彻底告别手动修改的苦逼日子!
二、什么是 CMake 变量?
CMake 变量就是用来存储信息,并在构建过程中被 CMake 调用的!最简单的理解,CMake 变量就是一个名字,对应着一个值。CMake 在构建项目的时候,会根据这些变量的值来做出相应的操作。
比如可以把项目名称记录在一个名为 PROJECT_NAME
的变量里,然后在构建的时候引用它。
CMake 变量的作用:
-
可以用来存储各种信息,例如:
- 项目信息: 项目名称、版本号。
- 路径信息: 源文件目录、头文件目录、库文件目录。
- 编译选项: 编译标志 (例如
-Wall
,-O2
)、链接选项。
-
用来控制构建过程的各个方面,例如:
- 选择编译器: 指定使用哪个 C++ 编译器 (例如 GCC, Clang)。
- 设置编译选项: 开启或关闭优化、添加调试信息。
- 指定链接库: 告诉链接器要链接哪些库。
- 控制安装目录: 指定程序安装到哪个目录。
-
避免硬编码,提高构建过程的灵活性和可维护性。只需要修改变量的值,就可以改变构建行为,而无需修改大量的代码。
CMake 变量的类型:
类型 | 描述 | 示例 | 常用场景 |
---|---|---|---|
字符串 (STRING) | 一段文本,可以包含任何字符。 | MY_NAME = "张三" , SRC_FILE = "main.cpp" | 存储文本信息,例如文件名、路径、项目名称。 |
列表 (LIST) | 一组字符串,用分号 ; 分隔。 | SRC_FILES = "main.cpp;utils.cpp" | 存储一组文件路径、库名称、编译选项。 |
布尔值 (BOOL) | 真 (TRUE, ON, 1, YES) 或 假 (FALSE, OFF, 0, NO)。 | ENABLE_FEATURE = TRUE | 启用或禁用某个功能、选项。 |
路径 (PATH) | 表示文件或目录的路径。 | INSTALL_DIR = "/usr/local/bin" | 存储文件或目录的路径,方便引用。 |
注意:
- CMake 对大小写不敏感 (变量名除外)。
- CMake 会自动推断变量的类型,也可以显式指定。
- 这只是最常用的几种类型,CMake 还有其他更复杂的类型。
三、CMake 变量的设置
使用 SET()
命令设置变量。语法:
SET(变量名 变量值 [FORCE])
变量名
: 变量的名称 (大小写不敏感,但建议大写)。变量值
: 变量的值,可以是一个字符串、列表或布尔值。FORCE
: (可选) 如果变量已经存在,则强制覆盖其值。
示例:
SET(MY_VARIABLE "Hello CMake") # 设置一个字符串变量
SET(SRC_FILES "main.cpp;utils.cpp") # 设置一个列表变量
SET(ENABLE_FEATURE TRUE) # 设置一个布尔变量
# 设置一个CACHE变量,类型为PATH
SET(INSTALL_PREFIX "/usr/local" CACHE PATH "安装路径")
CMake 变量有不同的作用域,决定了它们在 CMakeLists.txt 文件中的可见性和生命周期。
全局变量:
- 在顶级
CMakeLists.txt
文件中使用SET()
设置的变量,默认情况下是全局变量。 - 全局变量在整个 CMake 项目中都可见 (包括子目录)。
- 全局变量的值可以在任何
CMakeLists.txt
文件中被修改。
函数/目录变量 (局部变量):
- 在函数或宏内部使用
SET(变量名 ...)
设置的变量,作用域仅限于该函数或宏内部。 函数或宏执行完毕后,这些变量就会失效。 - 在
add_subdirectory()
命令创建的子目录中的CMakeLists.txt
文件中,也可以设置只在该目录及其子目录下有效的变量。这些变量不会影响父目录中的同名变量。
重要区别:
- 全局变量影响整个项目。
- 局部变量只影响函数/宏/目录内部。
四、CMake 变量的使用
使用 ${}
可以展开变量的值。这是在 CMake 中获取变量值的标准方法。语法: ${变量名}
示例:
SET(MY_VARIABLE "Hello CMake")
MESSAGE(STATUS "变量 MY_VARIABLE 的值为:${MY_VARIABLE}")
在编译选项中使用变量:
SET(MY_DEFINE "ENABLE_FEATURE")
ADD_DEFINITIONS(-D${MY_DEFINE}) #相当于 -DENABLE_FEATURE
或者,如果想要定义一个字符串值:
SET(MY_STRING_VALUE "My String")
ADD_DEFINITIONS(-DSTRING_VALUE="${MY_STRING_VALUE}") # 相当于 -DSTRING_VALUE="My String"
注意:如果变量包含空格或者特殊字符,使用双引号 "..."
包裹 ${MY_STRING_VALUE}
。
在源文件路径中使用变量:
SET(MY_SOURCE_FILE "main") #存储不带后缀的文件名
ADD_EXECUTABLE(my_program src/${MY_SOURCE_FILE}.cpp)
假如目录结构如下:
MyProject/
├── CMakeLists.txt
└── src/
├── main.cpp
└── utils.cpp
示例,变量在 ADD_EXECUTABLE
或 ADD_LIBRARY
中的使用:
cmake_minimum_required(VERSION 3.10)
project(MyProject)
# 设置源文件列表的变量
SET(SOURCE_FILES
src/main.cpp
src/utils.cpp
)
# 设置库的名称的变量
SET(LIBRARY_NAME "mylibrary")
# 创建可执行文件
ADD_EXECUTABLE(my_program ${SOURCE_FILES})
# 创建静态库
ADD_LIBRARY(${LIBRARY_NAME} STATIC ${SOURCE_FILES})
# 创建动态库 (共享库)
ADD_LIBRARY(${LIBRARY_NAME}_shared SHARED ${SOURCE_FILES})
总结:$ {}
是展开变量值的关键。
五、常见 CMake 变量
项目信息:
变量名 | 描述 | 常见用途 | 备注 |
---|---|---|---|
PROJECT_NAME | 项目的名称,由 project() 命令设置。 | 显示项目名称,用于生成文件名等。 | |
PROJECT_VERSION | 项目的版本号,由 project() 或 SET() 设置。 | 显示版本号,用于生成软件包名称等。 | |
PROJECT_SOURCE_DIR | 顶级 CMakeLists.txt 文件所在的目录的绝对路径(项目根目录)。 | 查找源文件、资源文件等。 | 也可使用 CMAKE_SOURCE_DIR |
PROJECT_BINARY_DIR | 构建目录的绝对路径。CMake 在此目录中生成构建文件。 | 指定生成的可执行文件、库文件的输出位置。 | 也可使用 CMAKE_BINARY_DIR |
构建类型和编译器:
变量名 | 描述 | 常见用途 | 备注 |
---|---|---|---|
CMAKE_BUILD_TYPE | 构建类型(Debug, Release, RelWithDebInfo, MinSizeRel)。 | 根据构建类型设置不同的编译选项。 | 可以在运行 cmake 命令时使用 -DCMAKE_BUILD_TYPE 设置。 |
CMAKE_CXX_COMPILER | C++ 编译器的完整路径。 | 获取编译器信息。 | |
CMAKE_C_COMPILER | C 编译器的完整路径。 | 获取编译器信息。 |
安装路径:
变量名 | 描述 | 常见用途 | 备注 |
---|---|---|---|
CMAKE_INSTALL_PREFIX | 安装路径的根目录,由 install() 命令使用。 | 指定程序安装的根目录。 | 可以在运行 cmake 命令时使用 -DCMAKE_INSTALL_PREFIX 设置。 |
CMAKE_INSTALL_BINDIR | 可执行文件安装到的目录 (相对于 CMAKE_INSTALL_PREFIX )。默认为 bin 。 | 指定可执行文件安装的子目录。 | |
CMAKE_INSTALL_LIBDIR | 库文件安装到的目录 (相对于 CMAKE_INSTALL_PREFIX )。默认为 lib 。 | 指定库文件安装的子目录。 | |
CMAKE_INSTALL_INCLUDEDIR | 头文件安装到的目录 (相对于 CMAKE_INSTALL_PREFIX )。默认为 include 。 | 指定头文件安装的子目录。 |
编译/链接选项:
变量名 | 描述 | 常见用途 | 备注 |
---|---|---|---|
CMAKE_CXX_FLAGS | C++ 编译器的编译选项。 | 添加编译标志,例如 -Wall -Wextra -std=c++11 。 | 一般建议使用 target_compile_options 为特定的目标 (target) 设置编译选项,而不是全局设置。 |
CMAKE_C_FLAGS | C 编译器的编译选项。 | 添加编译标志,例如 -Wall -Wextra 。 | 一般建议使用 target_compile_options 为特定的目标 (target) 设置编译选项,而不是全局设置。 |
CMAKE_EXE_LINKER_FLAGS | 可执行文件的链接器选项。 | 添加链接器标志,例如 -L/path/to/library 。 | 一般建议使用 target_link_libraries 为特定的目标 (target) 设置链接器选项,而不是全局设置。 |
CMAKE_SHARED_LINKER_FLAGS | 共享库的链接器选项。 | 添加链接器标志。 | 一般建议使用 target_link_libraries 为特定的目标 (target) 设置链接器选项,而不是全局设置。 |
系统信息和特性
变量名 | 描述 | 常见用途 | 备注 |
---|---|---|---|
CMAKE_SYSTEM_NAME | 操作系统名称 (例如 Linux, Windows, Darwin)。 | 编写平台相关的代码。 | |
CMAKE_SYSTEM_VERSION | 操作系统版本。 | 编写操作系统版本相关的代码。 | |
CMAKE_SYSTEM_PROCESSOR | 处理器架构 (例如 x86_64, arm64)。 | 编写处理器架构相关的代码。 |
其他有用的变量:
变量名 | 描述 | 常见用途 | 备注 |
---|---|---|---|
CMAKE_CURRENT_SOURCE_DIR | 当前处理的 CMakeLists.txt 文件所在的目录的绝对路径。 | 在子目录中查找文件。 | |
CMAKE_MODULE_PATH | CMake 模块的搜索路径。 | 指定查找自定义 CMake 模块的路径。 | |
BUILD_SHARED_LIBS | 如果设置为 TRUE,则所有库都将构建为共享库。 | 统一构建共享库或静态库。 | 可以在运行 cmake 命令时使用 -DBUILD_SHARED_LIBS=ON 设置。 |
CMAKE_EXPORT_COMPILE_COMMANDS | 如果设置为 TRUE,则 CMake 将生成一个 compile_commands.json 文件,其中包含构建项目所需的所有编译命令。 | 方便代码编辑器和静态分析工具使用。 |
更多内容查阅 CMake 官方文档以获取更完整的变量列表和详细信息:https://cmake.org/documentation/
尽量使用 target_*
系列命令 (例如 target_compile_options
, target_link_libraries
) 为特定的构建目标设置属性,而不是全局设置 CMAKE_*
变量,以避免不必要的副作用。
六、总结
本文深入探讨了 CMake 变量,CMake 变量是 CMake 构建系统中的核心组成部分,它们控制着项目的配置、构建和安装过程。
CMake 变量用于存储各种信息,例如项目名称、版本号、编译器路径、编译选项、安装路径等等。通过实践,能够更好地理解 CMake 变量的作用,并掌握更高级的 CMake 技术。
参考资源:
- CMake 官方文档: https://cmake.org/documentation/ (最权威的参考资料)
- CMake Tutorial: https://cmake.org/cmake/help/latest/guide/tutorial/index.html (CMake 官方提供的入门教程)
- Effective Modern CMake: https://effectivemoderncmake.com/ (介绍现代 CMake 最佳实践)