chadsgilbert
chadsgilbert

Reputation: 410

Transitive RPATH property in CMake

I am building a library that I bundle with a few tests and example programs. The project is able to honor the cmake property BUILD_SHARED_LIBS, building a .so if it's on and building a .a file if it is off. The library depends on a few other dynamic libraries, which are supposed to be found using an environment variable, LIBLOC (their convention, not mine).

When I build the dynamic version of the library, I am able to set up the install rpath so that the libraries are found correctly. E.g.:

add_library(lib ${SRC})
set_target_properties(lib INSTALL_RPATH $LIBLOC)
install(TARGETS lib LIBRARY DESTINATION lib)

And then in each test/example program's CMakeLists.txt:

add_executable(test ${SRC})
target_link_libraries(test lib)
set_target_properties(test INSTALL_RPATH $ORIGIN/../lib)
install(TARGETS test RUNTIME bin)

When BUILD_SHARED_LIBS is on, the test program is happy:

$ lddtree bin/test
test => bin/test (interpreter => /lib64/ld-linux-x86-64.so.2)
    liblib.so => /path/to/install/liblib.so
        libdep.so => /path/to/dependency/libdep.so

We can find everything because the rpath in test is $ORIGIN/../lib, which finds liblib.so and then the rpath in liblib.so is $LIBLOC, which is set in my environment to /path/to/dependency.

I would have expected that, when installing the static library, cmake would read the INSTALL_RPATH property from my library and put it in the test program so that the rpath in test would be $ORIGIN/../lib;$LIBLOC. Instead, $LIBLOC does not end up in any rpath.

My current solution is dissatisfying: I condition my INSTALL_RPATH on BUILD_SHARED_LIBS in each example or test program, e.g.

if (BUILD_SHARED_LIBS)
  set_target_properties(test INSTALL_RPATH "$ORIGIN/../lib")
else ()
  set_target_properties(test INSTALL_RPATH "$LIBLOC")
endif ()

Now I have several targets, all of which have to be aware of their dependencies' dependencies. Plus a bunch of smelly if statements. Yuck!

Is there a nicer way in cmake to pass the install rpath "up" to the first ELF file produced when my libraries are compiled statically?

Upvotes: 9

Views: 1267

Answers (1)

Alex Reinking
Alex Reinking

Reputation: 19956

The CMAKE_INSTALL_RPATH variable may be set externally and will apply to all targets for which RPATH is well defined (i.e. executables and shared libraries).

For libraries you author or vendor, you should install them into a consistent structure so that CMAKE_INSTALL_RPATH may be set to $LIBLOC or $ORIGIN/../lib uniformly across all targets. For true dependencies, it is reasonable to expect the user to have them installed system-wide, have set LD_LIBRARY_PATH, or use the find_dependency macro in your CMake package config to locate the correct libraries.

Here's a snippet that might help you, if placed before any targets are created:

if (NOT DEFINED CMAKE_INSTALL_RPATH)
  # or perhaps it's meant to be $env{LIBLOC}?
  set(CMAKE_INSTALL_RPATH "$<IF:$<BOOL:${BUILD_SHARED_LIBS}>,$ORIGIN/../lib,$LIBLOC>")
endif ()

Upvotes: 1

Related Questions