What's the difference between CMAKE_INSTALL_PREFIX and CMAKE_INSTALL_RPATH - cmake

I have a difficult time in understanding the difference between CMAKE_INSTALL_PREFIX and CMAKE_INSTALL_RPATH.
If I understand well, CMAKE_INSTALL_PREFIX is the prefixed directory that will be installed. Therefore, if I use the following script for installation:
project(hello)
add_library(hello hello.h hello.cpp)
set(CMAKE_INSTALL_PREFIX "c:/ABC/DEF")
INSTALL(TARGETS hello EXPORT hello_export
RUNTIME DESTINATION bin
LIBRARY DESTINATION bin
ARCHIVE DESTINATION lib
FRAMEWORK DESTINATION bin
INCLUDES DESTINATION include
)
Then the static library will be installed in C:/ABC/DEF/lib.
Then, my question is what's the point of using CMAKE_INSTALL_RPATH?

On a system which supports paths of the form c:/ABC/DEF (i.e. Windows), none. Windows binaries don't have a notion of rpath.
On systems which do have DT_RPATH and DT_RUNPATH (= those which use ELF binaries), the CMake variable CMAKE_INSTALL_RPATH is used to set up the value of DT_RPATH (or DT_RUNPATH) tags which will be written into the binaries at installation.

This is explained at CMake RPATH handling.
On Unix systems, dynamic libraries are searched for in a system-defined list of directories. (/etc/ld.so.conf -- Windows does this in its own way that is so convoluted that it usually boils down to "just use PATH". 😉)
If you install a library (like the one you just compiled) in a custom directory, not in that list, it will not be found if you run a dependent executable. RPATH is one way to fix this.
See the Wiki page linked above for details.

Firstly, CMAKE_INSTALL_PREFIX determines a "root" for the installed location of headers, libraries, executables, and other resources.
On a system which does not support the notion of a "search hierachy" for dependencies, CMAKE_INSTALL_RPATH is not used. However, on ELF-based systems (e.g. Linux) and Mach-based systems (e.g. macOS 10.5 and later) a set of additional locations to search can be set in executables and dynamic libraries (e.g. .so/.dylib files); this is the "Rpath" and you can set it during cmake's install phase, either for all targets by setting CMAKE_INSTALL_RPATH or for individual targets by setting INSTALL_RPATH on that target.
Static libraries are not dynamic (obviously!) so, CMAKE_INSTALL_RPATH has no utility at all for static libraries.
When installing dynamic objects, CMake will write the Rpath into the dynamic object provided CMAKE_SKIP_RPATH and CMAKE_SKIP_INSTALL_RPATH are both false. By default, the Rpath written will be set to CMAKE_INSTALL_PREFIX followed by the library destination, e.g. CMAKE_INSTALL_PREFIX/lib. On Linux systems, this would by default see an Rpath of /usr/local/lib written as Rpath.
You can examine the Rpath on Linux thus:
readelf -d libmylib.so
which produces something like:
0x000000000000000f (RPATH) Library rpath: [/usr/local/lib]
or on macOS:
otool -l libmylib.dylib | grep -A 2 LC_RPATH
which produces something like:
cmd LC_RPATH
cmdsize 40
path #loader_path/../Frameworks (offset 12)
To override the install Rpath you can set the variable CMAKE_INSTALL_RPATH. E.g. on Linux:
set(CMAKE_INSTALL_RPATH "\$ORIGIN/../lib")
or on macOS:
set(CMAKE_INSTALL_RPATH "#loader_path/../lib")

Related

How to understand the differences between CMAKE_BUILD_RPATH and CMAKE_INSTALL_RPATH in the right way?

As per the document, which says:
CMAKE_BUILD_RPATH¶ New in version 3.8.
Semicolon-separated list specifying runtime path (RPATH) entries to
add to binaries linked in the build tree (for platforms that support
it). The entries will not be used for binaries in the install tree.
See also the CMAKE_INSTALL_RPATH variable.
This is used to initialize the BUILD_RPATH target property for all
targets.
As per the document, which says:
CMAKE_INSTALL_RPATH¶ The rpath to use for installed targets.
A semicolon-separated list specifying the rpath to use in installed
targets (for platforms that support it). This is used to initialize
the target property INSTALL_RPATH for all targets.
How to understand the differences between CMAKE_BUILD_RPATH and CMAKE_INSTALL_RPATH in the right way?
A small and clear example is welcome.
When building binaries one can set the RPATH to support library path resolution at runtime.
There are two scenarios for which to build binaries. The first an obvious scenario is to debug a program. This means the binary will be built and (normally) executed from the location it has been built. Details can vary but in general it is under the cmake build directory.
This means if you build for example two libraries in your project libA and libB. libA dynamically linking libB. This means both libraries are located somewhere in the binary path. To run a binary in the build path with runtime dependency resolution you CAN specify the CMAKE_BUILD_PATH or the recommended target property BUILD_RPATH with an absolute or relative value.
lib
location
rpath
libA
/home/user/my_tool/build/liba
./../libb/ or /home/user/my_tool/build/libb
libB
/home/user/my_tool/build/libb
Then you can smoothly run your binary from the build path and everything should work without modifying the LD_LIBRARY_PATH system environment variable for dependency lookup.
The same applies to the RPATH if the binary gets installed (cmake install). In this case the value of the RPATH could be different. To accommodate this there are these two CMake features to distinguish between the scenarios.
lib
location
rpath
libA
/usr/lib/my_tool
. or /usr/bin/my_tool
libB
/usr/lib/my_tool

How can I change the library path of an executable after it has been built/installed?

Let's say I build myTest with cmake. myTest uses /opt/path1/lib/lib.so at compile and link time. After running it a few times I decide that I want myTest to now use /opt/path2/lib.so (same lib name, same interfaces, just different path).
This might be cause I want to temporarily test changes to lib.so without affecting others that might be using it. I also may not have the source to myTest but know that it uses lib.so.
If I used a Makefile and used regular gnu/g++ make I can make this happen by setting LD_LIBRARY_PATH in the local folder. CMake ignores LD_LIB_PATH - so how do I make this happen?
For find a library at runtime, ldd uses (among other things) RPATH directories, embedded into the executable.
By default, when build the executable/library, CMake adds to RPATH directories, where linked libraries are located.
E.g., when link with library /opt/path1/lib/lib.so, CMake adds directory /opt/path1/lib to RPATH. So ldd always finds lib.so library as /opt/path1/lib/lib.so.
For tell CMake to not set RPATH, set CMAKE_SKIP_RPATH variable:
set(CMAKE_SKIP_RPATH TRUE)
After that, ldd will search lib.so in directory, listed in LD_LIBRARY_PATH environment variable.

set PKG_CONFIG_PATH in cmake

I have built opencv locally and installed it to a local directory (not the system default ). opencv.pc is present under a folder pkgconfig in this local folder. How can I find this opencv.pc from cmake, because I want to link and include opencv files from my program.
pkg_search_module(<PREFIX> [REQUIRED] [QUIET] <MODULE> [<MODULE>]*)
does not have any parameter in which I can force the command to use a specific path (similar to HINTS with find_package()) and not the system default.
So basically .cmake works:
find_package(OpenCV REQUIRED HINTS "my/path/to/share/OpenCVConfig.cmake")
but I would like to use a opencv.pc located under my/path/to/pkgconfig/opencv.pc.
After doing some research and a hint from the #OlivierM, I found the answer.
Here are the steps:
Method I :
CMAKE_PREFIX_PATH can be set to find the .pc files
set(CMAKE_PREFIX_PATH "${CMAKE_SOURCE_DIR}/libs/opencv-install")
Method II
A second method is to use the PKG_CONFIG_PATH, which is a system environment variable to look for .pc files.
set(ENV{PKG_CONFIG_PATH} "${CMAKE_SOURCE_DIR}/libs/opencv-install/lib/pkgconfig")
Irrespective of which method you use,
For old (traditional) CMake:
find_package(PkgConfig REQUIRED)
pkg_search_module(PKG_OPENCV REQUIRED opencv) # this looks for opencv.pc file
Please note that the PKG_OPENCV variable can be named anything. Whatever it is is named, its used as a prefix. For example if you name ABCD, then include directories will be ABCD_INCLUDE_DIRS
The variable PKG_OPENCV_INCLUDE_DIRS and PKG_OPENCV_LIBRARIES contains the header files (compile stage) and libraries (link stage) respectively.
One very important thing I noticed was that the variable PKG_OPENCV_LIBRARIES just provides the libraries and not the library path during the link stage. In order to use the library path as well in one command, one has to use
PKG_OPENCV_LDFLAGS
This variable contains the library path as well as all the libraries listed in the package config file.
for examaple:
include_directories(${PKG_OPENCV_INCLUDE_DIRS})
target_link_libraries (FINAL_BINARY ${PKG_OPENCV_LDFLAGS})
For modern CMake:
In modern CMake we don't want variables, we want targets.
find_package(PkgConfig REQUIRED)
# this looks for opencv.pc file and creates a new target
# IMPORTED_TARGET requires CMake >= 3.6.3
pkg_search_module(PKG_OPENCV REQUIRED IMPORTED_TARGET opencv)
All variables will still be created for backwards compatibility, but IMPORTED_TARGET will create a target you can use in your project which will automatically propagate all of its build and usage requirements:
target_link_libraries(my_proj PRIVATE PkgConfig::PKG_OPENCV)
You could set PKG_CONFIG_PATH with the CMake line:
set(ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:/my/path/to/pkgconfig")
I did this workaround in this file
Interestingly, it seems CMake 3.1 extends PKG_CONFIG_PATH with some CMake variable see: https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=3df51470
I would propose you to call cmake with custom PKG_CONFIG_PATH variable, like below:
PKG_CONFIG_PATH=$PKG_CONFIG_PATH:my/path/to/pkgconfig cmake <some args>
Or can make PKG_CONFIG_PATH update to be permanent for whole bash session:
$ export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:my/path/to/pkgconfig
$ cmake <some args>

CMAKE RPATH not working - could not find shared object file

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

CMake cross compile target rpath

I am cross-compiling using CMake.
In my CMakeLists.txt (used for both compile and cross compile):
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
find_package(foo REQUIRED)
add_library(mylib SHARED ${SRCS})
target_link_libraries(mylib ${FOO_LIBRARIES)
In my toolchain.cmake:
set(CMAKE_CXX_FLAGS "... --sysroot=/path/to/sysroot/ ... ")
set(CMAKE_CXX_LINK_FLAGS "... --sysroot=/path/to/sysroot/ ... )
...
set(CMAKE_FIND_ROOT_PATH /path/to/sysroot)
Consider foo is located to /path/to/sysroot/usr/local/lib/foo.so, when i cross-compile the runtime path for mylib is /path/to/sysroot/usr/local/lib
I want that the runtime path is /usr/local/lib to reflect my target filesystem.
How can i do this without define a hard-coded CMAKE_INSTALL_RPATH variable in my CMakelists.txt ?
EDIT: I used /usr/local/lib for the example but foo lib are located to a specific folder that is not a part of the system dirs: /path/to/sysroot/usr/local/share/mypackage/lib/foo.so
Check out the wiki on CMake RPATH handling.
By default, CMake compiles your executable with an RPATH pointing to the host-system library location (/crosssdk/sysroot/usr/lib/) and then (I believe) when installing (ie. make install) it edits the RPATH in the executable to replace it with the appropriate target RPATH (/usr/lib or wherever you've got it). I think the idea is that then you can make changes to the shared lib and execute the output on your host system without having to install both the shared lib and executable every time.
In my case, my host is x86 and target is ARM, so I tell CMake to set the build RPATH the same as the install RPATH:
set_target_properties(mytarget PROPERTIES BUILD_WITH_INSTALL_RPATH TRUE)
I am not sure which cross compile toolchain you are using.
You need to specify the C/CXX compilers, Linker etc.
Along with that some of the important variables are CMAKE_FIND_ROOT_PATH_MODE_LIBRARY and CMAKE_FIND_ROOT_PATH_MODE_INCLUDE. If you set them to "ONLY", when you make calls to FindXXX(), the search happens only in the target root file system directory but not the build machine.
In my case I don't have to specify the sysroot as the cross compiler already knows that it's cross compiling and it also knows the location of the target root file system.
With this toolchain file, I just compile the sources without any additional flags, load the executable on the target and it runs fine picking up the *.so file directly from the right path.
Give it a try with this and let me know how it goes.
Here is my toolchain file:
set(ELDK_DIR /opt/eldk/ppc-v42-1)
set (CMAKE_C_COMPILER ${ELDK_DIR}/usr/bin/ppc_6xx-gcc)
set (CMAKE_CXX_COMPILER ${ELDK_DIR}/usr/bin/ppc_6xx-g++)
set (CMAKE_LINKER ${ELDK_DIR}/usr/bin/ppc_6xx-ld CACHE STRING "Set the cross-compiler tool LD" FORCE)
set (CMAKE_AR ${ELDK_DIR}/usr/bin/ppc_6xx-ar CACHE STRING "Set the cross-compiler tool AR" FORCE)
set (CMAKE_NM ${ELDK_DIR}/usr/bin/ppc_6xx-nm CACHE STRING "Set the cross-compiler tool NM" FORCE)
set (CMAKE_OBJCOPY ${ELDK_DIR}/usr/bin/ppc_6xx-objcopy CACHE STRING "Set the cross-compiler tool OBJCOPY" FORCE)
set (CMAKE_OBJDUMP ${ELDK_DIR}/usr/bin/ppc_6xx-objdump CACHE STRING "Set the cross-compiler tool OBJDUMP" FORCE)
set (CMAKE_RANLIB ${ELDK_DIR}/usr/bin/ppc_6xx-ranlib CACHE STRING "Set the cross-compiler tool RANLIB" FORCE)
set (CMAKE_STRIP ${ELDK_DIR}/usr/bin/ppc_6xx-strip CACHE STRING "Set the cross-compiler tool RANLIB" FORCE)
# Target environment
set (CMAKE_FIND_ROOT_PATH ${ELDK_DIR}/ppc_6xx)
# Don't search for programs in the host environment
set (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
# Search for the libraries and headers in the target environment
set (CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set (CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
all you need is:
set(CMAKE_BUILD_RPATH "/my/libs/location")
specifying runtime path (RPATH) entries to add to binaries linked in the build tree (for platforms that support it). The entries will not be used for binaries in the install tree. See also the CMAKE_INSTALL_RPATH variable.