103

I've got some config files (xml, ini, ...) in the config directory next to the source files. How can I copy all the files in the config directory into the build directory (next to the executable file) each time I make the project?

Lii
  • 11,553
  • 8
  • 64
  • 88
B Faley
  • 17,120
  • 43
  • 133
  • 223

5 Answers5

178

You can use add_custom_command.

Say your target is called MyTarget, then you can do this:

add_custom_command(TARGET MyTarget PRE_BUILD
                   COMMAND ${CMAKE_COMMAND} -E copy_directory
                       ${CMAKE_SOURCE_DIR}/config/ $<TARGET_FILE_DIR:MyTarget>)

This executes every time you build MyTarget and copies the contents of "/config" into the directory where the target exe/lib will end up.

As Mark Lakata points out in a comment below, replacing PRE_BUILD with POST_BUILD in the add_custom_command ensures that copying will only happen if the build succeeds.

Explanation

  • ${CMAKE_COMMAND} is the path to CMake
  • -E makes CMake run commands instead of building
  • copy_directory is a Command-Line Tool
  • config is the directory (that falls under the root of the project) whose contents will be copied into the build target
  • $<TARGET_FILE_DIR:MyTarget> is a generator expression, described in the add_custom_command documentation.
rturrado
  • 7,699
  • 6
  • 42
  • 62
Fraser
  • 74,704
  • 20
  • 238
  • 215
  • Can I use `all` for `MyTarget`? I used the following but it doesnt seem to work (it didn't copy the content of config directory into the build directory): `add_custom_command(TARGET all PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/config $)` – B Faley Nov 17 '12 at 11:34
  • No - unfortunately the CMake custom targets can't be used for this. I think there's a feature request in for this though. – Fraser Nov 17 '12 at 11:55
  • So, what should I use for MyTarget? Could you update your answer with a simplest possible CMakeLists.txt file that uses `add_custom_command`? – B Faley Nov 17 '12 at 11:56
  • 1
    One of your actual targets you've got in your CMakeLists.txt - i.e something added via `add_executable` or `add_library`. – Fraser Nov 17 '12 at 11:58
  • 1
    Ok, it worked. But config files are copied only after `make clean`. A simple make will not copy the config files. Any idea? – B Faley Nov 17 '12 at 12:03
  • Seems odd. Using VS on Windows, the files are always copied. – Fraser Nov 17 '12 at 12:08
  • I am on Linux, Mint - cmake version 2.8.5 – B Faley Nov 17 '12 at 12:09
  • 1
    Yeah - I think `make` is clever enough to not even execute `make MyTarget` if it detects it's unchanged. If anything changes in your target, then make will execute on that target, and the files will be copied. – Fraser Nov 17 '12 at 12:19
  • Please see this: [http://stackoverflow.com/q/13438512/69537](http://stackoverflow.com/q/13438512/69537) – B Faley Nov 18 '12 at 08:18
  • 7
    POST_BUILD is probably a better option, which means the files will only be copied if the build succeeds. – Mark Lakata Apr 20 '15 at 17:28
  • 7
    Is there a way to make `make clean` aware that the files copied must be deleted when the project cleans? – moonwalker Dec 19 '15 at 21:54
  • 3
    Is there a way to copy the directory itself instead of the contents? –  Mar 12 '16 at 06:00
  • 1
    I have a directories hierarchy, and want to copy whole hierarchy at some other location post build. Not able to do with this command. could you please give some hint/syntaxt cc:@Fraser – CodeWithVikas Jan 16 '19 at 10:10
24

In addition to the top answer,

To copy the directory itself instead of the contents, you can add /${FOLDER_NAME} to the end of the second parameter.

Like this:

add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
                   COMMAND ${CMAKE_COMMAND} -E copy_directory
                       ${CMAKE_SOURCE_DIR}/config $<TARGET_FILE_DIR:${PROJECT_NAME}>/config)
Pika Supports Ukraine
  • 3,612
  • 10
  • 26
  • 42
hyrchao
  • 241
  • 2
  • 3
7

CMake supports a shell type file copy. This link should be helpful for you - How to copy directory from source tree to binary tree?

Community
  • 1
  • 1
rajneesh
  • 1,709
  • 1
  • 13
  • 13
6

Use symbolic links [Dev only!]


CMake enables symbolic links via create_symlink:

add_custom_command(TARGET ${CMAKE_PROJECT_NAME} PRE_BUILD
                   COMMAND ${CMAKE_COMMAND} -E create_symlink
                   ${CMAKE_SOURCE_DIR}/config $<TARGET_FILE_DIR:${PROJECT_NAME}>/config)

It ensures that when you make a change to the files in the directory, build folder would subsequently be updated.

However, as @Vallerious pointed out, it is just a development only solution as it is not really copying the directory contents. It is just linking them.

satk0
  • 170
  • 1
  • 9
  • 1
    Love this! thank you! – frankelot Nov 10 '21 at 02:58
  • 3
    But if you are building a release, a symlink won't work as you wont have the files in place. What you are solving is that your path would be the same between debug and release, but still you would need a different command when releasing to copy the contents. – Vallerious Mar 30 '22 at 08:04
  • Proper `install` commands could make this a not-dev-only solution. Ex: a python module that has some compiled C code and some Python files. In the build directory where the compiled `.so` ends up, you put links to the other python files in the source dir. Then you can have `install(TARGET compiled_lib ...)` and `install(FILES ...)` that copies the python files from the source dir to somewhere in the install prefix. Non-devs should always use the installed package. – Philippe Carphin Jul 10 '23 at 22:32
  • @PhilippeCarphin Good idea, I will expand on this to include not-dev solution with `install` soon. – satk0 Jul 16 '23 at 18:28
-6

In my project i use INSTALL to specify in CMake, what and where i move my binary with conf file. After execution of cmake, use "make install".

  • OP asked how to do this upon completion of build. Implication is without another command entered into the shell. Perform the build and *poof* the files your binary need are magically there. I too use the `install()` command, but this is for a special case: the code builds, passes unit tests, and is ready for some level of use by others. I don't install it to debug it. – PfunnyGuy Nov 16 '17 at 14:49