【问题标题】:CMake & CTest : make test doesn't build testsCMake & CTest : make test 不构建测试
【发布时间】:2010-10-18 12:32:10
【问题描述】:

我正在 CMake 中尝试 CTest,以便使用 make test 目标自动运行我的一些测试。问题是 CMake 不“理解”我愿意运行的测试必须构建,因为它是项目的一部分。

所以我正在寻找一种方法来明确指定这种依赖关系。

【问题讨论】:

    标签: dependencies cmake ctest


    【解决方案1】:

    可以说bug in CMake(以前跟踪过here)这不是开箱即用的。一种解决方法是执行以下操作:

    add_test(TestName ExeName)
    add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND}
                      DEPENDS ExeName)
    

    然后你可以运行make check,它会编译并运行测试。如果您有多个测试,则必须在上述行中使用DEPENDS exe1 exe2 exe3 ...

    【讨论】:

    • 所以我猜“make test”目标将保持未使用状态,因为您似乎必须在 add_custom_target 命令中选择不同的目标名称?
    • @rq - 但是我怎样才能对多个项目执行此操作(当一个 CMakeLists.txt 是另一个项目的子项目时)所以每个项目都会定义 check 目标并且它们可能会发生冲突
    • @Artyom - 在这种情况下,您最好只使用等效的“进行所有测试”。事实上,无论如何我都是这样做的。
    • 实际上,有些人认为它是 cmake 的一个功能(不是错误),您可以运行“make test”并按原样运行测试,而无需重新执行-首先构建...
    • 这在 IDE 构建中不起作用,因为需要将“-C Debug”或类似内容传递给 ctest。 CMAKE_CFG_INTDIR 不起作用,因为在配置时不知道测试命令。
    【解决方案2】:

    其实有一种方法可以使用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)
    

    【讨论】:

    • 这是唯一可以扩展的,并且不会强迫您构建“make all”目标只是为了运行测试。一个可能的缺点:二进制文件中构建错误的详细信息仅显示在生成的 LastTest.log 文件中,而不显示在 stdout/stderr
    • 好答案!不过,您应该将配置添加到构建目标。否则不可能在所有配置中运行测试。 add_test( NAME "${ARGV0}_BUILD" COMMAND "${CMAKE_COMMAND}" --build ${CMAKE_BINARY_DIR} --target ${target} "--config" "$" )
    • 这用一堆虚假的测试阻塞了测试报告者。
    • 如果你使用 CMake >= 3.7,推荐的方法是使用fixtures。请参阅下面的my answer
    【解决方案3】:

    我使用了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
    

    【讨论】:

    • 在这个例子中,有没有办法让 make test 做 ctest -V 做的而不是 ctest? ctest 输出看起来很不完整,只是说只有一个测试。
    【解决方案4】:

    如果你使用 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,它会构建必要的可执行文件。

    【讨论】:

    • 如果 $&lt;CONFIG&gt; 未设置,--target 将成为 --config 的参数。
    • 我相信$&lt;CONFIG&gt; 总是非空的。它是配置名称的生成器表达式:cmake.org/cmake/help/latest/manual/… 我将编辑答案以将其括在引号中,因为它没有区别。
    • 你如何运行cmake?我这样做:mkdir build; cd build; cmake ..; make。看起来没有任何默认值并且所有相关变量都是空的,直到手动设置CMAKE_BUILD_TYPE。 (目前在Debian 10,没有检查其他平台)
    • 我不完全确定:你能同时在同一个构建目录上运行多个make 进程吗?这种情况可能会发生,因为测试可以由ctest并发执行
    【解决方案5】:

    如果您尝试模仿 make check,您可能会发现此 wiki 条目很有用:

    http://www.cmake.org/Wiki/CMakeEmulateMakeCheck

    我刚刚检查了它是否成功(CMake 2.8.10)。

    【讨论】:

    • 这将在运行make check 时构建所有可执行文件。对于编译时间占主导地位的测试,这使得 ctest -R 无用。
    【解决方案6】:

    免去你的头疼:

    make all test
    

    对我来说开箱即用,并且会在运行测试之前构建依赖项。鉴于这很简单,它几乎使本机 make test 功能很方便,因为它使您可以选择运行最后的编译测试,即使您的代码已损坏。

    【讨论】:

    • 不适用于 CDash。您必须调用 make all && ctest 然后该建筑物不是上传测试的一部分。所以构建警告或错误是不可见的。
    • 如果你想要并行构建也不能很好地工作,因为两者将并行运行:你需要make -j4 all &amp;&amp; make test。而且使用非 Make 构建工具也很不稳定。
    【解决方案7】:

    这是我敲定并一直在使用的:

    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

    【讨论】:

      【解决方案8】:

      对于 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 选项以仅构建测试所需的目标。

      【讨论】:

        【解决方案9】:

        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。)

        【讨论】:

          【解决方案10】:

          以上所有答案都是完美的。但实际上 CMake 使用 CTest 作为其测试工具,因此执行任务的标准方法(我认为是)是:

          enable_testing ()
          add_test (TestName TestCommand)
          add_test (TestName2 AnotherTestCommand)
          

          然后运行 ​​cmakema​​ke 来构建目标。之后,您可以运行 ma​​ke test,或者直接运行

          ctest
          

          你会得到结果。这是在 CMake 2.8 下测试的。

          查看详情:http://cmake.org/Wiki/CMake/Testing_With_CTest#Simple_Testing

          【讨论】:

          • 投反对票,因为有时您只想构建实际运行的测试所需的目标。
          • 这个答案似乎误解了这个问题:OP 已经按照这个答案的建议做:使用 CTest、enable_testing()add_test() 等。问题是他必须手动发出在运行测试之前构建命令。他希望make test 目标能够根据需要自动构建测试可执行文件。
          【解决方案11】:

          所有答案都很好,但它们暗示了通过命令make test 运行测试的传统违反。我已经完成了这个技巧:

          add_test(NAME <mytest>
          WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
          COMMAND sh -c "make <mytarget>; $<TARGET_FILE:<mytarget>>")
          

          这意味着测试包括构建(可选)和运行可执行目标。

          【讨论】:

          • :-D 规则 #1:不要使用没有 sh 的系统。你知道这样的系统吗?
          • 是的,Windows 就是其中之一。
          • 这也被硬编码为make,并且失去了 CMake 为其他构建工具生成脚本的功能。
          猜你喜欢
          • 2015-07-06
          • 2013-04-16
          • 1970-01-01
          • 2011-08-02
          • 1970-01-01
          • 2011-12-07
          • 1970-01-01
          相关资源
          最近更新 更多