该文档是基于CMake的官方教程翻译而来,并稍微添加了自己的理解:
cmake的官方网站为:CMake Tutorial
The CMake tutorial provides a step-by-step guide that covers common build system issues that CMake helps address. Seeing how various topics all work together in an example project can be very helpful.
CMake 教程提供了一个循序渐进的指南,涵盖了 CMake 可帮助解决的常见构建系统问题。在一个示例项目中了解各个主题是如何协同工作的,会非常有帮助。
The tutorial source code examples are available in this archive. Each step has its own subdirectory containing code that may be used as a starting point. The tutorial examples are progressive so that each step provides the complete solution for the previous step.
本文档中提供了教程源代码示例。每个步骤都有自己的子目录,其中包含可用作起点的代码。教程示例是循序渐进的,因此每一步都提供了前一步的完整解决方案。
Often, it is not enough to only build an executable, it should also be installable. With CMake, we can specify install rules using the install() command. Supporting local installations for your builds in CMake is often as simple as specifying an install location and the targets and files to be installed.
通常情况下,仅仅构建可执行文件是不够的,它还应该是可安装的。在 CMake 中,我们可以使用 install() 命令指定安装规则。在 CMake 中支持本地安装,通常只需指定安装位置、目标和要安装的文件。
Install the Tutorial executable and the MathFunctions library.
安装 Tutorial 可执行文件和 MathFunctions 库。
The starting code is provided in the Step5 directory. In this exercise, complete TODO 1 through TODO 4.
Step5 目录中提供了起始代码。在本练习中,完成 TODO 1 到 TODO 4。
First, update MathFunctions/CMakeLists.txt to install the MathFunctions and tutorial_compiler_flags libraries to the lib directory. In that same file, specify the install rules needed to install MathFunctions.h to the include directory.
首先,更新 MathFunctions/CMakeLists.txt
,将 MathFunctions
和 tutorial_compiler_flags
库安装到 lib
目录。在同一文件中,指定将 MathFunctions.h
安装到 include
目录所需的安装规则。
Then, update the top level CMakeLists.txt to install the Tutorial executable to the bin directory. Lastly, any header files should be installed to the include directory. Remember that TutorialConfig.h is in the PROJECT_BINARY_DIR.
然后,更新顶级 CMakeLists.txt
,将 Tutorial
可执行文件安装到 bin 目录。最后,任何头文件都应安装到 include
目录。请记住,TutorialConfig.h
位于 PROJECT_BINARY_DIR
中。
Make a new directory called Step5_build. Run the cmake executable or the cmake-gui to configure the project and then build it with your chosen build tool.
新建一个名为 Step5_build
的目录。运行 cmake 可执行文件或 cmake-gui 配置项目,然后使用所选的构建工具构建项目。
Then, run the install step by using the --install option of the cmake command (introduced in 3.15, older versions of CMake must use make install) from the command line. This step will install the appropriate header files, libraries, and executables. For example:
然后,在命令行中使用 cmake 命令的 --install 选项(在 3.15 中引入,旧版本的 CMake 必须使用 make install)运行安装步骤。这一步将安装相应的头文件、库和可执行文件。例如
cmake --install .
For multi-configuration tools, don’t forget to use the --config argument to specify the configuration.
对于多配置工具,不要忘记使用 --config
参数指定配置。
cmake --install . --config Release
If using an IDE, simply build the INSTALL target. You can build the same install target from the command line like the following:
如果使用集成开发环境,只需构建 INSTALL
目标即可。你也可以通过命令行构建相同的安装目标,如下所示:
cmake --build . --target install --config Debug
The CMake variable CMAKE_INSTALL_PREFIX is used to determine the root of where the files will be installed. If using the cmake --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"
Navigate to the install directory and verify that the installed Tutorial runs.
导航至安装目录并验证已安装的Tutorial是否运行。
The install rules for our project are fairly simple:
我们项目的安装规则相当简单:
For MathFunctions, we want to install the libraries and header file to the lib and include directories respectively.
对于 MathFunctions
,我们要将库和头文件分别安装到 lib 和 include 目录中。
For the Tutorial executable, we want to install the executable and configured header file to the bin and include directories respectively.
对于 Tutorial 可执行文件,我们希望将可执行文件和配置的头文件分别安装到 bin 和 include 目录中。
So to the end of MathFunctions/CMakeLists.txt we add:
因此,在 MathFunctions/CMakeLists.txt 的末尾,我们添加了
TODO 1: Click to show/hide answer
TODO 1: 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)
and
TODO 2: Click to show/hide answer
TODO 2: MathFunctions/CMakeLists.txt
install(FILES MathFunctions.h DESTINATION include)
The install rules for the Tutorial executable and configured header file are similar. To the end of the top-level CMakeLists.txt we add:
教程可执行文件和配置头文件的安装规则类似。在顶层 CMakeLists.txt
的末尾添加
TODO 3,4: Click to show/hide answer
CMakeLists.txt
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.
这就是在本地安装tutorial所需的一切。
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 "$" )
set(msvc_cxx "$" )
target_compile_options(tutorial_compiler_flags INTERFACE
"$<${gcc_like_cxx}:$>"
"$<${msvc_cxx}:$>"
)
# 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
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
)
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 our compiler flags interface library
target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags)
target_link_libraries(MathFunctions PRIVATE SqrtLibrary)
endif()
# link our compiler flags interface library
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)
$ cmake --install . --prefix "/home/test/sda3/work"
-- Install configuration: ""
-- Installing: /home/test/sda3/work/lib/libMathFunctions.a
-- Installing: /home/test/sda3/work/lib/libSqrtLibrary.a
-- Installing: /home/test/sda3/work/include/MathFunctions.h
-- Installing: /home/test/sda3/work/bin/Tutorial
-- Installing: /home/test/sda3/work/include/TutorialConfig.h
$
CTest offers a way to easily manage tests for your project. Tests can be added through the add_test() command. Although it is not explicitly covered in this tutorial, there is a lot of compatibility between CTest and other testing frameworks such as GoogleTest.
CTest 提供了一种轻松管理项目测试的方法。可以通过 add_test()
命令添加测试。虽然本教程没有明确涉及,但 CTest 和其他测试框架(如 GoogleTest)之间有很多兼容性。
Create unit tests for our executable using CTest.
使用 CTest 为我们的可执行文件创建单元测试。
The starting source code is provided in the Step5 directory. In this exercise, complete TODO 5 through TODO 9.
Step5 目录中提供了起始源代码。在本练习中,完成 TODO 5 到 TODO 9。
First, we need to enable testing. Next, begin adding tests to our project using add_test(). We will work through adding 3 simple tests and then you can add additional testing as you see fit.
首先,我们需要启用测试。接下来,开始使用 add_test()为我们的项目添加测试。我们将添加 3 个简单的测试,然后您可以根据需要添加其他测试。
Navigate to the build directory and rebuild the application. Then, run the ctest executable: ctest -N and ctest -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 use ctest -C Debug -VV from the build directory (not the Debug subdirectory!). Release mode would be executed from the same location but with a -C Release. Alternatively, build the RUN_TESTS target from the IDE.
导航至构建目录并重建应用程序。然后运行 ctest 可执行文件:ctest -N
和 ctest -VV
。对于多配置生成器(如 Visual Studio),必须使用 -C flag指定配置类型。例如,要在调试模式下运行测试,可从构建目录(而不是调试子目录!)使用 ctest -C Debug -VV
。要在 Release 模式下运行测试,则应在同一目录下使用 -C Release
。或者,从集成开发环境中构建 RUN_TESTS
目标。
Let’s test our application. At the end of the top-level CMakeLists.txt file we first need to enable testing with the enable_testing() command.
让我们测试应用程序。在顶层 CMakeLists.txt 文件的末尾,我们首先需要使用 enable_testing() 命令启用测试。
TODO 5: Click to show/hide answer
TODO 5: CMakeLists.txt
enable_testing()
With testing enabled, we will add a number of basic tests to verify that the application is working correctly. First, we create a test using add_test() which runs the Tutorial executable with the parameter 25 passed in. For this test, we are not going to check the executable’s computed answer. This test will verify that application runs, does not segfault or otherwise crash, and has a zero return value. This is the basic form of a CTest test.
启用测试后,我们将添加一些基本测试,以验证应用程序是否正常运行。首先,我们使用 add_test()
创建一个测试,在传入参数 25 后运行 Tutorial
可执行文件。在这个测试中,我们不会检查可执行文件的计算答案。该测试将验证应用程序是否运行,是否发生 segfault 或其他崩溃,以及返回值是否为零。这就是 CTest 测试的基本形式
TODO 6: Click to show/hide answer
TODO 6: CMakeLists.txt
add_test(NAME Runs COMMAND Tutorial 25)
Next, let’s use 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
测试属性来验证测试的输出是否包含特定字符串。在本例中,我们要验证当提供的参数数量不正确时,是否会打印使用信息。
TODO 7: Click to show/hide answer
TODO 7: CMakeLists.txt
add_test(NAME Usage COMMAND Tutorial)
set_tests_properties(Usage
PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number"
)
The next test we will add verifies the computed value is truly the square root.
下一个测试将验证计算值是否真的是平方根。
TODO 8: Click to show/hide answer
TODO 8: CMakeLists.txt
add_test(NAME StandardUse COMMAND Tutorial 4)
set_tests_properties(StandardUse
PROPERTIES PASS_REGULAR_EXPRESSION "4 is 2"
)
This one test is not enough to give us confidence that it will work for all values passed in. We should add more tests to verify this. To easily add more tests, we make a function called do_test that runs the application and verifies that the computed square root is correct for given input. For each invocation of do_test, another test is added to the project with a name, input, and expected results based on the passed arguments.
这一个测试还不足以让我们确信它对所有传入的值都有效。我们应该添加更多测试来验证这一点。为了方便地添加更多测试,我们制作了一个名为 do_test 的函数,用于运行应用程序并验证给定输入的平方根计算结果是否正确。每次调用 do_test,都会在项目中添加另一个测试,测试名称、输入内容和根据传入参数的预期结果。
TODO 9: Click to show/hide answer
TODO 9: CMakeLists.txt
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")
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 "$" )
set(msvc_cxx "$" )
target_compile_options(tutorial_compiler_flags INTERFACE
"$<${gcc_like_cxx}:$>"
"$<${msvc_cxx}:$>"
)
# 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
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")
$ ctest -C Debug -VV
UpdateCTestConfiguration from :/home/test/sda3/work/cmake/Step5_build/DartConfiguration.tcl
UpdateCTestConfiguration from :/home/test/sda3/work/cmake/Step5_build/DartConfiguration.tcl
Test project /home/test/sda3/work/cmake/Step5_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: /home/test/sda3/work/cmake/Step5_build/Tutorial "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/10 Test #1: Runs ............................. Passed 0.00 sec
test 2
Start 2: Usage
2: Test command: /home/test/sda3/work/cmake/Step5_build/Tutorial
2: Test timeout computed to be: 10000000
2: /home/test/sda3/work/cmake/Step5_build/Tutorial Version 1.0
2: Usage: /home/test/sda3/work/cmake/Step5_build/Tutorial number
2/10 Test #2: Usage ............................ Passed 0.00 sec
test 3
Start 3: StandardUse
3: Test command: /home/test/sda3/work/cmake/Step5_build/Tutorial "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/10 Test #3: StandardUse ...................... Passed 0.00 sec
test 4
Start 4: Comp4
4: Test command: /home/test/sda3/work/cmake/Step5_build/Tutorial "4"
4: Test timeout computed to be: 10000000
4: Computing sqrt of 4 to be 2.5
4: Computing sqrt of 4 to be 2.05
4: Computing sqrt of 4 to be 2.00061
4: Computing sqrt of 4 to be 2
4: Computing sqrt of 4 to be 2
4: Computing sqrt of 4 to be 2
4: Computing sqrt of 4 to be 2
4: Computing sqrt of 4 to be 2
4: Computing sqrt of 4 to be 2
4: Computing sqrt of 4 to be 2
4: The square root of 4 is 2
4/10 Test #4: Comp4 ............................ Passed 0.00 sec
test 5
Start 5: Comp9
5: Test command: /home/test/sda3/work/cmake/Step5_build/Tutorial "9"
5: Test timeout computed to be: 10000000
5: Computing sqrt of 9 to be 5
5: Computing sqrt of 9 to be 3.4
5: Computing sqrt of 9 to be 3.02353
5: Computing sqrt of 9 to be 3.00009
5: Computing sqrt of 9 to be 3
5: Computing sqrt of 9 to be 3
5: Computing sqrt of 9 to be 3
5: Computing sqrt of 9 to be 3
5: Computing sqrt of 9 to be 3
5: Computing sqrt of 9 to be 3
5: The square root of 9 is 3
5/10 Test #5: Comp9 ............................ Passed 0.00 sec
test 6
Start 6: Comp5
6: Test command: /home/test/sda3/work/cmake/Step5_build/Tutorial "5"
6: Test timeout computed to be: 10000000
6: Computing sqrt of 5 to be 3
6: Computing sqrt of 5 to be 2.33333
6: Computing sqrt of 5 to be 2.2381
6: Computing sqrt of 5 to be 2.23607
6: Computing sqrt of 5 to be 2.23607
6: Computing sqrt of 5 to be 2.23607
6: Computing sqrt of 5 to be 2.23607
6: Computing sqrt of 5 to be 2.23607
6: Computing sqrt of 5 to be 2.23607
6: Computing sqrt of 5 to be 2.23607
6: The square root of 5 is 2.23607
6/10 Test #6: Comp5 ............................ Passed 0.00 sec
test 7
Start 7: Comp7
7: Test command: /home/test/sda3/work/cmake/Step5_build/Tutorial "7"
7: Test timeout computed to be: 10000000
7: Computing sqrt of 7 to be 4
7: Computing sqrt of 7 to be 2.875
7: Computing sqrt of 7 to be 2.65489
7: Computing sqrt of 7 to be 2.64577
7: Computing sqrt of 7 to be 2.64575
7: Computing sqrt of 7 to be 2.64575
7: Computing sqrt of 7 to be 2.64575
7: Computing sqrt of 7 to be 2.64575
7: Computing sqrt of 7 to be 2.64575
7: Computing sqrt of 7 to be 2.64575
7: The square root of 7 is 2.64575
7/10 Test #7: Comp7 ............................ Passed 0.00 sec
test 8
Start 8: Comp25
8: Test command: /home/test/sda3/work/cmake/Step5_build/Tutorial "25"
8: Test timeout computed to be: 10000000
8: Computing sqrt of 25 to be 13
8: Computing sqrt of 25 to be 7.46154
8: Computing sqrt of 25 to be 5.40603
8: Computing sqrt of 25 to be 5.01525
8: Computing sqrt of 25 to be 5.00002
8: Computing sqrt of 25 to be 5
8: Computing sqrt of 25 to be 5
8: Computing sqrt of 25 to be 5
8: Computing sqrt of 25 to be 5
8: Computing sqrt of 25 to be 5
8: The square root of 25 is 5
8/10 Test #8: Comp25 ........................... Passed 0.00 sec
test 9
Start 9: Comp-25
9: Test command: /home/test/sda3/work/cmake/Step5_build/Tutorial "-25"
9: Test timeout computed to be: 10000000
9: The square root of -25 is 0
9/10 Test #9: Comp-25 .......................... Passed 0.00 sec
test 10
Start 10: Comp0.0001
10: Test command: /home/test/sda3/work/cmake/Step5_build/Tutorial "0.0001"
10: Test timeout computed to be: 10000000
10: Computing sqrt of 0.0001 to be 0.50005
10: Computing sqrt of 0.0001 to be 0.250125
10: Computing sqrt of 0.0001 to be 0.125262
10: Computing sqrt of 0.0001 to be 0.0630304
10: Computing sqrt of 0.0001 to be 0.0323084
10: Computing sqrt of 0.0001 to be 0.0177018
10: Computing sqrt of 0.0001 to be 0.0116755
10: Computing sqrt of 0.0001 to be 0.0101202
10: Computing sqrt of 0.0001 to be 0.0100007
10: Computing sqrt of 0.0001 to be 0.01
10: The square root of 0.0001 is 0.01
10/10 Test #10: Comp0.0001 ....................... Passed 0.00 sec
100% tests passed, 0 tests failed out of 10
Total Test time (real) = 0.01 sec
$