Copying files with destination folder depending on build config

I have some files that I want to copy to the binary folder (let’s say, data files required for the program to run). I typically build on Windows with Visual Studio, so the copying should support multi-config generators.

My current best approach is with an add_custom_command in a POST_BUILD step:

add_custom_command(TARGET mytarget POST_BUILD
	COMMAND ${CMAKE_COMMAND} -E copy_directory
        "${CMAKE_CURRENT_SOURCE_DIR}/datadir/"
        "$<TARGET_FILE_DIR:mytarget>/datadir")

In principle, this solution works, but it has two issues:

  1. I havent yet found a way to trigger a project build if only the files in the source datadir have changed. Using the other form of add_custom_command with OUTPUT, as for example proposed here cannot work as far as I see it, since the OUTPUT cannot contain target-dependent expressions according to the docs (which in my case it would need to; the output location is dependent on the currently active build configuration).
  2. In the form as shown above, it will just blindly copy everytime the build is performed, even if the datadir already exist in the output directory, and the files in the source datadir haven’t changed; I can circumvent this by, instead of just copying, invoking a custom cmake script which checks for the existence of datadir, and whether the files in the source datadir are newer than the ones in the output data dir (though this also is a bit tricky to check for all files in a folder, recursively; I haven’t found a predefined function for this yet?)

The only way I can think of so far of treating problem 1 is to implement basically a minor separate build tool in the script executed at the build step:

  • Use OUTPUT form of add_custom_command; specify some intermediate “dummy” file as output
  • in the called script:
    - use the dummy file as a “map” for last copy times per configuration
    - pass in the current configuration (via generator expression)
    - check the dummy file for entries for that configuration
    - if not present, or if entry is indicating a copy time older than the latest file modification date, copy

This seems a rather common thing to do, so I’m wondering - am I just thinking too complicated or backwards somehow, and there actually is some existing simple way to achieve this?

In CMake 3.26 there is now copy_directory_if_different.

Instead of using POST_BUILD which only runs when the target is built; I think a custom target may be more appropriate. Since your data can change without your program having to build.

add_custom_target(copy_data
	COMMAND ${CMAKE_COMMAND} -E copy_directory_if_different 
        "${CMAKE_CURRENT_SOURCE_DIR}/datadir/"
        "$<TARGET_FILE_DIR:mytarget>/datadir"
    COMMENT "Copying data"
)
add_dependencies(mytarget copy_data)
1 Like

It’s working nicely, thanks!

The only (minor) downside is that there now is an additional project cluttering the workspace for every such copying task (e.g. when using Visual Studio generator; there I “hide” them in a common folder).