xmlanttaskdef

Writing my first Ant task: what am I missing?


I have read this article and used it to write my first Ant task called AutoTestTask:

public class AutoTestTask extends org.apache.tools.ant.Task {
    // ...
}

I have also compiled and packaged this object into auto-test.jar.

If my understanding of Ant is correct, then to include it as part of another project's build, I use the following XML:

<project name="SomeProject" basedir="." default="deploy" xmlns:at="antlib:org.me.auto-test">
    <!-- Task definitions. -->
    <taskdef name="at-autotest" classname="org.me.auto-test.AutoTestTask"/>

    <!-- Use the task. -->
    <at:autotest/>
</project>

I believe I also need to copy auto-test.jar into my ${ANT_HOME}/lib directory - can someone please confirm? What's confusing me is the xmlns:at="antlib:org.me.auto-test" attribute at the top of the XML, and specifically, the antlib:<whatever> portion. Is this some kind of Ant-specific protocol that says "*anything qualified by the at namespace will be found inside ${ANT_HOME}/lib with a root package of org.me.auto-test*"? If not could someone explain what it means?

Also, if I'm missing anything obvious or have anything configured incorrectly, please let me know. Thanks in advance!


Solution

  • OK, it's simpler than you think, but you have to be a bit careful.

    The first problem is <taskdef name="at-autotest" .. />, because then you try to use the task as "at:autotest". "at" is the xml namespace prefix, so you need to drop the "at-" at the beginning of your taskdesk definition.

    The second problem is that you haven't associated your taskdef with the xml namespace "antlib:org.me.auto-test", so you can't use the prefix. At this stage, you could use it with the current namespace (i.e. no prefix) as in <autotest/>. But I encourage you to specify a separate namespace for all your external tasks, so the correct version is:

    <project name="SomeProject" basedir="." default="deploy" xmlns:at="antlib:org.me.auto-test">
        <!-- Task definitions. -->
        <taskdef name="autotest" classname="org.me.auto-test.AutoTestTask" uri="antlib:org.me.auto-test"/>
    
        <!-- Use the task. You can use the prefix "at:" here -->
        <at:autotest/>
    </project>
    

    A couple more things:

    1. this setup forces you to place your jar for the task class into ${ANT_HOME}/lib. But you don't have to do this, you can refer to the jar directly if you know the location (for example, a lib folder in your project root folder). You can specify a classpath containing the jar with <taskdef>, as in (assuming you have defined that classpath and assigned id="autotest.classpath"):

      <!-- Task definitions. -->
      <taskdef name="autotest" classname="org.me.auto-test.AutoTestTask" 
             uri="antlib:org.me.auto-test" classpathref="autotest.classpath" />
      
    2. As suggested by another person here, you can package your class in an antlib altogether. The only difference with the jar is that it includes a file 'antlib.xml" and that you can even skip the taskdef declaration if this jar is in the main classpath already. Read up here. This is where the "antlib:" namespace prefix brings some bonus. I personally don't like putting things in ${ANT_HOME}/lib so I'm happy to use the <taskdef /> with the classpath, but I would still recommend to package your jar as an antlib, and to use antlib: as a namespace prefix as a convention anyway.