Building C# Projects with NAnt

NAnt is a cross platform, open source, build tool for use with Mono or .NET. You can use it for automating builds, automating unit testing runs, or for version control. NAnt has no built in GUI interface, nor will it write your unit tests for you. Instead, it provides a powerful means of scripting these tasks so that they are performed automatically with a single command. With NAnt, it is easy to write scripts that work unchanged on both Linux and Windows.

There is a direct parallel between NAnt and the make or nmake tools used by C/C++ developers. The primary advantage that NAnt has over make is that it is written in C# and is designed for use with .NET and Mono. A secondary advantage is that it provides many tools that make it easier to create cross-platform code. For instance, NAnt has custom classes for copying files, deleting files, unzipping files, retrieving data over an HTTP connection, etc. Each of these tasks are written in C# code that works wherever the .NET platform has been implemented. In practice, this means it works on Linux and Windows.

If you are familiar with the Java tool from the Apache foundation called Ant, then you already understand most of what you need to know about NAnt. The primary reason for creating NAnt was simply to have a version of Ant that was optimized to work with .NET.

NOTE: There is no direct parallel in the Delphi IDE to NAnt, though if you have used batch files to create scripts for building your Delphi projects, then you have engaged in the kind of tasks that NAnt automates. There is a stable verion of Ant for Delphi called Want.

If you have been using Visual Studio or Delphi and found that the IDE was not powerful enough to perform your build tasks, then you have an obvious need for a tool like NAnt. In general, there is no build task, no matter how complex, that NAnt can’t be configured to run.For instance, NAnt makes it relatively easy to build multiple assemblies in a particular order and to copy the results to any location on a local or remote machine.

Even if you are happy building your projects inside Visual Studio or Delphi, you may still find that NAnt is useful. In particular, NAnt can help you automate the task of running unit tests, and it can help you automate other tasks. All in all, there are some 75 built in tasks that are available inside the current NAnt builds.

Installing NAnt

Short version: Download the NAnt binaries, unzip the package they come in, and put the bin directory on your path. That is really all there is to it, and if you have no further questions, you can safely skip ahead to the section on using NAnt.

NAnt comes with source, but I suggest getting the binary package first. If you want to work with the source packages, then I would use the binary version of NAnt to build the source package. After all, NAnt is designed to make the process of building C# code extremely simple.

You will find a link to the NAnt binary download on the NAnt home page at http://nant.sourceforge.net/., or else you can go to the NAnt SourceForge project and follow the link to the download page. At the time of this writing NAnt was up to release candidate 3 of version 0.85. This means that you can download the nant-0.85-rc3-bin.zip to get the binaries, or download nant-0.85-rc3-src.zip to get the source code. I provide these latter links primarily so you can see the file naming scheme. Since updates occur frequently, you should go directly to the download page and get the latest files yourself.

NOTE: If you are used to standard commercial releases, you might be a bit intimated by the fact that NAnt is only at version 0.85. However, you have to remember that there is no need to rush the delivery of free, open source, projects. As a result, an open source product at version 0.85 is often the rough equivalent of a 1.5 or 2.0 version of a commercial project. NAnt is unlikely to earn the 1.0 moniker until it contains a wide range of features and a very low bug count.

Once you have downloaded and unzipped the binary files, you should put the bin directory where NAnt.exe is stored on your system path. There are some 14 different assemblies included in this project, so it will not help to try to copy NAnt.exe to some convenient location. Furthermore, I would not suggest copying the exe and all 14 DLL’s somewhere, as that is likely to lead to DLL hell when you want to upgrade the product to a new version.

If you also downloaded the source, then you can now go to the root of the unzipped source project and type the word NAnt at the command prompt . This will automatically build the project, placing the output in a directory called build. If you don’t like the default location for this output, you can specify the output directory during the build process by typing:

NAnt prefix=<MyPreferredLocationForTheOutput>

For instance, you might write:

nant prefix=d:\bin\compilers\nant

NOTE: It is possible to download the source to NAnt and to build it using either Visual Studio or NMake. However, it is much simpler to follow the steps outlined above.

Using NAnt

NAnt is based on an easy to understand technology that is driven by XML. In its simplest form, you need only put the XML defining the tasks you wish to execute in a file called NAnt.build. Then place your XML file in an appropriate directory, usually the root directory of your project, and simply type the word NAnt.exe at the command line. :NAnt will automatically discover and run the script.

NOTE: If you have a large project, it is common to have one NAnt script calling another script. For instance, you might have one base script in your root directory, then have child scripts in the root directory of each of the assemblies making up your project. The exact syntax for doing this will be discussed in future articles. If you only have one script in each directory, then you can call them all NAnt.build. If you need to place multiple scripts in a single directory, then you can give them different names, and explicitly call the script when you run NAnt, using the following syntax: NAnt -buildfile:d:\src\csharp\Simple\MyFile.build.

Consider the following brief example

<?xml version="1.0"?>

<project name="Getting Started with NAnt" default="build" basedir=".">

  <target name="build" description="Build a simple project">
		<csc target="exe" output="Simple.exe" debug="true">
			<sources>
				<include name="simple.cs" />
			</sources>
		</csc>
	</target>

	</project>

This simple script will compile the following short C# program:

using System;


namespace SimpleNameSpace
{
	public class Simple
	{
		static void Main(string[] args)
		{
			Console.WriteLine("What we think, we become.");
		}
	}
}

Notice the project tag at the top of the build script:

<project name="..." default="build" basedir="."> 

As you can see, it states that the default target for the project is named build. Looking carefully at the script, you can see that there is a target named build:

 <target name="build" description="...">

This target has a single task in it called csc:

<csc target="exe" output="Simple.exe" debug="true">
   <sources> <include name="simple.cs" /> </sources> 
</csc>  

NAnt defines a series of tasks, which you can read about in a special section of the NAnt help file called the Task Reference. The csc task helps you build C# files. There are about 75 other tasks that come with NAnt, and you can create you own tasks by writing C# code and adding it to NAnt. Tasks that ship with NAnt include modules for copying, moving and deleting files, for running NUnit scripts, for the changing or reading the environment, for executing files, for accessing the Internet, for working with regular expressions, and so on.

Multiple Targets

You can define more than one task inside an NAnt XML file. Here is a complete script containing both a build target and a clean target

<?xml version="1.0"?>

<project name="Simple" default="build" basedir=".">

	<description>A simple NAnt script.</description>

	<property name="debug" value="true" overwrite="false" />

	<target name="clean" description="Clean up the directory">
		<delete file="Simple.exe" failonerror="false" />
		<delete file="Simple.pdb" failonerror="false" />
	</target>

	<target name="build" description="compile Simple.cs">
		<csc target="exe" output="Simple.exe" debug="${debug}">
			<sources>
				<include name="Simple.cs" />
			</sources>
		</csc>
	</target>

	</project>

The clean target calls the delete task twice in order to delete the files that were created when the build target was run. The clean target can be accessed by issuing the following command at the shell prompt:

nant clean  

As mentioned earlier, running NAnt without any parameters will run the default task, which in this script is defined as build.

<project name="Simple" default="build" basedir=".">
Defining Properties

Notice that a simple property is defined in the XML file:

<property name="debug" value="true" overwrite="false" />

The value of the property is then accessed by using a simple $ and curly brace syntax similar to that used to define a variable or a macro in a make file:

<csc target="exe" output="Simple.exe" debug="${debug}"> 

When the script is run, the ${debug} syntax is replaced with the value of the property called debug, which in this case is set to true.

<csc target="exe" output="Simple.exe" debug="true">

You can often simplify your XML files by defining several properties:

<?xml version="1.0"?>

<project name="Simple NAnt Script" default="build" basedir=".">

	<description>A simple NAnt build file.</description>

	<property name="debug" value="true" overwrite="false" />
	<property name="fileName" value="Simple" overwrite="false" />

	<target name="clean" description="clean up generated files">
		<delete file="${fileName}.exe" failonerror="false" />
		<delete file="${fileName}.pdb" failonerror="false" />
	</target>

	<target name="build" description="compile source">
		<echo message="${fileName}"  />
		<csc target="exe" output="${fileName}.exe" debug="${debug}">
			<sources>
				<include name="${fileName}.cs" />
			</sources>
		</csc>
	</target>

	</project>

Notice that this script defines a second property called fileName, which is set to the value Simple. By merely changing the value of this one property, you can effect changes in the five other locations where the property is used in the script:

<include name="${fileName}.cs" /> 

This gives you the same kind of support for reuse in your XML build files that you can get by defining properties or variables in your source code. Features of this kind are important because they help to show the power and flexibility of a tool like NAnt.

Summary

NAnt provides an intuitive and powerful means of controlling the build process, and of automating common tasks encountered during the development process. It comes with a rich set of predefined tasks that cover most developer’s needs. However, you can write C# code to add your own tasks to NAnt if you have special needs that are not available in the default release of the project.

Last week when discussing mock objects, I mentioned that there were commercial tools which perform a similar task. The is true for NAnt. There are commercial tools such as FinalBuilder that perform many of the same tasks that NAnt performs. Some of these tools have fancy features that can sometimes help speed the development cycle. I encourage you to explore these tools. NAnt, however, has the advantage of being a free, open source product that ships with source, and that is based upon respected technology which is not likely to become outdated in the foreseeable future. Because NAnt comes with source, and because it is designed to be extensible, you will find it easy to write your own NAnt modules that perform custom tasks. That kind of extensibility is not always available in commercial products.

Visual tools can solve a certain class of programming problem, but there are many instances in which source code proves to be the most powerful solution to a difficult programming problem. NAnt is a powerful and flexible enough tool to give you the kind of control that you need over project development. In future articles I will explore of the many advanced features available to developers who take the time to master the simple NAnt syntax.

No comments yet

Leave a Reply

You must be logged in to post a comment.