Some time ago, a good while actually, I wrote about getting started using NAnt to automate the build process for .NET projects. I still am, but I’ve learned more about NAnt, and have refined the typical build tasks that I use. In fact, my typical build file now looks rather different from how it looked back then.
I’m going to talk a bit about common build tasks, and then show my new standard build file, which should be useful enough to start any new .NET project with.
The Tasks Overview
The prupose of a build file is to, clearly, automate build-related tasks. Such tasks include:
- Cleaning out the build directory
- Initializing the build
- Compiling the code
- Running unit tests
- Gathering unit test coverage
- Running code analysis
- Updating build version number
- Packaging for deployment
Each task will be briefly described below.
You want to be creating build artifacts in a clean directory to ensure there are no leftovers from a previous build. This usually involves deleting the build directory if it exists.
Do any initialization that needs to be done; this can be creating a build directory, setting any initial properties for the build process, and any other initial tasks.
Pretty obvious: compile the code, the results of which will be placed in the build directory.
Run any tests that need to be done: unit tests, integration tests, system tests, etc.
Runs tests and gathers test coverage statistics. This could be part of the test task, but I like it seperate, as I sometimes only want to run the tests, and not bother about the coverage.
Perform analysis on your code; this can be static, meaning the code is merely examined, not run, or it can involve actually executing the code to gather information. Tools for this purpose, some of which I’ve written about before, include FxCop, NDepend, and Simian.
This task can handle the process of incrementing the project version number. It’s useful doing it here as it saves poking around in another location to manually update the number.
Gather up the distributable build artifacts and put them into a distrubution container; this could be a ZIP file, an MSI, or an EXE, among other options.
The Full Build File
Following is my revised main build file, proj.build. I say “main” for a reason – this file is the starting point for the build process, but does not contain all the build information. As indicated previously, my build file used to be fully self-contained, in that all properties and targets were in one place, somewhat comingled. When I copied the build file from one project to another, the copy of the file needed to be edited in the right places to have the right properties for the current project.
This was a tedious task, so I extracted standard information out of the main build file, and placed it in a seperate file – see next code sample. The sample shown below is proj.build – a generic name that I don’t need to change for each project. It contains properties that do not need to be edited, and all the build targets. The editable properties are located in the next file. With one exception: for the whole thing to work, the
name property (line three) must be set in the main file, since it is the entry point.
Note that the fourth line below includes the properties file into this main file.
This is a pretty straightforward build file, with a typical set of tasks for day-to-day programming work. There is room for improvement: I need to add a task for generating documentation from XML comments in code, and might be able to use additional analysis tools such as Simian. But such additions, or any other that follow the pattern, should be added without too much trouble.
And following is the proj.properties.build file. Note that it contains the configurable values that may need to change from one project to another. By containing these changeable values here, there should be very little need to change the main proj.build file. Unless perhaps if adding new tasks, as indicated above.
So those are my new standard build files. I can copy them to one project and only need to edit a relative handful of lines, and I know where they all are. The main build file makes some assumptions about the solution structure and file naming, but I think they are reasonable and intuitive. And if I decide to add a new task to the standard build file, I can do so, and can easily propagate that change among my various projects.
There are some specific tasks that I will be discussing in near-future blog posts, namely the ones on packaging and project versioning. Those tasks are included in the main build file shown above, but with minimal documentation. I shall dig into them in more depth fairly soon.
Update – I’ve published a post describing the
package target. There’s not much to it, but I explain the use of the
Update 2 – I’m on a roll. I’ve just published a post on the details of how I handle the project versioning, and my reasoning behind the decision to do it the way I did.
Just as in code, redundancy and ambiguity are not welcome in build files. I’ve recently reduced both in one project, and it has worked well so far. I plan to add the new standard build file to my various .NET projects, only needing to edit a few bits, and can then carry on as usual.