【发布时间】:2010-10-18 12:32:10
【问题描述】:
我正在 CMake 中尝试 CTest,以便使用 make test 目标自动运行我的一些测试。问题是 CMake 不“理解”我愿意运行的测试必须构建,因为它是项目的一部分。
所以我正在寻找一种方法来明确指定这种依赖关系。
【问题讨论】:
标签: dependencies cmake ctest
我正在 CMake 中尝试 CTest,以便使用 make test 目标自动运行我的一些测试。问题是 CMake 不“理解”我愿意运行的测试必须构建,因为它是项目的一部分。
所以我正在寻找一种方法来明确指定这种依赖关系。
【问题讨论】:
标签: dependencies cmake ctest
可以说bug in CMake(以前跟踪过here)这不是开箱即用的。一种解决方法是执行以下操作:
add_test(TestName ExeName)
add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND}
DEPENDS ExeName)
然后你可以运行make check,它会编译并运行测试。如果您有多个测试,则必须在上述行中使用DEPENDS exe1 exe2 exe3 ...。
【讨论】:
check 目标并且它们可能会发生冲突
其实有一种方法可以使用make test。您需要将测试可执行文件的构建定义为测试之一,然后在测试之间添加依赖关系。那就是:
ADD_TEST(ctest_build_test_code
"${CMAKE_COMMAND}" --build ${CMAKE_BINARY_DIR} --target test_code)
ADD_TEST(ctest_run_test_code test_code)
SET_TESTS_PROPERTIES(ctest_run_test_code
PROPERTIES DEPENDS ctest_build_test_code)
【讨论】:
我使用了richq 答案的变体。在顶级 CMakeLists.txt 中,我添加了一个自定义目标 build_and_test,用于构建和运行所有测试:
find_package(GTest)
if (GTEST_FOUND)
enable_testing()
add_custom_target(build_and_test ${CMAKE_CTEST_COMMAND} -V)
add_subdirectory(test)
endif()
在test/下的各个子项目CMakeLists.txt文件中,我将每个测试可执行文件添加为build_and_test的依赖:
include_directories(${CMAKE_SOURCE_DIR}/src/proj1)
include_directories(${GTEST_INCLUDE_DIRS})
add_executable(proj1_test proj1_test.cpp)
target_link_libraries(proj1_test ${GTEST_BOTH_LIBRARIES} pthread)
add_test(proj1_test proj1_test)
add_dependencies(build_and_test proj1_test)
使用这种方法,我只需要make build_and_test 而不是make test(或make all test),它的好处是只构建测试代码(及其依赖项)。很遗憾我不能使用目标名称test。在我的情况下,它并没有那么糟糕,因为我有一个顶级脚本,它通过调用cmake 然后调用make 进行树外调试和发布(和交叉编译)构建,它转换为test进入build_and_test。
显然,不需要 GTest 的东西。我只是碰巧使用/喜欢 Google Test,并想分享一个使用 CMake/CTest 的完整示例。恕我直言,这种方法还有一个好处是允许我使用ctest -V,它会在测试运行时显示 Google 测试输出:
1: Running main() from gtest_main.cc
1: [==========] Running 1 test from 1 test case.
1: [----------] Global test environment set-up.
1: [----------] 1 test from proj1
1: [ RUN ] proj1.dummy
1: [ OK ] proj1.dummy (0 ms)
1: [----------] 1 test from proj1 (1 ms total)
1:
1: [----------] Global test environment tear-down
1: [==========] 1 test from 1 test case ran. (1 ms total)
1: [ PASSED ] 1 test.
1/2 Test #1: proj1_test ....................... Passed 0.03 sec
【讨论】:
如果你使用 CMake >= 3.7,那么推荐的方法是使用fixtures:
add_executable(test test.cpp)
add_test(test_build
"${CMAKE_COMMAND}"
--build "${CMAKE_BINARY_DIR}"
--config "$<CONFIG>"
--target test
)
set_tests_properties(test_build PROPERTIES FIXTURES_SETUP test_fixture)
add_test(test test)
set_tests_properties(test PROPERTIES FIXTURES_REQUIRED test_fixture)
执行以下操作:
test.cpp 构建的test 可执行目标
test_build“测试”,运行Cmake来构建目标test
test_build 测试标记为夹具test_fixture 的设置任务
test 可执行文件的 test 测试test 测试标记为需要夹具test_fixture。因此,每次运行 test test 时,它首先运行 test test_build,它会构建必要的可执行文件。
【讨论】:
$<CONFIG> 未设置,--target 将成为 --config 的参数。
$<CONFIG> 总是非空的。它是配置名称的生成器表达式:cmake.org/cmake/help/latest/manual/… 我将编辑答案以将其括在引号中,因为它没有区别。
cmake?我这样做:mkdir build; cd build; cmake ..; make。看起来没有任何默认值并且所有相关变量都是空的,直到手动设置CMAKE_BUILD_TYPE。 (目前在Debian 10,没有检查其他平台)
make 进程吗?这种情况可能会发生,因为测试可以由ctest并发执行
如果您尝试模仿 make check,您可能会发现此 wiki 条目很有用:
http://www.cmake.org/Wiki/CMakeEmulateMakeCheck
我刚刚检查了它是否成功(CMake 2.8.10)。
【讨论】:
make check 时构建所有可执行文件。对于编译时间占主导地位的测试,这使得 ctest -R 无用。
免去你的头疼:
make all test
对我来说开箱即用,并且会在运行测试之前构建依赖项。鉴于这很简单,它几乎使本机 make test 功能很方便,因为它使您可以选择运行最后的编译测试,即使您的代码已损坏。
【讨论】:
make -j4 all && make test。而且使用非 Make 构建工具也很不稳定。
这是我敲定并一直在使用的:
set(${PROJECT_NAME}_TESTS a b c)
enable_testing()
add_custom_target(all_tests)
foreach(test ${${PROJECT_NAME}_TESTS})
add_executable(${test} EXCLUDE_FROM_ALL ${test}.cc)
add_test(NAME ${test} COMMAND $<TARGET_FILE:${test}>)
add_dependencies(all_tests ${test})
endforeach(test)
build_command(CTEST_CUSTOM_PRE_TEST TARGET all_tests)
string(CONFIGURE \"@CTEST_CUSTOM_PRE_TEST@\" CTEST_CUSTOM_PRE_TEST_QUOTED ESCAPE_QUOTES)
file(WRITE "${CMAKE_BINARY_DIR}/CTestCustom.cmake" "set(CTEST_CUSTOM_PRE_TEST ${CTEST_CUSTOM_PRE_TEST_QUOTED})" "\n")
YMMV
【讨论】:
对于 CMake 3.10 或更高版本,另一种选择是使用 TEST_INCLUDE_FILES 目录属性来设置一个脚本,以在运行测试之前触发构建。在最外层的CMakeLists.txt 添加以下代码:
set_property(DIRECTORY APPEND
PROPERTY TEST_INCLUDE_FILES "${CMAKE_CURRENT_BINARY_DIR}/BuildTestTarget.cmake")
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/BuildTestTarget.cmake"
"execute_process(COMMAND \"${CMAKE_COMMAND}\""
" --build \"${CMAKE_BINARY_DIR}\""
" --config \"\$ENV{CMAKE_CONFIG_TYPE}\")")
实际的测试配置通过环境变量CMAKE_CONFIG_TYPE 传递给构建。或者,您可以添加 --target 选项以仅构建测试所需的目标。
【讨论】:
Derrick 的回答,简化并评论:
# It is impossible to make target "test" depend on "all":
# https://gitlab.kitware.com/cmake/cmake/-/issues/8774
# Set a magic variable in a magic file that tells ctest
# to invoke the generator once before running the tests:
file(WRITE "${CMAKE_BINARY_DIR}/CTestCustom.cmake"
"set(CTEST_CUSTOM_PRE_TEST ${CMAKE_MAKE_PROGRAM})\n"
)
这并不完全正确,因为它不能解决运行ninja all test 的并发问题,以防万一有人这样做。相反,因为现在,你有两个忍者进程。
(Ftr,我也分享了这个解决方案here。)
【讨论】:
以上所有答案都是完美的。但实际上 CMake 使用 CTest 作为其测试工具,因此执行任务的标准方法(我认为是)是:
enable_testing ()
add_test (TestName TestCommand)
add_test (TestName2 AnotherTestCommand)
然后运行 cmake 和 make 来构建目标。之后,您可以运行 make test,或者直接运行
ctest
你会得到结果。这是在 CMake 2.8 下测试的。
查看详情:http://cmake.org/Wiki/CMake/Testing_With_CTest#Simple_Testing
【讨论】:
enable_testing()、add_test() 等。问题是他必须手动发出在运行测试之前构建命令。他希望make test 目标能够根据需要自动构建测试可执行文件。
所有答案都很好,但它们暗示了通过命令make test 运行测试的传统违反。我已经完成了这个技巧:
add_test(NAME <mytest>
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
COMMAND sh -c "make <mytarget>; $<TARGET_FILE:<mytarget>>")
这意味着测试包括构建(可选)和运行可执行目标。
【讨论】:
make,并且失去了 CMake 为其他构建工具生成脚本的功能。