Monday, November 6, 2006

Macrodef

The <macrodef/> task (new in Ant 1.6.5) can be used to define your own custom tasks from within an existing build file. In some cases it can eliminate the need to define your own custom tasks in the traditional fashion.

When possible, you should try to use <macrodef/> instead of the <antcall/> task. By using <antcall/>, you're invoking your target as if it were a task. This practice can become harder to maintain and result in performance problems in larger build files.

For example, here is a simple project file using the <antcall/> task to invoke the 'foo' target :

<project name="antcall">

<property name="A" value="A"/>
<property name="B" value="B"/>

<property name="C" value="C"/>

<target name="foo">
<echo>A: ${A}</echo>

<echo>B: ${B}</echo>
<echo>C: ${C}</echo>
</target>

<target name="foo.call.1">

<antcall target="foo">
</antcall>
</target>

<target name="foo.call.2">
<antcall target="foo">

<param name="A" value="eh?" />
<param name="B" value="bee" />

<param name="C" value="sea" />
</antcall>
</target>

</project>
And here is the same project file rewritten so that 'foo' is a task:
<project name="macrodef">


<macrodef name="foo">
<attribute name="A" default="A" />
<attribute name="B" default="B" />

<attribute name="C" default="C" />
<sequential>
<echo>A: @{A}</echo>

<echo>B: @{B}</echo>
<echo>C: @{C}</echo>
</sequential>
</macrodef>

<target name="foo.call.1">
<foo/>
</target>

<target name="foo.call.2">
<foo a="EH?" b="BEE" c="SEA"/>

</target>

</project>
The difference between the approaches is subtle but important;
  • The 'macrodef' project is going to be more maintainable because the 'foo' task attributes are encapsulated (rather than using <property/> elements).
  • Eclipse automatically detects the new task and provides the attributes via auto-completion. This saves a person having to scroll down to the target definition to determine what parameters for an <antcall> are required.
  • The performance of the 'macrodef' project will be better than the 'antcall' project because the <antcall/> will actually create an entirely new project instance in memory. This is mentioned on the ant wiki