步骤 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.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"

最后,使用包装函数 mathfunctions::sqrt 替换 sqrt

TODO 6:单击显示/隐藏答案
TODO 6:tutorial.cxx
  const double 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 来重新定向编译。

最后,通过在 MathFunctions/CMakeLists.txtUSE_MYMATH 块内部使其成为其自己的库,从而防止在 USE_MYMATH 开启时编译 mysqrt.cxx

构建并运行

由于我们已经从练习 1 中配置了构建目录,因此只需调用以下命令即可重建

cd ../Step2_build
cmake --build .

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

现在,让我们将 USE_MYMATH 的值更新为 OFF。最简单的办法是使用 cmake-gui,如果您在终端中,也可以使用 ccmake。或者,如果您希望在命令行中更改选项,可尝试

cmake ../Step2 -DUSE_MYMATH=OFF

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

cmake --build .

然后,再次运行可执行文件以确保它在将 USE_MYMATH 设置为 OFF 后仍然可以正常工作。哪个函数提供更好的结果,sqrt 还是 mysqrt

解决方案

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

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

接下来,使使用此新选项构建和链接我们与 mysqrt 函数的库成为条件。

创建一个if()语句,该语句将检查USE_MYMATH的值。在if()代码块中,将target_compile_definitions()命令与编译定义USE_MYMATH一同放入。

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

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

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

待办事项 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 以操控构建中使用的库。