CMake Memo

CMake Memo

作者:OL

本文档是参考了以下作者

相关链接:

[]和<>中为可选项

命令行命令

  • cmake -G

    可以查看所有的生成器

    默认使用Visual Studio 17 2022,前面带*的就是默认的生成器

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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
PS C:\Users\Administrator>cmake -G
CMake Error: No generator specified for -G

Generators
* Visual Studio 17 2022 = Generates Visual Studio 2022 project files. # 默认生成器
Use -A option to specify architecture.
Visual Studio 16 2019 = Generates Visual Studio 2019 project files.
Use -A option to specify architecture.
Visual Studio 15 2017 = Generates Visual Studio 2017 project files.
Use -A option to specify architecture.
Visual Studio 14 2015 = Generates Visual Studio 2015 project files.
Use -A option to specify architecture.
Borland Makefiles = Generates Borland makefiles.
NMake Makefiles = Generates NMake makefiles.
NMake Makefiles JOM = Generates JOM makefiles.
MSYS Makefiles = Generates MSYS makefiles.
MinGW Makefiles = Generates a make file for use with
mingw32-make.
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 - MinGW Makefiles = Generates CodeBlocks project files
(deprecated).
CodeBlocks - NMake Makefiles = Generates CodeBlocks project files
(deprecated).
CodeBlocks - NMake Makefiles JOM
= Generates CodeBlocks project files
(deprecated).
CodeBlocks - Ninja = Generates CodeBlocks project files
(deprecated).
CodeBlocks - Unix Makefiles = Generates CodeBlocks project files
(deprecated).
CodeLite - MinGW Makefiles = Generates CodeLite project files
(deprecated).
CodeLite - NMake Makefiles = Generates CodeLite project files
(deprecated).
CodeLite - Ninja = Generates CodeLite project files
(deprecated).
CodeLite - Unix Makefiles = Generates CodeLite project files
(deprecated).
Eclipse CDT4 - NMake Makefiles
= Generates Eclipse CDT 4.0 project files
(deprecated).
Eclipse CDT4 - MinGW Makefiles
= Generates Eclipse CDT 4.0 project files
(deprecated).
Eclipse CDT4 - Ninja = Generates Eclipse CDT 4.0 project files
(deprecated).
Eclipse CDT4 - Unix Makefiles= Generates Eclipse CDT 4.0 project files
(deprecated).
Kate - MinGW Makefiles = Generates Kate project files (deprecated).
Kate - NMake Makefiles = Generates Kate project files (deprecated).
Kate - Ninja = Generates Kate project files (deprecated).
Kate - Ninja Multi-Config = Generates Kate project files (deprecated).
Kate - Unix Makefiles = Generates Kate project files (deprecated).
Sublime Text 2 - MinGW Makefiles
= Generates Sublime Text 2 project files
(deprecated).
Sublime Text 2 - NMake Makefiles
= Generates Sublime Text 2 project files
(deprecated).
Sublime Text 2 - Ninja = Generates Sublime Text 2 project files
(deprecated).
Sublime Text 2 - Unix Makefiles
= Generates Sublime Text 2 project files
(deprecated).
  • cmake -S

    指定项目根目录,CMake 将在那里被建造。

  • cmake -B

    指定构建目录,CMake 将输出生成的构建系统,以及当构建系统已经运行。未指定时,默认为当前工作目录。

  • cmake --build

    在指定的构建目录中运行构建系统。这是通用的所有生成器的命令。对于多配置生成器,期望的配置请求可通过以下方式进行:

    cmake --build <dir> --config <cfg>

    <dir>是生成器的目录

    <cfg>可以是ReleaseDebug

    例如MinGW Makefiles要使用make编译,此命令可以将所有的生成器的命令统一

  • cmake -D

    cmake -D 是 CMake 中运行时手动设置 / 覆盖变量的核心命令,作用是:在执行 cmake 生成构建文件(Makefile、VS 项目等)时,直接指定 CMake 变量的值,无需修改 CMakeLists.txt

    它的使用场景非常广泛(比如启用测试、切换构建类型、指定路径等)

    核心语法:

    1
    cmake -D<变量名>=<变量值> ..
    • -D:固定前缀,后面紧跟「变量名 = 变量值」(无空格= 前后不能有空格);

    • <变量名>:可以是 CMake 内置变量(如 CMAKE_BUILD_TYPE),也可以是你自定义的选项(如 OL_WITH_TESTS);

    • <变量值>:根据变量类型不同,支持 3 类值(布尔、字符串、路径);

    • ..:最后是源文件目录(即 CMakeLists.txt 所在的上级目录,固定写法,除非你的目录结构特殊)。

      关键细节:

    • 变量名大小写敏感OL_WITH_TESTSol_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
2
add_executable(<target> [WIN32] [MACOSX_BUNDLE] <source1> [<source2> ...])
add_executable(<target> "") # 空目标,后续用target_sources补充

参数说明:

  • <target>:目标名称(全局唯一)。
  • WIN32:Windows 下创建 GUI 程序(无控制台窗口)。
  • MACOSX_BUNDLE:macOS 下创建应用 bundle 包。
  • <sourceN>:源文件路径列表,可空。

2. 创建库目标(ADD_LIBRARY)

命令格式:

1
2
add_library(<target> <type> <source1> [<source2> ...])
add_library(<target> "") # 空目标,后续补充源文件

参数说明:

  • <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_DEFMY_DEF=1

5. 设置编译选项(TARGET_COMPILE_OPTIONS)

命令格式:

1
target_compile_options(<target> <scope> <opt1> [<opt2> ...])

参数说明:

  • <target>:目标名称。
  • <scope>:作用域(必填):PRIVATE/INTERFACE/PUBLIC(同上方)。
  • <optN>:编译选项,如 -Wall(Linux)、/W4(MSVC)。

命令格式:

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

    1. 参数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
    5
    cmake_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
  1. 参数"helpString"是说明变量的用途

  2. 参数FORCE是用于强制刷新变量

    • FORCE,则当缓存没有这个变量才写入,有则不会更新

    • FORCE,则会强制更新缓存中的值

  • 普通变量 VS 缓存变量

    • 对于同名的普通变量和缓存变量,优先使用普通变量,例子:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    set(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
      2
      cmake -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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
set(a "hello")
set(b hello)
set(c 'hello')
set(d 'hello)
set(e [[hello]])

message(STATUS "a=" ${a})
message(STATUS "b=" ${b})
message(STATUS "c=" ${c})
message(STATUS "d=" ${d})
message(STATUS "e=" ${e})

# 输出
# -- a=hello
# -- b=hello
# -- c='hello'
# -- d='hello
# -- e=hello

- 作用域block()endblock()

例子1:

1
2
3
4
5
6
7
8
9
10
set(myVar cat)
block()
set(myVar dog)
message("myVar=${myVar}")
endblock()
message("myVar=${myVar}")

# 输出
# myVar=dog
# myVar=cat

例子2:

1
2
3
4
5
6
7
8
9
10
set(myVar cat)
block() # 进入局部,相当于拷贝父作用域的变量,值传递
set(myVar dog PARENT_SCOPE) # 使用父作用域的引用,引用传递
message("[block]myVar=${myVar}")
endblock()
message("[outer]myVar=${myVar}")

# 输出
# [block]myVar=cat
# [outer]myVar=dog

例子3:

1
2
3
4
5
6
7
8
9
10
set(myVar cat)
block(SCOPE_FOR VARIABLES PROPAGATE myVar) # PROPAGATE 后面的变量都是父作用域的引用
set(myVar dog)
message("[block]myVar=${myVar}")
endblock()
message("[outer]myVar=${myVar}")

# 输出
# [block]myVar=dog
# [outer]myVar=dog

- 条件判断

1
2
3
4
5
6
7
if(expr)
# expr == true
elseif(expr2)
# expr == false && expr2 == true
else()
# otherwise
endif()

expr:

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

例子:

1
2
3
4
5
6
7
8
9
10
11
12
set(v "hello") # 变量规则:此时v的值不是FALSE常量,所以为TRUE
if(v)
message("${v} is true")
else()
message("${v} if false")
endif()

if(${v}) # 字符串规则:此时${v}字符串不是TRUE常量,所以为FALSE
message("'${v}' is true")
else()
message("'${v}' is false")
endif()

- 逻辑运算符

  • 与:AND
  • 或:OR
  • 非:NOT

- 比较运算符

- 循环语句

  • continue():跳过循环

  • break():退出循环

  1. 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
    43
    foreach(<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()
  2. while循环

    1
    2
    3
    while(<condition>)
    <commands>
    endwhile()

- 注释

  • #:行注释
  • #[[]]:块注释

- 函数

1
2
3
function(function_name args...)
# do something
endfunction()
  1. 传参方式:

    • 命名参数
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    function(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
    18
    function(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,否则未定义行为

    • 关键字参数

    这个比较复杂,可以查相关资料,个人觉得用处不大

  2. 函数返回值:

返回值获取例子(引用):

1
2
3
4
5
6
7
8
9
10
11
# Example_func3
# 类似C++函数传引用获取返回值
function(my_function3 returnValue)
set(${returnValue} "hello world" PARENT_SCOPE)
endfunction()

my_function3(result) # 调用
message("result:${result}")

# 输出
result:hello world

- 宏

和C++的宏类似,都是用字符串替换

1
2
3
macro(myMacro arg...)
# commands
endmacro()
  • 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
2
3
4
5
6
7
8
9
10
11
# 1. 空列表
set(my_list "")

# 2. 初始化(空格分隔,含空格元素用引号)
set(numbers 1 2 3 4)
set(strings "a b" c "d e") # 实际元素:"a b"、c、"d e"(共3个元素)

# 3. 从变量拼接生成列表
set(prefix "file_")
set(suffixes 1 2 3)
set(files ${prefix}1 ${prefix}2) # files = "file_1 file_2"

2. 访问元素与长度

1
2
3
4
5
6
7
8
9
10
11
12
# 定义列表
set(my_list 1 2 3 4)

# 获取列表长度
list(LENGTH my_list len) # len = 元素个数
message("len:${len}")

# 按索引访问(0开始,负索引则是反的)
list(GET my_list 0 first_elem) # 获取第1个元素
message("first_elem:${first_elem}")
list(GET my_list -1 last_elem) # 倒数第1个元素
message("last_elem:${last_elem}")

3. 添加元素(APPEND/PREPEND)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
set(my_list 1 2)
message("初始列表: '${my_list}'")

# 末尾添加(单个/多个元素)
list(APPEND my_list 3 4) # my_list = 1 2 3 4
message("APPEND 后: '${my_list}'")

# 开头添加
list(PREPEND my_list 0) # my_list = 0 1 2 3 4
message("PREPEND 后: '${my_list}'")

# 插入到指定索引(索引2位置插入5)
list(INSERT my_list 2 5) # my_list = 0 1 5 2 3 4
message("INSERT 后: '${my_list}'")

4. 删除元素(REMOVE_AT/REMOVE_ITEM/REMOVE_DUPLICATES)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
set(my_list 0 1 5 2 3 4 2)
message("初始列表: '${my_list}'")

# 按索引删除(删除第3个元素,索引2)
list(REMOVE_AT my_list 2) # my_list = 0 1 2 3 4 2
message("REMOVE_AT 后: '${my_list}'")

# 按值删除(删除所有值为2的元素)
list(REMOVE_ITEM my_list 2) # my_list = 0 1 3 4
message("REMOVE_ITEM 后: '${my_list}'")

# 去重(保留首次出现的元素)
set(dup_list a b a c b)
list(REMOVE_DUPLICATES dup_list) # dup_list = a b c
message("REMOVE_DUPLICATES 后: '${dup_list}'")

5. 查找(FIND)

1
2
3
4
5
6
7
8
set(my_list a b c d)

# 查找元素位置(返回索引,未找到返回-1)
list(FIND my_list "c" idx) # idx = 2
message("元素 'c' 的索引: ${idx}")

list(FIND my_list "x" idx) # idx = -1
message("元素 'x' 的索引: ${idx}")

6. 拼接和切片(JOIN/SUBLIST)

1
2
3
4
5
6
7
8
9
set(my_list 0 1 2 3 4)

# 切片:从索引1开始,取3个元素(索引1、2、3)
list(SUBLIST my_list 1 3 sub_list) # sub_list = 1 2 3
message("SUBLIST 结果: '${sub_list}'")

# 拼接:用指定字符连接列表为字符串
list(JOIN my_list "," str) # str = "0,1,2,3,4"
message("JOIN 结果: '${str}'")

7. 排序(SROT)

命令格式:

1
list(SORT <list> [COMPARE <compare>] [CASE <case>] [ORDER <order>])

参数说明:

  • COMPARE <compare>:排序规则(默认 STRING):

    • STRING:按字母 / 数字字符串排序(默认)。
    • FILE_BASENAME:按文件基名排序(如 a/1.txtb/2.txt1.txt2.txt 排序)。
    • NATURAL:自然排序(如 file10.txt 排在 file2.txt 后,默认 STRING 会相反)。
  • CASE <case>:大小写敏感度(默认 SENSITIVE):

    • SENSITIVE:区分大小写(默认,A < a)。
    • INSENSITIVE:不区分大小写(A = a)。
  • ORDER <order>:排序顺序(默认 ASCENDING):

    • ASCENDING:升序(默认)。
    • DESCENDING:降序。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
# 1. 默认排序(STRING + SENSITIVE + ASCENDING)
set(strings Z a B 10 2)
list(SORT strings)
message("默认排序:${strings}") # 输出结果:默认排序:10 2 B Z a

# 2. 自然排序(解决数字字符串排序问题)
list(SORT strings COMPARE NATURAL)
message("自然排序:${strings}") # 输出结果:自然排序:2 10 a B Z

# 3. 不区分大小写 + 降序
set(letters A b C a)
list(SORT letters CASE INSENSITIVE ORDER DESCENDING)
message("不区分大小写降序:${letters}") # 输出结果:不区分大小写降序:C b A a

8. 翻转列表(REVERSE)

1
2
3
set(order 1 2 3 4)
list(REVERSE order) # 翻转列表
message("翻转后:${order}") # 输出结果:翻转后:4 3 2 1

- math()

命令格式

1
math(EXPR <输出变量> "<算术表达式>")

支持的运算符+(加)、-(减)、*(乘)、/(整数除法)、%(取模)

示例

1
2
3
4
5
6
7
8
9
set(a 10)
set(b 3)
math(EXPR sum "${a} + ${b}") # 加法:13
math(EXPR diff "${a} - ${b}") # 减法:7
math(EXPR product "${a} * ${b}") # 乘法:30
math(EXPR quotient "${a} / ${b}") # 整数除法:3(10/3=3.333,取整)
math(EXPR remainder "${a} % ${b}") # 取模:1(10%3=1)
message("和:${sum},商:${quotient},余数:${remainder}")
# 输出:和:13,商:3,余数:1

- 搜索文件

如果一个项目里边的源文件很多,在编写CMakeLists.txt文件的时候不可能将项目目录的各个文件一一罗列出来,这样太麻烦也不现实。所以,在CMake中为我们提供了搜索文件的命令,可以使用aux_source_directory命令或者file命令。

  1. 方式1
    在 CMake 中使用aux_source_directory 命令可以查找某个路径下的所有源文件,命令格式为:
    aux_source_directory(< dir > < variable >)
  • dir:要搜索的目录

  • variable:将从dir目录下搜索到的源文件列表存储到该变量中

1
2
3
4
5
cmake_minimum_required(VERSION 3.0)
project(CALC)
include_directories(${PROJECT_SOURCE_DIR}/include)
# 搜索 src 目录下的源文件
aux_source_directory(${CMAKE_CURRENT_SOURC})
  1. 方式2
    如果一个项目里边的源文件很多,在编写CMakeLists.txt文件的时候不可能将项目目录的各个文件一一罗列出来,这样太麻烦了。所以,在CMake中为我们提供了搜索文件的命令,他就是file(当然,除了搜索以外通过 file 还可以做其他事情)

file(GLOB/GLOB_RECURSE 变量名 要搜索的文件路径和文件类型)

  • GLOB: 将指定目录下搜索到的满足条件的所有文件名生成一个列表,并将其存储到变量中。

  • GLOB_RECURSE:递归搜索指定目录,将搜索到的满足条件的文件名生成一个列表,并将其存储到变量中。

搜索当前目录的src目录下所有的源文件,并存储到变量中

1
2
file(GLOB MAIN_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
file(GLOB MAIN_HEAD ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h)
  • 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
2
3
4
5
# 避免在源树创建
if("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}")
message(FATAL_ERROR "Error: In-source builds are not allowed.
Please create a separate directory for build files, such as a 'build' directory.")
endif()

- 确保CMAKE_BUILD_TYPE为单配置生成器指定默认值

1
2
3
4
5
6
# 确保CMAKE_BUILD_TYPE为单配置生成器指定默认值
if((NOT DEFINED CMAKE_BUILD_TYPE OR CMAKE_BUILD_TYPE STREQUAL "") AND NOT DEFINED CMAKE_CONFIGURATION_TYPES)
set(CMAKE_BUILD_TYPE Release CACHE STRING
"Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel Coverage."
FORCE) # FORCE 强制覆盖已定义但为空的情况
endif()