Auto build for .NET using NANT

This is hands on example of using nant to build a .net solution, including the tricky deployment project type, auto versioning of assembly files and deployment project file.

1. Install NANT
Download NANT, and unzip it, copy the folder to Program Files

Add nant bin path to system path: My computer, Properties, Advanced System Settings, Environment Variables, System Variables, Path, Edit, append ‘;C:\Program Files\nant-0.91\bin’

2. Create build file under the project folder in the names like Yourprojectname.build
In this way, nant will find *.build file automatically. All you need to do when doing a build is in cmd mode, cd to your project folder, then enter nant, all will be done

3. The build file is a xml file

<?xml version="1.0" encoding="utf-8" ?> 
<project name="yourprojectname" default="build">
…
</ project >

The root is project node, next level nodes are called , here the default attribute is called build which means if you have not told nant which target to run, then nant will run build target as default.

4. Build target

<target name="build" depends="deploymentbuild">
…
</target>

This defines build target, where depends attribute means deploymentbuild needs to be run and successful before build target is run
Through this you can split a build into many previous steps like running scripts, creating folders, running tests …

5. A few basic nant commands in build file

6. Define variables, they are called properties, placed just under project

<property name="version.major" value="1" />
<property name="version.minor" value="0" />
<property name="version.build" value="0" />
<property name="version.revision" value="2" />

<property name="project.root" value="D:\yourprojectfolder" />
<property name="build.root" value="${project.root}\Releases" />

To use the variable

<property name="project.fullversion" value="${version.major}.${version.minor}.${version.build}.${version.revision}" dynamic="true" />

7. To build a solution of Visual Studio

<target name="buildcompile" depends="version">
    <echo message="#### TARGET - build compile ####"/>
	<solution configuration="debug" 
		solutionfile="yourprojectsolution.sln" />
   </target>

Build it in release mode

<target name="releasebuild" depends="buildcompile">
	<echo message="#### TARGET - release ####"/>
         <solution configuration="release" 
	solutionfile="yourprojectsolution.sln" />
   </target>

8. To build a deployment project

If your solution has deployment project, above build does not generate setup.exe for your deployment project, it does not build vdproj, you will need to run this

<!-- must build the dependency first (code project), before building the MSI deployment project -->
<exec program="${ide.dir}\Devenv.exe" commandline='"${solution}" /rebuild "Release" /project "${MSIprojectfile}" /out "${project.root}\release.log"'/>

This is also example of how to run an exe with arguments.

Here are my variables settings

<property name="ide.dir" value="C:\Program Files\Microsoft Visual Studio 10.0\Common7\IDE" />
<property name="solution" value="yoursolutionfile.sln" />
<property name=" MSIprojectfile " value="${project.root}\ MSIproject \ MSIprojectfile.vdproj" />

9. Delete a folder

<delete dir="${build.dir}" failonerror="false" />

10. Create a folder

<mkdir dir="${build.dir}" />

11. Copy files

<copy file="${project.root}\Releases\xxxx\bin\setup.exe" tofile="${build.dir}\setup.exe" />
<copy todir="${build.dir}\Resources">
<fileset basedir="${project.root}\Releases\xxxx\Resources">
 <include name="*.*" />
</fileset>
</copy>

12. Auto versioning
Have you seen buildcompile depending on version, where is it and what it is for? We all want that this auto build toll can do auto versioning as the solution might have a dozen projects, it is pretty much a nuisance to go there to change versions manually.
While there is no built versioning command in nant, I did one myself, here is it. There might be other versions on internet at the time of my research, but most of them did not work.

<target name="version">
		<script language="C#">
			<references>
					<include name="System.dll"/>
			</references>
			<imports>
				<import namespace="System.Text.RegularExpressions"/>
			</imports>
          <code>
		  <![CDATA[
		  	
		         
				public static void ScriptMain(Project project)
                {
					
					NAnt.Core.Tasks.EchoTask echo = new NAnt.Core.Tasks.EchoTask();
	                echo.Project = project;

				
					UpdateAssemblyVersion(project.Properties["projectassemblyfile"], project.Properties["project.fullversion"],echo);
					UpdateProductVersion(project.Properties["msiprojectfile"], project.Properties["product.fullversion"]);
				
  }

public static void UpdateAssemblyVersion(string filename,string version, NAnt.Core.Tasks.EchoTask echo)
				{

echo.Message = String.Format("Updating Assembly file: {0} and project new version:{1}", filename,version);
     echo.Execute();
					
StreamReader reader = new StreamReader(filename);
string contents = reader.ReadToEnd();
  reader.Close();

echo.Message = String.Format("contenst: {0}", contents);
 echo.Execute();
        
					
 Regex expressionForAssemblyVersion = new Regex(@"(\[assembly:\s+AssemblyVersion\("")(?:[\d\.]+)(""\)\])");
				
contents = expressionForAssemblyVersion.Replace(contents, "${1}" + version+"${2}");
          
Regex expressionAssemblyFileVersionAttribute = new Regex(@"(\[assembly:\s+AssemblyFileVersionAttribute\("")(?:[\d\.]+)(""\)\])");
            
contents = expressionAssemblyFileVersionAttribute.Replace(contents, "${1}" + version+"${2}");

 StreamWriter writer = new StreamWriter(filename, false);
  writer.Write(contents);
   writer.Close();

echo.Message = String.Format("next text: {0}", contents);
  echo.Execute();

}

public static void UpdateProductVersion(string setupFileName,string productVersion)
{
	string setupFileContents;

	//read in the .vdproj file          
	using (StreamReader sr = new StreamReader(setupFileName))
	{
		setupFileContents = sr.ReadToEnd();
	}

	if (!string.IsNullOrEmpty(setupFileContents))
	{
 	Regex expression1 = new Regex(@"(""ARPCOMMENTS"" = ""8:.*Build )(?:[\d\.]+)");
						
	Regex expression2 = new Regex(@"(""ProductCode"" = ""8:{)(?:[\d\w-]+)");
	Regex expression3 = new Regex(@"(""PackageCode"" = ""8:{)(?:[\d\w-]+)");
	Regex expression4 = new Regex(@"(""ProductVersion"" = ""8:)(?:[\d\.]+)");
						
	setupFileContents = expression1.Replace(setupFileContents, "${1}"+productVersion);
						
	setupFileContents = expression2.Replace(setupFileContents,"${1}"+Guid.NewGuid().ToString().ToUpper());
	setupFileContents = expression3.Replace(setupFileContents,"${1}"+Guid.NewGuid().ToString().ToUpper());
	setupFileContents = expression4.Replace(setupFileContents,"${1}"+productVersion);

	using (TextWriter tw = new StreamWriter(setupFileName, false))
	{
		tw.WriteLine(setupFileContents);
	}
	}
	}


	]]>
        </code>
 
	</script>

   </target>
   

There are two functions I developed UpdateAssemblyVersion and UpdateProductVersion, the first one is for normal assembly.cs, the other function is for vdproj file

<property name=" projectassemblyfile " value="${project.root}\xxxxx\AssemblyInfo.cs" />
<property name=" msiprojectfile " value="${project.root}\xxxxx\yyyyyy. vdproj " />

I am sure you have found other useful commands, you can share them here if you want, the purpose of this article is to provide you an example that can get you going right now rather than a complete tutorial.

Have fun

This entry was posted on Tuesday, February 14th, 2012 at 11:12 pm and is filed under ASP.NET, Software Engineering. You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.

Leave a Reply

*