CMake 生成器已开启

Blog
Author:
Dori ExtermanDori Exterman
Published On:
2月 26, 2021
Estimated reading time:
1 minute

关于 CMake 的讨论已有不少,因为 CMake 无疑是一个伟大的工具。如果你搜索“CMake”,你肯定会看到这样一句话:“CMake 不是一个构建系统,而是一个构建系统生成器”。因此,本篇文章的内容主要围绕什么是 CMake 生成器,为什么 CMake 支持这么多生成器,以及在开发中何时使用。但在开始探讨这些问题之前,我们先复习一下 CMake 构建进程。

CMake-generator

上图中的配置(Configure)步骤仅与 GUI 构建相关。在使用 CMake 命令行时,生成(Generate)命令在内部处理配置步骤。但是生成步骤具体是做什么呢?请看下文。

 

CMake 生成器

什么是 CMake 生成器?从上图可以看出,本机构建工具负责实际编译源文件。这些构建工具要求它们的输入以特定的形式呈现,例如,Makefile。CMake 生成器负责为本机构建工具创建此输入。在生成步骤中,只有一个 CMake 生成器参与构建输出的创建。

 

生成器的类型

总的来说,有两种 CMake 生成器。

CMake-generator-2

命令行构建工具生成器

这类生成器支持命令行构建工具。CMake 是从一个命令行调用的,该命令行的环境已经为所选的编译器和构建工具进行了配置。

CMake-generator-3

Make 是一个著名的 Unix 实用程序,Makefiles 是它的输入。也有文章已深入探讨为何 CMake 完胜 Make。一些非标准的 Makefiles,受其他平台的程序支持。CMake 支持的一些非标准组件包括:

  • Borland Makefiles
  • MSYS Makefiles
  • MinGW Makefiles
  • NMake Makefiles
  • NMake Makefiles JOM
  • Watcom WMake

 

对于这些不同的 Makefiles,实际编译中使用的工具是不同的。例如,在Borland Makefile 中,用于编译 C++ 文件的编译器称为 bcc32。而 Nmake,是与微软 Visual Studio 捆绑的 make 工具,使用的编译器则是微软 C++ 编译器,cl。

尽管 make 已经存在很长时间了,但是维护 Makefiles 的复杂性促使了其他构建系统的开发——其中最成功的是 Ninja。Ninja 代替 make 的独特卖点,在于 Ninja 支持并行构建。在 Ninja 中,构建总是并行运行,而 make 依赖于底层编译器支持的标志来实现这一点。由于 Ninja 是一个现代工具,其暗含的意思是拥有多核 CPU,且所有 CPU 都可以在构建过程中使用,从而提高吞吐量。另外,Ninja 确保在命令完成前缓存其输出,这让并行编译期间出现的错误或失败显而易见。(如果你想进一步减少编译时间,可以查看 Incredibuild 解决方案。这个方案打破了一个系统的局限,让构建在本地网络甚至云中的空闲 CPU 上并发分布进行,因此构建速度非常快。)

 

构建工具生成器

夜间自动构建,人工干预最少,命令行构建工具生成器显然是最佳选择。然而,当开发人员试图使用集成开发环境调试程序时,从源文件和 CMakeLists.txt这些熟悉的开发环境中进行,调试难度将大大降低。CMake  支持以下 IDE:

  • Visual Studio (从版本 6 到版本16)
  • Xcode
  • CodeBlocks
  • CodeLite
  • Eclipse CDT
  • Kate
  • Sublime Text 2

 

CMake 生成器表达式

在编译器的世界里,表达式是需要计算的东西。例如,考虑下面的 C++ 行,其中包含三个变量 a、b 和 c:

a = b + c;

 

这是一个赋值语句,在赋值的右侧包含一个表达式 b+c。表达式 b+c 将被求值,其值将被赋给变量 a。

CMake 有一种语言可以编写 CMakeLists.txt 文件(以及编写脚本和模块)。当然,像你我这样的凡人不需要理解 CMake 语言就可以使用 CMake。生成器表达式用于 CMakeLists.txt 文件或其他地方(一些突出的地方,凭借我们有限的 CMake 语言理解不了)。说实话,我自己也不太喜欢生成器表达式,我相信可读性应该优于简洁性。所以,如果你看到一些类似的东西:

$<abc:xyz>

 

这意味着,如果生成器表达式 abc 的计算结果为正确,则生成器表达式的值为 xyz,否则为空字符串。让我们举个例子来说明这一点。将以下内容另存为 CMakeLists.txt 文件。

cmake_minimum_required(VERSION 3.8)

project(TestingGenerators)

message(STATUS “This message can be seen during generation”)

add_custom_target(print ${CMAKE_COMMAND} -E echo $<0:hello> $<0:world>)

先用这个命令生成:

cmake -S. -BBuild -G “Ninja”

然后构建自定义目标:

cmake –build . –target print

 

此时不应该得到任何输出。现在将上面的文件更改为:

add_custom_target(print ${CMAKE_COMMAND} -E echo $<1:hello> $<1:world>)

 

在命令行上,应该显示“hello world”。1 表示计算结果为正确,因此有一些输出,而 0 则表示计算结果错误,因此没有输出。这是通过一篇普通的博客文章可以解释的最低限度。如果你对生成器表达式感兴趣,我建议可以阅读 CMake 文件。

Speed_up_cpp

结语

CMake 有一些非常强大的功能,但很少使用。在本文中,我们研究了 CMake生成器和 CMake 生成器表达式。希望这些内容能激发你的兴趣,如果你有一定冒险精神,不妨勇敢地探索 CMake,那定是一场奇妙的旅行。祝大家“旅途”愉快!