cmake add_custom_command failure, target gets dele

2019-01-28 17:37发布

I am building a test executable using CMake. During the build process, I would like to run the executable, which returns whether the tests pass or not. If not, I would like the build to fail. However, when I use add_custom_command(... POST_BUILD ... ), and use a Makefile generator, the test executable will be deleted (explain in this question: Why does GNU make delete a file).

Is there a way to have CMake treat the executable as a .PRECIOUS, or otherwise change the CMakeLists.txt such that the executable doesn't get deleted if the tests fail?

For reference, my CMakeList.txt looks like the following (simplified from actual):

add_executable(UnitTest unittest.cpp)
add_custom_command(TARGET UnitTest POST_BUILD COMMAND $<TARGET_FILE:UnitTest>)

2条回答
欢心
2楼-- · 2019-01-28 18:20

I struggled with this same problem: I have a unit test I only want to run in the following conditions:

  1. The test has been modified.
  2. The code under test has been modified.
  3. The test failed its previous execution.

In the event the test is run and fails I want the test binary to remain for debugging.

The solution I ended up with uses a flag file to indicate the test has run and no longer needs to execute again. Note the flag must live in the binary directory so that other builds are not impacted. However the WORKING_DIRECTORY is set so the test can access read-only data files relative to its source location. Here's what it all looks like -- I stuck this in a macro so all my various unit tests can invoke this, the only input to the macro is the test executable:

set (TEST_FLAG_FILE ${CMAKE_CURRENT_BINARY_DIR}/${TEST_EXECUTABLE}.PASSED)

# This isn't normally needed since a rebuild will have a newer time stamp.
# But it adds robustness to handle changes to the system clock.
add_custom_command(TARGET ${TEST_EXECUTABLE} 
    COMMENT "Unit test ${TEST_EXECUTABLE} rebuilt; clearing flag ${TEST_FLAG_FILE}." 
    COMMAND ${CMAKE_COMMAND} -E remove -f ${TEST_FLAG_FILE}
    POST_BUILD 
    WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})

# This command only runs if the flag isn't present or is older than the executable.
add_custom_command(OUTPUT ${TEST_FLAG_FILE}
    COMMENT "Unit Test Execution: ${TEST_EXECUTABLE}" 
    COMMAND ${TEST_EXECUTABLE}
    COMMAND ${CMAKE_COMMAND} -E touch ${TEST_FLAG_FILE}
    MAIN_DEPENDENCY ${TEST_EXECUTABLE} 
    WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})

# Any "make all" will evaluate the preceding custom command.
add_custom_target(run_${TEST_EXECUTABLE} ALL DEPENDS ${TEST_FLAG_FILE})
查看更多
做自己的国王
3楼-- · 2019-01-28 18:27

The solution that I was alluding to was to use add_custom_target instead of add_custom_command. While it will not delete the executable if the test fails and the build process as a whole fails if runUnitTest fails, this target does not get built as a result of building the UnitTest target specifically.

add_executable(UnitTest unittest.cpp)
add_custom_target(runUnitTest UnitTest COMMAND $<TARGET_FILE:UnitTest> DEPENDS UnitTest)
查看更多
登录 后发表回答