Confused Development

I develop software and I often get confused in the process. I usually find the answers after a while, but a month later I can't remember them. So from now on, I will write them down here.

Wednesday, January 24, 2007

Shipping libical in a Mac Application Bundle

I have been working with the libical library for parsing and manipulating iCal data for a while now, and it works fine (even though we probably don't need it anymore, once Leopard is out). Now that I have finally put my code for semiBlog online, a number of new problems arose. So here is a little summary of what those problems were and how I solved them:

  • Making sure the library is a universal binary: Generating universal binaries in Xcode is very simple. However, when you want to turn a configure/make based project (like libical) into a universal binary, things get a little more complicated. Luckily, this document on Apple's developer pages explains it all.
  • Bundling a .a-type library with an application bundle: This is actually very easy. You need to do three things.
    1. Drag and drop the library files (in the case of libical those are libical.a, libicalss.a and libicalva.a) into the "Linked Frameworks" group of your project in Xcode (a little trick if you don't know how to get a finder window for hidden folders like /usr/local: move into the folder in the terminal window and then open a finder window for it by typing open .).including libical in Xcode project
    2. Set the header search paths correctly: make sure you point the "User Header Search Paths" of your target to the right location (e.g. /usr/local/include).
    3. Set the library search paths correctly: make sure you point the "Library Search Paths" of your target to the right location (e.g. /usr/local/lib).
    Setting header and library search paths in Xcode
  • Make sure libical finds those time zone info files: libical needs to have access to a bunch of time zone definition files. They are located in in a share sub-directory of the libical install directory. (.../share/libical/zoneinfo). By default, libical will look for that folder in the location it was first installed, which is fine in many cases. However, when you distribute your project's code or the application bundle, things can get messed up. If libical doesn't find the time zone information, you get errors like those:
    icalerror.c:99: FILE: An operation on a file failed. Check errno for more detail.
    icalerror.c:100: failed assertion `0'
    
    That's why you need to call set_zone_directory() at runtime, and pass it the right path. I copy the zoneinfo folder into the application bundle (using a bunch of Copy Files build phases), and then ask NSBundle for the right path, like so:
NSBundle *pluginBundle = [NSBundle bundleForClass: [SBICalInterface class]];
NSString *zoneinfoPath = [NSString stringWithFormat: @"%@/zoneinfo", [pluginBundle resourcePath]];
set_zone_directory([zoneinfoPath cString]);
(note that I use bundleForClass: because the zoneinfo folder is actually inside a plugin bundle within the real application bundle. If it was in the application bundle directly, I could just call mainBundle)

0 Comments:

Post a Comment

<< Home