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>)
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)
I struggled with this same problem: I have a unit test I only want to run in the following conditions:
- The test has been modified.
- The code under test has been modified.
- 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})