sharp bites

standing on the shoulders of giants

The road to Continuous Integration. Part 3: Script it, build it, test it, break it, fix it. Commit it.

Ideally, any new developer in your team should be able to build your project with a single command or double-click. To accomplish that, we are going to use a build script.
You can also pick your poison here, too. I am going to stick with nant for the moment, because it’s the most mature and broadly used, and the one I know better, despite of the XMHell that is the angle-bracket orgy it imposes. Rake seems nice, and has less noise than the others, but I don’t like the fact that I have to install ruby and rake separately. As I said, I like all of my dependencies being included in source control, so that my projects are self-contained (there are some efforts to get ruby and rake as a single executable, though). If you are hesitating between msbuild and nant, you can check out a comparison between nant and msbuild I did a while ago.
project-structure
As you can see on the picture, I have added two build scripts to the root of AltNerdDinner. common-targets.build has, you know, common nant targets. It’s a script I reuse between projects and it has all the tasks I usually execute (compile, recompile, publish or package the project, run the tests, analyze the code or binaries, etc). AltNerdDinner.build is the specific script for this project. By virtue of Convention over Configuration on the project structure and directory and file names, we can remove a lot of unnecessary noise. By adhering to conventions, we just have to set up a couple of properties and simply delegate on common-targets.build for most (if not all) of the tasks, just defining the dependencies between tasks and passing parameters to configure the task where needed.

You can see below how simple it gets, with only 16 lines of code:




<?xml version="1.0" ?>
<
project name="AltNerdDinner" default="compile">
<
property name="dir.root" value="${path::get-full-path('.')}" />
<
include buildfile="${dir.root}/common-targets.build" />
<
property name="file.solution" value="${dir.root}/NerdDinner.sln" />
<
property name="file.project" value="${dir.root}/src/NerdDinner/NerdDinner.csproj" />
<
property name="assembly.tests" value="NerdDinner.Tests.dll" />

<
target name="compile" depends="common.clean, common.init, common.compile" />
<
target name="test" depends="compile, common.test" />
<
target name="publish" depends="test, common.publish" />
<
target name="zip" depends="publish, common.zip" />
<
target name="analyze" depends="publish, common.stylecop, common.ndepend" />
<
target name="build" depends="publish, analyze" />
<
target name="build-full" depends="publish, analyze, zip" />
</
project>



There is also a go.bat script, which just calls nant, passing AltNerdDinner.build as the build file. As a side note, notice that my solution file is in the root of the project instead of in src. I like to keep it there so that I can easily refer to the root of the project from it (useful if you want to dome some pre/post build stuff on Visual Studio or for other tools than reference your solution).

Comments