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:
Use
INSTALL( FILES .... DESTINATION usr/myproject)
to install the files that lie directly ininstall/
, then useINSTALL( DIRECTORY .... DESTINATION usr/myproject
) and manually list the directories to install.-
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 ...)
andINSTALL( 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.