Category Archives: XCode

1802 Emulator

In a previous post I talked about the 1802 Assembler I was playing with when I revisited an ancient 1802-based FIG-Forth implementation.

Since it was a pain to get that code running on real hardware, I also threw together an emulator. This was done in Objective-C for the Mac.

Note that the core emulation code is written in ‘C’ however, so it should be quite portable.

It is fairly quick, although I have not tried to quantify it’s equivalent speed.

It will build under recent Xcode versions such as Xcode 6 or Xcode 7.

Listing files can be loaded and executed. There is application-specific code to treat some of the IO ports as serial input/output to a simple terminal facility.

This is in no way a polished final product, but it is made available on the off chance that someone else might find the code useful or interesting.

https://github.com/donmeyer/cosmac-emulator-mac

Xcode Build Version Script Revisited

On my first release build (versus an Ad Hoc one for TestFlight), I discovered that Apple apparently doesn’t like such a lengthy build version number for releases. (ok, it is rather long…)

An updated version of the script is now available via GitHub at Xcode Tools.

There are a couple other older, undocumented, and essentially experimental tools in that repository as well. Should I revisit and clean up those as well, I’ll add them to a future blog post.

XCode Build Versions

XCode Build Versions

So, one of the things I found myself wanting to do is automatically bump the version of my application. One good reason to do this is when using the amazing TestFlight to distribute beta builds.

Now, we actually have two “versions” to play with. The first is the actual “Version”, and the second is the “Build”. For my purposes, the Build is what I want to change with each release.

This could be an incrementing number, but I’d really like it to be more useful than that. I use Mercurial as my SCM, so what I decided I wanted was to make the Build “number” be the Mercurial changeset identifier. That way (assuming I’ve done a check in of the changes, as I should) the build number will always be different, and meaningful.

What follows is the result of inspiration gained from looking at solutions found by other developers, in particular from the blogs of Bill Woody and Daniel Jalkut.

Script Summary

This script read the changeset ID from Mercurial and writes it to the app’s plist as the build version.

In addition, this script checks to see if we are doing anything other than a “Debug” configuration build with uncommitted source changes, and if so it generates a build error.
(This feature can be customized or disabled as needed)

Script Details

This script could probably be done as a shell script, and certainly could be done using Perl or Ruby. Since Python is my go-to scripting language, that’s what I used.

First, we use the Mercurial command-line program (hg) to get the changeset ID.

We then check to see if the ID ends in a plus-sign. If it does we look at what build configurations are allowed to be run with uncommitted files. Typically, the Debug configuration is the only one you’d want to do with any changes to the source not checked in. This feature can be configured as needed for your particular build style.

Next, we optionally shorten the build version string. You may want to do this if you want a shorter ID. We could take this fairly lengthy hash and truncate it to wind up with a shorter build version that for all practical purposes would still probably be unique.

The path to the build files is created, and then we use the defaults command to update the build version in the plist.
Note that we do this to the plist after it’s been copied to the build products directory. The “source” plist is not modified by design.

Python Script


#!/usr/bin/python
#
# Obtain the current Mercurial version for the local repository, and write this to the
# build version of the XCode project this script is run from.
# Also can check the build configuration and generate an error if uncommited changes exist
# and (for example) a Release build is being made.
#
# This script expects to be run as a build phase between Link Binary With Libraries and
# Copy Bundle Resources.
#

import os
import os.path
import string
import sys


# Set this to true to cause the IDs to be shortened to "XXXX" or "XXXX+"
shortenID = 0

# Configurations that allow uncommitted changes.
# If a configuration like "Release" is not in this list, the script will generate an error
# if there are changes that haven't been committed when a release build is attempted.
uncommittedAllowed = [ 'Debug' ]
#uncommittedAllowed = [ 'Debug', 'AdHoc', 'Release' ]


fh = os.popen( "/usr/local/bin/hg id -i" )  # Ask Mercurial for the global version ID
buildVer = fh.readline().strip()            # Read it from the process, and strip the newline

if buildVer[-1:] == '+':
    # Plus sign at the end means uncommitted changes

    # Check to see if this should cause a build error.
    config = os.environ['CONFIGURATION']
    if not config in uncommittedAllowed:
        # For this configuration, doing a build with uncommitted changes is an error.
        print "ERROR: Uncommitted changes in the repository while doing %s build!" % config
        sys.exit( 1 )

    # Do we want a shorter ID?
    if shortenID:
        buildVer = buildVer[:2] + buildVer[-3:] # First two chars and the last three chars (plus sign)  
else:
    # Do we want a shorter ID?
    if shortenID:
        buildVer = buildVer[:2] + buildVer[-2:] # First two chars and the last two chars

# Build the path to the Info plist in the build products directory.
infoPath = os.path.join( os.environ['BUILT_PRODUCTS_DIR'], os.environ['WRAPPER_NAME'], "Info" )

print "Setting build version of '%s' to PList %s" % ( buildVer, infoPath )

cmd = "defaults write %s CFBundleVersion %s" % ( infoPath, buildVer )
os.system( cmd )

sys.exit( 0 )   # Success

Installation

This can be embedded in the project, or called from the build phase (as shown in the image).
Regardless, it must be placed in the list of build phases between Link Binary With Libraries and Copy Bundle Resources.

Both ways have their pros and cons. If it’s embedded you can’t “lose” it. If it’s located in a common location and called by all your various XCode projects, it’s easy to fix or modify when necessary.
If it was an integral part of the build, I’d prefer having it embedded, but since it isn’t (the project will build just fine if you take it out of the build phases) I’d rather call it.

This script uses the OSX defaults command, and as of this writing (Sep 2011) the man page for that command warns that it will lose the ability to modify plists in this fashion at some point. When that day comes, I’d much rather fix my script once than edit who knows how many projects.

XCode Environment Variables

Environment Variables

When XCode runs a script, it sets a lot of environment variables into the shell environment that the script executes in.

These are some of the most interesting environment variables available to scripts running from XCode.

CONFIGURATION=Debug
INFOPLIST_FILE=MyCoolApp/MyCoolApp-Info.plist
PRODUCT_NAME=MyCoolApp
PRODUCT_SETTINGS_PATH=/Users/donmeyer/Code/ObjC/MyCoolApp/MyCoolApp/MyCoolApp-Info.plist
PROJECT_DIR=/Users/donmeyer/Code/ObjC/MyCoolApp
PROJECT_FILE_PATH=/Users/donmeyer/Code/ObjC/MyCoolApp/MyCoolApp.xcodeproj
PROJECT_NAME=MyCoolApp
PROJECT=MyCoolApp
SOURCE_ROOT=/Users/donmeyer/Code/ObjC/MyCoolApp
TARGET_NAME=MyCoolApp
TARGETNAME=MyCoolApp

A full list can be generated by running (as a build phase) a script consisting of this line:

set >evars.txt