'Copying assets directory from source to build directory in CMake

I am trying to write a game for practice in C++. I am using CMake for this, and my project gets built in a separate build directory automatically by my IDE. However, my assets folder is not being copied over to the new directory.

Currently, I am attempting to resolve this with the following:

add_custom_command(TARGET ${PROJECT_NAME}
    POST_BUILD
    COMMAND cp -r ${PROJECT_SOURCE_DIR}/assets ${CMAKE_BINARY_DIR}
    )

However, this seems to have absolutely no effect on the outcome and the assets directory is still not present in my build directory.

How can I actually make it copy this asset directory to my build location? Is there a smarter way to point my program to my assets location (perhaps one that will also work with assets in /usr/share?)



Solution 1:[1]

After quite a while of more searching, I managed to find the file(COPY {files} DESTINATION {directory}) command:

file(COPY assets DESTINATION ${CMAKE_BINARY_DIR})

Solution 2:[2]

Two ways I've found, both ensuring that it copies on target build and stays up to date even if the target doesn't need to re-build.


The first uses the CMake command line tool copy_directory. It's unclear from the documentation whether this copy always happens, regardless if files in the assets directory were updated or not, or if it only copies when the asset files don't exist or an asset file was updated:

add_custom_target(copy_assets
    COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_LIST_DIR}/assets ${CMAKE_CURRENT_BINARY_DIR}/assets
)
add_dependencies(mytarget copy_assets)

The second requires a small additional CMake script file, but it allows us to use the file(COPY ...) CMake comamnd, which states in the documentation that "copying preserves input file timestamps, and optimizes out a file if it exists at the destination with the same timestamp."

First, create the CMake script file copy-assets.cmake in the same directory as your CMakeLists.txt, with the contents being (similar to Ethan McTague's answer)

file(COPY ${CMAKE_CURRENT_LIST_DIR}/assets DESTINATION ${CMAKE_CURRENT_BINARY_DIR})

And then in your CMakeLists.txt, add the following:

add_custom_target(copy_assets
    COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_LIST_DIR}/copy-assets.cmake
)
add_dependencies(mytarget copy_assets)

Solution 3:[3]

The solution above using add_custom_target is suitable for most cases, but has a few problems.

  • Does not support dependent generator expressions (e.g. $<TARGET_FILE_DIR:tgt>)
  • Re-copies every build (slow for numerous/large files)
  • Is not particularly readable, especially for copying from multiple locations

Instead, I've been using file GENERATE

set(ASSETS
    assetfile
    ...)
foreach(ASSET ${ASSETS})
    file(GENERATE OUTPUT ${ASSET} INPUT ${ASSET})
endforeach()

Note I haven't needed generator expressions yet, but it looks like the latest version of CMake (3.19) adds generator expression support to this function.

Solution 4:[4]

Here is what I do with vulkan shaders. Simply replace the compilation step with a copy command:

macro(add_shaders)
    cmake_parse_arguments("MY" "TARGET" "SOURCES" ${ARGN})

    foreach(src ${MY_SOURCES})
        set(OUTF "${CMAKE_CURRENT_BINARY_DIR}/shaders/${src}.spv")
        get_filename_component(PARENT_DIR "${OUTF}" DIRECTORY)
        add_custom_command(
            OUTPUT "${OUTF}"
            COMMAND ${CMAKE_COMMAND} -E make_directory "${PARENT_DIR}"
            COMMAND "${Vulkan_GLSLC_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/${src}" -o "${OUTF}"
            DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${src}"
            VERBATIM
        )
        # src in order to get listed in the IDE, OUTF to declare the build relationship
        target_sources("${MY_TARGET}" PRIVATE "${src}" "${OUTF}")
    endforeach()
endmacro()
add_shaders(TARGET my_target SOURCES asset1 asset2 asset3 ...)

You'll have to specify each asset on its own (or loop over a list). Including wildcards into a build is a bad idea [tm]. This will also only copy/process stuff if it has changed.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Ethan McTague
Solution 2 rturrado
Solution 3 Nick
Solution 4 user1050755