Friday, October 29, 2021

[SOLVED] How can I install a hierarchy of files using CMake?

Issue

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...)


Solution

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.



Answered By - Johannes S.