196

I'm trying to create a simple project on CLion. It uses CMake to generate Makefiles to build project (or some sort of it)

All I need to is transfer some non-project file (some sort of resource file) to binary directory each time when I run the my code.

That file contains test data and application open it to read them. I tried several ways to do so:

  • Via file(COPY ...

    file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/input.txt
            DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/input.txt
    

    Looking good but it work just once and not recopy file after next run.

  • Via add_custom_command

    • OUTPUT version

      add_custom_command(
              OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/input.txt
              COMMAND ${CMAKE_COMMAND} -E copy
                      ${CMAKE_CURRENT_SOURCE_DIR}/input.txt
                      ${CMAKE_CURRENT_BINARY_DIR}/input.txt)
      
    • TARGET version

      add_custom_target(foo)
      add_custom_command(
              TARGET foo
              COMMAND ${CMAKE_COMMAND} copy
                      ${CMAKE_CURRENT_BINARY_DIR}/test/input.txt
                      ${CMAKE_SOURCE_DIR})
      

    But no one of it work.

What am I doing wrong?

10 Answers 10

229

You may consider using configure_file with the COPYONLY option:

configure_file(<input> <output> COPYONLY)

Unlike file(COPY ...) it creates a file-level dependency between input and output, that is:

If the input file is modified the build system will re-run CMake to re-configure the file and generate the build system again.

Sign up to request clarification or add additional context in comments.

3 Comments

Please note that configure_file isn't going to work with sub-directories, even if you use GLOB to create a list of files.
Also note that this isn't a "file-level dependency" between input and output: it's a dependency between the input and the cmake configure step. So instead of a regular build dependency where changing a source file rebuilds only the corresponding object file, this causes the whole of cmake to reconfigure, which may have other side effects.
how we use this command to copy recursive directory files?
124

Both option are valid and targeting two different steps of your build:

  1. file(COPY ... copies the file during the configuration step and only in this step. When you rebuild your project without having changed your cmake configuration, this command won't be executed.
  2. add_custom_command is the preferred choice when you want to copy the file around on each build step.

The right version for your task would be:

add_custom_command(
        TARGET foo POST_BUILD
        COMMAND ${CMAKE_COMMAND} -E copy
                ${CMAKE_SOURCE_DIR}/test/input.txt
                ${CMAKE_CURRENT_BINARY_DIR}/input.txt)

you can choose between PRE_BUILD, PRE_LINK, POST_BUILD best is you read the documentation of add_custom_command

An example on how to use the first version can be found here: Use CMake add_custom_command to generate source for another target

5 Comments

Is it CMAKE_SOURCE_DIR or CMAKE_CURRENT_SOURCE_DIR?
@SyaifulNizamYahya use CMAKE_CURRENT_SOURCE_DIR if the test/input.txt file is relative to the current CMakeLists.txt file. If it's relative to the root CMakeLists.txt, then use CMAKE_SOURCE_DIR.
Is there a way to get the targets filename in cmake? without hardcoding the filename? e.g. I'm building a lib file, does cmake have a variable to get the targte filename? like libSomething.so?
Note: to copy a directory recursively instead of a file, use ${CMAKE_COMMAND} -E copy_directory
@jorisv92 how would you force copy or copy if different using copy_directory or some other recursive operation?
28

The first of option you tried doesn't work for two reasons.

First, you forgot to close the parenthesis.

Second, the DESTINATION should be a directory, not a file name. Assuming that you closed the parenthesis, the file would end up in a folder called input.txt.

To make it work, just change it to

file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/input.txt
     DESTINATION ${CMAKE_CURRENT_BINARY_DIR})

Comments

17

I would suggest TARGET_FILE_DIR if you want the file to be copied to the same folder as your .exe file.

$ Directory of main file (.exe, .so.1.2, .a).

add_custom_command(
  TARGET ${PROJECT_NAME} POST_BUILD
  COMMAND ${CMAKE_COMMAND} -E copy 
    ${CMAKE_CURRENT_SOURCE_DIR}/input.txt 
    $<TARGET_FILE_DIR:${PROJECT_NAME}>)

In VS, this cmake script will copy input.txt to the same file as your final exe, no matter it's debug or release.

2 Comments

This is great, but only guaranteed to work with POST_BUILD. Reference: gitlab.kitware.com/cmake/cmake/-/issues/21364#note_849331
This and only this worked for me. I am migrating a legacy library from autoconf to CMake. This setup works when using both gcc and msvc.
15

The suggested configure_file is probably the easiest solution. However, it will not rerun the copy command to if you manually deleted the file from the build directory. To also handle this case, the following works for me:

add_custom_target(copy-test-makefile ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/input.txt)
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/input.txt
                   COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/input.txt
                                                    ${CMAKE_CURRENT_BINARY_DIR}/input.txt
                   DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/input.txt)

1 Comment

IMO this is the only correct solution here. The first command tells cmake what files you need to exist and the second command tells cmake how to create them.
13

if you want to copy folder from currant directory to binary (build folder) folder

file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/yourFolder/ DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/yourFolder/)

then the syntexe is :

file(COPY pathSource DESTINATION pathDistination)

Comments

10

This is what I used to copy some resource files: the copy-files is an empty target to ignore errors

 add_custom_target(copy-files ALL
    COMMAND ${CMAKE_COMMAND} -E copy_directory
    ${CMAKE_BINARY_DIR}/SOURCEDIRECTORY
    ${CMAKE_BINARY_DIR}/DESTINATIONDIRECTORY
    )

2 Comments

I would also add add_dependencies(MainTarget copy-files) to make it run automatically when I build MainTarget
This seems like the best answer (+Herrgott's comment) as it actually ensures that the current version of the source is always in the destination post build. For small copy jobs this works well, thanks. Putting add_dependencies(MainTarget copy-files) in the root CMakeLists.txt file means that this can be used throughout the project.
3

Single File

First add a rule for the copying. OUTPUT specifies the produced file and DEPENDS creates a dependency, so when the input changes this rule is run again.

set(MY_RESOURCE_FILE input.txt)
add_custom_command(
        OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${MY_RESOURCE_FILE}
        COMMAND ${CMAKE_COMMAND} -E copy
            ${CMAKE_CURRENT_SOURCE_DIR}/${MY_RESOURCE_FILE}
            ${CMAKE_CURRENT_BINARY_DIR}/${MY_RESOURCE_FILE}
        DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${MY_RESOURCE_FILE}
)

Then add the 'result file' as a dependency to the target that needs it.

add_executable(main_executable
    ${CMAKE_CURRENT_BINARY_DIR}/${MY_RESOURCE_FILE}

    src/main.cpp
)

This is a modern and clean CMake solution, as all dependencies are defined target based, and are propagated up-wards the dependency tree as needed. We are not changing any global state. Furthermore the files are tracked, so are only copied when they are out-of-date or missing. This makes it also a robust and efficient solution.

If the file is needed by multiple targets, add them to all of them. This is good practice, as dependencies are explicitly specified, exactly where they are needed. And copied when those targets are build.

Multiple Files

As some people here asked for copying multiple files, I am adding this here as well, as a few more things need to be considered.

There are two options to list the files, manually listing them and using recursive glob rule, that discovers them automatically. The first is very straight forward, using relative paths to CMAKE_CURRENT_SOURCE_DIR.

set(MY_RESOURCE_FILES 
    resources/font.ttf
    resources/icon.svg
)

The auto-discovery rules is not much longer, however it requires more explanations.

file(GLOB_RECURSE
    MY_RESOURCE_FILES
    RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} 
    CONFIGURE_DEPENDS

    ${CMAKE_CURRENT_SOURCE_DIR}/resources/**.ttf    # adapt these to your needs
    ${CMAKE_CURRENT_SOURCE_DIR}/resources/**.svg
)

Specifying CONFIGURE_DEPENDS re-runs the glob at the beginning the build when any target is out-of date. This is needed, as otherwise the build system once configured will not look new files that would match the glob. And RELATIVE gives us relative path to our source directory. Note that stars **.svg recursively searches all sub-directories as well, a common glob rule.

Now we create again a separate rule to make each resource file.

FOREACH(MY_RESOURCE_FILE ${MY_RESOURCE_FILES})
    add_custom_command(
        OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${MY_RESOURCE_FILE}
        COMMAND ${CMAKE_COMMAND} -E copy
            ${CMAKE_CURRENT_SOURCE_DIR}/${MY_RESOURCE_FILE}
            ${CMAKE_CURRENT_BINARY_DIR}/${MY_RESOURCE_FILE}
        DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${MY_RESOURCE_FILE}
  )
ENDFOREACH()

This will also make it that only the resources missing or changed are copied.

And finally we add all of them to our executable. For that we just need to adjust the paths to the binary dir.

list(TRANSFORM MY_RESOURCE_FILES PREPEND ${CMAKE_CURRENT_BINARY_DIR}/)

add_executable(${MAIN_TARGET} 
    ${MY_RESOURCE_FILES}

    src/main.cpp
)

Comments

1

If you don't expect modifications to be made to the file in the binary directory, you could just save the complication of writing custom commands / targets for re-copying upon file changes and just use symlinks. CMake supports creating symlinks (or pretty much equivalent things on Windows). It has the file(CREATE_LINK <original> <linkname> [RESULT <result>] [COPY_ON_ERROR] [SYMBOLIC]) command, and the create_symlink <old> <new> commandline command.

If you don't expect the copy from the source directory to ever change / or are okay with re-configuring manually every time it changes, you can use the file(COPY_FILE <oldname> <newname> [RESULT <result>] [ONLY_IF_DIFFERENT] [INPUT_MAY_BE_RECENT]) or the file(COPY [...]) commands.

Comments

0

If you want to put the content of example into install folder after build:

code/
  src/
  example/
  CMakeLists.txt

try add the following to your CMakeLists.txt:

install(DIRECTORY example/ DESTINATION example)

2 Comments

But the 'install' command has a side effect: the installed files will appear in the cpack package . . .
This is a correct answer to a different question. :) Build (cmake --build ...) is a separate stage from install (cmake --install...). In particular, VSCode normally runs only the build command and debugs the program directly from CMAKE_BINARY_DIR. The question is kinda mushy at which stage, configure or build, the file needs to be copied, but the install phase is certainly too late. Yes the files will require separate install description with the install() command, best using target properties in the Modern CMake framework, but that's out of scope.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.