XCode 4.4.1忽略LD_RUNPATH_SEARCH_PATHS

时间:2012-09-01 02:02:18

标签: objective-c xcode cocoa sbjson

我正在处理XCode的奇怪行为:

dyld: Library not loaded: /Library/Frameworks/SBJson.framework/Versions/A/SBJson

基本上它会忽略我的Runpath Search PathLD_RUNPATH_SEARCH_PATHS)配置,实际上是@loader_path/../Frameworks

我现在无法加载任何嵌入式框架:/

otool

otool -L /Users/kilian/Library/Developer/Xcode/DerivedData/r-ghohkslxtxgpnuepmblogfjtuefx/Build/Products/Debug/r.app/Contents/MacOS/r
/Users/kilian/Library/Developer/Xcode/DerivedData/r-ghohkslxtxgpnuepmblogfjtuefx/Build/Products/Debug/r.app/Contents/MacOS/r:
  /System/Library/Frameworks/Cocoa.framework/Versions/A/Cocoa (compatibility version 1.0.0, current version 19.0.0)
  /Library/Frameworks/SBJson.framework/Versions/A/SBJson (compatibility version 1.0.0, current version 37.0.0)
  /System/Library/Frameworks/Foundation.framework/Versions/C/Foundation (compatibility version 300.0.0, current version 945.0.0)
  /usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0)
  /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 169.3.0)
  /System/Library/Frameworks/AppKit.framework/Versions/C/AppKit (compatibility version 45.0.0, current version 1186.0.0)

P.S。如果您想知道我是否将副本添加到框架构建阶段,答案是肯定的。

1 个答案:

答案 0 :(得分:19)

短版

问题在于,当您构建SBJSON.framework时,安装名称未配置为使用@rpath。因此,当您的应用加载动态库时,它会查看/Library/Frameworks而不是应用的运行路径告诉它的位置。

要解决此问题,您需要更改SBJSON.framework目标上的构建设置。将动态库安装名称设置更改为@rpath/${EXECUTABLE_PATH}。然后再次构建,链接新构建的框架,你很高兴。

长版本

框架的核心是动态库。这意味着库中包含的代码未嵌入到您的应用中,而是在运行时从SBJSON.framework中拉入。为此,您的应用需要知道在哪里寻找动态库。

这种方式的工作方式是,当您链接到框架时,链接器实际上并没有将整个库嵌入到您的应用程序中。相反,它只是添加了一个小部分,告诉应用程序在运行时在哪里找到库。当然,这意味着编译器必须知道库在运行时的位置。它通过查看动态库的“安装名称”来查找信息。

动态库的“安装名称”基本上只是库预期的路径。从历史上看,大多数动态库和框架都是在系统范围内共享的。例如,Foundation.frameworkCoreData.framework等内容位于/System/Library/Frameworks,所有应用都可以合理地期望在那里找到它们。因此,在构建CoreData.framework时,其安装名称将设置为/System/Library/Frameworks/...。当Xcode将您的应用与Core Data链接时,它会查看框架的安装名称,并告诉它在应用程序启动时加载到该路径的框架中。

这一切都很好,但是当你需要在你的应用程序中嵌入一个框架时,它并没有帮助你。您不知道应用程序在运行时将位于系统上的位置。用户可以从/Applications运行它,但也可以从~/Downloads运行它。您无法提供单个路径作为安装名称,它始终在运行时始终正确指向框架。

要处理此问题,您可以将框架的安装名称设置为@loader_path/../Frameworks。当动态加载程序看到@loader_path时,它会将其替换为当前加载的应用程序的路径。使用此安装名称将允许框架安装在任何应用程序的Frameworks文件夹中。

然而,事情仍然不完美。该框架仍在决定应该放置的位置。例如,如果另一个应用程序想要将框架放在Libraries文件夹中,那就不幸了。该框架负责放置它的位置,而不是应用程序。这是依赖树的反转,并不理想。应用程序应该能够从想要隐藏它的地方加载框架,而不管其他框架是做什么的。

因此,在OS X 10.5中,引入了@rpath。如果dylib或框架的安装名称以@rpath开头,则加载器将转向并询问应用程序它的“Runpath搜索路径”是什么,并替换它们。这允许应用程序指定其框架的位置会活下去的通过使用@rpath,框架将决策委托给应用程序。如果需要,应用可以使用@loader_path,或者可以根据需要指定绝对路径。它甚至可以指定一整套应用程序将使用的共享文件夹。

您的问题

所以,关于你的问题。您正确地将应用程序的运行路径搜索路径设置为@loader_path/../Frameworks。但是,框架的安装名称未使用@rpath;实际上,它仍在使用/Library/Frameworks/...的硬编码路径。由于框架的安装名称不使用@rpath,因此甚至不会查看应用程序的运行路径。它只是尝试从/Library文件夹中链接SBJSON。由于它不存在,您的应用程序甚至会在启动之前崩溃。

您需要更改SBJSON框架的安装名称以使用@rpath。将动态库安装名称设置为@rpath/${EXECUTABLE_PATH}。 (${EXECUTABLE_PATH}是内部动态库的相对路径,来自包含框架的文件夹。)

使用新安装名称构建框架后,您应该能够链接到新框架,确保将其复制到应用程序包的Frameworks/文件夹中,您就可以开始了!

P.S。如果不是很清楚,Mike Ash对@rpath和朋友们进行了很好的评论。你可以find it here