Default element for Ant's macrodef? - ant

I would like to create a macro as such:
<macrodef name="testing">
<element name="test" implicit="yes"/>
<sequential>
<test/>
</sequential>
</macrodef>
And then use it:
<testing>
<echo message="hello world"/>
</testing>
However, I would like to specify a default for the implicit element... something like:
<macrodef name="testing">
<element name="test" implicit="yes">
<echo message="hello world"/>
</element>
<sequential>
<test/>
</sequential>
</macrodef>
So I can then use it as such:
<testing/>
Except where I want to change the default element.
Is this possible without defining a task via a Java class? So far, I don't see any documentation that indicates how to do it, if so.
Update
I ended up resolving my particular issue by using refid for filesets (which is what I actually was trying to pull into an element). Using the refid, it was simple to just use a macrodef attribute, which CAN have a default value.
Another alternative would be to create a new base macro which uses the element, and then I could have kept my existing macro as using that one... but still, there is no real default mechanism for an element (which would be nice).
So, Simon gets the answer since he's correct! Thanks!

This is not possible based on the nested element element documentation for the macrodef task.
There is a Bugzilla issue open for exactly the functionality you describe, unfortunately it has been open since 2004.

if you define your macrodef as:
<macrodef name="testing">
<element name="additional" optional="true"/>
<sequential>
<echo message="hello"/>
<additional/>
</sequential>
</macrodef>
the following invocation:
<target name="testing-call">
<mylib:testing/>
<mylib:testing>
<additional>
<echo message="world!"/>
</additional>
</mylib:testing>
</target>
will output:
[echo] hello
[echo] hello
[echo] world!

Related

Optional attributes in a macro wrapping a Java Ant Task

Ant tasks implemented in Java, as opposed to XML Ant macros, have this peculiarity of offering a slightly different behavior for missing attributes.
In my case, I'm trying to wrap the <testng> Ant task, implemented in Java, with a macro. Specifically, I would like to expose most of the functionality offered by the TestNG ant task with some minor tweaks.
Among other similar attributes, timeOut seems a bit difficult to reproduce, since its omission behaves differently than specifying and empty string.
This is simplified version of my macro definition:
<macrodef name="my-wrapper">
<attribute name="timeOut" default=""/>
<element name="nested-elements" optional="true" implicit="true"/>
<sequential>
<testng timeOut="#{timeOut}">
<nested-elements/>
</testng>
</sequential>
</macrodef>
Which fails because Ant tries to convert the value to an integer:
Can't assign value '' to attribute timeout, reason: class java.lang.NumberFormatException with message 'For input string: ""'
I've been suggested to use <augment>, which seems to be the solution to this problem. However, I fail to understand how it should be used:
<macrodef name="my-wrapper">
<attribute name="timeOut" default=""/>
<element name="nested-elements" optional="true" implicit="true"/>
<sequential>
<augment unless:blank="timeOut" id="invocation" timeOut="#{timeOut}"/>
<testng id="invocation">
<nested-elements/>
</testng>
</sequential>
</macrodef>
The above fails because of a forward reference:
java.lang.IllegalStateException: Unknown reference "invocation"
Invering the order of <testng> and <augment> doesn't really work because the <testng> task starts executing before being augmented.
What I would need is a way to conditionally add an XML attribute to a task call. Is this possible only using Ant XML syntax?
In this situation, the simplest solution would just be to set the default for timeOut to a valid value. It expects a string that can be resolved as an integer, so try using -1 if you don't want there to be a max timeout.
<macrodef name="my-wrapper">
<attribute name="timeOut" default="-1"/>
<element name="nested-elements" optional="true" implicit="true"/>
<sequential>
<testng timeOut="#{timeOut}">
<nested-elements/>
</testng>
</sequential>
</macrodef>

Any way to check if an optional element in macrodef is provided?

is there any way to check if a given element X is passed to the macrodef. I have a case to decide if the element X should be required or optional. To achieve this I made the element optional for all the cases,
but I want to make validation in case the element is missing, if it’s allowed to be missing :-).
The macro is looking like this:
<macrodef name="test">
<attribute name="attribute1"/>
......
<attribute name="attributeN/>
<element name="X" optional="true/>
<element name="Y" optional="true/>
<sequential>
<local>
<!--here check if the element <X/> is passed -->
</local>
</sequential>
</macrodef>
<test attribute1="1", attributeN="N">
<!--Here do not provide element X. Only provide Y-->
<Y>
<nestedY1>Some text1</nestedY1>
<nestedY2>Some text2</nestedY2>
</Y>
</test>
The element X is looking just like element Y. I mean, in case it is present, it will contain another nested elements.
Maybe I am wrong in the way I understand this concept. I will try to give another example.
Currently the element X is mandatory and my task is to make it optional in some cases but mandatory in another cases. I want to be able to use the macro both ways, but I don’t know how to implement this task:
<macrodef name="test">
<attribute name="attribute1"/>
<element name="X"/>
<element name="MandatoryX" optional="true/>
<sequential>
<local>
<!--here check if the element <MandatoryX/> is passed and if Yes than make sure that element X is passed too-->
</local>
</sequential>
</macrodef>
<test attribute1="1">
<!--Here MandatoryX is missing and X can be missing too-->
</test>
or
<test attribute1="1">
<MandatoryX>In case MandatoryX is present, than element X must be present too</MandatoryX>
<X>Here X is mandatory</X>
</test>
I figured out a way of doing this. A bit kludgey, but works for me. The key is to use the <echoxml> task to write the macro element to a file, then read the file and look for some pattern in it. I write <stuff> and </stuff> around the macro element. When the macro element is not provided, the stuff element gets written out as simply <stuff />, and this can be searched for.
Note, I am also using antcontrib, hence the <if> block.
<macrodef name="processFiles">
<attribute name="workDir"/>
<attribute name="tempDir"/>
<element name="extra-deletes" optional="true"/>
<sequential>
<echoxml file="#{tempDir}/extra-deletes.xml"><stuff><extra-deletes/></stuff></echoxml>
<local name="extra-deletes-prop"/>
<loadfile property="extra-deletes-prop" srcfile="#{tempDir}/extra-deletes.xml"/>
<if>
<not><contains string="${extra-deletes-prop}" substring="<stuff />"/></not>
<then>
<delete>
<fileset dir="#{workDir}">
<extra-deletes/>
</fileset>
</delete>
</then>
</if>
</sequential>
</macrodef>
This macro would get called with some expression involving <filename .../> to identify which files to delete. (This is derived from a more complicated script, but basically there is some pattern of files to delete for every project and other projects have extra, project-specific delete patterns.)
So it would be called like this:
<processFiles workDir="..." tempDir="...">
<extra-deletes>
<or>
<filename name="..."/>
<filename regex="..."/>
</or>
</extra-deletes>
</processFiles>
... or in the case with no 'extra-deletes' to perform,
<processFiles workDir="..." tempDir="..."/>

Bug in ant macrodef

I have a bug in the following ant macrodef definition:
<macrodef name="xxx" description="does xxx">
<attribute name="attr1" default=""/>
<sequential>
<echo message="doing yyy"/>
<echo message="#{attr1}" file="${builddir}/zzz.xml"/>
<taskdef name="xmltask" classname="com.oopsconsultancy.xmltask.ant.XmlTask"/>
<xmltask report="true" source="${builddir}/zzz.xml">
<call path="spuds" target="process-spuds">
<param name="spud-kicker" path="#count"/>
</call>
</xmltask>
</sequential>
</macrodef>
When it tries to call the target of the call inside the xmltask, i get a null pointer exception.
What am I doing wrong?
The file zzz.xml contains (supposedly) well formed xml. It contains a top level tag called "spuds" with an attribute named count. I want to invoke the spud-kicker ant target on the spuds tag and pass the value of the count attribute to that task.

Macrodef and "local properties"

I am trying to move a file (specified by a pattern) to a given location in an Ant macrodef:
<macrodef name="extract">
<attribute name="package"/>
<sequential>
<!-- the path will contain the unique file in extracted regardless of the name -->
<path id="source_refid">
<dirset dir="${dep}/lib/#{package}/extracted/">
<include name="#{package}-*"/>
</dirset>
</path>
<!-- this is not working: properties are immutable -->
<property name="source_name" refid="source_refid"/>
<move
file="${source_name}"
tofile="${dep}/#{package}/"
overwrite="true"
/>
</sequential>
</macrodef>
This will work just once as ${source_name} is immutable.
An option would be to use the variable task but I didn't find a way to assign a refid to a var.
Is there a way to have something similar to local variable in a macrodef? Or (XY problem) is there a better way to solve my problem?
Since Ant 1.8 you can use the local task for this. For example:
<local name="source_name"/>
<property name="source_name" refid="source_refid"/>
Your example is just the sort of thing local is for!

Ant macrodef: Is there a way to get the contents of an element parameter?

I'm trying to debug a macrodef in Ant. I cannot seem to find a way to display the contents of a parameter sent as an element.
<project name='debug.macrodef'>
<macrodef name='def.to.debug'>
<attribute name='attr' />
<element name='elem' />
<sequential>
<echo>Sure, the attribute is easy to debug: #{attr}</echo>
<echo>The element works only in restricted cases: <elem /> </echo>
<!-- This works only if <elem /> doesn't contain anything but a
textnode, if there were any elements in there echo would
complain it doesn't understand them. -->
</sequential>
</macrodef>
<target name='works'>
<def.to.debug attr='contents of attribute'>
<elem>contents of elem</elem>
</def.to.debug>
</target>
<target name='does.not.work'>
<def.to.debug attr='contents of attribute'>
<elem><sub.elem>contents of sub.elem</sub.elem></elem>
</def.to.debug>
</target>
</project>
Example run:
$ ant works
...
works:
[echo] Sure, the attribute is easy to debug: contents of attribute
[echo] The element works only in restricted cases: contents of elem
...
$ ant does.not.work
...
does.not.work:
[echo] Sure, the attribute is easy to debug: contents of attribute
BUILD FAILED
.../build.xml:21: The following error occurred while executing this line:
.../build.xml:7: echo doesn't support the nested "sub.elem" element.
...
So I guess I need either a way to get the contents of the <elem /> into a property somehow (some extended macrodef implementation might have that), or I need a sort of <element-echo><elem /></element-echo> that could print out whatever XML tree you put inside. Does anyone know of an implementation of either of these? Any third, unanticipated way of getting the data out is of course also welcome.
How about the echoxml task?
In your example build file replacing the line
<echo>The element works only in restricted cases: <elem /> </echo>
with
<echoxml><elem /></echoxml>
results in
$ ant does.not.work
...
does.not.work:
[echo] Sure, the attribute is easy to debug: contents of attribute
<?xml version="1.0" encoding="UTF-8"?>
<sub.elem>contents of sub.elem</sub.elem>
Perhaps the XML declaration is not wanted though. You might use the echoxml file attribute to put the output to a temporary file, then read that file and remove the declaration, or reformat the information as you see fit.
edit
On reflection, you can probably get close to what you describe, for example this sequential body of your macrodef
<sequential>
<echo>Sure, the attribute is easy to debug: #{attr}</echo>
<echoxml file="macro_elem.xml"><elem /></echoxml>
<loadfile property="elem" srcFile="macro_elem.xml">
<filterchain>
<LineContainsRegexp negate="yes">
<regexp pattern=".xml version=.1.0. encoding=.UTF-8..." />
</LineContainsRegexp>
</filterchain>
</loadfile>
<echo message="${elem}" />
</sequential>
gives
$ ant does.not.work
...
does.not.work:
[echo] Sure, the attribute is easy to debug: contents of attribute
[echo] <sub.elem>contents of sub.elem</sub.elem>

Resources