CMake学习篇三:常用指令速查

💡 学习前提

  • 已完成 引入篇:理解为什么需要 CMake
  • 已完成 篇一:会创建基本的 CMake 项目
  • 已完成 篇二:理解 Target、变量、作用域等核心概念
  • 本篇目标:掌握常用指令的详细用法、跨平台知识和最佳实践

1. CMake 命令行选项

基本用法:

cmake [选项] <源代码目录>

1.1 常用选项速查

# 构建类型
cmake .. -DCMAKE_BUILD_TYPE=Debug          # 调试模式
cmake .. -DCMAKE_BUILD_TYPE=Release        # 发布模式
cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo # 优化+调试信息
cmake .. -DCMAKE_BUILD_TYPE=MinSizeRel     # 最小体积

# 指定编译器
cmake .. -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++
cmake .. -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++

# 安装路径
cmake .. -DCMAKE_INSTALL_PREFIX=/opt/myapp

# 调试选项
cmake .. -DCMAKE_VERBOSE_MAKEFILE=ON  # 持久详细输出
cmake --build . --verbose              # 临时详细输出
cmake .. --debug-output                # CMake 调试信息

# 生成器
cmake .. -G Ninja                      # 使用 Ninja(更快)
cmake .. -G "Unix Makefiles"           # 使用 Make
cmake .. -G "Visual Studio 16 2019"    # VS 2019

# 自定义变量
cmake .. -DBUILD_TESTS=ON -DENABLE_LOGGING=OFF

# 一行命令
cmake -B build -DCMAKE_BUILD_TYPE=Release && cmake --build build
⚠️ 选项类型

  • 配置选项(-D):配置阶段生效,写入 Makefile
  • 构建选项(--build 后):编译阶段生效,传递给底层工具

```bash
cmake .. -DCMAKE_BUILD_TYPE=Release # 配置选项
cmake --build . --verbose -j 8 # 构建选项
```


2. 常用指令详解

2.1 基础指令(快速参考)

# 版本要求
cmake_minimum_required(VERSION 3.10)

# 项目定义
project(MyProject VERSION 1.0.0 LANGUAGES CXX)

# 创建可执行文件
add_executable(app main.cpp utils.cpp)

# 创建库
add_library(mylib STATIC lib.cpp)        # 静态库
add_library(mylib SHARED lib.cpp)        # 动态库
add_library(mylib INTERFACE)             # 接口库(仅头文件)

# 链接库
target_link_libraries(app PRIVATE mylib)

# 头文件路径
target_include_directories(mylib
    PUBLIC include/    # 对外接口
    PRIVATE src/       # 内部实现
)
⚠️ 现代 CMake 原则

  • 使用 target_xxx 命令(精确控制每个目标)
  • 避免全局命令 include_directories/link_directories(影响所有目标)
  • 合理使用 PRIVATE/PUBLIC/INTERFACE(控制依赖传递)

2.2 target_xxx 系列(现代方式)

命令 对应参数 作用
target_include_directories -I 头文件搜索路径
target_compile_definitions -D 宏定义
target_compile_options -Wall -O2 编译选项
target_link_libraries -l 链接库
target_link_directories -L 库搜索路径

示例:

add_library(mylib lib.cpp)

target_include_directories(mylib PUBLIC include/)
target_compile_definitions(mylib PRIVATE DEBUG_MODE)
target_compile_options(mylib PRIVATE -Wall -Wextra)
target_link_libraries(mylib PUBLIC other_lib)

2.3 其他常用指令

target_compile_definitions

# 添加宏定义(对应 -D 参数)
target_compile_definitions(my_app
    PRIVATE DEBUG_MODE
    PUBLIC VERSION=1.0
    PRIVATE $<$<CONFIG:Debug>:DEBUG_BUILD>  # 仅 Debug 模式
)

target_compile_options

# 添加编译选项
target_compile_options(my_app
    PRIVATE -Wall -Wextra
    PRIVATE $<$<CXX_COMPILER_ID:MSVC>:/W4>        # MSVC 特定
    PRIVATE $<$<CXX_COMPILER_ID:GNU>:-Wpedantic>  # GCC 特定
)

add_subdirectory

# 添加子目录(子目录必须包含 CMakeLists.txt)
add_subdirectory(src)
add_subdirectory(libs/mylib)

# 指定构建输出路径
add_subdirectory(../shared_lib ${CMAKE_BINARY_DIR}/shared)

include

# 包含 CMake 模块或文件
include(CMakePrintHelpers)
include(CheckCXXCompilerFlag)
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/MyFunctions.cmake)

# 可选包含(文件不存在不报错)
include(OptionalFile OPTIONAL)

find_package

# 查找并加载外部库
find_package(OpenCV REQUIRED)
find_package(Boost 1.70 REQUIRED COMPONENTS filesystem system)

# 可选包
find_package(CUDA)
if(CUDA_FOUND)
    message("CUDA found: ${CUDA_VERSION}")
endif()

message

# 输出消息
message(STATUS "This is a status message")
message(WARNING "This is a warning")
message(FATAL_ERROR "This stops configuration")
message("Variable value: ${MY_VAR}")

option

# 定义布尔选项(可在命令行修改)
option(BUILD_TESTS "Build test programs" ON)
option(ENABLE_LOGGING "Enable logging" OFF)

if(BUILD_TESTS)
    add_subdirectory(tests)
endif()

# 命令行设置:cmake .. -DBUILD_TESTS=OFF

configure_file

# 配置文件生成(变量替换)
configure_file(
    ${CMAKE_CURRENT_SOURCE_DIR}/config.h.in
    ${CMAKE_CURRENT_BINARY_DIR}/config.h
)

config.h.in 模板:

#define PROJECT_VERSION "@PROJECT_VERSION@"
#define PROJECT_NAME "@PROJECT_NAME@"
#cmakedefine ENABLE_FEATURE
#cmakedefine01 USE_OPENMP

生成的 config.h:

#define PROJECT_VERSION "1.2.3"
#define PROJECT_NAME "MyProject"
#define ENABLE_FEATURE  // 如果 ENABLE_FEATURE 为 ON
/* #undef ENABLE_FEATURE */  // 如果为 OFF
#define USE_OPENMP 1  // 如果 USE_OPENMP 为 ON

3. 跨平台知识

3.1 库文件扩展名

平台 静态库 动态库 可执行文件
Linux .a .so 无扩展名
macOS .a .dylib 无扩展名
Windows .lib .dll + .lib(导入库) .exe

命名规则:

Linux/macOS:  lib + 库名 + 扩展名
  libmath.a, libmath.so, libmath.dylib

Windows:      库名 + 扩展名
  math.lib, math.dll
💡 CMake 自动处理跨平台差异

```cmake
add_library(mymath STATIC add.c)
# Linux/macOS: libmymath.a
# Windows: mymath.lib

add_library(mymath SHARED add.c)
# Linux: libmymath.so
# macOS: libmymath.dylib
# Windows: mymath.dll + mymath.lib

# 链接时不需要写扩展名
target_link_libraries(main PRIVATE mymath) # 自动找到正确的库
```


4. 进阶内容

4.1 find_package 深入

查找系统库:

# Threads
find_package(Threads REQUIRED)
target_link_libraries(my_app PRIVATE Threads::Threads)

# OpenMP
find_package(OpenMP REQUIRED)
target_link_libraries(my_app PRIVATE OpenMP::OpenMP_CXX)

# Boost
find_package(Boost 1.70 REQUIRED COMPONENTS filesystem system)
target_link_libraries(my_app PRIVATE Boost::filesystem Boost::system)

自定义 Find 模块(cmake/FindMyLib.cmake):

find_path(MYLIB_INCLUDE_DIR mylib.h
    PATHS /usr/include /usr/local/include
)

find_library(MYLIB_LIBRARY
    NAMES mylib
    PATHS /usr/lib /usr/local/lib
)

include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(MyLib
    REQUIRED_VARS MYLIB_LIBRARY MYLIB_INCLUDE_DIR
)

if(MyLib_FOUND AND NOT TARGET MyLib::MyLib)
    add_library(MyLib::MyLib UNKNOWN IMPORTED)
    set_target_properties(MyLib::MyLib PROPERTIES
        IMPORTED_LOCATION "${MYLIB_LIBRARY}"
        INTERFACE_INCLUDE_DIRECTORIES "${MYLIB_INCLUDE_DIR}"
    )
endif()

使用自定义模块:

list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
find_package(MyLib REQUIRED)
target_link_libraries(my_app PRIVATE MyLib::MyLib)

4.2 FetchContent(现代依赖管理)

include(FetchContent)

# 声明依赖
FetchContent_Declare(
    googletest
    GIT_REPOSITORY https://github.com/google/googletest.git
    GIT_TAG release-1.12.1
)

# 使依赖可用
FetchContent_MakeAvailable(googletest)

# 使用依赖
add_executable(my_test test.cpp)
target_link_libraries(my_test PRIVATE gtest_main)

多个依赖:

FetchContent_Declare(json
    GIT_REPOSITORY https://github.com/nlohmann/json.git
    GIT_TAG v3.11.2
)

FetchContent_Declare(fmt
    GIT_REPOSITORY https://github.com/fmtlib/fmt.git
    GIT_TAG 9.1.0
)

FetchContent_MakeAvailable(json fmt)

target_link_libraries(my_app PRIVATE nlohmann_json::nlohmann_json fmt::fmt)

4.3 安装和导出

# 安装目标
install(TARGETS mylib my_app
    EXPORT MyProjectTargets
    LIBRARY DESTINATION lib
    ARCHIVE DESTINATION lib
    RUNTIME DESTINATION bin
    INCLUDES DESTINATION include
)

# 安装头文件
install(DIRECTORY include/ DESTINATION include)

# 导出目标
install(EXPORT MyProjectTargets
    FILE MyProjectTargets.cmake
    NAMESPACE MyProject::
    DESTINATION lib/cmake/MyProject
)

# 生成配置文件
include(CMakePackageConfigHelpers)
configure_package_config_file(
    ${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in
    ${CMAKE_CURRENT_BINARY_DIR}/MyProjectConfig.cmake
    INSTALL_DESTINATION lib/cmake/MyProject
)

install(FILES
    ${CMAKE_CURRENT_BINARY_DIR}/MyProjectConfig.cmake
    DESTINATION lib/cmake/MyProject
)

5. 实战示例

5.1 第三方库集成

使用 find_package:

cmake_minimum_required(VERSION 3.15)
project(ThirdPartyDemo)

set(CMAKE_CXX_STANDARD 17)

# 查找库
find_package(OpenCV REQUIRED)
find_package(Boost 1.70 REQUIRED COMPONENTS filesystem system)
find_package(Threads REQUIRED)

add_executable(demo main.cpp)

target_link_libraries(demo
    PRIVATE ${OpenCV_LIBS}
    PRIVATE Boost::filesystem Boost::system
    PRIVATE Threads::Threads
)

使用 FetchContent:

cmake_minimum_required(VERSION 3.15)
project(FetchContentDemo)

set(CMAKE_CXX_STANDARD 17)

include(FetchContent)

FetchContent_Declare(json
    GIT_REPOSITORY https://github.com/nlohmann/json.git
    GIT_TAG v3.11.2
)

FetchContent_Declare(spdlog
    GIT_REPOSITORY https://github.com/gabime/spdlog.git
    GIT_TAG v1.11.0
)

FetchContent_MakeAvailable(json spdlog)

add_executable(demo main.cpp)
target_link_libraries(demo
    PRIVATE nlohmann_json::nlohmann_json
    PRIVATE spdlog::spdlog
)

5.2 跨平台配置

cmake_minimum_required(VERSION 3.15)
project(CrossPlatform)

set(CMAKE_CXX_STANDARD 17)

add_executable(app main.cpp)

# 平台特定的源文件
if(WIN32)
    target_sources(app PRIVATE src/windows_impl.cpp)
elseif(UNIX AND NOT APPLE)
    target_sources(app PRIVATE src/linux_impl.cpp)
elseif(APPLE)
    target_sources(app PRIVATE src/macos_impl.cpp)
endif()

# 平台特定的库
if(WIN32)
    target_link_libraries(app PRIVATE ws2_32)
elseif(UNIX)
    target_link_libraries(app PRIVATE pthread dl)
endif()

# 平台特定的编译定义
if(WIN32)
    target_compile_definitions(app PRIVATE PLATFORM_WINDOWS)
elseif(UNIX)
    target_compile_definitions(app PRIVATE PLATFORM_UNIX)
endif()

6. 最佳实践

6.1 现代 CMake 原则

使用 target_xxx 命令:

# ✅ 现代方式
target_include_directories(my_target PRIVATE include/)
target_compile_definitions(my_target PRIVATE DEBUG)
target_link_libraries(my_target PRIVATE mylib)

# ❌ 旧方式(避免使用)
include_directories(include/)
add_definitions(-DDEBUG)
link_libraries(mylib)

6.2 目录结构规范

project/
├── CMakeLists.txt           # 根配置
├── cmake/                   # CMake 模块和脚本
│   ├── FindXXX.cmake
│   └── CompilerWarnings.cmake
├── include/                 # 公共头文件
│   └── project/
│       └── api.h
├── src/                     # 源文件
│   └── api.cpp
├── apps/                    # 应用程序
│   └── main.cpp
├── tests/                   # 测试
│   └── test_api.cpp
├── docs/                    # 文档
├── external/                # 第三方库
└── build/                   # 构建目录(不提交到版本控制)

6.3 构建类型管理

# 设置默认构建类型
if(NOT CMAKE_BUILD_TYPE)
    set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE)
endif()

message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")

6.4 编译器警告配置

# cmake/CompilerWarnings.cmake
function(set_project_warnings target)
    set(MSVC_WARNINGS /W4 /WX)
    set(GCC_CLANG_WARNINGS
        -Wall -Wextra -Wpedantic -Wshadow
        -Wnon-virtual-dtor -Wold-style-cast
    )

    if(MSVC)
        set(PROJECT_WARNINGS ${MSVC_WARNINGS})
    else()
        set(PROJECT_WARNINGS ${GCC_CLANG_WARNINGS})
    endif()

    target_compile_options(${target} PRIVATE ${PROJECT_WARNINGS})
endfunction()

# 使用
include(cmake/CompilerWarnings.cmake)
set_project_warnings(my_target)

6.5 选项和缓存变量

# 布尔选项
option(BUILD_SHARED_LIBS "Build shared libraries" OFF)
option(BUILD_TESTS "Build test programs" ON)

# 字符串选项
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Build type")
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
    "Debug" "Release" "MinSizeRel" "RelWithDebInfo"
)

# 使用选项
if(BUILD_TESTS)
    enable_testing()
    add_subdirectory(tests)
endif()

6.6 版本管理

# 从文件读取版本
file(READ "${CMAKE_CURRENT_SOURCE_DIR}/VERSION" VERSION_STRING)
string(STRIP "${VERSION_STRING}" VERSION_STRING)

project(MyProject VERSION ${VERSION_STRING})

# 生成版本头文件
configure_file(
    ${CMAKE_CURRENT_SOURCE_DIR}/include/version.h.in
    ${CMAKE_CURRENT_BINARY_DIR}/include/version.h
)

version.h.in:

#pragma once

#define PROJECT_VERSION_MAJOR @PROJECT_VERSION_MAJOR@
#define PROJECT_VERSION_MINOR @PROJECT_VERSION_MINOR@
#define PROJECT_VERSION_PATCH @PROJECT_VERSION_PATCH@
#define PROJECT_VERSION "@PROJECT_VERSION@"

6.7 测试集成

# 启用测试
enable_testing()

# 添加测试
add_executable(test_math tests/test_math.cpp)
target_link_libraries(test_math PRIVATE math_lib)
add_test(NAME MathTests COMMAND test_math)

# 使用 Google Test
find_package(GTest REQUIRED)
add_executable(gtest_example tests/gtest_example.cpp)
target_link_libraries(gtest_example PRIVATE GTest::GTest GTest::Main)
gtest_discover_tests(gtest_example)

运行测试:

cd build
ctest                          # 运行所有测试
ctest -V                       # 详细输出
ctest -R Math                  # 运行匹配 "Math" 的测试
ctest --output-on-failure      # 失败时显示输出

6.8 安装规则

# 安装目标
install(TARGETS mylib myapp
    EXPORT MyProjectTargets
    LIBRARY DESTINATION lib
    ARCHIVE DESTINATION lib
    RUNTIME DESTINATION bin
    INCLUDES DESTINATION include
)

# 安装头文件
install(DIRECTORY include/
    DESTINATION include
    FILES_MATCHING PATTERN "*.h"
)

# 导出目标
install(EXPORT MyProjectTargets
    FILE MyProjectTargets.cmake
    NAMESPACE MyProject::
    DESTINATION lib/cmake/MyProject
)

安装命令:

cmake --build build
cmake --install build --prefix /usr/local

6.9 常用命令速查

# 配置
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake -B build -G Ninja

# 构建
cmake --build build
cmake --build build -j 8
cmake --build build --target my_app

# 安装
cmake --install build --prefix ~/myapp

# 测试
cd build && ctest

# 查看缓存
cmake -B build -L
cmake -B build -LAH

6.10 调试技巧

# 打印变量
message(STATUS "Variable: ${MY_VAR}")

# 使用 CMakePrintHelpers
include(CMakePrintHelpers)
cmake_print_variables(CMAKE_CXX_COMPILER CMAKE_BUILD_TYPE)
cmake_print_properties(TARGETS my_target PROPERTIES COMPILE_OPTIONS)

详细输出:

# 查看详细编译命令
cmake --build build --verbose

# 生成编译数据库
cmake -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON

7. 常见问题

Q1: 如何指定 C++ 标准?

# 方法 1:全局设置
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

# 方法 2:目标级别
target_compile_features(my_target PUBLIC cxx_std_17)

Q2: 如何处理头文件依赖?

target_include_directories(my_target
    PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include    # 公共接口
    PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src       # 内部实现
)

Q3: 如何添加编译宏?

# 目标级别宏
target_compile_definitions(my_target
    PRIVATE DEBUG_MODE
    PUBLIC API_VERSION=2
)

Q4: 如何链接系统库?

# Linux 系统库
target_link_libraries(my_app PRIVATE pthread m dl)

# Windows 系统库
target_link_libraries(my_app PRIVATE ws2_32 advapi32)

# 使用 find_package
find_package(Threads REQUIRED)
target_link_libraries(my_app PRIVATE Threads::Threads)

Q5: 如何处理不同平台的差异?

if(WIN32)
    target_compile_definitions(my_app PRIVATE PLATFORM_WINDOWS)
elseif(APPLE)
    target_compile_definitions(my_app PRIVATE PLATFORM_MACOS)
elseif(UNIX)
    target_compile_definitions(my_app PRIVATE PLATFORM_LINUX)
endif()

Q6: 如何生成静态库和动态库?

# 静态库
add_library(mylib_static STATIC src/lib.cpp)

# 动态库
add_library(mylib_shared SHARED src/lib.cpp)

# 自动选择(根据 BUILD_SHARED_LIBS)
add_library(mylib src/lib.cpp)

Q7: 如何使用预编译头?

# CMake 3.16+
target_precompile_headers(my_target
    PRIVATE
        <iostream>
        <vector>
        <string>
        "my_common_header.h"
)

Q8: 如何处理大型项目的编译时间?

# 1. 使用 Ninja 生成器
cmake -B build -G Ninja

# 2. 启用并行编译
cmake --build build -j $(nproc)

# 3. 使用 ccache
find_program(CCACHE_PROGRAM ccache)
if(CCACHE_PROGRAM)
    set(CMAKE_CXX_COMPILER_LAUNCHER "${CCACHE_PROGRAM}")
    set(CMAKE_C_COMPILER_LAUNCHER "${CCACHE_PROGRAM}")
endif()

# 4. 使用 Unity Build
set(CMAKE_UNITY_BUILD ON)

8. 学习资源


9. 总结

本篇核心要点

1. 命令行选项

  • 构建类型:-DCMAKE_BUILD_TYPE=Debug/Release
  • 编译器:-DCMAKE_CXX_COMPILER=g++
  • 调试:-DCMAKE_VERBOSE_MAKEFILE=ON--verbose

2. 常用指令

  • 基础:cmake_minimum_required, project, add_executable, add_library
  • 现代:target_include_directories, target_link_libraries, target_compile_options
  • 进阶:find_package, FetchContent, configure_file

3. 跨平台

  • CMake 自动处理库文件扩展名差异
  • 使用条件判断处理平台特定代码

4. 最佳实践

  • 使用 target_xxx 命令(现代 CMake)
  • 合理使用 PRIVATE/PUBLIC/INTERFACE
  • 保持项目结构清晰
  • 善用 find_packageFetchContent

学习路径:

✅ 引入篇:理解为什么需要 CMake
✅ 篇一:快速入门,创建基本项目
✅ 篇二:理解核心概念(Target、变量、作用域)
✅ 篇三:掌握常用指令和最佳实践(当前)

系列文章:

下一步:

  • 在真实项目中应用这些知识
  • 深入学习特定领域(测试、打包、安装等)
  • 阅读优秀开源项目的 CMakeLists.txt

最后更新:2026-05-28