CMake RPATH handling

From KitwarePublic
Revision as of 14:57, 13 June 2011 by Zack (talk | contribs) (Reverted edits by Sammy101Wilson (talk) to last revision by Daviddoria)
Jump to navigationJump to search

CMake gives the user complete control over the RPATH of executables and libraries created using CMake.

What is RPATH ?

If an executable foo links to the shared library bar, the library bar has to be found and loaded when the executable foo is executed. This is the job of the linker, under Linux this is usually ld.so. The linker searches a set of directories for the library bar, which will under most UNIXes have the name "libbar.so". The linker will search the libraries in the following directories in the given order:

  • RPATH - a list of directories which is linked into the executable, supported on most UNIX systems. It is ignored if RUNPATH is present.
  • LD_LIBRARY_PATH - an environment variable which holds a list of directories
  • RUNPATH - same as RPATH, but searched after LD_LIBRARY_PATH, supported only on most recent UNIX systems, e.g. on most current Linux systems
  • /etc/ld.so.conf - configuration file for ld.so which lists additional library directories
  • builtin directories - basically /lib and /usr/lib

There are different reasons why search directories additonal to the builtin ones can be needed - a user may install a library privately into his home directory, e.g. ~/lib/, or there may be two or more versions of the same library installed, e.g. /opt/kde3/lib/libkdecore.so and /opt/kde4/lib/libkdecore.so.

For the first case it would work if the user would set LD_LIBRARY_PATH accordingly:

export LD_LIBRARY_PATH=$HOME/lib:$LD_LIBRARY_PATH

This will break for the second case, where for some programs /opt/kde3/lib has to be searched and for other applications /opt/kde4/lib has to be searched, but in no case both. The only way to have an executable-dependent library search path is by using RPATH (or RUNPATH, but this isn't supported everywhere).

CMake and the RPATH

With CMake the developer has full control over the RPATH of his executables and shared libraries. This is controlled over various target properties, see the documentation of SET_TARGET_PROPERTIES(). These properties are initialized from a set of global CMake variables with the respective name. There is not something like the one "correct" RPATH set up, it depends on the wishes of the developer or distribution policies.

Different RPATH settings may be required when running a program from the build tree and when running it from its install location. If the program links to shared libraries which have been also built in the same project, then the RPATH needs to point to the directories in the build tree when running the executable from the build tree, and it must not point to the build tree anymore once the executable has been installed. Unfortunately there is no easy and fast way to change the RPATH of an existing executable or shared library. Since version 2.6 CMake builds the executable with the RPATH for the build tree, and adds padding to the RPATH if the RPATH for the install tree is longer. This way, when installing the executable (or library), CMake can change the RPATH inside the existing executable.

Before CMake 2.6 CMake had to link the executable (or library) again at install time, so that the installed executable had the modified RPATH.

If you want the same RPATH settings for executables and shared libraries in your source tree, all you have to do is to set the CMake variables CMAKE_INSTALL_RPATH, CMAKE_SKIP_BUILD_RPATH, CMAKE_BUILD_WITH_INSTALL_RPATH, CMAKE_INSTALL_RPATH_USE_LINK_PATH once, e.g. in the top level CMakeLists.txt.

Default RPATH settings

By default if you don't change any RPATH related settings, CMake will link the executables and shared libraries with full RPATH to all used libraries in the build tree. When installing, it will clear the RPATH of these targets so they are installed with an empty RPATH. This is equivalent to:

# use, i.e. don't skip the full RPATH for the build tree
SET(CMAKE_SKIP_BUILD_RPATH  FALSE)

# when building, don't use the install RPATH already
# (but later on when installing)
SET(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) 

# the RPATH to be used when installing
SET(CMAKE_INSTALL_RPATH "")

# don't add the automatically determined parts of the RPATH
# which point to directories outside the build tree to the install RPATH
SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH FALSE)

With these settings you are able to run the programs directly from the build tree. Once installed, you have to ensure that they find the required shared libraries (e.g. by setting LD_LIBRARY_PATH, or by installing shared libraries into one of the default library directories).

Always full RPATH

In many cases you will want to make sure that the required libraries are always found independent from LD_LIBRARY_PATH and the install location. Then you can use these settings:

# use, i.e. don't skip the full RPATH for the build tree
SET(CMAKE_SKIP_BUILD_RPATH  FALSE)

# when building, don't use the install RPATH already
# (but later on when installing)
SET(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) 

SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")

# add the automatically determined parts of the RPATH
# which point to directories outside the build tree to the install RPATH
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")

With these settings you will be able to execute your programs from the build tree and they will find the shared libraries in the build tree and also the shared libraries outside your project, when installing all executables and shared libraries will be relinked, so they will find all libraries they need.

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.

If your library is created and installed like this:

ADD_LIBRARY(bar SHARED bar.c blub.c)
INSTALL(TARGETS bar DESTINATION lib)

then you have to set the CMAKE_INSTALL_RPATH as shown above. If you are using some variable as destination for the library, you have to add this to CMAKE_INSTALL_RPATH.

If you want very tight control over the RPATH in the installed executable, you can disable CMAKE_INSTALL_RPATH_USE_LINK_PATH and just set the complete RPATH yourself via CMAKE_INSTALL_RPATH.


No RPATH at all

There is a builtin CMake option CMAKE_SKIP_RPATH, if it is enabled all other RPATH related options are ignored, no RPATH is built into anything.


Different RPATH settings within one project

Using the CMake variables as shown above you can get a global setup for RPATH for the whole project. If you need different RPATH settings for some targets, you have to use SET_TARGET_PROPERTIES() to set the respective properties, which have the same names as the variables, but without the CMAKE_ prefix.

Common questions

Q) I have created an executable like this: <source lang="cmake">

add_executable(ImageCompleter ${AppSources})
target_link_libraries(ImageCompleter ${LibrariesAlwaysUsed})
INSTALL( TARGETS ImageCompleter
  RUNTIME DESTINATION ${INSTALL_DIR} )

</source>

When I run the one that is created with 'make', it works fine. However, when I run the one that is copied by 'make install' I get something like:

 error while loading shared libraries: libwx_gtk2u_core-2.9.so.1: cannot open shared object file: No such file or directory

What do I do?

A) Add

SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)

to your CMakeLists.txt