CMake Memo
CMake Memo
作者:OL
本文档是参考了以下作者
相关链接:
- 编程Cat加加的CMake教程视频:https://www.bilibili.com/video/BV1rqPYehEsW
- 爱编程的大丙的CMake教程文章:https://subingwen.cn/cmake/CMake-primer/
- CMake 教程 | 菜鸟教程:https://www.runoob.com/cmake/cmake-tutorial.html
- CMake 官网:https://cmake.org/
- CMake 官方文档:https://cmake.org/cmake/help/latest/guide/tutorial/index.html
- CMake 源码:https://github.com/Kitware/CMake
- CMake 源码:https://gitlab.kitware.com/cmake/cmakeku
[]和<>中为可选项
命令行命令
-
cmake -G可以查看所有的生成器
默认使用
Visual Studio 17 2022,前面带*的就是默认的生成器
1 | PS C:\Users\Administrator>cmake -G |
-
指定项目根目录,CMake 将在那里被建造。
-
指定构建目录,CMake 将输出生成的构建系统,以及当构建系统已经运行。未指定时,默认为当前工作目录。
-
在指定的构建目录中运行构建系统。这是通用的所有生成器的命令。对于多配置生成器,期望的配置请求可通过以下方式进行:
cmake --build <dir> --config <cfg><dir>是生成器的目录<cfg>可以是Release、Debug例如
MinGW Makefiles要使用make编译,此命令可以将所有的生成器的命令统一 -
cmake -Dcmake -D是 CMake 中运行时手动设置 / 覆盖变量的核心命令,作用是:在执行cmake生成构建文件(Makefile、VS 项目等)时,直接指定 CMake 变量的值,无需修改CMakeLists.txt。它的使用场景非常广泛(比如启用测试、切换构建类型、指定路径等)
核心语法:
1
cmake -D<变量名>=<变量值> ..
-
-D:固定前缀,后面紧跟「变量名 = 变量值」(无空格,=前后不能有空格); -
<变量名>:可以是 CMake 内置变量(如CMAKE_BUILD_TYPE),也可以是你自定义的选项(如OL_WITH_TESTS); -
<变量值>:根据变量类型不同,支持 3 类值(布尔、字符串、路径); -
..:最后是源文件目录(即CMakeLists.txt所在的上级目录,固定写法,除非你的目录结构特殊)。关键细节:
-
变量名大小写敏感(
OL_WITH_TESTS≠ol_with_tests); -
多个
-D可以叠加(设置多个变量); -
变量值如果包含空格或特殊字符,需要用引号包裹(如
-DINSTALL_PATH="C:/Program Files/ol")。
-
CMake语法
- cmake_minimum_required(VERSION major.minor[.patch])
CMake要求的最低版本号,通常只需要主版本号和次版本号
- project(项目名 [VERSION 版本号] [DESCRIPTION "项目描述"] [LANGUAGES 编程语言...])
编程语言默认是C/C++,选择C++的话应该填CXX
- target相关命令
可以把target当做编程语言的类和对象看待


1. 创建可执行目标(ADD_EXECUTABLE)
命令格式:
1 | add_executable(<target> [WIN32] [MACOSX_BUNDLE] <source1> [<source2> ...]) |
参数说明:
<target>:目标名称(全局唯一)。WIN32:Windows 下创建 GUI 程序(无控制台窗口)。MACOSX_BUNDLE:macOS 下创建应用 bundle 包。<sourceN>:源文件路径列表,可空。
2. 创建库目标(ADD_LIBRARY)
命令格式:
1 | add_library(<target> <type> <source1> [<source2> ...]) |
参数说明:
<target>:库目标名称(全局唯一)。<type>:库类型(必填):STATIC:静态库(.a/.lib)。SHARED:动态库(.so/.dll)。MODULE:模块库(动态加载插件)。OBJECT:对象库(仅编译不归档)。
<sourceN>:源文件路径列表,可空。
3. 设置目标包含目录(TARGET_INCLUDE_DIRECTORIES)
命令格式:
1 | target_include_directories(<target> <scope> <dir1> [<dir2> ...]) |
参数说明:
<target>:目标名称。<scope>:作用域(必填):PRIVATE:仅当前目标编译生效。INTERFACE:仅依赖该目标的目标生效。PUBLIC:当前目标 + 依赖目标均生效。
<dirN>:头文件搜索目录路径。
4. 设置预编译宏(TARGET_COMPILE_DEFINITIONS)
命令格式:
1 | target_compile_definitions(<target> <scope> <def1> [<def2> ...]) |
参数说明:
<target>:目标名称。<scope>:作用域(必填):PRIVATE/INTERFACE/PUBLIC(同上方)。<defN>:预编译宏,格式如MY_DEF或MY_DEF=1。
5. 设置编译选项(TARGET_COMPILE_OPTIONS)
命令格式:
1 | target_compile_options(<target> <scope> <opt1> [<opt2> ...]) |
参数说明:
<target>:目标名称。<scope>:作用域(必填):PRIVATE/INTERFACE/PUBLIC(同上方)。<optN>:编译选项,如-Wall(Linux)、/W4(MSVC)。
6. 链接依赖库 / 选项(TARGET_LINK_LIBRARIES)
命令格式:
1 | target_link_libraries(<target> <scope> <dep1> [<dep2> ...]) |
参数说明:
<target>:目标名称。<scope>:作用域(必填):PRIVATE/INTERFACE/PUBLIC(同上方)。<depN>:依赖项,可是:- 自定义库目标名(如
MyLib)。 - 系统库名(如
pthread)。 - 链接选项(需引号包裹,如
-Wl,-rpath=./lib)。
- 自定义库目标名(如
7. 设置目标属性(SET_TARGET_PROPERTIES)
命令格式:
1 | set_target_properties(<target1> [<target2> ...] PROPERTIES <prop1> <val1> <prop2> <val2> ...) |
参数说明:
<targetN>:一个 / 多个目标名称。<propN>:常用属性:CXX_STANDARD:C++ 标准(如 17、20)。CXX_STANDARD_REQUIRED:是否强制标准(ON/OFF)。OUTPUT_NAME:目标输出文件名(覆盖默认名)。RUNTIME_OUTPUT_DIRECTORY:可执行 / 动态库输出目录(Windows)。LIBRARY_OUTPUT_DIRECTORY:动态库输出目录(Linux/Mac)。ARCHIVE_OUTPUT_DIRECTORY:静态库输出目录。
<valN>:属性对应值。
8. 补充目标源文件(TARGET_SOURCES)
命令格式:
1 | target_sources(<target> <scope> <source1> [<source2> ...]) |
参数说明:
<target>:已创建的目标名称。<scope>:作用域(必填):PRIVATE/INTERFACE/PUBLIC(INTERFACE 仅适用于库)。<sourceN>:需补充的源文件路径列表。
9. 创建别名目标(ALIAS)
命令格式:
1 | add_library(<alias> ALIAS <target>) |
参数说明:
<alias>:别名(规范格式:<命名空间>::<目标名>,如MyLib::Core)。<target>:已创建的库 / 可执行目标名称。
- 变量

CMAKE_开头的变量是CMAKE预定义的变量
还有其他的,如:

变量存储的都是字符串,且用列表存储,;是专门用于列表的分隔符
-
set(varName value... [PARENT_SCOPE])定义变量例如
set(myVar hello world),相当于myVar=hello;world下列语句与之等价:
set(myVar hello;world)set(myVar "hello;world")
如果不需要将
;用于列表的分隔符,可以用转义字符\;,例如set(myVar "hello\;world")如果**需要在字符串有空格
**可以用双引号""包裹字符串,例如set(myVar "hello world")当需要引用变量,可以使用
${变量名},可以出现在""中,例如${myVar},set(pet "${myVar}")字符串也可以用
[[]],也可以[=^n[]=^n](n>=0),其中=^n表示可以任意=但左右两边必须一样,这种方式不会进行替换,所以可以打印${} -
set(varName)、unset(varName)设置未定义变量,set(varName "")清空变量的值 -
set(ENV{varName} value)设置环境变量这种方式只是临时添加环境变量,不会改变系统环境变量
-
set(varName value... CACHE type "helpString" [FORCE])定义缓存变量(Cache Variable)存放在
\build\CMakeCache.txt- 参数
type主要用于图形界面的处理,虽然变量都为字符串,但是图形界面中会将变量分为以下类型:

BOOL类型值的设置
-
真:true、yes、Y、1
-
假:false、no、n、0
对于BOOL,也可以使用
option(optVar "helpString" [初始值]),相当于没有FORCE的BOOL值,即
set(optVar [初始值] CACHE BOOL helpString)
例如下面示例:
1
2
3
4
5cmake_minimum_required(VERSION 3.10)
project(LearnCMake VERSION 0.1 LANGUAGES CXX)
set(DEMO_VAR off CACHE BOOL "is test")
message("DEMO_VAR = ${DEMO_VAR}") - 参数
| ON | OFF |
|---|---|
![]() |
![]() |
-
参数
"helpString"是说明变量的用途 -
参数
FORCE是用于强制刷新变量-
无
FORCE,则当缓存没有这个变量才写入,有则不会更新 -
有
FORCE,则会强制更新缓存中的值
-
-
普通变量 VS 缓存变量
- 对于同名的普通变量和缓存变量,优先使用普通变量,例子:
1
2
3
4
5
6
7
8
9set(myVar "Hello World!" CACHE STRING "My Variable" FORCE)
set(myVar "normal variable")
message(STATUS "myVar=" ${myVar})
unset(myVar)
message(STATUS "myVar=" ${myVar})
# 输出
# myVar=normal variable
# myVar=Hello World!-
推荐的命名规范:普通变量用驼峰命名,缓存变量用纯大写字母
-
作用域:普通变量是局部(函数,子目录),缓存变量是全局
-
命令行定义缓存变量
-
cmake -D "varName:type=value"定义缓存变量 -
cmake -U ""删除缓存变量例如:
1
2cmake -D "MY_VAR:STRING=hello world" # 定义MY_VAR缓存变量
cmake -U "MY*" # 删除所有MY开头的缓存变量
-
- message()
为用户显示一条消息
message([STATUS|WARNING|AUTHOR_WARNING|FATAL_ERROR|SEND_ERROR] "message to display" ...)
- (无) :重要消息
- STATUS :非重要消息
- WARNING:CMake 警告, 会继续执行
- AUTHOR_WARNING:CMake 警告 (dev), 会继续执行
- SEND_ERROR:CMake 错误, 继续执行,但是会跳过生成的步骤
- FATAL_ERROR:CMake 错误, 终止所有处理过程
1 | set(a "hello") |
- 作用域block()和endblock()
例子1:
1 | set(myVar cat) |
例子2:
1 | set(myVar cat) |
例子3:
1 | set(myVar cat) |
- 条件判断
1 | if(expr) |
expr:


如果expr为字符串,那么是TRUE常量带引号模式才为真


例子:
1 | set(v "hello") # 变量规则:此时v的值不是FALSE常量,所以为TRUE |
- 逻辑运算符
- 与:
AND - 或:
OR - 非:
NOT
- 比较运算符



- 循环语句
-
continue():跳过循环 -
break():退出循环
-
foreach循环
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
43foreach(<loop_var> <items>)
<commands>
endforeach()
# Example1
message("Example1:")
foreach(v a b c)
message("v=${v}")
endforeach()
# Example2 使用IN和LISTS关键字
message("Example2:")
set(list1 2 4 6 8)
set(list2 1 3 5 7)
foreach(v IN LISTS list1 list2) # 也可以 foreach(v IN ITEMS ${list1} ${list2}) 注意这里是ITEMS
message("v=${v}")
endforeach()
# Example3 使用IN和ITEMS关键字 等价于 Example1
message("Example3:")
foreach(v IN ITEMS a b c)
message("v=${v}")
endforeach()
# Example4 使用IN和ZIP_LISTS关键字,同时获取不同列表的相同位置
message("Example4:")
set(list3 1 2 3 4)
set(list4 5 6 7 8)
foreach(v IN ZIP_LISTS list3 list4)
message("v=(${v_0},${v_1})") # 变量名_数字 来分别获取不同列表的元素
endforeach()
# Example5 使用RANGE关键字,设定开始和结尾
message("Example5:")
set(start 1)
set(end 8)
set(step 2) # 可选项
foreach(v RANGE ${start} ${end} ${step}) # step是缺省值,默认是1
message("v=${v}")
endforeach() -
while循环
1
2
3while(<condition>)
<commands>
endwhile()
- 注释
#:行注释#[[]]:块注释
- 函数
1 | function(function_name args...) |
-
传参方式:
- 命名参数
1
2
3
4
5
6
7
8
9
10function(my_function a b) # a 和 b 是命名参数
message("============= my_function =============")
message("a:${a},b:${b}")
endfunction()
my_function(hello world) # 调用
# 输出
============= my_function =============
a:hello,b:world- 未命名参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18function(my_function2 a b) # a 和 b 是命名参数
message("============= my_function2 =============")
message("a:${a},b:${b}")
message("ARGC:${ARGC}")
message("ARGV:${ARGV}")
message("ARGV0:${ARGV0},ARGV1:${ARGV1}")
message("argn:${ARGN}")
endfunction()
my_function2(hello world 1 2 3) # 调用,这里多出来的参数就是未命名参数
# 输出
============= my_function2 =============
a:hello,b:world
ARGC:5
ARGV:hello;world;1;2;3
ARGV0:hello,ARGV1:world
argn:1;2;3
ARGVn,n是访问第几个未命名参数,以0开始(0-based),n<ARGC,否则未定义行为- 关键字参数
这个比较复杂,可以查相关资料,个人觉得用处不大
-
函数返回值:
返回值获取例子(引用):
1 | # Example_func3 |
- 宏
和C++的宏类似,都是用字符串替换
1 | macro(myMacro arg...) |
- CMake中常用的预定义宏:
| 宏 | 功能 |
|---|---|
| PROJECT_SOURCE_DIR | 使用cmake命令后紧跟的目录,一般是工程的根目录 |
| PROJECT_BINARY_DIR | 执行cmake命令的目录 |
| CMAKE_CURRENT_SOURCE_DIR | 当前处理的CMakeLists.txt所在的路径 |
| CMAKE_CURRENT_BINARY_DIR | target 编译目录 |
| EXECUTABLE_OUTPUT_PATH | 重新定义目标二进制可执行文件的存放位置 |
| LIBRARY_OUTPUT_PATH | 重新定义目标链接库文件的存放位置 |
| PROJECT_NAME | 返回通过PROJECT指令定义的项目名称 |
| CMAKE_BINARY_DIR | 项目实际构建路径,假设在build目录进行的构建,那么得到的就是这个目录的路径 |
- List
官方文档:list — CMake 4.2.0 Documentation
1. 定义列表
1 | # 1. 空列表 |
2. 访问元素与长度
1 | # 定义列表 |
3. 添加元素(APPEND/PREPEND)
1 | set(my_list 1 2) |
4. 删除元素(REMOVE_AT/REMOVE_ITEM/REMOVE_DUPLICATES)
1 | set(my_list 0 1 5 2 3 4 2) |
5. 查找(FIND)
1 | set(my_list a b c d) |
6. 拼接和切片(JOIN/SUBLIST)
1 | set(my_list 0 1 2 3 4) |
7. 排序(SROT)
命令格式:
1 | list(SORT <list> [COMPARE <compare>] [CASE <case>] [ORDER <order>]) |
参数说明:
-
COMPARE <compare>:排序规则(默认STRING):STRING:按字母 / 数字字符串排序(默认)。FILE_BASENAME:按文件基名排序(如a/1.txt、b/2.txt按1.txt、2.txt排序)。NATURAL:自然排序(如file10.txt排在file2.txt后,默认STRING会相反)。
-
CASE <case>:大小写敏感度(默认SENSITIVE):SENSITIVE:区分大小写(默认,A<a)。INSENSITIVE:不区分大小写(A=a)。
-
ORDER <order>:排序顺序(默认ASCENDING):ASCENDING:升序(默认)。DESCENDING:降序。
示例:
1 | # 1. 默认排序(STRING + SENSITIVE + ASCENDING) |
8. 翻转列表(REVERSE)
1 | set(order 1 2 3 4) |
- math()
命令格式:
1 | math(EXPR <输出变量> "<算术表达式>") |
支持的运算符:+(加)、-(减)、*(乘)、/(整数除法)、%(取模)
示例:
1 | set(a 10) |
- 搜索文件
如果一个项目里边的源文件很多,在编写CMakeLists.txt文件的时候不可能将项目目录的各个文件一一罗列出来,这样太麻烦也不现实。所以,在CMake中为我们提供了搜索文件的命令,可以使用aux_source_directory命令或者file命令。
- 方式1
在 CMake 中使用aux_source_directory命令可以查找某个路径下的所有源文件,命令格式为:
aux_source_directory(< dir > < variable >)
-
dir:要搜索的目录
-
variable:将从dir目录下搜索到的源文件列表存储到该变量中
1 | cmake_minimum_required(VERSION 3.0) |
- 方式2
如果一个项目里边的源文件很多,在编写CMakeLists.txt文件的时候不可能将项目目录的各个文件一一罗列出来,这样太麻烦了。所以,在CMake中为我们提供了搜索文件的命令,他就是file(当然,除了搜索以外通过 file 还可以做其他事情)。
file(GLOB/GLOB_RECURSE 变量名 要搜索的文件路径和文件类型)
-
GLOB: 将指定目录下搜索到的满足条件的所有文件名生成一个列表,并将其存储到变量中。
-
GLOB_RECURSE:递归搜索指定目录,将搜索到的满足条件的文件名生成一个列表,并将其存储到变量中。
搜索当前目录的src目录下所有的源文件,并存储到变量中
1 | file(GLOB MAIN_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp) |
-
CMAKE_CURRENT_SOURCE_DIR 宏表示当前访问的 CMakeLists.txt 文件所在的路径。
-
关于要搜索的文件路径和类型可加双引号,也可不加:
1 | file(GLOB MAIN_HEAD "${CMAKE_CURRENT_SOURCE_DIR}/src/*.h") |
实用示例
- 防止在源树构建
-
CMAKE_SOURCE_DIR:CMakeLists.txt 所在目录(源树) -
CMAKE_BINARY_DIR:CMake构建目录 -
FATAL_ERROR:严重错误,会中断CMake后面的命令
1 | # 避免在源树创建 |
- 确保CMAKE_BUILD_TYPE为单配置生成器指定默认值
1 | # 确保CMAKE_BUILD_TYPE为单配置生成器指定默认值 |

