CMake 入门教程

PointY Lv1

CMake 简介

什么是 CMake

CMake(Cross-platform Make)是一个开源的跨平台构建系统生成器。它使用简单的配置文件(CMakeLists.txt)来控制软件编译过程,并生成适用于不同平台的原生构建文件(如 Unix Makefile、Visual Studio 项目文件等)。

cmake_generator.png

为什么使用 CMake

  • 跨平台支持:一套配置文件可在 Windows、Linux、macOS 等平台使用
  • IDE 集成:支持生成多种 IDE 项目文件
  • 现代化设计:支持目标为中心的构建系统
  • 强大的依赖管理:内置包查找和依赖管理功能
  • 广泛采用:大量开源项目使用 CMake,已经成为 C++ 项目构建的事实标准

安装 CMake

Windows:
CMake官网下载安装包

Linux (Ubuntu/Debian):

1
2
sudo apt update
sudo apt install cmake

安装完成后,可通过以下命令验证

1
cmake --version

快速开始

第一个 CMake 项目

创建项目结构:

1
2
3
my_project/
├── CMakeLists.txt
└── main.cpp

main.cpp:

1
2
3
4
5
6
#include <iostream>

int main() {
std::cout << "Hello, CMake!" << std::endl;
return 0;
}

CMakeLists.txt:

1
2
3
4
cmake_minimum_required(VERSION 3.15)
project(MyProject VERSION 1.0.0 LANGUAGES CXX)

add_executable(myapp main.cpp)

构建流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 创建构建目录
mkdir build
cd build

# 配置项目
cmake ..

# Ubunt 下将在 build 目录下生成 Makefile
# Windows 下将在 build 目录下生成 sln 和 vcxproj 文件

# Ubuntu 下使用 make 编译
make

# Windows 下使用 MSBuild 编译
MSBuild myapp.sln /p:Configuration=Release

# 在构建目录执行
./myapp
# 输出
> Hello, CMake!

基础概念

CMakeLists.txt 基本结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 1. 指定最低 CMake 版本
cmake_minimum_required(VERSION 3.15)

# 2. 定义项目
project(ProjectName
VERSION 1.0.0
DESCRIPTION "Project description"
LANGUAGES CXX
)

# 3. 设置 C++ 标准
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# 4. 添加可执行文件或库
add_executable(target_name source1.cpp source2.cpp)

# 5. 链接库(如果需要)
target_link_libraries(target_name PRIVATE some_library)

项目(Project)

Project 是 CMake 构建系统的顶层容器,使用 project() 命令定义:

Project 完整语法如下:

1
2
3
4
5
6
7
project(<PROJECT-NAME> [<language-name>...])

project(<PROJECT-NAME>
[VERSION <major>[.<minor>[.<patch>[.<tweak>]]]]
[DESCRIPTION <project-description-string>]
[HOMEPAGE_URL <url-string>]
[LANGUAGES <language-name>...])
  • <PROJECT-NAME> 项目的名称,必须唯一
  • [VERSION <major>[.<minor>[.<patch>[.<tweak>]]]] 项目的版本号,最多包含四个部分
  • [DESCRIPTION <project-description-string>] 项目的简要描述信息
  • [HOMEPAGE_URL <url-string>] 项目主页链接地址
  • [LANGUAGES <language-name>...] 显式声明项目所用的编程语言列表,如 C, CXX

示例:

1
2
3
4
5
6
project(MyProject
VERSION 1.2.3.4
DESCRIPTION "My awesome project"
HOMEPAGE_URL "https://example.com"
LANGUAGES CXX
)

项目定义后,CMake 会自动设置以下变量:

  • PROJECT_NAME: 项目名称
  • PROJECT_VERSION: 项目版本
  • PROJECT_SOURCE_DIR: 项目源码根目录,包含顶级 CMakeLists.txt 文件的目录
  • PROJECT_BINARY_DIR: 项目构建根目录,运行 cmake 命令时所在的目录

目标(Target)

目标是 CMake 的核心概念,代表构建系统的输出产物:

  • 可执行文件目标: add_executable()
  • 库目标: add_library()
  • 自定义目标: add_custom_target()

现代 CMake 推荐以目标为中心进行配置。

add_executable 完整语法如下:

1
2
add_executable(<name> [WIN32] [MACOSX_BUNDLE] [EXCLUDE_FROM_ALL]
[source1] [source2 ...])
  • <name> 目标的名称,必须唯一
  • [WIN32] (可选)在 Windows 上创建 Win32 GUI 应用程序
  • [MACOSX_BUNDLE](可选)在 macOS 上创建应用程序 bundle
  • [EXCLUDE_FROM_ALL](可选)目标不会被默认构建,需要显式指定才会构建
  • [source1] [source2 ...](可选)源文件列表,如果不带源文件列表,需要后续使用target_sources添加源文件

第一个CMake项目 中,我们创建了一个简单的可执行文件 myapp

1
add_executable(myapp main.cpp)

生成器(Generator)

生成器决定 CMake 生成何种构建文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 查看可用生成器
cmake --help

> Generators
The following generators are available on this platform (* marks default):
Green Hills MULTI = Generates Green Hills MULTI files
(experimental, work-in-progress).
* Unix Makefiles = Generates standard UNIX makefiles.
Ninja = Generates build.ninja files.
Ninja Multi-Config = Generates build-<Config>.ninja files.
Watcom WMake = Generates Watcom WMake makefiles.
CodeBlocks - Ninja = Generates CodeBlocks project files.
CodeBlocks - Unix Makefiles = Generates CodeBlocks project files.
CodeLite - Ninja = Generates CodeLite project files.
CodeLite - Unix Makefiles = Generates CodeLite project files.
Eclipse CDT4 - Ninja = Generates Eclipse CDT 4.0 project files.
Eclipse CDT4 - Unix Makefiles= Generates Eclipse CDT 4.0 project files.
Kate - Ninja = Generates Kate project files.
Kate - Unix Makefiles = Generates Kate project files.
Sublime Text 2 - Ninja = Generates Sublime Text 2 project files.
Sublime Text 2 - Unix Makefiles
= Generates Sublime Text 2 project files.

构建类型(Build Type)

CMake 支持多种构建类型:

  • Debug: 包含调试信息,无优化
  • Release: 优化,无调试信息
  • RelWithDebInfo: 优化 + 调试信息
  • MinSizeRel: 最小化大小优化
1
2
3
4
# 设置默认构建类型
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif()

注意: 通常不需要在CMake中设置,而是在IDE中配置


CMake 语法基础

命令语法

CMake 命令不区分大小写,但推荐使用小写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 基本语法
command(argument1 argument2 ...)

# 带关键字参数
command(TARGET target_name
PROPERTY value
OPTIONS opt1 opt2
)

# 注释
# 这是单行注释

#[[
这是多行注释
可以跨越多行
]]

变量

普通变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 设置变量
set(MY_VAR "value")
set(MY_LIST "item1" "item2" "item3")

# 使用变量
message("Value: ${MY_VAR}")

# 取消设置
unset(MY_VAR)

# 变量作用域
function(my_function)
set(LOCAL_VAR "local") # 函数内局部变量
set(PARENT_VAR "parent" PARENT_SCOPE) # 设置父作用域变量
endfunction()

缓存变量

1
2
3
4
5
6
7
8
# 缓存变量(保存在 CMakeCache.txt)
set(MY_CACHE_VAR "value" CACHE STRING "Description")

# 类型:BOOL, FILEPATH, PATH, STRING, INTERNAL
set(ENABLE_FEATURE ON CACHE BOOL "Enable feature")

# 强制设置缓存变量
set(MY_VAR "value" CACHE STRING "Description" FORCE)

环境变量

1
2
3
4
5
# 读取环境变量
message("PATH: $ENV{PATH}")

# 设置环境变量(仅在 CMake 进程中)
set(ENV{MY_VAR} "value")

CMAKE 内置变量

路径相关变量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 项目根目录(最顶层 project() 所在目录)
${PROJECT_SOURCE_DIR}

# 顶层源码目录(最顶层 CMakeLists.txt 所在目录)
${CMAKE_SOURCE_DIR}

# 当前 CMakeLists.txt 所在目录
${CMAKE_CURRENT_SOURCE_DIR}

# 当前正在处理的 CMakeLists.txt 文件所在目录
${CMAKE_CURRENT_LIST_DIR}

# 当前正在处理的 CMakeLists.txt 文件完整路径
${CMAKE_CURRENT_LIST_FILE}

# 项目构建根目录
${PROJECT_BINARY_DIR}

# 顶层构建目录
${CMAKE_BINARY_DIR}

# 当前构建目录
${CMAKE_CURRENT_BINARY_DIR}

# 安装目录前缀(默认 /usr/local)
${CMAKE_INSTALL_PREFIX}

项目信息变量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 项目名称
${PROJECT_NAME}

# 项目版本号
${PROJECT_VERSION}
${PROJECT_VERSION_MAJOR} # 主版本号
${PROJECT_VERSION_MINOR} # 次版本号
${PROJECT_VERSION_PATCH} # 补丁版本号
${PROJECT_VERSION_TWEAK} # 微调版本号

# 项目描述
${PROJECT_DESCRIPTION}

# 项目主页
${PROJECT_HOMEPAGE_URL}

编译器和系统变量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# C++ 编译器路径
${CMAKE_CXX_COMPILER}

# C 编译器路径
${CMAKE_C_COMPILER}

# 编译器 ID(如 GNU, Clang, MSVC)
${CMAKE_CXX_COMPILER_ID}

# 操作系统名称(如 Linux, Windows, Darwin)
${CMAKE_SYSTEM_NAME}

# 系统版本
${CMAKE_SYSTEM_VERSION}

# 处理器架构(如 x86_64, arm64)
${CMAKE_SYSTEM_PROCESSOR}

构建配置变量:

1
2
3
4
5
6
7
8
9
10
11
# 构建类型(Debug, Release, RelWithDebInfo, MinSizeRel)
${CMAKE_BUILD_TYPE}

# C++ 标准版本(如 11, 14, 17, 20)
${CMAKE_CXX_STANDARD}

# 是否要求 C++ 标准
${CMAKE_CXX_STANDARD_REQUIRED}

# 是否启用编译器扩展
${CMAKE_CXX_EXTENSIONS}

输出目录变量:

1
2
3
4
5
6
7
8
# 可执行文件输出目录
${CMAKE_RUNTIME_OUTPUT_DIRECTORY}

# 动态库输出目录
${CMAKE_LIBRARY_OUTPUT_DIRECTORY}

# 静态库输出目录
${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}

平台检测变量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Windows 平台(包括 MinGW)
${WIN32}

# Unix-like 平台(Linux, macOS, BSD)
${UNIX}

# macOS 平台
${APPLE}

# MSVC 编译器
${MSVC}

# MinGW 编译器
${MINGW}

# Cygwin 环境
${CYGWIN}

列表(Lists)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 创建列表
set(MY_LIST "a" "b" "c")
set(MY_LIST "a;b;c") # 等价写法

# 追加元素
list(APPEND MY_LIST "d" "e")

# 插入元素
list(INSERT MY_LIST 0 "first")

# 移除元素
list(REMOVE_ITEM MY_LIST "b")

# 获取长度
list(LENGTH MY_LIST list_length)

# 获取元素
list(GET MY_LIST 0 first_item)

# 查找元素
list(FIND MY_LIST "c" index)

# 排序
list(SORT MY_LIST)

# 反转
list(REVERSE MY_LIST)

字符串操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 字符串拼接
set(STR1 "Hello")
set(STR2 "World")
set(RESULT "${STR1} ${STR2}")

# 字符串替换
string(REPLACE "old" "new" output_var "${input_string}")

# 正则表达式匹配
string(REGEX MATCH "pattern" output_var "${input_string}")

# 大小写转换
string(TOUPPER "${input}" output)
string(TOLOWER "${input}" output)

# 字符串长度
string(LENGTH "${input}" length)

# 子字符串
string(SUBSTRING "${input}" 0 5 output)

条件语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# if-else
if(condition)
# ...
elseif(other_condition)
# ...
else()
# ...
endif()

# 常用条件
if(DEFINED MY_VAR) # 变量已定义
if(EXISTS "${file_path}") # 文件存在
if(IS_DIRECTORY "${path}") # 是目录
if(TARGET target_name) # 目标存在

# 逻辑运算
if(NOT condition)
if(cond1 AND cond2)
if(cond1 OR cond2)

# 比较
if(var EQUAL 5)
if(var LESS 10)
if(var GREATER 3)
if(str STREQUAL "value")
if(ver VERSION_GREATER "1.0")

# 平台检测
if(WIN32)
if(UNIX)
if(APPLE)
if(MSVC)

循环语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# foreach 循环
foreach(item IN LISTS my_list)
message("Item: ${item}")
endforeach()

# 范围循环
foreach(i RANGE 5)
message("i = ${i}") # 0 到 5
endforeach()

foreach(i RANGE 1 10 2)
message("i = ${i}") # 1, 3, 5, 7, 9
endforeach()

# while 循环
set(i 0)
while(i LESS 5)
message("i = ${i}")
math(EXPR i "${i} + 1")
endwhile()

# break 和 continue
foreach(item IN LISTS my_list)
if(item STREQUAL "skip")
continue()
endif()
if(item STREQUAL "stop")
break()
endif()
message("${item}")
endforeach()

函数和宏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# 函数定义
function(my_function arg1 arg2)
message("arg1: ${arg1}")
message("arg2: ${arg2}")

# 返回值通过 PARENT_SCOPE
set(result "value" PARENT_SCOPE)
endfunction()

# 调用函数
my_function("hello" "world")

# 宏定义(与函数的区别:宏在调用处展开,无独立作用域)
macro(my_macro arg)
message("Macro arg: ${arg}")
endmacro()

# 可变参数
# ARGN 是CMake的一个特殊变量,包含了传递给函数的所有参数(除了已命名的参数)
function(print_all)
foreach(arg IN LISTS ARGN)
message("${arg}")
endforeach()
endfunction()

print_all("a" "b" "c")

# 命名参数
function(create_target)
set(options OPTIONAL) # 开关参数
set(oneValueArgs NAME TYPE) # 单值参数,包括 NAME 和 TYPE
set(multiValueArgs SOURCES LIBS) # 多值参数,包括 SOURCES 和 LIBS

cmake_parse_arguments(ARG # cmake_parse_arguments 解析传入的参数,将它们转换为前缀为 ARG_ 的变量
"${options}"
"${oneValueArgs}"
"${multiValueArgs}"
${ARGN}
)

message("Name: ${ARG_NAME}")
message("Sources: ${ARG_SOURCES}")
endfunction()

create_target(
NAME myapp
TYPE EXECUTABLE
SOURCES main.cpp util.cpp
LIBS pthread
)

调试技巧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 打印变量值
message(STATUS "Project source dir: ${PROJECT_SOURCE_DIR}")
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")

# 打印所有变量(调试用)
get_cmake_property(_variableNames VARIABLES)
foreach(_variableName ${_variableNames})
message(STATUS "${_variableName}=${${_variableName}}")
endforeach()

# 打印目标属性
get_target_property(MYAPP_SOURCES myapp SOURCES)
message(STATUS "myapp sources: ${MYAPP_SOURCES}")

# 启用详细构建输出
set(CMAKE_VERBOSE_MAKEFILE ON)

# 或在命令行使用:
# cmake --build build --verbose

跨平台路径处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# CMake 自动处理路径分隔符,推荐始终使用正斜杠 /
set(MY_PATH "src/utils/logger.cpp") # 所有平台都支持
set(FULL_PATH ${PROJECT_SOURCE_DIR}/src/main.cpp) # 推荐写法

# 转换外部路径(如环境变量)为 CMake 格式
file(TO_CMAKE_PATH "$ENV{SOME_PATH}" CONVERTED_PATH)

# 路径拼接(CMake 3.20+)
cmake_path(APPEND PROJECT_SOURCE_DIR "src" "main.cpp" OUTPUT_VARIABLE FULL_PATH)

# 路径解析
get_filename_component(FILENAME ${FULL_PATH} NAME) # 文件名
get_filename_component(DIR ${FULL_PATH} DIRECTORY) # 目录
get_filename_component(EXT ${FULL_PATH} EXT) # 扩展名
get_filename_component(NAME_WE ${FULL_PATH} NAME_WE) # 无扩展名的文件名

预编译头文件(PCH)加速编译

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# CMake 3.16+ 支持
target_precompile_headers(myapp PRIVATE
<iostream>
<vector>
<string>
<memory>
)

# 或使用自定义头文件
target_precompile_headers(myapp PRIVATE
"${PROJECT_SOURCE_DIR}/include/pch.h"
)

# 多个目标共享 PCH(源目标需使用 PUBLIC 或 INTERFACE)
target_precompile_headers(mylib PUBLIC pch.h)
target_precompile_headers(myapp REUSE_FROM mylib)

参考资源

  • 标题: CMake 入门教程
  • 作者: PointY
  • 创建于 : 2025-12-14 16:13:52
  • 更新于 : 2026-01-12 20:51:22
  • 链接: https://siyuhong.github.io/2025/12/14/cmake-tutorial01/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论