这是本系列的第五篇。
上一篇,我们学习了如何对添加的库进行接口处理,即添加库的使用需求。这一篇,我们继续学习CMake Tutorial的一块重要内容。
工程应用中,编译还涉及到安装和测试的环节。我们给到客户的实际文件,不可能是源码的形式,而是一个安装包。那么CMake如何在发布安装时对导出哪些文件进行设置呢?对于我们写好的程序,可能要进行一个简单的测试,那么CMake是否也有支持测试环节的相应功能呢?
Now we can start adding install rules and testing support to our project.
现在我们可以开始为项目添加安装规则和测试支持了。
The install rules are fairly simple: for
MathFunctions
we want to install the library and header file and for the application we want to install the executable and configured header.
关于安装规则,其实也非常简单:对于MathFunctions
我们希望安装库和头文件,对于整体应用,我们希望安装可执行文件和相应的头文件。
So to the end of
MathFunctions/CMakeLists.txt
we add:
所以我们在MathFunctions/CMakeLists.txt
的末尾加上:
install(TARGETS MathFunctions DESTINATION lib)
install(FILES MathFunctions.h DESTINATION include)
小白按:此时完整的MathFunctions/CMakeLists.txt
文件为:
add_library(MathFunctions mysqrt.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}
)
install (TARGETS MathFunctions DESTINATION lib)
install (FILES MathFunctions.h DESTINATION include)
And to the end of the top-level
CMakeLists.txt
we add:
在顶层的CMakeLists.txt
文件的末尾加上:
install(TARGETS Tutorial DESTINATION bin)
install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"
DESTINATION include
)
小白按:此时完整的顶层CMakeLists.txt
文件为:
cmake_minimum_required(VERSION 3.10)
# set the project name and version
project(Tutorial VERSION 1.0)
# specify the C++ standard
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
# should we use our own math functions
option(USE_MYMATH "Use tutorial provided math implementation" ON)
# 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
if(USE_MYMATH)
add_subdirectory(MathFunctions)
list(APPEND EXTRA_LIBS MathFunctions)
endif()
# add the executable
add_executable(Tutorial tutorial.cxx)
target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS})
# 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}"
)
install(TARGETS Tutorial DESTINATION bin)
install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"
DESTINATION include
)
That is all that is needed to create a basic local install of the tutorial.
这些就完全足够创建一个本地安装包了。
Now run the
cmake
executable or thecmake-gui
to configure the project and then build it with your chosen build tool.
现在运行 cmake
可执行文件,或者cmake-gui
来指定项目,然后使用你选定的编译工具来进行编译。
Then run the install step by using the
install
option of thecmake
command (introduced in 3.15, older versions of CMake must usemake install
) from the command line. For multi-configuration tools, don’t forget to use the--config
argument to specify the configuration. If using an IDE, simply build theINSTALL
target. This step will install the appropriate header files, libraries, and executables. For example:
然后运行安装步骤,使用 cmake
命令行中的install
选项(从3.15版本中引入 ,老版本CMake中必须使用make install
)。对于多参数的工具,不要忘记使用--config
来指定参数。如果使用了IDE,只需要编译INSTALL
目标。这一步将会安装适用的头文件、库文件以及可执行文件。例如:
cmake --install .
小白按:划重点划重点,这里Tutorial可能是有一些没讲清楚的地方,也可能是小白用来学习的这台电脑很奇怪。总是是有一个重要问题要讲一讲。小白按照前面两步的基本操作做了一下操作,即
cmake ../Step4
cmake --build .
到这里都跟之前一样,完全没有问题,但是
cmake --install .
出了问题。
C:\Users\Admin\Desktop\cmake-3.24.0-rc3-tutorial-source\Step4_build>cmake --install .
-- Install configuration: "Release"
CMake Error at cmake_install.cmake:39 (file):
file INSTALL cannot find
"C:/Users/Admin/Desktop/cmake-3.24.0-rc3-tutorial-source/Step4_build/Release/Tutorial.exe":
File exists.
解释一下这个问题大概是怎么回事:在未指定参数的情况下,默认是编译debug版本,所以我们得到了一个Debug版本的Tutorial.exe文件,然而这里未指定参数的情况下,生成安装包时,却默认是Release版本,这样就导致因为没有生成Release版的Tutorial.exe而无法继续。天坑啊~
不过小白最终解决了这个问题,因为cmake命令总是可以指定的才对,于是指定方式就在Tutorial讲到但没讲清楚的--config
上了,需要这样:
cmake --install . --config debug
强行指定为生成Debug版的安装包即可。
换言之,如果
cmake --build . --config release
则此处也可以得到Release版本的安装包。
关于这个问题的示例,可以参考User Interaction Guide
小白也想去探究一下,这两者之间的默认设置为什么不设置成一样。(手动笑哭,Tutorial还埋雷实在太不厚道)
The CMake variable
CMAKE_INSTALL_PREFIX
is used to determine the root of where the files will be installed. If using thecmake --install
command, the installation prefix can be overridden via the--prefix
argument. For example:
CMake变量 CMAKE_INSTALL_PREFIX
是用来指定将安装根文件的路径位置。如果使用cmake --install
命令,安装位置的前缀可以通过--prefix
参数来覆盖。例如:
cmake --install . --prefix "/home/myuser/installdir"
小白按:小白此处的写法是:
cmake --install . --prefix "./Tutorial" --config debug
生成得到Debug版本的安装包,位置在/Step4_build/Tutorial
下,共包含三个文件夹,分别是bin
、include
、lib
。
Navigate to the install directory and verify that the installed Tutorial runs.
定位到安装路径然后测试一下安装好的软件。
小白按:小白此处的测试为:
cd ./Tutorial/bin
Tutorial 10
结果为
Computing sqrt of 10 to be 5.5
Computing sqrt of 10 to be 3.65909
Computing sqrt of 10 to be 3.19601
Computing sqrt of 10 to be 3.16246
Computing sqrt of 10 to be 3.16228
Computing sqrt of 10 to be 3.16228
Computing sqrt of 10 to be 3.16228
Computing sqrt of 10 to be 3.16228
Computing sqrt of 10 to be 3.16228
Computing sqrt of 10 to be 3.16228
The square root of 10 is 3.16228
测试成功!
Next let’s test our application. At the end of the top-level
CMakeLists.txt
file we can enable testing and then add a number of basic tests to verify that the application is working correctly.
接下来我们开始测试应用。在最顶层的CMakeLists.txt
文件末尾,我们可以启用测试功能,添加一些基础的测试来验证一下我们的应用是否正常工作。
CMakeLists.txt
enable_testing()
# does the application run
add_test(NAME Runs COMMAND Tutorial 25)
# does the usage message work?
add_test(NAME Usage COMMAND Tutorial)
set_tests_properties(Usage
PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number"
)
# define a function to simplify adding tests
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")
小白按:修改后CMakeLists.txt
文件完整版如下所示:
cmake_minimum_required(VERSION 3.10)
# set the project name and version
project(Tutorial VERSION 1.0)
# specify the C++ standard
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
# should we use our own math functions
option(USE_MYMATH "Use tutorial provided math implementation" ON)
# 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
if(USE_MYMATH)
add_subdirectory(MathFunctions)
list(APPEND EXTRA_LIBS MathFunctions)
endif()
# add the executable
add_executable(Tutorial tutorial.cxx)
target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS})
# 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}"
)
install(TARGETS Tutorial DESTINATION bin)
install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"
DESTINATION include
)
enable_testing()
# does the application run
add_test(NAME Runs COMMAND Tutorial 25)
# does the usage message work?
add_test(NAME Usage COMMAND Tutorial)
set_tests_properties(Usage
PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number"
)
# define a function to simplify adding tests
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")
The first test simply verifies that the application runs, does not segfault or otherwise crash, and has a zero return value. This is the basic form of a CTest test.
第一个测试简单地验证一下应用的运行,是否会出现段错误,或者其他崩溃问题,或者返回值为0。这是一个简单的CTest测试形式。
The next test makes use of the
PASS_REGULAR_EXPRESSION
test property to verify that the output of the test contains certain strings. In this case, verifying that the usage message is printed when an incorrect number of arguments are provided.
接下来另一个测试使用的 PASS_REGULAR_EXPRESSION
测试属性来验证测试的输出包含特定的字符串。在本例中,验证在提供的参数数量不正确时是否打印了用法消息。
Lastly, we have a function called
do_test
that runs the application and verifies that the computed square root is correct for given input. For each invocation ofdo_test
, another test is added to the project with a name, input, and expected results based on the passed arguments.
最后,我们有一个名为do_test
的函数,它运行应用并验证计算的平方根对于给定输入是否正确。对于do_test
的每次调用,将向项目中添加另一个测试,其中包含基于传递的参数的名称、输入和预期结果。
Rebuild the application and then cd to the binary directory and run the
ctest
executable:ctest -N
andctest -VV
. For multi-config generators (e.g. Visual Studio), the configuration type must be specified with the-C
flag. For example, to run tests in Debug mode usectest -C Debug -VV
from the binary directory (not the Debug subdirectory!). Release mode would be executed from the same location but with a-C Release
. Alternatively, build theRUN_TESTS
target from the IDE.
重新生成应用,然后将其cd到bin目录,并运行ctest
的可执行文件:ctest -N
和ctest -VV
。对于多配置生成器(例如Visual Studio),配置类型必须使用-C
标志指定。例如,要在调试模式下运行测试,请使用二进制目录(而不是调试子目录!)中的ctest -C Debug -VV
。Release模式将从同一位置执行,但使用-C Release
。或者,从IDE构建RUN_TESTS
项目。
小白按:这里又有一个神坑。这一篇仿佛步步是坑。大家有没有觉得上面这段,不管是中文还是英文都说得云里雾里?小白也是这样,这个所谓的“binary directory”是个什么东西?
幸运的是,小白没有一条胡同走到黑,先从IDE构建RUN_TEST
项目,发现编译通过运行成功了。于是,看了它的属性,终于找到了所谓的“binary directory”的蛛丝马迹。
在项目属性->配置属性->生成事件->生成后事件中,看到命令行:
setlocal
"C:\Program Files\CMake\bin\ctest.exe" --force-new-ctest-process -C $(Configuration)
if %errorlevel% neq 0 goto :cmEnd
:cmEnd
endlocal & call :cmErrorLevel %errorlevel% & goto :cmDone
:cmErrorLevel
exit /b %1
:cmDone
if %errorlevel% neq 0 goto :VCEnd
原来这里的ctest是指的cmake安装位置下bin目录里的ctest.exe,小白安装的位置恰好是C:\Program Files\CMake\bin\
。(无力吐槽,作为一个Tutorial,这个事情讲得这么模棱两可,真的是坑害菜鸟选手)
小白给出完整的编译命令:
mkdir Step4_build
cd Step4_build
cmake ../Step4
cmake --build .
"C:\Program Files\CMake\bin\ctest" -C Debug -VV
测试相关的输出结果是
UpdateCTestConfiguration from :C:/Users/Admin/Desktop/cmake-3.24.0-rc3-tutorial-source/Step4_build/DartConfiguration.tcl
UpdateCTestConfiguration from :C:/Users/Admin/Desktop/cmake-3.24.0-rc3-tutorial-source/Step4_build/DartConfiguration.tcl
Test project C:/Users/Admin/Desktop/cmake-3.24.0-rc3-tutorial-source/Step4_build
Constructing a list of tests
Done constructing a list of tests
Updating test list for fixtures
Added 0 tests to meet fixture requirements
Checking test dependency graph...
Checking test dependency graph end
test 1
Start 1: Runs
1: Test command: C:\Users\Admin\Desktop\cmake-3.24.0-rc3-tutorial-source\Step4_build\Debug\Tutorial.exe "25"
1: Test timeout computed to be: 10000000
1: Computing sqrt of 25 to be 13
1: Computing sqrt of 25 to be 7.46154
1: Computing sqrt of 25 to be 5.40603
1: Computing sqrt of 25 to be 5.01525
1: Computing sqrt of 25 to be 5.00002
1: Computing sqrt of 25 to be 5
1: Computing sqrt of 25 to be 5
1: Computing sqrt of 25 to be 5
1: Computing sqrt of 25 to be 5
1: Computing sqrt of 25 to be 5
1: The square root of 25 is 5
1/9 Test #1: Runs ............................. Passed 0.02 sec
test 2
Start 2: Usage
2: Test command: C:\Users\Admin\Desktop\cmake-3.24.0-rc3-tutorial-source\Step4_build\Debug\Tutorial.exe
2: Test timeout computed to be: 10000000
2: C:/Users/Admin/Desktop/cmake-3.24.0-rc3-tutorial-source/Step4_build/Debug/Tutorial.exe Version 1.0
2: Usage: C:/Users/Admin/Desktop/cmake-3.24.0-rc3-tutorial-source/Step4_build/Debug/Tutorial.exe number
2/9 Test #2: Usage ............................ Passed 0.01 sec
test 3
Start 3: Comp4
3: Test command: C:\Users\Admin\Desktop\cmake-3.24.0-rc3-tutorial-source\Step4_build\Debug\Tutorial.exe "4"
3: Test timeout computed to be: 10000000
3: Computing sqrt of 4 to be 2.5
3: Computing sqrt of 4 to be 2.05
3: Computing sqrt of 4 to be 2.00061
3: Computing sqrt of 4 to be 2
3: Computing sqrt of 4 to be 2
3: Computing sqrt of 4 to be 2
3: Computing sqrt of 4 to be 2
3: Computing sqrt of 4 to be 2
3: Computing sqrt of 4 to be 2
3: Computing sqrt of 4 to be 2
3: The square root of 4 is 2
3/9 Test #3: Comp4 ............................ Passed 0.01 sec
test 4
Start 4: Comp9
4: Test command: C:\Users\Admin\Desktop\cmake-3.24.0-rc3-tutorial-source\Step4_build\Debug\Tutorial.exe "9"
4: Test timeout computed to be: 10000000
4: Computing sqrt of 9 to be 5
4: Computing sqrt of 9 to be 3.4
4: Computing sqrt of 9 to be 3.02353
4: Computing sqrt of 9 to be 3.00009
4: Computing sqrt of 9 to be 3
4: Computing sqrt of 9 to be 3
4: Computing sqrt of 9 to be 3
4: Computing sqrt of 9 to be 3
4: Computing sqrt of 9 to be 3
4: Computing sqrt of 9 to be 3
4: The square root of 9 is 3
4/9 Test #4: Comp9 ............................ Passed 0.01 sec
test 5
Start 5: Comp5
5: Test command: C:\Users\Admin\Desktop\cmake-3.24.0-rc3-tutorial-source\Step4_build\Debug\Tutorial.exe "5"
5: Test timeout computed to be: 10000000
5: Computing sqrt of 5 to be 3
5: Computing sqrt of 5 to be 2.33333
5: Computing sqrt of 5 to be 2.2381
5: Computing sqrt of 5 to be 2.23607
5: Computing sqrt of 5 to be 2.23607
5: Computing sqrt of 5 to be 2.23607
5: Computing sqrt of 5 to be 2.23607
5: Computing sqrt of 5 to be 2.23607
5: Computing sqrt of 5 to be 2.23607
5: Computing sqrt of 5 to be 2.23607
5: The square root of 5 is 2.23607
5/9 Test #5: Comp5 ............................ Passed 0.01 sec
test 6
Start 6: Comp7
6: Test command: C:\Users\Admin\Desktop\cmake-3.24.0-rc3-tutorial-source\Step4_build\Debug\Tutorial.exe "7"
6: Test timeout computed to be: 10000000
6: Computing sqrt of 7 to be 4
6: Computing sqrt of 7 to be 2.875
6: Computing sqrt of 7 to be 2.65489
6: Computing sqrt of 7 to be 2.64577
6: Computing sqrt of 7 to be 2.64575
6: Computing sqrt of 7 to be 2.64575
6: Computing sqrt of 7 to be 2.64575
6: Computing sqrt of 7 to be 2.64575
6: Computing sqrt of 7 to be 2.64575
6: Computing sqrt of 7 to be 2.64575
6: The square root of 7 is 2.64575
6/9 Test #6: Comp7 ............................ Passed 0.01 sec
test 7
Start 7: Comp25
7: Test command: C:\Users\Admin\Desktop\cmake-3.24.0-rc3-tutorial-source\Step4_build\Debug\Tutorial.exe "25"
7: Test timeout computed to be: 10000000
7: Computing sqrt of 25 to be 13
7: Computing sqrt of 25 to be 7.46154
7: Computing sqrt of 25 to be 5.40603
7: Computing sqrt of 25 to be 5.01525
7: Computing sqrt of 25 to be 5.00002
7: Computing sqrt of 25 to be 5
7: Computing sqrt of 25 to be 5
7: Computing sqrt of 25 to be 5
7: Computing sqrt of 25 to be 5
7: Computing sqrt of 25 to be 5
7: The square root of 25 is 5
7/9 Test #7: Comp25 ........................... Passed 0.01 sec
test 8
Start 8: Comp-25
8: Test command: C:\Users\Admin\Desktop\cmake-3.24.0-rc3-tutorial-source\Step4_build\Debug\Tutorial.exe "-25"
8: Test timeout computed to be: 10000000
8: The square root of -25 is 0
8/9 Test #8: Comp-25 .......................... Passed 0.01 sec
test 9
Start 9: Comp0.0001
9: Test command: C:\Users\Admin\Desktop\cmake-3.24.0-rc3-tutorial-source\Step4_build\Debug\Tutorial.exe "0.0001"
9: Test timeout computed to be: 10000000
9: Computing sqrt of 0.0001 to be 0.50005
9: Computing sqrt of 0.0001 to be 0.250125
9: Computing sqrt of 0.0001 to be 0.125262
9: Computing sqrt of 0.0001 to be 0.0630304
9: Computing sqrt of 0.0001 to be 0.0323084
9: Computing sqrt of 0.0001 to be 0.0177018
9: Computing sqrt of 0.0001 to be 0.0116755
9: Computing sqrt of 0.0001 to be 0.0101202
9: Computing sqrt of 0.0001 to be 0.0100007
9: Computing sqrt of 0.0001 to be 0.01
9: The square root of 0.0001 is 0.01
9/9 Test #9: Comp0.0001 ....................... Passed 0.01 sec
100% tests passed, 0 tests failed out of 9
Total Test time (real) = 0.16 sec
因为小白用的编译器是Visual Studio,如果是其他编译器请参考原文要求来给定-N或-VV参数。
即
"C:\Program Files\CMake\bin\ctest" -N
"C:\Program Files\CMake\bin\ctest" -VV
至此,小白终于踩过了这篇Tutorial的两大神坑。天了噜~有点精疲力尽。。。
下一篇我们继续学习CMake Tutorial,“添加系统自察”,又会不会遇到什么神坑呢?
【水平所限,错漏难免,创作不易,轻喷勿骂】