My Blahg

April 21, 2008

NAnt build scripts for C#: Debug or Release configurations

Filed under: Continuous Integration/CruiseControl.NET, NAnt, c#, dotnet — treyhutcheson @ 1:19 pm

When one begins to delve into the use of NAnt for automating C# builds, one must choose which NAnt task to use to perform the compile.

One option is the csc task, which invokes the c# compiler via the command line. Another option is the solution task, which uses Visual Studio/devenv to compile a solution.

The solution task at first glance appears to be the more useful; after all, it respects all project properties such as optimization flags and compilation symbols. However, it has two critical flaws.

The first flaw becomes an issue in multi-user development environments; the settings of the solution must be synchronized in the repository across each development workstation/user. On the surface, this issue doesn’t seem to be very large. However, in practice, its all too common for users to check in changes to the solution that eventually cause the builds to fail. I want my builds to fail because of compile errors or failed unit tests; not because some one accidentally changed an assembly reference.

The other issue is that the solution task requires visual studio to be present(and properly configured) on the build environment. Although the size of my company’s software budget does not directly effect my salary, I see no reason to purchase a separate license just so I can automate my builds.

It is chiefly because of these two issues(and other, much more minor issues) that I choose to use the csc task. The main tradeoff is the fact that I must explicitly script the inclusion of source code, resources, and project references. That’s a tradeoff I eagerly welcome. However, there remains another tradeoff: project configurations.

Suppose you have a simple target defined as below:

<target name="build">
  <csc target="library" output="assembly.dll">
     ...
     ...
  </csc>
</target>

How does one go about supporting separate configurations, or defining conditional compilation symbols or to perform optimization?

I’ve settled on a technique that has served me well for a couple of years now. At the root of my nant script, I define a property named build.release, like so:

<property name="build.release" value="false" unless="${property::exists('build.release')}" />

This property is present to indicate that a release build should be performed. The property defaults to the value “false”, unless it has already been defined(such as in a calling script, or via the command line).

I then use this property within the call to the csc task:

<csc target="library" output="assembly.dll" debug="${not build.release}"
  optimize="${build.release}">
  ...
  ...
</csc>

The use of this property allows me to control both the optimize argument and the debug argument. If debug.release is true, then the assembly will be optimized, and the debug argument will be set to false. If debug.release is instead false, then the assembly will not be optimized, and the debug argument will be set to true. In the latter case, according to the NAnt docs, both the DEBUG and TRACE symbols will be defined.

If your project requires alternate compile symbols, one can always use a similar method to conditionally define them and pass them to the csc task(via it’s define argument).

Unfortunately, there’s one glaring flaw; fortunately, this flaw has not affected me yet in my 2+ years of this approach. The flaw is the fact that only two configurations are supported. It’s either a debug build, or a release build.

Great – so you now have one set of nant code that uses the csc task that can emit either debug or release builds. How does one take advantage?

One method is to change the initialized value of the debug.release property, either directly in the script or its calling script. Another method is to pass the value via the command line, via the line:
NAnt [buildfile] -D:debug.release=true

But what happens if you have the build triggered by a continuous integration environment, like CruiseControl.NET? That’s the topic of another post altogether.

2 Comments »

  1. [...] NAnt, c#, dotnet — treyhutcheson @ 3:28 pm I recently created a post regarding handling debug or release configurations within nant scripts. This post will extend the [...]

    Pingback by CruiseControl.NET and multiple build configurations: Part 1 - Introduction « My Blahg — April 21, 2008 @ 3:28 pm

  2. So when using CSC task how do u version your assembly.dll in the output.
    Each time CSC will set the assembly version to 1.0.0.0

    Comment by Barry — November 25, 2008 @ 5:26 am


RSS feed for comments on this post. TrackBack URI

Leave a comment

Blog at WordPress.com.