第 1 步:基本起点

我从哪里开始使用 CMake?此步骤将介绍 CMake 的一些基本语法、命令和变量。在介绍这些概念时,我们将完成三个练习并创建一个简单的 CMake 项目。

此步骤中的每个练习都将从一些背景信息开始。然后,提供目标和有用资源列表。 Files to Edit 部分中的每个文件都位于 Step1 目录中,并包含一个或多个 TODO 注释。每个 TODO 代表要更改或添加的一两行代码。 TODO 旨在按数字顺序完成,首先完成 TODO 1 然后是 TODO 2 等。Getting Started 部分将提供一些有用的提示并指导您通过练习。然后“构建和运行”部分将逐步介绍如何构建和测试练习。最后,在每个练习结束时讨论预期的解决方案。

另请注意,本教程中的每个步骤都建立在下一步之上。因此,例如,“Step2”的起始代码是“Step1”的完整解决方案。

练习 1 - 构建一个基本项目

最基本的 CMake 项目是从单个源代码文件构建的可执行文件。对于像这样的简单项目,只需要一个包含三个命令的“CMakeLists.txt”文件。

注意: 虽然 CMake 支持大写、小写和混合大小写命令,但首选小写命令,并将在整个教程中使用。

任何项目的最顶层 CMakeLists.txt 必须首先使用 cmake_minimum_required() 命令指定最低 CMake 版本。这会建立策略设置并确保以下 CMake 函数使用兼容版本的 CMake 运行。

要启动一个项目,我们使用 project() 命令来设置项目名称。每个项目都需要此调用,并且应在 cmake_minimum_required 之后立即调用。正如我们稍后将看到的,此命令还可用于指定其他项目级别的信息,例如语言或版本号。

最后, add_executable() 命令告诉 CMake 使用指定的源代码文件创建可执行文件。

目标

了解如何创建简单的 CMake 项目。

有用的资源

要编辑的文件

  • CMakeLists.txt

入门

tutorial.cxx 的源代码在``Help/guide/tutorial/Step1`` 目录中提供,可用于计算数字的平方根。此文件不需要在此步骤中进行编辑。

在同一目录中有一个 CMakeLists.txt 文件,您将完成该文件。从 TODO 1 开始,完成 TODO 3

构建并运行

一旦“TODO 1”到“TODO 3”完成,我们就可以构建和运行我们的项目了!首先,运行 cmake 可执行文件或 cmake-gui 来配置项目,然后使用您选择的构建工具构建它。

例如,从命令行我们可以导航到 CMake 源代码树的 Help/guide/tutorial 目录并创建一个构建目录:

mkdir Step1_build

接下来,导航到该构建目录并运行 cmake 来配置项目并生成本机构建系统:

cd Step1_build
cmake ../Step1

然后调用该构建系统来实际编译/链接项目:

cmake --build .

最后,尝试通过这些命令使用新构建的 Tutorial

Tutorial 4294967296
Tutorial 10
Tutorial

解决方案

如上所述,我们需要启动和运行三行“CMakeLists.txt”。第一行是使用 cmake_minimum_required() 设置 CMake 版本,如下:

TODO 1: Click to show/hide answer
TODO 1:CMakeLists.txt
cmake_minimum_required(VERSION 3.10)

制作基本项目的下一步是使用 project() 命令,如下所示设置项目名称:

TODO 2: Click to show/hide answer
TODO 2:CMakeLists.txt
project(Tutorial)

调用基本项目的最后一个命令是 add_executable()。我们这样称呼它:

TODO 3: Click to show/hide answer
TODO 3:CMakeLists.txt
add_executable(Tutorial tutorial.cxx)

练习 2 - 指定 C++ 标准

CMake 有一些特殊变量,这些变量要么在幕后创建,要么在项目代码设置时对 CMake 有意义。许多这些变量以 CMAKE_ 开头。在为您的项目创建变量时避免这种命名约定。其中两个特殊的用户可设置变量是 CMAKE_CXX_STANDARDCMAKE_CXX_STANDARD_REQUIRED。这些可以一起使用来指定构建项目所需的 C++ 标准。

目标

添加需要 C++11 的功能。

有用的资源

要编辑的文件

  • CMakeLists.txt

  • tutorial.cxx

入门

继续编辑 Step1 目录中的文件。从“TODO 4”开始,通过“TODO 6”完成。

首先,通过添加需要 C++11 的功能来编辑 tutorial.cxx。然后更新 CMakeLists.txt 以要求 C++11。

构建并运行

让我们再次构建我们的项目。由于我们已经为练习 1 创建了构建目录并运行了 CMake,因此我们可以跳到构建步骤:

cd Step1_build
cmake --build .

现在我们可以尝试使用新构建的 Tutorial 和以前一样的命令:

Tutorial 4294967296
Tutorial 10
Tutorial

解决方案

我们首先通过在 tutorial.cxx 中将 atof 替换为 std::stod 来向我们的项目添加一些 C++11 特性。这看起来像下面这样:

TODO 4: Click to show/hide answer
TODO 4:tutorial.cxx
  const double inputValue = std::stod(argv[1]);

要完成“TODO 5”,只需删除“#include <cstdlib>”。

我们需要在 CMake 代码中明确声明它应该使用正确的标志。在 CMake 中启用对特定 C++ 标准的支持的一种方法是使用 CMAKE_CXX_STANDARD 变量。对于本教程,将 CMakeLists.txt 文件中的 CMAKE_CXX_STANDARD 变量设置为 11 并将 CMAKE_CXX_STANDARD_REQUIRED 变量设置为 True。确保在对 add_executable() 的调用上方添加 CMAKE_CXX_STANDARD 声明。

TODO 6: Click to show/hide answer
TODO 6:CMakeLists.txt
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)

练习 3 - 添加版本号和配置的头文件

有时,在您的“CMakelists.txt”文件中定义的变量在您的源代码中也可用可能会很有用。在这种情况下,我们想打印项目版本。

实现此目的的一种方法是使用已配置的头文件。我们创建一个输入文件,其中包含一个或多个要替换的变量。这些变量有特殊的语法,看起来像“@VAR@”。然后,我们使用 configure_file() 命令将输入文件复制到给定的输出文件,并用 CMakelists.txt 文件中的 VAR 的当前值替换这些变量。

虽然我们可以直接在源代码中编辑版本,但首选使用此功能,因为它创建了单一的真实来源并避免了重复。

目标

定义并报告项目的版本号。

有用的资源

要编辑的文件

  • CMakeLists.txt

  • tutorial.cxx

入门

继续编辑“Step1”中的文件。从“TODO 7”开始,完成“TODO 12”。在本练习中,我们首先在 CMakeLists.txt 中添加项目版本号。在同一个文件中,使用 configure_file() 将给定的输入文件复制到输出文件,并替换输入文件内容中的一些变量值。

接下来,创建一个输入头文件 TutorialConfig.h.in 定义版本号,它将接受从 configure_file() 传递的变量。

最后,更新 tutorial.cxx 以打印出它的版本号。

构建并运行

让我们再次构建我们的项目。和以前一样,我们已经创建了一个构建目录并运行了 CMake,因此我们可以跳到构建步骤:

cd Step1_build
cmake --build .

验证在不带任何参数运行可执行文件时现在是否报告了版本号。

解决方案

在本练习中,我们通过打印版本号来改进我们的可执行文件。虽然我们可以在源代码中专门执行此操作,但使用 CMakeLists.txt 可以让我们维护版本号的单一数据源。

首先,我们修改 CMakeLists.txt 文件以使用 project() 命令设置项目名称和版本号。当 project() 命令被调用时,CMake 在幕后定义了 Tutorial_VERSION_MAJORTutorial_VERSION_MINOR

TODO 7: Click to show/hide answer
TODO 7:CMakeLists.txt
project(Tutorial VERSION 1.0)

然后我们使用 configure_file() 复制输入文件并替换指定的 CMake 变量:

TODO 8: Click to show/hide answer
TODO 8:CMakeLists.txt
configure_file(TutorialConfig.h.in TutorialConfig.h)

由于配置文件将写入项目二进制目录,我们必须将该目录添加到路径列表中以搜索包含文件。

注意: 在本教程中,我们将交替引用项目构建和项目二进制目录。这些是相同的,并不意味着引用 bin/ 目录。

我们使用 target_include_directories() 来指定可执行目标应该在哪里寻找包含文件。

TODO 9: Click to show/hide answer
TODO 9:CMakeLists.txt
target_include_directories(Tutorial PUBLIC
                           "${PROJECT_BINARY_DIR}"
                           )

TutorialConfig.h.in 是要配置的输入头文件。当从我们的 CMakeLists.txt 调用 configure_file() 时, @Tutorial_VERSION_MAJOR@@Tutorial_VERSION_MINOR@ 的值将被替换为 ` TutorialConfig.h`

TODO 10: Click to show/hide answer
TODO 10:TutorialConfig.h.in
// the configured options and settings for Tutorial
#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@

接下来,我们需要修改 tutorial.cxx 以包含配置的头文件 TutorialConfig.h

TODO 11: Click to show/hide answer
TODO 11:tutorial.cxx
#include "TutorialConfig.h"

最后,我们通过更新 tutorial.cxx 打印出可执行文件名称和版本号,如下所示:

TODO 12: Click to show/hide answer
TODO 12:教程.cxx
  if (argc < 2) {
    // report version
    std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "."
              << Tutorial_VERSION_MINOR << std::endl;
    std::cout << "Usage: " << argv[0] << " number" << std::endl;
    return 1;
  }