Technology with opinion

Friday, January 16, 2009

Getting started with NAnt Scripts (Automating Builds)

It doesn't take much work to setup a project to use NAnt scripts however the inevitable question is "Where do I start".  I would recommend starting by downloading the latest release 0.86 or the daily build from NAnt's website.  Next it's good to have a basic script for your assemblies and one for your ASP.Net website that you can copy and paste.

Basic .Net Assembly Build Script:
<?xml version="1.0" encoding="utf-8" ?>
<project xmlns="http://nant.sf.net/release/0.86-beta1/nant.xsd" name="Company.Portal.Domain" default="build">
  <property name="debug" value="false" />
  <property name="optimize" value="true"/>
  <property name="bin_name" value="${project::get-name()}.dll" />
  <property name="nant.settings.currentframework" value="net-2.0"/>
  <property name="output_bin" value="build\${bin_name} "/>
  <property name="build_path" value="..\build\" />
  <property name="build_bin" value="${build_path}${bin_name}"/>
  <target name="clean" description="Cleans the build folder which forces a rebuild.">
    <delete dir="build"  if="${directory::exists('build')}" />
  </target>
  <target name="debug" description="Specifies assembly to be compiled with debug symbols.">
    <property name="debug" value="true" overwrite="true" />
    <call target="build"/>
    <copy file="build\${project::get-name()}.pdb" todir="${build_path}" overwrite="true" />
  </target>
  <target name="build" description="Compiles assembly.">
    <mkdir dir="build" if="${not directory::exists('build')}" />
    <csc target="library" output="${output_bin}" debug="${debug}" optimize="${optimize}" >
      <sources>
        <include name="**\*.cs" />
      </sources>
      <references>
        <include name="..\lib\*.dll" />
        <include name="${build_path}*.dll"/>
        <exclude name="${build_bin}"/>
      </references>
      <resources dynamicprefix="true" prefix="${project::get-name()}">
        <include name="**\*.hbm.xml" />
      </resources>
    </csc>
    <mkdir dir="${build_path}" if="${not directory::exists(build_path)}"  />
    <copy file="${output_bin}" todir="${build_path}" overwrite="true" />
    <copy file="${output_bin}" todir="..\bin" overwrite="true" />
  </target>
</project>

 Basic ASP.Net NAnt Script
<?xml version="1.0" encoding="utf-8" ?>
<project xmlns="http://nant.sf.net/release/0.86-beta1/nant.xsd" name="Company.Portal.Web" default="build">
  
  <property name="debug" value="false" />
  <property name="optimize" value="true"/>
  <property name="bin_name" value="${project::get-name()}.dll" />
  <property name="output_bin" value="build\${bin_name} "/>
  <property name="build_path" value="..\build\" />
  <property name="build_bin" value="${build_path}${bin_name}"/>
  <property name="nant.settings.currentframework" value="net-2.0"/>
  <property name="web_path" value="..\web\"/>
  <property name="web_bin_path" value="${web_path}bin"/>
  
  <target name="clean" description="Cleans the build folder which forces a rebuild.">
    <delete dir="build"  if="${directory::exists('build')}" />
    <delete dir="${web_path}"  if="${directory::exists(web_path)}" failonerror="false" />
  </target>

  <target name="debug" description="Specifies assembly to be compiled with debug symbols.">
    <property name="debug" value="true" overwrite="true" />
    <call target="build"/>
    <copy file="build\${project::get-name()}.pdb" todir="${build_path}" overwrite="true" />
  </target>

  <target name="build" description="Compiles assembly.">
    <mkdir dir="build" if="${not directory::exists('build')}" />
    <csc target="library" output="${output_bin}" debug="${debug}" optimize="${optimize}" >
      <sources>
        <include name="**\*.cs" />
        <include name="**\*.resx" />
      </sources>
      <references>
        <include name="..\lib\*.dll" />
        <include name="${build_path}*.dll"/>
        <exclude name="${build_bin}"/>
      </references> 
    </csc>
    
    <copy file="${output_bin}" todir="${build_path}" overwrite="true" />
    <copy file="${output_bin}" todir="..\bin" overwrite="true" />
    <!-- Setup Web Site -->
    <mkdir dir="${build_path}" if="${not directory::exists(build_path)}"  />
    <mkdir dir="${web_path}" if="${not directory::exists(web_path)}"  />
    <mkdir dir="${web_bin_path}" if="${not directory::exists(web_bin_path)}"  />
    <copy todir="${web_bin_path}" overwrite="true">
      <fileset basedir="${build_path}">
        <include name="*.dll"/>
      </fileset>
    </copy>
    <copy todir="${web_bin_path}" overwrite="true">
      <fileset basedir="..\lib\">
        <include name="*.dll"/>
      </fileset>
    </copy>
    <copy todir="${web_path}" overwrite="true">
      <fileset>
        <include name="**.ascx"/>
        <include name="**.aspx"/>
        <include name="**.master"/>
        <include name="**.config"/>
        <include name="Config\**"/>
        <include name="IMG\**"/>
        <include name="App_Themes\**"/>
      </fileset>
    </copy>
  </target>
</project>

Setup:
  1. Install NAnt (your bin dir will likely be C:\Program Files\nant-0.86\bin)
  2.  Add NAnt bin directory to your path environmental variable so you can use nant from command-line interface anywhere
    1. Right-click "My Computer" then click "Properties"
    2. Click the "Advanced" tab then the "Environmental Variables" button
    3. Under the "System Variables" list find the item named "Path" then click "Edit"
    4. Add to the end of the "Variable value ";C:\Program Files\nant-0.86\bin"
    5. Click OK, OK, OK
    6. Now test it by clicking "Start" -> "Run"
    7. Type "cmd" then hit enter
    8. In the command prompt type "nant" then hit enter.  If it outputs a few lines and says BUILD FAILED then your path variables are correct
  3. In your solution directory create a folder called "lib" and put your libraries (anything 3rd party or not included with the target CLR version)
  4. Create your NAnt script in the target project directory naming your NAnt script: "default.build"
  5. Navigate to this location through command prompt and run one of the following commands
    1. nant - will do a build
    2. nant clean - will clean the build
    3. nant clean build - will clean the build and rebuild (recommended)
  6. Optionally you can create a make file at the solution directory which simply executes nant for each of the projects in the solution.  I call mine "make.cmd" and put it in your solution directory (one directory above your project directories assuming all project directories are siblings)
Sample Make File (make.cmd):
nant -buildfile:Company.Domain.Common\default.build %*
nant -buildfile:Company.Portal.Web\default.build %*

Usage: "make clean build"

Now you have NAnt scripts so that you can setup continuous integration for your project using something like CruiseControl.Net .