Broloco

NAnt vs MSBuild

I like to use NAnt for building my .Net projects.

I am not a huge fan of the IDE (Visual Studio), but I am well aware that I am in the minority here.

So if I plan to use NAnt on a project, I've got to do it in such a way that the other developers can still use the features of the IDE, mainly intelli-sense and the Error List window.

I've been using a very small .csproj file (described below) which calls out to NAnt, and a small custom MSBuild task to log errors so that the IDE will display them correctly.

Adding the source-code

Starting with a blank project file, you can add all the C# files with a single MSBuild entry like this:

<Compile Include="**\*.cs" />
        

And that's it! Now you can browse all your C# code using the solution explorer. The intelli-sense will recognise your classes and start auto-expanding as you type.

I typically add nodes for each file-type I want to be able to edit: .cs, .build, .xml, .config, etc.

Intelli-Sense

The IDE will use intelli-sense for the classes defined in your C# code files. You also want it to pick up external references. Add a line for each reference you need intelli-sense for in another ItemGroup:

<Reference Include="NHibernate" />
        

The IDE will look for the assemblies under the path defined in the <OutputPath> node near the top of your project file. If you want, you can add a <HintPath> sub-element:

<Reference Include="NHibernate">
    <HintPath>SDKs\NHibernate\NHibernate.dll</HintPath>
</Reference>
        

Now you'll have intelli-sense for your referenced assemblies too.

Running NAnt

When Visual Studio builds it runs the 'DefaultTargets' for the project (usually an MS-defined target named 'Build'). When Visual Studio cleans it runs a target named 'Clean', and when it re-builds it runs the target named 'Rebuild'.

You can change the 'DefaultTargets' for the project at the top of the project file:

<Project DefaultTargets="NAntBuild" ...
        

Then add a target that uses the Exec task to call NAnt:

<Target Name="NAntBuild">
  <Exec Command="SDKs\nant-0.85\bin\NAnt.exe" />
</Target>
        

Now the solution will build using NAnt. Add targets for 'Clean' and 'Rebuild' too if you want to do these via NAnt.

Note, when you customise your targets the IDE will give you a security warning. Since you customised it yourself, you can safely ignore this warning.

Error List

So far this works, but build errors are only found by examining the output window. It would be nice to have errors reported in the 'Error List' window so that you can just double-click them.

We wrote a custom MS-Build task called ExecParse, which inherits from 'Exec', to allow parsing of of the output and logging errors to the MS-Build engine.

The configuration for ExecParse takes a regular expression to search the output for, and allows logging of errors using text from captures in the regular expression.

A typical compiler error looks like:

[csc] c:\...\MyFile.cs(24,60): error CS0246: ...

So we can create a regular expression that captures this, and log an error using the MSBuild engine:

<UsingTask AssemblyFile="...\ExecParse.dll"
           TaskName="ExecParse.ExecParse" />
...
<PropertyGroup>
  <ExecParseConfiguration>
    <Error>
      <Search>\[csc\] (.*?)\((\d+),(\d+)\): error …
        ([^:]*): (.*?)[\n\r]</Search>
      <File>$1</File>
      <LineNumber>$2</LineNumber>
      <ColumnNumber>$3</ColumnNumber>
      <Subcategory>$4</Subcategory>
      <Message>$5</Message>
    </Error>
  </ExecParseConfiguration>
</PropertyGroup>
...
<Target Name="NAntBuild">
  <ExecParse Command="SDKs\nant-0.85\bin\NAnt.exe"
           Configuration="$(ExecParseConfiguration)" />
</Target>
        

Now you have a NAnt build, building within the IDE, using intelli-sense, and logging errors to the 'Error List' window.


Some links:

Submit this story to DotNetKicks Shout it