ANT: How to read property setted in a foreach loop
Asked Answered
P

5

6

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

Prurigo answered 16/3, 2011 at 12:27 Comment(0)
P
3

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.

Prurigo answered 22/3, 2011 at 9:25 Comment(1)
does not work with ant 1.9.4 :-/ ... worked around it using the file system ... see my answerFlite
M
7

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.

Martino answered 17/3, 2011 at 2:12 Comment(1)
neither does work: (ant 1.9.4, <for>, <var>, <groovy>properties.put(....)</groovy>, <property>). I'll try accepted answer with <antcallback> and report back.Flite
K
3

Not sure about your foreach problem, but can you not use the uptodate task for your requirement?

Kirkuk answered 16/3, 2011 at 12:43 Comment(1)
Hoo many thanks for your answer!! I didn't found this target before and it works with a much more cleaner code than before!Prurigo
P
3

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.

Prurigo answered 22/3, 2011 at 9:25 Comment(1)
does not work with ant 1.9.4 :-/ ... worked around it using the file system ... see my answerFlite
R
1

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

Ratel answered 16/3, 2011 at 12:34 Comment(3)
I think that's incorrect. Properties set once anywhere are in scope everywhere else thereafter.Kirkuk
Thanks for your rapid answers! The stuff is that I'm quite limited in the libraries I can use... using new libraries like Gradle will necessitate agreement of a bunch of person :sPrurigo
Thanks for clarification. I was about to comment with apologies to you after checking antcall behaviour for myself...Kirkuk
F
0

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)

Flite answered 10/11, 2017 at 12:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.