CMake 教程(五):安装和测试
通常,只构建出可执行程序是不够的,还需要能够安装可执行程序。在 CMake 中,我们可以使用 install()
指令指明安装规则。这条指令能够用来指明安装的程序、位置和文件。
本节使用的源代码材料是官网的 Step5
目录。下面开始本节的练习。
练习1:安装规则(Install Rules)
本项目的安装规则如下:
- 对于
MathFunctions
,需要将库和头文件分别安装到lib
和include
目录当中。 - 对于
Tutorial
可执行文件,需要把可执行文件和配置头文件分别安装到bin
和include
目录当中。
首先,修改 MathFunctions/CMakeLists.txt
文件:
set(installable_libs MathFunctions tutorial_compiler_flags)
if(TARGET SqrtLibrary)
list(APPEND installable_libs SqrtLibrary)
endif()
install(TARGETS ${installable_libs} DESTINATION lib)
install(FILES MathFunctions.h DESTINATION include)
上面指令的文字翻译:
• 首先,定义了一个要安装的目标库列表 installable_libs
,包含 MathFunctions
和 tutorial_compiler_flags
。
• 然后,判断是否存在名为 SqrtLibrary
的目标,如果存在,将它追加到 installable_libs
中。
• 最后,将 installable_libs
列表中的所有目标安装到 lib
目录下。
在顶层目录的 CMake 文件,我们需要做出如下修改:
install(TARGETS Tutorial DESTINATION bin)
install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"
DESTINATION include
)
构建和安装
接下来,我们使用带有 --install
选项的 cmake 指令安装。
cmake --install .
:会把头文件、库和可执行文件安装到合适的目录当中,通常是系统目录:
使用cmake
指令安装程序时,需要以sudo
权限。否则,会报出复制文件权限不足的错误。
或者,我们在使用cmake
命令时,指定安装的目录为不需要sudo
权限就可以写的目录。
使用命令:
cmake --install . --prefix "/home/coder/installdir/"
执行结果如下:
cmake --install . --config Release
:对于多配置工具,需要用参数--config
指明配置。
练习2:测试支持(Testing Support)
题解步骤
CTest 为项目提供了测试支持。可以通过 add_test()
指令添加测试。本节练习我们使用 CTest 编写简单的测试用例,需要完成 TODO5
到 TODO9
。
首先,我们需要启用测试。然后,使用 add_test()
指令添加几条简单的测试。下面是详细的步骤说明。
在顶层的 CMakeLists.txt
文件当中,需要我们启用测试:
enable_testing()
然后,添加几个基本的测试用以验证应用程序能够正确地运行。首先,添加一条测试,这条测试以参数 25
运行可执行文件 Tutorial
。我们并不打算用这条测试验证程序的计算结果。该测试只是用来验证程序可以正常运行,例如不会发生段错误或者其他的崩溃。
add_test(NAME Runs COMMAND Tutorial 25)
接着,使用 PASS_REGULAR_EXPRESSION
测试属性来验证测试的输出包括了必要的字符串。在本例中,当提供了不正确的参数时,应该打印用法信息。
add_test(NAME Usage COMMAND Tutorial)
set_tests_properties(Usage
PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number"
)
插播介绍一下上面两条指令的含义。
-
add_test(NAME StandardUse COMMAND Tutorial 4)
•
add_test()
是 CMake 中用于添加测试的命令。
•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
:这是要设置属性的测试的名称(在这里就是前面定义的 StandardUse 测试)。
•PROPERTIES PASS_REGULAR_EXPRESSION "4 is 2"
:这是为测试指定属性,PASS_REGULAR_EXPRESSION
表示通过测试的正则表达式。这意味着该测试的输出中必须包含字符串 “4 is 2”,否则测试会失败。
下一步需要验证的是程序的计算值等于它的真正的平方根。
add_test(NAME StandardUse COMMAND Tutorial 4)
set_tests_properties(StandardUse
PROPERTIES PASS_REGULAR_EXPRESSION "4 is 2"
)
当然仅仅编写这几个测试是远远不够的。为了容易地添加更多的测试,写了一个叫做 do_test()
的函数,用来运行应用程序并验证给定输入平方根的值是否正确。do_test()
函数是在 cmake 文件当中定义的,函数的参数是名称、输入和期望的结果。
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")
构建和运行
进入到 Step5_build
目录并重新构建程序。接着,运行 ctest
可执行文件:ctest -N
和 ctest -VV
。对于多配置的生成器(例如:Visual Studio),配置类型必须用 -C <mode>
指定。例如,以 Debug 模式运行所有的测试,在 build
目录需要使用命令 ctest -C Debug -VV
。发行模式需要在 build
目录使用 -C Release
。
ctest -N
可以用来查看都有哪些测试:
ctest -VV
用来运行所有测试,与此同时,输出每次测试的详细信息:
执行 ctest
会得到所有测出的执行结果:
如果只想执行某个测试用例,可以使用命令 ctest -R 测试用例名
,例如 ctest -R Comp9
:
最后,放上本文修改后的 CMake 文件。
顶层的 CMake 文件:
cmake_minimum_required(VERSION 3.15)
# set the project name and version
project(Tutorial VERSION 1.0)
# specify the C++ standard
add_library(tutorial_compiler_flags INTERFACE)
target_compile_features(tutorial_compiler_flags INTERFACE cxx_std_11)
# add compiler warning flags just when building this project via
# the BUILD_INTERFACE genex
set(gcc_like_cxx "$<COMPILE_LANG_AND_ID:CXX,ARMClang,AppleClang,Clang,GNU,LCC>")
set(msvc_cxx "$<COMPILE_LANG_AND_ID:CXX,MSVC>")
target_compile_options(tutorial_compiler_flags INTERFACE
"$<${gcc_like_cxx}:$<BUILD_INTERFACE:-Wall;-Wextra;-Wshadow;-Wformat=2;-Wunused>>"
"$<${msvc_cxx}:$<BUILD_INTERFACE:-W3>>"
)
# configure a header file to pass some of the CMake settings
# to the source code
configure_file(TutorialConfig.h.in TutorialConfig.h)
# add the MathFunctions library
add_subdirectory(MathFunctions)
# add the executable
add_executable(Tutorial tutorial.cxx)
target_link_libraries(Tutorial PUBLIC MathFunctions tutorial_compiler_flags)
# add the binary tree to the search path for include files
# so that we will find TutorialConfig.h
target_include_directories(Tutorial PUBLIC
"${PROJECT_BINARY_DIR}"
)
# TODO 3: Install Tutorial in the bin directory
# Hint: Use the TARGETS and DESTINATION parameters
# Target后跟要安装的目标,DESTINATION后跟安装到的目录
install(TARGETS Tutorial DESTINATION bin)
# TODO 4: Install TutorialConfig.h to the include directory
# Hint: Use the FILES and DESTINATION parameters
install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"
DESTINATION include)
# TODO 5: Enable testing
enable_testing()
# TODO 6: Add a test called Runs which runs the following command:
# $ Tutorial 25
add_test(NAME Runs COMMAND Tutorial 25)
# TODO 7: Add a test called Usage which runs the following command:
# $ Tutorial
# Make sure the expected output is displayed.
# Hint: Use the PASS_REGULAR_EXPRESSION property with "Usage.*number"
add_test(NAME Usage COMMAND Tutorial)
set_tests_properties(Usage
PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number"
)
# TODO 8: Add a test which runs the following command:
# $ Tutorial 4
# Make sure the result is correct.
# Hint: Use the PASS_REGULAR_EXPRESSION property with "4 is 2"
add_test(NAME StandardUse COMMAND Tutorial 4)
set_tests_properties(StandardUse
PROPERTIES PASS_REGULAR_EXPRESSION "4 is 2"
)
# TODO 9: Add more tests. Create a function called do_test to avoid copy +
# paste. Test the following values: 4, 9, 5, 7, 25, -25 and 0.0001.
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")
MathFunctions
目录下 CMake 文件:
add_library(MathFunctions MathFunctions.cxx)
# state that anybody linking to us needs to include the current source dir
# to find MathFunctions.h, while we don't.
target_include_directories(MathFunctions
INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
)
# should we use our own math functions
option(USE_MYMATH "Use tutorial provided math implementation" ON)
if (USE_MYMATH)
target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH")
# library that just does sqrt
add_library(SqrtLibrary STATIC
mysqrt.cxx
)
# link SqrtLibrary to tutorial_compiler_flags
target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags)
target_link_libraries(MathFunctions PRIVATE SqrtLibrary)
endif()
# link MathFunctions to tutorial_compiler_flags
target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags)
# TODO 1: Create a variable called installable_libs that is a list of all
# libraries we want to install (e.g. MathFunctions and tutorial_compiler_flags)
# Then install the installable libraries to the lib folder.
# Hint: Use the TARGETS and DESTINATION parameters
set(installable_libs MathFunctions tutorial_compiler_flags)
if(TARGET SqrtLibrary)
list(APPEND installable_libs SqrtLibrary)
endif()
install(TARGETS ${installable_libs} DESTINATION lib)
# TODO 2: Install the library headers to the include folder.
# Hint: Use the FILES and DESTINATION parameters
install(FILES MathFunctions.h DESTINATION include)
各位道友,码字不易,如有收获,记得一键三连啊。
看完觉得有帮助的道友,可以关注我的公众号,里面会分享一些高质量的个人成长类读书笔记、生活随笔以及职场经验等等。