第 2 步:添加库

至此,我们已经了解了如何使用 CMake 创建一个基本项目。在这一步中,我们将学习如何在我们的项目中创建和使用库。我们还将看到如何使我们的库的使用成为可选的。

练习 1 - 创建库

要在 CMake 中添加库,请使用 add_library() 命令并指定应由哪些源文件组成库。

我们可以用一个或多个子目录来组织我们的项目,而不是将所有源文件放在一个目录中。在这种情况下,我们将专门为我们的库创建一个子目录。在这里,我们可以添加一个新的 CMakeLists.txt 文件和一个或多个源文件。在顶层 CMakeLists.txt 文件中,我们将使用 add_subdirectory() 命令将子目录添加到构建中。

创建库后,它会通过 target_include_directories()target_link_libraries() 连接到我们的可执行目标。

目标

添加和使用库。

有用的资源

要编辑的文件

  • CMakeLists.txt

  • tutorial.cxx

  • MathFunctions/CMakeLists.txt

入门

在本练习中,我们将向我们的项目添加一个库,其中包含我们自己用于计算数字平方根的实现。然后可执行文件可以使用这个库而不是编译器提供的标准平方根函数。

对于本教程,我们会将库放入名为“MathFunctions”的子目录中。该目录已经包含一个头文件“MathFunctions.h”和一个源文件“mysqrt.cxx”。我们不需要修改这些文件中的任何一个。源文件有一个名为“mysqrt”的函数,它提供与编译器的“sqrt”函数类似的功能。

Help/guide/tutorial/Step2 目录中,从 TODO 1 开始,完成 TODO 6

首先,填写``MathFunctions`` 子目录中的一行``CMakeLists.txt``。

接下来,编辑顶层“CMakeLists.txt”。

最后,在 tutorial.cxx 中使用新创建的 MathFunctions

构建并运行

运行 cmake 可执行文件或 cmake-gui 来配置项目,然后使用您选择的构建工具构建它。

下面是从命令行看起来像什么的复习:

mkdir Step2_build
cd Step2_build
cmake ../Step2
cmake --build .

尝试使用新建的 Tutorial 并确保它仍然生成准确的平方根值。

解决方案

MathFunctions 目录下的 CMakeLists.txt 文件中,我们使用 add_library() 创建了一个名为 MathFunctions 的库目标。库的源文件作为参数传递给 add_library()。这看起来像下面一行:

TODO 1: Click to show/hide answer
TODO 1:MathFunctions/CMakeLists.txt
add_library(MathFunctions mysqrt.cxx)

为了使用新库,我们将在顶级 CMakeLists.txt 文件中添加一个 add_subdirectory 调用,以便构建库。

TODO 2: Click to show/hide answer
TODO 2:CMakeLists.txt
add_subdirectory(MathFunctions)

接下来,使用 target_link_libraries 将新库目标链接到可执行目标。

TODO 3: Click to show/hide answer

最后我们需要指定库的头文件位置。修改 target_include_directories() 以将 MathFunctions 子目录添加为包含目录,以便可以找到 MathFunctions.h 头文件。

TODO 4: Click to show/hide answer
待办事项 4:CMakeLists.txt
target_include_directories(Tutorial PUBLIC
                          "${PROJECT_BINARY_DIR}"
                          "${PROJECT_SOURCE_DIR}/MathFunctions"
                          )

现在让我们使用我们的库。在 tutorial.cxx 中,包含 MathFunctions.h

TODO 5: Click to show/hide answer
TODO 5:tutorial.cxx
#include "MathFunctions.h"

最后,将 sqrt 替换为我们的库函数 mysqrt

TODO 6: Click to show/hide answer
TODO 6:tutorial.cxx
const double outputValue = mysqrt(inputValue);

练习 2 - 让我们的图书馆成为可选的

现在让我们将 MathFunctions 库设为可选。虽然对于教程来说确实没有必要这样做,但对于较大的项目来说这是很常见的事情。

CMake 可以使用 option() 命令来做到这一点。这为用户提供了一个变量,他们可以在配置他们的 cmake 构建时更改该变量。此设置将存储在缓存中,因此用户无需在每次在构建目录上运行 CMake 时都设置该值。

目标

添加选项以在没有“MathFunctions”的情况下构建。

有用的资源

  • 如果()

  • 列表()

  • 选项()

  • cmakedefine

要编辑的文件

  • CMakeLists.txt

  • tutorial.cxx

  • TutorialConfig.h.in

入门

从练习 1 的结果文件开始。完成“TODO 7”到“TODO 13”。

首先在顶层 CMakeLists.txt 文件中使用 option() 命令创建一个变量 USE_MYMATH。在同一个文件中,使用该选项来确定是否构建和使用“MathFunctions”库。

然后,更新 tutorial.cxxTutorialConfig.h.in 以使用 USE_MYMATH

构建并运行

由于我们已经从练习 1 中配置了构建目录,我们可以通过简单地调用以下命令来重建:

cd ../Step2_build
cmake --build .

接下来,对几个数字运行“教程”可执行文件以验证它是否仍然正确。

现在让我们将 USE_MYMATH 的值更新为 OFF。最简单的方法是使用 cmake-guiccmake 如果您在终端中。或者,如果您想从命令行更改选项,请尝试:

cmake ../Step2 -DUSE_MYMATH=OFF

现在,使用以下内容重建代码:

cmake --build .

然后,再次运行可执行文件以确保它仍然可以在“USE_MYMATH”设置为“OFF”的情况下工作。哪个函数给出更好的结果,sqrtmysqrt

解决方案

第一步是向顶级“CMakeLists.txt”文件添加一个选项。此选项将显示在 cmake-guiccmake 中,默认值为 ON 可以更改由用户。

TODO 7: Click to show/hide answer
TODO 7:CMakeLists.txt
option(USE_MYMATH "Use tutorial provided math implementation" ON)

接下来,使构建和链接“MathFunctions”库成为条件。

首先为我们的项目创建可选库目标的 list()。目前,它只是``MathFunctions``。让我们将列表命名为“EXTRA_LIBS”。

同样,我们需要为可选的包含创建一个 list(),我们将其称为 EXTRA_INCLUDES。在此列表中,我们将“附加”我们的库所需的头文件的路径。

接下来,创建一个 if() 语句来检查 USE_MYMATH 的值。在 if() 块内,将练习 1 中的 add_subdirectory() 命令与其他 list() 命令放在一起。

当“USE_MYMATH”为“ON”时,将生成列表并将其添加到我们的项目中。当“USE_MYMATH”为“OFF”时,列表保持为空。通过这种策略,我们允许用户切换“USE_MYMATH”来操纵构建中使用的库。

顶级 CMakeLists.txt 文件现在如下所示:

TODO 8: Click to show/hide answer
TODO 8:CMakeLists.txt
if(USE_MYMATH)
  add_subdirectory(MathFunctions)
  list(APPEND EXTRA_LIBS MathFunctions)
  list(APPEND EXTRA_INCLUDES "${PROJECT_SOURCE_DIR}/MathFunctions")
endif()

现在我们有了这两个列表,我们需要更新 target_link_libraries()target_include_directories() 来使用它们。更改它们非常简单。

对于 target_link_libraries(),我们将写出的库名称替换为 EXTRA_LIBS。这看起来像下面这样:

TODO 9: Click to show/hide answer

然后,我们对 target_include_directories()EXTRA_INCLUDES 做同样的事情。

TODO 10: Click to show/hide answer

请注意,这是处理许多组件时的经典方法。我们将在本教程的第 3 步中介绍现代方法。

对源代码的相应更改非常简单。首先,在 tutorial.cxx 中,如果定义了 USE_MYMATH,我们会包含 MathFunctions.h 头文件。

TODO 11: Click to show/hide answer
TODO 11:教程.cxx
#ifdef USE_MYMATH
#  include "MathFunctions.h"
#endif

然后,在同一个文件中,我们让 USE_MYMATH 控制使用哪个平方根函数:

TODO 12: Click to show/hide answer
TODO 12:教程.cxx
#ifdef USE_MYMATH
  const double outputValue = mysqrt(inputValue);
#else
  const double outputValue = sqrt(inputValue);
#endif

由于源代码现在需要``USE_MYMATH``,我们可以使用以下行将其添加到``TutorialConfig.h.in``:

TODO 13: Click to show/hide answer
TODO 13:TutorialConfig.h.in
#cmakedefine USE_MYMATH

通过这些更改,我们的库现在对于构建和使用它的任何人来说都是完全可选的。

奖金问题

为什么在“USE_MYMATH”选项之后配置“TutorialConfig.h.in”很重要?如果我们将两者倒置会发生什么?

回答

Click to show/hide answer

我们在之后进行配置,因为 TutorialConfig.h.in 使用了 USE_MYMATH 的值。如果我们在调用 option() 之前配置文件,我们将不会使用 USE_MYMATH 的预期值。