ant macrodefs and element naming - ant

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 :(.

Related

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

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.

How to to pass configurable file mapping lists to ant macros?

I'm trying to refactor an Ant buildfile with many similar targets into a buildfile that uses macros. This roughly is what it looks like:
<macrodef name="build-text">
<argument name="lang" />
<element name="file-list"/>
<sequential>
<property name="lang" value="#{lang}" />
<xslt style="my_stylesheet.xsl" destdir="build" basedir="src">
<!-- lots of params here -->
<file-list />
</xslt>
</sequential>
</macrodef>
<target name="buildTextDE">
<build-text lang="DE">
<file-list>
<mapper>
<mapper type="glob" from="Text1_${lang}.html" to="Text1_${lang}.fo"/>
<mapper type="glob" from="Text2_${lang}.html" to="Text2_${lang}.fo"/>
</mapper>
</file-list>
</build-text>
</target>
There is another task called buildTextEN that is nearly identical except for the lang attribute. In some cases, the file list differs however. Now how would I like to simplify the buildfile further by defining a "global" mapping list that contains the file lists for German and English, each file with the placeholder for the language. I would like to reference this global mapping where no special case is needed. How would I do that?

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: how to write optional nested elements

Say that I need to do something like:
<copy todir="${DEPLOYMENT_DIR}" overwrite="true">
<fileset dir="dir1" />
<fileset dir="dir2" />
<fileset dir="dir3" />
...
<if>
<equals arg1="${SPECIAL_BUILD}" arg2="true"/>
<then>
<fileset dir="dir7" />
<fileset dir="dir8" />
...
</then>
</if>
</copy>
(The real task is not copy, I'm just using it to illustrate the point.)
Ant will complain that my task doesn't support nested <if> which is fair enough. I've been thinking along these lines:
I could add a macrodef with an "element" attribute like this:
<macrodef name="myCopy">
<element name="additional-path" />
<sequential>
<copy todir="${DEPLOYMENT_DIR}" overwrite="true">
<fileset dir="dir1" />
<fileset dir="dir2" />
<fileset dir="dir3" />
...
<additional-path/>
</copy>
</sequential>
</macrodef>
But that would mean that the caller (target) must specify the additional path which I want to avoid (if many targets call this task, they would have to repeat the fileset definitions in the additional-path element).
How to code the additional filesets inside the macrodef so that Ant doesn't complain?
AntContrib has an Ant FileSet object augmented with if and unless conditions.
http://ant-contrib.sourceforge.net/fileset.html
if Sets the property name for the 'if' condition. The fileset will be
ignored unless the property is
defined. The value of the property is
insignificant, but values that would
imply misinterpretation ("false",
"no") will throw an exception when
evaluated.
unless Set the property name for the 'unless' condition. If named
property is set, the fileset will be
ignored. The value of the property is
insignificant, but values that would
imply misinterpretation ("false",
"no") of the behavior will throw an
exception when evaluated.
You could use it like this:
<copy todir="${DEPLOYMENT_DIR}" overwrite="true">
<fileset dir="dir1" />
<fileset dir="dir2" />
<fileset dir="dir3" />
...
<fileset dir="dir7" if="SPECIAL_BUILD" />
<fileset dir="dir8" if="SPECIAL_BUILD" />
</copy>
One way (not sure if a good one) to achieve that is to create two macrodefs - one "public" for general use and one "internal" that does the real work and is intended to be called only from the "public" macro. Like this:
<macrodef name="task-for-public-use">
<sequential>
<if>
<equal arg1="${SPECIAL_BUILD}" arg2="true" />
<then>
<internal-task>
<additional-path>
...
</additional-path>
</internal-task>
</then>
<else>
<internal-task ... />
</else>
</if>
</sequential>
</macrodef>
<macrodef name="internal-task">
<element name="additional-path" />
<sequential>
<copy ...>
...
<additional-path/>
</copy>
</sequential>
</macrodef>
I don't like it much though and hope there's a better way.

Resources