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:
- dependent non-standard libraries
- executable binary
- icons and other support files
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:
- 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 binaryIn my case:
solaris:~/code/tcprose steve$ otool -L tcprose
/sw/lib/libSDL-1.2.0.dylib (compatibility version 8.0.0, current version 8.1.0)
/A/Cocoa (compatibility version 1.0.0, current version 9.0.0)
/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)
- 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.dylibNotice 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.
lrwxr-xr-x 1 root admin 19 20 Aug 03:35 libpcap.0.dylib -> libpcap.0.8.3.dylib
- 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 binaryIn tcprose's case:
solaris:/sw/lib steve$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
install_name_tool -change /sw/lib/libpcap.0.dylib @executable_path/../Frameworks/libpcap.0.8.3.dylib ./tcprose
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, 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:
.icns file with Icon Composer found in /Developer/Applications/Utilities then save the .icns file in the Resources folder, and add the CFBundleIconFile key.
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!