GpG
GpG

Reputation: 512

CMake - Copy file only if it doesnt exist

I would like to copy files at postbuild with cmake to the build directory, but only if they dont exist yet.

I know that there is a "copy_if_different" switch for "add_custom_command", but I cant find something like "copy_if_doesnt_exist".

Currently I am stuck with this:

file(GLOB CONFIGURATION_DATA ${PROJECT_SOURCE_DIR}/data/configurations/*)

foreach(data ${CONFIGURATION_DATA})
    #only copy if file doesnt exists
    if(NOT EXISTS ?? how to get same path as - $<TARGET_FILE_DIR:${PROJECT_NAME_STR}>/data/configurations) )
        add_custom_command(TARGET ${PROJECT_NAME_STR} POST_BUILD 
        COMMAND ${CMAKE_COMMAND} -E copy 
        ${data}
        $<TARGET_FILE_DIR:${PROJECT_NAME_STR}>/data/configurations)
    endif()
endforeach()

where I don't know how to get the destination file path

$<TARGET_FILE_DIR:${PROJECT_NAME_STR}>/data/configurations

in order to put it to if statement

Upvotes: 3

Views: 6267

Answers (1)

Florian
Florian

Reputation: 42952

For the not directly supported cases the file(GENERATE ...) command is very useful (since it does evaluate generator expressions).

So in your case I've successfully tested the following code example:

cmake_minimum_required(VERSION 2.8.12)

if (POLICY CMP0070)
    cmake_policy(SET CMP0070 NEW)
endif()

project(CopyFileIfNotExists)

file(
    GENERATE 
        OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/CopyConfigurationData$<CONFIG>.cmake"
        CONTENT 
            "if (NOT EXISTS \"$<TARGET_FILE_DIR:${PROJECT_NAME}>/data/configurations\")
                execute_process(
                     COMMAND \"${CMAKE_COMMAND}\" -E copy_directory
                         \"${PROJECT_SOURCE_DIR}/data/configurations\"
                         \"$<TARGET_FILE_DIR:${PROJECT_NAME}>/data/configurations\"
                )
            endif()"
)

file(WRITE "main.cpp" "int main() { return 0; }")

add_executable(${PROJECT_NAME} "main.cpp")

add_custom_command(
    TARGET ${PROJECT_NAME} 
    POST_BUILD 
    COMMAND "${CMAKE_COMMAND}" -P "CopyConfigurationData$<CONFIG>.cmake"
    WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
)

Alternative

Or if it's ok to run with every CMake (re-)configuration just add the following instead of a post-build step:

file(
    GLOB 
        CONFIGURATION_DATA 
        RELATIVE "${PROJECT_SOURCE_DIR}"
        "${PROJECT_SOURCE_DIR}/data/configurations/*"
 )

foreach(data IN LISTS CONFIGURATION_DATA)
    file(
        GENERATE 
            OUTPUT "$<TARGET_FILE_DIR:${PROJECT_NAME}>/${data}"
            INPUT  "${PROJECT_SOURCE_DIR}/${data}"
    )
endforeach()

Alternative: Optimized Version

Utilizing file(COPY ...) the shortest script based version I can think of that also optimizes already existing items out

"Copying preserves input file timestamps, and optimizes out a file if it exists at the destination with the same timestamp."

looks like this:

file(
    GENERATE 
        OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/CopyConfigurationData$<CONFIG>.cmake"
        CONTENT "file(
                    COPY \"${PROJECT_SOURCE_DIR}/data/configurations\"
                    DESTINATION \"$<TARGET_FILE_DIR:${PROJECT_NAME}>/data\"
                 )"
)

Upvotes: 3

Related Questions