CMAKE: How to install the dependencies of a target - c++

I am trying to install an executable using cmake. My real problem here is: how to install the executable and its dependencies.
Here an example:
I want to install one executable that depend on two libraries of my cmake and one 3rdparty (pre-compiled).
set(EXECUTABLE_NAME MyExecutable)
file(GLOB_RECURSE ${EXECUTABLE_NAME}_SOURCES *.cpp)
add_executable(${EXECUTABLE_NAME} ${${EXECUTABLE_NAME}_SOURCES})
target_link_libraries(${EXECUTABLE_NAME} MyLibrary1
MyLibrary2
${Boost_PROGRAM_OPTIONS_LIBRARY})
install(TARGETS ${EXECUTABLE_NAME}
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin
COMPONENT ${EXECUTABLE_NAME})
At the end I would like to find with this kind of CMakeLists.txt my two libraries, my executable and Boost_PROGRAM_OPTIONS_LIBRARY in the install folder and in my package.
What I have found, but I did not succeed to use:
BundleUtilities.cmake, here an example: https://cmake.org/Wiki/BundleUtilitiesExample.
As you can see it seems to do the perfect jobs, but it's complaining about "external prerequisites". I personally found that normal and I don't understand how to fix that. (Note: it succeeds in finding and copying the dependencies into a same folder before failing, Doc: https://cmake.org/cmake/help/v3.5/module/BundleUtilities.html)
GetPrerequisites.cmake. get_prerequisites is a lower level function that allow you to get the dependencies. The thing is, I don't any good way/best practice to use it.
Thanks,

I personally would make a library out of all of your dependencies with add_library(MyLibrary1 STATIC/SHARED/INTERFACE) and for boost most probably add_library(boost INTERFACE) followed by adding any include directories and linking the .as or .sos to them.
I would then link all my dependencies to MyExecutable as you have above. And at any point you can call the install function on all of these targets one at a time.
If you don't like repeating
install(TARGETS target
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin
COMPONENT target
)
then you could break it out in to a function, something like this
function(INSTALL_CUSTOM target
install(TARGETS target
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin
COMPONENT target
)
endfunction()
and then you could just call INSTALL_CUSTOM(MyLibrary1) or INSTALL_CUSTOM(boost).

Related

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.

How to link a target which might be a shared library when installing a library with CMake

I have a library that can be build as shared or static and uses another library that is already on the system and can be either shared or static too. The former is a C++ wrapper for the latter, which is a C library
What would the correct target_link_libraries call be?
If I do target_link_libraries(cpplib PUBLIC clib) then I'm also propagating the include directories of clib to consumers of cpplib but those should only use the interface provided by the C++ library which has no mention of the clib in its headers.
If I do target_link_libraries(cpplib PRIVATE clib) then the include path of consumers is clearer, but when I now do the export-install-install_exports dance of installing a CMake project with its config file and *targets.cmake then I have a problem: If both libraries are SHARED then the installed targets of cpplib have no mention of clib and linking against that consequently fails with libc.so, needed by libcpp.so, not found and many undefined references.
Both libraries use targets and especially under Windows there is no way to find whether clib is shared or not.
What is the idiomatic way?
CMakeLists.txt
cmake_minimum_required(VERSION 3.8.0)
project(cpplib)
find_package(clib REQUIRED)
add_library(cpplib SHARED cpplib.cpp)
target_link_libraries(cpplib PRIVATE clib::clib)
target_include_directories(cpplib PUBLIC $<INSTALL_INTERFACE:include/cpplib>)
include(CMakePackageConfigHelpers)
set(PROJECT_CONFIG_FILE "${CMAKE_CURRENT_BINARY_DIR}/cpplibConfig.cmake")
configure_package_config_file("Config.cmake.in" ${PROJECT_CONFIG_FILE} INSTALL_DESTINATION "lib/cmake")
install(
TARGETS cpplib
EXPORT cpplibTargets
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
RUNTIME DESTINATION bin
)
install(FILES cpplib.h DESTINATION include/cpplib)
install(FILES ${PROJECT_CONFIG_FILE} DESTINATION "share/cpplib")
install(EXPORT cpplibTargets NAMESPACE "cpplib::" DESTINATION "share/cpplib")
Config.cmake.in
#PACKAGE_INIT#
find_package(BZip2 REQUIRED)
include("${CMAKE_CURRENT_LIST_DIR}/#PROJECT_NAME#Targets.cmake")
check_required_components("#PROJECT_NAME#")
"user" project CMakeLists.txt
cmake_minimum_required(VERSION 3.8.0)
project(user)
find_package(cpplib REQUIRED)
add_executable(user main.cpp)
target_link_libraries(user cpplib::cpplib)
Edit: As I found it strange, that it works, when I add the executable in the same CMakeLists as the library (so not using the installed version) I dug deeper: I just noticed that CMake clears the rpath on installing:
-- Installing: /tmp/install/lib/cpplib.so
-- Set runtime path of "/tmp/install/lib/cpplib.so" to ""
Found this while comparing the output of ldd of the installed library and the one in the build tree where the latter is able to resolve the clib. Why is that?

Including external libraries in cpack output

Im currently working on a cmake project which is making use of external libraries which are being imported through the find_package function. My question revolves around cpack and how I am supposed to go about adding the found packages into the cpack output. For example if I use this
find_package(OpenGL REQUIRED)
add_executable(Example_App MACOSX_BUNDLE src/main.cpp)
target_include_directories(Example_App SYSTEM PUBLIC ${OPENGL_INCLUDE_DIR})
target_link_libraries(Example_App PUBLIC ${OPENGL_LIBRARIES})
install(TARGETS Example_App
BUNDLE DESTINATION "."
RUNTIME DESTINATION bin)
If I then run cmake .. followed by make and make package my output from cpack (I can include an example of my cpack code if needed too) would then be.
ExampleApp-linux.tar.bz2
- bin
-- Example_App
- Share
-- Resource files
Rather than something like:
- bin
-- Example_App
- Lib
-- OpenGL.a
- Share
-- Resource files
Any help would be much appreciated!
You would have to explicitly add an install rule for the external project libraries.
For example:
install(FILES ${OPENGL_LIBRARIES}
RUNTIME DESTINATION bin COMPONENT RuntimeLibraries
LIBRARY DESTINATION bin COMPONENT RuntimeLibraries
ARCHIVE DESTINATION Lib COMPONENT Development
)
Or you could use a specific library reference like ${OPENGL_gl_LIBRARY} instead of ${OPENGL_LIBRARIES}. See Modules/FindOpenGL.cmake for more details.

How to make cmake find a shared library in a subfolder

I'm trying to learn how to make a shared library. And the following seems to work (please comment if you have some feedback to this method, I basically have no idea what I'm doing).
In my library project, I've put the header files into a folder named "include", and the source files into "src".
My library's CMakeLists.txt:
cmake_minimum_required(VERSION 2.4.0)
project(mycustomlib)
# Find source files
file(GLOB SOURCES src/*.cpp)
# Include header files
include_directories(include)
# Create shared library
add_library(${PROJECT_NAME} SHARED ${SOURCES})
# Install library
install(TARGETS ${PROJECT_NAME} DESTINATION lib)
# Install library headers
file(GLOB HEADERS include/*.h)
install(FILES ${HEADERS} DESTINATION include)
My application's CMakeLists.txt:
cmake_minimum_required(VERSION 2.4.0)
project(myprogram)
# Find source files
file(GLOB SOURCES src/*.cpp)
# Create executable
add_executable(${PROJECT_NAME} ${SOURCES})
# Find and link library
find_library(MYCUSTOMLIB mycustomlib)
target_link_libraries(${PROJECT_NAME} ${MYCUSTOMLIB})
And this is working. The problem is that I want to put both the headers and the library into subfolders (specifically: /usr/local/include/mycustomlib/ for the headers, and /usr/local/lib/mycustomlib/ for the library).
So this is my attempt:
My library's new CMakeLists.txt:
cmake_minimum_required(VERSION 2.4.0)
project(mycustomlib)
# Find source files
file(GLOB SOURCES src/*.cpp)
# Include header files
include_directories(include)
# Create shared library
add_library(${PROJECT_NAME} SHARED ${SOURCES})
# Install library
install(TARGETS ${PROJECT_NAME} DESTINATION lib/${PROJECT_NAME})
# Install library headers
file(GLOB HEADERS include/*.h)
install(FILES ${HEADERS} DESTINATION include/${PROJECT_NAME})
My application's new CMakeLists.txt:
cmake_minimum_required(VERSION 2.4.0)
project(myprogram)
# Find source files
file(GLOB SOURCES src/*.cpp)
# Create executable
add_executable(${PROJECT_NAME} ${SOURCES})
# Find and link library
find_library(MYCUSTOMLIB mycustomlib/mycustomlib)
target_link_libraries(${PROJECT_NAME} ${MYCUSTOMLIB})
And this is not working. Now, I'm forced to specify the .so file of the library like this:
find_library(MYCUSTOMLIB mycustomlib/libmycustomlib.so)
How come?
I'll deal with your actual problem first and offer additional comments after that. Technically speaking, you are asking CMake to find a library named mycustomlib/mycustomlib, but what you really want to say is you want find mycustomlib and it can be found in a subdirectory called mycustomlib. A couple of alternative ways to call find_library() to achieve this for your second case would be:
find_library(MYCUSTOMLIB mycustomlib PATH_SUFFIXES mycustomlib)
find_library(MYCUSTOMLIB mycustomlib PATHS /usr/local/lib/mycustomlib)
The latter is making more assumptions than it should about where you have the library installed, so I'd favour the first option. The first option assumes CMake would already find libraries in /usr/local/lib, which it seems it is from your question. You can influence where CMake looks for libraries by modifying CMAKE_PREFIX_PATH and CMAKE_LIBRARY_PATH. I'd expect either of the above options to make your second case work.
Now to other observations. You've requested a very old minimum CMake version in the first line of each of your CMakeLists.txt files. You probably want to consider at the very least making this 2.8 (personally, I'd suggest more like 3.2 or later, but it depends on what your project needs to support).
You have used file globbing to obtain your list of sources and headers. This is not robust and should generally be avoided (see a discussion of this here). You will see plenty of example code use method this for simplicity, but it is not recommended for real world projects (the CMake documentation even says not to use it). Explicitly list out your source and header files individually if you want robust builds.
If you are happy to require CMake 2.8.11 or later (and you should be these days), rather than calling include_directories() which makes everything pick up the header search path you specified, you should prefer to attach the search path requirement to the target that needs it. You do this with target_include_directories(). The equivalent of your code above would be:
target_include_directories(${PROJECT_NAME} PUBLIC include)
This gives much better control of your inter-target dependencies as your project grows in size and complexity. For a more in-depth discussion of this topic, see this article and perhaps also this one (disclosure: I wrote both articles).
Are your library and program totally separate source code repositories? Can they be built in the same project? You can build multiple targets in one CMakeLists.txt file. The project name doesn't have to have any relationship to the names of any of the targets (you often see the PROJECT_NAME variable re-used for the target name in simple examples, which is unfortunate since it suggests a relationship between the two, but for all but simple projects this won't be the case). If they are in the same repository, building them together would be a much simpler build since you wouldn't have to install the library for the executable to find it and link to it.
If they must be built in separate projects, then something like the following for the application's project should get you close:
cmake_minimum_required(VERSION 2.8.11)
project(myprogram)
# List your program's sources here explicitly
add_executable(myprogram src/foo.cpp src/bar.cpp)
# Find and link library
find_library(MYCUSTOMLIB mycustomlib PATH_SUFFIXES mycustomlib)
target_link_libraries(myprogram PUBLIC ${MYCUSTOMLIB})
# Find library's headers and add it as a search path.
# Provide the name of one header file you know should
# be present in mycustomlib's include dir.
find_path(MCL_HEADER_PATH mycustomlib.h PATH_SUFFIXES mycustomlib)
target_include_directories(myprogram PUBLIC ${MCL_HEADER_PATH})
For extra points, you could try to confirm that the header path is in the same area as the library by checking the common path prefix, or you could just derive
the MCL_HEADER_PATH from the MYCUSTOMLIB path by assuming a directory structure. Both approaches have advantages and drawbacks. If you want to explore the latter, the get_filename_component() command will be your friend.
Hopefully that points you in the right direction.

How does CMake know which prefixes and suffixes to add to shared libraries?

I am trying to use INSTALL in CMake to copy some external binaries to an install directory. My code goes like:
SET(SimTK_SHARED_LIBS
SimTKsimbody
SimTKmath
SimTKcommon
SimTKmolmodel
)
INSTALL(TARGETS ${SimTK_SHARED_LIBS}
LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/lib
)
I get this error:
CMake Error at CMakeLists.txt:216 (INSTALL):
install TARGETS given target "SimTKsimbody" which does not exist in this
directory.
this is in spite of putting files called both libSimTKsimbody.so and (incorrectly) SimTKsimbody in the current directory as well as in the library directory.
Interestingly, this:
SET(SHARED_MMB_TARGET MMBlib)
ADD_LIBRARY(${SHARED_MMB_TARGET} SHARED
${MMB_LIBRARY_SOURCE_FILES}
${MMB_HEADER_FILES})
SET_TARGET_PROPERTIES(${SHARED_MMB_TARGET}
PROPERTIES
COMPILE_FLAGS "-DMMB_BUILDING_SHARED_LIBRARY"
PROJECT_LABEL "MMBlib (dynamic)")
TARGET_LINK_LIBRARIES(${SHARED_MMB_TARGET}
${SimTK_SHARED_LIBS_D}
${SimTK_SHARED_LIBS}
${OpenMM_SHARED_LIBS_D}
${OpenMM_SHARED_LIBS}
${SimTK_GENERAL_LIBS})
INSTALL(TARGETS ${SHARED_MMB_TARGET}
LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/lib
RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin
)
.. works fine. It installs libMMBlib.so in ${CMAKE_INSTALL_PREFIX}/lib as it should. Does this mean that INSTALL will only work for this if I issue ADD_LIBRARY and/or SET_TARGET_PROPERTIES? The SimTK_SHARED_LIBS are compiled separately, I really do not want to compile them here.
I have thought about using INSTALL FILES, and just writing code to process the library names for each operating system. However I am convinced that CMake has the means to do this for me easily and elegantly.
Many thanks
Sam
Yes, you should use INSTALL(FILES) for install external libraries files.
CMake uses CMAKE_SHARED_LIBRARY_PREFIX and CMAKE_SHARED_LIBRARY_SUFFIX as default prefix and suffix for libraries created with add_library(... SHARED), so you may expect these components from external library:
INSTALL(FILES /path/to/library/${CMAKE_SHARED_LIBRARY_PREFIX}SimTKsimbody${CMAKE_SHARED_LIBRARY_SUFFIX}
...)
Also you may use FIND_LIBRARY for automatic(and nice) check of your expectations about library suffix and prefix:
FIND_LIBRARY(SIMTK_SIMBODY_LIB
${CMAKE_SHARED_LIBRARY_PREFIX}SimTKsimbody${CMAKE_SHARED_LIBRARY_SUFFIX}
PATH /path/to/library)
INSTALL(FILES ${SIMTK_SIMBODY_LIB} ...)