Alternative to CMake POST_BUILD command when target is in subdirectory

cmake

Solution

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.

Problem

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

Original source