Deploy, test and pack your code - Part 1 Using NAnt

In this part, I show a way to build and deploy your project using NAnt. As, you already might know that NAnt is a tiny deployment tool that enables you to create a deployment package with some  *.build files which contains nothing but few XML directives.

Recently, I was trying to make a deployment package for my Athena project. I generally download the latest source from server , though I have the latest source in my machine. This ensures that I have checked everything in nicely. Now, I have created a tiny build.bat file that builds the code, tests it using NUnit mocked with Typemock and makes a zip distribution of the required binaries.

Now, to make things work we first need to find dependencies, move them first to the destination folder from source , then do the build. By default build in NAnt is in release mode unless otherwise you mention anything.

<target name="build" depends="clean">
<mkdir dir="bin" />
<csc target="library" output="bin/Linq.Flickr.dll" >
    <sources>
        <include name="Linq.Flickr\Attribute\XAttributeAttribute.cs" />
        <include name="Linq.Flickr\Attribute\XElementAttribute.cs" />
        <include name="Linq.Flickr\Attribute\FlickrMethodAttribute.cs" />
          ...
          ...
        <include name="Linq.Flickr\PopularTagQuery.cs" />
    </sources>
    <references basedir="${project::get-base-directory()}\External">
        <include name="LinqExtender.dll"/>
    </references>
</csc>

As the lines says, it creates the bin folder, takes the reference from External directory , there is also a depends node which means it will fire off the taget.name = "clean" first before executing the build target. Every .build file has a root project node under which other goes.

<project name="linqFlickr" default="build" >
...
...
</project>

Here , it shows the default target is build, if not mentioned , we have to pass in the taget name to start. In build package we also need the required dlls that user need to include.

<copy todir="${project::get-base-directory()}\bin" overwrite="true">
    <fileset basedir="${project::get-base-directory()}\External">
        <include name="LinqExtender.dll"/>
    </fileset>
</copy>

Copy can have multiple file sets or it can copy a single file. Here, incase of including single (ex. readme.txt) file the following can be done

<copy todir="${project::get-base-directory()}\bin" 
file="${project::get-base-directory()}\readme.txt" overwrite="true" />

There are some static calls that you can make , that should wrap in ${ ... }. In the above script block, project:: get-base-directory() will fill the gap with current path where the script is located.Once we have built, we also need to test things out. In that case, we might have a test project that we need to build and copy the necessary dlls to the deploy folder as well. Additionally, if our test project (In my case, Linq.Flickr.Test.dll) depends a config file, then we have to copy it in a form of SomethingProject.dll.config to the deployment folder

<copy file="${project::get-base-directory()}\bin\app.config" 
tofile="${project::get-base-directory()}\bin\Linq.Flickr.Test.dll.config" />
<delete file="bin/app.config" if="${file::exists('bin/app.config')}" />

So, first you will copy the file as usual from external directory , then you will rename, NAnt will be copying to a different named file and finally delete the main file.Now, as you have two scripts ready, one for building the library and another for testing it, it is easy to combine them under one script using buildFiles directive.

<target name="run">
<nant target="build">
<buildfiles>
    <include name="LinqFlickr.build" />
    <include name="LinqFlickrTest.build" />
</buildfiles>
</nant>
</target>

This is like running tiny NAnt scripts under .build file, here I have also specified the target type, which means the target to fire up under each sub-scripts. Now, NAnt has build in NUnit task which can be done using nunit2  directive

<nunit2>
    <formatter type="Plain" />
    <test assemblyname="bin\Linq.Flickr.Test.dll" appconfig="Linq.Flickr.Test.dll.config" />
</nunit2>

But, this is not a good approach at all, if you want to use the latest version and commends. The option suitable is <exec>. Now, running tests with Typemock has two ways, one is to use the NAnt task that it supports, with the release of Typemock 5, open source project can also use it with its auto deploy option, previously it was only for commercial editions.

Steps for using Typemock with auto deploy are

  • Load the Task dll
  • start mock engine.
  • register it , usually by the open source username and license you got
  • execute it.
  • stop the mock engine.
<loadtasks assembly="${typemock.dir}\TypeMock.NAntBuild.dll" />
<typemockstart target="2.0"/>
<typemockregister company ="Typemock" license="ABC" autodeploy="true"/>
<exec program="${nunit}" failonerror="false" verbose="true" >
        <arg value="bin\Linq.Flickr.Test.dll"  />
</exec>
<typemockstop/>

Here, program="${nunit}" will extract the value from the property node under project.

<property name="nunit" value="ThirdParty\Nunit\nunit-console.exe" />

Using the current community  distribution of Typemock, there is another way to make it happen, but that will require Typemock  installed in target machine and  which is possible by the TMockRunner.exe

<exec program="${typemock}" failonerror="false" verbose="true" >
    <arg value="${nunit}" />
    <arg value="bin\Linq.Flickr.Test.dll" />
</exec>

Here, program="${typemock}" maps to

<property name="typemock" value="C:\Program Files\Typemock\Isolator\4.3\TMockRunner.exe" />

Finally, if we want to pack everything up in a zip file. we can do it in the following way.

<zip zipfile="${deployZipFileLocation}" includeemptydirs="false">  
    <fileset basedir="${targetDirectory}">  
         <include name="Linq.Flickr.dll*" />  
        <include name="LinqExtender.dll*" />
        <include name="readme.txt*" />
    </fileset>  
 </zip>              

All we need now is a batch file with NAnt command that will call the entry point script. In the end, this is just a simple one about how can we automate testing and deployment , but more complex task like sending email while a build/ test fails , incorporate workflow all can be done and with build script the possibilities are endless. NAnt works with .net compact framework and mono distribution as well. It requires no installation.

image

You need to distribute these files with your source distribution, to get user cook a binary out of the latest code (esp for open source) just by a single click. In the next part of this post, I will do the whole thing with MSBuild and show the benefits and integration factor comparing with NAnt.

You can download the skeleton script that I have supplied with LINQToFlickr project here to have a look or get a running copy from the release page.

kick it on DotNetKicks.com

No Comments