CMAKE RPATH not working - could not find shared object file - cmake

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")

Related

CMake with custom install path and multiple libraries in different directories

I am new to cmake, I know a lot of similar questions are asked, but I don't seem to figure out how to do this.
I have very large project which can be simplified with this structure:
Parent_dir
--src
--main1.cpp
--main2.cpp.
--include
--lib_dir_1
--f1.cpp
--f1.h
--lib_dir_2
--f2.cpp
--f2.h
--build
--install
--include
--share
--lib
--bin
I do not have access to /usr/include, /usr/lib, /usr/bin, and /usr/share because it is an account on a cluster. My idea was to run cmake in build dir and install library .o and executives in install dir.
My CMakeFile.txt are:
Parent_dir/CMakeFile.txt
cmake_minimum_required(VERSION 3.13)
set(CMAKE_TRY_COMPILE_TARGET_TYPE "STATIC_LIBRARY")
project(MyProject VERSION 2.0)
set(CMAKE_INSTALL_PREFIX "${CMAKE_SOURCE_DIR}/install")
set(CMAKE_CXX_STANDARD 17)
add_subdirectory("include/")
add_subdirectory(src)
Parent_dir/include/CMakeFile.txt
add_subdirectory(lib_dir1)
add_subdirectory(lib_dir2)
Parent_dir/src/CMakeFile.txt
add_executable(M1 Main1.cpp)
target_include_directories(M1 PUBLIC "${CMAKE_SOURCE_DIR}/include/lib_dir1")
find_library(LIB_1 lib1)
target_link_libraries(M1 lib1)
install (TARGETS M1 DESTINATION ${CMAKE_INSTALL_PREFIX})
#add_executable(M2 Main2.cpp)
#target_include_directories(M2 PUBLIC "${CMAKE_SOURCE_DIR}/include/lib_dir2")
#find_library(LIB_2 lib2)
#target_link_libraries(M2 lib2)
#install (TARGETS M2 DESTINATION ${CMAKE_INSTALL_PREFIX})
Parent_dir/include/lib_dir1/CMakeFile.txt
add_library(lib1 SHARED f1.cpp f1.h)
FIND_PACKAGE(Boost COMPONENTS program_options REQUIRED)
INCLUDE_DIRECTORIES(${Boost_INCLUDE_DIRS})
TARGET_LINK_LIBRARIES(lib1 ${Boost_LIBRARIES})
target_include_directories(lib1 PUBLIC ${CMAKE_CURRENT_LIST_DIR})
Parent_dir/include/lib_dir2/CMakeFile.txt
add_library(lib2 SHARED f2.cpp f2.h)
target_include_directories(lib2 PUBLIC ${CMAKE_CURRENT_LIST_DIR})
find_library(LIB1 lib1)
target_link_libraries(lib2 lib1)
At the moment I am only testing building M1 that depends on lib1, I commented stuff referring to M2 executable. I know that find_library(LIB1 lib1) should find lib1 library and put its path to LIB1 variable, so clearly I am making a mistake when targeting and linking because for example in Parent_dir/src/CMakeFile.txtit should be target_link_libraries(M1 ${LIB1}), but I am getting an error: `The following variables are used in this project, but they are set to NOTFOUND.
Please set them or make sure they are set and tested correctly in the CMake files:
LIB_1.
So find_library never found lib1, if I link as in files above, cmake is fine, make is fine, make install is fine, and in install directory I see M1 executable which does not work when used stating that error while loading shared libraries: liblib1.so: cannot open shared object file:...
So clearly I messed something up and I've been running in circles for two days. Biggest issue with online resources is that a lot of tutorials don't cover the issue how to navigate installation to custom dirs. Note that I have built other packages with typical configure --prefix approaches before and my desire to have install/include share lib bin directories is simply due to previous experience, in CMakeFiles above I only tried to navigate executable M1 to install dir, it worked, but I have no idea where lib1.so is built and whether I messed everything in CmakeFiles.
Also I am not sure if the code structure I am making is "good", perhaps I should move lib1_dir and lib2_dir to src? Problem is that in these libraries I have 100s of files so the structure above made most sense.
PS:
I do not have cmake-gui, cluster has 3.15 and 2.18 version and gui only exists for 2.18 version.
UPDATE:
Managed to get installation to work (check answer below), but some issues still remain:
Executables are now in install/bin, library files are in install/lib. I am just not sure if everything is linked properly, both lib1 and lib2 need to be shared libraries as I have about 6 executables that need them. The xml resource is needed by lib1, I don't know how to change the code to look for it other than the current dir. I could set its location to install/bin, but that is a bit dirty as if someone installs the package on other machine, the path to file would be different. I doubt that CMAKE environment variables work within C++?
I would also like to have all .h files from both libraries in install/include dir, how should I go about that?
Can someone explain whether this is correct. I am skeptical with line in src where I link libraries. I did not use find library, because it fails, if I hint it with install/lib location, than it says it cannot find .h files needed by the library. I am suspecting that by doing target_link_libraries(M1 lib1) I am directly recompiling the whole library to attach it to executable, rather than finding existing one and doing simple linking.

CMake TARGET_RUNTIME_DLLS is empty

I have git cloned, built (with MSVC for both Debug and Release) and then installed wxWidgets:
cmake -B build wxWidgets
cmake --build build --config <CONFIG>
cmake --install build --prefix my_install --config <CONFIG>
with <CONFIG> = Debug and <CONFIG> = Release.
Then I used the following CMake script to link against it, as suggested by the wiki:
cmake_minimum_required(VERSION 3.16)
project(Test)
add_executable(Test WIN32 Main.cpp)
# wxWidgets
SET(wxWidgets_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../thirdparty/my_install)
find_package(wxWidgets COMPONENTS core base REQUIRED)
include(${wxWidgets_USE_FILE})
target_link_libraries(Test PRIVATE ${wxWidgets_LIBRARIES})
# Copy runtime DLLs to the directory of the executable.
add_custom_command(TARGET Test POST_BUILD
COMMAND ${CMAKE_COMMAND} -E echo "Runtime Dlls: $<TARGET_RUNTIME_DLLS:Test>"
)
My goal is to automatically copy the DLLs into the directory of the built executable, so that they can be found at runtime. For that I'm using the TARGET_RUNTIME_DLLS generator expression (follwing the sample code in the docs). In the code above, I only print out the expression at build time for testing purposes. The problem is that it is empty.
The approach worked for me before when installing and linking SDL, but SDL provides package configuration files which create imported targets, defining the DLL location(s) via IMPORTED_LOCATION_RELEASE or IMPORTED_LOCATION_DEBUG. For wxWidgets one is apparently supposed to use the FindwxWidgets.cmake script shipped with CMake, which sadly doesn't define the produced binaries. Maybe that's why TARGET_RUNTIME_DLLS isn't populated.
Does anyone know, either how to get TARGET_RUNTIME_DLLS filled or how to obtain the list of built wxWidgets DLLs for the current configuration (Release/Debug) post build copying?
Thanks a lot in advance!
I am dealing with a similar problem.
First sanity checks:
You have to work on windows platform otherwise this feature does not
work.
Your Cmake is 3.21 or above
Next comes fuzzy part. I think the library that you are trying to include have to be a Shared Imported library and you have to set a set_target_properties for IMPORTED_IMPLIB which is a path to a .lib file of sort (dll import library, I think it is called) So you have to make sure that it is all set in the package library that you trying to link with your executable.
If you have those dll avaiable and you just want to use them and not actually build them then you can write your own cmake script that will do just what I said above. Then you can include that cmake file in your project and then link against your app.
Note: I also work on similar issue right now and what I just said have not been working very reliably. I got some dlls to be copied and some do not.
Edit:
Cmake docs give a more detailed explanation on how this library setting should look like if you use find_package feature.
Found here: https://cmake.org/cmake/help/latest/command/add_library.html#imported-libraries
An UNKNOWN library type is typically only used in the implementation
of Find Modules. It allows the path to an imported library (often
found using the find_library() command) to be used without having to
know what type of library it is. This is especially useful on Windows
where a static library and a DLL's import library both have the same
file extension.

Install shared libraries only if required with cmake

I've got a cmake project that builds multiple executables and shared
libraries. There are option() settings that determine which of these
executables are built. What I'd like to be able to do is prevent the
building and installation of shared libraries that are not required. So
what I did is as follows:
set(CMAKE_SKIP_INSTALL_ALL_DEPENDENCY TRUE) on the top level
CMakeLists file
add_library(${SHARED_LIBRARY} SHARED EXCLUDE_FROM_ALL ...)
install(TARGETS ${SHARED_LIBRARY} DESTINATION ${DESTINATION} OPTIONAL)
These three settings result in the desired behaviour. However, cmake
generates the following warning:
WARNING: Target "xxxx" has EXCLUDE_FROM_ALL set and will not be built by
default but an install rule has been provided for it. CMake does not
define behavior for this case.
Is this warning still valid in this specific scenario?
If so is there another/more correct way to achieve what I want?
If what I'm doing is acceptable, is there a way to suppress this
warning?
Thank you

cmake: how to keep path to libraries during installation

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.

How can LD_LIBRARY_PATH be changed within CMake?

I have a local shared library which is not in $LD_LIBRARY_PATH.
I want to run my executable, but since it cannot find the shared library in the system folders, it outputs "error while loading shared libraries".
A possible fix for this is to export a new LD_LIBRARY_PATH containing the local library folder.
How can I export automatically this environment variable within CMake?
You can set the runtime shared library search path using the -rpath linker option:
SET(CMAKE_EXE_LINKER_FLAGS
"${CMAKE_EXE_LINKER_FLAGS} -Wl,-rpath -Wl,/usr/local/lib")
If your shared lib is not build in the same CMake project of your executable, you can use the CMake rpath handling like this:
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
When you will run make install, CMake will automatically set the runtime path of your executable to your shared library.
If your shared library is built in the same CMake project, use this:
set(CMAKE_INSTALL_RPATH "/usr/local/lib")
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
In this case you must add yourself the directory where your shared library will be installed to the runtime path.
For more information, you can read CMake rpath handling
When you use this solution:
SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
Remembter to set the RPATH before defining the targets in your CMake-File. So this instruction have to be before add_executable() or add_library() is called, otherwise it has no effect.