Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

redneon

macrumors member
Original poster
Apr 27, 2006
77
0
I have an XCode project which copies SDL.framework to my app bundle and, up until recently, this worked fine. I could give my bundle to someone who didn't have SDL.framework and it would work. But yesterday a friend of mine said my app was crashing because it couldn't find SDL.framework. I removed SDL.framework from my /Library/Frameworks folder to test it on my machine and he's right. Even though SDL.framework exists in my bundle the app still crashes because it can't find it.

Why would this be? The only thing I can think of is that the last time I did a working build was on MacOS 10.6 and using XCode 3. I'm now on 10.7. I've tested a new build with XCode 3 and XCode 4 but both produce the problem listed above.
 
Does he have SDL actually installed? None of my old SDL projects run on my new machine because I didn't have the libs installed.
 
If you haven't set the framework's load path or install-name to be app-relative, you'll need to do that.

Step 1 is probably finding out exactly what the current executable is trying to load. In Terminal:
Code:
otool -L /path/to/YourApp.app/Contents/MacOS/YourApp
The output will be a list of libraries/frameworks referenced by your app. Post it.

Step 2 is to learn how @rpath and friends work in libraries, frameworks, and executables. Start here:
https://developer.apple.com/library...s/100-Articles/RunpathDependentLibraries.html

If you didn't know that libraries and frameworks even had a path component aka an "install name", you should probably read about the basics of libraries, and spend some time looking at all the options in the commands otool and install_name_tool. Also, practice using otool and install_name_tool on executables in Apple standard apps (like TextEdit), until you can reliably use them to get info on dylibs.


If you want to diagnose why it was working before, you'll have to understand the above anyway, because it's the only way you'll be able to determine what your executable was actually trying to load. My first guess is it worked by accident, because the framework was already installed. But without evidence, such as from otool and install_name_tool, it's just guessing.
 
Thanks for the reply. I do understand how libraries work. I'm a professional game developer but I primarily use Windows and Linux. I use CMake to generate the XCode project file and don't really understand the intricacies of OSX development (though I'm wanting to get more into it).

Here's the otool output from when the build last worked:

Code:
	@executable_path/../Frameworks/SDL.framework/Versions/A/SDL (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/SDL_mixer.framework/Versions/A/SDL_mixer (compatibility version 1.0.0, current version 1.0.0)
	/System/Library/Frameworks/OpenGL.framework/Versions/A/OpenGL (compatibility version 1.0.0, current version 1.0.0)
	/usr/lib/libstdc++.6.dylib (compatibility version 7.0.0, current version 7.4.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/ApplicationServices.framework/Versions/A/ApplicationServices (compatibility version 1.0.0, current version 34.0.0)
	/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation (compatibility version 300.0.0, current version 677.26.0)
	/System/Library/Frameworks/AppKit.framework/Versions/C/AppKit (compatibility version 45.0.0, current version 949.54.0)

And here it is now it doesn't work:

Code:
	@rpath/SDL.framework/Versions/A/SDL (compatibility version 1.0.0, current version 12.4.0)
	/System/Library/Frameworks/Cocoa.framework/Versions/A/Cocoa (compatibility version 1.0.0, current version 15.0.0)
	@rpath/SDL_mixer.framework/Versions/A/SDL_mixer (compatibility version 1.0.0, current version 13.0.0)
	/System/Library/Frameworks/OpenGL.framework/Versions/A/OpenGL (compatibility version 1.0.0, current version 1.0.0)
	/usr/lib/libstdc++.6.dylib (compatibility version 7.0.0, current version 7.9.0)
	/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 125.2.0)
	/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 550.29.0)
	/System/Library/Frameworks/ApplicationServices.framework/Versions/A/ApplicationServices (compatibility version 1.0.0, current version 38.0.0)
	/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation (compatibility version 300.0.0, current version 751.29.0)
	/System/Library/Frameworks/AppKit.framework/Versions/C/AppKit (compatibility version 45.0.0, current version 1038.32.0)

It looks like the issue stems from the fact that in the previous build is was using the executable path to find the SDL framework from whereas in this build it's using rpath (presumably some kind of default resource path?). I'm not sure what I've changed to make it do that, however.
 
Works:
Code:
	@executable_path/../Frameworks/SDL.framework/Versions/A/SDL (compatibility version 1.0.0, current version 1.0.0)

Doesn't work:
Code:
	@rpath/SDL.framework/Versions/A/SDL (compatibility version 1.0.0, current version 12.4.0)
Taking these two as representative examples, the difference should be plain. In order to understand what it means, though, you'll need to read how @rpath is resolved at runtime by the dylib linker. I already posted a URL that explains run-time dylib references. Presuming it's a resource path is silly. Just read the doc.

As to why or how this change occurred, you should look at the SDL.framework's dylib file.

When the linker links an executable to a framework or lib, you tell it to use that framework or lib, typically as a command-line option or other means for telling the linker what frameworks or libs to use. The linker then looks in the actual dylib file of the lib or framework, and reads the dylib's install-name. It then writes that install-name in the executable as a reference to the dylib.

Note well: the path written into the executable is the install-name path that the library itself contains. So you should use install_name_tool or one of the other otool options to inspect what's in the framework's dylib.

If I were guessing, I'd say you linked the non-working build using a different framework than the working build. I base this guess on two things: first is the install-name (i.e. the @executable... vs. the @rpath pathnames), second is the "current version" values, which are markedly different.

You can change the install-name of the framework's dylib to be the @executable... form, or you can figure out how to use @rpath properly. For example, google search terms: "@rpath"
http://www.dribin.org/dave/blog/archives/2009/11/15/rpath/
 
Brillant. Thanks for the help. I haven't had time to look at it yet but I believe I have enough information to help me sort it out now.
 
If you haven't set the framework's load path or install-name to be app-relative, you'll need to do that.

Step 1 is probably finding out exactly what the current executable is trying to load. In Terminal:
Code:
otool -L /path/to/YourApp.app/Contents/MacOS/YourApp
The output will be a list of libraries/frameworks referenced by your app. Post it.

Nice tool, thanks for sharing.

I'm having a similar problem to the original posters.

Here's what that command outputs for me:

Code:
	/System/Library/Frameworks/WebKit.framework/Versions/A/WebKit (compatibility version 1.0.0, current version 536.28.10)
	/System/Library/Frameworks/Cocoa.framework/Versions/A/Cocoa (compatibility version 1.0.0, current version 19.0.0)
	[b]/Library/Frameworks/ParseKit.framework/Versions/A/ParseKit[/b] (compatibility version 1.0.0, current version 1.0.0)
	/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation (compatibility version 300.0.0, current version 945.16.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/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 744.18.0)
	/System/Library/Frameworks/AppKit.framework/Versions/C/AppKit (compatibility version 45.0.0, current version 1187.37.0)

I've bolded the framework that's having the issues. That path is wrong, but I don't know why.

The framework is actually a sibling of my .xcodeproj file... in my Build Settings tab I've included "$(SRCROOT)" as a Framework Search Path for both my project and my target. This is properly resolved to the path where my .xcodeproj (and by extension, ParseKit.framework) resides. I have tried setting it to both "recursive" and "non-recursive" but no dice... it insists on looking at that incorrect path and not the Framework Search Path I defined.

Suggestions on what I'm doing wrong?
 
Read the links I provided before. They still work.

First, I hope you realize that a framework file (i.e. the dylib for the framework) contains a pathname, which is the installed location of the library: its install name. When you dylink (as opposed to static-link) to a framework file, the linker copies the install-name from the framework file and writes it in your program's executable. It doesn't matter where the framework file is located when this happens. The path to the framework that's written into your executable is the install-name, not the location where the framework file itself is found.

Second, Xcode's "Framework Search Path" tells Xcode (or more specifically the linker) where it should search for framework files (dylibs). This is only useful at link time, when the app is being built. After the linker has run, your executable contains the install-name that was read from the frameworks the linker found. This is what 'otool' is showing you: the install-name for ParseKit (i.e. its absolute path) that was read at link-time and written into your executable.

So if you link to a framework (ParseKit), and that framework file (the dylib file) is found in a sibling to your project, the linker will copy the install-name from that file (the ParseKit install-name) and write it into your executable. Presumably, the sibling framework ParseKit has an install-name (install-path) of:
Code:
	/Library/Frameworks/ParseKit.framework/Versions/A/ParseKit
If you want it to be relative to your app, you need to change the install-name in that framework file, then relink your app. See the man page for install_name_tool.

To understand what the relative path should be, see the aforementioned links that document @rpath and @executable_path. You ought to read the entire "Dynamic Library Programming Topics", of which the "Run-Path Dependent Libraries" page is only one small part.
 
Read the links I provided before. They still work.

First, I hope you realize that a framework file (i.e. the dylib for the framework) contains a pathname, which is the installed location of the library: its install name. When you dylink (as opposed to static-link) to a framework file, the linker copies the install-name from the framework file and writes it in your program's executable. It doesn't matter where the framework file is located when this happens. The path to the framework that's written into your executable is the install-name, not the location where the framework file itself is found.

Second, Xcode's "Framework Search Path" tells Xcode (or more specifically the linker) where it should search for framework files (dylibs). This is only useful at link time, when the app is being built. After the linker has run, your executable contains the install-name that was read from the frameworks the linker found. This is what 'otool' is showing you: the install-name for ParseKit (i.e. its absolute path) that was read at link-time and written into your executable.

So if you link to a framework (ParseKit), and that framework file (the dylib file) is found in a sibling to your project, the linker will copy the install-name from that file (the ParseKit install-name) and write it into your executable. Presumably, the sibling framework ParseKit has an install-name (install-path) of:
Code:
	/Library/Frameworks/ParseKit.framework/Versions/A/ParseKit
If you want it to be relative to your app, you need to change the install-name in that framework file, then relink your app. See the man page for install_name_tool.

To understand what the relative path should be, see the aforementioned links that document @rpath and @executable_path. You ought to read the entire "Dynamic Library Programming Topics", of which the "Run-Path Dependent Libraries" page is only one small part.

If I were to move my framework into /Library, would I be able to upload the application to the Mac App Store? I'd think not - Apple requires the executable to be self contained, correct?

I need to read up more on this stuff and figure out what I want to do...

Edit: Dynamic libraries / frameworks are kind of dumb... I'm going to try to use only static frameworks (besides Apple's) in the future.
 
Last edited:
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.