一、CMake现代配置范式

CMake 3.21+ 引入了许多现代化特性:正确传播的导入目标、依赖管理协议(DEPfetcher)、NVIDIA/Apple编译器原生支持等。掌握这些特性是写出可维护构建配置的基础。

1.1 导出目标与依赖传播

# CMake 3.x 现代写法(优先使用):
cmake_minimum_required(VERSION 3.21)
project(MyProject VERSION 1.0.0 LANGUAGES CXX)

# 启用C++20
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)  # 用-std=c++20而非-gnu++20

# 现代 target_* 属性(自动传播):
add_library(my_lib STATIC src/lib.cpp)
target_compile_features(my_lib PUBLIC cxx_std_20)

# ✅ PUBLIC:自己和依赖者都用
# ✅ PRIVATE:只有自己用
# ✅ INTERFACE:只有依赖者用

target_include_directories(my_lib
    PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include
    PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src
)

# 依赖其他库(优先find_package):
find_package(Threads REQUIRED)           # 系统线程库
find_package(fmt REQUIRED)               # 第三方库
find_package(OpenSSL 1.1 REQUIRED)      # 带版本约束

add_executable(my_app src/main.cpp)
target_link_libraries(my_app
    PRIVATE
        my_lib
        Threads::Threads
        fmt::fmt
        OpenSSL::SSL
)

二、依赖管理:从vcpkg到CMake Presets

2.1 vcpkg集成

# vcpkg.json(项目级依赖管理)
{
  "$schema": "https://raw.githubusercontent.com/microsoft/vcpkg-tool/main/docs/vcpkg.schema.json",
  "name": "my-project",
  "version": "1.0.0",
  "dependencies": [
    "fmt",
    "nlohmann-json",
    "boost-asio",
    {
      "name": "opencv",
      "features": ["jpeg", "png", "gtk3"]
    }
  ],
  "overrides": [
    { "name": "fmt", "version": "10.2.1" }
  ]
}

# CMakeLists.txt中集成vcpkg:
# 方式一:工具链文件
# $ cmake -B build -DCMAKE_TOOLCHAIN_FILE=$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake

# 方式二:CMake 3.27+ 直接支持
find_package(nlohmann_json REQUIRED)
find_package(fmt REQUIRED)

2.2 CMake Presets(跨平台构建配置)

# CMakePresets.json(项目级,构建只需cmake --preset)
{
  "version": 6,
  "configurePresets": [
    {
      "name": "dev",
      "hidden": true,
      "binaryDir": "${sourceDir}/build/${presetName}",
      "cacheVariables": {
        "CMAKE_EXPORT_COMPILE_COMMANDS": "ON",
        "CMAKE_COLOR_DIAGNOSTICS": "ON"
      }
    },
    {
      "name": "linux-release",
      "inherits": "dev",
      "generator": "Ninja",
      "cacheVariables": {
        "CMAKE_BUILD_TYPE": "Release",
        "ENABLE_TESTING": "ON"
      }
    },
    {
      "name": "windows-debug",
      "inherits": "dev",
      "generator": "Visual Studio 17 2022",
      "architecture": {"value": "x64"},
      "cacheVariables": {
        "CMAKE_BUILD_TYPE": "Debug"
      }
    }
  ],
  "buildPresets": [
    {
      "name": "release",
      "configurePreset": "linux-release",
      "jobs": 8
    }
  ]
}

# 用户使用:
$ cmake --preset linux-release  # 配置
$ cmake --build --preset release # 构建
$ ctest --preset release         # 测试

三、CTest与测试集成

enable_testing()

# 添加单元测试
find_package(GTest REQUIRED)
add_executable(test_geometry test/geometry_test.cpp)
target_link_libraries(test_geometry PRIVATE
    geometry_lib
    GTest::gtest_main
)
include(GoogleTest)
gtest_discover_tests(test_geometry)

# 测试分级:单元/集成/性能
add_test(NAME Unit.BenchmarkFixture COMMAND test_benchmark)
set_tests_properties(Unit.BenchmarkFixture
    PROPERTIES PASS_REGULAR_EXPRESSION "ALL TESTS PASSED"
    FAIL_REGULAR_EXPRESSION "[  FAILED  ]"
)

# Benchmark(使用Google Benchmark):
add_executable(benchmark_algorithm
    benchmark/algorithm_benchmark.cpp)
target_link_libraries(benchmark_algorithm
    PRIVATE benchmark::benchmark)

3.1 GitHub Actions CI配置

# .github/workflows/cmake.yml
name: CMake Build

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  build:
    strategy:
      matrix:
        os: [ubuntu-latest, macos-latest]
        cmake-generator: [Ninja, "Unix Makefiles"]
        include:
          - os: windows-latest
            cmake-generator: "Visual Studio 17 2022"

    runs-on: $

    steps:
      - uses: actions/checkout@v4

      - name: Configure CMake
        run: |
          cmake -B build -G "$"
            -DCMAKE_BUILD_TYPE=Release
            -DENABLE_TESTING=ON

      - name: Build
        run: cmake --build build --parallel

      - name: Test
        run: ctest --output-on-failure --build-dir build

      - name: Benchmark
        if: matrix.os == 'ubuntu-latest'
        run: |
          cmake --build build --target benchmark_algorithm
          ./build/benchmark_algorithm --benchmark_format=json > benchmark.json

      - name: Upload benchmark
        if: matrix.os == 'ubuntu-latest'
        uses: benchmark-action/github-action-benchmark@v1
        with:
          tool: 'googlebenchmark'
          outputFilePath: benchmark.json

四、预编译头与增量构建优化

# 预编译头(PCH):加速编译约30-50%
target_precompile_headers(my_lib
    PRIVATE
        <vector>
        <string>
        <memory>
)

# 实际效果(1000个cpp文件,包含相同头):
# 无PCH:  3分12秒
# 有PCH:  1分28秒   ← 提速55%

# 模块化构建(CMake 3.28+,C++20 Modules):
# 注意:模块化构建还在成熟中,生产使用需谨慎
add_library(my_lib STATIC)
target_sources(my_lib
    PUBLIC
        FILE_SET my_modules
        BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/modules
        MODULES std.cppm  # C++20模块
)

# 分层构建(超级构建模式):
# Project/
#   ├── CMakeLists.txt(主构建)
#   └── src/
#       ├── superbuild/CMakeLists.txt
#       └── deps/(第三方库存放)

# 安装规则(跨平台):
include(GNUInstallDirs)
install(TARGETS my_lib my_app
    EXPORT MyProjectTargets
    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
)

install(EXPORT MyProjectTargets
    FILE MyProjectTargets.cmake
    NAMESPACE MyProject::
    DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/MyProject
)