02 November 2006

Create your custom xAnt task

Here are some notes on how to extend ant task to provide your custom ant task.

Both ant build (for Java) and nAnt build (for .net) are discussed.

1. nAnt

In nAnt for .net build you embedded c# code as script though semantically it is a derived class from Nant.Core.Task. (You don’t need to include Nant.Core assembly. It is loaded by default). A scripting block is a top level block, meaning side by side with <target> block.

In following sample script., the first static c# function is a - function - to work out number of running processes by given name.
The secnd task class is to kick off an external batch file - if you want the batch running in a separate process there isn't better way that this cumbersome one.
The third task class is to kill a process.

<script language="C#" prefix="custom">
<code>
<![CDATA[
[Function("NumberOfRunningProcess")]
public static int NumberOfRunningProcess(string name)
{
System.Diagnostics.Process[] procList = System.Diagnostics.Process.GetProcessesByName(name);
return procList.Length;
}

[TaskName("WriteRunXspBatchScript")]
public class WriteRunXspBatchScript: Task
{
private string _batchFilePath;
private string _httpRootPath;
private int _port;

[TaskAttribute("XspRunScript", Required=true)]
public string BatchFilePath
{
get{return _batchFilePath;}
set{_batchFilePath=value;}
}

[TaskAttribute("HttpRootPath", Required=true)]
public string HttpRootPath
{
get{return _httpRootPath;}
set{_httpRootPath=value;}
}

[TaskAttribute("Port", Required=true)]
public int HttpPort
{
get{return _port;}
set{_port=value;}
}

protected override void ExecuteTask()
{
string cmd = string.Format("start /MIN xsp --root \"{0}\" --port {1}", _httpRootPath, _port);
using (StreamWriter wr = new StreamWriter(_batchFilePath,false))
{
wr.WriteLine("@echo off");
wr.WriteLine(cmd);
}
}
}
[TaskName("StopXsp")]
public class StopXsp : Task{
private bool _deleteXsp;
private string _xspRunScript;

[TaskAttribute("DeleteXspRunScript", Required=false)]
public bool DeleteXspRunScript
{
get{return _deleteXsp;}
set{_deleteXsp=value;}
}

[TaskAttribute("XspRunScript", Required=false)]
public string XspRunScript
{
get{return _xspRunScript;}
set{_xspRunScript=value;}
}

protected override void ExecuteTask() {
System.Diagnostics.Process[] procList = System.Diagnostics.Process.GetProcessesByName("mono");
if (procList==null )
{
throw new Exception("No Mono process found. Expect to kill one and only one mono xsp process");
}
if (procList.Length>1)
{
throw new Exception("More than one Mono processes is running. Expect to kill one and only one mono xsp process");
}
try
{
procList[0].Kill();
}
catch(Exception){;}

if (_deleteXsp)
{
File.Delete(_xspRunScript);
}
}
}
]]>
</code>
</script>


Usage:
<echo message="process devenv ${custom::NumberOfRunningProcess('devenv')}" />
<WriteRunXspBatchScript Port="80" HttpRootPath="${webcontrolsUnitTest.src.dir}" XspRunScript="${build.dir}\${MonoXspRun}" />
<StopXsp DeleteXspRunScript="true" XspRunScript="${build.dir}\${MonoXspRun}"/>

2. ant
Reference to Extending Ant to support interactive builds, to archive this you need to write external java classes, expose package via classpath by system envronment variable or via <path> in the build script.

The only change to the sample code given in
Extending Ant to support interactive builds is instead of using classpath, it uses <path> :
<?xml version="1.0"?>
<project name="PropertyPromptExample" default="main" basedir=".">
<property name="promptTimeout" value="5"/>
<path id="extendedTask">
<fileset dir="c:\playpit\">
<include name="monkeyNuts.jar"/>
</fileset>
</path>
<taskdef name="propertyprompt" classname="com.ibm.samples.apache.tools.ant.taskdefs.optional.PropertyPrompt" classpathref="extendedTask"/>

<target name="main">
<!-- <javac srcdir="." destdir="." verbose="on"/> -->
<property name="propA" value="oldValA"/>
<property name="propA" value="oldValA1"/>
<echo>value of propA: ${propA}</echo>
<echo>value of propB: ${propB}</echo>
<propertyprompt propertyname="propA" promptcharacter=":">Enter value for propA</propertyprompt>
<propertyprompt propertyname="propB" defaultvalue="defvalB">What is the value for propB</propertyprompt>
<echo>value of propA: ${propA}</echo>
<echo>value of propB: ${propB}</echo>
</target>
</project>