Ant Script Loop through property file and replace values dynamically - ant

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>

Related

how to test symlinks with ant 1.9

I need to perform a command if the jar file is a file instead of a symlink. I have found a solution that works only with ant 1.10.
Does anyone know how to do it with ant 1.9 ?
Here is my build.xml.
<?xml version="1.0"?>
<project name="AsterixDecoder" default="bm" basedir=".">
<!-- set global properties for this build -->
<property name="src" location="src"/>
<property name="build" location="build"/>
<property name="dist" location="dist"/>
<property environment="env"/>
<condition property="exists.CCM_ADDR">
<isset property="env.CCM_ADDR"/>
</condition>
<target name="compile" description="compile the source " >
<mkdir dir="${build}"/>
<javac srcdir="${src}" destdir="${build}" includeantruntime="false"/>
<mkdir dir="${build}/resources"/>
<copy todir="${build}/resources">
<fileset dir="resources"/>
</copy>
</target>
<target name="checkout" if="exists.CCM_ADDR">
<ccmcheckout file="${dist}/AsterixDecoder.jar"/>
</target>
<target name="dist" depends="compile, checkout"
description="generate the distribution" >
<jar jarfile="${dist}/AsterixDecoder.jar" filesetmanifest="mergewithoutmain">
<manifest>
<attribute name="Main-Class" value="fr.eurocontrol.escape.ground.asterixdecoder.AsterixDataTree"/>
<attribute name="Class-Path" value="."/>
</manifest>
<fileset dir="${build}"/>
</jar>
</target>
<target name="check.symlink">
<fileset dir="${dist}" id="fileset" includes="AsterixDecoder.jar">
<symlink/>
</fileset>
<pathconvert refid="fileset" property="is.symlink" setonempty="false"/>
</target>
<target name="reconcile" depends="check.symlink" if="exists.CCM_ADDR" unless="is.symlink">
<exec executable="ccm">
<arg value="reconcile"/>
<arg value="-udb"/>
<arg value="${dist}/AsterixDecoder.jar"/>
</exec>
</target>
<target name="bm" description="build management" depends="dist, reconcile">
</target>
</project>
Do not hesitate to make any suggestion of improvements. I am still a beginner in writing ant files.
The most straightforward way to do this would be to use the record function of Ant's symlink task. This creates a property file that lists all of the symlinks found within a given resource collection. Here's an example target:
<target name="default">
<symlink link="testdir" resource="build" />
<symlink action="record" linkfilename="links.record">
<fileset dir="." includes="*" />
</symlink>
<property file="links.record" />
<condition property="testdir.is.symlink">
<isset property="testdir" />
</condition>
<echo message="${testdir.is.symlink}" />
</target>

How to develope a method to substitute repetitive code block

I am new in ant, so I wasn't able to find an approach to make my buld file a bit more elegant. I believe there is an approach to substitute repetitive block of code into my build. So here is build file:
<project basedir="../../../" name="do-report" default="zip-all">
<xmlproperty keeproot="false" file="implementation/xml/ant/properties.xml"/>
<!-- -->
<taskdef resource="net/sf/antcontrib/antcontrib.properties">
<classpath>
<pathelement location="${infrastructure-base-dir}/apache-ant-1.9.6/lib/ant-contrib-0.3.jar"/>
</classpath>
</taskdef>
<!-- -->
<target name="clean">
<delete dir="${dita-odt.path.odt-unzipped-base-dir}" includeemptydirs="true" failonerror="no"/>
<delete dir="examples/intermediate/odt-files" includeemptydirs="true" failonerror="no"/>
</target>
<!-- -->
<target name="unzip-writing-odt-file" depends="clean">
<unzip src="${dita-odt.path.writing-odt}" dest="${dita-odt.path.writing-odt-unzipped}"/>
</target>
<!-- -->
<target name="extract-common-paths" depends="unzip-writing-odt-file">
<foreach target="copy-text-path" param="file">
<path>
<fileset dir="${dita-odt.path.text-xml-base-dir}">
<include name="**/content.xml"/>
</fileset>
</path>
</foreach>
</target>
<!-- -->
<target name="copy-text-path" description="copy text-xml path relative to text-xml-base-dir">
<dirname property="text-absolute-dir-path" file="${file}"/>
<property name="absolute-path-text-base-dir" location="${dita-odt.path.text-xml-base-dir}"/>
<pathconvert property="common-path" dirsep="/">
<path location="${text-absolute-dir-path}"/>
<map from="${absolute-path-text-base-dir}/" to=""/>
</pathconvert>
<antcall target="copy-writing-unzipped">
<param name="common-path" value="${common-path}"/>
</antcall>
</target>
<!-- -->
<target name="copy-writing-unzipped">
<echo>${common-path}</echo>
<copy todir="${dita-odt.path.odt-unzipped-base-dir}/${common-path}">
<fileset dir="${dita-odt.path.writing-odt-unzipped}">
<include name="**/*"/>
</fileset>
</copy>
</target>
<!-- -->
<target name="transform-all" depends="extract-common-paths">
<foreach target="transform" param="file">
<path>
<fileset dir="${dita-odt.path.text-xml-base-dir}">
<include name="**/content.xml"/>
</fileset>
</path>
</foreach>
</target>
<!-- -->
<target name="transform">
<basename property="file-base-name" file="${file}"/>
<dirname property="file-dir-absolute-path" file="${file}"/>
<property name="text-xml-base-dir-absolute-path" location="${dita-odt.path.text-xml-base-dir}"/>
<pathconvert property="common-path" dirsep="/">
<path location="${file-dir-absolute-path}"/>
<map from="${text-xml-base-dir-absolute-path}/" to=""/>
</pathconvert>
<!--Substitutes backslashes with forword slashes. Basedir is a reserved property that returns absolute path with separator symbols of the current OS.-->
<pathconvert dirsep="/" property="base-dir-unix">
<path location="${basedir}"/>
</pathconvert>
<echo>TRANSFORM TO: ${dita-odt.path.odt-unzipped-base-dir}/${common-path}/${file-base-name}</echo>
<xslt in="${file}" out="${dita-odt.path.odt-unzipped-base-dir}/${common-path}/${file-base-name}" style="${dita-odt.path.text-odt-xsl}" extension=".xml" force="true">
<param name="dir-path-styles-xml" expression="${dita-odt.path.odt-unzipped-base-dir}/${common-path}"/>
<param name="project-base-dir-absolute-path" expression="${base-dir-unix}"/>
<classpath location="${infrastructure-base-dir}/${dita-odt.text-odt-xsl.processor}"/>
</xslt>
</target>
<!-- -->
<target name="zip-all" depends="transform-all" description="Turns all unzipped text folders into ODT files">
<foreach target="zip-odt" param="file">
<path>
<fileset dir="${dita-odt.path.odt-unzipped-base-dir}" includes="**/content.xml" excludes="writing/**"/>
</path>
</foreach>
</target>
<!-- -->
<target name="zip-odt">
<basename property="file-base-name" file="${file}"/>
<dirname property="file-dir-absolute-path" file="${file}"/>
<!--This property will be used to provided name for the produced ODT file. The document will have the same name as the folder that contains it.-->
<basename property="odt-doc-name" file="${file-dir-absolute-path}.odt"/>
<property name="odt-unzipped-base-dir-absolute-path" location="${dita-odt.path.odt-unzipped-base-dir}"/>
<pathconvert property="common-path" dirsep="/">
<path location="${file-dir-absolute-path}"/>
<map from="${odt-unzipped-base-dir-absolute-path}/" to=""/>
</pathconvert>
<echo>COMMON PATH: ${common-path}</echo>
<zip destfile="examples/intermediate/odt-files/${common-path}/${odt-doc-name}" basedir="${dita-odt.path.odt-unzipped-base-dir}/${common-path}" update="true"/>
</target>
<!-- -->
</project>
So this part of the script does pretty much the same, but shared among almost all the target in the project:
<dirname property="file-dir-absolute-path" file="${file}"/>
<property name="text-xml-base-dir-absolute-path" location="${dita-odt.path.text-xml-base-dir}"/>
<pathconvert property="common-path" dirsep="/">
<path location="${file-dir-absolute-path}"/>
<map from="${text-xml-base-dir-absolute-path}/" to=""/>
</pathconvert>
This part does nothing but to obtain part of a path. For example if ${file} stands for /folder/subfolder1/subfolder2 then take the path after /folder namely subfolder1/subfolder2 and assign it to a property. I this case that property is named common-path that holds same path for all the target. I examined MacroDef Task, but as far as I understand it doesn't return, only accepts some parameters in form of attributes. Anyway, any help would be much appreciated.
You are on the right track in considering <macrodef> to reduce repetitive code.
While it's true that <macrodef> doesn't return anything, <macrodef> can be given the name of a property to set. For example...
<macrodef name="my-hello">
<attribute name="person"/>
<attribute name="output-property"/>
<sequential>
<property name="#{output-property}" value="Hello, #{person}!"/>
</sequential>
</macrodef>
<my-hello person="Riko" output-property="say-hi-to-riko"/>
<echo>my-hello said: ${say-hi-to-riko}</echo>
...outputs...
[echo] my-hello said: Hello, Riko!
In this example, the caller of <my-hello> tells the macrodef to "return" its results in the say-hi-to-riko property.
Knowing this, several of the <target>s in your script can be converted to <macrodef>s that set properties...
<project name="ant-macrodef-pathconvert" default="extract-common-paths">
<taskdef resource="net/sf/antcontrib/antlib.xml" />
<property name="dita-odt.path.text-xml-base-dir" value="C:\temp\dita-odt"/>
<macrodef name="my-pathconvert">
<attribute name="file"/>
<attribute name="common-path-property"/>
<sequential>
<!-- <local> allows multiple calls to a macrodef. -->
<local name="file-dir-absolute-path"/>
<echo>In my-pathconvert for #{file}</echo>
<dirname property="file-dir-absolute-path" file="#{file}"/>
<property name="text-xml-base-dir-absolute-path"
location="${dita-odt.path.text-xml-base-dir}"/>
<pathconvert property="#{common-path-property}" dirsep="/">
<path location="${file-dir-absolute-path}"/>
<map from="${file-dir-absolute-path}/" to=""/>
</pathconvert>
</sequential>
</macrodef>
<macrodef name="copy-text-path"
description="copy text-xml path relative to text-xml-base-dir">
<attribute name="file"/>
<sequential>
<local name="common-path"/>
<echo>In copy-text-path for #{file}</echo>
<my-pathconvert file="#{file}" common-path-property="common-path"/>
<copy-writing-unzipped common-path="${common-path}"/>
</sequential>
</macrodef>
<macrodef name="copy-writing-unzipped">
<attribute name="common-path"/>
<sequential>
<echo>In copy-writing-unzipped for #{common-path}</echo>
<echo>copy task goes here.</echo>
</sequential>
</macrodef>
<target name="extract-common-paths">
<for param="file">
<path>
<fileset dir="${dita-odt.path.text-xml-base-dir}">
<include name="**/content.xml"/>
</fileset>
</path>
<sequential>
<copy-text-path file="#{file}"/>
</sequential>
</for>
</target>
</project>
In general, it's better to prefer calling <macrodef>s over calling <target>s directly. In the above example, <foreach> is replaced with <for> because <for> lets us call <macrodef>s.
Output
[echo] In copy-text-path for C:\temp\dita-odt\dir1\content.xml
[echo] In my-pathconvert for C:\temp\dita-odt\dir1\content.xml
[echo] In copy-writing-unzipped for C:/temp/dita-odt/dir1
[echo] copy task goes here.
[echo] In copy-text-path for C:\temp\dita-odt\dir2\content.xml
[echo] In my-pathconvert for C:\temp\dita-odt\dir2\content.xml
[echo] In copy-writing-unzipped for C:/temp/dita-odt/dir2
[echo] copy task goes here.

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 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}"/>

Multiple renaming using ant script

Let me explain the scenario:
D:\project\src\one.txt
D:\project\src\two.txt
D:\project\src\three.txt
D:\project\src\four.txt
The above files should be copied as :
D:\project\dst\one.xls
D:\project\dst\two.xls
D:\project\dst\three.xls
D:\project\dst\four.xls
I need to change the extension without using the mapper and move task. I need to rename as above using a for loop with fte:filecopy function inside. Is this possible ???
For anyone arriving here without the negative requirement afflicting the OP, the much simpler answer is to use a mapper.
<project default="move_files">
<target name="move_files">
<copy todir="dst">
<fileset dir="src">
<include name="*.txt"/>
</fileset>
<globmapper from="*.txt" to="*.xls"/>
</copy>
</target>
</project>
This works for me :
<?xml version="1.0"?>
<project name="so-copy-rename" default="build2">
<property name="ant-contrib-jar" value="${user.home}/.ant/lib/ant-contrib-1.0b3.jar"/>
<target name="setup" unless="ant-contrib.present">
<echo>Getting ant-contrib</echo>
<mkdir dir="${user.home}/.ant/lib"/>
<!--
Note: change this to a locally hosted maven repository manager such as nexus http://nexus.sonatype.org/
-->
<get dest="${ant-contrib-jar}"
src="http://repo1.maven.org/maven2/ant-contrib/ant-contrib/1.0b3/ant-contrib-1.0b3.jar"/>
</target>
<target name="taskdefs">
<taskdef resource="net/sf/antcontrib/antlib.xml">
<classpath>
<pathelement location="${ant-contrib-jar}"/>
</classpath>
</taskdef>
</target>
<target name="build" depends="taskdefs">
<property name="srcdir" value="src"/>
<property name="targetdir" value="target"/>
<property name="files" value="file1,file2,file3,file4"/>
<mkdir dir="${targetdir}"/>
<foreach list="${files}" target="copy-rename" param="srcfile" trim="true">
<param name="srcdir" value="${srcdir}" />
<param name="targetdir" value="${targetdir}" />
</foreach>
</target>
<target name="copy-rename">
<var name="src-suffix" value="txt"/>
<var name="tgt-suffix" value="xls"/>
<copy file="${srcdir}/${srcfile}.${src-suffix}" tofile="${targetdir}/${srcfile}.${tgt-suffix}" />
</target>
<target name="build2" depends="taskdefs">
<property name="srcdir" value="src"/>
<property name="targetdir" value="target"/>
<mkdir dir="${targetdir}"/>
<foreach target="copy-rename2" param="srcfile">
<path id="srcfilepath">
<fileset dir="${srcdir}" casesensitive="yes">
<include name="*.txt"/>
</fileset>
</path>
<param name="targetdir" value="${targetdir}" />
</foreach>
</target>
<target name="copy-rename2">
<var name="basefile" value="" unset="true"/>
<basename property="basefile" file="${srcfile}" suffix=".txt"/>
<var name="tgt-suffix" value="xls"/>
<copy file="${srcfile}" tofile="${targetdir}/${basefile}.${tgt-suffix}" />
</target>
</project>
Can you slice it the other way and perform the renaming inside the fte:filecopy command? Looking at the IBM documentation, you can specify tasks to be carried out at the source or destination agents either before or after the copy, using presrc, postdst etc. This task could be an Ant task that does the renaming?

Resources