February 17, 2005

M(i|a)croscopic

This blog's been neglected, so I think I'll post some musings from the past few weeks.

Having recently returned to the city from a small country town, the difference amazes me. I've never lived in the country before in my life and it was a totally new experience.

Everyone's friendly, people get to know you quickly, yet for some reason that tends to make me uncomfortable and vaguely paranoid. There is a sense of security in the anonymity of the city that I was hitherto unaware of. And yet, out there, the atmosphere is more relaxed. World events push at the same rate, but somehow the country absorbs them more slowly. The days do not rush, they meander.

But the biggest difference is the people. In the city, a business suit and a laptop bag are marks of status, of acceptibility, There, I felt self-conscious walking down the street with my mp3 player and mobile phone. Here, people hurry, honed in on their targets, dodging and ducking through the obstacle course that is the city pavements. There, they stroll along, pausing to have a chat or see what takes their eye, and amicably give way to each other. Here, a university degree is how you get a decent career. There, leaving school at Year 10 and helping your father work the farm is the sign of a good future.

In this day and age, what makes the difference? A country town is no less connected than the city, news reaches there as quickly as it does here, so wherein lies the vastly separated mindset? I think it's because it's always been like that, and now it's self-perpetuating. People go to the city to be connected, to be on the cutting edge, at the centre of things. People go to the country to slow down, to get back to the land, to enjoy a sense of community.

Out there, the world is at once very big and very small - and sometimes it's hard to tell the difference.

M.

February 01, 2005

Creating OS X application bundles step by step

Introduction

This guide is the result of attempting to package a network visualiser (tcprose) I wrote so that it can be distributed as an application bundle. I gathered information from many places, and while I try to be accurate as possible should you notice any bugs feel free to contact me.

Step 1 - The folder hierachy

Each application bundle is on disk a file that ends in .app and contains a strict folder hierachy. The folder hierarchy is the form:
  • application.app
    • Contents
      • Info.plist
      • Frameworks
        • dependent non-standard libraries
      • MacOS
        • executable binary
      • Resources
        • icons and other support files
The first thing you need to do then is to create such a folder tree somewhere.

Step 2 - The binaries

The binaries of your program reside in the MacOS folder. If your program depends on libraries that are not present by default, then you need to do the following:
  1. Figure out what libraries your application depends on, and figure out what libraries need to be distributed with the program. In order to see the dynamic libraries your binaries depend on, use the otool thus:
    otool -L binary
    In my case:
    solaris:~/code/tcprose steve$ otool -L tcprose
    tcprose:
    /sw/lib/libSDL-1.2.0.dylib (compatibility version 8.0.0, current version 8.1.0)
    /System/Library/Frameworks/Cocoa.framework/Versions
    /A/Cocoa (compatibility version 1.0.0, current version 9.0.0)

    /System/Library/Frameworks/OpenGL.framework/Versions
    /A/OpenGL (compatibility version 1.0.0, current version 1.0.0)

    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 71.1.1)
    /sw/lib/libpcap.0.dylib (compatibility version 0.8.3, current version 0.8.3)
    solaris:~/code/tcprose steve$
  2. Anything in /System or /usr can be safely assumed to be present on all default systems. Notice then the 2 libraries in /sw/lib. For those unfamiliar with fink, its where it installs all its packages. Those 2 libraries need to be copied into the Frameworks folder. Do be careful however that you copy the actual file, not the symbolic links. For example:
    solaris:/sw/lib steve$ ls -l libpcap.0.dylib
    lrwxr-xr-x 1 root admin 19 20 Aug 03:35 libpcap.0.dylib -> libpcap.0.8.3.dylib
    solaris:/sw/lib steve$
    Notice that libpcap.0.dylib is in fact a symlink to libpcap.0.8.3.dylib, thus you need to copy /sw/lib/libpcap.0.8.3.dylib, not /sw/lib/libpcap.0.dylib.
  3. Once the required libraries have been copied into Frameworks folder, its time to do some linking manipulation. Use the install_name_tool to remapped the dynamically linked libraries so your binary links against the libraries in Frameworks folder. To do this you issue the command:
    install_name_tool -change old_library_path new_library_path binary
    In tcprose's case:
    solaris:/sw/lib steve$
    install_name_tool -change /sw/lib/libpcap.0.dylib @executable_path/../Frameworks/libpcap.0.8.3.dylib ./tcprose
    Notice the use of @executable_path: this is what makes it "tick". @executable_path will automatically map to the path where the executable is located. Now if you move the binary into the MacOS directory, it will link against the libraries you have copied into the Frameworks folder
Word of caution: as far as I know there is no way to re-map the paths of any files in the binaries. Thus if you open files by using fopen("file", "rw"); then it will fail, because the working directory will be the directory that contains the application bundle, not the directory where the binary is stored. You will therefore need to take this into account, and adjust your paths accordinly.

Note
: It might be possible to set the environmental variable PWD via Info.plist (see next step) to certain directory so that the working directory is somewhat more manageable. I didn't have any luck with this, so kindly let me know if you figure something out.

Update - 6/3/05: as pointed out by a reader, you can extract the path of the executable from argv[0], and thus determine the relative paths of whatever resources your program needs.

Step 3 - Info.plist

Now we come to the most important step of creating an application bundle: creating a proper Info.plist inside the Contents folder. Info.plist is an xml file that describes properties that finder reads Info.plist and determines many things, including which binary inside MacOS to execute, what icon inside Resources to display, etc. The best tool to use for editing it is no doubt Property List Editor in /Developer/Applications/Utilities. You can of course do this by hand. Apple has a detailed document on how to create a proper Info.plist file, but at the absolute minimum the following keys are required:
Extra: if you want to specify a custom icon, first create a .icns file with Icon Composer found in /Developer/Applications/Utilities then save the .icns file in the Resources folder, and add the CFBundleIconFile key.

Conclusion

Creating an application bundle is a relatively easy affair, the most difficult part being remapping the dynamically linked libraries and adjusting any hardwired paths and generating a proper Info.plist file. Have fun, and if you have any suggestions or bug reports, do let me know!

Cheers,
Steve