CMake:在MacOS上动态链接资源时出现运行时错误(dyld:未加载库)

9

问题

在MacOS上,我在依赖于动态链接资源的CMake项目运行时遇到了连接问题——但仅在安装该项目后才会出现!如果只构建二进制文件而未将其安装,则不会出现此问题。

$ ./testapp
Hello world!
$ $INSTALLDIR/testapp
dyld: Library not loaded: @rpath/libvtkDomainsChemistryOpenGL2-7.1.1.dylib
  Referenced from: /Users/normanius/workspace/installdir/testapp
  Reason: image not found
[1]    76964 trace trap  /Users/normanius/workspace/installdir/testapp   

最简单的例子

我能够在一个最小的设置中重现问题,该设置由CMakeLists.txtmain.cpp组成。我链接的库叫做VTK(v7.1.1),它已经使用共享库构建(有关更多详细信息,请参见下文)。

# CMakeLists.txt
cmake_minimum_required(VERSION 2.8 FATAL_ERROR)
project(test)

# Test application.
add_executable(testapp
               main.cpp)

# Find vtk (library that has to be linked to dynamically).
find_package(VTK REQUIRED)
include(${VTK_USE_FILE})
target_link_libraries(testapp ${VTK_LIBRARIES}) # <---- this causes the problem

# Install instructions.
install(TARGETS testapp DESTINATION "${CMAKE_INSTALL_PREFIX}")

main.cpp并没有使用任何VTK对象。

// main.cpp
#include <iostream>

int main (int argc, char* argv[])
{
    std::cout << "Hello world!" << std::endl;
    return 0;
}

我使用以下命令构建项目。 我设置了标志CMAKE_PREFIX_PATH,以向CMake提供有关在哪里找到VTK库的提示。

$ INSTALLDIR="path/to/installation"
$ mkdir build && cd build
$ cmake .. -DCMAKE_PREFIX_PATH="$DEVPATH/lib/vtk/cmake" \
           -DCMAKE_BUILD_TYPE=Release \
           -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" 
$ make
$ make install

当在构建文件夹中执行testapp时,一切看起来都很好:
$ ./testapp
Hello world!
$ cp testapp $INSTALLDIR/testapp
$ $INSTALLDIR/testapp
Hello world!

然而,如果我在 INSTALLDIR 中运行可执行文件,会出现运行时错误:

$ $INSTALLDIR/testapp
dyld: Library not loaded: @rpath/libvtkDomainsChemistryOpenGL2-7.1.1.dylib
  Referenced from: /Users/normanius/workspace/installdir/testapp
  Reason: image not found
[1]    76964 trace trap  /Users/normanius/workspace/installdir/testapp    

当我在 CMakeLists.txt 中删除 target_link_libraries() 指令时,问题自然而然地消失了。
那么安装 CMake 项目时到底会发生什么?我的情况出了什么问题?我测试了不同的 CMake 版本(3.5、3.9 和 3.10),但行为都是相同的。
详情:
显然,在 MacOS 上,示例的 RPATH 机制没有正确设置。
这是 testapp 二进制链接结构的摘录:
$ otool -L testapp
testapp:
    @rpath/libvtkDomainsChemistryOpenGL2-7.1.1.dylib (compatibility version 0.0.0, current version 0.0.0)
    @rpath/libvtkFiltersFlowPaths-7.1.1.dylib (compatibility version 0.0.0, current version 0.0.0)
    @rpath/libvtkFiltersGeneric-7.1.1.dylib (compatibility version 0.0.0, current version 0.0.0)
    @rpath/libvtkFiltersHyperTree-7.1.1.dylib (compatibility version 0.0.0, current version 0.0.0)
...

由于这可能会影响VTK库(另一个CMake项目)的构建方式:为了支持Python,必须设置项目标志VTK_WRAP_PYTHON=ONBUILD_SHARED_LIBS=ON。安装前缀设置为CMAKE_INSTALL_PREFIX="$VTK_INSTALL_DIR"。为确保在运行时找到资源,还必须通过CMAKE_MACOSX_RPATH=ONCMAKE_INSTALL_RPATH="$VTK_INSTALL_DIR/lib"启用RPATH支持。

总结

当使用make install安装项目时,我有什么概念上的错误?这会发生什么?这个问题可以在CMake中解决吗?还是只与VTK以及共享库的构建方式有关?
2个回答

12
CMake在运行make install时会更改所有已安装目标的RPATH。
想象一下,在同一个CMake项目中构建共享库和可执行文件。为了能够运行可执行文件,它必须能够在运行时动态加载共享库。因此,默认情况下,CMake将构建树中动态库的完整(绝对)路径添加到可执行文件的rpath中。这对于开发非常方便,因为我们可以直接从构建树运行可执行文件,但我们可能不想以这种方式发布可执行文件。
这就是为什么CMake会在安装时更改rpath以仅包含可移植路径(即删除指向构建树的条目)。也就是说,除非将共享库放入系统默认位置之一,否则在安装后可执行文件将无法找到它。
然而,CMake允许您指定一个安装rpath,该rpath将用指定的路径替换已删除的构建树条目。有关详细信息,请参见INSTALL_RPATHINSTALL_RPATH_USE_LINK_PATH目标属性。

由于所有这些rpath的内容都与平台有关,因此OSX配备了自己的特殊规则。可以在(不幸的是相当过时的)CMake wiki上找到相当全面的解释:

与其他UNIX不同,Darwin链接器dyld使用每个dylib的完整路径来定位依赖的动态库。例如,在可执行文件“foo”中,记录的完整路径是每个依赖dylib的安装名称。并且库“/usr/lib/libSystem.dylib”的安装名称为“/usr/lib/libSystem.B.dylib”,如“otool-D”所示。当链接到“foo”时,“foo”对“/usr/lib/libSystem.B.dylib”有一个依赖项。可以使用“otool-L foo”查看此依赖项。对于可重定位二进制文件,@executable_path、@loader_path和@rpath可供使用。在“foo”示例中,@executable_path和@loader_path被替换为“foo”的位置。@rpath用“foo”中的RPATH来定位依赖的dylibs。因此,RPATH机制就发挥作用了。链接器将按以下顺序搜索@rpath/依赖项:

  • DYLD_LIBRARY_PATH——一个保存目录列表的环境变量
  • RPATH——链接到可执行文件中的目录列表。它们可以包含@loader_path和@executable_path。
  • 内置目录——/lib /usr/lib
  • DYLD_FALLBACK_LIBRARY_PATH——一个保存目录列表的环境变量

你应该能够通过调整相应的目标属性来解决这个问题,但是这需要一些细致的操作,并且很难做到完美。


2
太棒了!非常感谢您的回答!!!我终于明白它是如何工作的了。确实,如果我在cmake命令中添加标志-DCMAKE_INSTALL_RPATH_USE_LINK_PATH="ON",问题就解决了! - normanius
我完全同意共享库链接有些麻烦。在Mac OS ElCapitan之前,我通常依赖于DYLD_LIBRARY_PATH技巧,但这个“hack”并不总是有效,因为苹果引入了系统完整性保护,这是我在早期的SO帖子中处理过的问题。 - normanius

2
这是解决我的问题的方法:

最初的回答:

set(CMAKE_MACOSX_RPATH OFF)

add_library(your-lib SHARED)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接