A quick introduction to NAnt

There’s lots of open source libraries out there which have become ubiquitous in today’s world. One of those is NAnt, a .Net build tool.

I’ve wanted to use NAnt for a while but never had a sufficient pressing need to do so. After forcing myself to get familiar with it so I can build the latest Castle trunk, I decided to add it to a few of my own projects. What follows is a summary of that experience, and some helpful NAnt tips for notices.

The first step is to set up your development environment. Download the NAnt binaries for the latest release (0.85 as of this writing) and extract those files somewhere on your computer (it went to c:\dev\utils\nant on my computer). Add the NAnt bin folder to your system path so you can run nant.exe from any command prompt.

Now that you have NAnt on your system, you need to create a build file. Build files are XML files which contain the instructions on building your application. Before creating your build file, you need to understand your project’s structure and requirements.

I was writing an NAnt script for my O/R mapper framework, so my project structure was as follows.

CodeSmith
ORMapper.Entities
ORMapper.Services
ORMapper.Services.Log4NetIntegration
ORMapper.Services.NLogIntegration
References
ORMapper.sln

The CodeSmith folder contains CodeSmith code generation templates; the References folder contains DLLs for dependent libraries; ORMapper.* folders contain Visual Studio projects. My build process should build each VS project, adding references as needed, dumping the output in a bin\ directory.

I added a file, default.build (NAnt uses the .build extension by default), to my project directory and started it with the following.

<?xml version="1.0"?>
<project name="ORMapper" default="build" basedir=".">
<description>ORMapper Build</description>
<property name="debug" value="true" />
<property name="build.dir" value="bin" />
<echo message="build.dir: ${build.dir}" />
<echo message="debug: ${debug}" />

The <property /> tags let you define variables which can be overwritten in the command line. The debug property can thus be overwritten to false if you want to compile with debugging off. The build.dir property set the default output (build) directory.

Next, I added a build step (called a target) to initialize the output directory (i.e. create it). This step ensures the bin\ directory exists. I named this target init.

<target name="init">
<mkdir dir="${build.dir}" />
</target>

To ensure the build directory is clean (i.e. it doesn’t include any files from old builds that are not part of new builds), I added a target named clean which deletes any files in it.

<target name="clean" description="remove all generated files">
<delete dir="${build.dir}" failonerror="false" />
<delete>
<fileset basedir="${build.dir}">
<include name="*.*" />
</fileset>
</delete>
</target>

On to the actual compiling of our projects. Since there is a specific build order, I need to be sure the compile instructions are in the appropriate order. NAnt has a <csc /> tag which executes the C# compiler from the .Net Framework. You send instructions to the compiler by adding XML properties to your build script. The below code shows the compilation instructions for the ORMapper.Entities project.

<target name="build" description="debug build (default)" depends="clean,init">
<csc target="library"
output="${build.dir}ORMapper.Entities.dll"
debug="${debug}"
doc="${build.dir}ORMapper.Entities.xml">
<sources basedir="ORMapper.Entities">
<include name="**/*.cs" />
</sources>
<references basedir="References">
<include name="System.dll" />
<include name="WilsonORMapper.dll" />
</references>
</csc>

Take note of the following key elements in the above:

  • The <target /> is dependent on other targets (depends=”clean,init”). This ensures certain build targets are executed prior to this target.
  • Compiler instructions are provided for the type of file to compile, output file name, and so-forth. Note the use of properties — for example, the debug=”${debug}” command will insert the value of the debug property, allowing you to use a single build file for debug and release compilation.
  • To identify the files to include during compilation, you use the <sources /> tag. One or more <include /> tags specify which files in the base directory to compile. (In this case, there are only *.cs files.) By specifying **/ at the start of the name, you tell NAnt to include all files that match the mask in the base directory and all subdirectories.
  • Finally, we specify our references. Framework DLLs (like System.dll) do not need any special path designations; NAnt will find them. For all others, you need to specify the path. In my case, I put reference libraries in a References folder, so I specify that as the base directory. This is where NAnt will find WilsonORMapper.dll.

For subsequent projects, the steps are mostly the same. The only significant difference is in output file names, base directory names, and references. Below is the XML code to compile the other three projects.

<csc target="library"
output="${build.dir}ORMapper.Services.dll"
debug="${debug}"
doc="${build.dir}ORMapper.Services.xml">
<sources basedir="ORMapper.Services">
<include name="**/*.cs" />
</sources>
<references basedir="References">
<include name="System.dll" />
<include name="System.Configuration.dll" />
<include name="System.Data.dll" />
<include name="System.Web.dll" />
<include name="WilsonORMapper.dll" />
<include name="..${build.dir}ORMapper.Entities.dll" />
</references>
</csc>
<csc target="library"
output="${build.dir}ORMapper.Services.Log4NetIntegration.dll"
debug="${debug}"
doc="${build.dir}ORMapper.Services.Log4NetIntegration.xml">
<sources basedir="ORMapper.Services.Log4NetIntegration">
<include name="**/*.cs" />
</sources>
<references basedir="References">
<include name="System.dll" />
<include name="System.Configuration.dll" />
<include name="System.Data.dll" />
<include name="log4net.dll" />
<include name="WilsonORMapper.dll" />
<include name="..${build.dir}ORMapper.Services.dll" />
</references>
</csc>
<csc target="library"
output="${build.dir}ORMapper.Services.NLogIntegration.dll"
debug="${debug}"
doc="${build.dir}ORMapper.Services.NLogIntegration.xml">
<sources basedir="ORMapper.Services.NLogIntegration">
<include name="**/*.cs" />
</sources>
<references basedir="References">
<include name="System.dll" />
<include name="System.Data.dll" />
<include name="NLog.dll" />
<include name="WilsonORMapper.dll" />
<include name="..${build.dir}ORMapper.Services.dll" />
</references>
</csc>

The last thing to do is to copy the reference DLLs to the build directory (the compiler doesn’t do this). This is done using the <copy /> tag, which lets you specify a source fileset and an output directory. In the below, we add instructions to copy all DLL and XML files from the References folder to the build folder. Finally, we add the end tag for the target and project, finishing our NAnt build file.

<copy todir="${build.dir}">
<fileset basedir="References">
<include name="*.dll" />
<include name="*.xml" />
</fileset>
</copy>
</target>
</project>

That’s it! There’s a lot more NAnt can do, and the above just brushes the surface. Hopefully the above example helps you get started with NAnt and take your first steps toward incorporating it into your projects.

0 thoughts on “A quick introduction to NAnt

  • Julian Skagen says:

    Hi!

    Nice demonstration of NAnt. I have been trying to set up NAnt for 5 hours now. I’m able to compile 3 of 45 projects and run 1 unit test! The fourth project I’m stuck on is the domain model and I keep hitting the CS0234 error message. The strange thing is that the compile process is successful in VS. Did you do any tricks to avoid this silly error message?

    The MSDN docs isn’t very helpful either. I cannot really understand what they are trying to explain in this sample:
    http://msdn2.microsoft.com/en-us/library/0e92xd7b(VS.80).aspx

    I hope you can enlighten me,
    thanks,
    Julian.

  • Julian Skagen says:

    Hi!

    It’s a compile output error given by the csc command executed through the csc tag. I was able to resolve the problem by explicitly defining all needed assemblies in the references (all the assemblies that devenv pulled into the bin/debug folder). This is more assemblies then referenced in VS.

    Thanks,
    Julian.

  • You should check your project references — particularly any System.* references that are out of the ordinary — and make sure each is listed in your NAnt build script. Note how above, different target projects reference different sets of System DLLs. You may need one that isn’t being referenced.

    Hope that helps!

  • Julian Skagen says:

    Hi Brian!

    Thanks for the feedback. Seems like you pretty much confirm the behavior I experience aswell. Some System.XYZ assemblies must be referenced explicitly as they will not be resolved automatically by calling GetDepndentAssemblies().

    Thanks,
    Julian.

  • Hi,

    Can you please tell me, how to add multiple sources and in which tag i can add web references to make a
    build for vb project in nant.

  • For multiple sources (i.e. projects), simply duplicate the .. section for each project.

    References are added in the same section (within ). As for VB-specific, I can’t help you out there — I don’t use VB!

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

This site uses Akismet to reduce spam. Learn how your comment data is processed.