CMake 现代实践
现代 CMake(3.x)以 target 为中心,通过
target_*命令管理依赖和属性,告别全局变量的混乱时代。
核心概念:Target
cmake
# 现代 CMake 的核心:一切皆 target
# target 有三种可见性:
# PRIVATE — 只影响当前 target
# PUBLIC — 影响当前 target 和依赖它的 target
# INTERFACE — 只影响依赖它的 target(不影响自身)
add_library(mylib STATIC src/lib.cpp)
# 头文件路径
target_include_directories(mylib
PUBLIC include/ # 使用者也能看到
PRIVATE src/internal/ # 只有 mylib 自己用
)
# 编译选项
target_compile_options(mylib PRIVATE -Wall -Wextra)
# 链接库
target_link_libraries(mylib
PUBLIC fmt::fmt # 使用者也需要链接
PRIVATE spdlog::spdlog # 只有 mylib 需要
)
# 编译定义
target_compile_definitions(mylib PRIVATE MYLIB_INTERNAL=1)
target_compile_definitions(mylib PUBLIC MYLIB_VERSION=2)查找与使用依赖
cmake
# 方式 1:find_package(系统安装或 vcpkg)
find_package(fmt REQUIRED)
find_package(spdlog REQUIRED)
find_package(nlohmann_json REQUIRED)
find_package(GTest REQUIRED)
target_link_libraries(myapp PRIVATE
fmt::fmt
spdlog::spdlog
nlohmann_json::nlohmann_json
GTest::gtest_main
)
# 方式 2:FetchContent(自动下载,适合小依赖)
include(FetchContent)
FetchContent_Declare(
CLI11
GIT_REPOSITORY https://github.com/CLIUtils/CLI11.git
GIT_TAG v2.4.1
)
FetchContent_MakeAvailable(CLI11)
target_link_libraries(myapp PRIVATE CLI11::CLI11)
# 方式 3:vcpkg 工具链(推荐)
# cmake -DCMAKE_TOOLCHAIN_FILE=/path/to/vcpkg/scripts/buildsystems/vcpkg.cmake ..多目标项目结构
cmake
# 根 CMakeLists.txt
cmake_minimum_required(VERSION 3.25)
project(MyProject VERSION 1.2.3)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# 全局编译选项(通过 INTERFACE 库传递)
add_library(project_warnings INTERFACE)
target_compile_options(project_warnings INTERFACE
$<$<CXX_COMPILER_ID:GNU,Clang>:-Wall;-Wextra;-Wpedantic>
$<$<CXX_COMPILER_ID:MSVC>:/W4>
)
add_library(project_options INTERFACE)
target_compile_features(project_options INTERFACE cxx_std_20)
# 子目录
add_subdirectory(src)
add_subdirectory(tests)
add_subdirectory(benchmarks)
# 安装规则
install(TARGETS mylib myapp
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)
install(DIRECTORY include/ DESTINATION include)cmake
# src/CMakeLists.txt
add_library(mylib STATIC
engine.cpp
parser.cpp
utils.cpp
)
target_include_directories(mylib PUBLIC ${PROJECT_SOURCE_DIR}/include)
target_link_libraries(mylib
PUBLIC project_options
PRIVATE project_warnings fmt::fmt
)
add_executable(myapp main.cpp)
target_link_libraries(myapp PRIVATE mylib project_warnings)生成器表达式
cmake
# 生成器表达式:在生成阶段求值(不是配置阶段)
# 语法:$<condition:value>
# 根据构建类型设置选项
target_compile_options(mylib PRIVATE
$<$<CONFIG:Debug>:-g;-O0;-DDEBUG>
$<$<CONFIG:Release>:-O3;-DNDEBUG>
)
# 根据编译器设置选项
target_compile_options(mylib PRIVATE
$<$<CXX_COMPILER_ID:GNU>:-fstack-protector-strong>
$<$<CXX_COMPILER_ID:Clang>:-fsanitize=address>
$<$<CXX_COMPILER_ID:MSVC>:/GS>
)
# 区分构建时和安装时的头文件路径
target_include_directories(mylib PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)版本管理
cmake
# 从 CMake 版本号生成头文件
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/include/version.h.in
${CMAKE_CURRENT_BINARY_DIR}/include/version.h
)
target_include_directories(mylib PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/include)cpp
// include/version.h.in
#pragma once
#define MYPROJECT_VERSION_MAJOR @PROJECT_VERSION_MAJOR@
#define MYPROJECT_VERSION_MINOR @PROJECT_VERSION_MINOR@
#define MYPROJECT_VERSION_PATCH @PROJECT_VERSION_PATCH@
#define MYPROJECT_VERSION "@PROJECT_VERSION@"常用 CMake 命令速查
bash
# 配置
cmake -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=vcpkg/scripts/buildsystems/vcpkg.cmake
# 构建
cmake --build build --parallel 8
# 安装
cmake --install build --prefix /usr/local
# 运行测试
ctest --test-dir build --output-on-failure --parallel 4
# 打包
cpack --config build/CPackConfig.cmake
# 查看所有 target
cmake --build build --target help
# 详细构建输出
cmake --build build --verbose关键认知
现代 CMake 的核心原则:用 target_* 命令,不用全局变量。PUBLIC/PRIVATE/INTERFACE 控制属性传播。compile_commands.json(CMAKE_EXPORT_COMPILE_COMMANDS=ON)是 IDE 和工具链的基础,必须生成。