The most straight forward way to copy a directory in CMake during build time is:
add_custom_target(my-copy-dir ALL COMMAND ${CMAKE_COMMAND} -E copy_directory
${INPUT_DIR} ${OUTPUT_DIR})
However, this approach is not desired because the copy will be executed every single time even when everything is up-to-date, which slow things down. Therefore, the below approach is better
file(
GLOB_RECURSE FILES
LIST_DIRECTORIES false
RELATIVE ${INPUT_DIR}/
${INPUT_DIR}/*)
foreach(FILE ${FILES})
add_custom_command(
OUTPUT ${OUTPUT_DIR}/${FILE}
DEPENDS ${INPUT_DIR}/${FILE}
COMMAND ${CMAKE_COMMAND} -E copy ${INPUT_DIR}/${FILE} ${OUTPUT_DIR}/${FILE})
list(APPEND ALL_OUTPUT_FILES ${OUTPUT_DIR}/${FILE})
endforeach()
add_custom_target(my-copy-dir ALL DEPENDS ${ALL_OUTPUT_FILES})
The copy will only be executed if there are changes to the files in the input directory. Most of the time this approach works perfectly, however, I recently encountered a situation where there are symlinks in the input folder, and the symlinks are ignored during the file glob, so unfortunately I have to use the first approach to copy this folder in order to preserve the symlink. Is there a way to make the second approach work with a folder that contains symlinks?
For more context, the folder I am copying is a macOS framework. For example, assuming the name of the framework is Foo.framework, Foo.h is inside Foo.framework/Versions/A/Headers, and there’s also a symlink to Foo.h in Foo.framework/Headers. If I use the second approach, I can see Foo.h in Foo.framework/Versions/A/Headers but not Foo.framework/Headers, whereas if I use the first approach I can see Foo.h in both, but I would like to make the second approach work in order to avoid copying every single time when everything is up-to-date. How can I achieve this?
>Solution :
Just use the FOLLOW_SYMLINKS argument of file(GLOB_RECURSE) 😛
Also, note that globbing also always has a tradeoff. See the CONFIGURE_DEPENDS argument. Either you don’t get glob match updates at build time, or you do (in which case there’s the cost of the glob matching having to run for every build). Take that into account in your statement "Therefore, the below approach is better".