What is NAnt
NAnt
is an automated build tool for .NET
based applications. That means, you can write some scripts in NAnt and let it build your projects without any manual intervention. You can also trigger the script from batch jobs for CI (Continuous Integration) systems.
But why? There is Visual Studio
which can build (compile) all .NET projects, what is the need for a separate build tool?
To build a project/solution from Visual Studio, you need to open it, open the project, click on Build
etc. That is doable in the development environment where the developer is actually coding the application with Visual Studio IDE. But, if we want to
- Trigger the build based on some pre-defined events (e.g. nightly job or a code check-in)
- Build on a system without Visual Studio installed on it
- Do other stuffs like - creating output directories, running unit tests, deploying a site on IIS etc.
Visual Studio does not prove to be very useful. There we use some tool to automatically kick-off a build process, and automate other tasks around it. These tools are build tools like NAnt or MSBuild.
Why use NAnt for building .NET projects
NAnt is a build tool for .NET application (based on Ant for Java), that can help in building .NET application, run unit tests, do configuration and lot more things. It is actually older than MSBuild
and arguably, the most widely used build tool in the .NET community.
Though NAnt is used mostly as a build tool, because of its generic nature and lot of pre-configured tasks, it can be used simply as an automation tool as well! Some of the main features or NAnt are
- Free to use, and open source
- Runs on
Windows
andLinux
systems (based on Mono framework) - Build scripts are basically
XML
files in specific format (with.build
extension) - Workflow is created with a series of “tasks”
- It can run
NAnt
specific tasks, and any other command-line tools with<exec>
task
NAnt also comes pre-included with a bunch of open source libraries. Most recent versions of NAnt distribution includes - NUnit for unit testing, NDoc for documentation, SharpZipLib for binary compression etc.
But, is NAnt still relevant?
From the NAnt official site we can see the last release v0.92
was in 2012, and it never made it to version 1.0
. Is it still usable?
Though the project was not updated recently, it is completely stable and usable for production release. All the useful tasks are already included in NAnt and the community supported NAntContrib project. And anything else can be run simply as a normal command-line application with exec
task. And, probably, most of the .NET teams still use NAnt behind their build or CI system.
Installing NAnt
NAnt has binary and source distributions. Binary is enough to setup and run builds. We’ll install it on a Windows
based system, for other platforms or more details, see documentation here.
- Download the current version from here - see “Releases” menu on the left, e.g. nant-0.92-bin.zip
- Extract the contents to a directory where you have access. e.g. “C:\nant”
- Add this path to “System Environment PATH” for easy access. Once done, command
nant -help
should work - OR, in case you do not want to add another new path, create a wrapper over
nant.exe
on a system accessible path. For instance, create a batch file asnant.bat
and save it inside (for example)C:\Windows
with following content (make sure the exe path is correct)
@echo off
"C:\nant\bin\NAnt.exe" %*
Note: The NAnt configuration is present in “C:\nant\bin\NAnt.exe.config” (based on your installation path). You can check and change some configurations, if needed e.g. default .NET framework. Also, global properties can be set in this file.
Installing NAntContrib
NAntContrib is another community driven tool that adds lot of additional features (tasks) to the NAnt like adding binaries to GAC, checking-out source code, creating IIS virtual directory etc. At some point, you’ll definitely need it to complement NAnt.
How to use NAntContrib?
When NAnt runs a build script, for each task it runs the code from the installed assemblies. If we want to add more tasks, we need to include assemblies that has code for those new tasks. NAntContrib basically adds bunch of additional tasks that are defined in it’s own binaries. So, to enable NAnt to execute those NAntContrib tasks, either we have to include the path to the binaries in the script with <loadtasks>
or simply add the NAntContrib binaries directly inside nant\bin
so that NAnt can find them. Once we’ve copied the binaries to nant\bin correctly (see below) you can use NAntContrib tasks just like the default NAnt taks in any build script, without any additional configuration.
- Download the latest version from here - see “Releases” menu on the left, e.g. 0.92
- Download the required package, e.g.
nantcontrib-0.92-bin.zip
- Extract them to a directory with access, e.g.
C:\nantcontrib-0.92\bin
- Now “ideally” you “should” be able to use it by adding a
<loadtasks>
in your build file with the location of the NAntContrib assemblies, like
<loadtasks>
<fileset>
<include name="C:/nantcontrib-0.92/bin/**/*.dll" />
</fileset>
</loadtasks>
But that may not work, as it didn’t for me!
Note: You’ll most probably get an error like “Failure scanning CollectionGen.dll for extensions. Could not load file or assembly ‘Microsoft.VSDesigner, Version=7.0.3300.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a’ or one of its dependencies. The system cannot find the file specified.”
Unfortunately the solution is not clear from the official documentation. Here you’ll need some weird wizard skills to make it work!! Thanks to posts like this and this for the solution.
Solution to “CollectionGen.dll” error
- Copy
CollectionGen.dll
, and the twoSLiNgshoT
assemblies in a newtasks
folder insidenant\bin
directory e.g. “C:\nant\bin\tasks” - Copy all the other (main) assemblies, including
NAnt.Contrib.Tasks
directly insidenant\bin
directory e.g. “C:\nant\bin” - Now the NAntContrib tasks will be part of all the NAnt scripts by default
Anatomy of NAnt build files
NAnt runs based on .build
files. Each files define a bunch of tasks grouped in targets, and optionally some properties. Then NAnt can run one or more targets based on instructions. Here we’ll look at some basic constructs of a build file.
Each NAnt
build file is an XML
file with bunch of command and configuration.
<project>
- Build files will have
<project>
as the root element - Basically each file is a NAnt project
- Project can have optional
description
andbasedir
which will be base root path for all relative paths - Project generally defines the
default
target to be executed
<property>
- A project can define as many properties as it wants
- Properties are like variables with a
name
andvalue
- Property names are case-sensitive & can include alphabets, numbers, hyphen, underscore and dot (.). The value of a property can be used as
${property.name}
. See the property documentation here - Value of a property can be passed as command line argument while running NAnt as
-D:prop.name=value
. Properties can also be set to be read-only by specifyingreadonly="true"
- Changing value of a property in the script is like declaring the same property again with new value
Note: The properties those are set with command-line arguments become read-only
, and cannot be modified inside the NAnt script.
<target>
- A
<target>
is a single or set of tasks, with a name & optionaldescription
and other attributes. Generally, a target represents a complete step in the whole build process, like - init, build, run-tests etc. - Target can optionally have
unless
orif
attributes to control execution based on some condition. Ifif
evaluates totrue
, the target executes, else that is skipped. Theunless
works the opposite way - The optional
depends
attribute creates a dependency on another target, such that current target will not run until that target is executed i.e. the depends-on target will be run before running current task
Expressions & functions
- A NAnt script can also have expressions like
if
condition - And call simple function call like
${datetime::now()}
- Functions can take arguments, see reference
- Functions can use properties by the name, see example below
# general syntax for functions with arguments
${prefix::func-name(arg1, ..., argN)}
# a function that uses a property and a string value
${path::combine(src.dir, 'app.sln')}
Tasks
- A
task
is the smallest unit of work in NAnt, it is just one command within a<target>
. A task can do stuffs like creating or deleting a directory, building a set of C# files or projects, send email, calling anothertarget
etc. There is big bunch of useful tasks included in NAnt by default. - Almost anything that can be run from the command line, can be executed with a
<exec>
task - Each task has an optional
failonerror
boolean attribute which says where the build should fail on failure of this task or not. The default is to fail the buildfailonerror="true"
- Many more additional tasks are available with the community driven project NAntContrib like interacting with source control, building with
MSBuild
etc. - Custom tasks can also be built with C# or VB. See the abstract base class here, and some sample code to print all the properties, including the default ones!
Running NAnt scripts
Let’s look at a very basic NAnt script, and what options we have for running that
<?xml version="1.0"?>
<project name="Hello World" default="time">
<property name="user.name" value="friend" />
<target name="hello" description="Says hello to user">
<echo message="Hello ${user.name}" />
</target>
<target name="time" description="Says current time" depends="hello">
<echo message="The current time is ${datetime::now()}" />
</target>
</project>
When a NAnt script is run with nant
command
- NAnt looks for a file with
.build
extension. If there are more than one, it takesdefault.build
. One can also specify-buildfile:<build-filename>
(or-f:
) command line argument. If it cannot find/specify the build file, it’ll exit with error. - The default
<target>
is specified asdefault
attribute at<project>
level, or it can be passed as runtime argument just by specifying the name - There are many options for running a NAnt script (in the following examples, nant is being run in the same directory where the build files are)
# single or default build file and default target
$ nant
# single or default build file with specific target
# can specify multiple targets, separated by space
$ nant hello
# with specific build file and default target, -f: also works
$ nant -buildfile:hello.build
# with specific build file and specific target
$ nant -f:hello.build hello
# passing a property value, enclose in quotes if value has spaces
$ nant -D:user.name=Arghya
The above script produces output like this
Note: If no default target is mentioned in .build file and no target specified as command-line argument, NAnt will simply exit without doing anything.
Building a .NET project
There are multiple options for building a .NET project with NAnt.
The <csc>
task can be used to compile one or more C#
files with the C# compiler. Similar tasks are available for VB
, F#
etc. When csc task is used, it’ll build the files as mentioned in <sources>
and create assemblies as specified with target
and output
attributes.
Specifying a big list of files is cumbersome and will need to be updated every time the actual code is updated. But, generally we have *.csproj
or *.sln
files that defines the whole hierarchy of files, it’s dependencies etc. So, it makes complete sense to use that directly rather than manually creating the same setup again in the .build
file.
There is a <solution>
task that can directly build a solution or one or more project files. See more details here.
The limitation of the <solution>
task is, it cannot build solutions that are created with Visual Studio versions later that 2003! So, for those projects, the easiest solution is to use the <msbuild>
task from NAntContrib. See this NAntContrib MSBuild reference.
Note: While building a newer (VS 2012+) solution file (generally a MVC web project) with NAntContrib msbuild
task, you may face this error: “[msbuild] err or MSB4019: The imported project “C:\Program Files (x86)\MSBuild\Microsoft \VisualStudio\v11.0 \WebApplications \Microsoft.WebApplication.targets” was not found. Confirm that the path in the <Import> declaration is correct, and that the file exists on disk.”
This happens for a target mismatch with MSbuild versions, as explained here and here. There are two solutions to the problem.
- To solve it for a specific project/solution, add the Web.targets
NuGet
to the project/solution. - To solve it for all builds on the machine. Simply copy the targets from a compatible MSBuild location to target MSBuild directory. For example, copy from “C:\Program Files (x86)\ MSBuild\Microsoft\ VisualStudio\v10.0\ WebApplications” to “C:\Program Files (x86)\ MSBuild\Microsoft\ VisualStudio\v11.0\ WebApplications”
Running MSBuild.exe directly with <exec>
The problem with NAntContrib <msbuild>
task is, it is not updated to work with newer solution or project formats & framework versions. The .NET framework and Visual Studio keeps updating almost every year, and so does the MSBuild. So, at some point the msbuild task will get outdated, if it isn’t already!
The solution is to directly run the MSBuild.exe
as a command-line executable with <exec>
command. This way we need not care about the .NET framework or the solution version, we just need to update the path to the exe every time we need to upgrade. See the sample target below.
<property name="build.dir" value="C:\Publish\MyApp" />
<property name="build.config" value="debug" />
<property name="pathto.solution" value="C:\CodeBase\MyApp\MyApp.Core.sln" />
<property name="pathto.msbuild" value="C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\MSBuild.exe" />
<target name="build">
<exec program="${pathto.msbuild}" verbose="true">
<arg line="${pathto.solution}" />
<arg value="/property:Configuration=${build.config}"/>
<arg value="/property:OutDir=${build.dir}"/>
</exec>
</target>
Alternatives to NAnt
For building your project to automating everyday tasks, you are not bound to NAnt. There are plenty of other tools which can do the same things. Some of the options are
- MSBuild is a close competitor, uses similar XML based build files. It is created and maintained by Microsoft, and the
.csproj
files are basically MSBuild files! But NAnt (with NAntContrib) is even older than MSBuild, and in some cases it seems to be slightly more flexible. - Rake (aka Ruby Make) is a
Ruby
based build tool, that can also be used for .NET. Some drawbacks are - needs installing Ruby and related stuffs, scripts are also written in Ruby which is not native to .NET developers. - psake is a build tool written in
PowerShell
. Good thing is .NET is natively supported and language is similar to simple command-line. Now that PowerShell is cross-platform,psake
should be cross-platform too, but I haven’t tested yet. - Cake is another good option. The plus points are -
C#
code for build script and fully cross-platform based onroslyn
. I’ll probably explore more on it and see it as a possible replacement of my current build tool in the future.
Meanwhile I’ll continue to use NAnt mostly because the infrastructure is already setup and its working fine in my current CI build system.
For a complete and runnable NAnt script that does bunch of stuffs for a CI build system, see this Sample NAnt script for CI build system. With this, you can do end-to-end build like - prepare build directories, build solution(s), run unit test & coverage, static analysis with SonarQube, package artifacts to Nexus, and deploy website to IIS.
References
- NAnt help reference
- NAntContrib
- NAnt fundamentals
- Quick start articles on - 4guysfromrolla, Code magazine, DZone
- All tasks list - NAant core, NAntContrib
- MSBuild reference - Using MSBuild, Command-Line Reference