How can I install a hierarchy of files using CMake?

I've created a list of files using:

file(GLOB_RECURSE DEPLOY_FILES "${PROJECT_SOURCE_DIR}/install/*")

I want to install all of these files in /usr/myproject/, but I want to maintain the file tree on the installed folder:

install/junk
install/other/junk2
install/other/junk3

If I use:

install(FILES ${DEPLOY_FILES} DESTINATION "usr/myproject")

All the files end up in /usr/myproject as:

/usr/myproject/junk
/usr/myproject/junk2
/usr/myproject/junk3

How can I make the install command keep track of relative paths?

I've worked around the issue by doing it manually in a for loop:

set(BASE "${PROJECT_SOURCE_DIR}/install")
foreach(ITEM ${DEPLOY_FILES})
  get_filename_component(ITEM_PATH ${ITEM} PATH)
  string(REPLACE ${BASE} "" ITEM_PATH ${ITEM_PATH})
  install(FILES ${ITEM} DESTINATION "usr/myproject${ITEM_PATH}")
endforeach()

...but this is annoying to do. Surely there's an easier way?

(I can't see anything in the install documentation though...)


The simplest way to install everything from a given directory is:

install(DIRECTORY mydir/ DESTINATION dir/newname)

Trailing '/' is significant. Without it mydir would be installed to newname/mydir.

From the CMake documentation:

The last component of each directory name is appended to the destination directory but a trailing slash may be used to avoid this because it leaves the last component empty.


I am assuming you have a list of files, let's say INCLUDE_FILES. Possibly a selection of files spread over a number of subdirectories, e.g. header files from across the source tree, as opposed to everything in a single subdir like in the other answers.

You can loop over the file list and use get_filename_component() to extract the directory part, then use that in a subsequent install() to set the DESTINATION subdirectory:

foreach ( file ${INCLUDE_FILES} )
    get_filename_component( dir ${file} DIRECTORY )
    install( FILES ${file} DESTINATION include/${dir} )
endforeach()

Done. ;-)

Edit: If all the files you want to install that way match a particular file pattern -- e.g. "all header files" -- then brno's answer has the edge over this one.


Use:

INSTALL( DIRECTORY <directory> DESTINATION usr/myproject )

(See here for details: http://www.cmake.org/cmake/help/v2.8.8/cmake.html#command:install)

INSTALL( DIRECTORY ... ) preserves the directory structure. But, if you use install as <directory>, you would end up with usr/myproject/install/.... which is not what you want.

There are two ways to do this:

  1. Use INSTALL( FILES .... DESTINATION usr/myproject) to install the files that lie directly in install/, then use INSTALL( DIRECTORY .... DESTINATION usr/myproject) and manually list the directories to install.

  2. Use your globbing command in your original post, and then determine which entries are files, which are directories, move the directory entries to a separate list, feed the lists to INSTALL( FILES ...) and INSTALL( DIRECTORY ...), respectively.

    file(GLOB DEPLOY_FILES_AND_DIRS "${PROJECT_SOURCE_DIR}/install/*")
    foreach(ITEM ${DEPLOY_FILES_AND_DIRS})
       IF( IS_DIRECTORY "${ITEM}" )
          LIST( APPEND DIRS_TO_DEPLOY "${ITEM}" )
       ELSE()
          LIST( APPEND FILES_TO_DEPLOY "${ITEM}" )
       ENDIF()
    endforeach()
    INSTALL( FILES ${FILES_TO_DEPLOY} DESTINATION usr/myproject )
    INSTALL( DIRECTORY ${DIRS_TO_DEPLOY} DESTINATION usr/myproject )
    

Note: Depending on the type of files you install, other INSTALL( ...) commands might be more appropriate (for example, INSTALL( TARGETS .... ) to install your libraries/executables.


Since globbing is not recommended, and running loops in CMakeLists.txt files is clunky, a pattern matching option of DIRECTORY did the trick for me.

install(DIRECTORY src/ DESTINATION include FILES_MATCHING PATTERN "*.h")

This took the entire folder structure inside src/ and reproduced it within <INSTALL_DIR>/include, header files only.