1,基础语法
CMakeLists.txt 目录组织文件;
*.cmake 脚本文件
运行:
$ cmake -P xxx.cmake
*.cmake 模块文件
include 命令来引用 模块文件。
自定义模块;
cmake 预制模块;
单行注释
# com
括号注释
#[[
.... ]]
#[==[
.... ]==]
程序构成
cmake 程序由命令调用构成;
控制结构也是命令;
变量:
分类:
普通变量,${var}
缓存变量,$CACHE{var} ${var}
环境变量,$ENV{var}
赋值兼定义:
set(var XXXXX)
使用:
${var}
可嵌套
2, POLICY
if(POLICY CMP0068)
cmake_policy(SET CMP0068 NEW)
set(CMAKE_BUILD_WITH_INSTALL_NAME_DIR ON)
endif()
功能:控制 macOS 动态库的 INSTALL_NAME_DIR 行为。
NEW 行为:在构建时(BUILD)使用 INSTALL_NAME_DIR 设置动态库的 @rpath 或安装路径,而不是默认的构建目录。
相关变量:CMAKE_BUILD_WITH_INSTALL_NAME_DIR(设置为 ON 时,构建时也使用安装路径)。
用途:确保 macOS 动态库在构建时就能正确解析依赖路径,而不需要等到安装后。
if(POLICY CMP0075)
cmake_policy(SET CMP0075 NEW)
endif()
功能:控制 find_package() 如何处理 CMAKE_FIND_PACKAGE_REDIRECTS_DIR。
NEW 行为:允许 find_package() 优先检查 CMAKE_FIND_PACKAGE_REDIRECTS_DIR 目录下的包配置文件。
用途:用于自定义包查找逻辑,例如在特定目录下覆盖默认的包查找行为。
if(POLICY CMP0077)
cmake_policy(SET CMP0077 NEW)
endif()
功能:控制 option() 命令如何处理变量的预定义值。
NEW 行为:如果 option() 的变量已经通过 set(... CACHE ...) 定义,则 option() 不会覆盖它。
用途:避免 option() 意外覆盖已经定义的缓存变量。
if(POLICY CMP0057)
cmake_policy(SET CMP0057 NEW)
endif()
功能:控制 if() 命令如何处理 IN_LIST 操作符。
NEW 行为:if(var IN_LIST list_var) 可以正确检查变量是否在列表中。
用途:使 IN_LIST 语法更加可靠,避免旧版本中的潜在问题。
if(POLICY CMP0074)
cmake_policy(SET CMP0074 NEW)
endif()
功能:控制 find_package() 如何处理 <PackageName>_ROOT 变量。
NEW 行为:<PackageName>_ROOT 变量会影响 find_package() 的搜索路径。
用途:允许用户通过 <PackageName>_ROOT 指定包的根目录,类似于 CMAKE_PREFIX_PATH。
if(POLICY CMP0135)
cmake_policy(SET CMP0135 NEW)
endif()
功能:控制 file(GENERATE) 命令如何处理相对路径。
NEW 行为:file(GENERATE) 输出的相对路径相对于当前 CMakeLists.txt 所在目录。
用途:使 file(GENERATE) 的行为更符合预期,避免路径解析问题。
if(POLICY CMP0116)
cmake_policy(SET CMP0116 OLD)
endif()
功能:控制 FetchContent 是否在配置阶段(configure-time)填充内容。
OLD 行为:FetchContent 在配置阶段填充内容(旧行为)。
NEW 行为:FetchContent 默认在构建阶段(build-time)填充内容(CMake 3.24+ 默认)。
用途:这里显式选择 OLD 行为,可能是为了兼容某些依赖配置阶段下载的逻辑。
3,常用命令
3.1 get_filename_component()
get_filename_component 是 CMake 中的一个命令,用于从文件路径中提取特定组成部分。它可以获取路径中的目录名、文件名、扩展名或文件名不带扩展名等部分。get_filename_component 是 CMake 中处理文件路径时非常有用的工具命令。
基本语法
get_filename_component(<VAR> <FileName> <COMP> [CACHE])
<VAR>: 存储结果的变量名
<FileName>: 要处理的文件路径
<COMP>: 指定要提取的部分,可以是以下之一:
DIRECTORY: 目录部分
NAME: 文件名部分(带扩展名)
EXT: 文件扩展名(带点)
NAME_WE: 文件名不带扩展名
LAST_EXT: 最后一个扩展名(对于类似 file.tar.gz 的文件)
NAME_WLE: 文件名不带最后一个扩展名
ABSOLUTE: 完整绝对路径
REALPATH: 解析所有符号链接后的绝对路径
使用示例
# 获取当前目录
get_filename_component(MY_DIR "path/to/file.txt" DIRECTORY)
# MY_DIR 将包含 "path/to"
# 获取文件名
get_filename_component(MY_NAME "path/to/file.txt" NAME)
# MY_NAME 将包含 "file.txt"
# 获取不带扩展名的文件名
get_filename_component(MY_NAME_WE "path/to/file.txt" NAME_WE)
# MY_NAME_WE 将包含 "file"
# 获取扩展名
get_filename_component(MY_EXT "path/to/file.txt" EXT)
# MY_EXT 将包含 ".txt"
# 获取绝对路径
get_filename_component(MY_ABS_PATH "path/to/file.txt" ABSOLUTE)
# MY_ABS_PATH 将包含完整绝对路径
实际应用场景
这个命令常用于:
处理用户输入的文件路径
从完整路径中提取特定部分用于后续操作
确保路径格式正确
解析符号链接获取真实路径
3.2 execute_process()
execute_process(COMMAND ${GIT_EXECUTABLE} -c submodule.tpls/llvm.update=none submodule update --init --recursive
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
RESULT_VARIABLE GIT_SUBMOD_RESULT)
COMMAND 部分
${GIT_EXECUTABLE}
通过 find_package(Git) 找到的 Git 可执行文件路径。
-c submodule.tpls/llvm.update=none
临时配置 Git,对子模块 tpls/llvm 禁用自动更新(即使远程有新提交,也不拉取)。
submodule update --init --recursive
--init:如果子模块未初始化(未克隆),先初始化。
--recursive:递归处理所有嵌套子模块。
WORKING_DIRECTORY
${CMAKE_CURRENT_SOURCE_DIR}
指定命令在 CMake 当前源码目录 中执行(通常是项目的根目录),确保正确访问 .gitmodules。
RESULT_VARIABLE GIT_SUBMOD_RESULT
存储命令的退出状态码(0 表示成功,非 0 表示失败)。
git -c submodule.tpls/llvm
git -c submodule.tpls/llvm.update=none submodule update --init --recursive
.gitmodules中,存在如下内容:
[submodule "tpls/llvm"]
path = tpls/llvm
url = https://github.com/llvm/llvm-project.git
shallow = true
ignore = dirty
git -c submodule.tpls/llvm.update=none submodule update --init --recursive
1. -c submodule.tpls/llvm.update=none
-c <name>=<value>
Git 的全局临时配置选项,仅在本次命令中生效。
这里设置了子模块的更新行为:
submodule.tpls/llvm.update=none
表示对路径为 tpls/llvm 的子模块禁用自动更新(即使子模块有新的提交,也不会拉取)。
如果不加 -c submodule.tpls/llvm.update=none,子模块 tpls/llvm 会正常更新。
2. submodule update --init --recursive
submodule update
更新子模块到父仓库中记录的提交(默认不会自动拉取最新提交,除非显式配置)。
--init
如果子模块未初始化(未克隆),先执行初始化(相当于 git submodule init)。
--recursive
递归操作所有嵌套的子模块(子模块的子模块也会被更新)。
4,cmake 内置变量
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED on)
将 C++ 语言标准设置为 C++20
REQUIRED 表示如果编译器不支持 C++20 将报错(而不是降级)
set(CMAKE_POSITION_INDEPENDENT_CODE TRUE)
启用位置无关代码(PIC)生成
对于生成共享库(.so/.dll)通常是必需的
也会影响静态库如果它们可能被链接到共享库中
if(NOT DEFINED CMAKE_COMPILE_WARNING_AS_ERROR)
set(CMAKE_COMPILE_WARNING_AS_ERROR ON)
endif()
默认将所有编译警告视为错误(严格模式)
允许通过命令行或特定目标覆盖此设置(因为先检查是否已定义)
有助于保持代码质量,强制解决所有警告
set(CMAKE_EXPORT_COMPILE_COMMANDS 1)
功能
生成 compile_commands.json 文件
启用后,CMake 会在构建目录中生成一个名为 compile_commands.json 的文件,记录项目中所有源文件的编译命令(包括编译器路径、编译选项、头文件路径等)。
用途
工具链集成:
被静态分析工具(如 clang-tidy、cppcheck)、代码索引工具(如 rtags、ccls)或 IDE(如 VSCode、CLion)使用,以准确理解项目的编译环境。
调试构建问题:
手动检查单个文件的编译参数是否符合预期。
示例输出片段(compile_commands.json)
json
[
{
"directory": "/path/to/build",
"command": "/usr/bin/c++ -I../include -O2 -std=c++20 -o CMakeFiles/example.dir/src/main.cpp.o -c ../src/main.cpp",
"file": "../src/main.cpp"
}
]
find_package(Git QUIET)
功能
查找 Git 可执行文件
在系统中搜索 git 命令的路径,并将结果保存在变量 Git_EXECUTABLE 中。如果找到,还会定义 Git_FOUND 为 TRUE。
参数
QUIET:
禁止输出找不到 Git 时的警告信息(静默模式)。