CTest: How to set WORKING_DIRECTORY globally? - cmake

Recently, I have been building a simple test-suite with CTest. In its most simple form it looked something like this:
cmake_minimum_required(VERSION 3.7)
project(testsuite)
enable_testing()
add_test(NAME test_0
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMAND script.sh arg-0)
add_test(NAME test_1
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMAND script.sh arg-1)
add_test(NAME test_2
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMAND script.sh arg-2)
However, as it was growing, I quickly became tired of having to repeat WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} in every test.
Question: Is there a way to set WORKING_DIRECTORY globally (or, yet better, locally within each test subdirectory)? Something like
cmake_minimum_required(VERSION 3.7)
project(testsuite)
enable_testing()
set(DEFAULT_WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
add_test(NAME test_0
COMMAND script.sh arg-0)
add_test(NAME test_1
COMMAND script.sh arg-1)
add_test(NAME test_2
COMMAND script.sh arg-2)
The closest to a solution so far is a recommendation of my dear colleague to define a test-generating function:
cmake_minimum_required(VERSION 3.7)
project(testsuite)
enable_testing()
function(add_my_test TEST_NAME TEST_COMMAND TEST_ARGUMENTS)
add_test(NAME ${TEST_NAME}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMAND ${TEST_COMMAND} ${TEST_ARGUMENTS})
endfunction()
add_my_test(test_0 script.sh arg-0)
add_my_test(test_1 script.sh arg-1)
add_my_test(test_2 script.sh arg-2)
This works fine but it feels like an unnecessary level of indirection.
In the end, I opted for using a fixture to copy the necessary parts into the build folder, test there and clean up again; this is probably a cleaner and more idiomatic solution. Nevertheless, out of curiosity I would still be interested in a solution to the original problem.

Is there a way to set WORKING_DIRECTORY globally
No. The default WORKING_DIRECTORY for add_test is specified in documentation.
This works fine
Do it. I would do:
macro(add_my_test)
add_test(
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
${ARGV}
)
endmacro()
add_my_test(
NAME test_1
COMMAND script.sh arg-1
)
You can also roll your own functionality 'Overwrite' cmake command using a macro and restore its default behavior , but I believe just a wrapper function is great and clean.

Related

CMake add_custom_command ('POST_BUILD') 'DEPENDS' option is ignored

I have a library and a test projects on CMake, and I'm using this directory structure with two (project) CMakeLists.txt:
/
|- CMakeLists.txt
|- include/libName
|- src/...
|
|- test/
|- CMakeLists.txt
|- src/...
The outer project list defines the library, like:
add_library(libName ${SRC} ${INCLUDE})
And adds 'test' as subdirectory:
add_subdirectory(test)
The test project list defines the executable and a test, like:
add_executable(NameTest ${SRC})
target_link_libraries(NameTest libName)
add_test(NAME NameTest COMMAND NameTest)
The problem
I'm trying to build and execute the test program when the library is built. If any test fails, I want the build of the library fail too.
This is what I have (inside the outer lists file):
add_custom_command(
TARGET libName
POST_BUILD
COMMAND CTEST_OUTPUT_ON_FAILURE=1 ctest
DEPENDS NameTest # <- This is driving me crazy!
)
This command ignores completely if the target 'NameTest' is built, if there is a file with that name, or if not. I can't notice any difference if the whole 'DEPENDS' option is removed.
I even modified like:
add_custom_command(
TARGET libName
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E echo "Bip! Bip! Bip!"
DEPENDS this_is_not_an_existent_file_nor_target
)
And the command is triggered anyway. I'm not very sure about if this is the option I need, so:
Why is this not working?
How can I achieve my real purpose?
Thank you.
Edit: ctest will execute every test (add_test), but the NameTest executable (yet listed) must be built before calling it! Now would be built after the library, but before the 'POST_BUILD' custom command. It fails, of course.
I want CMake realize NameTest is necessary for running that custom command.
Edit: I find useful the Angew's answer, so I accepted his answer and refined it a little bit:
add_custom_command(
TARGET libName
POST_BUILD
COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target NameTest --config $<CONFIG>
COMMAND ${CMAKE_CTEST_COMMAND} -C $<CONFIG> --output-on-failure
)
Thank you!
1. Why is this not working?
Because you're mixing options from two distinct signatures of add_custom_command. DEPENDS comes from the form which is used to generate a file. TARGET and POST_BUILD are from the form which adds pre/post build commands to existing targets.
See the documentation of add_custom_command for more details on the two uses.
2. How can I achieve my real purpose?
I believe the following should do what you want to:
add_custom_command(
TARGET libName
POST_BUILD
COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target NameTest --config $<CONFIG>
COMMAND CTEST_OUTPUT_ON_FAILURE=1 ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target test --config $<CONFIG>
)

add_custom_command after running a test (CMakeLists)

I'm using a CMakeLists.txt file to generate (among others) some tests. Therefore I have something like
ENABLE_TESTING()
ADD_TEST(NAME my_name COMMAND my_command)
I'd like to add somme commands after the test is run. I found ADD_CUSTOM_COMMAND which seems to do exactly what I need but unfortunately it doesn't work.
Here is what I have tried
ADD_CUSTOM_COMMAND(
TARGET my_name
POST_BUILD
COMMAND my_other_command
)
It seems that I'm not using the right TARGET.
Could you help me by telling me what I'm supposed to do?
Many thanks in advance,
My goal was to execute a diff command after the test execution finished. This is very similar to the problem you are facing. I used this cmake script technique. Leaving it as an answer in case it helps someone else.
In the original CMakeLists.txt file I added a test
add_test(NAME testCommand
COMMAND ${CMAKE_COMMAND}
-DCMD1=$<TARGET_FILE:target>
-DTEST_DATA_DIR=${CMAKE_SOURCE_DIR}
-P ${CMAKE_SOURCE_DIR}/runtests.cmake)
Create a file called runtests.cmake in the directory which has the CMakeLists.txt file. This new file will hold the commands to execute the test and run commands after
include(FindUnixCommands)
macro(EXEC_CHECK CMD)
execute_process(COMMAND ${CMD} ${TEST_DATA_DIR}/test_input.txt RESULT_VARIABLE CMD_RESULT)
if(CMD_RESULT)
message(FATAL_ERROR "Error running ${CMD}")
else()
if (BASH)
execute_process(COMMAND ${BASH} -c "diff -b ${TEST_DATA_DIR}/test_input.txt ${TEST_DATA_DIR}/test_output.txt" RESULT_VARIABLE RES)
if(RES)
message(FATAL_ERROR "Diff is not clean")
endif()
else(BASH)
message(FATAL_ERROR "BASH not found : no diff script run")
endif(BASH)
endif()
endmacro()
exec_check(${CMD1})
From the build directory I can now issue make test and it picks up the cmake script properly.

Add files in sub folders to target

I'd like to add output files in subdirectory to a build target.
I wrote in CMakeLists.txt like below:
file(GLOB srcfiles "src/*.txt")
add_custom_target(subtask ALL)
set(dest_dir ${PROJECT_SOURCE_DIR}/sub/)
foreach(srcfile ${srcfiles})
string(REGEX REPLACE "^.*/(.*).txt$" filename ${srcfile})
add_custom_command(OUTPUT ${dest_dir}/${filename}.txt
COMMAND ${CMAKE_COMMAND} ARGS -E make_directory ${dest_dir}
COMMAND ${PROJECT_SOURCE_DIR}/process.sh ARGS ${srcfile}
MAIN_DEPENDENCY ${PROJECT_SOURCE_DIR}/process.sh)
add_dependencies(subtask ${dest_dir}/${filename}.txt)
endforeach(srcfile)
And executed:
mkdir build && cmake .. && make
But sub/*.txt are not created after build.
How should I do to build all commands on build?
updated (2017/2/4)
I solved the issue: use add_custom_command for each target and then declare add_custom_target that depends on all targets of add_custom_command.
set(TARGET_FILES "")
file(GLOB SRC_FILES "src/*.txt")
foreach(SRC_FILE ${SRC_FILES})
string(REGEX REPLACE "^.*/(.*).txt$" "\\1-foo.txt" TARGET_FILE ${SRC_FILE})
add_custom_command(
OUTPUT ${PROJECT_SOURCE_DIR}/sub/${TARGET_FILE}
COMMAND ${CMAKE_COMMAND} ARGS -E make_directory ${PROJECT_SOURCE_DIR}/sub
COMMAND ${PROJECT_SOURCE_DIR}/process.sh ARGS ${SRC_FILE}
MAIN_DEPENDENCY ${PROJECT_SOURCE_DIR}/process.sh)
list(APPEND TARGET_FILES "${PROJECT_SOURCE_DIR}/sub/${TARGET_FILE}")
endforeach()
add_custom_target(foo_txt ALL DEPENDS ${TARGET_FILES})
It's a little unclear what you are trying to achieve, but just from looking at it I would say it should be:
set(dest_dir ${PROJECT_SOURCE_DIR}/sub)
add_custom_target(
subtask ALL
COMMAND ${CMAKE_COMMAND} -E make_directory ${dest_dir}
COMMAND ${PROJECT_SOURCE_DIR}/process.sh
)
Only if the output.txt is an input for something else, you need a custom command:
set(dest_dir ${PROJECT_SOURCE_DIR}/sub)
add_custom_command(
OUTPUT ${dest_dir}/output.txt
COMMAND ${CMAKE_COMMAND} -E make_directory ${dest_dir}
COMMAND ${PROJECT_SOURCE_DIR}/process.sh
MAIN_DEPENDENCY ${PROJECT_SOURCE_DIR}/process.sh
)
add_custom_target(
subtask ALL
DEPENDS ${dest_dir}/output.txt
)
Note that the default working directory for those commands is CMAKE_CURRENT_BINARY_DIR.
Edit: I think the problem in your code is the use of add_dependencies() for file level dependencies. But add_dependencies() can only be used to declare target dependencies.
Edit: With a foreach() you can either collect the dependencies or APPEND them with to a dummy output. The first looks something like this:
file(GLOB srcfiles "src/*.txt")
set(dest_dir "${PROJECT_SOURCE_DIR}/sub")
file(MAKE_DIRECTORY "${dest_dir}")
foreach(srcfile ${srcfiles})
get_filename_component(filename "${srcfile}" NAME_WE)
add_custom_command(
OUTPUT "${dest_dir}/${filename}.txt"
COMMAND ${PROJECT_SOURCE_DIR}/process.sh ${srcfile}
MAIN_DEPENDENCY "${srcfile}"
DEPENDS "${PROJECT_SOURCE_DIR}/process.sh"
WORKING_DIRECTORY "${dest_dir}"
)
list(APPEND subtask_deps "${dest_dir}/${filename}.txt")
endforeach(srcfile)
add_custom_target(
subtask ALL
DEPENDS ${subtask_deps}
)

CMake/CPack: add_custom_command TARGET package POST_BUILD

Having the same problem as here described, I want to execute a shell script as a POST_BUILD command of TARGET "package". Target platform is Debian/Ubuntu.
I add following to the end of my CMakeLists:
add_custom_command(
TARGET package
POST_BUILD
COMMAND bash ${PROJECT_BINARY_DIR}/fixup_deb.sh
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
COMMENT "Fix file-permissions of md5sum files in debian package"
)
But this does not work. When I call "make package" the script doesn't change.
At the moment I have a workaround with a custom target:
add_custom_target(
correctDeb
COMMAND bash ${PROJECT_BINARY_DIR}/fixup_deb.sh
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
)
But it would be much more comfortable and more fail-safe for me if this would be automagically done when calling "make package".
By the way, generation of ${PROJECT_BINARY_DIR}/fixup_deb.sh works also well with:
configure_file( "${CMAKE_CURRENT_LIST_DIR}/debian/fixup_deb.sh.in" "${PROJECT_BINARY_DIR}/fixup_deb.sh" #ONLY IMMEDIATE )

I can not get environment at custom target shell

I can not get environment at custom target shell.
CMakeList.txt
set( ENV{TEST_VAR} "Hello" )
add_custom_target( test
COMMAND ./test.sh
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} )
test.sh
echo test:${TEST_VAR}
when try to "make test", shell can't get ${TEST_VAR}.
Thank you.
You have to use a trick because environment variables SET in the CMakeLists.txt only take effect for cmake itself, so you cannot use this method to set an environment variable that a custom command might need:
test.cmake
set( ENV{TEST_VAR} "Hello" )
execute_process(
COMMAND ./test.sh
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} )
CMakeLists.txt
add_custom_target( test
COMMAND ${CMAKE_COMMAND} -P test.cmake )