Cmake: add_custom_command argument based on variable content
Asked Answered
I

2

8

I'd like to have a Cmake function to copy some binaries to a specific location. Fo this I have the following function definition :

function ( collect_binaries TARGET_NAME DEST_DIR )
   set ( targetsToCopy ${ARGN} )

   set ( copy_cmd "COMMAND ${CMAKE_COMMAND} -E make_directory ${DEST_DIR}\n" )

   foreach ( target ${targetsToCopy} )
      LIST( APPEND copy_cmd "COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:${target}> ${DEST_DIR}$<TARGET_FILE_NAME:${target}>\n")
   endforeach( target ${targetsToCopy} )

   #message( FATAL_ERROR ${copy_cmd} )
   add_custom_target( ${TARGET_NAME} )
   add_custom_command( TARGET ${TARGET_NAME} PRE_BUILD ${copy_cmd} )

endfunction( collect_binaries )

And the following usage :

collect_binaries( bin_copy ${PROJECT_BINARY_DIR}/out/ target_1 target_2 target3 )

I have target_1, target_2 and target_3 defined inside my project tree. With this in mind I got the following Cmake configure output :

CMake Warning (dev) at binary_copy.cmake:15 (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.

It seems that target in unknown in this context...but it does exist and there is no typo. What is the issue here?

Ideally answered 13/11, 2015 at 15:40 Comment(1)
I tried doing the same as you with custom command, possible but ugly. What you really want to do is INSTALL binaries into a location, less code and nicerCaroylncarp
R
4

You are setting up the copy_cmd variable in the collect_binaries function as a CMake string. The add_custom_command however requires a CMake list to parse the arguments correctly, i.e.:

function ( collect_binaries TARGET_NAME DEST_DIR )
   set ( targetsToCopy ${ARGN} )

   set ( copy_cmd COMMAND ${CMAKE_COMMAND} -E make_directory ${DEST_DIR} )

   foreach ( target ${targetsToCopy} )
      LIST( APPEND copy_cmd COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:${target}> "${DEST_DIR}$<TARGET_FILE_NAME:${target}>")
   endforeach( target ${targetsToCopy} )

   #message( FATAL_ERROR ${copy_cmd} )
   add_custom_target( ${TARGET_NAME} )
   add_custom_command( TARGET ${TARGET_NAME} PRE_BUILD ${copy_cmd} )

endfunction( collect_binaries )
Rheotaxis answered 13/11, 2015 at 17:9 Comment(1)
Thanks a lot for your solution and explanations - it works !Ideally
H
0

Command flow add_custom_command(TARGET ...) is for library and executable targets only. It is not intended for targets, created with add_cusom_target.

You need to make your custom target to be depended from binaries copied, and create commands for copy that binaries. (Note, that list of arguments for any cmake command shouldn't be enclosed into quotes).

function ( collect_binaries TARGET_NAME DEST_DIR )
    set ( targetsToCopy ${ARGN} )

    set (create_directory_cmd COMMAND ${CMAKE_COMMAND} -E make_directory ${DEST_DIR})

    # Create one custom command per every file to be copied.
    # Also collect list of output files.
    set(output_files)
    foreach ( target ${targetsToCopy} )
        set(output_file "${DEST_DIR}/$<TARGET_FILE_NAME:${target}>")
        add_custom_command(OUTPUT ${output_file}
            ${create_directory_cmd}
            COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:${target}> ${output_file})
        list(APPEND output_files ${output_file})
        # It is sufficient to create directory only once. Just clear corresponded variable.
        set(create_directory_cmd)
    endforeach( target ${targetsToCopy} )

    add_custom_target( ${TARGET_NAME} DEPENDS ${output_files})
endfunction( collect_binaries )

If you want to have only copied binaries, but original ones are not interested for you, it is simpler to tell CMake to create binaries where you need them. Either

set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/out/)

before creating targets or

set_target_properties(target_1 target_2 target3
    PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/out/)

after creating targets is sufficient.

Havre answered 13/11, 2015 at 17:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.