Bug with setting rpath in incremental builds

Hi all,

It looks like I found a bug in CMake. It sets the rpath (e.g. set(BUILD_RPATH “lib/dir/here”) when I delete the build folder and build everything fresh. However, if I do an incremental build, the RPATH is not set properly (set to a directory in the build directory itself). I’ve confirmed this using
objdump -x binary_name | grep ‘R.*PATH’

Any thoughts on where to go from here?

I’d recommend coming up with a complete, minimal project that can be used to reproduce the problem. Make sure you clearly explain what you expect to happen versus what does happen (the more specific the better).

set(BUILD_RPATH "/some/path")

…If we take your code literally, that command does nothing. It sets a local variable named BUILD_RPATH in your CMakeLists.txt code, but that variable has no meaning unless it’s used somewhere.

CMake does support a variable CMAKE_BUILD_RPATH that can be set to initialize the value of the BUILD_RPATH target property. But the property BUILD_RPATH (which is a completely separate thing from a variable named BUILD_RPATH) can only be set on a build target using set_property() or set_target_properties().

You normally don’t need to mess with the BUILD_RPATH property, though, because by default CMake will automatically set RUNPATHs on all of the output binaries in your build directory so that they can load any linked build artifacts. Those RUNPATH entries are then removed when the binaries are re-linked during installation.

So, for example, if you have a CMakeLists.txt like this:

project(myproj VERSION 0.1 LANGUAGES CXX)

add_executable(binary_name ${sourcefiles})
add_library(somelib SHARED ${libsources})
set_target_properties(somelib PROPERTIES
  VERSION 0.1
  SOVERSION 0
)
target_link_libraries(binary_name PRIVATE somelib)

…Then CMake will automatically set a RUNPATH on the <builddir>/binary_name file that points to /full/path/to/<builddir>, so that binary_name can find the libsomelib.so.0 file in that directory.

If you need additional RUNPATHs set on your output binaries, you can add them using the property BUILD_RPATH:

set_target_properties(binary_name PROPERTIES
 BUILD_RPATH "/path/to/some/library"
)

…But set(BUILD_RPATH ...) isn’t doing anything in your CMake code, and any RUNPATHs you see on your output binaries in the build directory are probably the ones being set by CMake itself.

Regardless whether they’re set by CMake automatically or manually using BUILD_RPATH, by default all of the binaries will be re-linked during install so that the installed binary files have no RUNPATH entries. If you need RUNPATHs set on your installed binaries, you can point the INSTALL_RPATH target property to the location of any installed targets, or ask CMake to automatically include the location of any externally-linked libraries:

set_target_properties(binary_name PROPERTIES
  # manually
  INSTALL_RPATH "/path/to/some/libdir"
  # automatically for any external (to the project) directories
  # that are found in the linker path of project binaries
  INSTALL_RPATH_USE_LINK_PATH TRUE

In a scenario like this:

add_executable(foo ...)
add_library(bar ...)
set_target_properties(bar PROPERTIES
  SOVERSION 0
)
set_target_properties(foo PROPERTIES
  INSTALL_RPATH_USE_LINK_PATH TRUE
)
target_link_libraries(foo PRIVATE bar /opt/dep/lib/libbaz.so)
install(TARGETS foo bar
  RUNTIME DESTINATION bin
  LIBRARY DESTINATION lib/foo
)

Building that project and installing it to /usr will cause CMake to install /usr/bin/foo with a RUNPATH of /opt/dep/lib, so that it can find the linked libbaz.so.1. But if you also want /usr/lib/foo on the RUNPATH to find libbar.so.0, you have to do that yourself:

set_target_properties(foo PROPERTIES
   INSTALL_RPATH "${ORIGIN}/../lib/foo"
)

Excellent response overall, but to correct a small thing in the above, CMake doesn’t relink during install on most platforms now. It has its own code to directly modify the binaries and rewrite the RUNPATH details without needing to re-link. You can force it to do it via re-linking if you really want to, I think there are still some less common platforms where that is still necessary, but for most folks, re-linking is no longer a thing.

1 Like

D’oh, that’s right, I forgot CMake had acquired chrpath technology. :laughing: