本节将学习如何在项目中创建和使用库,还将看到如何使库的使用成为可选的。
本节中使用的示例代码下载见step1-简单开始cmake实践-CSDN博客。
练习1 -创建一个库
要在CMake中添加一个库,使用add_library()命令并指定哪些源文件应该组成该库。
我们可以使用一个或多个子目录组织项目,而不是将所有源文件放在一个目录中。在本例中,我们将专门为库创建一个子目录。在这里,我们可以添加一个新的CMakeLists.txt文件和一个或多个源文件。在顶级的CMakeLists.txt文件中,我们将使用add_subdirectory()命令将子目录添加到构建中。
创建库之后,使用target_include_directories()和target_link_libraries()将其连接到可执行目标。
目标
添加并使用库。
开始
在本练习中,我们将在项目中添加一个库,其中包含我们自己的用于计算数字平方根的实现。然后,可执行文件可以使用这个库,而不采用编译器提供的标准平方根函数。
在本教程中,我们将把库放入名为MathFunctions的子目录中。这个目录已经包含了头文件MathFunctions.h和mysqrt.h。还提供了它们各自的源文件MathFunctions.cxx和mysqrt.cxx。我们不需要修改这些文件。mysqrt.cxx有一个名为mysqrt的函数,它提供了与编译器的SQRT函数类似的功能。MathFunctions.cxx包含一个函数SQRT,用于隐藏我们自己写的SQRT函数的实现细节。
从在本文提供的代码实例文件夹下/Step2目录中,从TODO 1开始,完成TODO 6。
首先,在MathFunctions子目录中填写一行CMakeLists.txt。
接下来,编辑顶层的CMakeLists.txt。
最后,使用tutorial.cxx中新创建的MathFunctions库。
操作
在MathFunctions目录下的CMakeLists.txt文件中,我们使用add_library()创建了一个名为MathFunctions的库目标。库的源文件作为参数传递给add_library()。这看起来像下面这行:
// TODO 1: MathFunctions/CMakeLists.txt
add_library(MathFunctions MathFunctions.cxx mysqrt.cxx)
为了使用新库,我们将在顶级CMakeLists.txt文件中添加add_subdirectory()调用,以便库将被构建。
// TODO 2: CMakeLists.txt
add_subdirectory(MathFunctions)
接下来,使用target_link_libraries()将新的库目标链接到可执行目标。
// TODO 3: CMakeLists.txt
target_link_libraries(Tutorial PUBLIC MathFunctions)
最后,我们需要指定库的头文件位置。修改现有的target_include_directories()调用,将MathFunctions子目录添加为包含目录,这样就可以找到MathFunctions.h头文件。
// TODO 4: CMakeLists.txt
target_include_directories(Tutorial PUBLIC
"${PROJECT_BINARY_DIR}"
"${PROJECT_SOURCE_DIR}/MathFunctions"
)
现在让我们使用库。在tutorial.cxx
中包含MathFunctions.h:
// TODO 5: tutorial.cxx
#include "MathFunctions.h"
最后,用包装函数mathfunctions::sqrt替换sqrt。
// TODO 6: tutorial.cxx
const double outputValue = mathfunctions::sqrt(inputValue);
编译和运行
mkdir Step2_build
cd Step2_build
cmake ../Step2
cmake --build .
尝试使用编译好的项目,并确保它仍然产生准确的平方根值。
./Tutorial 4294967296
./Tutorial 10
./Tutorial
练习2 -添加选项
现在让我们在MathFunctions库中添加一个选项,允许开发人员选择自定义的平方根实现或内置的标准实现。
CMake可以使用option()命令执行此操作。这为用户提供了一个可以在配置cmake构建时更改的变量。
目标
添加不使用MathFunctions函数的编译选项。
步骤
首先使用MathFunctions/CMakeLists.txt中的option()命令创建一个变量USE_MYMATH。在同一个文件中,使用该选项将编译定义传递给MathFunctions库。
然后,更新MathFunctions,基于USE_MYMATH重定向编译。
最后,当USE_MYMATH打开时,通过在MathFunctions/CMakeLists.txt的USE_MYMATH块中使其成为自己的库。
操作
第一步是在MathFunctions/CMakeLists.txt中添加一个选项,默认值为ON,可由用户更改。
// 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: MathFunctions/CMakeLists.txt
if (USE_MYMATH)
target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH")
endif()
条件判断:
-
if (USE_MYMATH)
这里使用了 CMake 的
if
命令来判断变量USE_MYMATH
是否为真(即非空或者非零)。如果USE_MYMATH
被定义为ON
、TRUE
、非空字符串或者被设置为一个非零数值,条件判断将会成立。 -
目标编译定义:
-
target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH")
如果
USE_MYMATH
的条件成立,那么就会为目标MathFunctions
添加一个编译定义。在这里,编译定义是"USE_MYMATH"
。这意味着在编译MathFunctions
库或可执行文件时,会将"USE_MYMATH"
添加到编译器的预处理器定义中。 -
PRIVATE 关键字:
PRIVATE
关键字指定了这个编译定义的作用域。在 CMake 中,PRIVATE
表示这个定义只会影响到MathFunctions
目标本身及其直接依赖的其他目标。这意味着其他使用MathFunctions
的目标或库不会自动继承"USE_MYMATH"
这个定义,除非它们也显式地声明。
这段代码的作用是根据 USE_MYMATH
变量的值来决定是否为 MathFunctions
添加一个编译定义 "USE_MYMATH"
。这样的做法通常用于根据不同的构建选项或条件,启用或禁用特定的功能或代码路径。
当USE_MYMATH为ON时,编译定义USE_MYMATH将被设置。然后,我们可以使用这个编译定义来启用或禁用源代码的部分。
接下来在MathFunctions.cxx文件中,我们让USE_MYMATH控制使用哪个平方根函数:
#ifdef USE_MYMATH
return detail::mysqrt(x);
#else
return std::sqrt(x);
#endif
接下来,如果定义了USE_MYMATH,我们需要包含mysqrt.h。
// TODO 10: MathFunctions/MathFunctions.cxx
#ifdef USE_MYMATH
# include "mysqrt.h"
#endif
最后,在使用std::sqrt时,我们需要包含cmath。
// TODO 11 : MathFunctions/MathFunctions.cxx
#include <cmath>
此时,如果USE_MYMATH为OFF, mysqrt.cxx不会被使用,但它仍然会被编译,因为MathFunctions目标有mysqrt.cxx列在来源下面。
有几种方法可以解决这个问题。第一个选项是使用target_sources()来添加mysqrt.cxx从USE_MYMATH块中取出。另一个选择是在USE_MYMATH块中创建一个额外的库,它负责编译mysqrt.cxx。在本教程中,我们将创建一个额外的库。
首先,在USE_MYMATH中创建一个名为SqrtLibrary的库,其源代码为mysqrt.cxx。
// 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/CMakeLists.txt
target_link_libraries(MathFunctions PRIVATE SqrtLibrary)
最后,我们可以从MathFunctions库源列表中删除mysqrt.cxxcxx,因为它将在包含SqrtLibrary时被拉入。
// TODO 14 : MathFunctions/CMakeLists.txt
add_library(MathFunctions MathFunctions.cxx)
通过这些更改,mysqrt函数现在对于正在构建和使用MathFunctions库的人来说完全是可选的。用户可以切换USE_MYMATH来操作编译中使用的库。
编译并运行
由于我们已经在练习1中配置了编译目录,我们可以通过简单地调用以下命令进行编译:
cd ../Step2_build
cmake --build .
现在让我们将USE_MYMATH的值更新为OFF。
cmake ../Step2 -DUSE_MYMATH=OFF
现在,用下面的代码重新编译代码:
cmake --build .
然后,再次运行可执行文件,以确保它在USE_MYMATH设置为OFF时仍然工作。