CMakeLists

37 阅读3分钟

读懂 CMakeLists.txt && *.cmake 文件

CMakeLists.txt -> CMakeCache.txt -> compile_commands.json

CMakeLists         定义怎么配置编译器、toolchain
CMakeCache         看实际缓存变量值
compile_commands   最终每个文件的编译命令

编译流程

CMakeLists.txt → CMake → Makefile / Ninja → GCC / Clang

CMakeLists.txt 常见指令

指令作用
cmake_minimum_required()指定最低 CMake 版本
project()定义项目
set()设置变量
add_executable()添加可执行文件
add_library()添加库
target_link_libraries()链接库
target_include_directories()添加头文件路径
target_compile_definitions()添加编译宏
target_compile_options()添加编译选项
add_subdirectory()添加子目录
include()包含 CMake 模块
find_package()查找已安装的包
foreach() / endforeach()循环
function() / endfunction()定义函数
message()打印信息

常用变量与路径配置

CMAKE_CURRENT_LIST_DIR

当前 CMakeLists.txt 所在的路径。

CMAKE_MODULE_PATH

用于 include() 查找 .cmake 模块文件的路径列表。

list(APPEND CMAKE_MODULE_PATH
    "${CMAKE_CURRENT_LIST_DIR}/XX/XX/cmake"
    "${CMAKE_CURRENT_LIST_DIR}/YY/YY/cmake"
)

include(rb_testing)

路径格式转换

file(TO_CMAKE_PATH "${CMAKE_MODULE_PATH}" CMAKE_MODULE_PATH)

将路径中的反斜杠 `` 转换为正斜杠 /。Linux 和 CMake 都使用 /,此操作确保跨平台兼容性。

CMAKE_PREFIX_PATH

用于 find_package() 查找第三方库和依赖包的路径列表。

list(APPEND CMAKE_PREFIX_PATH
    "${CMAKE_CURRENT_LIST_DIR}/XX/XX/cmake"
    "${CMAKE_CURRENT_LIST_DIR}/YY/YY/cmake"
)

xxx_ROOT 提示变量

find_package() 提供优先搜索路径。

set(mom_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/c_if/xxx/yyy)
set(vfc_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/c_if/xxx/yyy)

find_package(mom REQUIRED)
find_package(vfc REQUIRED)

当调用 find_package(mom REQUIRED) 时,CMake 会优先在 mom_ROOT 指定的目录中查找。


代码生成相关配置

模板路径

set(RB_TEMPLATE_PATH "${CMAKE_CURRENT_LIST_DIR}/tools/volt/Modules/templates")

指定代码生成模板的位置。

生成文件输出目录

set(RB_ScomGenerator_OUTPUT_DIR "${CMAKE_BINARY_DIR}/scom")

${CMAKE_BINARY_DIR} 表示构建目录(通常是 build 目录)。


Windows 平台特殊处理

路径长度限制

Windows 有 260 字符的路径长度限制,以下设置让 CMake 控制生成的 .obj 文件路径不要太长:

set(CMAKE_OBJECT_PATH_MAX 230)

Response File

当链接的 .obj 文件过多时,命令行会超长。以下设置让编译器把文件列表写入临时文件(Response File):

set(CMAKE_CXX_USE_RESPONSE_FILE_FOR_OBJECTS 1)
set(CMAKE_C_USE_RESPONSE_FILE_FOR_OBJECTS 1)

对比示例:

不使用 Response File:

link.exe a.obj b.obj c.obj d.obj e.obj f.obj ...  # 命令行超长

使用 Response File:

link.exe @files.rsp  # 文件列表写入 files.rsp

构建类型配置映射

CMake 有不同的构建类型:DebugReleaseCoverage 等。

当使用 Coverage 配置构建时,find_package() 找到的第三方库可能没有 Coverage 版本,只有 Release 版本。以下设置让 CMake 在找不到 Coverage 版本时回退到 Release

set(CMAKE_MAP_IMPORTED_CONFIG_COVERAGE Release "")

重要指令详解

get_filename_component()

get_filename_component(
    RB_PROJECT_TARGET
    "${RB_PROJECT_TARGET}"
    REALPATH
    BASE_DIR "${CMAKE_BINARY_DIR}"
)

作用:将相对路径转换为绝对路径。

参数说明

参数说明
RB_PROJECT_TARGET输出变量名
"${RB_PROJECT_TARGET}"输入路径
REALPATH解析符号链接,返回真实绝对路径
BASE_DIR "${CMAKE_BINARY_DIR}"相对路径的基准目录

示例

# CMAKE_BINARY_DIR = C:/repo/fvg3/build
# RB_PROJECT_TARGET = "../config/target.cmake"
# 结果: RB_PROJECT_TARGET = "C:/repo/fvg3/config/target.cmake"

string(REGEX REPLACE ...)

string(REGEX REPLACE "\.cmake$" ".prebuild.cmake" RB_PROJECT_TARGET_PREBUILD "${RB_PROJECT_TARGET}")

作用:使用正则表达式进行字符串替换。

参数说明

参数说明
"\.cmake$"正则表达式(匹配 .cmake 结尾)
".prebuild.cmake"替换后的字符串
RB_PROJECT_TARGET_PREBUILD输出变量名
"${RB_PROJECT_TARGET}"输入字符串

示例

# 输入: /path/to/variation_cfg.cmake
# 输出: /path/to/variation_cfg.prebuild.cmake

正则转义说明

写法CMake 解析后正则含义
..任意字符
\..字面点号 .

\. 中第一个 转义第二个,第二个 `` 转义点号,最终匹配真正的 . 字符。


set(... CACHE ...)

set(RB_PROJECT_TARGET "" CACHE STRING "Path to CMake build target file")

作用:定义缓存变量,保存到 CMakeCache.txt 文件中。

参数说明

参数说明
RB_PROJECT_TARGET变量名
""默认值
CACHE标记为缓存变量
STRING变量类型(可选:BOOLPATHFILEPATH
"Path to..."CMake GUI 中显示的描述

特点:缓存变量可通过命令行 -D 选项设置,重新配置时保留之前的值。


include(... OPTIONAL)

include("${RB_PROJECT_TARGET_PREBUILD}" OPTIONAL)

作用:包含并执行另一个 CMake 脚本文件。

参数说明

参数说明
"${RB_PROJECT_TARGET_PREBUILD}"要包含的文件路径
OPTIONAL文件不存在时静默跳过,不报错

相当于把目标文件的内容"复制粘贴"到当前位置执行。


include(... RESULT_VARIABLE)

include(${RB_PROJECT_TARGET} RESULT_VARIABLE l_result)

作用:包含文件并获取执行结果。

参数说明

参数说明
${RB_PROJECT_TARGET}要包含的文件
RESULT_VARIABLE l_result将结果存入 l_result

返回值

状态l_result 的值
成功被包含文件的完整路径
失败NOTFOUND

使用示例

include(${RB_PROJECT_TARGET} RESULT_VARIABLE l_result)
if(l_result STREQUAL NOTFOUND)
    message(FATAL_ERROR "Cannot find file: ${RB_PROJECT_TARGET}")
endif()
add_executable(myapp main.cpp utils.cpp)

定义一个可执行文件target

main.cpp ──→ 编译 ──→ main.obj utils.cpp ──→ 编译 ──→ utils.obj ↓ 链接 ↓ myapp.exe (Windows) myapp (Linux)

add_library()

add_library(mylib STATIC helper.cpp)

定义一个静态库target helper.cpp ──→ 编译 ──→ helper.obj ↓ 打包 ↓ mylib.lib (Windows) libmylib.a (Linux) 把.obj文件打包在一起,方便复用

target_link_libraries()

target_link_libraries(myapp mylib)

myapp 链接 mylib

main.obj ──┐ utils.obj ─┼──→ 链接 ──→ myapp.exe mylib.lib ─┘

源文件 编译 链接 产物 ───────────────────────────────────────────────────────────── main.cpp ──→ main.obj ──┐ utils.cpp ──→ utils.obj ──┼──→ myapp.exe │ helper.cpp ──→ helper.obj ──┼──→ mylib.lib ──┘

rb_testing.cmake

include(rb_option)

rb_option_add_constrained_option(
    NAME
        TESTING_TOOL
    OPTIONS
        googletest cantata
    DEFAULT
        googletest
)

上述代码中rb_option_add_constrained_option()函数详见rb_option.cmake

rb_option.cmake

function(rb_option_add_constrained_option)
  set(options)
  set(oneValueArgs NAME DESCRIPTION DEFAULT)
  set(multiValueArgs OPTIONS)
  cmake_parse_arguments(f "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})

  set(${f_NAME} ${f_DEFAULT} CACHE STRING "${f_DESCRIPTION}")
  set_property(CACHE ${f_NAME} PROPERTY STRINGS ${f_OPTIONS})

endfunction()