CMAKE 用法备忘

CMake是一个很常用的“跨平台的构建系统生成工具”,通过CMake我们可以将不同系统的源码程序便捷的移植到其他系统。

CMake是一个很常用的“跨平台的构建系统生成工具”,通过CMake我们可以将不同系统的源码程序便捷的移植到其他系统。

5th Jan 2026

7 min read

cmake使用

1.1 注释

1.1.1 注释行

# 此为注释内容
cmake_minimum_required(VERSION 3.0)

1.1.2 注释块

[[此为注释内容
*********注释内容
此为注释内容]]
cmake_minimum_required(VERSION 3.0)

1.2 只有源文件

1.2.1 共处一室

  1. 准备.c文件
  2. 将文件置于同一个文件夹下
  3. 添加CMakeLists.txt文件
cmake_minimum_required(VERSION 3.0)
project(CALC)
add_executable(app add.c div.c main.c mult.c sub.c)
  • cmake_minimum_required: 指定使用的cmake的最低版本
    • 可选,非必须,但不加会有警告
  • project:定义工程名称,并可指定工程的版本、工程描述、web主页地址、支持的语言,如果不需要这些都是可以忽略的,只需要输入指定工程名字即可。
    #projet指令的语法是:
    project(<PROJECT-NAME> [<language-name>...])
    project(<PROJECT-NAME>
                    [VERSION <major>[.<minor>[.<path>[.<tweak>]]]]
                    [DESCRIPTION <project-description-string>]
                    [HOMEPAGE_URL <url-string>]
                    [LANGUAGE <language-name>...])
    
  • add_executable: 定义工程会生成一个可执行程序
    add_executable(可执行程序名 源文件名)
    
    • 这里的可执行程序名与project中的项目名无关
    • 源文件名可以是一个也可以十多个,如有多个可用空格或;间隔
  1. 执行cmake指令
$ cmake CMakeLists.txt
# 必须在对应目录下

由于会产生大量无关文件,可以创建一个build 文件夹,并在该文件目录下执行cmake 指令,即可完成与源文件的分割,使代码更清爽。

1.3 私人订制

1.3.1 定义变量

在上面的例子中一共提供了五个源文件,假设这五个源文件需要反复被使用,每次都直接将他们的名字写出来比较麻烦,此时我们就需要定义一个变量,将文件名对应的字符串储存起来,在cmake里定义变量需要使用set

# SET 指令的语法
# [] 中的参数为可选项,如不需要可以不写
set(VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE]])
  • VAR :变量名
  • VALUE :变量值
set(SRC_LIST add.c div.c main.c mult.c sub.c)
# set(SRC_LIST add.c;div.c;main.c;mult.c;sub.c)
add_executable(app ${SRC_LIST})

1.3.2 指定使用的C++标准

在编写C++程序的时候,根据不同的需求,对标准的使用会有差异。如果想要修改编译标准,可以通过set 命令指定标准,或者在编译命令中指定标准。

$ g++ *.cpp -std=c++11 -o app

cmake CMakeLists.txt文件路径 -DMAKE_CXX_STANDARD=11

set(CMAKE_CXX_STANDARD 11)

1.3.3 指定输出的路径

在CMake中指定可执行程序输出的路径,也对应一个宏,叫做EXECUTABLE_OUTPUT_PATH ,他的值还是通过set 命令进行设置:

set(HOME /home/robin/Linux/Sort)
set(EXECUTABLE_OUTPUT_PATH ${HOME}/bin)
  • 第一行:定义一个变量用于存储一个绝对路径
  • 第二行:将拼接好的路径值设置给EXECUTABLE_OUTPUT_PATH
    • 如果这个路径的子目录不存在,会自动创建

由于可执行程序是基于cmake命令生成的makefile文件然后再执行make命令得到的,所以如果此处指定可执行程序生成路径的时候使用的是相对路径 ./xxx/xxx,那么这个路径中的./对应的就是makefile文件所在的那个目录

1.4 搜索文件

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

1.4.1 方法一

在cmake中使用aux_source_directory 命令可以查找某个路径下的所有源文件 ,命令格式为:

aux_source_directory(< dir > < variable >)
  • dir :要搜索的目录
  • variable :将从dir 目录下搜索到的源文件存储到该变量中
cmake_minimum_required(VERSION 3.0)
project(CALC)
include_directories(${PROJECT_SOURCE_DIR}/include) # 该路径宏为cmake指令后面跟随的路径
# 搜索 src 目录下的源文件
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/src SRC_LIST)
add_executable(app ${SRC_LIST})

1.4.2 方法二

在CMake中为我们提供了搜索文件的命令file

file(GLOB/GLOB_RECURSE 变量名 要搜索的文件路径和文件类型)
  • GLOB :将指定目录下搜索到的满足条件的所有文件名生成一个列表,并将其存储到变量中。
  • GLOB_RECURSE :递归搜索指定目录,将搜索到的满足条件的文件名生成一个列表,并将其存储到变量中。

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

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文件所在的路径。
  • 关于要搜索的文件路径和类型可加双引号,也可以不加:
    file(GLOB MAIN_HEAD "${CMAKE_CURRENT_SOURCE_DIR}/src/*.h")
    

1.5 包含头文件

在编译项目源文件的时候,很多时候都要将源文件对应的头文件路径制定出来,这样才能保证在编译过程中编译器能够找到这些头文件,并顺利通过编译。在CMake中设置要包含的目录也很简单,用过一个命令就可以搞定,即inlcude_directories

include_directories(headpath)

1.6 制作动态库或静态库

有些时候我们编写的源代码并不需要将他们编译成可执行程序,而是生成一些静态库提供给第三方使用,下面来讲解在cmake中生成这两类库文件的方法。

1.6.1 制作静态库

在CMake中,如果要制作静态库,需要使用的命令如下:

add_library(库名称 STATIC 源文件1 [原件2] ...

在Linux中,静态库名字分为三个部分:lib+库名字+.a ,此处只需要指定库的名字就可以了,另外两个部分在生成该文件的时候会自动补充。

在Windows中虽然库名和Linux格式不同,但也只需指出名字即可。

1.6.2 制作动态库

在CMake中,如果要制作动态库,需要使用的命令如下:

add_library(库名称 SHARED 源文件)

在Linux中,静态库名字分为三个部分:lib+库名字+.so ,此处只需要指定库的名字就可以了,另外两个部分在生成该文件的时候会自动补充。

在Windows中虽然库名和Linux格式不同,但也只需指出名字即可。

1.6.3 指定输出的路径

方式一 - 适用于动态库

对于生成的库文件来说和可执行程序一样都可以指定输出路径。由于在Linux下生成的动态库默认是有执行权限的,所以可以按照生成可执行程序的方式去指定他生成的目录:

cmake_minimum_required(VERSION 3.0)
project(CALC)
include_directories(${PROJECT_SOURCE_DIR}/include)
flie(GLOB SRC_LIST "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp")
# 设置动态库生成路径
set(EXECUTABLE_OUTPUT_PATH ${PORJECT_SOURCE_DIR}/lib)
add_library(calc SHARED ${SRC_LIST})

对于这种方式来说,其实就是通过set 命令给EXECUTABLE_OUTPUT_PATH ,这个路径就是可执行文件生成的路径。

方式二 - 都适用

由于在Linux下生成的静态库默认不具有可执行权限,所有在指定静态库生成的路径的时候就不能用EXECUTABLE_OUTPUT_PATH 宏了,而应该使用LIBRARY_OUTPUT_PATH ,这个宏对应静态库文件和动态库文件都适用。

cmake_minimum_required(VERSION 3.0)
project(CALC)
include_directories(${PROJECT_SOURCE_DIR}/include)
flie(GLOB SRC_LIST "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp")
# 设置动态库生成路径
set(LIBRARY_OUTPUT_PATH ${PORJECT_SOURCE_DIR}/lib)
add_library(calc SHARED ${SRC_LIST})

1.6.4 在静态库中链接静态库

一、背景说明

在 CMake 中,

  • 静态库 (static library).a.lib
  • 目标 (target):由 add_library()add_executable() 定义。

问题:如果 libA 依赖 libB,如何保证 target_link_libraries(libA libB) 正确生效?

老版本 CMake (1.6.4) 中:

  • 没有 target_link_libraries PRIVATE/INTERFACE/PUBLIC 这种现代语法。
  • 只能用简单的 target_link_libraries(),CMake 不会自动处理依赖,需要你在最终可执行程序里显式链接。

二、最小示例结构

假设工程目录结构:

project/
├── CMakeLists.txt
├── libA/
│   ├── CMakeLists.txt
│   └── a.cpp
├── libB/
│   ├── CMakeLists.txt
│   └── b.cpp
└── app/
    ├── CMakeLists.txt
    └── main.cpp


三、libB(底层库)

libB/CMakeLists.txt

# 生成静态库 libB.a
add_library(B STATIC b.cpp)


四、libA(依赖 libB)

libA/CMakeLists.txt

# 生成静态库 libA.a
add_library(A STATIC a.cpp)

# 告诉 CMake: A 依赖 B
# 在 CMake 1.6.4 中,这里只是“记录依赖”,不会自动传递
target_link_libraries(A B)


五、app(最终可执行程序)

app/CMakeLists.txt

# 生成可执行程序
add_executable(my_app main.cpp)

# 显式链接静态库
# 注意:必须同时写 A 和 B(因为 CMake 1.6.4 不会自动传递依赖)
target_link_libraries(my_app A B)


六、顶层 CMakeLists.txt

CMakeLists.txt

cmake_minimum_required(VERSION 1.6)

project(StaticLibDemo)

# 递归进入子目录
add_subdirectory(libB)
add_subdirectory(libA)
add_subdirectory(app)


七、编译

mkdir build
cd build
cmake ..
make

生成:

  • libB.a
  • libA.a(内部依赖 B,但不会自动传递)
  • my_app(手动写 A B 才能编译通过)

1.6.5 在静态库中链接动态库

一、背景知识

CMake 1.6.4(非常老的版本)里:

  • 没有 PRIVATE / PUBLIC / INTERFACE 语法。
  • 所有 target_link_libraries() 都是 直接附加
  • 静态库本身不会真正链接动态库,它只是记录依赖关系,最终还是要由 可执行程序 去链接动态库。

所以流程是:

  1. 定义一个静态库 libA.a,它调用了动态库函数。
  2. CMakeLists.txttarget_link_libraries(A some_shared_lib)
  3. 在最终的可执行程序 my_app 中,依赖 A 时,还必须把 some_shared_lib 显式写出来(因为老版本 CMake 不会自动传递依赖)。

二、目录结构示例

project/
├── CMakeLists.txt
├── libA/
│   ├── CMakeLists.txt
│   └── a.cpp
└── app/
    ├── CMakeLists.txt
    └── main.cpp


三、libA 静态库(依赖动态库)

假设 libA 里调用了 m 数学库(Linux 下是 libm.so)。

libA/CMakeLists.txt

# 生成静态库 libA.a
add_library(A STATIC a.cpp)

# 让 A 知道它依赖 m(动态库)
# 在 CMake 1.6.4 中,这里只是“记录”,不会自动传递
target_link_libraries(A m)

a.cpp

#include <math.h>

double square_root(double x) {
    return sqrt(x); // 调用动态库 libm.so
}

四、app 可执行程序

app/CMakeLists.txt

# 生成可执行程序
add_executable(my_app main.cpp)

# 必须同时显式写 A 和 m
target_link_libraries(my_app A m)

main.cpp

#include <stdio.h>

double square_root(double x);

int main() {
    printf("sqrt(16) = %f\n", square_root(16));
    return 0;
}

五、顶层 CMakeLists.txt

cmake_minimum_required(VERSION 1.6)

project(StaticLinkSharedDemo)

add_subdirectory(libA)
add_subdirectory(app)


六、编译 & 运行

mkdir build
cd build
cmake ..
make
./app/my_app

输出:

sqrt(16) = 4.000000

1.7 包含库文件

在编写程序的过程中,可能会用到一些系统提供的动态库或者自己制作的动态库或者静态库文件,cmake中也为我们提供了相关的加载动态库的命令。

1.7.1 链接静态库

在cmake中,链接静态库的命令如下:

link_libraries(<static lib> [<static lib>...])
  • 参数 1:指定出要链接的静态库的名字
    • 可以是全名libxxx.a
    • 也可以是掐头去尾的名字xxx
  • 参数 2-N:要链接的其他静态库的名字

如果该静态库不是系统提供的(自己制作或者使用第三方提供的静态库)可能出现静态库找不到的情况,此时可以将静态库的路径也指定出来:

link_directories(<lib path>)

这样修改之后的CMakeLists.txt 文件内容如下:

cmake_minimum_required(VERSION 3.0)
project(CALC)
# 搜索指定目录下源文件
file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
# 包含头文件路径
include_directories(${PROJECT_SOURCE_DIR}/include)
# 包含静态库路径
link_directories(${PROJECT_SOURCE_DIR}/lib)
# 链接静态库
link_libraries(calc)

1.7.2 链接动态库

在cmake中链接动态库的命令如下:

target_link_libraries(
        <target>
        <PRIVATE|PUBLIC|INTERFACE> <item>...
        [<PRIVATE|PUBLIC|INTERFACE> <item>...]...)
  • target:指定动态库的文件的名字
    • 该文件可能是一个源文件
    • 该文件可能是一个动态库文件
    • 该文件可能是一个可执行文件
  • PRIVATE|PUBLIC|INTERFACE:动态库的访问权限,默认为PUBLIC
    • 如果各个动态库之间没有依赖关系,无需做任何设置,三者没有区别,一般无需指定,使用默认的PUBLIC即可。
    • 动态库具有传递性(在权限为PUBLIC时),如果动态库A链接了B,同时动态库C链接了A,那么动态库C同时也会链接动态库B。
      • PUBLIC :在PUBLIC后面的库会被Link到前面的target中,并且里面的符号也会被导出,提供给第三方使用。
      • PRIVATE :在PRIVATE后面的库仅被link到前面的target中,并且中介掉,第三方不能感知你掉了啥库。
      • INTERFACE :在interface后面引入的库不会被链接到前面的taget中,只会导出符号。

链接系统动态库

动态库的链接和静态库是完全不同的:

  • 静态库会在生成可执行程序的链接阶段被打包到可执行程序中,所以可执行程序启动,静态库就被加载到内存中了。
  • 动态库在生成可执行程序的链接阶段不会被打包到可执行程序中,当可执行程序被启动并且调用了动态库中的函数的时候,动态库才会被加载到内存。

因此,在cmake中要制定要链接的动态库的时候,应该将那个命令写到生成了可执行文件之后:

cmake_minimum_required(VERSION 3.0)
project(TEST)
file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
# 添加并指定最终生成的可执行程序名
add_executable(app ${SRC_LIST})
# 指定可执行程序需要链接的动态库的名字
target_link_libraries(app pthread) # 此为一个系统动态库
# 如果动态库非系统动态库,那么需要包含一下动态库的地址,即在前面加上link_directories()

target_link_libraries(app pthread) 中:

  • app :对应的是最终生成的可执行程序的名字
  • pthread :这是可执行程序要加载的动态库,这个库是系统提供的线程库,全名为libpthread.so ,在指定的时候一般会掐头去尾。

1.8 日志

在CMake中可以用用户显示一条消息,该命令的名字为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错误,种植所有处理过程

CMake的命令行工具会在stdout上显示STATUS 消息,在stderr上显示其他所有消息。CMake的GUI会在他的的log区域显示所有消息

CMake警告和错误消息的文本显示使用的是一种简单的标记语言,文本没有缩进,超过长度的行就会回卷,段落之间一新行作为分隔符。

# 输出一般日志信息
message(STATUS "source path: ${PROJECT_SOURCE_DIR}")
# 输出警告信息
message(WARNING "source path: ${PROJECT_SOURCE_DIR}")
# 输出错误信息
message(FATAL_ERROR "source path: ${PROJECT_SOURCE_DIR}")

1.9 变量操作

1.9.1 追加

有时候项目中的源文件并不一定都在同一个目录中,但是这些源文件最终却需要一起进行编译来生成最终的可执行文件或者库文件。如果我们通过file命令对各个目录下的源文件进行搜索,最后还需要做一个字符串拼接的操作,关于字符串拼接可以使用set命令也可以使用list命令。

使用set拼接 如果使用set进行字符串拼接,对应的命令格式如下:


set(变量名1 ${变量名1} ${变量名2} ...)

关于上面的命令其实就是将从第二个参数开始往后所有的字符串进行拼接,最后将结果存储到第一个参数中,如果第一个参数中原来有数据会对原数据就行覆盖。

cmake_minimum_required(VERSION 3.0)
project(TEST)
set(TEMP "hello,world")
file(GLOB SRC_1 ${PROJECT_SOURCE_DIR}/src1/.cpp)
file(GLOB SRC_2 ${PROJECT_SOURCE_DIR}/src2/.cpp)
# 追加(拼接)
set(SRC_1 ${SRC_1} ${SRC_2} ${TEMP})
message(STATUS "message: ${SRC_1}")

使用list拼接 如果使用list进行字符串拼接,对应的命令格式如下:

list(APPEND <list> [<element> ...])

list命令的功能比set要强大,字符串拼接只是它的其中一个功能,所以需要在它第一个参数的位置指定出我们要做的操作,APPEND表示进行数据追加,后边的参数和set就一样了。

cmake_minimum_required(VERSION 3.0)
project(TEST)
set(TEMP "hello,world")
file(GLOB SRC_1 ${PROJECT_SOURCE_DIR}/src1/.cpp)
file(GLOB SRC_2 ${PROJECT_SOURCE_DIR}/src2/.cpp)
# 追加(拼接)
list(APPEND SRC_1 ${SRC_1} ${SRC_2} ${TEMP})
message(STATUS "message: ${SRC_1}")

在CMake中,使用set命令可以创建一个list。一个在list内部是一个由分号;分割的一组字符串。例如,set(var a b c d e)命令将会创建一个list:a;b;c;d;e,但是最终打印变量值的时候得到的是abcde。


set(tmp1 a;b;c;d;e)
set(tmp2 a b c d e)
message(${tmp1})
message(${tmp2})

输出的结果:


abcde
abcde

1.9.2 字符串移除

我们在通过file搜索某个目录就得到了该目录下所有的源文件,但是其中有些源文件并不是我们所需要的,比如:

$ tree
.
├── add.cpp
├── div.cpp
├── main.cpp
├── mult.cpp
└── sub.cpp

0 directories, 5 files

在当前这么目录有五个源文件,其中main.cpp是一个测试文件。如果我们想要把计算器相关的源文件生成一个动态库给别人使用,那么只需要add.cpp、div.cp、mult.cpp、sub.cpp这四个源文件就可以了。此时,就需要将main.cpp从搜索到的数据中剔除出去,想要实现这个功能,也可以使用list

list(REMOVE_ITEM <list> <value> [<value> ...])

通过上面的命令原型可以看到删除和追加数据类似,只不过是第一个参数变成了REMOVE_ITEM

可以看到,在第8行把将要移除的文件的名字指定给list就可以了。但是一定要注意通过 file 命令搜索源文件的时候得到的是文件的绝对路径(在list中每个文件对应的路径都是一个item,并且都是绝对路径),那么在移除的时候也要将该文件的绝对路径指定出来才可以,否是移除操作不会成功。

cmake_minimum_required(VERSION 3.0)
project(TEST)
set(TEMP "hello,world")
file(GLOB SRC_1 ${PROJECT_SOURCE_DIR}/*.cpp)

# 移除前日志

message(STATUS "message: ${SRC_1}")

# 移除 main.cpp

list(REMOVE_ITEM SRC_1 ${PROJECT_SOURCE_DIR}/main.cpp)

# 移除后日志

message(STATUS "message: ${SRC_1}")

关于list命令还有其它功能,但是并不常用,在此就不一一进行举例介绍了。

  1. 获取 list 的长度。
    list(LENGTH <list> <output variable>)
    
    • LENGTH:子命令LENGTH用于读取列表长度
    • <list>:当前操作的列表
    • <output variable>:新创建的变量,用于存储列表的长度。
  2. 读取列表中指定索引的的元素,可以指定多个索引
    list(GET <list> <element index> [<element index> ...] <output variable>)
    
    • <list>:当前操作的列表
    • <element index>:列表元素的索引
      • 从0开始编号,索引0的元素为列表中的第一个元素;
      • 索引也可以是负数,-1表示列表的最后一个元素,-2表示列表倒数第二个元素,以此类推
      • 当索引(不管是正还是负)超过列表的长度,运行会报错
    • <output variable>:新创建的变量,存储指定索引元素的返回结果,也是一个列表。
  3. 将列表中的元素用连接符(字符串)连接起来组成一个字符串
    list (JOIN <list> <glue> <output variable>)
    
    • <list>:当前操作的列表
    • <glue>:指定的连接符(字符串)
    • <output variable>:新创建的变量,存储返回的字符串
  4. 查找列表是否存在指定的元素,若果未找到,返回-1
    list(FIND <list> <value> <output variable>)
    
    • <list>:当前操作的列表
    • <value>:需要再列表中搜索的元素
    • <output variable>:新创建的变量
      • 如果列表<list>中存在<value>,那么返回<value>在列表中的索引
      • 如果未找到则返回-1。
  5. 将元素追加到列表中
    list (APPEND <list> [<element> ...])
    
  6. 在list中指定的位置插入若干元素
    list(INSERT <list> <element_index> <element> [<element> ...])
    
  7. 将元素插入到列表的0索引位置
    list (PREPEND <list> [<element> ...])
    
  8. 将列表中最后元素移除
    list (POP_BACK <list> [<out-var>...])
    
  9. 将列表中第一个元素移除
    list (POP_FRONT <list> [<out-var>...])
    
  10. 将指定的元素从列表中移除
    list (REMOVE_ITEM <list> <value> [<value> ...])
    
  11. 将指定索引的元素从列表中移除
    list (REMOVE_AT <list> <index> [<index> ...])
    
  12. 移除列表中的重复元素
    list (REMOVE_DUPLICATES <list>)
    
  13. 列表翻转
    list(REVERSE <list>)
    
  14. 列表排序
    list (SORT <list> [COMPARE <compare>] [CASE <case>] [ORDER <order>])
    
    • COMPARE:指定排序方法。有如下几种值可选:
      • STRING:按照字母顺序进行排序,为默认的排序方法
      • FILE_BASENAME:如果是一系列路径名,会使用basename进行排序
      • NATURAL:使用自然数顺序排序
    • CASE:指明是否大小写敏感。有如下几种值可选:
      • SENSITIVE: 按照大小写敏感的方式进行排序,为默认值
      • INSENSITIVE:按照大小写不敏感方式进行排序
    • ORDER:指明排序的顺序。有如下几种值可选:
      • ASCENDING:按照升序排列,为默认值
      • DESCENDING:按照降序排列

1.10 宏定义

在进行程序测试的时候,我们可以在代码中添加一些宏定义,通过这些宏来控制这些代码是否生效,如下所示:

#include <stdio.h>
#define NUMBER  3

int main()
{
    int a = 10;
#ifdef DEBUG
    printf("我是一个程序猿, 我不会爬树...\n");
#endif
    for(int i=0; i<NUMBER; ++i)
    {
        printf("hello, GCC!!!\n");
    }
    return 0;
}

在程序的第七行对DEBUG宏进行了判断,如果该宏被定义了,那么第八行就会进行日志输出,如果没有定义这个宏,第八行就相当于被注释掉了,因此最终无法看到日志输入出(上述代码中并没有定义这个宏)。

为了让测试更灵活,我们可以不在代码中定义这个宏,而是在测试的时候去把它定义出来,其中一种方式就是在gcc/g++命令中去指定,如下:

$ gcc test.c -DDEBUG -o app

在gcc/g++命令中通过参数 -D指定出要定义的宏的名字,这样就相当于在代码中定义了一个宏,其名字为DEBUG

在CMake中我们也可以做类似的事情,对应的命令叫做add_definitions:

add_definitions(-D宏名称)

针对于上面的源文件编写一个CMakeLists.txt,内容如下:

cmake_minimum_required(VERSION 3.0)
project(TEST)
# 自定义 DEBUG 宏
add_definitions(-DDEBUG)
add_executable(app ./test.c)

通过这种方式,上述代码中的第八行日志就能够被输出出来了。

2.0 预定义宏

下面的列表中为大家整理了一些CMake中常用的宏:

功能
PROJECT_SOURCE_DIR使用cmake命令后紧跟的目录,一般是工程的根目录
PROJECT_BINARY_DIR执行cmake命令的目录
CMAKE_CURRENT_SOURCE_DIR当前处理的CMakeLists.txt所在的路径
CMAKE_CURRENT_BINARY_DIRtarget 编译目录
EXECUTABLE_OUTPUT_PATH重新定义目标二进制可执行文件的存放位置
LIBRARY_OUTPUT_PATH重新定义目标链接库文件的存放位置
PROJECT_NAME返回通过PROJECT指令定义的项目名称
CMAKE_BINARY_DIR项目实际构建路径,假设在build目录进行的构建,那么得到的就是这个目录的路径

3.0 嵌套的CMake

内容简单,但内容较多,此部分给出教程链接