CMake change forced DESTINATION directory name (install target) - c++

I'm trying to add install target to my library. I have my source code & header files located in sub-directories under src/.
Relevant chunk of my CMakeLists.txt file:
install(
TARGETS "${PROJECT_NAME}"
EXPORT ionir-config LIBRARY
DESTINATION ${CMAKE_INSTALL_LIBDIR} # Destination is relative to ${CMAKE_INSTALL_PREFIX}.
)
install(
EXPORT ionir-config
NAMESPACE ionir::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/ionir
)
install(
DIRECTORY src
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
FILES_MATCHING PATTERN "*.h" PATTERN "LICENSE" # Match only header files & LICENSE file.
)
The problem is that once installed, the output directory's name is src:
I would like it to be ionir, otherwise I'd have to import files like this:
#include <ionir/src/something/file.h>
^^^
How can I accomplish this?

For avoid adding directory name to installation path, terminate the directory with a slash (/):
install(
DIRECTORY src/
...
)
This is explicitly stated in the documentation for install command:
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.

Related

CMake Install/CPack Problems: Improper Permissions AND Wrong Executables

I'm currently working on a project that is being built using CMake; it is then subsequently packaged up with CPack into an RPM.
The following is packaged into the RPM:
Several Executables
Configuration files
Some more context:
This project is running using OpenMPI, and there are X amount of nodes, depending on user input AND based on the # of nodes the user specifies, X docker containers are spawned.
There are instances of each executable running in tandem on each container
There is an OpenMPI crash and it is difficult to debug without GDB
The executables have been compiled as Release. However, if I want to make debugging possible I have been compiling the CMake project as Debug.
The executables are created as Debug. I have the VERBOSE=1 flag on when making the package and can confirm that -g flag is present, and the executables have debug symbols when loading them into GDB locally
Some lines in the CMakeLists.txt:
# Bunch of compilation lines above
install(TARGETS executable_1 executable_2 executable_3 DESTINATION bin) # Installs the executables into a local bin dir
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
set(CMAKE_INSTALL_PREFIX /usr)
install(DIRECTORY bin/ DESTINATION /usr/bin)
install(DIRECTORY config/ DESTINATION /etc/PROJECT_NAME FILES_MATCHING PATTERN "*")
# Making of the package
set(CPACK_PACKAGE_NAME "PROJECT_NAME")
set(CPACK_PACKAGE_VERSION 0.1)
set(CPACK_PACKAGE_RELEASE 1)
set(CPACK_GENERATOR "RPM")
set(CPACK_PACKAGE_VENDOR "ME")
set(CPACK_PACKAGING_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX})
set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${CPACK_PACKAGE_RELEASE}.${CMAKE_SYSTEM_PROCESSOR}")
set(CPACK_RPM_PACKAGE_AUTOREQ 0)
set(CPACK_RPM_PACKAGE_RELOCATABLE True)
include(CPack)
The problem:
When CPack is doing its thing and creating the RPM, the executables can be packaged into the RPM, but the problem is they have improper permissions; aka when the executables are installed on the Containers, they cannot be executed. I can address this by adding:
# Bunch of compilation lines above
install(TARGETS executable_1 executable_2 executable_3 DESTINATION bin) # Installs the executables into a local bin dir
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
set(CMAKE_INSTALL_PREFIX /usr)
# Permissions Fix
set(PROGRAM_PERMISSIONS_DEFAULT
OWNER_WRITE OWNER_READ OWNER_EXECUTE
GROUP_READ GROUP_EXECUTE
WORLD_READ WORLD_EXECUTE)
install(DIRECTORY bin/ PERMISSIONS ${PROGRAM_PERMISSIONS_DEFAULT} DESTINATION /usr/bin)
install(DIRECTORY config/ DESTINATION /etc/PROJECT_NAME FILES_MATCHING PATTERN "*")
# Making of the package
set(CPACK_PACKAGE_NAME "PROJECT_NAME")
set(CPACK_PACKAGE_VERSION 0.1)
set(CPACK_PACKAGE_RELEASE 1)
set(CPACK_GENERATOR "RPM")
set(CPACK_PACKAGE_VENDOR "ME")
set(CPACK_PACKAGING_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX})
set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${CPACK_PACKAGE_RELEASE}.${CMAKE_SYSTEM_PROCESSOR}")
set(CPACK_RPM_PACKAGE_AUTOREQ 0)
set(CPACK_RPM_PACKAGE_RELOCATABLE True)
include(CPack)
After adding the permissions, the permission are fixed, but for some reason the executables that are copied do not have debug symbols anymore; my local executables in the bin/ folder are the proper executables which have symbols, but the executables packaged into the RPM DOT NOT have debug symbols. Meaning somewhere along the way, something really odd is happening.
My question is, why? Either way results in a different problem. I'm wondering if there is a way to make sure the permissions are correct AND the proper executables get copied and packaged into the RPM. Any suggestions would be much appreciated.
Thanks!
Do not use absolute install destinations and do not read CMAKE_INSTALL_PREFIX, if you can avoid it. Using absolute install destinations (possibly indirectly through the use of CMAKE_INSTALL_PREFIX) forces you to run the install target with privileges that allow the process to modify those absolute paths. This usually means you have to use sudo to run the install target, even when installing to a directory owned by the standard user.
Since running cpack involves running the install target with the install prefix replaced with a directory inside the build directory (basically cmake --install ... --prefix ...), you can avoid the need for root privileges, if you use relative install locations.
My preferred the install logic here would be:
# allow the installation into any directory below the file system root
set(CMAKE_INSTALL_PREFIX /)
# set the default executable install location, see https://cmake.org/cmake/help/latest/command/install.html#installing-targets
# Note: could be set "globally", i.e. from the toplevel CMakeLists.txt allowing installation of individual targets
set(CMAKE_INSTALL_BINDIR usr/bin)
install(TARGETS executable_1 executable_2 executable_3 RUNTIME)
#preferrably replace this with install(FILES ... TYPE BIN) to install individual files
set(PROGRAM_PERMISSIONS_DEFAULT
OWNER_WRITE OWNER_READ OWNER_EXECUTE
GROUP_READ GROUP_EXECUTE
WORLD_READ WORLD_EXECUTE)
install(DIRECTORY bin/ PERMISSIONS ${PROGRAM_PERMISSIONS_DEFAULT} DESTINATION usr/bin)
# preferrably replace this with install(FILES ... TYPE SYSCONFIG)
install(DIRECTORY config/ DESTINATION etc/PROJECT_NAME FILES_MATCHING PATTERN "*")
# Making of the package
...
I assume in your scenario somewhere along the way you got the file permissions incorrect the cmake build / install / package process.

macOS cmake display"install TARGETS given no ARCHIVE DESTINATION for static library target "SO_kolev"."

When My MacBook run cmake, it display
install TARGETS given no ARCHIVE DESTINATION for static library target
"SO_kolev".
How can I fix it?
The following is my cmake file:
# Create shared library with a generated dictionary.
add_library(SO_${PROJECT_NAME} ${SOURCES} D_${PROJECT_NAME}.cxx)
# Link against shared library and list of ROOT libraries
target_link_libraries(SO_${PROJECT_NAME} PUBLIC ${LIB_NAMES})
# Find location of the enrty point file (main.c*)
file(GLOB_RECURSE MAIN ${CMAKE_CURRENT_SOURCE_DIR}/*.cxx ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/*.cc ${CMAKE_CURRENT_SOURCE_DIR}/*.c)
list(FILTER MAIN INCLUDE REGEX "main\\.c")
# message(STATUS "Found entry point file: ${MAIN}")
# Create the main program using the library.
add_executable(${PROJECT_NAME} ${MAIN})
target_link_libraries(${PROJECT_NAME} SO_${PROJECT_NAME})
# Compose the install target
install(TARGETS ${PROJECT_NAME} SO_${PROJECT_NAME}
RUNTIME DESTINATION bin
LIBRARY DESTINATION ${ROOTSYS}/lib)
install(FILES ${PROJECT_BINARY_DIR}/libD_${PROJECT_NAME}.rootmap
DESTINATION $ENV{ROOTSYS}/lib)
install(FILES ${PROJECT_BINARY_DIR}/libD_${PROJECT_NAME}_rdict.pcm
DESTINATION /usr/local/bin)
When installed with install(TARGETS) command, a static library is treated as ARCHIVE kind of artifact, so CMake needs to know into which directory to install such kind of artifacts.
This directory is specified with DESTINATION argument, but your install command has only directories for RUNTIME and LIBRARY artifacts. So you need to add appropriate argument for ARCHIVE:
install(TARGETS ${PROJECT_NAME} SO_${PROJECT_NAME}
RUNTIME DESTINATION bin
LIBRARY DESTINATION ${ROOTSYS}/lib
ARCHIVE DESTINATION ${ROOTSYS}/lib)
Since CMake 3.14 some kind of artifacts has default values for installation directories, so DESTINATION argument for them is no longer required. This is explicitely noted in the documentation:
For regular executables, static libraries and shared libraries, the DESTINATION argument is not required. For these target types, when DESTINATION is omitted, a default destination will be taken from the appropriate variable from GNUInstallDirs, or set to a built-in default value if that variable is not defined.

CMake Package Support - Includes and Libraries not found

I am currently developing a software package, for which I'd like to provide the cmake package support (so users can find it with find_package(...)). The Problem is, the package is found but FOO_INCLUDE_DIR and FOO_LIBRARIES is empty.
Within my package I have several modules, each with a CMakeLists file which installs the respective library and headers with:
install(TARGETS ${LIBRARY_NAME} EXPORT FooTargets
RUNTIME DESTINATION ${Foo_RUNTIME_INSTALL_DIR}
LIBRARY DESTINATION ${Foo_LIBRARY_INSTALL_DIR}
ARCHIVE DESTINATION ${Foo_ARCHIVE_INSTALL_DIR}
FRAMEWORK DESTINATION ${Foo_FRAMEWORK_INSTALL_DIR})
# Headers
install(
DIRECTORY include/${LIBRARY_NAME}
DESTINATION include/${PROJECT_NAME}
FILES_MATCHING
PATTERN "*.h"
PATTERN "*.hpp"
)
The headers for the library are included with target_include_directories like this:
target_include_directories(${LIBRARY_NAME} PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> # for headers when building
$<INSTALL_INTERFACE:${Foo_INC_INSTALL_DIR}> # for client in install mode
)
I checked the folders and all libraries and headers are correctly installed. In my toplevel CMakeLists I export my targets with:
install(
EXPORT FooTargets
DESTINATION ${Foo_CMAKE_CONFIG_INSTALL_DIR}
FILE FooConfig.cmake
)
The config is where I assume it to be (usr/local/lib/cmake/Foo). So everything seems to be correct. When I look into my FooConfig.cmake it says:
# Create imported target realm_densifier_base
add_library(FooLib1 SHARED IMPORTED)
set_target_properties(FooLib1 PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "/usr/local/include/Foo"
INTERFACE_LINK_LIBRARIES "...several libraries..."
)
...which is absolutely correct and exactly what I expected. What part of the puzzle is missing? Is INTERFACE_INCLUDE_DIRECTORIES and INTERFACE_LINK_LIBRARIES not the correct flag to be set?
Thanks for the help and best regards,
Alex
Edit:
#Guillaume Racicot already cleared most things up, I only knew the "non target" way of adding headers to my project, that was with include_directories(Foo_INCLUDE_DIRS). However, in the target-world linking against my library Foo was enough. Another thing was that I messed up some directories in the target_include_directories(...) command, so directories were wrong and therefore could not be found in my other project. Thanks for the help!
Why would FOO_INCLUDE_DIR or FOO_LIBRARIES be set? This may be how old find modules worked, but not how config files work. Even newer find modules expose targets instead of directory variables.
When generating an XYZConfig.cmake file, informations about targets will be exported, not informations on the directory.
With such exportation:
install(
EXPORT FooTargets
NAMESPACE Foo::
DESTINATION ${Foo_CMAKE_CONFIG_INSTALL_DIR}
FILE FooConfig.cmake
)
You would expect users of the package to use it like so:
find_package(Foo REQUIRED)
# or PUBLIC ------v
target_link_libraries(bar PRIVATE Foo::FooLib1)
If your package has multiple targets in the export set, then you can link to both or only one
target_link_libraries(bar PRIVATE Foo::FooLib1 Foo::FooLib2)
target_link_libraries(baz PUBLIC Foo::FooLib2) # link to lib2 only
When you link to an exported target like Foo::FooLib1, its public interface will be transitively transmitted to its user. In the example above, bar will inherit properties from the linked target.
So the INTERFACE_INCLUDE_DIRECTORIES of Foo::FooLib1 and Foo::FooLib2 will be appended to bar's INCLUDE_DIRECTORIES. Same for LINK_LIBRARIES.
For baz, not only its INCLUDE_DIRECTORIES will contain Foo::FooLib2 entries, but also its own INTERFACE_INCLUDE_DIRECTORIES will transitively transmit the usage requirements of Foo::FooLib2

install() with FILES_MATCHING PATTERN ignoring pattern and installing all

I'm currently having a problem with the install() function of CMake v3.13.4. My code is as follows:
install(
DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
DESTINATION ${CMAKE_INSTALL_PREFIX}
FILES_MATCHING PATTERN "*.cfg"
)
My understanding is that CMake will copy all files matching this pattern to my location given by ${CMAKE_INSTALL_PREFIX}, but all the subfolders in my current directory also get copied. Also, how is it possible to copy more than one file-ending pattern to the destination? Simply putting *.cfg | *.xyz or *.cfg || *.xyz does not work.
Edit:
Also tried to replace the FILES_MATCHING PATTERN with:
FILES_MATCHING REGEX "[a-zA-Z0-9]*.ate|[a-zA-Z0-9]*.reserved"
which only copies files *.reserved AND all folders again.
If any of the subfolders in your directory also contain .cfg files, CMake will copy those as well. You need to explicitly tell CMake to ignore these using the EXCLUDE directive. Also, you can concatenate multiple PATTERN directives for CMake to search for and install:
install(
DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
DESTINATION ${CMAKE_INSTALL_PREFIX}
FILES_MATCHING
PATTERN "*.cfg"
PATTERN "*.xyz"
PATTERN "subFolderA" EXCLUDE
)

CMake autogenerated export file for relocatable library

I have a very simple library with a header file and a source file. I'm using CMake to compile it, initially like this:
add_library(libOEInfoProvider SHARED
${CMAKE_CURRENT_LIST_DIR}/src/OE/InfoProvider.h
${CMAKE_CURRENT_LIST_DIR}/src/OE/InfoProvider.cpp)
# see: https://stackoverflow.com/a/25681179/276451
target_include_directories(libOEInfoProvider
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
$<INSTALL_INTERFACE:dist/include>)
I've now configured it so that make install generates all necessary files in a dist folder, like so:
install(TARGETS libOEInfoProvider EXPORT libOEInfoProviderConfig
LIBRARY DESTINATION ${CMAKE_SOURCE_DIR}/dist/lib
)
install(DIRECTORY
src/ DESTINATION ${CMAKE_SOURCE_DIR}/dist/include)
install(EXPORT
libOEInfoProviderConfig DESTINATION ${CMAKE_SOURCE_DIR}/dist/cmake)
export(TARGETS libOEInfoProvider FILE libOEInfoProviderConfig.cmake)
The problem I have is that the geneerated .cmake files contain absolute paths to the dist folder, but I need to deploy this library somewhere else with a different directory structure. Therefore I'd need to have relative paths in the .cmake, so wherever I place the library, the moment I use find_package in the client code it should be able to find the code.