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).