Tool and library for performing automated modifications to Xcode project files
Ruby C
Pull request Compare This branch is 8 commits ahead of ddribin:master.
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
bin
lib
test
.document
.gitignore
.project
CHANGELOG
Gemfile
Gemfile.lock
LICENSE
Manifest
README.textile
RUBYFORGE
Rakefile
VERSION

README.textile

ZergXcode

ZergXcode is a tool and library for performing automated modifications to Xcode
project files. It can be used to work around Xcode’s limitations, such as

  • the cumbersome UI for assigning files to the main target vs the test target
    (zerg-xcode retarget)
  • the impossibility to easily drop other people’s code in your project
    (zerg-xcode import)
  • a problem that annoys you enough to learn the API and write a ruby script

User Instructions

If you’re on OSX Leopard, you already have most of the infrastructure in place.
Install the gem by typing the following in Terminal.

sudo gem install zerg_xcode

Stay in Terminal and take a look at the available commands.

zerg-xcode help

Then start playing with your code project.


zerg-xcode help
zerg-xcode help ls
zerg-xcode ls ProjectName

You can learn all about importing projects in
this blog post

ZergSupport is an example of a project
that you can import. You can read more about it in
this blog post

Developer Notes

The rest of the file is useful if you’re considering tweaking the code. Here are
a few reasons to use this library:

  • ruby and rubygems come pre-installed on every Mac
  • full test coverage, so it’s high quality and easy to refactor
  • MIT license, so your boss will not hate you
  • all commands are plug-ins, so all the infrastructure is in place

Getting started.

It’s recommended to start by playing with the API in
lib/zerg_xcode/shortcuts.rb

moonstone:ZergSupport victor$ zerg-xcode irb ZergSupport
>> $p
*snip*
>> $p.attrs   # shows the names of all the attributes of the Xcode object
=> ["isa", "buildConfigurationList", "hasScannedForEncodings", "targets", "projectDirPath", "compatibilityVersion", "projectRoot", "mainGroup"]
>> $p['targets'].first.attrs   # navigates a list 
=> ["name", "isa", "productType", "buildConfigurationList", "productReference", "productName", "buildRules", "dependencies", "buildPhases"]
>> $p['targets'].first['name']   # inspects attributes
=> "ZergSupport"
>> $p['targets'].map { |target| target['name'] }  # more attribute inspection
=> ["ZergSupport", "ZergTestSupport", "ZergSupportTests"]
>> $p.all_files.length  # call method in PBXProject 
=> 116

You can up load your favorite Xcode project in irb, and understand the object
graph. Once you feel you have a decent grasp, you can start experimenting with
changing the graph, as shown below.

moonstone:ZergSupport victor$ zerg-xcode irb ZergSupport
>> $p['targets'][2]['name'] 
=> "ZergSupportTests"
>> $p['targets'][2]['name'] = 'ZergSupportTestz'
=> "ZergSupportTestz"
>> $p.save!  # the project remembers where it was loaded from
=> 54809
>> quit  # now load the project in Xcode and see the change

Plug-ins Extend the Command Set

The set of commands accepted by the zerg-xcode tool can be extended
by adding a plug-in, which is nothing but a ruby file that exists in
lib/zerg_xcode/plugins. An easy example to get you started can be
found at lib/zerg_xcode/plugins/ls.rb. The code in there should
help you if you want to write your own command-line tool as well.

Plug-ins must implement the methods run(args) (called when the
command is executed) and help (called when the user needs help on
your plug-in), which must return a Hash with the keys :short and
:long.

If you write a plug-in that seems even remotely useful, please don’t be shy and
send a Pull Request on Github.

Encoding and Decoding Files

The code is in lib/zerg_xcode/file_format. You can safely ignore it
unless you’re handling a new file format, or you want to make the decoding
better.

A .pbxproj file is decoded by the chain Lexer → Parser → Archiver.unarchive
into an object graph. An object graph is encoded back into a .pbxproj by the
chain Archiver.archive → Encoder.

The decoding process discards comments and the order of objects in the graph, so
an encoded .pbxproj will not be the same as the original. However, Xcode will
happily open an encoded file, and that’s all that matters.

Xcode Object Graph

Objects in the Xcode object graph are represented by XcodeObject instances. The
code for XcodeObject is in lib/zerg_xcode/objects/xcode_object.rb.
Xcode objects use the ‘isa’ attribute to indicate their class.
XcodeObject.new implements isa-based polymorphism as follows: if
ZergXcode::Objects has a class with the same name as the ‘isa’
property, that class is instantiated instead of XcodeObject.new.

So, you can add magic methods to Xcode objects by implementing them in
XcodeObject subclasses contained in the ZergXcode::Objects module.
By convention, these classes are implemented in
lib/zerg_xcode/objects.

XcodeObject stores the attributes of the original object, and also keeps track
of subtle meta-data from the original objects, such as the object version and
archive identifier. This is done so modified Xcode files resemble the originals
as much as possible.

Testing

The test directory contains a parallel tree to
lib/zerg_xcode. Coverage must remain excellent, because this is a
tool that operates on developers’s hard work, which may not be protected by
version control.