跳到主要内容

Cmake配置文件

· 阅读需 6 分钟
XingHunm
Tech Enthusiast

CMake 配置文件是现代 C/C++项目构建的核心组件,涵盖了项目构建、工具链配置、依赖管理、打包测试等多个方面。本文将从六个关键配置文件类型详细介绍 CMake 配置体系。

1. CMakeLists.txt - 项目构建规则

CMakeLists.txt 是 CMake 项目的核心配置文件,定义项目构建规则和目标。

# 基本项目配置
cmake_minimum_required(VERSION 3.16)
project(MyProject VERSION 1.0.0 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)

# 目标定义
add_executable(myapp src/main.cpp)
add_library(mylib STATIC src/lib.cpp)

# 目标配置
target_include_directories(myapp PRIVATE include)
target_link_libraries(myapp PRIVATE mylib)

# 编译选项
if(MSVC)
target_compile_options(myapp PRIVATE /W4)
else()
target_compile_options(myapp PRIVATE -Wall -Wextra)
endif()

2. toolchain.cmake - 工具链/交叉编译

工具链文件定义编译器和构建工具配置,用于交叉编译(在一台电脑上编译,但编译出来的程序是给另一种架构/系统 运行的)。toolchain.cmake 本质上还是一个 普通的 CMake 脚本文件,只是内容和用途不同。

# toolchain.cmake - 简单的ARM交叉编译配置-x86 PC 上用这个配置编译出在 ARM Linux 上能运行的程序
# 告诉 CMake 目标系统是 Linux,不是宿主机的系统。
set(CMAKE_SYSTEM_NAME Linux)

#告诉 CMake 目标 CPU 架构是 ARM
set(CMAKE_SYSTEM_PROCESSOR arm)

# 设置交叉编译器
# 指定 C 语言编译器,用交叉编译器arm-linux-gnueabihf-gcc而不是本地 gcc。
set(CMAKE_C_COMPILER arm-linux-gnueabihf-gcc)
# 指定 C++ 编译器,同样是交叉编译器 arm-linux-gnueabihf-g++
set(CMAKE_CXX_COMPILER arm-linux-gnueabihf-g++)

# 设置查找路径
set(CMAKE_FIND_ROOT_PATH /usr/arm-linux-gnueabihf)
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)

使用方法:

cmake -DCMAKE_TOOLCHAIN_FILE=toolchain.cmake ..

3. CMakeCache.txt - 缓存配置

CMakeCache.txt 是 CMake 自动生成的文件,位于你的 build 目录下(比如 build/CMakeCache.txt)。它存储了配置阶段的所有缓存变量,包括编译器路径、编译选项、自定义缓存变量等。CMake 下次配置时会直接读取这个文件,加快配置速度,并保留用户手动修改的变量。

缓存变量管理

# 设置缓存变量
set(BUILD_TYPE "Release" CACHE STRING "Build configuration type")
set(ENABLE_TESTING ON CACHE BOOL "Enable unit testing")
set(INSTALL_PREFIX "/usr/local" CACHE PATH "Installation directory")

# 缓存变量选项
set_property(CACHE BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")

# 强制缓存变量
set(FORCE_REBUILD OFF CACHE BOOL "Force complete rebuild" FORCE)

缓存操作命令

# 查看缓存内容
cmake -L

# 查看高级缓存选项
cmake -LA

# 清除缓存
rm CMakeCache.txt
# 或
cmake --build . --target clean

# 修改缓存变量
cmake -DCMAKE_BUILD_TYPE=Debug .

4. FindXXX.cmake / XXXConfig.cmake - 依赖库查找

这些文件定义了如何查找和配置外部依赖库。

FindXXX.cmake 模块

FindMyLibrary.cmake 定义了 CMake 如何找到你的库,也就是告诉 find_package(MyLibrary):

  • 去哪些路径找头文件 (.h)
  • 去哪些路径找库文件 (.so/.a/.lib)
  • 可选地去哪里读取版本信息
  • 最后生成 MyLibrary_FOUND 变量和一个现代 CMake target

换句话说,就是在告诉 CMake “如果有人调用 find_package(MyLibrary),就按我定义的规则去找这个库”。

# FindMyLibrary.cmake
# 查找MyLibrary库的CMake模块

# 查找头文件
find_path(MYLIBRARY_INCLUDE_DIR
NAMES mylibrary.h
PATHS
/usr/include
/usr/local/include
${MYLIBRARY_ROOT}/include
PATH_SUFFIXES mylibrary
)

# 查找库文件
find_library(MYLIBRARY_LIBRARY
NAMES mylibrary libmylibrary
PATHS
/usr/lib
/usr/local/lib
${MYLIBRARY_ROOT}/lib
PATH_SUFFIXES x86_64-linux-gnu
)

# 版本检查
if(MYLIBRARY_INCLUDE_DIR)
file(READ "${MYLIBRARY_INCLUDE_DIR}/mylibrary_version.h" VERSION_FILE)
string(REGEX MATCH "#define MYLIBRARY_VERSION \"([0-9.]+)\"" _ ${VERSION_FILE})
set(MYLIBRARY_VERSION ${CMAKE_MATCH_1})
endif()

# 结果处理
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(MyLibrary
REQUIRED_VARS MYLIBRARY_LIBRARY MYLIBRARY_INCLUDE_DIR
VERSION_VAR MYLIBRARY_VERSION
)

# 创建导入目标
if(MyLibrary_FOUND AND NOT TARGET MyLibrary::MyLibrary)
# 创建空target
add_library(MyLibrary::MyLibrary UNKNOWN IMPORTED)
# 设置属性,这一步把 target 变得完整,上层 target 才能直接 target_link_libraries 使用它。
set_target_properties(MyLibrary::MyLibrary PROPERTIES
IMPORTED_LOCATION "${MYLIBRARY_LIBRARY}"
INTERFACE_INCLUDE_DIRECTORIES "${MYLIBRARY_INCLUDE_DIR}"
)
endif()

示例:

find_package(MyLibrary REQUIRED)
target_link_libraries(MyApp PRIVATE MyLibrary::MyLibrary)

XXXConfig.cmake 配置文件

通俗的讲:FindXXX.cmake → “去哪里找这个库?怎么找?”而 XXXConfig.cmake → “库就在这里,按我给的规则用它就行。”

# MyLibraryConfig.cmake
# 由库提供的配置文件

@PACKAGE_INIT@

# 检查组件
set(_supported_components Core Utils Network)

foreach(_comp ${MyLibrary_FIND_COMPONENTS})
if(NOT _comp IN_LIST _supported_components)
set(MyLibrary_FOUND False)
set(MyLibrary_NOT_FOUND_MESSAGE "Unsupported component: ${_comp}")
endif()
endforeach()

# 包含目标文件
# MyLibraryTargets.cmake 包含了库的目标文件和头文件路径,不用像FindMyLibrary.cmake那样去自己搜索
include("${CMAKE_CURRENT_LIST_DIR}/MyLibraryTargets.cmake")

# 版本检查
check_required_components(MyLibrary)

示例:

# 在CMakeLists.txt中使用
find_package(MyLibrary 2.0 REQUIRED COMPONENTS Core Utils)

if(MyLibrary_FOUND)
target_link_libraries(myapp PRIVATE MyLibrary::MyLibrary)
message(STATUS "Found MyLibrary version: ${MyLibrary_VERSION}")
endif()

5. CPackConfig.cmake / CTestConfig.cmake - 打包与测试

这些配置文件用于项目的打包发布和测试管理。

CPackConfig.cmake - 打包配置

CPackConfig.cmake 和 CMakeLists.txt 在功能上有明确的分工 - CMakeLists.txt 负责项目的构建规则和目标定义,而 CPackConfig.cmake 专注于打包配置和发布相关的设置。虽然这些打包配置技术上可以直接写在 CMakeLists.txt 中,但分离到独立的 CPackConfig.cmake 文件可以让项目结构更清晰,维护更方便。

# CPackConfig.cmake - 简单的打包配置
set(CPACK_PACKAGE_NAME "MyApplication")
set(CPACK_PACKAGE_VERSION "${PROJECT_VERSION}")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "My awesome application")
set(CPACK_PACKAGE_VENDOR "My Company")

# 设置打包格式
set(CPACK_GENERATOR "TGZ;ZIP")

# 包含CPack
include(CPack)

使用方式:

# 在CMakeLists.txt中引用
include(CPackConfig.cmake)

# 构建并打包
cmake --build build
cpack --config build/CPackConfig.cmake

# 或者直接在构建目录中
cd build
cpack

CTestConfig.cmake - 测试配置

本质上,CTestConfig.cmake 里的配置完全可以写进普通的 CMakeLists.txt,只是“职责分工”不同:

# CTestConfig.cmake
set(CTEST_PROJECT_NAME "MyProject")
set(CTEST_NIGHTLY_START_TIME "00:00:00 EST")

# 测试超时设置
set(CTEST_TEST_TIMEOUT 300)

# 内存检查
set(CTEST_MEMORYCHECK_COMMAND "valgrind")
set(CTEST_MEMORYCHECK_COMMAND_OPTIONS "--leak-check=full --show-reachable=yes")

# 覆盖率检查
set(CTEST_COVERAGE_COMMAND "gcov")

测试脚本示例

# 在CMakeLists.txt中启用测试
enable_testing()

# 添加测试
add_test(NAME unit_tests COMMAND myapp_tests)
add_test(NAME integration_tests COMMAND myapp_integration)

# 测试属性
set_tests_properties(unit_tests PROPERTIES
TIMEOUT 60
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/tests
)

6. CMakePresets.json - 构建预设

CMakePresets.json,CMake 3.19+ 引入,提供了标准化的构建配置预设,简化了不同环境下的构建过程。该文件应放在项目根目录(与 CMakeLists.txt 同级)。

{
"version": 3,
"configurePresets": [
{
"name": "debug",
"displayName": "Debug Build",
"binaryDir": "build/debug",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug"
}
},
{
"name": "release",
"displayName": "Release Build",
"binaryDir": "build/release",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release"
}
}
],
"buildPresets": [
{
"name": "debug",
"configurePreset": "debug"
},
{
"name": "release",
"configurePreset": "release"
}
]
}

使用方式:

# 使用预设配置
cmake --preset debug
cmake --build --preset debug

# 列出可用预设
cmake --list-presets

总结

通过这六种配置文件的合理组织和使用,可以构建出功能完整、易于维护的 CMake 项目构建系统:

  • CMakeLists.txt 定义核心构建逻辑
  • toolchain.cmake 处理交叉编译和工具链配置
  • CMakeCache.txt 管理构建缓存和变量
  • FindXXX.cmake/XXXConfig.cmake 处理依赖库查找
  • CPackConfig.cmake/CTestConfig.cmake 支持打包和测试
  • CMakePresets.json 提供标准化的构建预设

这种模块化的配置方式不仅提高了项目的可维护性,还增强了跨平台兼容性和团队协作效率。