【问题标题】:Transitive RPATH property in CMakeCMake 中的传递 RPATH 属性
【发布时间】:2026-01-12 05:30:01
【问题描述】:

我正在构建一个包含一些测试和示例程序的库。该项目能够尊重 cmake 属性BUILD_SHARED_LIBS,如果它打开,则构建一个.so,如果它关闭,则构建一个.a 文件。该库依赖于其他一些动态库,应该使用环境变量LIBLOC(他们的约定,不是我的)找到这些动态库。

当我构建库的动态版本时,我可以设置安装 rpath 以便正确找到库。例如:

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

然后在每个测试/示例程序的 CMakeLists.txt 中:

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

BUILD_SHARED_LIBS 开启时,测试程序很开心:

$ 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

我们可以找到一切,因为测试中的rpath是$ORIGIN/../lib,它找到liblib.so,然后liblib.so中的rpath是$LIBLOC,在我的环境中设置为/path/to/dependency

我原以为,在安装静态库时,cmake 会从我的库中读取INSTALL_RPATH 属性并将其放入测试程序中,这样测试中的rpath 将是$ORIGIN/../lib;$LIBLOC。相反,$LIBLOC 不会出现在任何 rpath 中。

我当前的解决方案令人不满意:我在每个示例或测试程序中将INSTALL_RPATH 设置为BUILD_SHARED_LIBS,例如

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

现在我有几个目标,所有这些目标都必须了解它们的依赖关系。加上一堆臭的 if 语句。呸!

在 cmake 中是否有更好的方法将安装 rpath “向上”传递到静态编译我的库时生成的第一个 ELF 文件?

【问题讨论】:

  • 语法$LIBLOC不访问CMake中的环境变量。

标签: cmake


【解决方案1】:

CMAKE_INSTALL_RPATH 变量可以在外部设置,并将应用于所有已明确定义RPATH 的目标(即可执行文件和共享库)。

对于您创作或供应商的库,您应该将它们安装到一致的结构中,以便 CMAKE_INSTALL_RPATH 可以在所有目标中统一设置为 $LIBLOC$ORIGIN/../lib。对于真正的依赖项,期望用户在系统范围内安装它们、设置 LD_LIBRARY_PATH 或在 CMake 包配置中使用 find_dependency 宏来定位正确的库是合理的。

如果在创建任何目标之前放置一个可能对您有所帮助的 sn-p:

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

【讨论】: