My favorites | Sign in
Project Home Downloads Wiki Issues Source
Search
for
RpathSaver  
Demonstration of using @rpath in a framework's install path.
Updated Feb 4, 2010 by preston....@gmail.com

Background

I wrote a couple of blog entires at http://setdoggedly.blogspot.com about my troubles with frameworks. My problem seems unusual but it might be familiar to plugin writers. I found that I couldn't link to a private framework from both a plugin and from one of the plugin's frameworks. However, in !MacOS X 10.5 (Leopard), new features have been added to ld, the linker used at compile time, and dyld, the dynamic linker used at run time, to add arbitrary freedom in specifying the installed path of dynamically linked libraries.

@rpath

The solution is elegant. Instead of requiring a framework or dylib to have an install location relative to @loader_path, the path of the code requiring the framework to be loaded, or @executable_path, the path of the main executable, they can now have an install location relative to @rpath, the "run path" specified in the code requiring the framework to be loaded. This is explained pretty well in the Release Notes.

The process of using @rpath requires two steps.

  1. The framework (or dylib) specifies its install directory to be relative to the "run path" (the "Dynamic Library Install Name" build setting in Xcode or the -install_name command line flag in ld). For example, the framework example below uses the name @rpath/LevelOne.framework/Versions/A/LevelOne as its "Dynamic Library Install Name." Xcode makes this a little easier by allowing you to specify the "Install Name" as "@rpath" and then it figures out the rest.
  2. The application (or plugin, or another framework) that loads the framework must specify its "run path." Specifying the "run path" is done in Xcode by specifying the "Runpath search paths" build setting or the -rpath option if using ld from the command line.

And that's it. It's pretty easy and almost limitless in flexibility.

Example

Everything is simpler with an example. Here's the scenario: I want to write a screen saver plugin called RpathSaver.saver that uses a framework called LevelTwo.framework. The plugin and the framework require loading a second framework called LevelOne.framework. I can't use an install path relative to the @executable_path, since that would require installing the screen saver plugin is in a known location (i.e. /System/Library/Screen Savers/. Bad form. I can't use an install path relative to the @loader_path since both the plugin and the framework are loaders of LevelOne.framework. It seems I have three bad options.

  1. Pull the executable code out of the two frameworks and put it in RpathSaver.plugin/Contents/MacOS with the rest of the executable code and specify their install path to be @loader_path.
  2. Put the frameworks in a global location like System/Library/Application Support.
  3. Statically link all of the source together.

But in Mac OS X 10.5, I can use the @rpath in the install path, and everything sorts itself out. Here's the solution:

  1. Set the runpath of the RpathSaver.saver plugin to be @loader_path/../Frameworks. That will let dyld search in the frameworks directory of the plugin for loadable code.
  2. Set the runpath of the LevelTwo.framework to be @loader_path/../../... That will let dyld search in the frameworks directory of the plugin for loadable code.
  3. Set the install directory of LevelTwo.framework to be @rpath/LevelTwo.framework/Versions/A/LevelTwo. At run time, dyld will replace the @rpath portion with the runpath specified in the loading code (in this case the plugin) and will find the framework in @loader_path/../Frameworks/LevelTwo.framework/Versions/A/LevelTwo.
  4. Set the install directory of LevelOne.framework to be @rpath/LevelOne.framework/Versions/A/LevelOne. At runtime, the @rpath will be replaced by the runpath of the loading code. In the case of the RpathSaver.saver plugin, it will load from this path: @loader_path/../Frameworks/LevelOne.framework/Versions/A/LevelOne. In the case of LevelTwo.framework, in will load from this path: @loader_path/../../../Frameworks/LevelOne.framework/Versions/A/LevelOne.

I'm enclosing the sample code below. It is the screen saver code that I discuss above. It simply displays some text from LevelOne.framework and LevelTwo.framework, which in turn uses LevelOne.framework to generate its own text. I hope you find it useful.

RpathSaver.saver download code


Sign in to add a comment
Powered by Google Project Hosting