上一篇《Linux C++ 开发4 - 入门makefile一篇文章就够了》我们讲解了通过Makefile
来编译 包含多个.cpp
和多个.h
文件 的复杂C++项目。这种方式用来构建中小型的Linux(或类Unix系统)C++项目,是没有问题的。但如果是跨平台项目或者大型项目,Makefile
就显得力不从心了;因为Makefile
不具备良好的跨平台性,大型项目的编译规则和依赖项也是比较复杂的,Makefile
的编写和维护成本都比较高。这时,CMake
就可以派上用场了。
1. 什么是CMake?
1.1. CMake的定义
CMake是一个跨平台的开源构建系统生成器。它能够生成各种构建系统文件,如Makefile
、Visual Studio 项目文件
等。CMake
通过读取一个或多个CMakeLists.txt文件来配置项目的构建过程。
1.2. CMake有哪些优势?
相较于Makefile
,CMake有以下优势。
- 跨平台支持: CMake支持多种操作系统和编译器,使得项目能够在不同平台上进行构建。
- 简化构建过程: 通过CMake,开发者可以编写一次构建脚本,然后在不同平台上生成相应的构建文件。
- 模块化: CMake支持模块化开发,可以方便地管理项目的依赖关系。
1.3. CMake 的特点
跨平台支持:
CMake
支持多种操作系统,包括 Linux、Windows、macOS 等。- 它能够生成适用于不同编译器的构建文件,如
Makefile
、Ninja
、Visual Studio 项目文件
等。
简化构建过程:
- 通过 CMake,开发者可以编写一次构建脚本(CMakeLists.txt),然后在不同平台上生成相应的构建文件,简化了构建过程。
CMake
提供了丰富的命令和选项,使得构建配置更加灵活和高效。
模块化:
CMake
支持模块化开发,可以方便地管理项目的依赖关系。- 通过
add_subdirectory
命令,可以将大型项目拆分为多个子项目,每个子项目都有自己的 CMakeLists.txt 文件。
可扩展性:
CMake
提供了丰富的模块和函数,可以方便地扩展其功能。- 开发者可以编写自定义的
CMake
模块和函数,以满足特定项目的需求。
依赖管理:
CMake
支持外部依赖的管理,可以通过find_package
命令查找和链接外部库。- 它还支持通过
FetchContent
模块下载和集成第三方库。
1.4. Cmake 、CMakeLists.txt 、Make 、Makefile 之间的关系
- CMakeLists.txt 是
CMake
的配置文件,定义了项目的源文件、构建规则和依赖关系。 - CMake 是一个构建系统生成器,负责读取一个或多个 CMakeLists.txt 文件并生成相应的构建文件(如
Makefile
、Visual Studio 项目文件
等)。 - Makefile 是
Make
工具的配置文件,它包含了一系列规则和指令,定义了如何编译和链接源代码。 - Make 是一个构建工具,负责读取 Makefile 文件并执行编译和构建过程,生成最终的构建产物。
他们之间的关系可以用下面这张图来表示。
2. 应用案例
《Linux C++ 开发4 - 入门makefile一篇文章就够了》一文中,我们用Makefile
编译了Iterator
项目。现在我们任然以这个项目为例,将其改成通过CMake来构建。
2.1. 项目概述
一个公司有多个部门,每个部门有多个人组成,这些人中有开发人员,有测试人员,和与项目相关的其它人员,其结构如下图片。
现在要遍历这个公司的所有开发人员,遍历这个公司的所有测试人员。
在项目的源代码中,我们用迭代器模式实现了这个需求,类的结构图是这样的:
详细代码参见: https://gitee.com/spencer_luo/iterator/tree/cmake/
现在我们就以这个项目为例,看看这个项目的CMakeLists.txt
需要怎么写?
2.2. CMakeLists.txt
2.2.1. 基本用法
设置 cmake的最低版本号:
1 | cmake_minimum_required(VERSION 3.28.3) |
注意:这一项要放在CMakeLists.txt
的第一行,否则可能会报错。
设置 项目名称、版本、语言:
1 | project(Iterator VERSION 1.0.0 LANGUAGES CXX) |
这里CXX
表示C++
语言。
设置 C/C++ 的标准:
1 | set(CMAKE_C_STANDARD 11) |
可以根据自己的需求设置编译时使用的C++版本,如:98/11/14/17/20。(注意:你的编译也要能支持你设置的C++版本)
查找要编译的.cpp文件:
1 | file(GLOB SRC_FILES |
CMAKE_CURRENT_SOURCE_DIR
是CMake的内置变量,表示当前CMakeLists.txt
文件所在的目录,通过$(CMAKE_CURRENT_SOURCE_DIR)
方式来使用该变量,更多内置变量参见官方文档《cmake-variables》。GLOB
命令会搜索当前目录下所有.cpp
文件,并将它们添加到SRC_FILES
变量中。你也可以使用GLOB_RECURSE
,与GLOB
相比,它不仅会搜索当前目录,还会递归搜索所有子目录。message
是CMake的内置命令,用于输出构建相关的信息。第一个参数表示消息的类型,可以是以下这些值(按优先级大小排序):FATAL_ERROR
(致命错误消息) >SEND_ERROR
(错误消息) >AUTHOR_WARNING
(警告消息) >NOTICE
(重要消息) >STATUS
(状态消息) >DEBUG
(调试消息) >TRACE
(跟踪消息)。
构建可执行文件:
1 | add_executable(${PROJECT_NAME} ${SRC_FILES}) |
- 表示:要将所有
.cpp
文件(${SRC_FILES}
)编译并链接成可执行的二进制文件,可执行文件名为项目名。 - 如果要编译链接成静态库,可以替换成
add_library(${PROJECT_NAME} STATIC ${SRC_FILES})
。 - 如果要编译链接成动态库,可以替换成
add_library(${PROJECT_NAME} SHARED ${SRC_FILES})
。
根据不同的编译模式添加不同的编译选项:
1 | # 设置构建类型: Debug/Release |
这里通过 if(CMAKE_BUILD_TYPE STREQUAL "Debug")
来判断是Debug
模式还是Release
模式,然后设置不同的编译选项,Debug
模式下编译时保留调试符号表,Release
模式下编译时会优化代码。
2.2.2. 完整内容
1 | # 要求的cmake的最低版本号 |
2.2.3. 构建执行
构建项目:
1 | # 开始构建项目,生成Makefile构建系统文件 |
cmake -B ./build -S ./
说明:
-B
: 指定构建目录,这里是./build
。-S
: 指定源码目录,这里是./
。 这一参数可以不写,不写时表示:源码目录就是当前目录。--log-level
: 可以指定CMakeLists.txt
中message
输出的日志级别,如可以使用这个命令来构建:cmake -B ./build --log-level DEBUG
开始编译链接:
1 | # 进入 build 目录 |
执行编译结果:
1 | # 进入 build 目录。此时会发现多了一个可执行文件 Iterator |