CMake

目录

[1. CMake概念](#1. CMake概念)

[1.1 工作流程](#1.1 工作流程)

[2. 构建一个Hello CMake项目](#2. 构建一个Hello CMake项目)

[2.1 命令解析](#2.1 命令解析)

[3. 项目指令总结](#3. 项目指令总结)

[3.1 项目级指令](#3.1 项目级指令)

cmake_minimum_required

project

enable_testing

[3.2 变量与字符串操作](#3.2 变量与字符串操作)

set

list

string

option

[3.3 条件控制](#3.3 条件控制)

[if / elseif / else / endif](#if / elseif / else / endif)

[3.4 查找依赖](#3.4 查找依赖)

find_package

find_path

find_library

find_program

[3.5 信息输出](#3.5 信息输出)

message

[3.6 目录与包含](#3.6 目录与包含)

target_include_directories

add_subdirectory

[3.7 目标构建](#3.7 目标构建)

add_library

add_executable

target_link_libraries

set_target_properties

[3.8 测试](#3.8 测试)

add_test

[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 ------ 执行实际编

  1. 生成构建系统
  • Windows + MinGW: 使用 "MinGW Makefiles"

  • Windows + MSVC : 使用 "NMake Makefiles" 或 "Visual Studio 17 2022"

  • Linux / WSL : 使用 "Unix Makefiles"(可省略 -G 参数)

cmake .. -G "MinGW Makefiles"

  1. 编译全部目标(多线程加速)

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+ 扩展 :现代写法推荐使用 VERSIONDESCRIPTION 字段,配合 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)

  • 使用场景 :需要调用外部代码生成工具(如 protocthrift)时使用。

  • 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_DIRECTORYCONFIGURATIONS 等参数;可配合 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")

相关推荐
郝学胜-神的一滴3 小时前
Qt 高级开发 022:栅格布局深度实战
开发语言·c++·qt·软件构建·用户界面
郝学胜-神的一滴2 天前
Qt 高级开发 020:水平布局手写代码实战
开发语言·c++·qt·系统架构·软件构建·用户界面
workflower3 天前
具身智能研究对象:物理交互中的智能行为
设计模式·动态规划·软件工程·软件构建·scrum
草莓熊Lotso3 天前
【CMake】静态库的编译、链接与引用全解析
linux·c语言·数据库·c++·软件工程·cmake
郝学胜-神的一滴3 天前
CMake 012:Linux 下动态库与可执行程序的单文件构建
linux·服务器·开发语言·c++·软件构建·cmake
皮皮木子4 天前
rl_locomotion 编译过程三
编译·强化学习·cmake·蒸馏
郝学胜_神的一滴4 天前
CMake 012:Linux 下动态库与可执行程序的单文件构建
c++·cmake
皮皮木子4 天前
rl_locomotion 编译过程四
编译·cmake
cy_cy0024 天前
折幕影院怎样实现虚实一体?
大数据·科技·人机交互·交互·软件构建