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

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="..."/>

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>

How to pass paramters by refrence in ant

Hi all this is my code for target calling.
<target name="abc">
<var name="x" value="10"/>
<antcall target="def"/>
<!--Again Access The value of x here and also change it here-->
</target>
<target name="def">
<!--Access The value of x here and also change it here-->
</target>
and also i want to access this X in other build file,is there any way
This is not possible with ant. In an properties are immutable and cannot be reset. The var task from ant contrib can be used to override values, but should be used sparingly.
You could use a temporary file to achieve what you want. But probably you are trying something weird, which can be solved in a different way.
This would also work across buildfiles if they have access to the property file.
<target name="abc">
<var name="x" value="10"/>
<antcall target="def"/>
<!--Again Access The value of x here and also change it here-->
<var unset="true" file="myproperty.properties" /> <!-- read variable from property file-->
</target>
<target name="def">
<echo file="myproperty.properties" append="false">x=12</echo> <!-- create a new propertyfile-->
</target>
For the sake of justice, there is a hack that allows to alter ant's immutable properties without any additional libs (since java 6):
<scriptdef name="propertyreset" language="javascript"
description="Allows to assing #{property} new value">
<attribute name="name"/>
<attribute name="value"/>
project.setProperty(attributes.get("name"), attributes.get("value"));
</scriptdef>
Usage:
<target name="abc">
<property name="x" value="10"/>
<antcall target="def"/>
</target>
<target name="def">
<propertyreset name="x" value="11"/>
</target>
As #oers mentioned, this should be used with care after all canonical approaches proved not to fit.
It is difficult to suggest further without knowing the goal behind the question.

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 macrodefs and element naming

I have a macrodef with a an element called "libs"
<macrodef name="deploy-libs">
<element name="libs"/>
<sequential>
<copy todir="${glassfish.home}/glassfish/domains/domain1/lib">
<fileset dir="${lib}">
<libs/>
</fileset>
</copy>
</sequential>
</macrodef>
which is then invoked as
<deploy-libs>
<libs>
<include name="mysql-connector-*.jar"/>
<include name="junit-*.jar" />
<!-- .... -->
</libs>
</deploy-libs>
I then have another macrodef which calls several macrodefs including "deploy-libs". It would be nice if this macrodef had an element "libs" too but:
<macrodef name="init-glassfish">
<element name="libs"/>
<sequential>
<!-- other stuff -->
<deploy-libs>
<libs>
<libs/>
</libs>
</deploy-libs>
<!-- other stuff -->
</sequential>
</macrodef>
is obviously not working (because of <libs><libs/></libs>):
Commons/ant-glassfish-server.xml:116: unsupported element include
A solution could be to name the element in "init-glassfish" in a different way:
<macrodef name="init-glassfish">
<element name="libraries"/>
<sequential>
<!-- other stuff -->
<deploy-libs>
<libs>
<libraries/>
</libs>
</deploy-libs>
<!-- other stuff -->
</sequential>
</macrodef>
Is there a way to have the element to be named in the same way for both macrodefs?
I found a solution to my question which solves the original problem using a path id
<macrodef name="deploy-libs">
<attribute name="libraries-path-refid"/>
<sequential>
<copy todir="${glassfish.home}/glassfish/domains/domain1/lib">
<path refid="#{libraries-path-refid}"/>
</copy>
</sequential>
</macrodef>
Now this does not solve the issue with the nested elements tags but (XY problem) solves the practical issue. It would be still be nice to know if something similar to the original question is possible.
Apparently, the solution is wrapping additional <libs> elements around in the original call depending on how deep the macros nest. Of course this is a horrible solution because it requires that the macro nesting depth is known on invocation, e.g.:
<deploy-libs>
<libs>
<libs>
<include name="mysql-connector-*.jar"/>
<include name="junit-*.jar" />
<!-- .... -->
</libs>
</libs>
</deploy-libs>
Ant bug 29153 addressing this problem was unfortunately resolved as invalid :(.

Default element for Ant's macrodef?

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!

Resources