如何构建一个CMake项目(译)

Posted 42&Curry

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何构建一个CMake项目(译)相关的知识,希望对你有一定的参考价值。

CMake是一个能帮助你在几乎所有平台上构建C/C++项目的工具。很多流行的开源项目都使用了CMake,例如:LLVM, Qt, KDE 和 Blender。

所有的CMake项目都包含一个叫做 CMakelists.txt 的脚本,这篇博客就是为了指导如何配置和构建CMake项目而写作的。这篇博客不会告诉你如何编写CMake脚本-那样做的话有些超前了。

举个例子,我准备了一个使用SDL2 和 OpenGL来渲染一个3D logo的CMake项目,你可以在Windows, MacOS 或者 Linux下构建此项目。如果你还没有安装CMake,你可以去CMake官网下载安装器或者二进制分发版本,也可以通过系统的包管理工具下载安装(Unix-like 环境)。

 

Source和Binary文件夹

CMake会生成一个构建流水线(build pipelines)文件。一个构建流水线文件可能是Visual Studio的 .sln 文件, Xcode的 .xcodeproj 文件 或者是Unix风格的 Makefile 文件。构建流水线文件也可能以其他形式出现。

要生成构建流水线文件,CMake需要知道source目录和binary目录。source目录会存放CMakeLists.txt 文件。 binary目录下存放CMake生成的构建流水线文件。你可以将binary目录放到任何位置,一个常见的做法是在CMakelist.txt下创建一个叫build的子目录作为binary目录。

 

通过分离binary和source目录,你可以在任何时候删掉binary目录来回到干净的状态。你甚至可以创建多个binary目录,每一个对应不同的构建系统或者参数配置。

缓存(cache)也是一个重要的概念。它是binary目录下一个叫做 CMakeCache.txt 的文件,里面存放着缓存变量(cache variables)。缓存变量包括用户配置选项-例如CMakeDemo中的 DEMO_ENABLE_MULTISAMPLE 选项, 以及用于加入编译的预计算信息。binary文件夹一般不会加入版本管理,通常做法是每次clone时重新构建binary目录。

 

配置(Configure)和生成(Generate)

下文将介绍几种不同运行CMake的方式。但不管你如何运行,CMake都会执行以下两步: 配置(configure)和 生成(generate)。

CMakeLists.txt在配置阶段执行。这个脚本负责定义目标文件(Targets)。每个目标文件可以是可执行文件,库文件或者是构建流水线的其他输出。

如果配置阶段成果完成-即 CMakeLists.txt运行成功-CMake将用CMakeLists.txt定义的目标文件生成一个构建流水线。 构建流水线的类型取决于生成器(generator)的类型,后面章节将详细解释。

根据CMakeLists.txt内容的不同,配置阶段可能发生额外的事情。例如,在上述 CMakeDemo 项目中,配置阶段还做了以下:

  • 寻找 SDL2 和 OpenGL的头文件和库
  • 在binary目录生成头文件 demo-config.h- 它将在C++代码中被引用  

在更复杂的项目中,配置阶段也许还会检测系统函数是否可用(传统Unix configue脚本都会这么做),或者定义特殊的“安装(install)”目标文件(为了创建一个发行包)。如果你在binary文件夹下重新运行CMake的话,因为使用了缓存,很多缓慢的步骤都会被跳过。

 

在命令行中运行CMake

在运行CMake之前,请确保你有项目和平台要求的依赖。我们经常需要告诉CMake用何种生成器(generator),你可以使用 cmake --help来查看可以选择的生成器。

创建一个binary目录,cd 到该目录,然后运行cmake, 在命令行中指定到source目录的路径。通过 -G 选项指定生成器(如果未指定的话,会使用默认生成器)。

mkdir build
cd build
cmake -G "Visual Studio 15 2017" ..

 如果有项目自身的配置选项的话,你也可以在命令行中指定。例如, CMakeDemo 项目中的 DEMO_ENABLE_MULTISAMPLE 的默认值是0, 你可以通过制定 -DDEMO_ENABLE_MULTISAMPLE=1 来打开此功能。修改 DEMO_ENABLE_MULTISAMPLE 的值会修改 demo-config.h中的内容。变量的值也存储在缓存中,这样我们可以在后续运行中使用。

cmake -G "Visual Studio 15 2017" -DDEMO_ENABLE_MULTISAMPLE=1 ..

如果你后面想改变 DEMO_ENABLE_MULTISAMPLE 的值,只用重新运行CMake即可。后续重新运行不需要传入source目录路径,只用简单制定已经存在的binary目录即可。CMake将在binary目录下找到之前缓存中的设置并且重用它们。

cmake -DDEMO_ENABLE_MULTISAMPLE=0 .

你可以通过 cmake -L -N . 来查看项目定义的缓存变量。如下图所示,你可以看到 CMakeDemo 的 DEMO_ENABLE_MULTISAMPLE 选项被置回0: 

 

运行 cmake-gui

我更喜欢通过命令行使用CMake,但CMake也有GUI。GUI提供了一个交互式的方式来设置缓存变量。再强调一遍,首先确认你安装好了项目所需的依赖。

运行 cmake-gui 来打开图形界面,填好source目录和binary目录的路径,然后点击Configure 。

如果binary目录还不存在,CMake会询问你是否创建该文件夹。然后CMake会让你选择一个生成器。

在初始配置完成后, GUI会显示一列缓存变量 - 和在命令行中运行 cmake -L -N . 的结果类似。新的缓存变量会以红色高亮显示(在下图中,所有的缓存变量都是新的)。如果你再次点击Configure, 红色的高亮将会消失,因为这些缓存变量不再是新的了。 

当你完成缓存变量的设置后,点击Generate。这将在binary目录下生成构建流水线文件,你可以用它们来构建你的项目。

运行 ccmake

ccmake 就像是命令行下的 cmake-gui。就像GUI一样,你可以交互式的设置缓存变量。

 

使用Unix Makefiles来构建

在Unix-like环境下运行CMake会默认生成一个Unix makefile。当然,你可以用 -G 选项显式生成makefiles。生成makefile时,你应该定义 CMAKE_BUILD_TYPE 变量。假如souce目录是binary目录的父目录:

cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Debug ..

因为CMake生成的makefiles都是 单一配置(single-configuration)的,你需要自己定义 CMAKE_BUILD_TYPE。不像Visual Studio中的解决方案(solution),你不能用同一个makefile来构建诸如Debug和Release等多种配置。一个makefile只能完成一种构建。默认的构建类型有: Debug,MinSizeRel,RelwithDebInfo 和 Release。当心使用 CMAKE_BUILD_TYPE, 如果你忘了定义 CMAKE_BUILD_TYPE,你可能得到一个没有debug信息的未优化的构建。要改变构建类型,你必须重新运行CMake并生成新的makefile。

个人而言,我发现CMake默认的 Release 配置没什么用,因为这个配置不生成任何debug信息。 如果你曾打开过崩溃信息(crash dump)或者在 Release 版本中修复过bug, 你将感谢即使是在优化构建中debug信息的存在。因此,在我其他的CMake项目中,我经常删除CMakeLists.txt中的Release配置并使用 RelWithDebInfo配置。

makefile文件存在后,你就能用 make 命令来构建你的项目了。make 默认会构建 CMakeLists.txt 中定义的每个目标文件。 在 CMakeDemo 的例子中,只有一个目标文件需要构建。你还可以通过传入目标文件的名字给 make 来构建一个指定的目标文件:

make CMakeDemo

由CMake生成的makefile能自动检测头文件依赖,所以编辑一个都文件不必重新构建成歌项目。你还可以通过传入 -j 4(或者更大的数字)给 make 来并行化构建。

CMake还可使用 Ninja生成器。 Ninja类似于 make, 但是比 make 更快。Ninja生成器会生成一个 build.ninja 文件 - 类似于 Makefile 文件。Ninja 生成器同样是单一配置的。Ninja的 -j 选项自动调整成可用的 CPU 数。

其他CMake 特性

  • 你可以在命令行下不指定生成器运行构建
  • cmake --build . --target CMakeDemo --config Debug
  • 你可以借助 CMAKE_TOOLCHAIN_FILE 变量来穿件构建流水线以实现交叉编译
  • 你可以生成一个 compile_commands.json 文件给 Clang 的 LibTooling 库使用

 

限于我的兴趣范围,描述在 VS, Xcode, Qt下使用CMake的章节暂且不翻译,有兴趣的读者可以参阅原文(https://preshing.com/20170511/how-to-build-a-cmake-based-project/)。

 

以上是关于如何构建一个CMake项目(译)的主要内容,如果未能解决你的问题,请参考以下文章

Cmake 无法在 qt creator/collect2 上编译简单的测试程序:错误:ld

如何构建 CMake 项目

如何构建具有 cmake 文件的 c++/c 项目?

如何使用 Visual Studio Code 为 cmake 添加预构建步骤?

如何将 TensorFlow Lite 构建为静态库并从单独的(CMake)项目链接到它?

15分钟学习CMake脚本(译)