第二步:添加库

至此,我们已经了解了如何使用 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.hmysqrt.h。它们各自的源文件 MathFunctions.cxxmysqrt.cxx 也已提供。我们无需修改任何这些文件。mysqrt.cxx 有一个名为 mysqrt 的函数,它提供类似于编译器 sqrt 函数的功能。MathFunctions.cxx 包含一个函数 sqrt,用于隐藏 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:点击显示/隐藏答案
TODO 1:MathFunctions/CMakeLists.txt
add_library(MathFunctions MathFunctions.cxx mysqrt.cxx)

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

TODO 2:点击显示/隐藏答案
TODO 2:CMakeLists.txt
add_subdirectory(MathFunctions)

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

TODO 3:点击显示/隐藏答案

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

TODO 4:点击显示/隐藏答案
TODO 4:CMakeLists.txt
target_include_directories(Tutorial PUBLIC
                          "${PROJECT_BINARY_DIR}"
                          "${PROJECT_SOURCE_DIR}/MathFunctions"
                          )

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

TODO 5:点击显示/隐藏答案
TODO 5:tutorial.cxx
#include "MathFunctions.h"

最后,将 sqrt 替换为包装函数 mathfunctions::sqrt

TODO 6:点击显示/隐藏答案
TODO 6:tutorial.cxx
  double const outputValue = mathfunctions::sqrt(inputValue);

练习 2 - 添加选项

现在让我们在 MathFunctions 库中添加一个选项,以允许开发人员选择自定义平方根实现或内置的标准实现。虽然对于本教程来说,实际上没有任何必要这样做,但对于较大的项目来说,这种情况很常见。

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

目标

添加不构建 MathFunctions 的选项。

有用的资源

要编辑的文件

  • MathFunctions/CMakeLists.txt

  • MathFunctions/MathFunctions.cxx

开始入门

从练习 1 的结果文件开始。完成 TODO 7TODO 14

首先在 MathFunctions/CMakeLists.txt 中使用 option() 命令创建一个变量 USE_MYMATH。在同一文件中,使用该选项将编译定义传递给 MathFunctions 库。

然后,更新 MathFunctions.cxx 以根据 USE_MYMATH 重定向编译。

最后,通过使 mysqrt.cxxUSE_MYMATH 为启用状态时不在编译,方法是使其位于 MathFunctions/CMakeLists.txtUSE_MYMATH 代码块内成为其自己的库。

构建和运行

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

cd ../Step2_build
cmake --build .

接下来,在一些数字上运行 Tutorial 可执行文件,以验证它是否仍然正确。

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

cmake ../Step2 -DUSE_MYMATH=OFF

现在,使用以下命令重新构建代码

cmake --build .

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

解决方案

第一步是在 MathFunctions/CMakeLists.txt 中添加一个选项。此选项将显示在 cmake-guiccmake 中,默认值为 ON,用户可以更改。

TODO 7:点击显示/隐藏答案
TODO 7:MathFunctions/CMakeLists.txt
option(USE_MYMATH "Use tutorial provided math implementation" ON)

接下来,使用这个新选项使构建我们的库并将其与 mysqrt 函数链接成为有条件的。

创建一个 if() 语句,该语句检查 USE_MYMATH 的值。在 if() 代码块内,放置带有编译定义 USE_MYMATHtarget_compile_definitions() 命令。

TODO 8:点击显示/隐藏答案
TODO 8:MathFunctions/CMakeLists.txt
if (USE_MYMATH)
  target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH")
endif()

USE_MYMATHON 时,将设置编译定义 USE_MYMATH。然后,我们可以使用此编译定义来启用或禁用我们源代码的某些部分。

对源代码的相应更改非常简单。在 MathFunctions.cxx 中,我们使 USE_MYMATH 控制使用哪个平方根函数

TODO 9:点击显示/隐藏答案
TODO 9:MathFunctions/MathFunctions.cxx
#ifdef USE_MYMATH
  return detail::mysqrt(x);
#else
  return std::sqrt(x);
#endif

接下来,如果定义了 USE_MYMATH,我们需要包含 mysqrt.h

TODO 10:点击显示/隐藏答案
TODO 10:MathFunctions/MathFunctions.cxx
#ifdef USE_MYMATH
#  include "mysqrt.h"
#endif

最后,既然我们正在使用 std::sqrt,我们需要包含 cmath

TODO 11:点击显示/隐藏答案
TODO 11 : MathFunctions/MathFunctions.cxx
#include <cmath>

此时,如果 USE_MYMATHOFF,则不会使用 mysqrt.cxx,但它仍将被编译,因为 MathFunctions 目标在源文件下列出了 mysqrt.cxx

有几种方法可以解决这个问题。第一个选项是使用 target_sources()USE_MYMATH 代码块中添加 mysqrt.cxx。另一个选项是在 USE_MYMATH 代码块内创建一个额外的库,该库负责编译 mysqrt.cxx。为了本教程的目的,我们将创建一个额外的库。

首先,从 USE_MYMATH 中创建一个名为 SqrtLibrary 的库,该库的源文件为 mysqrt.cxx

TODO 12:点击显示/隐藏答案
TODO 12 : MathFunctions/CMakeLists.txt
  add_library(SqrtLibrary STATIC
              mysqrt.cxx
              )

  # TODO 6: Link SqrtLibrary to tutorial_compiler_flags

  target_link_libraries(MathFunctions PRIVATE SqrtLibrary)
endif()

接下来,当启用 USE_MYMATH 时,我们将 SqrtLibrary 链接到 MathFunctions

TODO 13:点击显示/隐藏答案

最后,我们可以从我们的 MathFunctions 库源列表中删除 mysqrt.cxx,因为它将在包含 SqrtLibrary 时被拉入。

TODO 14:点击显示/隐藏答案
TODO 14 : MathFunctions/CMakeLists.txt
add_library(MathFunctions MathFunctions.cxx)

通过这些更改,mysqrt 函数现在对于构建和使用 MathFunctions 库的任何人来说都是完全可选的。用户可以切换 USE_MYMATH 以操纵构建中使用的库。