2718.us blog » build script http://2718.us/blog Miscellaneous Technological Geekery Tue, 18 May 2010 02:42:55 +0000 en hourly 1 http://wordpress.org/?v=3.0.4 Simplifying the Assembly of Localizations in Xcode http://2718.us/blog/2010/04/22/simplifying-the-assembly-of-localizations-in-xcode/ http://2718.us/blog/2010/04/22/simplifying-the-assembly-of-localizations-in-xcode/#comments Thu, 22 Apr 2010 20:11:51 +0000 2718.us http://2718.us/blog/?p=254 A comment on a bitbucket fork of Murky led me to “Automatically localize your nibs when building“, which suggests a great way to automate the hard developer-side stuff in localizing in Xcode–pulling the original strings from the XIB files and putting the translated strings back in.  You absolutely should read the original blog post there, because I cannot adequately explain the big-picture part of the idea with a short quote.

My one complaint with the setup described is that the script for the “Run Script” build phase described there is a maintenance headache that I could do without.  Here’s my solution:  Create two new build targets, both of the “Shell Script” type–these are targets that just run a shell script, so they are created with only a “Run Script” build phase.  I called my two new targets “Create/Update English .strings files” and “Create/Update l10n XIBs” but you can call them whatever you want.

In the “Create/Update English .strings files” target’s “Run Script” phase, the script is:

# Create/Update English .strings files
  1. for xibFile in "$PROJECT_DIR/English.lproj/"*.xib; do
  2.  ibtool –generate-strings-file "${xibFile}.strings" "$xibFile"
  3. done
  4. exit 0

This uses ibtool to create a strings file for every XIB in the English localization (from foo.xib, foo.xib.strings will be created).

In the “Create/Update l10n XIBs” target’s “Run Script” phase, the script is:

# Create/Update l10n XIBs
  1. originalResourceDirectory="$PROJECT_DIR/English.lproj"
  2. for localizedDirectory in "$PROJECT_DIR/"*.lproj; do
  3.  if [ localizedDirectory != originalResourceDirectory ]; then
  4.   for xibFile in "${originalResourceDirectory}/"*.xib; do
  5.    xibBaseName=$(basename "${xibFile}")
  6.    ibtool –strings-file "${localizedDirectory}/${xibBaseName}.strings" \
  7.     –write "${localizedDirectory}/${xibBaseName}" \
  8.     "$xibFile"
  9.   done
  10.  fi
  11. done
  12. exit 0

This goes through every .lproj directory except English.lproj and uses ibtool to apply the .xib.strings files in those localizations to the XIB files in English.lproj.

By having these as two separate targets, they aren’t run every time I build and each part can be run on its own, on demand.  By using the power of shell scripting, I avoid having to alter the scripts for every new localization or XIB.

]]>
http://2718.us/blog/2010/04/22/simplifying-the-assembly-of-localizations-in-xcode/feed/ 0
Integrating Version Control Revisions in Mac App Version Numbers http://2718.us/blog/2010/03/22/integrating-version-control-revisions-in-mac-app-version-numbers/ http://2718.us/blog/2010/03/22/integrating-version-control-revisions-in-mac-app-version-numbers/#comments Mon, 22 Mar 2010 13:57:26 +0000 2718.us http://2718.us/blog/?p=238 When I was using Subversion for version control in working on my Mac apps in Xcode, I had come to use a build script phase to tack on the subversion revision number as the last part to the version number:

# tack the subversion revision number onto the CFBundleVersion
  1. REV=`svnversion -n "${PROJECT_DIR}"`
  2. REV=${REV/#[0-9]*:/}
  3. REV=${REV//[^0-9]/}
  4. BASE=`/usr/libexec/PlistBuddy -c "Print :CFBundleVersion" "${PROJECT_DIR}/Info.plist"`
  5. /usr/libexec/PlistBuddy -c "Set :CFBundleVersion $BASE.$REV" "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}"

This takes the CFBundleVersion from the project’s Info.plist, adds a period and the subversion revision number, and injects that into the Info.plist of the built product.

When I made the move to Mercurial, I couldn’t quite do this anymore.  While subversion revision numbers are monotone increasing and consistent everywhere (they are the unique identifiers of a particular version), Mercurial’s “revision numbers” are local to a particular instance of the repository and may not match up from one instance to another.  Mercurial uses a hash as the unique identifier of a particular version, and these hexadecimal hashes are not monotone, making them unsuitable for use in CFBundleVersion.

Though it took some time, I’ve finally come to a system with Mercurial that I think is workable:

# use the number of minutes between the tip and the most recent tagged commit
  1. HG_LAST_TAG_TIMESTAMP=$(/usr/local/bin/hg log -r "$(/usr/local/bin/hg log -r '.' –template '{latesttag}' –repository "${PROJECT_DIR}|>")" –template "{date|hgdate}\n" –repository "${PROJECT_DIR}" | cut -f1 -d' ')
  2. HG_TIP_TIMESTAMP=$(/usr/local/bin/hg log -r '.' –template "{date|hgdate}\n" –repository "${PROJECT_DIR}" | cut -f1 -d' ')
  3. REV=$(( ($HG_TIP_TIMESTAMP$HG_LAST_TAG_TIMESTAMP) / 60 ))
  4. BASE=`/usr/libexec/PlistBuddy -c "Print :CFBundleVersion" "${PROJECT_DIR}/Info.plist"`
  5. /usr/libexec/PlistBuddy -c "Set :CFBundleVersion $BASE.$REV" "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}"

This calculates the number of minutes between the most recent tagged ancestor of the tip and the tip itself and adds that to the CFBundleVersion.  Because it’s time-based, it is guaranteed to be monotone increasing, consistent, and unique (well, provided multiple commits don’t happen within a 1-minute span, which is safe for me).

]]>
http://2718.us/blog/2010/03/22/integrating-version-control-revisions-in-mac-app-version-numbers/feed/ 0