October 20, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

Introduction to Custom Ant Tasks

  • September 7, 2006
  • By Rob Lybarger
  • Send Email »
  • More Articles »

Apache Ant is a great tool to manage your build tasks. It streamlines weekend hobby development, and it brings automated sanity to very complex, commercial endeavors. Ant provides well over a hundred different task handlers, and although this is usually sufficient, you may find yourself needing to step beyond Ant's current abilities: You may need to run some complex post-build verification checks that you want to hide behind a simple lone element, you may need to use arcane options beyond those the standard tasks provide, or you just may want to rot13 your source files... just because. Fortunately, it is fairly simple to create your own task handler in Java and connect it to Ant to give you that extra little feature only you need. In this article, I will walk you through the basic steps needed to create a custom task for Ant.

This article assumes a basic familiarity with Ant. I am working with Ant version 1.6.5 and Java 5 on Mac OS X; however, the examples in this article should work with any reasonably modern version of Ant and Java on any platform. I include commands to demonstrate how this works on a command line. Those of you using an IDE should include the "ant.jar" file located in the "lib" folder of your Ant installation. Those of you following along on a command line should take care to include ant.jar into your CLASSPATH system environment variable. (As an aside, an IDE makes a great tool to poke a little deeper into the details of the standard tasks, and source code for the tasks is available both online and via the "source distribute" of Ant.)

Overview

The basic procedure for creating a custom task includes:

  1. Creating a Java class that subclasses org.apache.tools.ant.Task and overrides the execute() method.
  2. Packaging your class file(s) into a jar file.
  3. In a standard Ant build.xml file, registering your custom task by using the standard taskdef task.

I will discuss the above points in further detail below.

It should be noted that your task can interact with other, nested Ant tasks such as the standard FileSet task. Your task also may look up and set project-level properties and emit log messages at any standard log level (warning, debug, and so forth). In short, your custom task exists as much as a first class citizen as any core Ant task.

Example One: Barebones Hello World

No how-to guide would be complete without including a "Hello World" example. (Before you scoff at the apparent uselessness, starting at "Hello World" gives you a known reference point from which you can build out into a new direction.) Consider that you are in the highly hypothetical situation where you need a custom Ant task to emit the phrase "Hello, World!" into the standard output of the execution of an Ant project. Yes, the standard echo task would this quite well, but see how you would accomplish this on your own. Consider this example:

<project name="demo" basedir="." default="demo">

   <target name="demo">
      <helloworld/>
   </target>

</project>

This example is highly simplified: The task has no attributes or nested elements, and it does not need to interact with the project. You will use this to get the mechanics out of the way so that you can focus in more detail later on the things that will allow your tasks to do increasingly useful things.

The choice of element name (or tag name, if you prefer) to be used in the build.xml file is somewhat arbitrary. Here, I have decided to use "helloworld" as the element name. This element name does not have to match the class name of your Java file in any way, but it is obviously advantageous to relate them. Using the javac task as an example, the core tasks in Ant seem to generally follow this pattern:

  1. The element name is in fully lower case, such as javac.
  2. The Java class file is in standard, mixed case, such as Javac.
  3. The Java class file is in a package, such as org.apache.tools.ant.taskdefs.

I will adopt this pattern for the discussion, using element name "helloworld" and a Java package and classname of org.roblybarger.ant.taskdefs.HelloWorld. The declaration and use of a package is highly encouraged. If you are a weekend coder and do not know what to use as a package name, use something like "org.yourname." or perhaps "org." followed by your initials.

Step One: Create the Java Class

The class for your custom Ant task should follow these rules:

  1. Subclass org.apache.tools.ant.Task.
  2. Override public void execute() and, if needed, public void init() method.
  3. Use the inherited log() method for any output message.

If you define a constructor, it should be a public, no-arg constructor. In fact, Ant will call your init() method (prior to calling your other methods to deal with nested elements and attributes and such) to do initialization work, so you can dispense with explicitly defined constructors entirely.

This is how it might look:

package org.roblybarger.ant.taskdefs;

import org.apache.tools.ant.Task;
import org.apache.tools.ant.Project;

public class HelloWorld extends Task {

   public void execute() {
      log("Hello, World!", Project.MSG_INFO);
   }

}
Important: We are sending our message by way of the inherited log() method. Ant connects the standard input and output streams to the appropriate places (such as the user running Ant with the "-logfile" option), so use the log method instead of using System.out.println and/or System.err.println methods.

The second argument in the log method is the priority level that the message is emitted at. This is an optional argument. If you use the log("some message") version, you get an "INFO" level message. Possible values are available in a set of constants in the Project class. These include:

  • Project.MSG_ERR
  • Project.MSG_WARN
  • Project.MSG_INFO
  • Project.MSG_VERBOSE
  • Project.MSG_DEBUG
Note: You need to pass the "-verbose" and/or "-debug" option when you run Ant to get the last two message levels to display. This allows you to include diagnostic messages in your task that you might not want to show up all the time.

I have saved my source file underneath a "src" folder, and I am compiling this file into the "classes" folder. My CLASSPATH environment variable contains ant.jar as well as the classes and src directories. (As I'm on a Mac, this looks like "/Developer/Java/Ant/lib/ant.jar:classes:src")

javac -d classes src/org/roblybarger/ant/taskdefs/HelloWorld.java

Step Two: Package into a Jar File

Strictly speaking, this step is optional, but creating a jar file for your Ant task will enhance portability and ease-of-use. Also, you have a few choices to register your task with Ant in the next step, but the easiest choice, in my opinion, involves the use of a properties file.

Create a text file with this information:

helloworld=org.roblybarger.ant.taskdefs.HelloWorld

Save this as a properties file. I will use "MyAntTasks.properties" for this example. Now, create a standard jar file which includes your class file and this properties file. On a command line, you might do this:

jar cf mytasks.jar MyAntTasks.properties -C classes .

Step Three: Register Custom Task with Ant

With this jar file available, you are nearly ready to use your custom task in your own build.xml file. The final step is to register your task name and class file so Ant knows where to pass control to at the proper time. Because you have a jar file with a properties file, this is rather simple. You started this article with a build.xml file, so you just add a taskdef entry to it:

<project name="demo" basedir="." default="demo">

   <taskdef resource="MyAntTasks.properties" classpath="mytasks.jar"/>

   <target name="demo">
      <helloworld/>
   </target>

</project>

That is it. Run your build.xml file through Ant, and you should see output similar to this:

Buildfile: build.xml

demo:
<helloworld> Hello, World!

BUILD SUCCESSFUL
Total time: 0 seconds

You probably can see the virtues of using the property file method of task registration: New tasks and classes are wired in one place and are packaged up together with the properties file. When you add more tasks to your custom toolset, you just add lines to the properties file instead of adding taskdef statements into your build.xml files, which are likely cluttered enough already.





Page 1 of 3



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel