目录
[1. CMake概念](#1. CMake概念)
[1.1 工作流程](#1.1 工作流程)
[2. 构建一个Hello CMake项目](#2. 构建一个Hello CMake项目)
[2.1 命令解析](#2.1 命令解析)
[3. 项目指令总结](#3. 项目指令总结)
[3.1 项目级指令](#3.1 项目级指令)
[3.2 变量与字符串操作](#3.2 变量与字符串操作)
[3.3 条件控制](#3.3 条件控制)
[if / elseif / else / endif](#if / elseif / else / endif)
[3.4 查找依赖](#3.4 查找依赖)
[3.5 信息输出](#3.5 信息输出)
[3.6 目录与包含](#3.6 目录与包含)
[3.7 目标构建](#3.7 目标构建)
[3.8 测试](#3.8 测试)
[3.9 安装](#3.9 安装)
[install(TARGETS ...)](#install(TARGETS ...))
[install(FILES ...)](#install(FILES ...))
[3.10 文件操作](#3.10 文件操作)
[file(GLOB ...)](#file(GLOB ...))
1. CMake概念
CMake(Cross-platform Make)是一个开源的、跨平台的构建系统生成器。它使用简单的平台和编译器独立的配置文件(CMakeLists.txt)来控制软件编译过程。
-
跨平台:一套脚本,多处使用(Linux、Windows、macOS)
-
生成本地构建系统:可生成 Makefile、Ninja、Visual Studio 解决方案、Xcode 项目等
-
支持多语言:C、C++、CUDA、Fortran、Objective-C 等
-
强大的查找库功能:通过
find_package自动定位系统安装的依赖库 -
可扩展:支持自定义模块和函数
1.1 工作流程
源码目录 构建目录 编译产物
├─ CMakeLists.txt ──→ cmake .. ──→ Makefile ──→ make ──→ 可执行文件/库
├─ src/
└─ include/
经典的两步构建法(out-of-source build):
1. cmake -S . -B build ------ 读取源码目录,在 build 目录生成构建系统
2. cmake --build build ------ 执行实际编
- 生成构建系统
-
Windows + MinGW: 使用 "MinGW Makefiles"
-
Windows + MSVC : 使用 "NMake Makefiles" 或 "Visual Studio 17 2022"
-
Linux / WSL : 使用 "Unix Makefiles"(可省略 -G 参数)
cmake .. -G "MinGW Makefiles"
- 编译全部目标(多线程加速)
cmake --build . -j$(nproc)
2. 构建一个Hello CMake项目
.
├── CMakeLists.txt
└── test.cc
# 实则最低的cmake版本
cmake_minimum_required(VERSION 3.18)
# 设置项目的名称
project(Cmake02)
# 添加构建⽬标
add_executable(Mytest test.cc)
cpp
include <iostream>
int main()
{
std::cout << "Hello, CMake!" << std::endl;
return 0;
}
执行以下命令:
cmake -S . -B build
cd build
cmake --build .
2.1 命令解析
cmake_minimum_required(VERSION [...] [FATAL_ERROR])
指定项目所需的最低CMake版本,应放在顶部CMakeLists.txt的第一行。
-
VERSION 后跟着版本号
-
FATAL_ERROR 设置不符合版本后的报错
project(<PROJECT-NAME>)
指定项目名字,放在顶级CMakeLists文件的第二行,子目录中⼀般无需调用。

add_executable(Mytest test.cc)
3. 项目指令总结
3.1 项目级指令
cmake_minimum_required
-
作用:声明构建该项目所需的最低 CMake 版本,确保解析器行为一致。
-
样例 :
cmake_minimum_required(VERSION 3.18) -
使用场景 : 必须放在
CMakeLists.txt的第一行(非注释)。 -
CMake 3.18+ 扩展 :建议直接声明
VERSION 3.18,可配合FATAL_ERROR显式终止(虽然已默认),并可利用策略(Policy)自动提升至新版本行为。
project
-
作用:定义项目名称及项目所使用的编程语言。
-
样例 :
project(MyProjectName VERSION 1.0.0 LANGUAGES CXX) -
使用场景 :根目录
CMakeLists.txt中必须调用一次;子目录中可选。 -
CMake 3.18+ 扩展 :现代写法推荐使用
VERSION和DESCRIPTION字段,配合project()自动生成的变量(如${PROJECT_NAME}、${PROJECT_VERSION})统一管理版本号。
enable_testing
-
作用 :启用项目的测试机制,使得
add_test()生效,并支持ctest命令行运行。 -
样例 :根目录调用一次
enable_testing(),子目录使用add_test()注册用例。 -
使用场景:任何包含单元测试的项目根目录。
-
CMake 3.18+ 扩展 :与
include(CTest)配合可提供更丰富的测试仪表盘;3.18 后ctest支持--output-on-failure等更友好的输出控制。
3.2 变量与字符串操作
set
-
作用:创建或修改变量,可为普通变量、列表(分号分隔的字符串)或缓存变量。
-
样例:
-
set(SRCS main.cc utils.cc) -
set(DEBUG ON CACHE BOOL "Debug mode")
-
-
使用场景:收集源文件列表、配置编译选项、设置开关。
-
CMake 3.18+ 扩展 :现代 CMake 更推荐用
target_sources()直接附加到目标,而非全局set收集源文件;设置编译选项优先使用target_compile_options()。
list
-
作用:对 CMake 列表(分号分隔的字符串)进行增删改查。
-
样例:
-
list(APPEND mylist a b c)--- 追加元素 -
list(REMOVE_ITEM mylist a)--- 删除指定元素 -
list(LENGTH mylist len)--- 获取长度
-
-
使用场景:动态管理编译选项列表、源文件列表、链接库列表。
-
CMake 3.18+ 扩展 :列表操作功能稳定;现代项目中建议减少全局列表的使用,转向目标级属性(
target_*系列)。
string
-
作用:字符串替换、大小写转换、正则匹配等。
-
样例:
-
string(REPLACE "foo" "bar" OUT "${IN}")--- 替换子串 -
string(TOUPPER "debug" UPPER)--- 转大写,结果DEBUG
-
-
使用场景:将列表转换为空格分隔的字符串(供编译器使用)、构建类型大小写标准化。
-
CMake 3.18+ 扩展 :
string(REPEAT)、string(JSON)等新增功能可用于生成复杂配置。
option
-
作用:定义一个布尔型缓存变量(ON/OFF),通常用于控制功能开关。
-
样例:`option(BUILD_SHARED_LIBS "Build shared libraries" OFF)
-
使用场景:控制是否构建示例、是否开启测试、是否编译共享库等。
-
CMake 3.18+ 扩展 :现代项目常结合
cmake_dependent_option()(CMakeDependentOption模块)实现条件化选项。
3.3 条件控制
if/elseif/else/endif
-
作用:根据条件执行不同的 CMake 代码块。
-
样例:
-
if(TARGET mylib) -
if(EXISTS path) -
if(var STREQUAL "value")
-
-
使用场景:平台差异处理、编译器适配、可选依赖检测。
-
CMake 3.18+ 扩展:逻辑表达式能力增强;现代 CMake 更推荐将条件收敛到目标属性中,减少全局条件分支。
3.4 查找依赖
find_package
-
作用:查找并加载外部库的配置(Config 模块)或使用 Find 模块定位库。
-
样例:
-
find_package(Threads REQUIRED) -
find_package(spdlog CONFIG REQUIRED)
-
-
使用场景:项目依赖第三方库(如 Boost、Protobuf、ZLIB)时必选。
-
CMake 3.18+ 扩展 :支持
REQUIRED强制查找失败时终止;现代项目推荐库提供*Config.cmake,使用CONFIG模式替代传统MODULE模式。
find_path
-
作用:查找包含指定头文件的目录路径。
-
样例 :
find_path(MYLIB_INCLUDE_DIR mylib.h PATHS /opt/mylib/include) -
使用场景:当库未提供 CMake 配置时,手动定位头文件目录。
-
CMake 3.18+ 扩展 :结果变量默认写入缓存;现代项目建议封装为
Find*.cmake模块或要求库提供 Config 模块,减少手动查找。
find_library
-
作用:查找指定名称的库文件路径。
-
样例 :
find_library(MYLIB_LIBRARY NAMES mylib PATHS /opt/mylib/lib) -
使用场景 :手动定位
.so/.a/.lib文件,通常与find_path配对使用。 -
CMake 3.18+ 扩展 :与
find_package相比,手动查找方式已显落后;现代做法是将库封装为 Imported Target。
find_program
-
作用:查找可执行程序的路径。
-
样例 :
find_program(PROTOC protoc PATHS /usr/local/bin) -
使用场景 :需要调用外部代码生成工具(如
protoc、thrift)时使用。 -
CMake 3.18+ 扩展 :常与
add_custom_command配合实现代码生成;建议加REQUIRED确保工具存在。
3.5 信息输出
message
-
作用:向用户输出信息、警告或错误。
-
样例:
-
message(STATUS "Configuring done")--- 普通状态信息(带--前缀) -
message(WARNING "Something is wrong")--- 警告 -
message(FATAL_ERROR "Abort")--- 致命错误,立即终止
-
-
使用场景:配置阶段打印关键变量、依赖查找结果。
-
CMake 3.18+ 扩展 :
message(VERBOSE "...")支持更细粒度的日志级别,仅在cmake --log-level=verbose时输出。
3.6 目录与包含
target_include_directories
- 作用 :为特定目标 精确添加头文件搜索路径,通过
PUBLIC/PRIVATE/INTERFACE控制传递性,避免全局污染。
- 样例:
cpp
target_include_directories(myapp PRIVATE include)
target_include_directories(mylib PUBLIC ${CMAKE_SOURCE_DIR}/include)
target_include_directories(mylib INTERFACE ${CMAKE_SOURCE_DIR}/include) # 仅影响使用者
-
使用场景 :现代 CMake 项目的首选方式。
PRIVATE用于内部实现依赖的头文件;PUBLIC用于暴露给下游的 API 头文件;INTERFACE用于纯头文件库或仅影响链接方的配置。 -
CMake 3.18+ 扩展 :支持
FILE_SET(3.23+ 正式完善)将头文件作为目标的一部分进行安装和导出;与target_sources(... PUBLIC FILE_SET HEADERS BASE_DIRS ...)结合,可完全替代手动install(INCLUDE)逻辑,实现更清晰的库接口管理。
add_subdirectory
-
作用 :进入子目录并解析其
CMakeLists.txt,将子目录纳入构建体系。 -
样例 :
add_subdirectory(third_party/googletest) -
使用场景 :多目录项目组织;
EXCLUDE_FROM_ALL表示默认不构建该目录目标,仅在被依赖或显式构建时生效。 -
CMake 3.18+ 扩展 :可配合
FetchContent将外部项目通过add_subdirectory引入;SYSTEM属性(通过target_include_directories(SYSTEM ...))可抑制第三方头文件警告。
3.7 目标构建
add_library
-
作用 :定义一个库目标(静态库
.a/.lib或共享库.so/.dll)。 -
样例:
-
add_library(mylib STATIC src.cc)--- 显式静态库 -
add_library(mylib SHARED src.cc)--- 显式共享库 -
add_library(mylib INTERFACE)--- 头文件-only库(无源文件)
-
-
使用场景:项目内模块拆分、封装公共代码。
-
CMake 3.18+ 扩展 :推荐使用
target_sources()分阶段添加源文件;OBJECT库可用于编译一次、多次链接。
add_executable
-
作用:定义一个可执行文件目标。
-
样例 :
add_executable(myapp main.cc utils.cc) -
使用场景:构建测试程序、示例程序、主程序入口。
-
CMake 3.18+ 扩展 :
add_executable(myapp WIN32 main.cc)可指定 Windows GUI 子系统;现代 CMake 建议用target_sources()追加源文件。
target_link_libraries
-
作用:为目标(库或可执行文件)指定链接依赖。
-
样例 :
target_link_libraries(myapp PRIVATE mylib Boost::system) -
使用场景:任何需要链接库的目标都必须使用。
-
CMake 3.18+ 扩展 :必须显式指定可见性 ---
PRIVATE(仅自身使用)、PUBLIC(传递给依赖方)、INTERFACE(仅传递,自身不链接)。这是现代 CMake 的核心实践,替代了旧式的全局link_directories。
set_target_properties
-
作用:设置目标的属性(如编译标志、输出名称、C++ 标准等)。
-
样例:
-
set_target_properties(mylib PROPERTIES CXX_STANDARD 17) -
set_target_properties(mylib PROPERTIES OUTPUT_NAME "mylib_alias")
-
-
使用场景:对单个目标施加特殊编译选项。
-
CMake 3.18+ 扩展 :
COMPILE_FLAGS已不推荐;现代用法是target_compile_options()、target_compile_definitions()、target_compile_features()等命令,语义更清晰,支持PRIVATE/PUBLIC/INTERFACE。
3.8 测试
add_test
-
作用:向 CTest 注册一个测试用例。
-
样例 :
add_test(NAME my_test COMMAND my_test_runner --gtest_filter=Foo.*) -
使用场景 :配合
enable_testing()使用,ctest会自动发现并执行。 -
CMake 3.18+ 扩展 :支持
WORKING_DIRECTORY、CONFIGURATIONS等参数;可配合gtest_discover_tests()(GoogleTest 模块)自动发现 GoogleTest 用例。
3.9 安装
install(TARGETS ...)
-
作用:指定构建完成后将目标(库/可执行文件)安装到哪个目录。
-
样例 :
install(TARGETS mylib EXPORT MyLibTargets LIBRARY DESTINATION lib ARCHIVE DESTINATION lib RUNTIME DESTINATION bin) -
使用场景:制作可分发软件包、将库安装到系统目录。
-
CMake 3.18+ 扩展 :现代 CMake 强烈建议配合
EXPORT生成目标导出文件(*Targets.cmake),使安装后的库能被其他 CMake 项目通过find_package直接使用。
install(FILES ...)
-
作用:安装任意文件(通常是头文件、配置文件、资源文件)。
-
样例 :
install(FILES mylib.h DESTINATION include) -
使用场景:安装公共头文件供其他项目使用。
-
CMake 3.18+ 扩展 :
install(DIRECTORY ...)可批量安装整个目录;配合COMPONENT可实现分组件安装。
3.10 文件操作
file(GLOB ...)
-
作用:按通配符模式收集文件列表到变量中。
-
样例 :
file(GLOB_RECURSE SRCS "src/*.cc")--- 递归收集 -
使用场景:快速收集源文件,避免手动列举。
-
CMake 3.18+ 扩展 :CMake 官方不推荐使用
GLOB收集源文件 ,因为新增/删除文件不会自动触发 CMake 重新配置(需手动 touch)。现代项目推荐显式列出源文件,或配合CONFIGURE_DEPENDS标志(CMake 3.12+):file(GLOB SRCS CONFIGURE_DEPENDS "*.cc")。