第一步:基本起点¶
我应该如何开始使用 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
” 中将 “atof
” 替换为 “std::stod
” 来为我们的项目添加一些 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
”。 确保在调用 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
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;
}