Having been working with our build setup a lot over the last 6 months I thought I’d share some of NAnt goodness I’ve learnt.
NAnt In General
Firstly, NAnt is quite simple. There’s not a huge amount to learn. It’s basically an application calling other applications and commands for you, all done using xml configuration files (that have a “.build” extension).
By using the same NAnt scripts that the build server uses on your development machines, you can be sure that when you write some code, run the script and it passes on your local machine, it will also pass on the build server. This can give developers more confidence in their code prior to a commit, and also stops the build being broken as much
Break Up Your Build
There’s no need to place everything inside one large build file. To make things easier to find and more maintainable I like to break down my build into multiple .build files. For example:
- start.build
- compilation.build
- metrics.build
- package.build
- clean.build
- live.build
For each .build file I have an associated folder (ie compilation.build > “compilation” folder). The compilation task can then run multiple build files related to it (rather than putting it all in one .build file as per the start of this point). For example inside my “compilation” folder I have “pre-compile.build” and “post-compile.build” for tasks that occur before and after the compilation. A few additional points:
- Just like with code make sure there are no dependencies. For example package.build should not be dependent on metrics.build
- Only introduce variables (ie “properties”) when you need them. For example in start.build you don’t need to include the path to NCover if you’re not going to be doing metrics. That variable should be included in metrics.build instead. Only place “global” or common variables inside start.build
- Make it executable via command line (see my go.bat article). If you’ve followed the above tips you should be able to enter at virtually any step in the process.
Run Just About Anything
If the application you want to use can be run via command line, then you can use NAnt to call it (using the “exec” task).
<exec program="${fxcop.console}" failonerror="false"> <arg line="/p:..\metrics\fxcop\default.fxcop /o:${report.directory}\fxcop.xml" /> <arg line="/summary /searchgac" /> </exec>
For some tasks, rather than using “exec” there are custom tasks already written. For example the “nunit2″ task. NAnt Contrib is a handy library for a lot of additional commands plus you can download additional ones people have written and add them as part of NAnt.
Write Your Own Tasks
I won’t go into huge detail here, as there are plenty of tutorials out there. Jake Opines has written one of the best Custom NAnt Tasks articles I’ve come across so far.>
It’s very easy to write your own custom tasks. It can get trickier when using nested variables, but for a single element task it’s very easy. I’ve written a custom task to use Robocopy to deploy files (using Mirror as it’ll sync faster than just deleting and copying everything again). Here’s theĀ Robocopy.cs source.
Speeding Up The Build
As our code base has become bigger and more complex, the build gets slower. To combat this I’m always trying to implement little changes to make it faster. Here are a few:
- Only run what you need to. We were originally running most things as part of every build. For example, metrics (which are quite processor intensive). Instead we now run metrics/reports as part of a nightly build. Each “commit” only runs MSBuild, aspnet_compiler and NUnit.
- Use Robocopy or BeyondCompare. When deploying files, only deploy what you need to. I often found things likes our huge image libraries didn’t need to be overwritten as they had never changed. In most cases it was just the .dll’s that had changed, so a Robocopy sync works much faster.
- It doesn’t relate to NAnt, but if using CruiseControl.Net, only merge what you need as part of that project. Some of the xml report files that are generated using NAnt can be huge, causing the webdashboard for CruiseControl.Net to slow down dramatically.