With the introduction of OS X Apple began supplying its developers with a means of building their own factory-sanctioned software installer packages. Gone were the days of paying thousands of dollars per year for InstallerVISE and Stuffit InstallerMaker licenses, and as is usually the case when Mike Ferris and the rest of the NeXT nimrods who think shell scripting support is an adequate substitute for product features, gone were the comprehensive and functional user interfaces as well. Much like MPW, ProjectBuilder, XCode and every other developer tool Apple's ever released, PackageMaker is an awkward, clunky pile of crap whose behavior bears quite a striking resemblance to many of the quickly thrown together AppleScript Studio GUI wrappers for command line tools you'd expect to find littering your search results on VersionTracker. The fact that it's also incredibly buggy, crashes constantly and allows scripts contained within the packages it generates to run as root certainly doesn't bolster my opinion of it, but it's there and it's free, so there are now billions and billions of these generic, ambiguous box icons floating around out there, and odds are good you've seen more than a barrel in your day.

Defining the Problem

Now consistency is certainly a good thing; when you look at that icon there's no question as to what it is and what you should do with it (actually, there might be since package receipts use the exact same icon, but most people don't look in that folder anyway). The problem is that there's also no indication of what the hell's inside it, and the names most developers come up with for them don't usually help. Wouldn't it be nice if we could, oh I don't know, do the same thing we did ten years ago with 'BNDL' resources and provide custom icons for our installer packages so people might have some clue what they're for or where they came from?

Well hey, packages are just bundles, so your initial inclination is probably to slam a CFBundleIconFile declaration in your Info.plist and be done with it. Unfortunately, not only does PackageMaker spit out uncustomizable Info.plist files which have their permissions set to disallow write access entirely, but the Finder will not honor the CFBundleIconFile info in an installer package at all, so you're doubleplus screwed.

-r--r--r-- 1 nate staff 57522 Nov 3 03:30 Archive.bom -r--r--r-- 1 nate staff 4758 Nov 3 03:30 Archive.pax.gz -r--r--r-- 1 nate staff 1359 Nov 3 03:30 Info.plist -r--r--r-- 1 nate staff 8 Nov 3 03:30 PkgInfo drwxr-xr-x 11 nate staff 374 Nov 3 03:30 Resources

You'd probably see better security at a Raffi concert

Luckily, while all packages are bundles, all bundles are directories, and the Finder's lumbering legacy code still honors the tried-and-true "Icon\r" file for directories as long as you ask it nicely. Herein lies the solution.

I'm a Sad Old Man

Now if you've been just recently lured into Mac programming by an iPod commercial, IRC channel, idiotic product manager or some goony looking doofus who slaps a lot of pictures of himself wearing a cowboy hat on his overpriced books, then you probably don't know what an "Icon\r" file is, so here's a bit of a recap:

Back in the good old days of Pascal and printed documentation, we needed a way to copy and paste custom icons onto our folders and hard drives in order to let everyone who happened to glance at our screens momentarily know who our favorite Bloom County character was. The solution Apple came up with was to shove a file named "Icon" with a carriage return at the end (pick a name that's impossible to type and you avoid naming conflicts) into the directory, make it invisible, and set a bit flag on the directory itself informing the Finder that this folder had a custom icon which should be drawn instead of the generic one. All of this "Icon\r" file's data lived in its resource fork in the form of a single icon suite resource with an ID of -16455. Nowadays you'll get chastised for doing anything with resource forks, but Apple's reliance on them is still quite rampant, and nobody's come up with a better way of specifying custom folder icons so far. Actually, they probably have come up with a lot of better ways, the Finder team just hasn't taken the time to implement any of them. They have a lot of shiny pinball machines in their office lounge you see, so it's easy for an engineer to get distracted from the fact that the last seven years of his life have been spent playing feature catch-up with Finder versions that shipped in 1991.

Into the Fray

So how do you make these "Icon\r" files? ResEdit is as dead as a Jim Belushi fanclub meeting, and I can't imagine anyone paying for Resorcerer in this day and age. Rez is still shipping with the developer's tools, but who the hell wants to design icons in ASCII? The quickest way to summon one of these living relics is to simply design your icon as you normally would and save it as an .icns file. Select this file, do a Get Info on it, click on its icon in the resulting window and hit Copy. Now go make an empty folder, do a Get Info on it, select its icon and hit Paste. Whether you realize it or not, the Finder just translated your .icns file into a properly formed "Icon\r" file for you, all you have to do is fish it out.

To do this, you can either make this file visible and use it as-is, or you can copy it somewhere else with a different name that's actually type-able i.e. no carriage return. All the data we want is in the resource fork of course, so we'll have to use ditto rather than cp to do the work.

ditto "Folder With Custom Icon/Icon"$'\r' "Destination Folder/icon.rsrc"

Note that I am assuming you're using a recent version of ditto which copies resource forks by default.

Now we've got the file we need and can store it wherever we like. I'd suggest the project resources directory for whatever it is you're working on, but it's your drive.

From here the path should be obvious: create a script, Automator action or XCode build phase which will put together the installer—via either command line arguments or PackageMaker project files—and plop that icon into it when it's done. What would this look like you ask? Let's take a look at an example of a build phase script which assumes we've defined some environment variables called INSTALLER_PACKAGE_NAME and INSTALLER_DMG_VOLUME_NAME to specify the name of the .pkg and .dmg wrappers to generate respectively.

First we make the package assuming that the rest of our build products have been created successfully and placed somewhere appropriate. I'm using a .pmproj file for simplicity's sake, but you could just as easily have some elaborate, flowery affair here if you prefer.

#Assume these have been defined in the target's build settings #INSTALLER_PACKAGE_NAME="FancyPantsKnowItAllInstaller"
#INSTALLER_DMG_VOLUME_NAME="Fancy Pants Know It All"


version=`grep -A1 -E "<key>CFBundleVersion</key>" "$BUILT_PRODUCTS_DIR/$FULL_PRODUCT_NAME/Contents/Info.plist" | grep -E "<string>.*</string>" | sed 's!<string>\(.*\)</string>!\1!g' | awk '{ print $1 }'`

/Developer/usr/bin/packagemaker -d $PWD/Installer/KHIProfileMenu.pmdoc -n "$version" -o "$BUILT_PRODUCTS_DIR/$INSTALLER_PACKAGE_NAME.pkg"

Next we copy our icon.rsrc file into the package with its original, magic name.

ditto "$PWD/Installer/icon.rsrc" "$BUILT_PRODUCTS_DIR/$INSTALLER_PACKAGE_NAME.pkg/Icon"$'\r'

Then we tell the Finder this directory has a custom icon file that needs to be honored via the handy dandy SetFile tool.

/Developer/Tools/SetFile -a C "$BUILT_PRODUCTS_DIR/$INSTALLER_PACKAGE_NAME.pkg"

And finally we make the Icon file invisible. This isn't actually necessary, but it helps perpetuate the ancient magic, so what the hey.

/Developer/Tools/SetFile -a V "$BUILT_PRODUCTS_DIR/$INSTALLER_PACKAGE_NAME.pkg/Icon"$'\r'

And viola! Every time you build your product from XCode it will also spit out a custom branded installer whose icon doesn't look like something that creepy guy from The Little Prince would have drawn to hide his lack of talent. But why stop there? You can just as easily make XCode build your disk image for distribution of this package while you're at it.

mkdir "$BUILT_PRODUCTS_DIR/staging"

ditto "$BUILT_PRODUCTS_DIR/$INSTALLER_PACKAGE_NAME.pkg" "$BUILT_PRODUCTS_DIR/staging/$INSTALLER_PACKAGE_NAME.pkg"

hdiutil create -ov -srcfolder "$BUILT_PRODUCTS_DIR/staging" "$BUILT_PRODUCTS_DIR/$INSTALLER_PACKAGE_NAME.dmg" -volname "$INSTALLER_DMG_VOLUME_NAME"

rm -r "$BUILT_PRODUCTS_DIR/staging"

If they ever make a Babylon 5 movie with a hobbit quoting Monty Python in it this man will probably ejaculate hot grease out of his ponytail

And there you go. A nice compressed .dmg file with a non-generic installer package all ready to upload to your web server for distribution. Heck, you could probably even talk curl into doing the upload for you! Ideally you'd break these two tasks out into two separate build phases or targets with appropriate dependencies so nothing gets rebuilt if it doesn't need to be, but hopefully you can figure that part out on your own. The only way life could be better is if all of these incredibly common tasks could be accomplished with a checkbox instead of a bunch of crappy shell scripts, but why should a company like Apple care about something as stupid as a GUI? It makes so much more sense to spend millions of dollars ensuring that Steve Jobs never has to click more than three times to get to his favorite Beatles song during SEC meetings than it does to hire an engineer who wants to do something other than start a bunch of territorial pissing wars anytime anyone dares suggest that maybe that giant pile of code he wrote back in 1988 isn't quite up to snuff by today's standards. Face it, if you make software development fun or easy, nobody will think software developers are superhuman geniuses anymore, and then how will they pay for their Beamers, blazers and khaki shorts? IDEs and modeless text editors are for pussies, anyway.

Of course you probably shouldn't bother with any of this unless you actually have a decent icon to begin with. While I've gone well out of my way to malign it here, the box and the comfortable uniformity it brings is still infinitely preferable to jaggy MS Paint-esque displays of primary colors that fly on other platforms. I know you've convinced some venture capital guy that Mac users are going to flock around your hastily ported social networking browser plugins if you can just find the right Bangalorean trade school dropout to translate some Java garbage you found on an internet forum into a working Mac application for minimum wage, but please, at least hire a real icon designer if you don't know what you're doing. They need the money.