« Frameworks Within The App Bundle

Posted by Brian Christensen on November 13, 2001 [Feedback (12) & TrackBack (0)]

// Introduction

You might have come across several third-party frameworks such as the Omni frameworks or perhaps you wrote your own for easy code reuse across several applications. If you use a framework like that in your application, you probably don't want your users to have to install it separately in /Library/Frameworks just to run your app. Trust me, they will not appreciate it and it most certainly isn't the Mac way of doing things. A much better alternative is to simply include the relevant frameworks in your application's bundle. That way you can avoid having to make an installer and let your users comfortably drag your application to their desired location.

OmniWeb
Figure 1: OmniWeb is an example of an application that makes heavy use of frameworks, all of which are included within its own app bundle. (Go have a look inside OmniWeb.app/Contents/Frameworks to see for yourself.)

 

// Framework Settings

First off, we need to make some changes in your framework's build settings. Open your framework project file in Project Builder and switch to the "Targets" tab in the left-hand pane. Select the target from the resulting list on the left to edit its settings. On the right, you'll see several tabs for the different settings. Click on the "Build Settings" tab and scroll down to the "Installation Location" heading. As you can see in Figure 2, we need to change the path field to @executable_path/../Frameworks. This lets the framework know that it will be placed inside an application bundle. (Note that in reality the executable is located in MyApp.app/Contents/MacOS/MyApp, so @executable_path/../Frameworks translates into MyApp.app/Contents/Frameworks.)

Installation Location
Figure 2: Installation Location Settings

Now, continue to scroll down to "Linker Settings." Make sure the "Prebind" checkbox is selected, as this will enable your application to launch faster. To make sure that prebinding actually works, we'll need to change the preferred address of our framework. By default, it's set to 0x00000000, which means it will overlap with the application you want to use it with, since all executables start at that address. To avoid this problem, you need to add a linker flag called -seg1addr my_address_here. In place of my_address_here, you need to specify an address in the range of 0x00000000 to 0x3FFFFFFF, as well as anything above 0x900000000 (the remaining addresses are reserved by Apple). Obviously using 0x00000000 would be pointless, so you should try something like -seg1addr 0x10000000 or above.

Linker Settings
Figure 3: Linker Settings

Well, that's it for this part. Don't forget to build the framework when you're finished so that we can use it in the next part of this article. I also recommend you take a look at the notes about prebinding over on Apple's developer site if you want to know more about it.

 

// Application Settings

So, now that we have the framework built the right way, you can add it to your application. Add it the same way you would add any of the Apple frameworks (using the "Add Frameworks..." item located in the "Project" menu). Unfortunately, that isn't the end of it yet. We need to have Project Builder automatically copy our framework into the application bundle when it's built (more specifically, we want it placed in the MyApplicationHere.app/Frameworks directory). Luckily, this isn't all that hard to do, since Project Builder provides a very handy "Copy Files Build Phase." This will do is exactly as the name implies: copy files to whatever location you specify.

Edit the target settings of your application (the same way we did above with the framework) and click the "Files & Build Phases" tab. Scroll down to the "Frameworks & Libraries" section. There you should see the framework we added earlier, among others. Select that framework, go up to the "Project" menu, and navigate your way down to the "New Build Phase" submenu. Yep, you guessed correctly, you need choose "New Copy Files Build Phase" from that submenu.

New Copy Phase
Figure 4: New Copy Phase

In the resulting "Copy Files" section, choose "Frameworks" from the "Where:" pop-up menu. The last thing we need to do is add the actual framework to the "Files:" list. Click the "Files" tab on the left-hand pane of the project window (to get back to your list of project files) and then proceed to drag the MyFramework.framework file (or whatever your framework is called) to the "Files:" list on the right.

Settings for the copy phase
Figure 5: Settings for the copy phase

After you're done with all that, go ahead and build your app... boom! You should now be able to use any of the classes in your framework.

 

// Conclusion

Well, there's not much left to talk about. Go out and start using the Omni frameworks, roll your own frameworks, or just have fun playing around with this stuff! As a final note, I'd like to thank Kurt Revis for telling me how to make my framework cooperate properly with prebinding and not produce pesky linker warnings everytime I build my app. Like always, if there are any questions, comments, or corrections, feel free to contact me.


Comments

I'm having a problem with the OmniFoundation framework. I've compiled it according to the these(and the previous tutorial on the subject's) directions and even tried a number of variations and each time, the framework compiles correctly, but when I link an app against it, as soon as I add the line

#import <OmniFoundation/OmniFoundation.h>

I get this error when building the project:

ld: Undefined symbols:
.objc_class_name_OFCharacterSet

OmniBase works fine under the same circumstances. Any ideas?

Posted by: Mark T on January 6, 2003 04:17 PM

Is OmniFoundation dependant on OmniBase? Perhaps you need to link both frameworks.

Posted by: dave on February 7, 2003 03:37 PM

Does this mean if a framework hasn't been built with the @executable_path trick then you are out of luck as far as bundling it with your app?

This seems to be the way it is and it just doesn't seem right.

Posted by: Rod Schmidt on March 10, 2003 12:09 PM

You can also place any framework in the same folder as the application that uses it (ie the build folder in your project directory), and dyld will be able to find it, even if it wasn't built with the @executable_path/../Frameworks install path.

Posted by: Jeremy on April 14, 2003 04:41 PM

I rebuilt the frameworks MOKit and EDCommon to be embeddable using this tutorial, but if I ever move either of these frameworks out of /Library/Frameworks, my application stops working! dyld reports that it cannot find those frameworks in the /Library/Frameworks folder, even though both frameworks are inside my application's bundle.

What's going on here?

Posted by: Marc W. on June 10, 2003 09:38 AM

Hi,

is there a way to include libraries like this? ie, glib

please email me if you have the answer. adlr@engin.umich.edu

-andrew

Posted by: Andrew de los Reyes on June 14, 2003 03:41 PM

You can include any kind of library, as long as it's linked as a dylib with the correct install_path. I've posted some basic informational instructions at http://qin.laya.com/tech_coding_help/dylib_linking.html -- basically you need your library to be compiled with the correct install_path, otherwise your app will not get the correct path to the library when you compile it later, and it won't be able to find the dylib inside of the app package frameworks folder. Apparently panther fixes this, but this means that if you're running panther, you could create an application that doesn't run properly on 10.0-10.2

Posted by: Robert Chin on September 23, 2003 01:02 PM

Any pointers for doing this with xcode? The copy files
build phase inspector does not seem to have a "Where"
field ?!?

Posted by: static on November 17, 2003 08:46 AM

It's called "Destination" in Xcode, available through the copy files inspector.

Posted by: Brian Christensen on November 17, 2003 04:51 PM

I tried to leave off the @executable_path HACK, and Panther wasn't able to find the framework when embedded in the application.

I agree with Rod in that having to build the library with this flag just doesn't seem right. Why doesn't the library resolution code look in the application bundle first.

Can a library built with the flag also be shared in one of the system library paths?

Posted by: Tod Cunningham on November 17, 2003 11:59 PM

xcode:

In my situation i want the opensource libraries quesa and openal inside my gameApplication. I have followed the guide and also have the copy-phase when building. The application is built and i can run it from within xcode, but not independent since it can use some functions of both frameworks but crashes on the openal.framework function "alutLoadWAVFile".

Another strange thing is that when i build from the commandline the frameworks are not copied inside the application and starting it gives the error: "an unexpected error has occured: -10810".
When the frameworks are built from the commandline (with xcodebuild install) it does not understand "@executable_path/../Frameworks" and instead installs in the temp directory. These issues clearly hint out that (I am/xcode is) doing something wrong?

Posted by: Steven Verstoep on December 7, 2003 07:00 AM

solved xcode:

I found my own problem. It seems that there is a bug in OpenAL. It uses fopen to load wav files, but when stand alone applications are used unix fopen tries to accces the file from ROOT instead of the executable directory. I made a workarround.

Posted by: Steven Verstoep on December 7, 2003 12:51 PM
Post a comment