如何在Mac OS X中使用dylib(C ++)

37

我制作了一个应用程序(一个可执行文件),成功调用了一些动态链接库(dylib)。但是,这些dylib文件和可执行文件在不同的目录中。我将包含dylib文件的目录添加到$PATH环境变量中,但它仍然无法加载。当我将所有dylib文件复制到可执行文件所在的目录时,程序终于运行了。这证实了dylib文件没有问题。但是,我该如何告诉操作系统去查找它呢?
在Windows中,我只需要将包含dll文件的目录路径添加到$PATH中。对于Mac OS X,我需要做什么呢?


6
使用命令otool -L app检查您的应用程序的依赖项。如果它们是硬编码为绝对路径,则使用命令install_name_tool -change old new file修改它们以变成相对路径。然后,链接器可能会在您的DYLD_LIBRARY_PATH环境变量中找到一个库。 - linuxbuild
4个回答

40
阅读Justin提供的链接后,我成功地使用@executable_path标记将我的dylib install_name更改为指向与我的可执行文件位于同一目录的位置。 @executable_path 绝对路径很烦人。有时您想将框架嵌入应用程序,而不是将框架安装到/Library或类似位置中。Mac的解决方法是@executable_path。这是一个神奇的令牌,当放置在库的安装名称开头时,会扩展为加载它的可执行文件的路径,减去最后一个组件。例如,假设Bar.app链接到Foo.framework。如果Bar.app安装在/Applications中,则@executable_path将扩展为/Applications/Bar.app/Contents/MacOS。如果您打算将框架嵌入Contents/Frameworks中,则可以将Foo.framework的安装名称设置为@executable_path/../Frameworks/Foo.framework/Versions/A/Foo。动态链接器将将其扩展为/Applications/Bar.app/Contents/MacOS/../Frameworks/Foo.framework/Versions/A/Foo,并在那里找到框架。 http://www.mikeash.com/pyblog/friday-qa-2009-11-06-linking-and-install-names.html 我将举例说明。
假设我有以下可执行文件/opt/local/bin/convert,它的dylibs在/opt/local/lib中。 我想将其复制到另一个目录,并使其从与我复制可执行文件的同一目录加载dylibs。
> mkdir ~/tmp/bin
> cp /opt/local/bin/convert ~/tmp/bin

获取可执行文件dylibs的列表
> otool -L ~/tmp/bin/convert
~/tmp/bin/convert:
    /opt/local/lib/libtiff.3.dylib (compatibility version 13.0.0, current version 13.5.0)
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 159.1.0)
    /opt/local/lib/libjpeg.8.dylib (compatibility version 12.0.0, current version 12.0.0)
    /opt/local/lib/libfontconfig.1.dylib (compatibility version 6.0.0, current version 6.4.0)
    /opt/local/lib/libiconv.2.dylib (compatibility version 8.0.0, current version 8.1.0)
    /opt/local/lib/libfreetype.6.dylib (compatibility version 15.0.0, current version 15.0.0)
    /opt/local/lib/libexpat.1.dylib (compatibility version 7.0.0, current version 7.2.0)
    /opt/local/lib/libbz2.1.0.dylib (compatibility version 1.0.0, current version 1.0.6)
    /opt/local/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.6)
    ...

我只关心位于/opt/local/lib目录中的动态库,因此我们只提取/opt中的动态库。我希望保留所有其他动态库引用,特别是对/usr/lib/libSystem的引用。
> DYLIBS=`otool -L ~/tmp/bin/convert | grep "/opt" | awk -F' ' '{ print $1 }'`

将可执行文件引用的所有dylibs复制到可执行文件所在的相同目录中。
> for dylib in $DYLIBS; do cp $dylib ~/tmp/bin/; done;

使用install_name_tool更改我们在上一步中提取的所有dylib的安装名称,并通过在dylib名称前添加@executable_path来替换它们。这将使动态链接器在可执行文件所在的目录中查找dylib。
> for dylib in $DYLIBS; do install_name_tool -change $dylib @executable_path/`basename $dylib` ~/tmp/bin/convert; done;

确认已更改安装名称,并且libSystem仍然指向/usr/lib/libSystem

> otool -L ~/tmp/bin/convert
~/tmp/bin/convert:
    @executable_path/libtiff.3.dylib (compatibility version 13.0.0, current version 13.5.0)
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 159.1.0)
    @executable_path/libjpeg.8.dylib (compatibility version 12.0.0, current version 12.0.0)
    @executable_path/libfontconfig.1.dylib (compatibility version 6.0.0, current version 6.4.0)
    @executable_path/libiconv.2.dylib (compatibility version 8.0.0, current version 8.1.0)
    @executable_path/libfreetype.6.dylib (compatibility version 15.0.0, current version 15.0.0)
    @executable_path/libexpat.1.dylib (compatibility version 7.0.0, current version 7.2.0)
    @executable_path/libbz2.1.0.dylib (compatibility version 1.0.0, current version 1.0.6)
    @executable_path/libz.1.dylib (compatibility version 1.0.0, current version 1.2.6)
    ...

请注意,似乎您需要针对每个库文件执行此操作,因为它们也可能包含指向绝对路径的链接。 - Claudiu
1
整个“install_name_tool”操作必须递归进行,直到您到达仅依赖于系统库的“叶子”lib,即位于“/usr/lib”或“/System/Library”下的内容。 - kakyo

15

您需要设置DYLD_LIBRARY_PATH环境变量。

根据dyld man页面

      This  is  a  colon  separated  list  of directories that contain libraries. The dynamic linker
      searches these directories before it searches the default locations for libraries.  It  allows
      you to test new versions of existing libraries.

      For  each  library  that  a program uses, the dynamic linker looks for it in each directory in
      DYLD_LIBRARY_PATH in turn. If it still can't find the library,  it  then  searches  DYLD_FALL-
      BACK_FRAMEWORK_PATH and DYLD_FALLBACK_LIBRARY_PATH in turn.

谢谢!我也是通过谷歌找到这个的。我已经设置了DYLD_LIBRARY_PATH,但它没有起作用,问题仍然存在!我很困惑。 - Alfred Zhong
嗨,EmeryBerger,你的DYLD_LIBRARY_PATH变量最初是空的。我的也是。我不知道这是否一开始就是错误的。 - Alfred Zhong
但是@Justin答案中的页面包含了更好的解决方案,即使用-rpath编译您的代码并将其指向该目录(如果这对您是可选项)。 - EmeryBerger
我阅读了这个文档,但是无法理解。我尝试向链接器标志中添加-rpath <动态库路径>,但它没有起作用,向编译器标志中添加也没有效果... 我只是想知道为什么 DYLD_LIBRARY_PATH 对我不起作用?难道我需要设置其他内容吗? - Alfred Zhong
2
@AlfredZhong 确保在设置 DYLD_LIBRARY_PATH 时使用 export。详情请参见此处:http://stackoverflow.com/a/8699244/402807 - AlcubierreDrive
显示剩余2条评论

6

2
如果dylib文件在库的INSTALL_NAME指定的位置,它将可以正常工作*。
否则,您可以将dylib文件的位置添加到DYLD_LIBRARY_PATH中。您可能希望阅读dyld文档
*) 要完全准确,它需要在DYLD_ROOT_PATH/INSTALL_NAME下,但是调整DYLD_ROOT_PATH相当罕见。

抱歉,什么是“库的INSTALL_NAME”?我添加了DYLD_LIBRARY_PATH,但它没有起作用,所以很困惑。 - Alfred Zhong
你需要导出变量:export DYLD_LIBRARY_PATH=/usr/local/lib` - Michel

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