Date: 2004-03-16T07:54:42 Editor: 193.109.238.66 <> Wiki: Ant Wiki Page: AntTutorialWritingTasks URL: http://wiki.apache.org/ant/AntTutorialWritingTasks
hint to manual; remove my name; format codings Change Log: ------------------------------------------------------------------------------ @@ -1,9 +1,9 @@ = Tutorial: Writing Tasks = -''Jan Matčrne, 2003-09-01'' - This document provides a step by step tutorial for writing tasks. +This tutorial is part of the [http://ant.apache.org/manual/tutorial-writing-tasks.html Ant-Manual] + == Set up the build environment == Ant builds itself, we are using Ant too (why we would write a task if not? :-) therefore we should use Ant for our build. @@ -14,58 +14,60 @@ So the buildfile contains three targets. -{{{ <?xml version="1.0" encoding="ISO-8859-1"?> }}} -{{{ <project name="MyTask" basedir="." default="jar"> }}} +{{{<?xml version="1.0" encoding="ISO-8859-1"?> +<project name="MyTask" basedir="." default="jar"> -{{{ <target name="clean" description="Delete all generated files"> }}} -{{{ <delete dir="classes"/> }}} -{{{ <delete file="MyTasks.jar"/> }}} -{{{ </target> }}} - -{{{ <target name="compile" description="Compiles the Task"> }}} -{{{ <javac srcdir="src" destdir="classes"/> }}} -{{{ </target> }}} - -{{{ <target name="jar" description="JARs the Task"> }}} -{{{ <jar destfile="MyTask.jar" basedir="classes"/> }}} -{{{ </target> }}} + <target name="clean" description="Delete all generated files"> + <delete dir="classes"/> + <delete file="MyTasks.jar"/> + </target> + + <target name="compile" description="Compiles the Task"> + <javac srcdir="src" destdir="classes"/> + </target> + + <target name="jar" description="JARs the Task"> + <jar destfile="MyTask.jar" basedir="classes"/> + </target> -{{{ </project> }}} +</project> +}}} This buildfile uses often the same value (src, classes, MyTask.jar), so we should rewrite that using <property>s. On second there are some handicaps: <javac> requires that the destination directory exists; a call of "clean" with a non existing classes directory will fail; "jar" requires the execution of some steps bofore. So the refactored code is: -{{{ <?xml version="1.0" encoding="ISO-8859-1"?> }}} -{{{ <project name="MyTask" basedir="." default="jar"> }}} -{{{ }}} -{{{ '''<property name="src.dir" value="src"/>''' }}} -{{{ '''<property name="classes.dir" value="classes"/>''' }}} -{{{ }}} -{{{ <target name="clean" description="Delete all generated files"> }}} -{{{ <delete dir="'''${classes.dir}'''" '''failonerror="false"'''/> }}} -{{{ <delete file="${ant.project.name}.jar"/> }}} -{{{ </target> }}} -{{{ }}} -{{{ <target name="compile" description="Compiles the Task"> }}} -{{{ '''<mkdir dir="${classes.dir}"/>''' }}} -{{{ <javac srcdir="${src.dir}" destdir="${classes.dir}"/> }}} -{{{ </target> }}} -{{{ }}} -{{{ <target name="jar" description="JARs the Task" '''depends="compile"'''> }}} -{{{ <jar destfile="${ant.project.name}.jar" basedir="${classes.dir}"/> }}} -{{{ </target> }}} -{{{ }}} -{{{ </project> }}} +{{{<?xml version="1.0" encoding="ISO-8859-1"?> +<project name="MyTask" basedir="." default="jar"> + + <property name="src.dir" value="src"/> + <property name="classes.dir" value="classes"/> + + <target name="clean" description="Delete all generated files"> + <delete dir="${classes.dir}" failonerror="false"/> + <delete file="${ant.project.name}.jar"/> + </target> + + <target name="compile" description="Compiles the Task"> + <mkdir dir="${classes.dir}"/> + <javac srcdir="${src.dir}" destdir="${classes.dir}"/> + </target> + + <target name="jar" description="JARs the Task" depends="compile"> + <jar destfile="${ant.project.name}.jar" basedir="${classes.dir}"/> + </target> + +</project>}}} ''ant.project.name'' is one of the [http://ant.apache.org/manual/using.html#built-in-props build-in properties [1]] of Ant. == Write the Task == Now we write the simplest Task - a HelloWorld-Task (what else?). Create a text file ''HelloWorld.java'' in the src-directory with: -{{{ public class HelloWorld { }}} -{{{ public void execute() { }}} -{{{ System.out.println("Hello World"); }}} -{{{ } }}} -{{{ } }}} +{{{public class HelloWorld { + public void execute() { + System.out.println("Hello World"); + } +} +}}} and we can compile and jar it with {{{ ant }}} (default target is "jar" and via its ''depends''-clause the "compile" is executed before). @@ -73,120 +75,140 @@ == Use the Task == But after creating the jar we want to use our new Task. Therefore we need a new target "use". Before we can use our new task we have to declare it with [http://ant.apache.org/manual/CoreTasks/taskdef.html <taskdef> [2]]. And for easier process we change the default clause: -{{{ <?xml version="1.0" encoding="ISO-8859-1"?> }}} -{{{ <project name="MyTask" basedir="." default="use"> }}} -{{{ }}} -{{{ ... }}} -{{{ }}} -{{{ '''<target name="use" description="Use the Task" depends="jar">''' }}} -{{{ '''<taskdef name="helloworld" classname="HelloWorld" classpath="${ant.project.name}.jar"/>''' }}} -{{{ '''<helloworld/>''' }}} -{{{ '''</target>''' }}} -{{{ }}} -{{{ </project> }}} +{{{ +<?xml version="1.0" encoding="ISO-8859-1"?> +<project name="MyTask" basedir="." default="use"> + + ... + + <target name="use" description="Use the Task" depends="jar"> + <taskdef name="helloworld" classname="HelloWorld" classpath="${ant.project.name}.jar"/> + <helloworld/> + </target> + +</project> +}}} Important is the ''classpath''-attribute. Ant searches in its /lib directory for tasks and our task isnīt there. So we have to provide the right location. Now we can type in {{{ ant }}} and all should work ... -{{{ Buildfile: build.xml }}} -{{{ }}} -{{{ compile: }}} -{{{ ["mkdir"] Created dir: C:\tmp\anttests\MyFirstTask\classes }}} -{{{ ["javac"] Compiling 1 source file to C:\tmp\anttests\MyFirstTask\classes }}} -{{{ }}} -{{{ jar: }}} -{{{ ["jar"] Building jar: C:\tmp\anttests\MyFirstTask\MyTask.jar }}} -{{{ }}} -{{{ use: }}} -{{{ ["helloworld"] Hello World }}} -{{{ }}} -{{{ BUILD SUCCESSFUL }}} -{{{ Total time: 3 seconds }}} +{{{ +Buildfile: build.xml + +compile: + [mkdir] Created dir: C:\tmp\anttests\MyFirstTask\classes + [javac] Compiling 1 source file to C:\tmp\anttests\MyFirstTask\classes + +jar: + [jar] Building jar: C:\tmp\anttests\MyFirstTask\MyTask.jar + +use: +[helloworld] Hello World + +BUILD SUCCESSFUL +Total time: 3 seconds +}}} + == Integration with TaskAdapter == Our class has nothing to do with Ant. It extends no superclass and implements no interface. How does Ant know to integrate? Via name convention: our class provides a method with signature public void execute(). This class is wrapped by Antīs {{{ org.apache.tools.ant.TaskAdapter }}} which is a task and uses reflection for setting a reference to the project and calling the ''execute()'' method. ''Setting a reference to the project?'' Could be interesting. The Project class gives us some nice abilities: access to Antīs logging facilities getting and setting properties and much more. So we try to use that class: -{{{ import org.apache.tools.ant.Project; }}} -{{{ }}} -{{{ public class HelloWorld { }}} -{{{ }}} -{{{ private Project project; }}} -{{{ }}} -{{{ public void setProject(Project proj) { }}} -{{{ project = proj; }}} -{{{ } }}} -{{{ }}} -{{{ public void execute() { }}} -{{{ String message = project.getProperty("ant.project.name"); }}} -{{{ project.log("Here is project '" + message + "'.", Project.MSG_INFO); }}} -{{{ } }}} -{{{ } }}} +{{{ +import org.apache.tools.ant.Project; + +public class HelloWorld { + + private Project project; + + public void setProject(Project proj) { + project = proj; + } + + public void execute() { + String message = project.getProperty("ant.project.name"); + project.log("Here is project '" + message + "'.", Project.MSG_INFO); + } +} +}}} and the execution with ant will show us the expected -{{{ use: }}} -{{{ Here is project 'MyTask'. }}} +{{{ +use: +Here is project 'MyTask'. +}}} == Deriving from Antīs Task == Ok, that works ... But usually you will extend {{{ org.apache.tools.ant.Task }}}. That class is integrated in Ant, getīs the project-reference, provides documentation fiels, provides easier access to the logging facility and (very useful) gives you the exact location ''where in the buildfile this task instance is used''. Oki-doki - letīs us use some of these: -{{{ import org.apache.tools.ant.Task; }}} -{{{ }}} -{{{ public class HelloWorld extends Task { }}} -{{{ public void execute() { }}} -{{{ // use of the reference to Project-instance }}} -{{{ String message = getProject().getProperty("ant.project.name"); }}} -{{{ }}} -{{{ // Taskīs log method }}} -{{{ log("Here is project '" + message + "'."); }}} -{{{ }}} -{{{ // where this task is used? }}} -{{{ log("I am used in: " + getLocation() ); }}} -{{{ } }}} -{{{ } }}} +{{{ +import org.apache.tools.ant.Task; + +public class HelloWorld extends Task { + public void execute() { + // use of the reference to Project-instance + String message = getProject().getProperty("ant.project.name"); + + // Task's log method + log("Here is project '" + message + "'."); + + // where this task is used? + log("I am used in: " + getLocation() ); + } +} +}}} which gives us when running -{{{ use: }}} -{{{ ["helloworld"] Here is project 'MyTask'. }}} -{{{ ["helloworld"] I am used in: C:\tmp\anttests\MyFirstTask\build.xml:23: }}} + +{{{ +use: +[helloworld] Here is project 'MyTask'. +[helloworld] I am used in: C:\tmp\anttests\MyFirstTask\build.xml:23: +}}} == Attributes == Now we want to specify the text of our message (it seems that we are rewriting the <echo/> task :-). First we well do that with an attribute. It is very easy - for each attribute provide a public void {{{ set<attributename>(<type> newValue) }}} method and Ant will do the rest via reflection. -{{{ import org.apache.tools.ant.Task; }}} -{{{ import org.apache.tools.ant.BuildException; }}} -{{{ }}} -{{{ public class HelloWorld extends Task { }}} -{{{ }}} -{{{ String message; }}} -{{{ public void setMessage(String msg) { }}} -{{{ message = msg; }}} -{{{ } }}} -{{{ }}} -{{{ public void execute() { }}} -{{{ if (message==null) { }}} -{{{ throw new BuildException("No message set."); }}} -{{{ } }}} -{{{ log(message); }}} -{{{ } }}} -{{{ }}} -{{{ } }}} +{{{ +import org.apache.tools.ant.Task; +import org.apache.tools.ant.BuildException; + +public class HelloWorld extends Task { + + String message; + public void setMessage(String msg) { + message = msg; + } + + public void execute() { + if (message==null) { + throw new BuildException("No message set."); + } + log(message); + } + +} +}}} Oh, whatīs that in execute()? Throw a ''BuildException''? Yes, thatīs the usual way to show Ant that something important is missed and complete build should fail. The string provided there is written as build-failes-message. Here itīs necessary because the log() method canīt handle a ''null'' value as parameter and throws a NullPointerException. (Of course you can initialize the ''message'' with a default string.) After that we have to modify our buildfile: -{{{ <target name="use" description="Use the Task" depends="jar"> }}} -{{{ <taskdef name="helloworld" }}} -{{{ classname="HelloWorld" }}} -{{{ classpath="${ant.project.name}.jar"/> }}} -{{{ <helloworld '''message="Hello World"'''/> }}} -{{{ </target> }}} +{{{ + <target name="use" description="Use the Task" depends="jar"> + <taskdef name="helloworld" + classname="HelloWorld" + classpath="${ant.project.name}.jar"/> + <helloworld message="Hello World"/> + </target> +}}} + + Thatīs all. @@ -200,14 +222,16 @@ == Nested Text == Maybe you have used the <echo> task in a way like <echo>Hello World</echo>. For that you have to provide a public void addText(String text) method. -{{{ ... }}} -{{{ public class HelloWorld extends Task { }}} -{{{ ... }}} -{{{ public void addText(String text) { }}} -{{{ message = text; }}} -{{{ } }}} -{{{ ... }}} -{{{ } }}} +{{{ +... +public class HelloWorld extends Task { + ... + public void addText(String text) { + message = text; + } + ... +} +}}} But here properties are '''not''' resolved! For resolving properties we have to use Projectīs {{{ replaceProperties(String propname) : String }}} method which takes the property name as argument and returns its value (or ${propname} if not set). @@ -218,240 +242,252 @@ * A factory method instantiates an object, saves the reference in the list and returns it to Ant Core. * The execute() method iterates over the list and evaluates its values. -{{{ import java.util.Vector; }}} -{{{ import java.util.Iterator; }}} -{{{ ... }}} -{{{ public void execute() { }}} -{{{ if (message!=null) log(message); }}} -{{{ for (Iterator it=messages.iterator(); it.hasNext(); ) { // 4 }}} -{{{ Message msg = (Message)it.next(); }}} -{{{ log(msg.getMsg()); }}} -{{{ } }}} -{{{ } }}} -{{{ }}} -{{{ }}} -{{{ Vector messages = new Vector(); // 2 }}} -{{{ }}} -{{{ public Message createMessage() { // 3 }}} -{{{ Message msg = new Message(); }}} -{{{ messages.add(msg); }}} -{{{ return msg; }}} -{{{ } }}} -{{{ }}} -{{{ public class Message { // 1 }}} -{{{ public Message() {} }}} -{{{ }}} -{{{ String msg; }}} -{{{ public void setMsg(String msg) { this.msg = msg; } }}} -{{{ public String getMsg() { return msg; } }}} -{{{ } }}} -{{{ ... }}} +{{{ +import java.util.Vector; +import java.util.Iterator; +... + public void execute() { + if (message!=null) log(message); + for (Iterator it=messages.iterator(); it.hasNext(); ) { // 4 + Message msg = (Message)it.next(); + log(msg.getMsg()); + } + } + + + Vector messages = new Vector(); // 2 + + public Message createMessage() { // 3 + Message msg = new Message(); + messages.add(msg); + return msg; + } + + public class Message { // 1 + public Message() {} + + String msg; + public void setMsg(String msg) { this.msg = msg; } + public String getMsg() { return msg; } + } +... +}}} Then we can use the new nested element. But where is xml-name for that defined? The mapping XML-name : classname is defined in the factory method: {{{ public classname createXML-name() }}}. Therefore we write in the buildfile -{{{ <helloworld> }}} -{{{ <message msg="Nested Element 1"/> }}} -{{{ <message msg="Nested Element 2"/> }}} -{{{ </helloworld> }}} +{{{ + <helloworld> + <message msg="Nested Element 1"/> + <message msg="Nested Element 2"/> + </helloworld> +}}} + == Our task in a little more complex version == For recapitulation now a little refactored buildfile: -{{{ <?xml version="1.0" encoding="ISO-8859-1"?> }}} -{{{ <project name="MyTask" basedir="." default="use"> }}} -{{{ }}} -{{{ <property name="src.dir" value="src"/> }}} -{{{ <property name="classes.dir" value="classes"/> }}} -{{{ }}} -{{{ <target name="clean" description="Delete all generated files"> }}} -{{{ <delete dir="${classes.dir}" failonerror="false"/> }}} -{{{ <delete file="${ant.project.name}.jar"/> }}} -{{{ </target> }}} -{{{ }}} -{{{ <target name="compile" description="Compiles the Task"> }}} -{{{ <mkdir dir="${classes.dir}"/> }}} -{{{ <javac srcdir="${src.dir}" destdir="${classes.dir}"/> }}} -{{{ </target> }}} -{{{ }}} -{{{ <target name="jar" description="JARs the Task" depends="compile"> }}} -{{{ <jar destfile="${ant.project.name}.jar" basedir="${classes.dir}"/> }}} -{{{ </target> }}} -{{{ }}} -{{{ }}} -{{{ <target name="use.init" }}} -{{{ description="Taskdef the HelloWorld-Task" }}} -{{{ depends="jar"> }}} -{{{ <taskdef name="helloworld" }}} -{{{ classname="HelloWorld" }}} -{{{ classpath="${ant.project.name}.jar"/> }}} -{{{ </target> }}} -{{{ }}} -{{{ }}} -{{{ <target name="use.without" }}} -{{{ description="Use without any" }}} -{{{ depends="use.init"> }}} -{{{ <helloworld/> }}} -{{{ </target> }}} -{{{ }}} -{{{ <target name="use.message" }}} -{{{ description="Use with attribute 'message'" }}} -{{{ depends="use.init"> }}} -{{{ <helloworld message="attribute-text"/> }}} -{{{ </target> }}} -{{{ }}} -{{{ <target name="use.fail" }}} -{{{ description="Use with attribute 'fail'" }}} -{{{ depends="use.init"> }}} -{{{ <helloworld fail="true"/> }}} -{{{ </target> }}} -{{{ }}} -{{{ <target name="use.nestedText" }}} -{{{ description="Use with nested text" }}} -{{{ depends="use.init"> }}} -{{{ <helloworld>nested-text</helloworld> }}} -{{{ </target> }}} -{{{ }}} -{{{ <target name="use.nestedElement" }}} -{{{ description="Use with nested 'message'" }}} -{{{ depends="use.init"> }}} -{{{ <helloworld> }}} -{{{ <message msg="Nested Element 1"/> }}} -{{{ <message msg="Nested Element 2"/> }}} -{{{ </helloworld> }}} -{{{ </target> }}} -{{{ }}} -{{{ }}} -{{{ <target name="use" }}} -{{{ description="Try all (w/out use.fail)" }}} -{{{ depends="use.without,use.message,use.nestedText,use.nestedElement" }}} -{{{ /> }}} -{{{ }}} -{{{ </project> }}} -{{{ }}} +{{{ +<?xml version="1.0" encoding="ISO-8859-1"?> +<project name="MyTask" basedir="." default="use"> + + <property name="src.dir" value="src"/> + <property name="classes.dir" value="classes"/> + + <target name="clean" description="Delete all generated files"> + <delete dir="${classes.dir}" failonerror="false"/> + <delete file="${ant.project.name}.jar"/> + </target> + + <target name="compile" description="Compiles the Task"> + <mkdir dir="${classes.dir}"/> + <javac srcdir="${src.dir}" destdir="${classes.dir}"/> + </target> + + <target name="jar" description="JARs the Task" depends="compile"> + <jar destfile="${ant.project.name}.jar" basedir="${classes.dir}"/> + </target> + + + <target name="use.init" + description="Taskdef the HelloWorld-Task" + depends="jar"> + <taskdef name="helloworld" + classname="HelloWorld" + classpath="${ant.project.name}.jar"/> + </target> + + + <target name="use.without" + description="Use without any" + depends="use.init"> + <helloworld/> + </target> + + <target name="use.message" + description="Use with attribute 'message'" + depends="use.init"> + <helloworld message="attribute-text"/> + </target> + + <target name="use.fail" + description="Use with attribute 'fail'" + depends="use.init"> + <helloworld fail="true"/> + </target> + + <target name="use.nestedText" + description="Use with nested text" + depends="use.init"> + <helloworld>nested-text</helloworld> + </target> + + <target name="use.nestedElement" + description="Use with nested 'message'" + depends="use.init"> + <helloworld> + <message msg="Nested Element 1"/> + <message msg="Nested Element 2"/> + </helloworld> + </target> + + + <target name="use" + description="Try all (w/out use.fail)" + depends="use.without,use.message,use.nestedText,use.nestedElement" + /> + +</project> +}}} + + And the code of the task: -{{{ import org.apache.tools.ant.Task; }}} -{{{ import org.apache.tools.ant.BuildException; }}} -{{{ import java.util.Vector; }}} -{{{ import java.util.Iterator; }}} -{{{ }}} -{{{ /** }}} -{{{ * The task of the tutorial. }}} -{{{ * Printīs a message or let the build fail. }}} -{{{ * @author Jan Matčrne }}} -{{{ * @since 2003-08-19 }}} -{{{ */ }}} -{{{ public class HelloWorld extends Task { }}} -{{{ }}} -{{{ }}} -{{{ /** The message to print. As attribute. */ }}} -{{{ String message; }}} -{{{ public void setMessage(String msg) { }}} -{{{ message = msg; }}} -{{{ } }}} -{{{ }}} -{{{ /** Should the build fail? Defaults to false. As attribute. */ }}} -{{{ boolean fail = false; }}} -{{{ public void setFail(boolean b) { }}} -{{{ fail = b; }}} -{{{ } }}} -{{{ }}} -{{{ /** Support for nested text. */ }}} -{{{ public void addText(String text) { }}} -{{{ message = text; }}} -{{{ } }}} -{{{ }}} -{{{ }}} -{{{ /** Do the work. */ }}} -{{{ public void execute() { }}} -{{{ // handle attribute 'fail' }}} -{{{ if (fail) throw new BuildException("Fail requested."); }}} -{{{ }}} -{{{ // handle attribute 'message' and nested text }}} -{{{ if (message!=null) log(message); }}} -{{{ }}} -{{{ // handle nested elements }}} -{{{ for (Iterator it=messages.iterator(); it.hasNext(); ) { }}} -{{{ Message msg = (Message)it.next(); }}} -{{{ log(msg.getMsg()); }}} -{{{ } }}} -{{{ } }}} -{{{ }}} -{{{ }}} -{{{ /** Store nested 'message's. */ }}} -{{{ Vector messages = new Vector(); }}} -{{{ }}} -{{{ /** Factory method for creating nested 'message's. */ }}} -{{{ public Message createMessage() { }}} -{{{ Message msg = new Message(); }}} -{{{ messages.add(msg); }}} -{{{ return msg; }}} -{{{ } }}} -{{{ }}} -{{{ /** A nested 'message'. */ }}} -{{{ public class Message { }}} -{{{ // Bean constructor }}} -{{{ public Message() {} }}} -{{{ }}} -{{{ /** Message to print. */ }}} -{{{ String msg; }}} -{{{ public void setMsg(String msg) { this.msg = msg; } }}} -{{{ public String getMsg() { return msg; } }}} -{{{ } }}} -{{{ }}} -{{{ } }}} -{{{ }}} +{{{ +import org.apache.tools.ant.Task; +import org.apache.tools.ant.BuildException; +import java.util.Vector; +import java.util.Iterator; + +/** + * The task of the tutorial. + * Print a message or let the build fail. + * @author Jan Matčrne + * @since 2003-08-19 + */ +public class HelloWorld extends Task { + + + /** The message to print. As attribute. */ + String message; + public void setMessage(String msg) { + message = msg; + } + + /** Should the build fail? Defaults to false. As attribute. */ + boolean fail = false; + public void setFail(boolean b) { + fail = b; + } + + /** Support for nested text. */ + public void addText(String text) { + message = text; + } + + + /** Do the work. */ + public void execute() { + // handle attribute 'fail' + if (fail) throw new BuildException("Fail requested."); + + // handle attribute 'message' and nested text + if (message!=null) log(message); + + // handle nested elements + for (Iterator it=messages.iterator(); it.hasNext(); ) { + Message msg = (Message)it.next(); + log(msg.getMsg()); + } + } + + + /** Store nested 'message's. */ + Vector messages = new Vector(); + + /** Factory method for creating nested 'message's. */ + public Message createMessage() { + Message msg = new Message(); + messages.add(msg); + return msg; + } + + /** A nested 'message'. */ + public class Message { + // Bean constructor + public Message() {} + + /** Message to print. */ + String msg; + public void setMsg(String msg) { this.msg = msg; } + public String getMsg() { return msg; } + } + +} +}}} + And it works: -{{{ C:\tmp\anttests\MyFirstTask>ant }}} -{{{ Buildfile: build.xml }}} -{{{ }}} -{{{ compile: }}} -{{{ ["mkdir"] Created dir: C:\tmp\anttests\MyFirstTask\classes }}} -{{{ ["javac"] Compiling 1 source file to C:\tmp\anttests\MyFirstTask\classes }}} -{{{ }}} -{{{ jar: }}} -{{{ ["jar"] Building jar: C:\tmp\anttests\MyFirstTask\MyTask.jar }}} -{{{ }}} -{{{ use.init: }}} -{{{ }}} -{{{ use.without: }}} -{{{ }}} -{{{ use.message: }}} -{{{ ["helloworld"] attribute-text }}} -{{{ }}} -{{{ use.nestedText: }}} -{{{ ["helloworld"] nested-text }}} -{{{ }}} -{{{ use.nestedElement: }}} -{{{ ["helloworld"] }}} -{{{ ["helloworld"] }}} -{{{ ["helloworld"] }}} -{{{ ["helloworld"] }}} -{{{ ["helloworld"] Nested Element 1 }}} -{{{ ["helloworld"] Nested Element 2 }}} -{{{ }}} -{{{ use: }}} -{{{ }}} -{{{ BUILD SUCCESSFUL }}} -{{{ Total time: 3 seconds }}} -{{{ C:\tmp\anttests\MyFirstTask>ant use.fail }}} -{{{ Buildfile: build.xml }}} -{{{ }}} -{{{ compile: }}} -{{{ }}} -{{{ jar: }}} -{{{ }}} -{{{ use.init: }}} -{{{ }}} -{{{ use.fail: }}} -{{{ }}} -{{{ BUILD FAILED }}} -{{{ C:\tmp\anttests\MyFirstTask\build.xml:36: Fail requested. }}} -{{{ }}} -{{{ Total time: 1 second }}} -{{{ C:\tmp\anttests\MyFirstTask> }}} -{{{ }}} +{{{ +C:\tmp\anttests\MyFirstTask>ant +Buildfile: build.xml + +compile: + [mkdir] Created dir: C:\tmp\anttests\MyFirstTask\classes + [javac] Compiling 1 source file to C:\tmp\anttests\MyFirstTask\classes + +jar: + [jar] Building jar: C:\tmp\anttests\MyFirstTask\MyTask.jar + +use.init: + +use.without: + +use.message: +[helloworld] attribute-text + +use.nestedText: +[helloworld] nested-text + +use.nestedElement: +[helloworld] +[helloworld] +[helloworld] +[helloworld] +[helloworld] Nested Element 1 +[helloworld] Nested Element 2 + +use: + +BUILD SUCCESSFUL +Total time: 3 seconds +C:\tmp\anttests\MyFirstTask>ant use.fail +Buildfile: build.xml + +compile: + +jar: + +use.init: + +use.fail: + +BUILD FAILED +C:\tmp\anttests\MyFirstTask\build.xml:36: Fail requested. + +Total time: 1 second +C:\tmp\anttests\MyFirstTask> +}}} + Next step: test ... == Test the Task == @@ -461,137 +497,143 @@ For executing the test and creating a report we need the optional tasks <junit> and <junitreport>. So we add to the buildfile: -{{{ ... }}} -{{{ <project name="MyTask" basedir="." '''default="test"'''> }}} -{{{ ... }}} -{{{ '''<property name="ant.test.lib" value="ant-testutil.jar"/>''' }}} -{{{ <property name="report.dir" value="report"/> }}} -{{{ <property name="junit.out.dir.xml" value="${report.dir}/junit/xml"/> }}} -{{{ <property name="junit.out.dir.html" value="${report.dir}/junit/html"/> }}} -{{{ }}} -{{{ <path id="classpath.run"> }}} -{{{ <path path="${java.class.path}"/> }}} -{{{ <path location="${ant.project.name}.jar"/> }}} -{{{ </path> }}} -{{{ }}} -{{{ <path id="classpath.test"> }}} -{{{ <path refid="classpath.run"/> }}} -{{{ <path location="${ant.test.lib}"/> }}} -{{{ </path> }}} -{{{ }}} -{{{ <target name="clean" description="Delete all generated files"> }}} -{{{ <delete failonerror="false" includeEmptyDirs="true"> }}} -{{{ <fileset dir="." includes="${ant.project.name}.jar"/> }}} -{{{ <fileset dir="${classes.dir}"/> }}} -{{{ <fileset dir="${report.dir}"/> }}} -{{{ </delete> }}} -{{{ </target> }}} -{{{ }}} -{{{ <target name="compile" description="Compiles the Task"> }}} -{{{ <mkdir dir="${classes.dir}"/> }}} -{{{ <javac srcdir="${src.dir}" destdir="${classes.dir}" '''classpath="${ant.test.lib}"'''/> }}} -{{{ </target> }}} -{{{ ... }}} -{{{ <target name="junit" description="Runs the unit tests" depends="jar"> }}} -{{{ <delete dir="${junit.out.dir.xml}" /> }}} -{{{ <mkdir dir="${junit.out.dir.xml}" /> }}} -{{{ <junit printsummary="yes" haltonfailure="no"> }}} -{{{ <classpath refid="classpath.test"/> }}} -{{{ <formatter type="xml"/> }}} -{{{ <batchtest fork="yes" todir="${junit.out.dir.xml}"> }}} -{{{ <fileset dir="${src.dir}" includes="**/*Test.java"/> }}} -{{{ </batchtest> }}} -{{{ </junit> }}} -{{{ </target> }}} -{{{ }}} -{{{ <target name="junitreport" description="Create a report for the rest result"> }}} -{{{ <mkdir dir="${junit.out.dir.html}" /> }}} -{{{ <junitreport todir="${junit.out.dir.html}"> }}} -{{{ <fileset dir="${junit.out.dir.xml}"> }}} -{{{ <include name="*.xml"/> }}} -{{{ </fileset> }}} -{{{ <report format="frames" todir="${junit.out.dir.html}"/> }}} -{{{ </junitreport> }}} -{{{ </target> }}} -{{{ }}} -{{{ <target name="test" }}} -{{{ depends="junit,junitreport" }}} -{{{ description="Runs unit tests and creates a report" }}} -{{{ /> }}} -{{{ ... }}} -{{{ }}} +{{{ +... +<project name="MyTask" basedir="." default="test"> +... + <property name="ant.test.lib" value="ant-testutil.jar"/> + <property name="report.dir" value="report"/> + <property name="junit.out.dir.xml" value="${report.dir}/junit/xml"/> + <property name="junit.out.dir.html" value="${report.dir}/junit/html"/> + + <path id="classpath.run"> + <path path="${java.class.path}"/> + <path location="${ant.project.name}.jar"/> + </path> + + <path id="classpath.test"> + <path refid="classpath.run"/> + <path location="${ant.test.lib}"/> + </path> + + <target name="clean" description="Delete all generated files"> + <delete failonerror="false" includeEmptyDirs="true"> + <fileset dir="." includes="${ant.project.name}.jar"/> + <fileset dir="${classes.dir}"/> + <fileset dir="${report.dir}"/> + </delete> + </target> + + <target name="compile" description="Compiles the Task"> + <mkdir dir="${classes.dir}"/> + <javac srcdir="${src.dir}" destdir="${classes.dir}" classpath="${ant.test.lib}"/> + </target> +... + <target name="junit" description="Runs the unit tests" depends="jar"> + <delete dir="${junit.out.dir.xml}" /> + <mkdir dir="${junit.out.dir.xml}" /> + <junit printsummary="yes" haltonfailure="no"> + <classpath refid="classpath.test"/> + <formatter type="xml"/> + <batchtest fork="yes" todir="${junit.out.dir.xml}"> + <fileset dir="${src.dir}" includes="**/*Test.java"/> + </batchtest> + </junit> + </target> + + <target name="junitreport" description="Create a report for the rest result"> + <mkdir dir="${junit.out.dir.html}" /> + <junitreport todir="${junit.out.dir.html}"> + <fileset dir="${junit.out.dir.xml}"> + <include name="*.xml"/> + </fileset> + <report format="frames" todir="${junit.out.dir.html}"/> + </junitreport> + </target> + + <target name="test" + depends="junit,junitreport" + description="Runs unit tests and creates a report" + /> +... +}}} + Back to the ''src/HelloWorldTest.java''. We create a class extending ''BuildFileTest'' with String-constructor (JUnit-standard), a ''setUp()'' method initializing Ant and for each testcase (targets use.*) a ''testXX()'' method invoking that target. -{{{ import org.apache.tools.ant.BuildFileTest; }}} -{{{ }}} -{{{ public class HelloWorldTest extends BuildFileTest { }}} -{{{ }}} -{{{ public HelloWorldTest(String s) { }}} -{{{ super(s); }}} -{{{ } }}} -{{{ }}} -{{{ public void setUp() { }}} -{{{ // initialize Ant }}} -{{{ configureProject("build.xml"); }}} -{{{ } }}} -{{{ }}} -{{{ public void testWithout() { }}} -{{{ executeTarget("use.without"); }}} -{{{ assertEquals("Message was logged but should not.", getLog(), ''''''); }}} -{{{ } }}} -{{{ }}} -{{{ public void testMessage() { }}} -{{{ // execute target 'use.nestedText' and expect a message }}} -{{{ // 'attribute-text' in the log }}} -{{{ expectLog("use.message", "attribute-text"); }}} -{{{ } }}} -{{{ }}} -{{{ public void testFail() { }}} -{{{ // execute target 'use.fail' and expect a BuildException }}} -{{{ // with text 'Fail requested.' }}} -{{{ expectBuildException("use.fail", "Fail requested."); }}} -{{{ } }}} -{{{ }}} -{{{ public void testNestedText() { }}} -{{{ expectLog("use.nestedText", "nested-text"); }}} -{{{ } }}} -{{{ }}} -{{{ public void testNestedElement() { }}} -{{{ executeTarget("use.nestedElement"); }}} -{{{ assertLogContaining("Nested Element 1"); }}} -{{{ assertLogContaining("Nested Element 2"); }}} -{{{ } }}} -{{{ } }}} -{{{ }}} +{{{ +import org.apache.tools.ant.BuildFileTest; + +public class HelloWorldTest extends BuildFileTest { + + public HelloWorldTest(String s) { + super(s); + } + + public void setUp() { + // initialize Ant + configureProject("build.xml"); + } + + public void testWithout() { + executeTarget("use.without"); + assertEquals("Message was logged but should not.", getLog(), ""); + } + + public void testMessage() { + // execute target 'use.nestedText' and expect a message + // 'attribute-text' in the log + expectLog("use.message", "attribute-text"); + } + + public void testFail() { + // execute target 'use.fail' and expect a BuildException + // with text 'Fail requested.' + expectBuildException("use.fail", "Fail requested."); + } + + public void testNestedText() { + expectLog("use.nestedText", "nested-text"); + } + + public void testNestedElement() { + executeTarget("use.nestedElement"); + assertLogContaining("Nested Element 1"); + assertLogContaining("Nested Element 2"); + } +} +}}} + When starting {{{ ant }}} weīll get a short message to STDOUT and a nice HTML-report. -{{{ C:\tmp\anttests\MyFirstTask>ant }}} -{{{ Buildfile: build.xml }}} -{{{ }}} -{{{ compile: }}} -{{{ ["mkdir"] Created dir: C:\tmp\anttests\MyFirstTask\classes }}} -{{{ ["javac"] Compiling 2 source files to C:\tmp\anttests\MyFirstTask\classes }}} -{{{ }}} -{{{ jar: }}} -{{{ ["jar"] Building jar: C:\tmp\anttests\MyFirstTask\MyTask.jar }}} -{{{ }}} -{{{ junit: }}} -{{{ ["mkdir"] Created dir: C:\tmp\anttests\MyFirstTask\report\junit\xml }}} -{{{ ["junit"] Running HelloWorldTest }}} -{{{ ["junit"] Tests run: 5, Failures: 0, Errors: 0, Time elapsed: 2,334 sec }}} -{{{ }}} -{{{ }}} -{{{ }}} -{{{ junitreport: }}} -{{{ ["mkdir"] Created dir: C:\tmp\anttests\MyFirstTask\report\junit\html }}} -{{{ ["junitreport"] Using Xalan version: Xalan Java 2.4.1 }}} -{{{ ["junitreport"] Transform time: 661ms }}} -{{{ }}} -{{{ test: }}} -{{{ }}} -{{{ BUILD SUCCESSFUL }}} -{{{ Total time: 7 seconds }}} -{{{ C:\tmp\anttests\MyFirstTask> }}} +{{{ +:\tmp\anttests\MyFirstTask>ant +Buildfile: build.xml + +compile: + [mkdir] Created dir: C:\tmp\anttests\MyFirstTask\classes + [javac] Compiling 2 source files to C:\tmp\anttests\MyFirstTask\classes + +jar: + [jar] Building jar: C:\tmp\anttests\MyFirstTask\MyTask.jar + +junit: + [mkdir] Created dir: C:\tmp\anttests\MyFirstTask\report\junit\xml + [junit] Running HelloWorldTest + [junit] Tests run: 5, Failures: 0, Errors: 0, Time elapsed: 2,334 sec + + + +junitreport: + [mkdir] Created dir: C:\tmp\anttests\MyFirstTask\report\junit\html +[junitreport] Using Xalan version: Xalan Java 2.4.1 +[junitreport] Transform time: 661ms + +test: + +BUILD SUCCESSFUL +Total time: 7 seconds +C:\tmp\anttests\MyFirstTask> +}}} == Resources == This tutorial and its resources are available via [http://nagoya.apache.org/bugzilla/show_bug.cgi?id=22570 BugZilla [6]]. The ZIP provided there contains @@ -604,11 +646,13 @@ * generated jar * generated reports -Used Links: -{{{ [1] http://ant.apache.org/manual/using.html#built-in-props }}} -{{{ [2] http://ant.apache.org/manual/CoreTasks/taskdef.html }}} -{{{ [3] http://ant.apache.org/manual/develop.html#set-magic }}} -{{{ [4] http://ant.apache.org/manual/develop.html#nested-elements }}} -{{{ [5] http://gump.covalent.net/jars/latest/ant/ant-testutil.jar }}} -{{{ [6] http://nagoya.apache.org/bugzilla/show_bug.cgi?id=22570 }}} +{{{Used Links: + +[1] http://ant.apache.org/manual/using.html#built-in-props +[2] http://ant.apache.org/manual/CoreTasks/taskdef.html +[3] http://ant.apache.org/manual/develop.html#set-magic +[4] http://ant.apache.org/manual/develop.html#nested-elements +[5] http://gump.covalent.net/jars/latest/ant/ant-testutil.jar +[6] http://nagoya.apache.org/bugzilla/show_bug.cgi?id=22570 +}}} --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]