set cmake variable from custom command before every build - cmake

How can I run a custom command before every build and pass the result to a cmake variable?
I know I can do that on the terminal for example
cmake -DMY_VARIABLE=$(echo FOOBAR)
But I'd like to integrate that in my CMakeLists.txt using
add_custom_command(TARGET ${MY_APP} PRE_BUILD ...).
Then I want to pass that variable as a compile definition.
add_cmpile_definitions(MY_DEFINITION="${MY_VARIABLE}")
I found something similar in the command execute_process which has an argument OUTPUT_VARIABLE that stores the output of the command into that variable. But I think it doesn't run before every build.

Related

What cmake command will copy a directory of files to a directory in a post build step

I have a set of resource files that have nothing to do with the build steps of GCC or some other compiler's output. I need them copied from a folder in my project to the cmake build output folder. The goal is for the executable, when run from the build output folder, can see the resources.
How do people typically copy and install resources in cmake builds? Additionally, I want them copied regardless of changes in the build and I want it executed every time I run some cmake command, like build. See below for what I tried to solve this issue.
For example:
I have a bunch of shader files that I want copied. All shaders/* files should be copied into a directory in the build output called "shaders", because that's where the executable for the program lives.
file(GLOB out shaders/*)
foreach (o ${out})
message("${o} was copied to shaders")
file(COPY ${o} DESTINATION shaders)
endforeach ()
This only works sometimes, like when I reload the CMake project, e.g.:
/opt/clion-2021.2.3/bin/cmake/linux/bin/cmake \
-DCMAKE_BUILD_TYPE=Debug -DCMAKE_DEPENDS_USE_COMPILER=FALSE \
-G "CodeBlocks - Unix Makefiles" \
/home/hack/glad
Also, it doesn't execute "POST_BUILD", so the lastest version of the shaders/a.vert file doesn't get copied to the shaders/ directory in the output.
I tried using this, too, but it gave me some headaches:
add_custom_command(TARGET my-executable POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy shaders/* shaders)
I think there's something incorrect with that above, because it wasn't run every POST_BUILD if the build's code didn't change. I don't care if the build's code doesn't change because the files in shaders/* could have changed and should be copied regardless of cmake determining if there was a change in my-executable.
This gist on github was very helpful, here but the gist that applies to my question is included below.
add_custom_target(bar
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/shaders
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/shaders ${CMAKE_BINARY_DIR}/shaders
COMMENT "copying ${CMAKE_SOURCE_DIR}/shaders to ${CMAKE_BINARY_DIR}/shaders"
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)
The above code creates a bar cmake target that can be run separately with Make bar; then, if you add another target that requires those resources (the shaders above are resources for this other executable) then you can tie the dependency together using add_dependencies like this:
add_executable(a
main.c
opengl.c)
add_dependencies(a bar)
Now, every time the a target is run, the bar target is run as well, which has the effect of creating the directory and copying the files.
This was good enough for me, but to finish up, you can use this to create the files in a post build step after the other dependency is finished running:
add_custom_command(TARGET bar
# Run after all other rules within the target have been executed
POST_BUILD
COMMAND echo "executing a POST_BUILD command"
COMMENT "This command will be executed after building bar"
VERBATIM
)
Note that ${CMAKE_COMMAND} in the above examples of add_custom_command is a variable that points to the cmake executable, and you can run cmake -E to see the very helpful list of commands that come with cmake.
YIKES the post build step is only running after bar's target is built, not a's target. Hopefully, somebody can help me answer this better. I would still like to know how to copy files after a target is built, unless that's completely unnecessary and nobody should ever want to do that.

How to use CMake cached variables inside subprocess called by custom target?

My project contains a custom target which generates some output via .cmake script. It looks like this:
add_custom_target(TargetName
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_SOURCE_DIR}/cmake/script.cmake
BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/generated/output
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
VERBATIM
)
But now I want to set come cache variables inside the script. I tried doing like that:
message("MY_CACHE_VARIABLE = ${MY_CACHE_VARIABLE}")
set(MY_CACHE_VARIABLE "VALUE" CACHE INTERNAL "")
And I faced with the problem that cache variables are not saved. It always prints me empty output:
MY_CACHE_VARIABLE =
I already tried setting working directory as CMAKE_BINARY_DIR, or passing CMAKE_BINARY_DIR of the last argument of cmake command, or passing -B ${CMAKE_BINARY_DIR} or -C ${CMAKE_BINARY_DIR}/CMakeCache.txt as arguments and etc. None of these worked.
So is there any way to reuse existing cache inside CMake subprocess or I just should write my own cache inside the script?
You have to distinguish between running CMake to generate build files (for Make, Ninja, etc.) and running CMake in script mode:
Script mode simply runs the commands in the given CMake Language source file and does not generate a build system. It does not allow CMake commands that define build targets or actions.
-- cmake-language(7)
No configure or generate step is performed and the cache is not modified.
-- cmake(1)
So in script mode (-P), CMake is not aware of the cache or any variable/target/etc. defined in your regular CMakeLists.txt files. It is more similar to executing a bash/shell script than to processing a "usual" CMakeLists.txt.
But don't worry, there is still a solution to your problem. You can simply pass your arguments as -D options to your script:
add_custom_target(TargetName
COMMAND ${CMAKE_COMMAND}
-DMY_VAR="..."
-DANOTHER_VAR="..."
-P ${CMAKE_SOURCE_DIR}/cmake/script.cmake
...
)
Note however:
If variables are defined using -D, this must be done before the -P argument.
-- cmake(1)

How do you make CMake print *all* commands not just build commands?

So you can use the VERBOSE option to get CMake to print all the compiler command lines to the console as it builds, but that doesn't seem to have any effect on other commands that aren't compiler commands, e.g. this execute_process command:
execute_process(
COMMAND ${_TEST_EXECUTOR} "${_TEST_EXECUTABLE}" --gtest_list_tests
WORKING_DIRECTORY "${_TEST_WORKING_DIR}"
TIMEOUT ${_TEST_DISCOVERY_TIMEOUT}
OUTPUT_VARIABLE output
RESULT_VARIABLE result
)
It's actually part of the google test module in CMake. I had an issue where I needed to track the exact command line that was being executed. I was able to but only by manually hacking the files. If I had just been able to spew all the commands CMake was executing it would have been much much quicker.
Is there some way to do that in CMake?
To get the commands in execute_process printed, you can set where you want them printed individually, in each execute_process command using COMMAND_ECHO:
execute_process(
COMMAND ${_TEST_EXECUTOR} "${_TEST_EXECUTABLE}" --gtest_list_tests
WORKING_DIRECTORY "${_TEST_WORKING_DIR}"
TIMEOUT ${_TEST_DISCOVERY_TIMEOUT}
OUTPUT_VARIABLE output
RESULT_VARIABLE result
COMMAND_ECHO STDOUT
)
or universally for all execute_process commands throughout your CMake project by setting this variable in your top-level CMake file:
set(CMAKE_EXECUTE_PROCESS_COMMAND_ECHO STDOUT)
These features are available in CMake versions 3.15 and above.
For what it's worth, you can also get the full printout of every command CMake runs (with expanded CMake variables) using the cmake command line option:
cmake --trace-expand ..
but this may be much more verbosity than you're looking for.

cmake compare output of two executables [duplicate]

I have a console application called "foo", which takes a reference text file as input (in.txt) and generates text at standard output (I want to keep this behaviour).
In make (not cmake), I use a test target, which calls foo and redirects the output to a file (out.txt) as follows. Then, I use diff to compare the file out.txt with the expected refernece (ref.txt)
test:
./foo -a test/in.txt > test/out.txt
diff test/out.txt test/ref.txt
This works fine using make. Now my question is; how can I use cmake to create a similar Makefile?
From within a subdrectory called build, I tried
project(foo)
...
add_test(NAME test1 COMMAND ./foo ../test/in.txt > ../test/out.txt)
enable_testing()
Using cmake version 3.5, I get a Makefile without errors, but when I call make test, the test itself fails. It seems the cmake command add_test supports command line arguments, but not the redirection. I tried quotes and escaping witout success. Since I could not pass this part, I didn't try to use diff. I just imagine that I could pack foo and diff in one line using & as you can do with bash. That would be the second step.
Turning my comment into an answer
As #Tsyvarev has stated, CTest commands are not run in a shell's context. But you could just add the shell needed yourself and use e.g. sh as the command to be called with add_test().
I've run some tests with your example code and the following did work successfully:
add_test(NAME test1 COMMAND sh -c "$<TARGET_FILE:foo> ../test/in.txt > ../test/out.txt")
This solution is not platform independent (it depends on sh to be available in the search paths).
So if you want to be more flexible you could do something like:
include(FindUnixCommands)
file(TO_NATIVE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/test/in.txt" _in)
file(TO_NATIVE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/test/out.txt" _out)
if (BASH)
add_test(
NAME test1
COMMAND ${BASH} -c "$<TARGET_FILE:foo> ${_in} > ${_out}"
)
else()
if (WIN32)
add_test(
NAME test1
COMMAND ${CMAKE_COMMAND} -E chdir $<TARGET_FILE_DIR:foo> $ENV{ComSpec} /c "$<TARGET_FILE_NAME:foo> ${_in} > ${_out}"
)
else()
message(FATAL_ERROR "Unknown shell command for ${CMAKE_HOST_SYSTEM_NAME}")
endif()
endif()
Additionally there is the possibility to execute a more platform independent diff with ${CMAKE_COMMAND} -E compare_files <file1> <file2>. So you could simplify your complete makefile based example in CMake with:
add_custom_command(
TARGET foo
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E echo "Running $<TARGET_FILE_NAME:foo> ..."
COMMAND foo in.txt > out.txt
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/test
)
add_test(
NAME test1
COMMAND ${CMAKE_COMMAND} -E compare_files in.txt out.txt
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/test
)
References
Integrate bash test scripts in cmake
CMake: piping commands to executable
cmake: make tests successfully passing part of the build process
They say you cannot:
There is no redirection of output using add_test arguments.
Unlike to commands in add_custom_command, which are executed as a part of makefile receipts (that is, in the context of some shell), tests are executed directly by CTest, without any shell involved. So, shell mechanisms don't work for tests.
You may create wrapper script, which calls program, given as parameter, and performs redirection, futher diff and so on. Then use this script (with appropriate arguments) as a COMMAND for add_test.

How to use redirection in cmake add_test

I have a console application called "foo", which takes a reference text file as input (in.txt) and generates text at standard output (I want to keep this behaviour).
In make (not cmake), I use a test target, which calls foo and redirects the output to a file (out.txt) as follows. Then, I use diff to compare the file out.txt with the expected refernece (ref.txt)
test:
./foo -a test/in.txt > test/out.txt
diff test/out.txt test/ref.txt
This works fine using make. Now my question is; how can I use cmake to create a similar Makefile?
From within a subdrectory called build, I tried
project(foo)
...
add_test(NAME test1 COMMAND ./foo ../test/in.txt > ../test/out.txt)
enable_testing()
Using cmake version 3.5, I get a Makefile without errors, but when I call make test, the test itself fails. It seems the cmake command add_test supports command line arguments, but not the redirection. I tried quotes and escaping witout success. Since I could not pass this part, I didn't try to use diff. I just imagine that I could pack foo and diff in one line using & as you can do with bash. That would be the second step.
Turning my comment into an answer
As #Tsyvarev has stated, CTest commands are not run in a shell's context. But you could just add the shell needed yourself and use e.g. sh as the command to be called with add_test().
I've run some tests with your example code and the following did work successfully:
add_test(NAME test1 COMMAND sh -c "$<TARGET_FILE:foo> ../test/in.txt > ../test/out.txt")
This solution is not platform independent (it depends on sh to be available in the search paths).
So if you want to be more flexible you could do something like:
include(FindUnixCommands)
file(TO_NATIVE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/test/in.txt" _in)
file(TO_NATIVE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/test/out.txt" _out)
if (BASH)
add_test(
NAME test1
COMMAND ${BASH} -c "$<TARGET_FILE:foo> ${_in} > ${_out}"
)
else()
if (WIN32)
add_test(
NAME test1
COMMAND ${CMAKE_COMMAND} -E chdir $<TARGET_FILE_DIR:foo> $ENV{ComSpec} /c "$<TARGET_FILE_NAME:foo> ${_in} > ${_out}"
)
else()
message(FATAL_ERROR "Unknown shell command for ${CMAKE_HOST_SYSTEM_NAME}")
endif()
endif()
Additionally there is the possibility to execute a more platform independent diff with ${CMAKE_COMMAND} -E compare_files <file1> <file2>. So you could simplify your complete makefile based example in CMake with:
add_custom_command(
TARGET foo
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E echo "Running $<TARGET_FILE_NAME:foo> ..."
COMMAND foo in.txt > out.txt
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/test
)
add_test(
NAME test1
COMMAND ${CMAKE_COMMAND} -E compare_files in.txt out.txt
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/test
)
References
Integrate bash test scripts in cmake
CMake: piping commands to executable
cmake: make tests successfully passing part of the build process
They say you cannot:
There is no redirection of output using add_test arguments.
Unlike to commands in add_custom_command, which are executed as a part of makefile receipts (that is, in the context of some shell), tests are executed directly by CTest, without any shell involved. So, shell mechanisms don't work for tests.
You may create wrapper script, which calls program, given as parameter, and performs redirection, futher diff and so on. Then use this script (with appropriate arguments) as a COMMAND for add_test.