×

Xcode 中的链接路径问题

96
纸简书生
2016.11.10 19:36* 字数 1095

关于这个知识点,如果你没有遇到类似的坑是不会去关注的。这里做个总结。

MacDown项目的Link设置

install Name

install Name 本质是一个路径,告诉连接器在运行时在哪里找到需要的库。比如libfoo.dylib有一个/usr/lib/libfoo.dylib.的install name.在链接的时候install name 会被拷贝到应用程序。当动态链接器需要libfoo.dylib的时候,它将会从应用程序中找到这个install Name,然后知道在/usr/lib/找到libfoo.dylib这个库。

executable_path

有时候你需要嵌入一个库到应用程序中,而不是将这个库安装到\Library下,绝对路径是不合适的。

Mac下的解决方案就是@executable_path。当放在 install Name 前面的时候,比如Bar.app依赖于Foo.framework,并且Bar.app安装在/Applications@executable_path被展开为/Applications/Bar.app/Contents/MacOS。如果想嵌入一个库在Contents/Frameworks。只需要设置Foo.framework的install Name 为@executable_path/../Frameworks/Foo.framework/Versions/A/Foo。动态链接库会把它扩展为/Applications/Bar.app/Contents/MacOS/../Frameworks/Foo.framework/Versions/A/Foo。这样就可以找到库了。

loader_path

查找可执行文件并不总是好用的,想象一下你需要传递一个已经嵌入了另一个库的库。比如Foo.framework嵌入Baz.framework.即使是Foo.framework请求加载,当确实@executable_path的具体指向的时候,动态链接器也找不到Bar.app

苹果提供了@loader_path解决类似问题。他会扩展所有的路径,去掉最后的部分,无论是什么原因让库加载。如果是应用程序,则和@executable_path一样,如果是Framework或者plugin,则和Framework或者plugin相关,这样更有用。

@rpath

上面的方案是可行的,但也有问题。问题是,库的单个拷贝只能用一种方式使用。如果想当Foo.framework嵌入在一个应用程序或者安装到/Library/Frameworks使用,你需要提供两个单独的不同的install name包。

苹果提供了@rpath解决这个问题。当把@rpath 放在install name前面,就将告诉动态链接器去本地的列表搜索这个库。这个列表嵌入到这个应用程序,因此能够被应用程序的编译过程控制,而不是framework.因此单个framework可以用于多个目的。

To make this work, Foo.framework's install name would be set to @rpath/Foo.framework/Versions/A/Foo. An application that intends to embed Foo.framework would then pass -rpath @executable_path/../Frameworks to the linker at build time, which tells the dynamic linker to search for @rpath frameworks there. An application that intends to install the framework would pass -rpath /Library/Frameworks, telling the dynamic linker to search there. An application that for some reason doesn't want to commit to one or the other at build time can just pass both sets of parameters, which will cause the dynamic linker to try both locations.

例子

Absolute paths

对于安装在共享位置的库可用

Install path: /Library/Frameworks/Foo.framework/Versions/A/Foo

@executable_path

用于内嵌在应用程序中的库,允许设置相对于应用程序可执行文件的路径。

  • Install path: @executable_path/../Frameworks/Foo.framework/Versions/A/Foo
  • Application location: /Applications/Foo.app
  • Executable path: /Applications/Foo.app/Contents/MacOS
  • Framework location: /Applications/Foo.app/Contents/Frameworks/Foo.framework
  • 把相关的路径链接在一起确定库的路径: /Applications/Foo.app/Contents/MacOS/../Frameworks/Foo.framework/Versions/A/Foo

@loader_path

Mac OSX 10.4才可用。用于内嵌在插件中的库,允许设置这个库相对于插件的路径。(注意,相对于应用程序而言,插件不知道他从哪里被加载。所以知道@executable_path是没有用的。)

@rpath

Mac OSX 10.5之后可用

  • @rpath知识动态连接器,所有路径列表来找到对应的库。
  • 重要是,这个路径是了已经加载的应用中存在
  • 意味着当个库,比如@rpath/Foo.framework/Versions/A/Foo能够通过多种方式使用。也就是不在限制通过@executable_path or @loader_path设置install name
  • 但是你必须传递额外的链接标识,当编译主应用的时候

参考

@executable path, @load path and @rpath

MacOS平台下@rpath在动态链接库中的应用

Build Settings中的变量@rpath,@loader_path,@executable_path

客户端技术
Web note ad 1