任何语言,在我们刚刚接触时,IDE都是一个"平易近人"的工具,帮助我们一键实现了项目的:"构建 -> 编译 -> 运行",这些工作看似简单实则繁琐,只是IDE屏蔽了它们的复杂性。后来,你可能会参与到大型项目的开发中,IDE就无法再轻而易举地帮你构建项目了,于是,你们的团队需要在专职的服务器上使用专门的构建工具去构建和编译你们的大型项目,再用持续集成工具把"构建 -> 编译 -> 发布" 的一整套流程自动化运转起来。
不管你处在学习一门语言的什么阶段,或早或晚都将会学习和使用"构建 -> 编译 -> 运行"这个链条上的工具,本文会对 C++ 生态下的构建、编译工具做一个入门级的介绍,但是会参照其他语言里的对等物给出一些关联解释,方便有其他高级语言背景的人快速理解每一种工具的定位和作用。
作为一门古老的语言,C++ 的 IDE、构建工具和编译工具非常多,本文我们定位在:一个C++初学者如何将C++教程中的示例代码迁移到一个IDE的空项目中并实现自动+手动(基于命令行)的项目构建和运行。
1. 构建工具选型
对于C++的"构建 -> 编译 -> 运行"工具,我并没有什么偏好,我选择的技术堆栈来自于一个很"刁钻"的线索,就是观察使用的 IDE,看它们集成了什么构建和编译工具,又是以什么工具作为默认选项的,然后我们就在本地安装对应的工具,用命令行再手搓一遍构建全流程,这样就完成了比较系统的初步学习。以我过去在 IntelliJ 上的经验来看,从这个角度切入筛选出的工具基本都是行业主流,IDE 在选择它们时也是经过评估的,我们就欣然接纳好了。以下是本文要介绍的工具:
| 阶段/领域 | 工具 |
|---|---|
| 代码编写 / IDE | Jetbrains Clion |
| 项目构建 | CMake + Ninja |
| 编译器 | MinGW |
-
代码编写 / IDE
基于一些个人经验和喜好,我目前使用的 IDE 是 Jetbrains 的 Clion,因为我曾长期使用 Jetbrains 的 IntelliJ,编写了很多种语言的代码,包括 Java、Scala、Shell、Python,都工作得很好,没有办法,我太喜欢也太习惯类 IntelliJ 的开发环境了。
-
项目构建
基于前面提到的工具考察思路,我们用 Clion 新建一个 C++ 空项目,你会发现它总是自动生成一个main文件和一个 CMakeLists.txt,这说明 Clion 默认使用 CMake 构建项目,而事实上 CMake 也几乎是 C++ 领域构建应用的事实标准。

对 CMake 稍作研究你就会发现,它并不直接负责构建,而是为下一级的构建工具生成构建描述文件,这里所谓的下一级构建工具一般包括:make(makefile)、ninja(build.ninja)、msvc(project.sln),通过查看 Clion 的 CMake 配置和实际的构建测试可知,Clion 在 CMake 之下使用的是 Ninja:

-
编译器
在 Windows 平台上有两个主流的C++编译器:MSVC 和 MinGW,前者不用多介绍了,后者可以认为是GCC编译器和GNU工具(如make)在 Windows 平台上的实现,开源,遵循严格的 C++ 标准,可移植型好。从上一图截图的"Toolchain"一栏可以看出,Clion 默认使用的就是 MinGW(Clion 也支持MSVC,是可选的,但不是默认编译器)。
最后,作为一个补充说明,我们可以在 Clion 安装目录下的 bin 文件夹中找到cmake、ninja、mingw三个子文件夹,里面是它们各自的程序文件,可知 Clion 确实是直接把它们集成到了自己身上,而后面我们要做的是:在本地再单独安装一边这三个工具,用命行行来构建我们的程序。这样做并没有什么额外收益或非做不可的理由,主要是学习这些工具的安装和使用,且有些时候我们在开发过程中更倾向于打开一个命令窗口直接build程序,使用IDE会很笨重,再者就是单独构建服务器上构建时,你就只能依赖这些命令行工具了。下面我们分别介绍一下三个工具,给出下载地址的安装说明。
1.1 构建工具:CMake
在Java生态里,IntelliJ 是最早全面拥抱 Maven 的 IDE,从项目构建的角度看,IntelliJ 简直是在 Maven 上套的一层壳(GUI),这一点,平推到其他语言,一般也是成立的,只要在你这种语言生态里有主流的构建工具就会发生这种状况。在 C++ 的生态里,占据主导构建工具位置的应该就是 CMake 了。
不过和很多构建工具不同的是,CMake并不算是一个真正的构建工具,因为它本身并不直接构建项目,而为其他构建/编译工具生成配置文件!它的作用在于:提供一种更友好的构建描述规范让用户使用,然后把它"翻译"成其他更底层构建工具所使用的描述,下图能很好地反映 CMake 和另外三种构建/编译工具:make、ninja、msvc 三者之间的关系:

下面简单介绍一下 CMake 的安装:
- 下载 CMake 的 Windows Installer:https://cmake.org/download/
- 在安装界面中有一个选项是将cmake加入到系统的PATH环境变量中,这一项务必勾选上,以确保我们在任意命令行窗口中可以直接使用cmake命令
- 安装完毕后,打开一个命令行窗口,输入:
cmake -version验证安装是否成功
1.2 构建工具:Ninja
相对于 CMake 来说,Ninja 负责执行实际的构建任务,它的角色应该是和 make 对齐的,看上去现代的 C++ 语言都倾向于使用 CMake + ninja 的组合来构建项目了,而 CMake + Make 被认为是过去的传统组合了。不过一个非常有意的地方是,我们似乎不用过多专注于 Ninja,因为,对项目构建的"描述"已经给到 CMake 了,它会负责生成 build.ninja 文件,也就是 ninja 的构建任务描述文件,所以我们不必再学习如何编写 build.ninja 文件,并且 ninja 官方也表示:build.ninja 文件并不适合手写!所以,我们就简单介绍它的安装吧:
- 下载 Ninja 的 Windows 程序包:https://github.com/ninja-build/ninja/releases
- 解压到指定目录
- 手动修改系统环境变量PATH,将 Ninja 的安装目录添加到 PATH 变量中
- 打开一个命令行窗口,输入:
ninja --version验证安装是否成功
1.3 编译器:MinGW
前面已经介绍过了,简单地说:MinGW 就是 Windows 版的 GCC,安装之后,就可以在Windows上执行 gcc 以及 make命令了。MinGW 也是编译好的程序包,下载,解压,添加环境变量即可使用。具体如下:
- 下载 Ninja 的 Windows 程序包:https://github.com/niXman/mingw-builds-binaries/releases, 这里需要解释一下各种包的区别:i686(32位架构) ,x86_64(64位架构),dwarf(适用32位系统),seh(适用64位系统),msvcrt(早期VC++的C运行时库),ucrt(现代随Windows系统发布的新C运行时),所以大部分用户应该选择:
x86_64-xx.x.x-release-win32-seh-ucrt-rt版本 - 解压到指定目录
- 手动修改系统环境变量PATH,将 MinGW 的 bin 目录路径添加到 PATH 变量中
- 打开一个命令行窗口,输入:
gcc --version验证安装是否成功
2. 示例实操
接下来,我们进入实际操作,首先用 IDE 创建一个项目,然后为其添加一些现成的源代码文件,配置 CMake 让其可以在 IDE 中运行起来,然后再改用命令行,手动完成一遍构建工作。
2.1 创建项目
打开 Clion,创建一个名为 Demo 的 C++ 项目,删除自动生成的 main.cpp 文件,在项目根目录下创建两个文件夹:src 和 include 分别存放主程序文件和库文件。这种工程布局不是必须的,只是为了规范的组织源文件人为设定的。
2.2 引入源代码文件
然后,我们可以找一些现成的 C++ 源代码文件添加到项目中以驱动后续的构建流程,本文,我们选择《C++ Primer》第五版第七章的四个示例代码文件:Version_test.h、Sales_data.h、Sales_data.cpp、avg_price.cpp、add_item.cpp,其中后两个是带有main方法的可执行类(你可以从这里:https://www.informit.com/store/c-plus-plus-primer-9780321714114 获得这些源码文件)。我们把前两个头文件放到 include 文件夹中,其余三个源文件放到 src 文件夹中。一切准备妥当后,工程目录应该如下:

2.3 修改 CMake 配置(更新项目构建描述)
此时的项目还不能自动构建运行,因为我们只添加了源代码,却没有告诉 IDE 这些源文件之间是怎样的依赖关系,哪些是可执行的,哪些是库文件,这些都要通过修改 CMake 配置文件 CMakeLists.txt 来实现,这个文件就是对项目构建的"描述"。打开根目录下的 CMakeLists.txt 文件,删除文件的最后一行:add_executable(Demo main.cpp),这是配合新键项目时自动生成的 main.cpp 而存在的。然后添加我们自己的配置,最终完整的 CMakeLists.txt 文件如下:
cmake_minimum_required(VERSION 4.1) # 指定最低 CMake 版本
project(Demo) # 定义项目名称
set(CMAKE_CXX_STANDARD 20) # 设置 C++ 标准为 C++20
# 将工程目录下的 include 文件夹添加到搜索路径中,这意味着:
# 该目录下的所有文件纳入 CMake 管理,被视作了源代码文件,在编译时都是可见的
# 用于存放头文件的 include 目录通常都会使用 include_directories
# 进行添加,以便在编译时所有头文件都是可见的
include_directories(include)
# 定义一个库: sales_lib
add_library(
sales_lib
src/Sales_data.cpp
)
# 定义一个可执行目标: avg_price
add_executable(
avg_price
src/avg_price.cpp
)
# 将可执行目标: avg_price 和库: sales_lib 链接到一起
target_link_libraries(
avg_price sales_lib
)
# 定义第二个可执行目标: add_item
add_executable(
add_item
src/add_item.cpp
)
# 将可执行目标: add_item 和库: sales_lib 链接到一起
target_link_libraries(
add_item sales_lib
)
上面的配置可读性很好,我们不再详细解释了,更多配置解释可以参考 CMake 官方文档:https://cmake.org/documentation/
2.4 在IDE中编译运行
编辑好 CMakeLists.txt 文件后,在工程窗口上选中 CMakeLists.txt 文件,右键点选"Reload CMake Project",Clion 就会同步更新,这时,右上角的 Run Widget 下拉列表框中就出现了我们通过 CMakeLists.txt 定义的三个构建目标,点选 avg_price 或 add_item 就可以运行了!

2.5 在命令行中编译运行
最后,我们再用命令行把这个项目执行一遍,前提的是你已经按照 1.1. - 1.3. 节的说明在本地安转好了 CMake、Ninja、MinGW 三个构建和编译工具。首先,我们在工程目录下新建一个目录,取名:build,用于放置通过命令行方式运行 CMake 产生的文件(使用 Clion IDE 同样会生成文件,放在了 IDE 自动生成的 cmake-build-debug 目录下了),然后打开命令行窗口,进入到 build 目录,执行:
bat
cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug ..
其中 -G Ninja 是让 CMake 生成 Ninja 的工程构建描述, .. 是上一级目录,也就是工程的根目录,因为 CMakeLists.txt 文件在上一级目录。执行后就可以在 build 目录里看到一系列的生成文件了,其中最重要的就是 build.ninja 了,然后,再在命令行执行第二个命令:
bat
ninja
编译就完成了,在 build 目录下会生成 add_item.exe 和 avg_price.exe 两个编译出来的可执行文件。以下命令行窗口和 build 目录截图:
