It's often necessary to ensure that CMake build projects end up in a certain place after compilation, and the add_custom_command(..POST_BUILD...)
command is a common design pattern to accomplish that:
add_custom_command(
TARGET mytarget
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:mytarget> ${CMAKE_BINARY_DIR}/final_destination
)
Sadly it does not work when the target in question is in a subdirectory relative to the file containing the add_custom_command
call, which was compiled recursively via the add_subdirectory()
command. Attempting to do so leads to the following error message:
CMake Warning (dev) at CMakeLists.txt:4 (add_custom_command):
Policy CMP0040 is not set: The target in the TARGET signature of
add_custom_command() must exist. Run "cmake --help-policy CMP0040" for
policy details. Use the cmake_policy command to set the policy and
suppress this warning.
TARGET 'mytarget' was not created in this directory.
This warning is for project developers. Use -Wno-dev to suppress it.
In many cases, there is a straightforward workaround: simply ensure that the add_custom_command()
call occurs in the subdirectory's CMakeLists.txt
file, and everything will work.
However, this is not always possible! The subdirectory could be the CMake project of an external dependency over which we have no control. For instance, it's fairly common to combine CMake recursive compilations with Git submodules, in which case there is no way to permanently store modifications of the child projects's build system.
My question then boils down to the following: Does CMake offer another mechanism to create a target that will be automatically triggered when a child project's target is rebuilt, and which can be used to copy the final executable or shared library to some other location?
My goal is that this happens automatically without needing to specifically call 'make'/'ninja' with another target. Also, the copy should only be executed when it is actually necessary (according to the cmake docs, some of the add_custom_* commands don't track whether they actually need to be run and conservatively assume that the target is always stale).
Just use common combination of add_custom_command
and add_custom_target
, when the first one produces file for the second one:
# Because OUTPUT option may not use generator expressions,
# extract name of file from target's properties.
get_target_property(mytarget_basename mytarget OUTPUT_NAME)
get_target_property(mytarget_suffix mytarget SUFFIX)
set(mytarget_filename ${mytarget_basename}${mytarget_suffix})
# make copied file be dependent from one which is build.
# Note, that DEPENDS here creates dependencies both from the target
# and from the file it creates.
add_custom_command(OUTPUT
${CMAKE_BINARY_DIR}/final_destination/${mytarget_filename}
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:mytarget>
${CMAKE_BINARY_DIR}/final_destination
DEPENDS mytarget
)
# Create target which consume the command via DEPENDS.
add_custom_target(copy_files ALL
DEPENDS ${CMAKE_BINARY_DIR}/final_destination/${mytarget_filename}
)
Comparing with POST_BUILD using, this code uses additional target. But you have no other choice: add_custom_command
cannot be attached to the target created in other directory.
Usually, instead of copiing executable/library into other binary directory it is simpler to specify this directory via CMAKE_<TYPE>_OUTPUT_DIRECTORY
variable.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With