How to <foreach> in a <macrodef>? - ant

I have a xml just like below:
<data>
<foo>value1</foo>
<foo>value2</foo>
<foo>value3</foo>
</data>
I want to create macrodef which implements below function:
<?xml version="1.0"?>
<project name="OATS" default="execute" basedir=".">
<xmlproperty file="data.xml" collapseAttributes="true"/>
<target name="execute">
<foreach list="${data.foo}" target="runScript" param="script"/>
</target>
<target name="runScript">
<echo>Doing things with ${script}</echo>
</target>
</project>
Anybody knows how to ? Thanks in advance.

xmltask is the best choice in the Ant community for this purpose, and you don't have to define your own macrodef.
So for instance:
<tools:xmltask source="data.xml" report="false" >
<tools:call path="data/foo">
<param name="value" path="text()"/>
<actions>
<echo>Doing things with #{value}</echo>
</actions>
</tools:call>
</tools:xmltask>
I encourage you to read the user manual, for xmltask has lots of options. It basically supports XPath to extract and iterate any portion of your xml. It also supports calls to existing targets in addition to anonymous code blocks (as in the example).
It's just hard to beat.

The following example uses the groovy ANT task
<project name="OATS" default="execute" basedir=".">
<taskdef name="groovy" classname="org.codehaus.groovy.ant.Groovy">
<classpath>
<pathelement location="lib/groovy-all-2.1.0-rc-2.jar"/>
</classpath>
</taskdef>
<target name="execute">
<groovy>
def data = new XmlSlurper().parse(new File("data.xml"))
data.foo.each {
properties["script"] = it
ant.project.executeTarget("runScript")
}
</groovy>
</target>
<target name="runScript">
<echo>Doing things with ${script}</echo>
</target>
</project>

This is my macrodef.
<?xml version="1.0" encoding="UTF-8"?>
<project name="OATS" default="test" basedir=".">
<property environment = "env"/>
<path id = "antcontrib.path">
<fileset file = "${env.ANT_HOME}/../net.sf.antcontrib_1.1.0.0_1-0b2/lib/ant-contrib.jar"/>
</path>
<taskdef resource="net/sf/antcontrib/antlib.xml" classpathref="antcontrib.path"/>
<macrodef name="runOATS">
<attribute name="suite"/>
<attribute name="toDir"/>
<sequential>
<delete dir="#{toDir}"/>
<mkdir dir="#{toDir}"/>
<xmlproperty file="#{suite}" collapseAttributes="true"/>
<for list="${data.foo}" param="script">
<sequential>
<runScript script="#{script}"/>
</sequential>
</for>
</sequential>
</macrodef>
<macrodef name="runScript">
<attribute name="script"/>
<sequential>
<echo>Doing things with #{script}</echo>
</sequential>
</macrodef>
<target name="test">
<runOATS toDir="/OATS/results" suite="data.xml"/>
</target>
</project>

Related

my property file seems not to be implemented by build.xml

I have a problem with properties.
here is content of my build.xml file:
<?xml version="1.0" ?>
<project name="default" default="package">
<target name="init">
<mkdir dir="build/classes" />
<mkdir dir="dist" />
</target>
<property file="${basedir}/localproperties"/>
<property name="javac.debug" value="off"/>
<target name="compile" depends="init" description="Compiles JAVA files">
<echo message="Debug: ${javac.debug}"/>
<javac srcdir="src"
destdir="build/classes"
classpathref="compile.classpath"
debug="${javac.debug}"/>
</target>
<path id="compile.classpath">
<fileset dir="lib" includes="*.jar"/>
</path>
</project>
and here is content of my localproperties file:
javac.debug = on
please note that I have saved localproperties as .xml file and put it into the same directory as build.xml
the problem is that it does not work as the output I get is:
Debug: off
clearly it should be:
Debug: on
please advise.
It should be:
<property file="${basedir}/localproperties.xml"/>
There is no extension assumed by the property task. As long as the content follows the key-value convention, it can be any file extension. But it would be confusing to save as .xml. Just save it as localproperties.properties, or simply local.properties.

How to use macrodef?

Lets assume this is my code. buildJar is my macrodef.
<target name="build">
<buildJar build.dir="module1"/>
<buildJar build.dir="module2"/>
</target>
How to invoke macrodef "buildJar" based on some condition? For example, above code can be:
<target name="build">
<if module="module1" >
<buildJar build.dir="module1"/>
</if>
<if module="module2" >
<buildJar build.dir="module2"/>
</if>
</target>
Ant supports if and unless attributes. These can be combined with a directory check using the available task:
<project name="demo" default="build" xmlns:if="ant:if">
<macrodef name="greeting">
<attribute name="name"/>
<sequential>
<echo message="found #{name}"/>
</sequential>
</macrodef>
<available property="found.module1" file="module1"/>
<available property="found.module2" file="module2"/>
<target name="build">
<greeting name="module1" if:true="${found.module1}"/>
<greeting name="module2" if:true="${found.module2}"/>
</target>
</project>

Ant Script Loop through property file and replace values dynamically

I got a requirement to loop through some XML files, replace the environment specific values in it and create new set of XML files. The environment specific values are to be taken from property file. I am able to loop through a directory to read all the files and replace some specific value using xmltask as below.
<target name="updateConfig" description="update the configuration" depends="init">
<xmltask todir="${ConfigDestDirectory}" report="false" failwithoutmatch="true">
<fileset dir="${ConfigSourceDirectory}">
<include name="*.xml"/>
</fileset>
<replace path="/:application/:NVPairs/:NameValuePair[:name='Connections/HTTP/HostName']/:value/text()" withXml="localhost"/>
</xmltask>
<echo>Replaced Successfully</echo>
</target>
But I would like to read through a property file and get the path/value from it.
I tried using property selector,property,var as different options for this case and manage to get the path but not the value. Below are the snippet of property file and the target that I am using.
#DEV.properties
HostName.xpath=/:application/:NVPairs/:NameValuePair[:name='Connections/HTTP/HostName']/:value/text()
HostName.value=localhost
<project name="TestBuild" default="ReadPropertyFile" basedir=".">
<target name="init">
<property file="DEV.properties"/>
<taskdef name="xmltask" classname="com.oopsconsultancy.xmltask.ant.XmlTask" classpath="${xmltaskPath}"/>
<taskdef resource="net/sf/antcontrib/antlib.xml" classpath="${antcontribPath}"/>
<taskdef resource="net/sf/antcontrib/antcontrib.properties"/>
</target>
<target name="ReadPropertyFile" description="update the configuration" depends="init">
<property file="DEV.properties" prefix="x"/>
<propertyselector property="propertyList" delimiter="," select="\0" match="([^\.]*)\.xpath" casesensitive="true" distinct="true"/>
<for list="${propertyList}" param="sequence">
<sequential>
<propertyregex property="destproperty" input="#{sequence}" regexp="([^\.]*)\." select="\1" />
<property name="tempname" value="${destproperty}.value" />
<var name="localprop" value="${tempname}"/>
<echo> #{sequence} </echo>
<echo> ${x.#{sequence}} </echo>
<echo>destproperty --> ${destproperty}</echo>
<echo>tempname --> ${tempname}</echo>
<echo> localprop --> ${localprop}</echo>
<echo>${x.${localprop}} </echo> <!--This is not working -->
</sequential>
</for>
</target>
It would be really helpful if you guys can throw some light.
Thanks,
Venkat
Would this work better ?
I think you got yourself confused with the "x." prefix.
<project name="TestBuild" default="ReadPropertyFile" basedir=".">
<target name="init">
<property file="DEV.properties"/>
<taskdef name="xmltask" classname="com.oopsconsultancy.xmltask.ant.XmlTask" classpath="${xmltaskPath}"/>
<taskdef resource="net/sf/antcontrib/antlib.xml" classpath="${antcontribPath}"/>
<taskdef resource="net/sf/antcontrib/antcontrib.properties"/>
</target>
<target name="ReadPropertyFile" description="update the configuration" depends="init">
<property file="DEV.properties" prefix="x"/>
<local name="propertyList"/>
<propertyselector property="propertyList" delimiter="," select="\1" match="x\.([^\.]*)\.xpath" casesensitive="true" distinct="true"/>
<for list="${propertyList}" param="sequence">
<sequential>
<echo> #{sequence} </echo>
<echo> #{sequence}.xpath = ${x.#{sequence}.xpath} </echo>
<echo> #{sequence}.value = ${x.#{sequence}.value} </echo>
</sequential>
</for>
</target>
</project>

How do I select the first element of a filelist in ant?

How do I take only the first element of an ant <filelist> and also <echo> that filename as a string?
Here's a solution that doesn't require external tasks
<project name="demo" default="run">
<path id="files.path">
<first>
<filelist dir="dir1" files="foo.jar,file1.jar,file2.jar"/>
</first>
</path>
<target name="run">
<pathconvert property="path.output" refid="files.path"/>
<echo message="Output: ${path.output}"/>
</target>
</project>
You will need ant-contrib and the trick used in the code is the fact that properties cannot be changed once they are set.
<for param="file">
<path>
<filelist
id="docfiles"
dir="toto"
files="foo.xml
bar.xml"/>
</path>
<sequential>
<basename property="package" file="#{file}"/>
</sequential>
</for>
<echo>${package}</echo>
EDIT:
An alternative solution is using the break task from Antelope

ant build script issue

I have 2 ant build scripts named "build" and "tarne"
Build:
<?xml version="1.0" ?>
<project name="build" default="zip">
<property name="project.name" value="projectName"/>
<property name="version" value="default_version_value"/>
<taskdef resource="net/sf/antcontrib/antcontrib.properties">
<classpath>
<pathelement location="lib/build/ant-contrib.jar"/>
</classpath>
</taskdef>
<var name="version2" value="default_version_value"/>
<property name="tmp" value="tmp"/>
<property name="build.dir" location="${tmp}/component/${project.name}"/>
<property name="java.classes" location="${tmp}/component/${project.name}/classes"/>
<property name="weblayout.dir" location="${tmp}/weblayout/resources/${project.name}"/>
<path id="compile.classpath">
<fileset dir="lib" includes="**/*.jar" />
<fileset dir="lib/build" includes="*.zip" />
</path>
<target name="clean">
<delete dir="${tmp}" />
</target>
<target name="init" depends="clean">
<mkdir dir="${java.classes}" />
</target>
<target name="compile" depends="init">
<javac srcdir="src" source="1.5" target="1.5" encoding="utf-8" includes="**/*.java" destdir="${java.classes}" classpathref="compile.classpath" />
</target>
<target name="copy-resources" depends="compile">
//Lots of copying here
</target>
<target name="read.version" description="Parses the hda file for your version number">
<property file="${project.name}.hda" prefix="hda"/>
<propertyregex property="version" input="${hda.version}" regexp="\." replace="-" global="true" override="true"/>
<var name="version2" value="${version}"/>
<echo>${version}</echo>
<echo>${version2}</echo>
</target>
<target name="zip" depends="copy-resources, read.version" description="Package component">
<zip destfile="${project.name}-${version}.zip" basedir="${tmp}" />
<delete dir="${tmp}" />
</target>
</project>
Tarne:
<?xml version="1.0" ?>
<project default="tarne">
<include file="build.xml"/>
<property name="project.name" value="build.project.name"/>
<target name="tarne">
<antcall target="build.read.version" inheritRefs="true"></antcall>
<property name="version" value="build.version"/>
<property name="version2" value="build.version2"/>
<echo>${version}</echo>
<echo>${version2}</echo>
</target>
</project>
And the output I get when I run tarne.xml is:
Buildfile: tarne.xml
tarne:
build.read.version:
[echo] v1-0-1
[echo] v1-0-1
[echo] default_version_value
[echo] default_version_value
Where the first 2 lines (v1-0-1) are from inside the read.version target of build.xml and the next 2 lines are from tarne.xml. The general idea is that I should be able to access the version number in my tarne.xml build script.
Any ideas on what's going wrong?
Antcall does not support what you intend to do:
http://ant.apache.org/manual/Tasks/antcall.html :
The called target(s) are run in a new project; be aware that this means properties, references, etc. set by called targets will not persist back to the calling project.
you could try:
<target name="tarne" depends="build.read.version">
</target>
which would keep the new values.
Try
<property name="version" value="${build.version}"/>
<property name="version2" value="${build.version2}"/>

Resources