How to run shell script in Ant while doing search and replace? - ant

I am trying to achieve following with Ant.
I have a property file where we have list of tokens and their values.
This file is passed to Ant and it works great and does all the normal string replacement.
<copy todir="${target_direcotry}" overwrite="true">
<fileset dir="config">
<include name="*.change_me"/>
</fileset>
<filterset begintoken="<" endtoken=">">
<filtersfile file="${property_file}"/>
</filterset>
<mapper type="glob" from="*.change_me" to="*"/>
</copy>
Now If i have one of the token-value pair as follows :
TOKEN_VALUE1=`./run_me.ksh` in property file.
Target file test.xml.change_me has content :
You have <TOKEN_VALUE1> entries present !!!
With above code in build.xml and this new token in property file i am getting content of test.xml after running ant is :
You have `./run_me.ksh` entries present !!!
Output of script run_me.ksh will decide the value this token and give me output as follows :
Scenario 1 :
Run_me.ksh output: 10
Required content of file test.xml after execution :
"You have 10 entries present !!!"
Scenario 1 :
Run_me.ksh output: 20
Required content of file test.xml after execution :
"You have 20 entries present !!!"
Can i achieve this with Ant function/commands to run such shell script during replacements and how ?

It is technically possible to achieve this with Ant using FilterChain and FilterReader (specifically this one: ScriptFilter). However this is not so trivial and may have some portability issues. You may consider either writing your own task (or a specific FilterReader), or simply define a target that executes your shell script and store its result in a property and the use that property as value for token replacement.
Hereafter a snippet of how to launch a script in your token replacement task :
<copy todir="${target.dir}" overwrite="true">
<fileset dir="${config.dir}">
<include name="*.change_me" />
</fileset>
<filterchain>
<!-- replace token with value in the property file (i.e. `run_me.sh`) -->
<filterreader classname="org.apache.tools.ant.filters.ReplaceTokens">
<param type="propertiesfile" value="${token.properties}" />
</filterreader>
<tokenfilter>
<!-- split result along a specific pattern ('`' character for instance). -->
<stringtokenizer delims="`" delimsaretokens="false" />
<!-- apply a script filter to execute shell if token is script name -->
<scriptfilter language="javascript">
<![CDATA[
if( self.getToken().indexOf(".sh") != -1 ){
<!-- rely on ant Exec task to run script -->
exectask = project.createTask("exec");
exectask.setExecutable("sh");
exectask.createArg().setValue(self.getToken());
<!-- store output in an ant property : -->
exectask.setOutputproperty("result");
exectask.perform();
<!-- retrieve the ant property and use it to replace current token -->
self.setToken(project.getProperty("result"));
}
]]>
</scriptfilter>
</tokenfilter>
</filterchain>
<mapper type="glob" from="*.change_me" to="*" />
</copy>

Related

How to identify if a file is copied from a particular archive?

I am new to ANT.
I have a very specific scenario to handle in this:
STEP-1: I need to look for the pattern of filenames in certain ear files. If the pattern matches then I need to extract those files.
STEP-2: And if any file is extracted from a certain ear (similar to zip-file) file, then I need to search for another set of files, and copy those set of files too.
The case to handle is "How to identify if a file is copied from a particular archive" if found then proceed to step 2, else move to next archive.
I have achieved STEP-1 but no idea how to achieve step-2.
STEP-1
<!-- Set via arguments passed -->
<patternset id="pattern.needtocopy" includes="${needtocopyfile.pattern}" excludes="${ignore.pattern}">
</patternset>
<target name="get-binaries-from-baseline">
<for param="binary">
<path>
<fileset dir="${baseline.dir}/target/aaa/bbb/ccc" includes="*.ear" />
</path>
<sequential>
<basename file="#{binary}" property="#{binary}.basename" />
<unzip src="#{binary}" dest="${baseline.dir}">
<patternset refid="pattern.needtocopy" />
<mapper type="flatten" />
</unzip>
</sequential>
</for>
</target>
STEP-2:
????
Need help in this.
Thanks.
Well I resolved the same, using a groovy script based on the resources I could find.
<target name="findJars">
<zipfileset id="found" src="${ear-name}">
<patternset refid="${patternsetref}" />
</zipfileset>
<groovy>
project.references.found.each {
println it.name
println project.properties.'ear-name'
println project.properties.'dest.dir'
}
</groovy>
</target>
And then I added another task which takes this filename and ear-file-name as input and extracts the related jars based on file to search pattern.

Using ant to modify a file containing values in another file

I'm currently using ant to remove lines from a file if the line matches any of a list of email addresses, and output a new file without these email addresses as follows:
<copy file="src/emaillist.tmp2" tofile="src/emaillist.txt">
<filterchain>
<linecontains negate="true"><contains value="paultaylor#hotmail.com"/>
</linecontains>
<linecontains negate="true"><contains value="paultaylor2#hotmail.com"/>
</linecontains>
........
........
</filterchain>
</copy>
But I already have a file containing a list of the invalid email addresses (invalidemail.txt) I want to remove, so I want my ant file to read the list of invalid email addresses from this file rather than having to add a element for each email I don't want. Cannot work out how to do this.
Ant has some useful resource list processing tasks. Here's a 'prototype' solution.
Load the input and exclusion lists to two resource collections, tokenized by default on line breaks.
<tokens id="input.list">
<file file="src/emaillist.tmp2"/>
</tokens>
<tokens id="invalid.list">
<file file="invalidemail.txt"/>
</tokens>
Do some set arithmetic to produce a resource list of clean emails:
<intersect id="to_be_removed.list">
<resources refid="input.list"/>
<resources refid="invalid.list"/>
</intersect>
<difference id="clean.list">
<resources refid="input.list"/>
<resources refid="to_be_removed.list"/>
</difference>
Here's some diagnostics that may be useful:
<echo message="The input list is: ${ant.refid:input.list}" />
<echo message="Emails to be removed: ${ant.refid:to_be_removed.list}" />
<echo message="The clean list is: ${ant.refid:clean.list}" />
Use the pathconvert task to reformat the clean list into one entry per line:
<pathconvert property="clean.prop" refid="clean.list"
pathsep="${line.separator}" />
Finally, output to a file:
<echo file="src/emaillist.txt">${clean.prop}
</echo>

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 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