It is a bit serendipitous that Gus Mueller’s discussion of Automated Builds on the Late Night Cocoa podcast should happen soon after I finally got my own act together and automated most of my build process. So, taking advantage of the momentum I thought I’d ramble on for a bit on the topic.
My process does not do as much as Gus’ (I don’t automatically upload) and it’s all triggered from within XCode when I do a Release build (Gus uses a script) but the important part is in the end I have a procedure that does most of the tedious parts for me. The problem with automating the process with XCode is that there are certain types of phases or resources that it doesn’t not handle natively so you have to script them yourself or it’s not obvious how to do it the right way. I thought I’d go over a couple of the oddball cases. Not all will be applicable but hopefully you can find something useful.
This is something I neglected to deal with for the longest time. The notion is to strip your code but keep an unstripped version around so that you can use it with atos when you get a stack trace. I never got around to figuring out the right combination of settings on my own but this post gives you everything you need. Just stick this in a shell script phase after the product is built and use the XCode settings outlined in the comments. A big thanks to Andy O’Meara and Rob Barris for figuring it all out and sharing.
This is one a bit trickier. Everyone seems to have their own methods and requirements for this so I’ll just point out some specific things here. First off, it’s all done with hdituil. If you don’t feel like learning hdiutil there are higher-level commandline programs and scripts you can use such as buildDMG or DropDMG (the latter is a GUI app which also comes with a commandline program).
In my case, I don’t construct the DMG from scratch. I created a template disk image with placeholders for my files. There are ways to deal with icon positioning programmatically but really, it’s just easier to do it by hand, save the image and use it over and over again. During a build, I mount the disk image, replace the files and then unmount. The disk image is then converted to a compressed format. For the mounting part, use the “-private” flag. It prevents Finder from seeing it so you don’t have to deal with the DMG window popping up while it’s being packaged. Unfortunately, my current script has some quirks specific to me so I’m not posting it but feel free to drop me a line if you have questions.
Also, credit goes to Jonathan Wight for providing the ideas and script that formed the basis of the DMG build procedure outlined above.
Useful Commandline Tools
If you are building an app then this is not an issue but for preference panes, it’s common to have a customized Finder icon for the .prefPane file itself (note that the icon specified in your bundle is for within the System Preferences app). For this, I downloaded the osxutils package. It contains a commandline program called “seticon” which, as the name indicates, sets an icon on a file. There are other little commandline programs to do things like set the Finder comment or the color label.
[Update: Tom Harrington was kind enough to point out my boneheadedness. Setting bundle icons in Info.plist for pref panes works fine so using the seticon program above is totally unnecessary. There was a misconfiguration in my project that lead to my faulty assumption that you had to set it separately.]
There’s also SetFile (comes with the dev tools) which is useful for setting HFS+ attributes. For instance, if you want to script setting the background image file in your DMG to be invisible, a
SetFile -a V yourfile will do the trick.
So, basically most of the things you can and want to do to files in the Finder can be done on the commandline (therefore making them available for scripts).
XCode and External Targets/Dependencies
This is something that I always forget how to do. If your project has a dependency on some other project, such as a framework, you can drag the xcodeproj file into your project. But to create an actual dependency between your target and the external target, you can’t drag the thing underneath your target nor can you do a “Get Info” on the external target and check off the targets it’s a part of. Instead, select your target and do a “Get Info” on it. Under the “General” tab, hit the plus (+) button at the bottom to add the external product as a dependency. Also, you need to have both projects build to the same directory (“Get Info”->”General” tab). Now, building your target will build the dependent target as necessary. The important thing is that you do not depend on specific files. If you find yourself typing in paths that refer to another project’s build directory, you are probably doing it incorrectly.
XCode and Applescripts
I have some Applescripts in my project. Before, I saved them as .scpt files which are already compiled. Because they were already compiled, XCode’s “Compile AppleScripts” phase didn’t apply. Since I wanted to convert them to be “Run only”, I added a shell script build phase to use osacompile but this was a little clunky. I had to run it once to get XCode to generate the new .scpt files and then I added those to the XCode project so that they could be copied into the bundle during a build.
Instead of using .scpt, I now use plain, uncompiled .applescript files. XCode knows how to deal with these via the “Compile AppleScripts” phase. To make the compiled script “Run only”, add “-x” to the OTHER_OSAFLAGS setting in your build settings. Note that you may have to add “OTHER_OSAFLAGS” if it doesn’t exist.
• • •
So, that’s it for my random XCode tips. My build procedure is not perfect. I need to better automate versioning but for now it’s relatively pain free. If you have any tidbits, post them here.