练习1 -安装规则
通常,仅仅构建可执行文件是不够的,它还应该是可安装的。使用CMake,我们可以使用install()命令指定安装规则。
目标
安装教程可执行文件和MathFunctions库。
需要编辑的文件
MathFunctions/CMakeLists.txt
CMakeLists.txt
步骤
在Step5目录中提供了起始代码。在这个练习中,完成TODO 1到TODO 4。
首先,更新MathFunctions/CMakeLists.txt,将MathFunctions和tutorial_compiler_flags库安装到lib目录中。在同一个文件中,指定将MathFunctions.h安装到include目录所需的安装规则。
然后,更新顶层的CMakeLists.txt,将教程可执行文件安装到bin目录中。最后,所有头文件都应该安装到include目录中。请记住,TutorialConfig.h位于PROJECT_BINARY_DIR中。
实现操作
项目的安装规则为:
对于MathFunctions,我们希望分别将库和头文件安装到lib
和include
目录中。
对于教程可执行文件,我们希望分别将可执行文件和配置的头文件安装到bin和include目录中。
因此,在MathFunctions/CMakeLists.txt的末尾,我们添加:
第1步:
//定义一个变量installable_libs,其中包含了两个值:MathFunctions和tutorial_compiler_flags。这些通常是指CMake项目中的目标(target)或库(library),用于后续的安装步骤。
set(installable_libs MathFunctions tutorial_compiler_flags)
//判断是否存在名为SqrtLibrary的目标。如果存在(即TARGET SqrtLibrary条件成立),则通过list(APPEND ...)命令将SqrtLibrary添加到installable_libs变量的末尾。这样,如果项目中定义了SqrtLibrary目标,它就会被包含在安装列表中。
if(TARGET SqrtLibrary)
list(APPEND installable_libs SqrtLibrary)
endif()
//install命令来指定将哪些目标安装到指定的目标路径(DESTINATION lib)。${installable_libs}变量在这里用作install命令的参数,表示安装这些目标或库。根据之前的定义和条件判断,可能包括MathFunctions、tutorial_compiler_flags以及可选的SqrtLibrary(如果存在)。
install(TARGETS ${installable_libs} DESTINATION lib)
第2步:
install(FILES MathFunctions.h DESTINATION include)
install 是CMake提供的一个命令,用于安装文件或目标到指定的位置。
FILES MathFunctions.h 指定了要安装的文件列表,这里只有一个文件 MathFunctions.h。
DESTINATION include 指定了安装的目标路径。在这里,include 表示将文件安装到目标路径下的 include 子目录中。
这行代码的作用是将项目中的 MathFunctions.h 文件安装到目标路径的 include 目录下,使得其他项目或者用户在使用这个库时可以直接包含这个头文件。
教程可执行文件和配置的头文件的安装规则是相似的。在顶级CMakeLists.txt的末尾,我们添加:
install(TARGETS Tutorial DESTINATION bin)
install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"
DESTINATION include
)
这就是创建本教程的基本本地安装所需的全部内容。
编译和运行
新建一个名为Step5_build的目录。从命令行使用cmake命令的--install选项(在3.15中引入,旧版本的cmake必须使用make install)运行安装步骤。这一步将安装适当的头文件、库和可执行文件。例如:
cmake --install .
对于多配置工具,不要忘记使用--config参数来指定配置。
cmake --install . --config Release
如果使用IDE,只需构建INSTALL目标。您可以从命令行构建相同的安装目标,如下所示:
cmake --build . --target install --config Debug
CMake变量CMAKE_INSTALL_PREFIX用于确定文件安装的根目录。如果使用cmake--install命令,可以通过--prefix参数重写安装前缀。例如:
cmake --install . --prefix "/home/myuser/installdir"
移动到安装目录,并验证安装的教程库是否运行。
练习2 -测试支持
CTest提供了一种轻松管理项目测试的方法。可以通过add_test()命令添加测试。
目标
使用CTest为我们的可执行文件创建单元测试。
步骤
源代码在Step5目录中提供。在这个练习中,完成TODO 5到TODO 9。
首先,我们需要启用测试。接下来,开始使用add_test()向项目添加测试。我们将添加3个简单的测试。
具体操作
在顶层CMakeLists.txt文件的末尾,我们首先需要使用enable_testing()命令启用测试。
enable_testing()
启用测试后,我们将添加一些基本测试来验证应用程序是否正常工作。首先,使用add_test()创建一个测试,它运行教程可执行文件,并传入参数25。此测试将验证应用程序是否运行,没有出现分段故障或以其他方式崩溃,并且返回值为零。
add_test(NAME Runs COMMAND Tutorial 25)
add_test
是 CMake 的命令,用于添加一个测试。它会创建一个新的测试,并将其添加到 CTest 的测试套件中。NAME Runs
指定了测试的名称为 "Runs"。这个名称是用来标识测试的唯一标识符,用于在测试结果中查找和报告。COMMAND Tutorial 25
指定了要运行的命令。在这里,"Tutorial" 是要执行的可执行文件的名称,而 "25" 则是传递给该可执行文件的参数。
这行代码的目的是创建一个名为 "Runs" 的测试,该测试会运行名为 "Tutorial" 的可执行文件,并传递整数参数 25 给这个可执行文件。此测试的主要目标是验证以下几点:
- 应用程序能够正常运行,即不会因为段错误或其他崩溃而终止。
- 应用程序的返回值应为零,这表示程序在运行过程中没有遇到严重错误或异常情况。
这种测试方法适用于验证应用程序的基本运行情况,而不涉及具体的计算结果是否正确,主要关注程序是否能够在最基本的情况下正常启动和结束。
接下来,让我们使用PASS_REGULAR_EXPRESSION测试属性来验证测试的输出是否包含某些字符串。在本例中,验证当提供的参数数量不正确时是否打印usage消息。
add_test(NAME Usage COMMAND Tutorial)
set_tests_properties(Usage
PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number"
)
-
add_test(NAME Usage COMMAND Tutorial)
:add_test
命令创建一个名为 "Usage" 的测试。NAME Usage
指定了测试的名称为 "Usage"。这个名称用于在测试结果中标识和查找测试。COMMAND Tutorial
指定了要运行的命令或可执行文件为 "Tutorial"。这表示测试会执行名为 "Tutorial" 的可执行文件。
-
set_tests_properties(Usage PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number")
:set_tests_properties
是 CMake 的命令,用于设置测试的属性。Usage
是之前定义的测试名称,这里是 "Usage"。PROPERTIES
关键字用于指定要设置的属性。PASS_REGULAR_EXPRESSION
是一个测试属性,它指定了一个正则表达式模式。"Usage:.*number"
是一个正则表达式,它描述了期望在测试的输出中匹配的字符串模式。具体含义是:Usage:
:输出中应包含 "Usage:" 这个字符串。.*
:表示任意数量的任意字符,这里是用来匹配 "Usage:" 后面可能存在的其他字符。number
:输出中还应包含 "number" 这个单词。
这段代码的作用是创建一个名为 "Usage" 的测试,测试会运行名为 "Tutorial" 的可执行文件,并且验证该可执行文件的输出中是否包含 "Usage:" 和 "number"。如果测试输出符合这个正则表达式模式,那么测试将被视为通过;否则,测试将失败。这种方法可以用来验证程序在特定情况下是否输出了预期的使用说明或错误信息。
接下来将添加的下测试命令来验证计算值为平方根,如下所示:
add_test(NAME StandardUse COMMAND Tutorial 4)
set_tests_properties(StandardUse
PROPERTIES PASS_REGULAR_EXPRESSION "4 is 2"
)
-
add_test(NAME StandardUse COMMAND Tutorial 4)
:add_test
命令创建一个名为 "StandardUse" 的测试。NAME StandardUse
指定了测试的名称为 "StandardUse"。COMMAND Tutorial 4
指定了要运行的命令为 "Tutorial",并传递整数参数 4 给这个可执行文件。这表示测试会执行名为 "Tutorial" 的可执行文件,并向其传递参数 4。
-
set_tests_properties(StandardUse PROPERTIES PASS_REGULAR_EXPRESSION "4 is 2")
:set_tests_properties
命令用于设置测试的属性。StandardUse
是之前定义的测试名称。PROPERTIES
关键字用于指定要设置的属性。PASS_REGULAR_EXPRESSION
是一个测试属性,指定了一个正则表达式模式。"4 is 2"
是正则表达式,描述了测试输出中预期要匹配的字符串。
这段代码的目的是创建一个名为 "StandardUse" 的测试,该测试会运行名为 "Tutorial" 的可执行文件,传递参数 4 给它。然后,它验证该可执行文件的输出中是否包含字符串 "4 is 2"。这样的验证通常用于确保程序在计算平方根时,输出确实是预期的平方根值。例如,这里期望的输出是 "4 is 2",表示对于输入 4,程序应输出其平方根值为 2。如果输出符合这个正则表达式模式,测试将被视为通过;否则,测试将失败。
这一个测试还不足以让我们确信它对传入的所有值都有效。我们应该增加更多的测试来验证这一点。为了方便地添加更多测试,我们创建了一个名为do_test的函数,该函数运行应用程序并验证给定输入的计算平方根是否正确。
function(do_test target arg result)
add_test(NAME Comp${arg} COMMAND ${target} ${arg})
set_tests_properties(Comp${arg}
PROPERTIES PASS_REGULAR_EXPRESSION ${result}
)
endfunction()
# do a bunch of result based tests
do_test(Tutorial 4 "4 is 2")
do_test(Tutorial 9 "9 is 3")
do_test(Tutorial 5 "5 is 2.236")
do_test(Tutorial 7 "7 is 2.645")
do_test(Tutorial 25 "25 is 5")
do_test(Tutorial -25 "-25 is (-nan|nan|0)")
do_test(Tutorial 0.0001 "0.0001 is 0.01")
1.函数定义 do_test
:
function(do_test target arg result)
add_test(NAME Comp${arg} COMMAND ${target} ${arg})
set_tests_properties(Comp${arg}
PROPERTIES PASS_REGULAR_EXPRESSION ${result}
)
endfunction()
-
function(do_test target arg result)
:定义了一个名为do_test
的函数,它接受三个参数:target
:表示要运行的可执行文件的名称。arg
:表示传递给可执行文件的参数。result
:是一个正则表达式,用于验证测试的输出是否符合预期。
-
add_test(NAME Comp${arg} COMMAND ${target} ${arg})
:在这个函数中,首先使用add_test
命令创建一个新的测试。NAME Comp${arg}
:指定了测试的名称,这里会根据传入的参数arg
动态生成,确保每个测试名称是唯一的。COMMAND ${target} ${arg}
:指定要运行的命令和参数,即运行${target}
可执行文件,并传递${arg}
作为参数。
-
set_tests_properties(Comp${arg} PROPERTIES PASS_REGULAR_EXPRESSION ${result})
:接着,使用set_tests_properties
命令设置测试的属性。Comp${arg}
:指定要设置属性的测试名称。PROPERTIES PASS_REGULAR_EXPRESSION ${result}
:将PASS_REGULAR_EXPRESSION
属性设置为${result}
,这个属性告诉测试框架应该用正则表达式${result}
来匹配测试输出,以验证测试是否通过。
2.调用 do_test
函数:
-
# do a bunch of result based tests do_test(Tutorial 4 "4 is 2") do_test(Tutorial 9 "9 is 3") do_test(Tutorial 5 "5 is 2.236") do_test(Tutorial 7 "7 is 2.645") do_test(Tutorial 25 "25 is 5") do_test(Tutorial -25 "-25 is (-nan|nan|0)") do_test(Tutorial 0.0001 "0.0001 is 0.01")
- 这部分代码展示了如何使用
do_test
函数来添加多个测试。 - 每个
do_test
调用都会添加一个新的测试,指定了要运行的可执行文件Tutorial
,传递给它的参数以及预期的输出结果。 - 例如,
do_test(Tutorial 4 "4 is 2")
表示添加一个测试,运行Tutorial
可执行文件,传递参数 4,并期望输出包含字符串 "4 is 2"。
- 这部分代码展示了如何使用
通过这种方式,使用 do_test
函数可以大大简化添加测试的过程,并确保每个测试都验证了平方根计算的正确性,从而增加了代码的可靠性和测试覆盖率。
编译和运行
移动到编译目录并重新编译程序。然后,运行ctest执行命令:ctest -N或ctest -VV。对于多配置生成器(例如Visual Studio),必须使用-C <mode>标志指定配置类型。例如,要在Debug模式下运行测试,执行ctest -C Debug -VV。
-
ctest -N
:-N
选项用于进行测试的预览,也称为显示测试。它不会真正运行测试,而是列出将要运行的测试列表。- 用法示例:在终端中进入构建目录,然后运行
ctest -N
。这将显示将要运行的测试的列表,但不会实际执行测试代码。
-
ctest -VV
:-VV
选项用于详细输出,也称为详细输出模式。它会显示更多关于测试的详细信息,包括每个测试用例的输出和其他相关信息。- 用法示例:在终端中进入构建目录,然后运行
ctest -VV
。这将以详细模式运行测试,并显示详细的输出信息,帮助诊断测试失败或其他问题。
-
ctest -C Debug -VV
:-C <mode>
选项用于指定配置模式,例如 Debug 或 Release。对于多配置生成器(如 Visual Studio),这是必需的。-VV
选项用于详细输出模式,详细描述见上述。- 用法示例:在终端中进入构建目录,然后运行
ctest -C Debug -VV
。这将以 Debug 模式运行测试,并显示详细的输出信息。
总结:
ctest -N
:显示将要运行的测试列表,但不运行实际的测试代码。ctest -VV
:以详细输出模式运行测试,显示更多的测试执行信息。ctest -C Debug -VV
:以 Debug 配置模式运行测试,并显示详细的输出信息。
这些命令可以帮助开发人员管理和执行项目中的测试,提高代码质量和可靠性。