I was browsing the web for a bunch of guidelines on how to structure NAnt build files. I found something useful, called TreeSurgeon, but it wasn't exactly what I was looking for. After a good 30 minutes of googling around, I decided to write my own post about it.

First of all, I separate each NAnt file based on its set of tasks. For example, if I have a build that includes unit tests and documentation, I'll make 3 NAnt files:

  • build.xml
  • test.xml
  • doc.xml

I'll also add a fourth NAnt file called base.xml, which is included in each file using the <include> NAnt task. base.xml will hold common tasks like getting a bunch of files from a SCM or build a version number.

The next step will be to add targets to each file. Usually, I put a "buildAll" or "testAll" target to launch all the targets. This would normally be called from CruiseControl .NET configuration file.

I'll also include a "beforeBuild" and "afterBuild" task in each NAnt file. Imagine these targets as things you would want to do before running a target. For example, you might want to copy a set of files before compiling your source code. This file copy could end up in the "beforeBuild" target. The same applies to the "afterBuild" target. As an analogy, think of those targets as fixture setup and teardown in the unit test paradigm.

I also try to make my NAnt files generic so they can be reusable with a branch. This means I put all possible paths as properties so they can be reassigned.

One thing I'm not doing anymore is the help target. I would maintain a help target so people could get a quick ref of common targets to call. This rapidly became a nightmare to maintain as people were modifying the build files all the time. They were aware of their content so the help target was useless. Another thing I didn't do early on was documenting my targets or specific areas that would require a detailed explanation. You don't look at those build files everyday. So, when you're in a hurry to fix a build, and you're wondering why you wrote a NAnt script in a very inconvenient way, a few lines of comments helps! 

In doc.xml, you can put all sort of documentation. The first thing you could do is build the SDK of your program with Sandcastle. You can also generate a code coverage report or a unit test report as Html files. You can call <codestats> from NAnt Contrib and create a report about it. Another thing to document is the list of bugs that were fixed in a release. If you have a bug reporting software like Bugzilla, you can extract a report from it with the <get> NAnt task.

The 3 types of build files given above can be extended. If you have a deployment scenario, you can have a deploy.xml file. It can have targets to build a zip or msi of your program. It can also deploy your .msi to a virtual machine. Another possibility would be an environment.xml file where it loads up databases required for your entire build process. It can start a Windows service if its required during your unit tests. I've used this kind of file for our integration tests that were run once a week. deploy.xml can also deploy reports you did in doc.xml on a Wiki. If you have a customer who is curious about reports, just upload them on a collaboration portal. I found that Wikis are great to accomplish this because of their flexibility. I tried it with SharePoint and gave up after an hour of looking around.

If things get to complicated with NAnt tasks, I'll use a <script> task and write a C# method to solve a specific problem. For example, you might want to use the current date to build a version number. A C# method can do this for you. If some of your methods are redundant in each build file, think about moving them in base.xml. Don't forget to add a prefix. This way, you'll know where it's coming from when you call it inside a specific task.

Automating tasks with a build process adds value to your entire development process. As always, it might get neglected because it will be perceived as slowing people down instead on focusing on new features to be added to the software. Unfortunately, this statement couldn't be more wrong. A well maintained build process will actually show problems before the product gets shipped. It will force you to deliver a quality product at any time of the day.