Introducing Phantom - a build system written in C# and Boo

*Update* Since I published this post, Spectre has been renamed to 'Phantom' in order to avoid confusion with the Specter BDD framework.

*Update 2* The syntax used in this post is out of date and will not work with Phantom v0.2 or later. Please check out the Phantom Documentation to see the updated syntax.

I've recently been reading Ayende's excellent book Building Domain Specific Languages in Boo and wanted to try out the techniques I've been learning on a real project. As such, I've created a Boo-based build system called 'Phantom'.

Why a build system?

In the past I've tended to use NAnt for building my projects, but I've never really liked the XML-driven build files. I much prefer the approach taken by tools such as Rake that allow you to configure your build through code.

While it is possible to use Rake with .NET projects (for example, Fluent NHibernate uses Rake), it requires that you have Ruby, Gems and Rake installed in order to use it. One thing that I really like about NAnt is that I can store it alongside my code inside my source control repository. This way, when someone checks out the source code they can run the build without having to install any additional tools.

Like Rake, Phantom is completely code-driven and like Nant, it is completely standalone with no installation required.

How does a Phantom build script differ from a NAnt script?

Here is a typical NAnt build script that declares three targets: 'default', 'compile' and 'test'. The 'default' target depends on both 'compile' and 'test'. 'Compile' will build a project using MSBuild and 'test' will run the project's unit tests:

<project xmlns="http://nant.sf.net/release/0.86-beta1/nant.xsd" name="MyProject" default="default">
	<target name="default" depends="compile test" />
	<target name="compile" description="Compiles the project">
		<msbuild project="pathtomyproject.sln" target="Build">
			<property name="configuration" value="release" />
		</msbuild>
	</target>
	<target name="test" description="Runs tests">
		<nunit2 failonerror="true" verbose="true">
			<formatter type="Plain" />
			<test assemblyname="pathtomytestassembly.dll" />
		</nunit2>
	</target>
</project>

Here is the same script using Phantom:

target default, (compile, test):
   pass
 
desc "Compiles the project"
target compile:
   msbuild("path/to/myproject.sln", { @configuration: 'release' })
 
desc "Runs tests"
target test:
  nunit("path/to/testassembly.dll")

Note that the syntax is very similar to Rake. To run this script you would save it as 'default.boo' and then run 'Phantom.exe' in the same directory. Much like Rake, you can run phantom -t to get a list of targets in the current project:

Phantom v0.1.0.0
Copyright (c) Jeremy Skinner 2009 (http://www.jeremyskinner.co.uk)
http://github.com/JeremySkinner/Phantom


Targets in build.boo:
compile          Compiles the solution
default
package          Creates zip package
test             Executes tests

Utility functions

At the moment, Phantom only has a small number of utility functions. Currently these are:

  • msbuild(project, options) - executes msbuild against a project/solution file.
  • nunit(assemblies, options) - executes nunit-console.exe against a collection of test assemblies.
  • exec(command, args) - executes an arbitrary program.
  • env(variable) - gets an environment variable.

These are in addition to Boo's built-in functions. I plan to add more over time.

Does the world really need another build system?

Probably not. This more an exercise for me in learning how to build DSLs with Boo and Rhino DSL than anything else. Nevertheless, I plan to use it with a couple of real projects. If anyone else decides to use it, please let me know!

Where's the source?

You can grab the source from GitHub and run Build.bat in order to compile the latest binaries.

Written on August 11, 2009