步骤 1:基本起点¶
从哪里开始使用 CMake?这一步将介绍 CMake 的一些基本语法、命令和变量。在介绍这些概念时,我们将完成三个练习并创建一个简单的 CMake 项目。
本步骤中的每个练习都将从一些背景信息开始。然后,将提供目标和有用的资源列表。Files to Edit
部分中的每个文件都位于 Step1
目录中,并包含一个或多个 TODO
注释。每个 TODO
代表要更改或添加的一两行代码。这些 TODO
应按数字顺序完成,首先完成 TODO 1
,然后完成 TODO 2
等。Getting Started
部分将提供一些有用的提示并指导您完成练习。然后,Build and Run
部分将逐步介绍如何构建和测试练习。最后,在每个练习的末尾将讨论预期的解决方案。
另请注意,教程中的每个步骤都建立在下一个步骤的基础上。因此,例如,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 .
对于多配置生成器(例如 Visual Studio),首先导航到相应的子目录,例如
cd Debug
最后,尝试使用新构建的 Tutorial
Tutorial 4294967296
Tutorial 10
Tutorial
注意:根据 shell 的不同,正确的语法可能是 Tutorial
、./Tutorial
或 .\Tutorial
。为简便起见,练习将始终使用 Tutorial
。
解决方案¶
如上所述,我们只需要一个三行 CMakeLists.txt
即可启动和运行。第一行是使用 cmake_minimum_required()
设置 CMake 版本,如下所示
TODO 1:单击以显示/隐藏答案
cmake_minimum_required(VERSION 3.10)
创建基本项目的下一步是使用 project()
命令,如下所示,以设置项目名称
TODO 2:单击以显示/隐藏答案
project(Tutorial)
创建基本项目所需的最后一个命令是 add_executable()
。我们将其调用如下
TODO 3:单击以显示/隐藏答案
add_executable(Tutorial tutorial.cxx)
练习 2 - 指定 C++ 标准¶
CMake 有一些特殊的变量,这些变量要么是在幕后创建的,要么在项目代码设置后对 CMake 具有意义。许多这些变量以 CMAKE_
开头。在为项目创建变量时,请避免使用此命名约定。这两个特殊的用户可设置变量是 CMAKE_CXX_STANDARD
和 CMAKE_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
中用 std::stod
替换 atof
,将一些 C++11 特性添加到我们的项目中。这看起来如下所示。
TODO 4:单击以显示/隐藏答案
const double inputValue = std::stod(argv[1]);
要完成 TODO 5
,只需删除 #include <cstdlib>
。
我们需要在 CMake 代码中明确说明它应该使用正确的标志。在 CMake 中启用对特定 C++ 标准的支持的一种方法是使用 CMAKE_CXX_STANDARD
变量。对于本教程,将 CMAKE_CXX_STANDARD
变量在 CMakeLists.txt
文件中设置为 11
,并将 CMAKE_CXX_STANDARD_REQUIRED
设置为 True
。确保在调用 add_executable()
之前添加 CMAKE_CXX_STANDARD
声明。
TODO 6:单击以显示/隐藏答案
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_MAJOR
和 Tutorial_VERSION_MINOR
。
TODO 7:单击以显示/隐藏答案
project(Tutorial VERSION 1.0)
然后我们使用 configure_file()
将输入文件复制到指定 CMake 变量替换后的输出文件。
TODO 8:单击以显示/隐藏答案
configure_file(TutorialConfig.h.in TutorialConfig.h)
由于配置后的文件将被写入项目二进制目录,因此我们必须将该目录添加到包含文件搜索路径列表中。
**注意:**在本教程中,我们将项目构建和项目二进制目录互换使用。这些是相同的,并不意味着引用 bin/ 目录。
我们使用 target_include_directories()
指定可执行目标应该在哪里查找包含文件。
TODO 9:单击以显示/隐藏答案
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:单击以显示/隐藏答案
// 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:单击以显示/隐藏答案
#include "TutorialConfig.h"
最后,我们通过更新 tutorial.cxx
如下打印出可执行文件名称和版本号。
TODO 12:单击以显示/隐藏答案
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;
}