deqyra
deqyra

Reputation: 764

CMake: custom command to copy files matching a pattern and preserve directory structure

I have the following project layout:

project/
├─ CMakeLists.txt
├─ main.cpp
╘═ my_lib/
   ├─ CMakeLists.txt
   ╞═ some_package1/
   │  ├── header.hpp
   │  └── impl.cpp
   ╘═ some_package2/
      ├── header.hpp
      └── impl.cpp

my_lib is added as a subdirectory by the CMakeLists.txt from project.
What I would like to have is a target in my_lib CMakeLists.txt which, when built, executes a command that copies all headers in my_lib to a destination directory, while preserving directory structure. The destination directory would be defined by the project CMakeLists.txt.

Basically, if I could somehow get add_custom_command to do this:

file(COPY ${CMAKE_SOURCE_DIR}/my_lib DESTINATION ${COPY_DEST_DIR} FILES_MATCHING PATTERN "*.hpp")

I would be extremely happy. But I haven't found a way to do that. This is very similar to an install target, except I need it done before anything gets even compiled.

I tried doing the following:

set(MY_LIB_HEADER_FILES
    some_package1/header.hpp
    some_package2/header.hpp
)

add_custom_target(copy_headers)
add_custom_command(
    TARGET copy_headers
    COMMAND ${CMAKE_COMMAND} -E copy "${MY_LIB_HEADER_FILES}" ${COPY_DEST_DIR}
    WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/my_lib
)

But that doesn't preserve directory structure, which in just this example case would lead to one header being overwritten by the other. I haven't been able to find a workaround to that.

The only solution I can imagine is a hack in which I loop over every file in ${MY_LIB_HEADER_FILES}, remove whatever follows the last forward slash, create that directory, and then copy the file over to it. But that really isn't something I want to resort to and come to think of it, I'm not even sure how I'd go about it considering it must be done in an add_custom_command call.

Surely there is a solution, right?

Upvotes: 3

Views: 2098

Answers (1)

gordan.sikic
gordan.sikic

Reputation: 1650

One of the ways to achieve this is to write a "CMake script" file that will contain your file(COPY ....) command and to execute it within add_custom_command. In brief, your code snippet would look like this:

#create script file during configure phase...
file(WRITE ${CMAKE_BINARY_DIR}/cp.cmake
  "file(COPY ${CMAKE_SOURCE_DIR}/my_lib DESTINATION ${COPY_DEST_DIR} FILES_MATCHING PATTERN *.hpp)\n"
)

#execute the script during the build phase...
add_custom_command(
  TARGET my_lib
  POST_BUILD
  COMMAND ${CMAKE_COMMAND} -P ${CMAKE_BINARY_DIR}/cp.cmake
)

file(WRITE .... statement will create a new cmake script file within the root build folder named cp.cmake.

Note 1: The $ preceding variables CMAKE_SOURCE_DIR and COPY_DEST_DIR are not escaped, meaning that the cp.cmake will contain their evaluated values.

Note 2: It is assumed that your library creates a target named my_lib and add_custom_command is added as a POST_BUILD event for that target.

In the end, here is the cmake documentation about add_custom_command, file command and running CMake in script mode

Upvotes: 3

Related Questions