CPack Debian Packaging and RUNPATH/RPATH for dependencies picked up from IMPORTED_RUNTIME_ARTIFACTS

Hi,

I’m trying to get cmake to package my Linux executable (+ dependent libraries) as a deb package.
The executable should live in /opt/foo/bin and the libraries it depends on should live in /opt/foo/lib.

However, I’m having problems with the executable finding dependent/transitive libraries in /opt/foo/lib when the libraries are picked up from IMPORTED_RUNTIME_ARTIFACTS.

I first tried setting the RUNPATH of the executable to point to /opt/foo/lib:

cmake_minimum_required( VERSION 3.24 )

add_executable(myprogram myprogram.cpp)
target_link_libraries(myprogram PUBLIC libraryA)

set(CMAKE_INSTALL_PREFIX "/opt/foo" CACHE PATH "Default install path" FORCE)
set(CPACK_PACKAGING_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX})

include(GNUInstallDirs)
# set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_FULL_LIBDIR}") # I tried setting this but it doesn't seem to do anything?

install(TARGETS myprogram
  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
  CONFIGURATIONS Release
)
# Automatically pick up non system dependencies
install(IMPORTED_RUNTIME_ARTIFACTS myprogram
    RUNTIME_DEPENDENCY_SET MyProgramDeps
)
install(RUNTIME_DEPENDENCY_SET MyProgramDeps
    POST_EXCLUDE_REGEXES "^/lib" "^/usr/lib"
    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)

# Set runpath of my program to the libdir
set_target_properties(myprogram
PROPERTIES
    INSTALL_RPATH ${CMAKE_INSTALL_FULL_LIBDIR}
)

set(CPACK_GENERATOR "DEB")
set(CPACK_DEBIAN_FILE_NAME DEB-DEFAULT)
include(CPack)

but I notice that all transitive dependencies do not get picked up when I try to launch myprogram since this only sets the RUNPATH of the executable. (The libraries that libraryA depends on, even though they are successfully placed in /opt/foo/lib from install(RUNTIME_DEPENDENCY_SET ...))
In my case, the dependent libraries which are pulled into /opt/foo/lib are libgtsam.so and libgtsam-metis.so.

I then tried to set the RPATH instead of RUNPATH of my program, as I read that when the RPATH of the executable is set, the dynamic linker will use that to find all transitive dependencies as well.

# append to previous code
target_link_options(myprogram PRIVATE -Wl,--disable-new-dtags)

But I then encountered this issue (c++ - Binary with RPATH not finding transitive dependencies if one of them has RUNPATH - Stack Overflow) where transitive libraries of a dependent libraries with a RUNPATH set cannot be found. When I try to launch my program, I see that libgtsam-metis.so cannot be found, even though it exists in /opt/foo/lib and the RPATH of myprogram points there.

In this case, my libraryA directly links to the library libgtsam.so, but libgtsam.so has its RUNPATH set to when I installed it on my build machine (/usr/local/GTSAM). libgtsam.so itself has a dependency on another library libgtsam-metis.so (this is the transitive dependency that cannot be found, even though both were successfully picked up and placed in /opt/foo/lib). When I inspect the libgtsam.so library on the install machine: readelf -d /opt/foo/lib/libgtsam.so I see that the runpath still points to /usr/local/GTSAM (which does not exist on the install machine).

  1. In the end, I gave up on trying to set RPATH/RUNPATH and packaged a foo.conf file to /etc/ld.so.conf.d to point to /opt/foo/lib but I think that’s a bad solution as it also exposes my bundled libraries to other programs on the install machine - is there a cleaner solution?
  2. Is it possible to get cmake to overwrite the RUNPATH’s of all dependencies installed during install(RUNTIME_DEPENDENCY_SET ...) to whatever CMAKE_INSTALL_RPATH is set to? I think such a solution would be the the nicest.
    I was looking around and it may be possible using CPACK_PRE_BUILD_SCRIPTS + patchelf, so that I don’t have to force an RPATH, and just ensure that every library/executable in my package has their RUNPATH point to /opt/foo/lib

Thank you!