views:

67

answers:

3

I'm writing a framework (called Lighthouse.framework) that, in turn, uses code from another framework (RegexKit.framework, to be precise). I have copied RegexKit.framework into my own framework, so that it has a structure like the following:

Lighthouse.framework/
  Versions/
    A/
      Frameworks/
        RegexKit.framework
      Lighthouse

However, when I try to run an application that uses Lighthouse.framework (my framework), I get the following error:

dyld: Library not loaded: @executable_path/../Frameworks/RegexKit.framework/Versions/A/RegexKit

Referenced from: /Users/mdippery/Developer/Projects/Current/lighthouse/build/Debug/Lighthouse.framework/Versions/A/Lighthouse

Reason: image not found

Obviously, the loader isn't finding RegexKit.

Here're the paths the loader expects to load, courtesy otool:

build/Debug/Lighthouse.framework/Versions/A/Lighthouse:
    /Users/mdippery/Library/Frameworks/Lighthouse.framework/Versions/A/Lighthouse (compatibility version 1.0.0, current version 1.0.0)
    /System/Library/Frameworks/Cocoa.framework/Versions/A/Cocoa (compatibility version 1.0.0, current version 12.0.0)
    @executable_path/../Frameworks/RegexKit.framework/Versions/A/RegexKit (compatibility version 0.4.0, current version 0.6.0)
    /usr/lib/libgcc_s.1.dylib (compatibility version 1.0.0, current version 1.0.0)
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 111.1.4)
    /usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 227.0.0)
    /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 476.19.0)
    /System/Library/Frameworks/Foundation.framework/Versions/C/Foundation (compatibility version 300.0.0, current version 677.26.0)

Can I include a framework in another framework? Is this the proper way to do it? How can I resolve my error?

+1  A: 

Yes, you can.

However, you need the included framework to "know" what its installed location will be at the time that it is built; otherwise, dyld won't be able to find it at run time, as you saw.

The relevant settings in XCode, if I recall correctly, are "Installation Directory" and "Framework Install Name". The latter probably won't matter for your usage, but you're going to need the former to be something along the lines of: @executable_path/../Frameworks/Lighthouse.framework/Versions/A/Frameworks/RegexKit.framework/Versions/A/

Stephen Canon
Do I set the installation directory when building Lighthouse.framework (for Lighthouse.framework)? I gave that a try, but it didn't work (although maybe I did something wrong).
mipadi
@mipadi: If you're not building `RegExKit.framework`, you won't be able to do this. Why do you want RegExKit to be contained in the other framework? What you're doing will work just fine without that.
Stephen Canon
It would be nice if consumers of my framework only had to include my framework, and not also include my framework's dependencies.
mipadi
+1  A: 

The easiest way is to use @rpath. Your configuration should look like:

  1. Set RegExKit.framework's Installation Directory to @rpath
  2. Set Lighthouse.frameworks's Installation Directory to @rpath
  3. Set Lighthouse.framework's Runpath Search Paths to @loader_path/Frameworks
  4. Ensure that RegExKit.framework is copied into Lighthouse.framework's Framework subfolder (use a custom build phase for this)

Finally, any applications linking to Lighthouse.framework should set Runpath Search Paths to @loader_path/../Frameworks

sbooth
I'm not building RegexKit.framework (I'm using a pre-compiled binary). Can I still set the installation directory for it (I didn't see a way to do that)?
mipadi
You can use install_name_tool for this- something like install_name_tool -change @executable_path/../Frameworks @rpath/Frameworks
sbooth
A: 

I discovered a fix for this problem. I incorporated some ideas from sbooth's answer, but the fix was simpler. I ran this script:

install_name_tool -change @executable_path/../Frameworks/RegexKit.framework/Versions/A/RegexKit @loader_path/Frameworks/RegexKit.framework/Versions/A/RegexKit "${TARGET_BUILD_DIR}/${PRODUCT_NAME}.framework/Versions/A/${PRODUCT_NAME}"

as a Run Build Script phase.

Note that, for the general case, you have to change @executable_path/../ to @loader_path/, and all is well.

mipadi