Why changes in Ant plugins taskdefs?

I am really confused with the correct (most modern, best practice, etc.) way of defining taskdefs for Ant plugins. Below is a snippet of code that I put together from one of our built-in Java applications build.xml . Note that all of the following Ant targets work 100% perfectly (even if I chopped-n-paste them together and shared most of my content for the sake of brevity):

 <project name="MyApp" default="package" basedir="." xmlns:jacoco="antlib:org.jacoco.ant"> <path id="cobertura.path"> <!-- ... --> </path> <taskdef name="jacoco-coverage" classname="org.jacoco.ant.CoverageTask"/> <taskdef name="jacoco-report" classname="org.jacoco.ant.ReportTask"/> <taskdef classpathref="cobertura.path" resource="tasks.properties" /> <taskdef name="findbugs" classname="edu.umd.cs.findbugs.anttask.FindBugsTask"/> <target name="run-coco" depends="doOtherStuff"> <jacoco:coverage> <!-- ... --> </jacoco:covergae> <jacoco-report> <!-- ... --> </jacoco-report> </target> <target name="findbugs"> <antcall target="compile" /> <findbugs home="${findbugs.home}" output="xml:withMessages" outputFile="findbugs.xml"> <!-- ... --> </findbugs> </target> </project> 
  • Why do I need to define an XML namespace for JaCoCo, but not for Findbugs?
  • What is antlib? Sometimes I see this inside an XML namespace definition, and sometimes not there.
  • Although both the jacoco-coverage and javacoco-report taskdefs functions use a hyphen ("-") in their name, the corresponding tasks are called jacoco:coverage and jacoco-report respectively ... why the colon (":") for coverage, and how it works (this is true, trust me!)?

Thanks in advance!

+4
source share
1 answer

Let's take this question in one step:

Step # 1: Defining Your Task

You can define a task in one of two ways:

  • You can specify a class file, and then give the job that classfile defines the name.
  • You can specify a resource file inside the jar that contains the task names and the class used by these task names.

In your case, for JaCoCo you did the first:

 <taskdef name="jacoco-coverage" classname="org.jacoco.ant.CoverageTask"/> <taskdef name="jacoco-report" classname="org.jacoco.ant.ReportTask"/> 

However, you can do this in another case:

 <taskdef resource="org/jacoco/ant/antlib.xml"/> 

If you open the jacoco jar file and expand it in this directory, you will see a file called antlib.xml . If you look at this file, you will see the following:

 <antlib> <taskdef name="coverage" classname="org.jacoco.ant.CoverageTask"/> <taskdef name="agent" classname="org.jacoco.ant.AgentTask"/> <taskdef name="report" classname="org.jacoco.ant.ReportTask"/> <taskdef name="merge" classname="org.jacoco.ant.MergeTask"/> <taskdef name="dump" classname="org.jacoco.ant.DumpTask"/> </antlib> 

So, if there is a resource file located in the bank, you can define all your tasks with a single <taskdef> . Note that what you called jacoco-coverage is simply called coverage here, and what you called jacoco-report is called report here.

If you used <jacoco-coverage/> as an Ant task, I would use <coverage/> .

Step # 2: Where is the JAR file?

In the above, I defined the task as:

 <taskdef resource="org/jacoco/ant/antlib.xml"> 

Somewhere, Ant should find this path in its class path, but where? If you put the JaCoCo jar in your $ANT_HOME/lib folder, it will be automatically included in your classpath. This makes the task definition very simple. Unfortunately, this also means that if someone wants to run your Ant script, they will have to download this JaCoCo jar and place it in the $ANT_HOME/lib folder. Not very portable.

Fortunately, you can indicate where the JaCoCo can is located. Since your project is usually under version control, the best place to place it is under your project directory, located below where build.xml is located. When someone validates your project and build.xml , it will also receive JaCoCo.jar.

My preference is to create a directory called antlib , and then create a subdirectory in this antlib directory for each set of tasks. In this case, I would have a directory named antlib/jacoco .

Once my jacoco.jar file is securely arranged. in this directory, I can add the <classpath> sub-entity to my <taskdef> , where you can find jacoco.jar :

Before Classpath:

 <taskdef resource="org/jacoco/ant/antlib.xml"/> 

After Classpath:

 <taskdef resource="org/jacoco/ant/antlib.xml"> <classpath> <fileset dir="${basedir}/antlib/jacoco"/> </classpath> </taskdef> 

Please note, using <fileset/> , I don’t care whether the jar file is called jacoco.jar or jacoco-2.3.jar . If you know the exact name of the bank, you can do this:

 <taskdef resource="org/jacoco/ant/antlib.xml"> <classpath path="${basedir}/antlib/jacoco/jacoco.jar"/> </taskdef> 

And save a couple of lines.

Step # 3: XML Namespace

As of this point, I don't need jacoco: prefixed with the names of my task.

I could just do this:

 <project name="MyApp" default="package" basedir="."> <taskdef resource="org/jacoco/ant/antlib.xml"> <classpath path="${basedir}/antlib/jacoco/jacoco.jar"/> </taskdef> <target name="run-coco" depends="doOtherStuff"> <coverage> <!-- ... --> <coverage> <report> <!-- ... --> <report> </target> </project> 

And you could leave that at that. No problem. Simple and clean.

However, if you use two different sets of Ant tasks, one of which is called Foo and one is called Bar , and both of them have the <munge/> task defined in them?

 <taskdef resource="org/foo/ant/antlib.xml"> <classpath path="${basedir}/antlib/foo.jar/> </taskdef> <taskdef resource="org/bar/ant/antlib.xml"> <classpath path="${basedir}/antlib/bar.jar/> </taskdef> <target name="munge-this"> <!-- Is this foo.jar or bar.jar munge task? --> <munge vorbix="fester"/> </target> 

What munge task is munge performed? Is it in foo.jar or in bar.jar ?

To get around this, Ant allows you to define an XML namespace for each set of tasks. You can define such a namespace at the very top of the <project> , inside the task itself or in the name <target> . 99% of the time, this is done in the <project> object.

 <project name="demo" default="package" basename="." xmlns:foo="I-will-have-fries-with-that" xmlns:bar="Never-on-a-first-date"> <taskdef uri="I-will-have-fries-with-that" resource="org/foo/ant/antlib.xml"> <classpath path="${basedir}/antlib/foo.jar/> </taskdef> <taskdef uri="Never-on-a-first-date" resource="org/bar/ant/antlib.xml"> <classpath path="${basedir}/antlib/bar.jar/> </taskdef> <target name="munge-this"> <!-- Look the 'foo:' XMLNS prefix! It foo.jar much task! --> <foo:munge vorbix="fester"/> </target> 

Now I know that this is the <munge/> task from the Foo task list!

xmlns defines the XML namespace. xmlns:foo defines the namespace for the Foo taks set, and xmlns:bar defines it for the set of task bars. When I use a task from foo.jar , I prefix it with the foo: namespace. Note that this prefix is ​​correct after xmlns:

uri is just a string that matches a namespace string. I used the I-will-have-fries-with-that and Never-on-a-first-date strings to show you that the strings themselves are not important. The important thing is that this line corresponds to the uri parameter of the <taskdef> task. Thus, the defined tasks know which namespace they should use.

Now, usually a URI string is a URI definition. There are two ways to do this:

  • Use the antlib: URI, which is somewhat standardized, and uses this naming convention for the namespace: antlib:org.jacoco.ant .
  • Use the URL pointing to the project: http://www.eclemma.org/jacoco/ .

I prefer the latter because it will point to the documentation. However, it seems that the former is more popular.

So let's see how Jacoco works:

 <project name="MyApp" default="package" basedir="." xmlns:jacoco="antlib:org.jacoco.ant"> <taskdef uri="antlib:org.jacoco.ant" resource="org/jacoco/ant/antlib.xml"> <classpath path="${basedir}/antlib/jacoco/jacoco.jar"/> </taskdef> <target name="run-coco" depends="doOtherStuff"> <jacoco:coverage> <!-- ... --> <jacoco:coverage> <jacoco:report> <!-- ... --> <jacoco:report> </target> </project> 

Note that uri in JaCoCo <taskdef/> matches the xmlns:jacoco .

This is all that is needed to define tasks. I hope he explains where the jacoco: prefix jacoco: and how you can define tasks by pointing to the actual class name that contains this class, or a resource file that points to the task name and class.

+10
source

All Articles