第 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
。
首先,编辑 tutorial.cxx
,添加一个需要 C++11 的功能。然后更新 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:点击显示/隐藏答案
double const 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
。确保将 CMAKE_CXX_STANDARD
声明放在调用 add_executable()
的上方。
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
TutorialConfig.h.in
入门¶
继续编辑 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;
}