自己在使用CLion集成环境编写C++程序时发现其自动生成了CMakeLists.txt文件,通过查阅相关资料发现其跟CMake有关,于是便写下这篇文章进行相关知识记录方便自己复习。
一、前言
CMake
是一个支持跨平台的项目构建工具。另外还有一个更为人熟知的项目构建工具——Makefile(通过make
命令进行项目的构建),然而大多数IDE
软件都集成了Make
,比如:Visual Code
的nmake
、Linux
下的GNU make
以及QT
的QMake
等。Makefile
通常依赖于当前的编译平台,有时程序员编写Makefile
工作量会比较大且解决依赖关系时也容易出错。而CMake
恰好能解决上述问题,其允许开发者指定整个工程的编译流程,再根据编译平台,自动生成本地化的Makefile
和工程文件,最后用户只需要make
编译即可。其编译流程如下图:
- 蓝色虚线表示使用
Makefile
构建项目的过程 - 红色实现表示使用
CMake
构建项目的过程
二、CMake的使用
CMake
支持大写、小写以及混合大小写的命令。如果在编写CMakeLists.txt
文件时使用的工具有对应的命令提示,那么大小写随缘即可,无需太过在意。
2.1 注释
- 注释行:
CMake
中使用#
实现行注释,可以放在任何位置 - 注释块:
CMake
中使用#[[]]
实现块注释
1 | # 这是一个CMakeLists.txt文件 |
2.2 CMakeLists.txt文件编写
假设源文件夹中文件结构如下:
1 | tree |
2.2.1 生成的配置文件不单独放入源文件夹中的子文件中
在上述源文件夹中添加CMakeLists.txt
。文件内容如下:
1 | cmake_minimum_required(VERSION 3.0) |
上述文件中三个命令含义如下:
cmake_minimum_required
: 指定使用的CMake
最低版本。(改命令可选,若不加可能会有警告!)project
: 定义工程的名称并可指定工程的版本、工程描述、web主页地址、支持的语言(默认支持所有的语言)。若不需要则可省略,只需指定出工程名即可。add_executable
: 定义工程生成的可执行程序名字。(这里的可执行程序名字与项目名字无关)
上述命令官方语法如下:
1 | # PROJECT 指令的语法是: |
Ps: 源文件名称可以有多个,若有多个可用空格
或;
隔开
在控制台执行cmake CMakeLists.txt文件所在路径
后,CMakeLists.txt
中的命令就会被执行。执行之后,源文件夹中的文件结构如下:
1 | tree -L 1 |
可以看见在对应目录下生成了一个Makefile
文件,这时再在控制台中执行make
命令就可以对项目进行构建得到所需要的可执行程序了。结果如下:
1 | make |
最终可执行程序app
就被编译出来了,其名字由CMakeLists.txt
中指定。
2.2.2 生成的配置文件单独放入源文件夹中的子文件中
在上面的例子中,若CMakeLists.txt
文件所在目录执行了cmake
命令之后就会生成一些目录和文件(包括makefile
)文件。若再基于makefile
文件执行make
命令,程序在编译过程中还会产生一些中间文件和可执行文件,这样会导致整个项目目录看起来很混乱,不太容易管理和维护,此时我们就可以把生成的这些与项目源码无关的文件统一放到一个对应的目录里边,通常将整个目录命名为build
。执行代码以及执行结果为:
1 | mkdir build |
在上面的代码中,首先创建了一个目录build
并进入至该目录中,然后再再build
目录中执行cmake
命令,由于CMakeLists.txt
文件中build
的上级目录中,因此执行Shell
指令为cmake ..
。(..
代表当前目录的上级目录)
当上述命令执行完毕后,在build
目录中会生成一个makefile
文件。该目录下的文件结构如下:
1 | tree build -L 1 |
这样就可以在build
目录中执行make
命令编译项目,生成的相关文件自然也就被存储到build
目录中了。
2.3 进阶语法
在上面的例子中一共提供了5个源文件,假设这五个源文件需要被反复使用,每次都直接将它们的名字写出来比较麻烦,这时我们需要定义一个变量,该变量可将这些文件名存储起来,在CMake
中定义变量需要使用关键字set
。
1 | # SET指令语法: |
- VAR: 变量名
- VALUE: 变量值
1 | # 方式一: 各个源文件之间使用空格间隔 |
2.3.1 指定使用C++标准
在编写C++程序时可能会用到C++11、C++14、C++17、C++20等新特性,那么就需要在编译的时候指定出要使用哪个标准:g++ *.cpp -std=c++11 -o app
。此命令通过-std=c++11
指定出要使用C++11编译程序,C++标准对应有一宏叫做DCMAKE_CXX_STANDARD
。在CMake
中想要指定C++有两种方式:
- 方式一:在CMakeLists.txt中通过set命令指定
1
2
3
4
5
6# 增加-std=c++11
set(CMAKE_CXX_STANDARD 11)
# 增加-std=c++14
set(CMAKE_CXX_STANDARD 14)
3 增加-std=c++17
set(CMAKE_CXX_STANDARD 17) - 方式二:在执行
cmake
命令时指定出这个宏的值1
2
3
4
5
6增加-std=c++11
cmake CMakeLists.txt文件路径 -DCMAKE_CXX_STANDARD=11
增加-std=c++14
cmake CMakeLists.txt文件路径 -DCMAKE_CXX_STANDARD=14
增加-std=c++17
cmake CMakeLists.txt文件路径 -DCMAKE_CXX_STANDARD=17
2.3.2 指定输出的路径
在CMake
中指定可执行程序输出的路径,也对应一个宏叫做EXECUTABLE_OUTPUT_PATH
,它的值还是通过set
命令进行设置。
1 | set(HOME /home/robin/Linux/Sort) |
- 第一行:定义一个变量用于存储一个绝对路径。
- 第二行:将拼接好的路径值设置给EXECUTABLE_OUTPUT_PATH`宏。(若这个路径的子目录不存在则会自动生成)
由于可执行程序是基于 cmake 命令生成的 makefile 文件然后再执行 make 命令得到的,所以如果此处指定可执行程序生成路径的时候使用的是相对路径 ./xxx/xxx,那么这个路径中的 ./ 对应的就是 makefile 文件所在的那个目录。
2.3.3 搜索文件
若一个项目里边的源文件很多,在编写CMakeLists.txt
文件的时候不可能将项目目录的各个文件一一罗列出来。在CMake
中提供了文件搜索命令,可以使用aux_source_directory
命令或者file
命令。
- 方式一:在
CMake
中使用aux_source_directory
命令可以查找某个路径下的的所有源文件。1
aux_source_directory(<dir> <variable>)
- dir: 要搜索的目录
- variable: 将从
dir
目录下搜索到的源文件列表存储到该变量中
1 | cmake_minimum_required(VERSION 3.0) |
- 方式二:采用
file
命令1
file(GLOB/GLOB_RECURSE 变量名 要搜索的文件路径和文件类型)
GLOB
: 将指定目录下搜索到满足条件的所有文件名生成一个列表并将其存储到变量中。GLOB_RECURSE
: 递归搜索指定目录,将搜索到的满足条件的文件名生成一个列表并将其存储到变量中。
1 | file(GLOB MAIN_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp) |
搜索当前目录的src目录下所有源文件并存储到变量中。
三、最后的话
以上是常用基础的CMake
命令,还有很多需要完善的地方,后续会慢慢补充…
- 本文链接: https://www.hoi3vel.cn/2025/06/30/t-cmake/
- 版权声明: 本博客所有文章除特别声明外,均默认采用 CC BY-NC-SA 4.0 许可协议。