Preprocessing Info.plist files: not well documented

In Sandvox, we have a lot of plug-ins, each with their own Info.plist file. Plus the application, of course. I had really wanted to employ some automated system to keep the version numbers of everything in sync, plus have "build numbers" so it's easy to determine exactly which version of the program is running, especially now that it's getting very, very close to being seen by people other than its developers.

I had scratched my head over agvtool from time to time; reading the man pages this time didn't help. But I remembered that XCode 2.1+ can preprocess the info.plist files. I figured this was a good route to take.

Problem is, there's scarce documentation on how to go about actually doing the preprocessing. Apple has a page here and here that is a start. Jonathan Wight was the only obvious resource on the web that talked about the syntax of what to put in for a substitution, in his weblog post that provided a handy hint. But I couldn't really get it to work other than using Jonathan's $(PRODUCT_NAME) substitution. I wanted to include a file containing the substitution values, but I couldn't figure out what format that file should be in.

I stumbled upon a mention of preprocessing in the change logs for Growl, so I checked out the latest version of the source. It was a little hard to figure out what the included file looked like, because it is not in the source; it's built by a script. Eventually, I figured out that the format of the file to include is essentially like a ".h" file: lines of #define keyword substitution.

Interestingly, with the exception of the XCode variables like $(PRODUCT_NAME), it seems to be a strict substitution with no special characters. So if you have this defined in your include file:

#define SVN_VERSION 1838
#define APP_VERSION 1.0b1
... then you would want your info.plist to contain something like this:
<key>CFBundleShortVersionString</key>
<string>APP_VERSION</string>
<key>CFBundleVersion</key>
<string>SVN_VERSION</string>

Of course, you still need a shell script to generate your include file to put the current source code version in there. We get the current subversion head's number by using this snippet:

svnVersion=`/usr/local/bin/svn info -r HEAD | awk '$1=="Revision:" { print $2 }'`;

Update: Mark notes something from WWDC in the comments; I thought I'd paste it here:

#ifdef _RELEASE_ 
<string>Copyright (c) 2005</string> 
#else 
<string>_CONFIGURATION_ version built on __DATE__ __TIME__</string> 
#endif
So that's cool, you can use C-style processing right in the file, and you have substitutions for date and time available.

I'll post more here if I discover other goodies.