How can I install a hierarchy of files using CMake? - 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 in install/, then use INSTALL( 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 ...) 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.

Related

CMake: install(FILES ...) for a file containing a list of files

Consider I have a simple text file containing a list of (absolute) file paths.
Is there any easy way to make CMake install those files (as if install(FILES ...) was used)?
Directory structure (minus some constant leading path components) should be maintained.
Right now the only option I could come up with was to use
install(CODE "execute_process(COMMAND my_script.sh)")
and do it using normal shell commands, but that seems to defeat the purpose of using a build system in the first place...
I believe this would do the trick:
# 'filename' is the file that contains a list ';' separated paths relative to that input file
function(install_my_files filename)
file(READ ${filename} relative_paths)
get_filename_component(parent_directory ${filename} DIRECTORY) # parent directory of input file
foreach(relative_path ${relative_paths})
get_filename_component(relative_directory ${relative_path} DIRECTORY)
install(FILES "${parent_directory}/${relative_path}" DESTINATION ${relative_directory})
endforeach()
endfunction()

install specific pattern files with cpack and cmake

assuming i have the next structure in my project:
src_dir\a
src_dir\b\b2
src_dir\c\c2\c3
and in each folders i have few types of files (.txt, .lib, .dll....)
i want to install only the dll files in directory X, so i tried:
install(
DIRECTORY src_dir
DESTINATION X/
COMPONENT DLLS
FILES_MATCHING PATTERN "*.dll"
)
it does work fine but it give me the full structure of my original structure (and i only want the dll files in X directory). the output it:
X/a/a.dll
X/b/b2/b.dll
X/c/c2/c3/c.dll
and i want it to be like that a.dll, b.dll and c.dll will be in X (without any sub-folders).
is there a way to do it without giving the full paths to every dll file i have in my project?
Thanks :)
You should be able to get the behaviour you want by listing each directory, not necessarily each DLL. If you include a trailing forward slash at the end of the DIRECTORY, it will omit the directory name when copying to the destination. I'd expect something like the following to achieve what you want:
install(
DIRECTORY src_dir/a/
DESTINATION X
COMPONENT DLLS
FILES_MATCHING PATTERN "*.dll"
)
install(
DIRECTORY src_dir/b/b2/
DESTINATION X
COMPONENT DLLS
FILES_MATCHING PATTERN "*.dll"
)
install(
DIRECTORY src_dir/c/c2/c3/
DESTINATION X
COMPONENT DLLS
FILES_MATCHING PATTERN "*.dll"
)

Install all files in a directory except folders in CMake

I want to install all files under a folder called scfg which contains .cfg, .xml files, and folders to another location which contains scfg. The code below installs correctly.
file(GLOB config_files "scfg/*.cfg")
install(FILES ${config_files} DESTINATION scfg)
file(GLOB xml_files "scfg/*.xml")
install(FILES ${xml_files} DESTINATION scfg)
But it is possible that there will be more files with types other than .cfg and .xml in the future. To prevent changing this file frequently, I am looking for a more generic solution, such as installing all file types other than folders. I have heard about the EXCLUDE keyword. But I don't know what PATTERN I should use for folders.
It should exclude subdirectories explicitly and then all other files will be installed correctly as usual.
install(DIRECTORY scfg/ DESTINATION scfg
PATTERN "scfg/config/" EXCLUDE
PATTERN "scfg/xml/" EXCLUDE
)

Add sources from other paths don't have CMakeLists.txt, and move output files to other path in CMake

My source tree:
/project
/project/src/<my sources>
/project/build/vs2008
/project/build/ubuntu
I want to put my CMakeLists.txt in vs2008 and ubuntu. I can accept put one CMakeLists.txt to each folder and put another global CMakeLists.txt on /project/build, but I just don't want any CMakeLists.txt in /project/src(So I can't use add_subdirectory command). I need my solution files of visual studio in /project/build/vs2008 and Makefile in /project/build/ubuntu. What commands I should know about?
I think there's nothing special you would need to know -- you just need to specify paths to your source files relative to where your CMakeLists.txt is located, and your targets should build the same as if you had placed CMakeLists.txt in your src folder. For example, project/build/ubuntu/CMakeLists.txt could look like this:
set( SrcDir ../../src )
add_executable( MyApp
${SrcDir}/file1.cpp
${SrcDir}/file2.cpp
# and so on
)

CMake: adding custom resources to build directory

I am making a small program which requires an image file foo.bmp to run
so i can compile the program but to run it, i have to copy foo.bmp to 'build' subdirectory manually
what command should i use in CMakeLists.txt to automatically add foo.bmp to build subdirectory as the program compiles?
In case of this might help, I tried another solution using file command. There is the option COPY that simply copy a file or directory from source to dest.
Like this:
FILE(COPY yourImg.png DESTINATION "${CMAKE_BINARY_DIR}")
Relative path also works for destination (You can simply use . for instance)
Doc reference: https://cmake.org/cmake/help/v3.0/command/file.html
To do that you should use add_custom_command to generate build rules for file you needs in the build directory. Then add dependencies from your targets to those files: CMake only build something if it's needed by a target.
You should also make sure to only copy files if you're not building from the source directory.
Something like this:
project(foo)
cmake_minimum_required(VERSION 2.8)
# we don't want to copy if we're building in the source dir
if (NOT CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR)
# list of files for which we add a copy rule
set(data_SHADOW yourimg.png)
foreach(item IN LISTS data_SHADOW)
message(STATUS ${item})
add_custom_command(
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${item}"
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/${item}" "${CMAKE_CURRENT_BINARY_DIR}/${item}"
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${item}"
)
endforeach()
endif()
# files are only copied if a target depends on them
add_custom_target(data-target ALL DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/yourimg.png")
In this case I'm using a "ALL" custom target with a dependency on the yourimg.png file to force the copy, but you can also add dependency from one of your existing targets.