Antonio
Antonio

Reputation: 20266

Cycling on part of the arguments of a macro in cmake

Inspired by this, I was trying to create a macro that would copy at build time a list of files.

macro   (copy_files_at_build_time targetname) #usage: copy_files_at_build_time(<targetname> <file> [<files>] <destination>)
    if(ARGC LESS 3)
        message(ERROR "copy_files_at_build_time called with less than 3 arguments")
    endif(ARGC LESS 3)
    MATH(EXPR LAST_INDEX "${ARGC}-2")
    MATH(EXPR DEST_INDEX "${ARGC}-1")
    set(MY_DESTINATION ${ARGV${DEST_INDEX}})
    foreach(I RANGE 1 ${LAST_INDEX})
        message (STATUS "${CMAKE_COMMAND} -E copy ${ARGV${I}} ${MY_DESTINATION}")
        add_custom_command(TARGET ${targetname} PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${ARGV${I}} ${MY_DESTINATION})
    endforeach(I)
endmacro(copy_files_at_build_time)

As you can see there is a debug message, and unfortunately both ${ARGV${I}} and ${MY_DESTINATION} (which is ${ARGV${DEST_INDEX}}) are empty when I print them. So, it seems that is not possible to access to ARGV1, ARGV2 etc. (see macro documentation) by using a variable as last part of the variable name. I will now implement a workaround using the list commands, but my question is:

Is there an easier way to cycle on the ARGV0, ARGV1, ARGV2 elements, if some of the elements (e.g. the first and the last) have to be skipped?

Edit

Sadly enough, I am seeing that not even the LIST commands work on ARGV! But they work if first I create a copy of ARGV and then perform the LIST commands on it.

Upvotes: 0

Views: 1543

Answers (2)

Antonio
Antonio

Reputation: 20266

This is the implementation I ended up with, I am open to better solutions!
Note: the second function (the one that copies also directories) is probably safer in case some of the destination directories did not exist.

macro   (copy_files_at_build_time targetname) #usage: copy_files_at_build_time(<targetname> <file> [<files>] <destination>)
    if(ARGC LESS 3)
        message(ERROR "copy_files_at_build_time called with less than 3 arguments")
    endif(ARGC LESS 3)
    SET(ARGN_COPY ${ARGN})
    MATH(EXPR DEST_INDEX "${ARGC}-2") #It should be "${ARGC}-1", but we are using ARGN that doesn't contain the first element of the list
    list(GET ARGN_COPY ${DEST_INDEX} MY_DESTINATION)
    list(REMOVE_AT ARGN_COPY ${DEST_INDEX})
    foreach(MY_FILE ${ARGN_COPY})
#       message (STATUS "${CMAKE_COMMAND} -E copy_if_different ${MY_FILE} ${MY_DESTINATION}")
        add_custom_command(TARGET ${targetname} PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${MY_FILE} ${MY_DESTINATION})
    endforeach(MY_FILE)
endmacro(copy_files_at_build_time)

If we want to copy also directories (beware: not tested if it works recursively!)

macro   (copy_at_build_time targetname) #usage: copy_at_build_time(<targetname> <file|directory> [<files|directories>] <destination>)
    if(ARGC LESS 3)
        message(ERROR "copy_files_at_build_time called with less than 3 arguments")
    endif(ARGC LESS 3)
    SET(ARGN_COPY ${ARGN})
    MATH(EXPR DEST_INDEX "${ARGC}-2") #It should be "${ARGC}-1", but we are using ARGN that doesn't contain the first element of the list
    list(GET ARGN_COPY ${DEST_INDEX} MY_DESTINATION)
    list(REMOVE_AT ARGN_COPY ${DEST_INDEX})
    foreach(MY_FILE ${ARGN_COPY})
        get_filename_component(LEAF_NAME "${MY_FILE}" NAME)
        if (IS_DIRECTORY ${MY_FILE})
            add_custom_command(TARGET ${targetname} PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${MY_FILE} ${MY_DESTINATION}/${LEAF_NAME})
        else(IS_DIRECTORY ${MY_FILE})
            add_custom_command(TARGET ${targetname} PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${MY_FILE} ${MY_DESTINATION}/${LEAF_NAME})
        endif(IS_DIRECTORY ${MY_FILE})
    endforeach(MY_FILE)
endmacro(copy_at_build_time)

Upvotes: 0

Gabriel Petrovay
Gabriel Petrovay

Reputation: 21864

${ARGV${DEST_INDEX}}) is not supported in MACROS (at least form my experience). You must perform LIST(...) operations to handle the MACRO arguments using the ${ARGV} list of arguments.

Use either:

  • LIST(REMOVE_AT, ...) or similar list operations to remove arguments or
  • iterate using FOREACH(loop_var IN ...)

Upvotes: 2

Related Questions