Install target dependencies with cmake - boost

I have a project that is supposed to install a static library and all its dependencies. One of these dependencies is boost.
I have something like the following but it doesn't work:
cmake_minimum_required(VERSION 3.9.0)
project(install_test)
find_package(Boost REQUIRED COMPONENTS system)
set(Boost_USE_STATIC_LIBS TRUE)
add_executable(test src/main.cpp)
target_link_libraries(test Boost::boost)
add_dependencies(test Boost::boost)
install(TARGETS test DESTINATION ${CMAKE_INSTALL_LIBDIR})
install(TARGETS Boost::boost DESTINATION ${CMAKE_INSTALL_LIBDIR})
Note: boost can be found, if I remove the latest install statement everything works.
How can do this?

Since the installation is likely to happen to the same machine (on wich your library is to be compiled), and the Boost library is already installed (since it can be found), you should export your library with its dependencies via Config.cmake file. All the dependencies are to be found via find_dependency (I do not think that you want to copy all the boost libraries). It will search for required packages on linking with your installed library.
${PROJECT_NAME}Config.cmake.in file
#PACKAGE_INIT#
include(CMakeFindDependencyMacro)
find_dependency(glad REQUIRED)
find_dependency(glm REQUIRED)
include("${CMAKE_CURRENT_LIST_DIR}/#targets_export_name#.cmake")
check_required_components(gl_traits)
CMakeLists.txt
#... define here sources and public headers
add_library(${PROJECT_NAME}
STATIC
${PUBLIC_HEADERS}
${SOURCES}
)
set_target_properties(${PROJECT_NAME}
PROPERTIES
OUTPUT_NAME ${PROJECT_NAME}
DEBUG_POSTFIX "_d"
)
target_link_libraries(${PROJECT_NAME}
PUBLIC
Boost::boost
)
target_include_directories(${PROJECT_NAME}
PUBLIC
$<INSTALL_INTERFACE:${INSTALL_INCLUDEDIR}/${PROJECT_NAME}>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/${PROJECT_NAME}>
PRIVATE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
)
set(targets_export_name ${PROJECT_NAME}Targets)
set(project_config ${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake)
set(version_config ${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake)
include(CMakePackageConfigHelpers)
write_basic_package_version_file(
${version_config}
VERSION
${PROJECT_VERSION}
COMPATIBILITY SameMajorVersion
)
configure_package_config_file(
${PROJECT_SOURCE_DIR}/cmake/${PROJECT_NAME}Config.cmake.in
${project_config}
INSTALL_DESTINATION
${INSTALL_CMAKEDIR}
)
install(TARGETS
${PROJECT_NAME}
EXPORT
${targets_export_name}
ARCHIVE
DESTINATION ${INSTALL_LIBDIR}
COMPONENT lib
LIBRARY
DESTINATION ${INSTALL_LIBDIR}
COMPONENT lib
RUNTIME
DESTINATION ${INSTALL_BINDIR}
COMPONENT bin
)
install(EXPORT
${targets_export_name}
DESTINATION
${INSTALL_CMAKEDIR}
)
install(FILES ${PUBLIC_HEADERS} DESTINATION ${INSTALL_INCLUDEDIR}/${PROJECT_NAME} COMPONENT dev)
install(FILES
${project_config}
${version_config}
DESTINATION
${INSTALL_CMAKEDIR}
)
If you need to install target's files use $<TARGET_FILE:target_name>

Related

Installation issue with a `FetchContent`ed library

I am trying to install and export the target defined by the following CMakeLists.txt in a Windows environment. It uses FetchContent to download a library from github. I can build the library with no problem and generate nfd.lib. My goal is to use this library in my other projects.
cmake_minimum_required(VERSION 3.24)
project(mynfd VERSION 1.0.0)
include(FetchContent)
FetchContent_Declare(nfd
GIT_REPOSITORY https://github.com/btzy/nativefiledialog-extended.git
GIT_TAG c++-version)
FetchContent_MakeAvailable(nfd)
At this point, nfd.lib is successfully built in _deps/nfd-build/src/nfd.lib.
When I add the following install commands to the CMakeLists.txt above, I run into issues.
# Installation
include(GNUInstallDirs)
install(TARGETS nfd
EXPORT ${PROJECT_NAME}Targets
FILE_SET HEADERS
PUBLIC_HEADER DESTINATION ${nfd_SOURCE_DIR}/src/include
)
install(EXPORT ${PROJECT_NAME}Targets
FILE ${PROJECT_NAME}Targets.cmake
NAMESPACE ${PROJECT_NAME}::
DESTINATION lib/cmake/${PROJECT_NAME})
I get the following error messages.
CMake Error in cmake-build-debug-visual-studio/_deps/nfd-src/src/CMakeLists.txt:
Target "nfd" INTERFACE_INCLUDE_DIRECTORIES property contains path:
"C:/Dev/myproject/src/test/cmake-build-debug-visual-studio/_deps/nfd-src/src/include/"
which is prefixed in the build directory.
CMake Error in cmake-build-debug-visual-studio/_deps/nfd-src/src/CMakeLists.txt:
Target "nfd" INTERFACE_INCLUDE_DIRECTORIES property contains path:
"C:/Dev/myproject/src/test/cmake-build-debug-visual-studio/_deps/nfd-src/src/include/"
which is prefixed in the build directory.Target "nfd"
INTERFACE_INCLUDE_DIRECTORIES property contains path:
"C:/Dev/myproject/src/test/cmake-build-debug-visual-studio/_deps/nfd-src/src/include/"
which is prefixed in the source directory.
How can I resolve this problem?

Executable built using cmake cannot find shared library

I have been working on a Visual Studio 2019 project with the objective of porting the application to Centos 8. I'm using a CMake project in VS2019 with remote building on my Centos machine. Basically, all the files are transferred to the remote machine and the project is built. My project directory looks as
Child
| CMakeLists.txt
| source
|__public
| headers
| libs
Parent
| CMakeLists.txt
| source
Child generates a shared library (.so) and some public headers which are to be linked in Parent. The so files and public headers are present in the public directory of Child. The CMake files of both the files are as follows
Child/CMakeLists.txt
set(PROJECT_NAME "Child")
set(Header_Files
#set of public headers and source headers
)
set(Source_Files
#set of source files
)
set(ALL_FILES
${Header_Files}
${Source_Files}
)
if (UNIX)
#Target is an SO
add_library(${PROJECT_NAME} SHARED ${ALL_FILES})
set(BOOST_INCLUDEDIR "${CMAKE_HOME_DIRECTORY}/Boost")
set(BOOST_LIBRARYDIR "${CMAKE_HOME_DIRECTORY}/Boost/libs/linux64-clang-9.0.0/static/release")
find_package(Boost REQUIRED)
if (Boost_FOUND)
target_include_directories(${PROJECT_NAME} PRIVATE ${Boost_INCLUDE_DIRS})
target_link_libraries(${PROJECT_NAME} ${Boost_LIBRARIES})
endif()
set(XercesC_INCLUDE_DIR "${CMAKE_HOME_DIRECTORY}/Xercesc/Linux-Clang-Release/include")
set(XercesC_LIBRARY "${CMAKE_HOME_DIRECTORY}/Xercesc/Linux-Clang-Release/lib64")
find_package(XercesC REQUIRED)
if (XercesC_FOUND)
target_include_directories(${PROJECT_NAME} PRIVATE ${XercesC_INCLUDE_DIR})
target_link_libraries(${PROJECT_NAME} "${CMAKE_HOME_DIRECTORY}/Xercesc/Linux-Clang-Release/lib64/libxerces-c.so")
endif()
endif()
Parent/CMakeLists.txt
set(PROJECT_NAME "Parent")
set(Header_Files
#set of headers
)
set(Source_Files
#set of source files
)
set(ALL_FILES
${Header_Files}
${Source_Files}
)
if (UNIX)
set(CHILD_HEADERS "${CMAKE_HOME_DIRECTORY}/Child/public/headers")
set(CHILD_LIB_DIR "${CMAKE_BINARY_DIR}/Child/libs")
link_directories(${CHILD_LIB_DIR})
add_executable(${PROJECT_NAME} ${ALL_FILES})
target_include_directories(${PROJECT_NAME} PRIVATE ${CHILD_HEADERS})
target_link_libraries(${PROJECT_NAME} PRIVATE Child)
#Copy libChild.so to Parent executable directory
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD COMMAND
cp "${CHILD_LIB_DIR}/libChild.so"
"${CMAKE_CURRENT_BINARY_DIR}/libChild.so"
)
endif()
Problem
My objective is to have a build for my customer. Hence, I need to package all the executables and libs under a single zip folder. However, upon doing so I find the linker paths to have taken absolute path values of the sort
$ldd Parent
libChild.so => /root/.vs/Project/829a6827-2641-4552-a2fb-abec91ed47a2/out/build/Linux-Clang-Release/Child/libChild.so (0x00007f6934908000)
However, concerning my final build folder will be like this
Parent (executable)
libChild.so
libboost_regex...
libxerces... etc. etc.
I kinda want a relative path link to the same directory
libChild.so => ./libChild.so
Thus, when I export as a zip to another machine, it is unable to find the shared library. I have tried a couple of things (using other CMake functions) as well as copying the library into the executable directory first and then linking. However, it still links it as an absolute path which is likely to cause the same issue again. I have gone through the CMake documentation and perused through cmake-packages and relocatable packages but unable to fix the problem.
The issue can be resolved by placing these two statements in the CMake files for Project Child and Project Parent.
set(CMAKE_INSTALL_RPATH ".")
set(CMAKE_BUILD_WITH_INSTALL_RPATH true)
Details about what these variables mean can be found in the CMake documentation.
Although this resolves the issue at the moment, however, this is not the correct approach for creating builds for a customer, as pointed out here.

cmake import exist SimpleAmqp library

Now my CMakeList.txt looking like this.
cmake_minimum_required(VERSION 3.6)
project(RabbitMQClient)
set(CMAKE_CXX_STANDARD 11)
set(SOURCE_FILES main.cpp)
include_directories(src)
include_directories(dependentFile)
add_executable(RabbitMQClient ${SOURCE_FILES})
find_library(SimpleAmqpClient SimpleAmqpClient.2.lib PATHS ./SimpleAmqpClientLib/SimpleAmqpClient.2.lib)
set(IMPORTED_IMPLIB ./SimpleAmqpClientLib)
target_link_libraries(RabbitMQClient PUBLIC SimpleAmqpClient)
when Linking the RabbitMQClient it complains.
cannot find -lSimpleAmqpClient
I want to use the SimpleAmqpClient library in the project,however not quite familiar with cmake not sure the find_library,IMPORTED_IMPLIB,PUBLIC in target_link_libraries was correctly used.Any help would be appreciate.
You have messed up with variables, targets and properties.
Proper usage of IMPORTED libraries for linking would be:
# This command sets *variable* SimpleAmqpClient_LIBRARY
find_library(SimpleAmqpClient_LIBRARY SimpleAmqpClient.2.lib
PATHS ${CMAKE_SOURCE_DIR}/SimpleAmqpClientLib # Specify a *directory*, not a library *file*
)
# Next, create an IMPORTED *target*
add_library(SimpleAmqpClient SHARED IMPORTED)
# And set IMPORTED_LIB *property* for this target
set_target_properties(SimpleAmqpClient PROPERTIES IMPORTED_LIB ${SimpleAmqpClient_LIBRARY})
# Then use library *target* for linking with
target_link_libraries(RabbitMQClient PUBLIC SimpleAmqpClient)
However, there some simplifications which could be done:
Normally, find_library is used when you don't know complete path to the library file. E.g., its directory may be different on different machines, or its prefix/extension may be different on different plaforms.
If you know complete path to the library, just use this path directly (e.g., assign it to the variable).
Normally, property IMPORTED_LOCATION is used for specify library to link with. Property IMPORTED_LIB is specific for Windows .dlls, when linking requires not a library file (.dll), but some other one (.lib).
However, CMake perfectly understands .lib file in IMPORTED_LOCATION property even for Windows .dlls, so your code need not distinguish SHARED Windows libraries from others: just use IMPORTED_LOCATION property in all cases.
Simplified version of the code:
# Create an IMPORTED library *target*
add_library(SimpleAmqpClient IMPORTED)
# Set IMPORTED_LOCATION *property* for this target
set_target_properties(SimpleAmqpClient PROPERTIES
IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/SimpleAmqpClientLib/SimpleAmqpClient.2.lib)
# Then use library *target* for linking with
target_link_libraries(RabbitMQClient PUBLIC SimpleAmqpClient)

Adding a rpath item to the build tree generated executable

I always run my executable in the build tree (I don't run it from a cmake "install"). A library, let's call it fruit, is built as a framework:
add_library( fruit SHARED ${FRUIT_SOURCES} )
set_target_properties( fruit PROPERTIES FRAMEWORK TRUE)
set_target_properties( fruit PROPERTIES BUILD_WITH_INSTALL_RPATH TRUE
INSTALL_NAME_DIR "#rpath/Frameworks" )
Now I want to set a custom rpath for main application (called executable) with cmake. I thought I could use the INSTALL_RPATH target property of executable to define my rpaths for the generated program, but this seems to only work for an installed executable (remember I always run my application in the cmake build folder):
# this rpath is not shown in the generated executable (otool -l -v executable):
set_target_properties( executable PROPERTIES INSTALL_RPATH "#executable_path/lib/" )
How define/add a rpath item to the program generated in the build tree?
PS. This library fruit is just an example of my actual problem. The library is created in a sub cmake project (a git submodule) which adds the BUILD_WITH_INSTALL_RPATH property to the library. But I have the possibility to change the code for this project. Is there a better way to let my executable work in the build tree?
This is not an answer to the main question, but I figured out an answer to the post scriptum. This is a better solution than the one searced for in the main question, but I let that question persist.
set( CMAKE_MACOSX_RPATH TRUE )
set( CMAKE_SKIP_BUILD_RPATH FALSE )
set( CMAKE_BUILD_WITH_INSTALL_RPATH FALSE )
################################################################################
# this is more interesting later when we implement install (i.e. creating a
# bundle on macOS)in this CMakeLists!
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
# the RPATH to be used when installing, but only if it's not a system directory
list(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${CMAKE_INSTALL_PREFIX}/lib" isSystemDir)
if("${isSystemDir}" STREQUAL "-1")
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
endif("${isSystemDir}" STREQUAL "-1")
################################################################################
# add the dynamic library
add_library( fruit SHARED ${FRUIT_SOURCES} )
set_target_properties( fruit PROPERTIES FRAMEWORK TRUE)
# remove the properties INSTALL_NAME_DIR and BUILD_WITH_INSTALL_RPATH:
#set_target_properties( fruit PROPERTIES BUILD_WITH_INSTALL_RPATH TRUE
# INSTALL_NAME_DIR "#rpath/Frameworks" )
The code is taken from here. Also see this blog post.

Building a library with an executable that uses it

I have a library "lib" and an executable "demo".
The issue I'm having is that "demo" can't get lib_INCLUDES and lib_LIBS lib variables, set by "lib". I want them set because in demo.h I do #include <lib.h> and it fails to find the include. I want CMake to make lib.h a global include, i.e. pass -I /path/to/the/dir/with/lib.h/ to the compiler.
Source tree:
- src/
- CMakeLists.txt
- lib/
- CMakeLists.txt
- lib.cpp
- lib.h
- demo/
- CMakeLists.txt
- demo.cpp
- demo.h
src/CMakeLists.txt
cmake_minimum_required(VERSION 2.8)
project(test)
add_subdirectory(lib)
add_subdirectory(demo)
src/lib/CMakeLists.txt
add_library(lib SHARED lib.h lib.cpp)
target_include_directories(lib ${CMAKE_CURRENT_SOURCE_DIR})
# defined for later use in src/demo/CMakeLists.txt
set(lib_INCLUDES ${CMAKE_CURRENT_SOURCE_DIR})
set(lib_LIBS lib)
src/demo/CMakeLists.txt
include_directories(${lib_INCLUDES})
add_executable(demo demo.h demo.cpp)
target_link_libraries(demo ${lib_LIBS})
# empty! why?
message(STATUS ${lib_INCLUDES})
Tell me how "demo" can access lib_INCLUDES and lib_LIBS libraries set by "lib", they seem to be empty in src/demo/CMakeLists.txt.
I don't want src/demo/CMakeLists.txt contain paths to "lib", only src/lib/CMakeLists.txt should know details of "lib" and it should define nice variables for use in src/demo/CMakeLists.txt that hide all those details.
Please include a working CMakeLists.txt in your answer, if possible.
Please read carefully documentation of SET command. Your variable lib_INCLUDES has local scope to lib\CMakeLists.txt, so for example you can CACHE it.
Better way is to populate INTERFACE_INCLUDE_DIRECTORIES of lib by
target_include_directories(lib INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
so the target_link_libraries automatically appends lib's include directories.

Resources