Ant filterset task not recursing - ant

Using Apache Ant 1.7.1
It looks like the Ant Filter task can't resolve the same property several times in one line when recurse is set to true. I can't find any mention of this in the Ant docs. Is this supposed to happen?
Using this ant build file:
<project basedir="." default="assemble" >
<macrodef name="copy-and-filter">
<sequential>
<copy tofile="to.txt" file="from.txt" overwrite="true">
<filterset recurse="true">
<filtersfile file="filters.properties"/>
</filterset>
</copy>
</sequential>
</macrodef>
<target name="assemble">
<copy-and-filter />
</target>
</project>
with these files:
from.txt:
I want my broker to be: #broker.url#
and my client to be: #client.url#
filters.properties:
myval=fish
broker.url=-#myval#-
client.url=#myval#-#myval#
I get the output to be:
i want my broker to be: -fish-
and my client to be: myval
and not what I expected which would be this:
i want my broker to be: -fish-
and my client to be: fish-fish
If I set recurse to false then I get the 'correct' behaviour.
i want my broker to be: -#myval#-
and my client to be: #myval#-#myval#
Why is this?

The recurse flag is intended to look for more tokens after an initial replace occurs, but it won't work if the same token is used again. The problem is that setting recurse=true causes an infinite loop. See the output from Ant:
Infinite loop in tokens. Currently known token tokens: [client.url, myval]
Problem token: #myval# called from #myval#
I don't think its possible to do what you want to be able to do using the filterset task. What are you using the to.txt file for? Is it to provide a system configuration file?

Related

In ant, how does one create and use a 'library' of targets?

To eliminate redundancy in my ant build.xml files, I decided out-factor the repeated targets into mytargets.xml file, publish it to to the artifact repository, and then import it in the following way:
<import>
<url url="http://mycompany.com/artifacts/mycompany.com/mytargets/1.2.3/mytargets-1.2.3.xml"/>
</import>
There are two things I don't like about this approach:
mytargets-1.2.3.xml never appears anywhere on the disk where I can easily look at it.
I absolutely need access to http://mycompany.com/artifacts in order to do anything in the project---it completely undermines offline work.
So, I tried creating a setup target to fetch a local copy of mytargets.xml and adjusted my <import> to use this local copy.
<import file="${basedir}/antlib/mytargets/mytargets.xml"/>
However, as you have probably guessed, I cannot even execute my setup my target after adjusting my <import> in this way because the file does not yet exist:
Buildfile: /home/me/myproject/build.xml
BUILD FAILED
/home/me/myproject/build.xml:265: Cannot find /home/me/myproject/antlib/mytargets/mytargets.xml imported from /home/me/myproject/build.xml
Adding optional="true" to the <import> only defers the problem to the first target that depends upon mytargets.xml.
I looked at https://ant.apache.org/manual/Types/antlib.html, but this approach does not appears to permit you to define a <target>.
So, how does someone share bits of ant XML across multiple projects? Am I already doing it the 'one true way'? Or, is there a better way?
If you're mainly just trying to avoid download the remote copy when you have a local copy already available, you can try something like this:
<condition property="mytargets.xml.available">
<available file="${basedir}/antlib/mytargets/mytargets.xml" />
</condition>
<target name="setup" unless="mytargets.xml.available">
<get
src="http://mycompany.com/artifacts/mycompany.com/mytargets/1.2.3/mytargets-1.2.3.xml"
dest="${basedir}/antlib/mytargets"
/>
</target>
<target name="main" depends="setup">
<import file="${basedir}/antlib/mytargets/mytargets.xml" />
...
</target>
So, it seems to me that <target> is inherently local and not intended for reuse. On the other hand, <macrodef> appears intended for reuse.
Here is the 'library', mymacros.xml:
<?xml version="1.0"?>
<antlib>
<macrodef name="mymacro">
...
</macrodef>
</antlib>
Here is the client, myproject/build.xml:
<?xml version="1.0"?>
<project name="myproject">
<target name="mytarget">
<mymacro/>
</target>
<taskdef file="mymacros.xml"/>
</project>
Unlike <import> and <include>, <taskdef> will not cause the build to fail immediately if mymacros.xml is missing, which gives you the opportunity to download it.

how to pass parameters to ant task's depends field

I have a build.xml that should receive dynamically parameters to the depends field.
I define this parameter in some other app.xml such as:
ops=op1, op2, op3,op4,op5,.... opn
then I import this app.xml into build.xml and want to use the parameter ops there.
<project name="Project" basedir="." default="help">
<target name="test" depends="{$ops}" description="executea series of commands in ant">
<echo message="batch operation job done. tasks = {$ops}"/>
</target>
</project>
How can I pass a parameter from one ant file to another?
The depends parameter does not take properties.
Ant uses a dependency matrix to determine what should be built and in what order. This matrix is calculated before any part of the build file itself is executed, so properties aren't even set when this is done.
What are you trying to accomplish? Maybe if we have a better idea what you want, we can help you with it. Ant isn't a scripting language like BASH or Python.
As already mentioned, you can't put properties into the Depends field. However, if you are setting a property, you can use it in the If field. Example
<project name="appProject">
<target name="test" depends="target1,target2,target3" description="execute series of commands"/>
<target name="target1" if="do.target1">
<echo message="Target1 executed." />
</target>
<target name="target2" if="do.target2">
<echo message="Target2 executed." />
</target>
<target name="target3" if="do.target3">
<echo message="Target3 executed." />
</target>
</project>
Then you set in your build.xml the given target flag do.target1, do.target2 or do.target3 and it gets executed. Basically what you wanted to have. In the If field properties are only checked for value. Also, you don't have to use the ${ } construction for the properties.

How to acess property within a property in ant

Hi all please give a look to this code
in my properties file i have
win-x86.pc-shared-location=E:\Ant_Scripts
Now below i am trying to call PrintInstallerName_build from my build.xml,while as PrintInstallerName_build is in test.xml. In build.xml file,${platform.id} has value=win-x86 in the calling target and in called target param1 also has value=win-x86
<target name="PrintInstallerName" >
<echo>PlatForm.Id====>${platform.id}</echo>
<ant antfile="test.xml" target="PrintInstallerName_build">
<property name="param1" value="${platform.id}"/>
</ant>
<target name="PrintInstallerName_build" >
<echo>${param1.pc-shared-location}</echo><!--${param1.pc-shared-location}-->
<echo>${param1}.pc-shared-location}</echo><!--win-x86.pc-shared-location-->
<echo>${win-x86.pc-shared-location}</echo><!--E:\\Ant_Scripts-->
</target>
as you can see only the last statement gives correct output but it is hardcoded,i want to use param1 and the output should be E:\\Ant_Scripts i tried to use $ and # but none works,may be i am doing wrong somewhere can someone help please,i am struck and tomorrow is its DOD.
See Nesting of Braces in the Properties page of the Ant Manual.
In its default configuration Ant will not try to balance braces in
property expansions, it will only consume the text up to the first
closing brace when creating a property name. I.e. when expanding
something like ${a${b}} it will be translated into two parts:
the expansion of property a${b - likely nothing useful.
the literal text } resulting from the second closing brace
This means you can't use easily expand properties whose names are
given by properties, but there are some workarounds for older versions
of Ant. With Ant 1.8.0 and the the props Antlib you can configure Ant
to use the NestedPropertyExpander defined there if you need such a
feature.
You can use <propertycopy> to make it happen.
Consider that you need to have the property value of ${propA${propB}}
Use ant tag of propertycopy as follows:
<propertycopy property="myproperty" from="PropA.${PropB}"/>
<echo >${myproperty}</echo>
This will echo the value of ${propA${propB}}
<target name="PrintInstallerName_process" >
<echo>${param1}</echo><!--win-x86-->
<macrodef name="testing">
<attribute name="v" default="NOT SET"/>
<element name="some-tasks" optional="yes"/>
<sequential>
<echo>Source Dir of ${param1}: ${#{v}}</echo><!-- Dir of Win-x86:E:\Ant_Scripts-->
<some-tasks/>
</sequential>
</macrodef>
<testing v="${param1}.pc-shared-location">
<some-tasks>
</some-tasks>
</testing>
</target>
this is the way it works and for me it works fine anyways #sudocode your tip took me there so thank you very much

ANT: How to read property setted in a foreach loop

Dear, I currently face some problem to retrieve the value of a property setted in a foreach loop. Maybe one of you could help me...
The purpose is to check if one file of a folder has been modified since the corresponding jar has been generated. This way I know if I have to generate the jar again.
What I do is to go through the folder with a foreach loop and if one file match my test, set a property to true.
The problem is that my variable doesn't seems to exist after my loop... Here is a simplified code example that has the same problem:
<target name="target">
<taskdef resource="net/sf/antcontrib/antlib.xml" classpath="${lib.dir}/ant-contrib.jar"></taskdef>
<foreach target="setVar" param="var" list="a,b"/>
<echo>myreturn in target: ${env.myreturn}</echo>
<property name="env.myreturn" value="c"/>
<echo>myreturn in second: ${env.myreturn}</echo>
</target>
<target name="setVar">
<property name="env.myreturn" value="${var}"/>
<echo>myreturn in setVar: ${env.myreturn}</echo>
</target>
The result of this code is:
target:
setVar:
[echo] myreturn in setVar: a
setVar:
[echo] myreturn in setVar: b
[echo] myreturn in target: ${env.myreturn}
[echo] myreturn in second: c
BUILD SUCCESSFUL
It seems that the variable is correctly set as it could be printed in the "setVar" target but no way to retrieve value from the calling target.
I also know it's not possible to assign a value to a property twice. But the problem doesn't even occurs... When it'll be the case I could add a check on the value of the property before to assign it to be sure it is not already initialized...
Do you have a clue on the way I can solve my problem ???
Many thanks in advance for your help :)
Try <for> task from ant-contrib instead of <foreach>. The <for> task takes advantage of Ant macro facility that came later. It works faster and is more flexible than the older <foreach> task. You are in the same project context when using <for>. That means properties set in the loop will be visible outside of the loop. Of course, normal rules for properties apply... you only get to set it once... unless you use <var> task from ant-contrib to overwrite or unset previously set properties.
Ah the joys of Ant hacking.
Not sure about your foreach problem, but can you not use the uptodate task for your requirement?
Even if I don't need it anymore thanks to sudocode, I found a solution for my question. Maybe it could be useful for someone else...
A collegue talked about the "antcallback" target of ant-contrib: it allows to return a result from a called target to the calling one. With a combination of "for" target and "antcallback" it is possible to do what I wanted to do:
<target name="target">
<taskdef resource="net/sf/antcontrib/antlib.xml" classpath="${lib.dir}/ant-contrib.jar"></taskdef>
<for param="file">
<path>
<fileset dir="../myDirectory" includes="**/*" />
</path>
<sequential>
<antcallback target="setVar" return="retValue">
<param name="file" value="#{file}"/>
</antcallback>
</sequential>
</for>
<echo>result: ${retValue}</echo>
</target>
<target name="setVar">
<property name="retValue" value="${file}"/>
</target>
"file" contains the name of the file in the directory. It is given to the called target as parameter with value "#{file}" ('#' necessary due to "for" target implementation).
At the end of the main target, ${retValue} contains the first value setted by the "setVar" target. No error is thrown when trying to set it multiple times, so it's not necessary to check if variable has already been instantiated before to set it in "setVar" target.
The <foreach> task uses the same logic as <antcall> under the covers, and any proprrties set inside a target invoked by <antcall> do not have scope beyond the execution of that target.
In other words, the env.myreturn property that you define in the setVar target is lost as soon as execution of that target completes.
This sort of scripting really isn't what Ant is designed for. The Ant-contrib library tries to patch up the holes, but it's still bending it way out of shape.
If you need to write such scripts, and want to use Ant tasks to achieve them, have a look at Gradle instead. It's a rather lovely blend of Groovy (for scripting) and Ant (for the tasks).
The other approaches here (<for>, <var>, <groovy>properties.put(....)</groovy>, <property>, <antcallback>) did not work with ANT 1.9.4, so I used the file system similar to this (pseudocode):
<target name="outer">
<for> <antcall target="inner" /> </for>
<loadproperties srcfile="tmpfile.properties" />
<echo message="${outerprop}" />
</target>
<target name="inner">
<!-- did not work: -->
<!--
<property name="outerprop" value="true" />
<var name="outerprop" value="true" />
<groovy>properties.put('outerprop','true')</groovy>
<antcallback target="setouterprop" />
-->
<echo message="outerprop=true" file="tmpfile.properties" />
</target>
Maybe the other approaches did not work because of my <antcall>, but I need it here. (outerprop is initially unset)

Ant - conditional statement

I'm using ant to build my app, and I want to have single process for dev/qa/prod versions of the app. I want to do be able to specify the build target from command line:
ant -Dbuildtarget=dev|qa|prod
and in build.xml check for the value of buildtarget and set an application specific base URL property based on the buildtarget specified by the user. I will subsequently set the correct runtime param using
<copy file="pre.app.properties" tofile="./app.properties" overwrite="true">
<filterset>
<filter token="BASE_URL" value="${baseurl}" />
</filterset>
</copy>
What I am stuck on is how to express this in and build.xml ?
if buildtarget=='dev'
baseurl="http://my_dev_url"
else if buildtarget=='qa'
baseurl="http://my_qa_url"
else if buildtarget=='prod'
baseurl="http://my_prod_url"
I've searched around, but this seems to be difficult to do in ant. Any ideas ?
When starting your ant script with ant -Dbuildtarget=dev|qa|prod it's as simple as =
<project >
<property name="baseurl" value="http://my_${buildtarget}_url"/>
<echo>$${baseurl} => ${baseurl}</echo>
</project>
The buildtarget property can be used as dynamic part of the baseurl property.Afterwards ${buildurl} can be used for further processing..
Perhaps you should try using the condition task of ant?

Resources