I have some executable which depends on config files which relative path are setup in the source.
The executable links against a library, which is created in the same project.
What I am hoping to achieve, is having the executable working out of the box after installation, i.e. the installation would copy the executable, config files and library in a suitable location, and the executable would be linked to the library.
What I have for the moment:
install(TARGETS ${test_executables} ${PROJECT_NAME}
RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin
ARCHIVE DESTINATION ${CMAKE_INSTALL_PREFIX}/lib
LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/lib)
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/plot"
DESTINATION .)
${PROJECT_NAME} is the library, plot is the folder in which the config files are.
What happens after install is that all files are in the right place in the install folder, but the executable does not find the library.
ps:
I tried to add this before :
set(CMAKE_SKIP_BUILD_RPATH FALSE)
set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) # tried also with TRUE
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
set(CMAKE_INSTALL_RPATH ${CMAKE_INSTALL_PREFIX}/lib)
but this did not work
It is INSTALL_RPATH target's property which affects on RPATH for installed executable. This property is set to value of variable CMAKE_INSTALL_RPATH at target creation time.
So, variable CMAKE_INSTALL_RPATH needs to be set before add_executable() call for make effect on the target.
Most of global variables and target-unaware commands affect on the target only at target creation time.
There are exceptions, like command include_directories(), which affects on all targets created in the current directory. But preparing everything before creation of the target could be good practice.
I met the same issue, and just added
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
before add_library and add_executable command, then it worked.
And you can find more details here.
Related
There is a static library called revolta which is being built and then installed into a sysroot:
set( CMAKE_INSTALL_PREFIX <path to sysroot> )
# ReVolta c++ library name
set( TARGET_LIBREVOLTA "revolta" )
add_library( ${TARGET_LIBREVOLTA} STATIC )
target_include_directories( ${TARGET_LIBREVOLTA}
PUBLIC
# Once the librevolta targets are being exported, this include directory in which the lib is installed is used
$<INSTALL_INTERFACE:${CMAKE_INSTALL_PREFIX}/include>
PRIVATE
# Include directory used privately just to build the library itself
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}>
)
target_sources( ${TARGET_LIBREVOLTA}
PUBLIC
...
)
Later then once the librevolta is built, it is installed into the sys root using:
# Install all the revolta headers into include directory and copy the built library
install( TARGETS ${TARGET_LIBREVOLTA} EXPORT ${TARGET_LIBREVOLTA}
FILE_SET HEADERS DESTINATION "${CMAKE_INSTALL_PREFIX}/include"
ARCHIVE DESTINATION "${CMAKE_INSTALL_PREFIX}/lib"
)
and the connected custom command:
# Once the librevolta is built, install it to the sysroot as specified by 'install()' commands
add_custom_command( TARGET ${TARGET_LIBREVOLTA} POST_BUILD COMMAND ${CMAKE_COMMAND} ARGS --install . )
So far so good. This works as intended, once CMake builds the "revolta" target, it is built and installed into the sysroot as installed using the ${CMAKE_INSTALL_PREFIX}.
My problem is once I try to add the target as the linked one in other lib/executable, it includes somehow automatically the librevolta source path into includes and links the library using the relative path in the build directory rather than the one installed into sysroot as performed in the step right after the librevolta build.
Some other lib/executable:
target_link_libraries( ${APP_EXECUTABLE}
PRIVATE
revolta
)
Once being built, the include path -I/home/martin/git/revolta/source/librevolta is added (the source location) even though it is stated as PRIVATE in the snipped above:
PRIVATE
# Include directory used privately just to build the library itself
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}>
and only the ${CMAKE_INSTALL_PREFIX}/include is made public...
Additionally, the library is taken from the build tree rather than from the location where it is installed:
../../librevolta/librevolta.a
instead of
/home/martin/git/revolta/sysroot/lib/librevolta.a
Could you please advice me how to correctly set the revolta target the way it correctly uses its sources for building itself but once used elsewhere it provides the sysroot installed headers and built library from the same location (respecting the standard locations)?
HINT: I also tried to remove the revolta target from the app completely, specifying only to use the sys root (gcc option --sysroot=/home/martin/git/revolta/sysroot), it works fine correct headers and lib is used BUT once the librevolta is not built and installed, the target is not run prior to app build as the dependency is not defined then...
TL;DR: You need to do what's done here:
How to create a ProjectConfig.cmake file
I see a few issues with these CMakeLists.txt files but they aren't related to your problem, because if I understand correctly what you are trying to do here, then there is no problem and it is used as intended.
Let me clarify:
You have a library project that has it's own CMakeLists.txt, where you define the target revolta
You have an executable project that has it's own CMakeLists.txt, where you define your executable target and then you add the revolta target via add_subdirectory() and target_link_libraries(my_executable revolta)
If that's the case then this is just bad:
# Once the librevolta is built, install it to the sysroot as specified by 'install()' commands
add_custom_command( TARGET ${TARGET_LIBREVOLTA} POST_BUILD COMMAND ${CMAKE_COMMAND} ARGS --install . )
Forcing your build to automatically install this library is not the way to go, ever (you for example, need elevated privileges to build it in the first place, because of this command and that poses a security risk).
That being said what is happening is perfectly fine, because from the perspective of the my_executable's CMakeLists.txt you are still building i.e. you use the BUILD_INTERFACE. It is however something you do not want to do.
What instead you want to do is:
Create generator files for a revoltaConfig.cmake file. For that I will refer you to this tutorial:
How to create a ProjectConfig.cmake file
After you create such file, i.e. after building and installing revolta. You will (in the process) also create a revoltaConfig.cmake file. Which helps you populate the my_executable project via find_package(revolta).
The above is probably what you are interested in.
The generator expressions that you use to distinguish BUILD_INTERFACE and INSTALL_INTERFACE are mainly for header file locations (or other linked libraries). Because when you build the library the header files can have a different structure then when you install it (as you already know). And as such work perfectly fine in your CMakeLists.txt, because when you think about it:
You don't want to copy changes to your library files (into the install directory) just to test ongoing development (features/bugfixes) in your executable.
And during the build of the executable if your building another target then IT IS NOT INSTALLED but rather BEING BUILT. And you are most likely adding it as a built target.
So to sum up what would most likely happen here (using your old CMakeLists.txt) is that
The moment you start building the executable which adds the target library as a dependency via add_subdirectory you are implicitly using BUILD_INTERFACE because you are building.
If you were to then install both the executable and the library it would again use the correct install paths, i.e. you would then implicitly start using INSTALL_INTERFACE.
You could hack it without the projectConfig file using the same generator expressions by mixing them up, but I don't recommend it, because then the CMakeLists.txt wouldn't work without doing some weird steps beforehand.
Basically I want CMake to copy dependency's dll to the same directory of the executable. Suppose I have the following directory structure:
my-project/
CMakeLists.txt
lib/
CMakeLists.txt
... # Some source files
app/
CMakeLists.txt
... # Some source files
The library lib depends on some third party dll, say foo.dll. The executable in app, say app.exe, depends on lib.
I've written a FindFOO.cmake to make the third party library foo.dll an imported target named foo.
Now when I compile app, in order to run the executable, foo.dll is required to be in the same directory as app.exe. How can this be achieved automatically with cmake? And what if I want to use CPack to package the application into an installer?
CMAKE_RUNTIME_OUTPUT_DIRECTORY is your friend.
If this variable is created before creating some target, if the target is RUNTIME, it will define where the output of the target will be placed.
In your case, it can be used to force foo.dll and app.exe to be in the same folder. Your root CMakeLists.txt should look like this:
cmake_minimum_required(VERSION 3.15)
project(foo_and_app)
# app.exe and foo.dll will be in bin subfolder of build dir
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
add_subdirectory(lib)
add_subdirectory(app)
#not connected to placement, but only to package creation
include(CPack)
It should be noted that this variable is used to initialize the properties of the targets added, meaning that everything may also be achieved by directly manipulating appropriate target properties.
Regarding packaging, what you ask is possible, regardless of the placement of runtime targets, by using install cmake statement. In lib/CMakeLists.txt you should add something like this:
# suppose that the target named `foo`,
# i.e. it is added by add_library(foo SHARED .....)
install(TARGETS foo
RUNTIME DESTINATION bin
)
same should be done for app/CMakeLists.txt:
# suppose that the target named `app`,
# i.e. it is added by add_executable(app .....)
install(TARGETS app
RUNTIME DESTINATION bin
)
If you have these install statements, the final destination will be bin folder within the chosen install folder.
In the end, here are the links for CMake documentation describing:
CMAKE_RUNTIME_OUTPUT_DIRECTORY variable
RUNTIME cmake targets
install(TARGETS ...)
I'd like to add another directory to a target's BUILD_RPATH property, but I'd like it at the end of the list, so it's searched last, after the other directories that cmake automatically adds to target's BUILD_RPATH. But there doesn't seem to be way to add to the property after the automatic RPATH directories.
At build time, my system libraries are not in the normal locations, but in a staging area. In order to run uninstalled built binaries, I need to add this staging area to the binaries' RPATHs. And this part is straightforward to do and works fine, like this:
add_executable(mybinary ${BINARY_SOURCES})
set_property(TARGET mybinary APPEND PROPERTY BUILD_RPATH ${STAGING_LIB_DIR})
But mybinary also uses a library that it built as part of the same project:
add_library(mylib SHARED ${LIB_SOURCES})
target_link_libraries(mybinary PRIVATE mylib)
When mybinary is run, I'd like it to use the mylib that was just built and is in ${CMAKE_CURRENT_BINARY_DIR}, not another copy somewhere else, perhaps in the system library directory from the last time make install was run to install the project. Or, in my case, a copy of the library in ${STAGING_LIB_DIR}.
cmake will automatically add ${CMAKE_CURRENT_BINARY_DIR}, or whatever is appropriate, for any libraries not from the system to the build RPATH of produced binaries. So when one runs mybinary from the build directory it will search for the mylib in the build directory.
But the problem is it appends these automatic library directories to whatever I have set BUILD_RPATH to. So one gets a final RPATH of ${STAGING_LIB_DIR}:${CMAKE_CURRENT_BINARY_DIR} and the wrong copy of mylib is used.
You could set the SKIP_BUILD_RPATH target property:
SKIP_BUILD_RPATH is a boolean specifying whether to skip automatic generation of an rpath allowing the target to run from the build tree. This property is initialized by the value of the variable CMAKE_SKIP_BUILD_RPATH if it is set when a target is created.
And then manually set the RPATH in whatever way/order you would like without worrying about CMake doing additional things to it.
I have the simplest possible c-library which builds and is packed using the following CMakeLists.txt:
cmake_minimum_required(VERSION 3.5)
project (libfoo C)
add_library(foo SHARED impl.c)
target_link_libraries(foo)
install(TARGETS foo LIBRARY DESTINATION lib/)
install(FILES public_header.h DESTINATION include/libfoo)
set(CPACK_GENERATOR "TGZ")
include(CPack)
Working example is located here: https://github.com/bjarkef/cmake-simple/tree/master/libfoo
I execute mkdir -p build; (cd build/; cmake ../; make all package;) to build a .tar.gz package with the compiled shared library along with its public header file. This is all working fine.
Now I wish to modify the CMakeLists.txt to create the FooConfig.cmake and FooConfigVersion.cmake files needed for CMake find_package in a different project to find the foo library. How do I do this?
I have discovered I should used the CMakePackageConfigHelpers: configure_package_config_file and write_basic_package_version_file, and I should create a FooLibraryConfig.cmake.in file. However I cannot figure out how to put it all together.
Note that it is important the the resulting .cmake files only contains relative paths.
I have cmake module included in the top level CmakeList.txt:
# Generate and install package config files
include(PackageConfigInstall)
Within the generic PackageConfigInstall.cmake file, the config files are created from the cmake.in files, and installed. This module can be reused for other packages.
include(CMakePackageConfigHelpers)
# Generate package config cmake files
set(${PACKAGE_NAME}_LIBRARY_NAME ${CMAKE_SHARED_LIBRARY_PREFIX}${PACKAGE_NAME}${CMAKE_STATIC_LIBRARY_SUFFIX})
configure_package_config_file(${PACKAGE_NAME}-config.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/${PACKAGE_NAME}-config.cmake
INSTALL_DESTINATION ${CMAKE_INSTALL_DIR}/${PACKAGE_NAME}
PATH_VARS LIB_INSTALL_DIR INCLUDE_INSTALL_DIR APP_INCLUDE_INSTALL_DIR )
configure_file(${PACKAGE_NAME}-config-version.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/${PACKAGE_NAME}-config-version.cmake #ONLY)
# Install package config cmake files
install(
FILES
${CMAKE_CURRENT_BINARY_DIR}/${PACKAGE_NAME}-config.cmake
${CMAKE_CURRENT_BINARY_DIR}/${PACKAGE_NAME}-config-version.cmake
DESTINATION
${CMAKE_INSTALL_DIR}/${PACKAGE_NAME}
COMPONENT
devel
)
You'll need a package file for your library, such as your_lib-config.cmake.in, which will become your_lib-config.cmake. This will contain the include and library variables that can be used.
get_filename_component(YOUR_LIB_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
# flag required by CMakePackageConfigHelpers
#PACKAGE_INIT#
set_and_check(YOUR_LIB_INCLUDE_DIR #PACKAGE_YOUR_LIB_INCLUDE_INSTALL_DIR#/hal)
set_and_check(YOUR_LIB_LIBRARY #PACKAGE_LIB_INSTALL_DIR#/#CMAKE_STATIC_LIBRARY_PREFIX##PROJECT_NAME_LIB##CMAKE_STATIC_LIBRARY_SUFFIX#)
set_and_check(YOUR_LIB_LIBRARIES #PACKAGE_LIB_INSTALL_DIR#/#CMAKE_STATIC_LIBRARY_PREFIX##PROJECT_NAME_LIB##CMAKE_STATIC_LIBRARY_SUFFIX#)
You'll also want a config-version.cmake.in file like this:
set(PACKAGE_VERSION #PACKAGE_VERSION#)
# Check whether the requested PACKAGE_FIND_VERSION is compatible
if("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}")
set(PACKAGE_VERSION_COMPATIBLE FALSE)
else()
set(PACKAGE_VERSION_COMPATIBLE TRUE)
if ("${PACKAGE_VERSION}" VERSION_EQUAL "${PACKAGE_FIND_VERSION}")
set(PACKAGE_VERSION_EXACT TRUE)
endif()
endif()
There's quite a bit to the packaging scripts to get it all to work just right. I went through a lot of trial and error to finally get something that works on different targets (both linux server and embedded target). I might have left something out, so please just comment and I'll update answer.
I am trying to get rid of setting LD_LIBRARY_PATH everytime time I run my program. After adding in the library and targeting my executable to the library, when I run it tells me it can not open shared object library, no such file or directory.
In my CMakeLists.txt I have:
add_library(heart SHARED ${HEART_FILES})
add_executable(run ${RUN_FILES})
target_link_libraries(run heart)
set(CMAKE_SKIP_BUILD_PATH FALSE)
set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)
set(CMAKE_INSTALL_RPATH "~/person/target/usr/local/lib")
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
I set an absolute link to my library folder to test out whether this would create an rpath to my library and it seems like there isn't. I have checked and made sure that the shared library is indeed in lib. libheart.so is the file that is being linked. What else am I missing?
It is because you build heart and run from the same cmake project:
CMAKE_INSTALL_RPATH_USE_LINK_PATH is an interesting and very useful option. When building a target with RPATH, CMake determines the RPATH by using the directories of all libraries to which this target links. Some of these libraries may be located in the same build tree, e.g. libbar.so, these directories are also added to the RPATH.
If this option is enabled, all these directories except those which are also in the build tree will be added to the install RPATH automatically. The only directories which may then still be missing from the RPATH are the directories where the libraries from the same project (i.e. libbar.so) are installed to. If the install directory for the libraries is not one of the systems default library directories, you have to add this directory yourself to the install RPATH by setting CMAKE_INSTALL_RPATH accordingly
You can try this:
SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
More documentation here cmake rpath handling
EDIT:
Only this should work:
set(CMAKE_VERBOSE_MAKEFILE ON)
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
add_library(heart SHARED ${HEART_FILES})
add_executable(run ${RUN_FILES})
target_link_libraries(run heart)
install(
TARGETS heart run
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
)
Clean your build directory and then:
cmake -G "Unix Makefiles" -DCMAKE_INSTALL_PREFIX=/home/person/target/usr/local ..
make install
At the end of the g++ line Linking CXX executable run you should see like -Wl,-rpath,/home/person/target/usr/local/lib
If you want a fully relocatable package:
set(CMAKE_INSTALL_RPATH "$ORIGIN/../lib")
PS: are you sur that it is libheart.so that is not found ?
In your CMake file, set the RPATH before defining the targets. The CMAKE_INSTALL_RPATH must be defined before calling add_executable(), otherwise it has no effect.
I had a similar issue as the original post. I created executables which linked to external shared libraries. This approach compiled and executed fine from the build directory. However, the executable that was installed to a separate directory could not find a shared library at runtime:
error while loading shared libraries: libxxxx.so.1: cannot open shared object file: No such file or directory
To solve, I
1) upgraded to CMake 3.17
2) used Craig Scott's recommended:
set(CMAKE_INSTALL_RPATH $ORIGIN)
as explained in his talk
3) set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) as directly mentioned to solve this error in the second common question in Kitware's documention
4) Put all this before adding the targets as mentioned in this post
5) Used the "$ORIGIN/../lib" syntax instead of Craig's Scott's mentioned $ORIGIN as mentioned by #explo91
In summary, and to my suprise, only the "$ORIGIN/../lib" before the target definition was necessary from above (I tested the other combinations which did not fix the cannot open shared object file runtime issue).
Anyway the solution I finally applied, which may be of better, fine-grained CMake style or at least may be helpful to others on their RPATH journey is:
set_target_properties(target_defined_above PROPERTIES INSTALL_RPATH "$ORIGIN/../lib")