Ant-contrib foreach task - last value in the list - ant

I am currently executing a deploy using foreach ant task and the list has app server names (2 servers). There is a logic to wait for 2 mins (sleep) before moving to next app server.
The logic is working fine but the sleeps is applied even after the 2nd (last) app server, which is adding additional 2 minutes.
Is there a way to find that I have reached the last value in the list? so that I can exit without waiting.
sl.app.server.list=lstrdrasv01.str.staples.com
<foreach list="${sl.app.server.list}" param="sl.app.server" target="Storelocator.app.deploy" parallel="false" inheritall="true"/>
<sshexec host="${sl.app.server}"
username="${sl.app.username}"
password="${sl.as.password}"
trust="true"
output="output"
command="${start}"/>
<echo message="Waiting for Rolling deploy 2 min..."/>
<sleep seconds="120"/>
</target>
The sleep is executed 2 times where my list contains 2 servers, ideally speaking it should sleep only 1 time because there is no more servers after the 2nd server.

You should use for instead of foreach, as foreach uses antcall whereas for uses macrodef.
Note : works with antcontrib1.0b2, the latest release antcontrib1.0b3 has some bugs and will fail with :
BUILD FAILED
if doesn't support the nested "isgreaterthan" element.
A solution with for, no extra target needed, all work is done within sequential :
<project>
<!-- Import AntContrib -->
<taskdef resource="net/sf/antcontrib/antlib.xml" />
<property name="sl.app.server.list" value="server1,server2"/>
<!-- make use of jdk builtin javascript engine (since jdk 1.6.0_06) -->
<script language="javascript">
project.setProperty('serveritems', project.getProperty("sl.app.server.list").split(',').length);
</script>
<for list="${sl.app.server.list}" param="server" delimiter=",">
<sequential>
<echo>
Remaining $${serveritems} => ${serveritems}
process Server : #{server}
</echo>
<!--
NOTE : you have to use #{..} syntax here !
<sshexec host="#{server}" ...
-->
<if>
<isgreaterthan arg1="${serveritems}" arg2="1"/>
<then>
<echo>Waiting for Rolling deploy 2 min...</echo>
<sleep seconds="120"/>
</then>
</if>
<!-- this doesn't work correctly, note the output
Remaining ${serveritems} => -1 in second loop -->
<!--
<math result="serveritems" operand1="${serveritems}" operation="-" operand2="1" datatype="int"/>
-->
<!-- decrease serveritems -->
<script language="javascript">
project.setProperty('serveritems', parseInt(project.getProperty('serveritems')) - 1);
</script>
</sequential>
</for>
</project>
output with wrong working <math result ... /> for decrease serveritems :
[echo] Remaining ${serveritems} => 2
[echo] process Server : server1
[echo]
[echo] Waiting for Rolling deploy 2 min...
[echo] Remaining ${serveritems} => -1
[echo] process Server : server2
[echo]
output with script language javascript decreasing of serveritems :
[echo] Remaining ${serveritems} => 2
[echo] process Server : server1
[echo]
[echo] Waiting for Rolling deploy 2 min...
[echo] Remaining ${serveritems} => 1
[echo] process Server : server2
[echo]
Finally => Note the use of #{..} for parameter substition as in macrodef within sequential !

Related

Propertyregex to read a CSV line and assign fields to variables

I have a csv/txt file with below entries
abc,123
xyz,678
ijk,921
I'm trying to read the file through for loop and assign first element to var1, and second to var2
var1=abc
var2=123
I need to use these variables to perform some task and return back to the loop to read the next line of the file and assign new values.
Below is what I have, and I'm not able to assign the variable
<loadfile property="message" srcFile="test.txt" />
<target name="compile">
<for param="line" list="${message}" delimiter="${line.separator}">
<sequential>
<echo>#{line}</echo>
<propertyregex property="var1"
input="#line"
regexp="/^(.+?),(.+)/"
select="\1" />
<echo message="${var1}" />
</sequential>
</for>
</target>
Below is the output I get, showing no assignment of value to var1.
[echo] abc,123
[echo] ${var1}
[echo] xyz,678
[echo] ${var1}
[echo] ijk,921
[echo] ${var1}
You have a couple of errors, the below loop body works for me, see output at the end.
The first problem is that the #{line} parameter substitution syntax in the for loop uses an antcontrib macrodef, and the {} brakets are not optional - you had missed them out of the input="" attribute, whereas in the echo task you had included them.
The second problem is that you need not specify enclosing slashes /../ in the rexexp= attribute.
<echo>#{line}</echo>
<propertyregex property="var1"
override="yes"
input="#{line}"
regexp="^(.+?),(.+)"
select="\1"/>
<propertyregex property="var2"
override="yes"
input="#{line}"
regexp="^(.+?),(.+)"
select="\2"/>
<echo message="${var1},${var2}" />
Output:
[echo] abc,123
[echo] abc,123
[echo] xyz,678
[echo] xyz,678
[echo] ijk,921
[echo] ijk,921

Ant # variable not resolved

Here is my snippet of build.xml
<target name="compile">
<for param="rsync.destination.host" list="${file}" delimiter="${line.separator}">
<sequential>
<echo message="${rsync.ssh.user}##{rsync.destination.host}:${rsync.destination.base.dir}/"/>
</sequential>
</for>
</target>
I received following output,
compile:
[echo] root#{rsync.destination.host}:/tmp/
[echo] root#{rsync.destination.host}:/tmp/
BUILD SUCCESSFUL
Total time: 0 seconds
So here #{rsync.destination.host} variable is not interpreted because of double ## character. If I put space in between them
<echo message="${rsync.ssh.user}# #{rsync.destination.host}:${rsync.destination.base.dir}/"/>
then variable is resolved as expected.
compile:
[echo] root# server1:/tmp/
[echo] root# server2:/tmp/
BUILD SUCCESSFUL
Total time: 0 seconds
Since there is space in username and server it will through exception if we perform ssh here. Any idea how to solve this problem.
There are no variables in (core) ant, but properties and attributes.
#{foo} is the syntax for accessing the value of a macrodef attribute inside a macrodef. As antcontrib for task uses ant macrodef task under the hood it has the same syntax.
Try with :
...
<echo message="${rsync.ssh.user}###{rsync.destination.host}:${rsync.destination.base.dir}/"/>
...
means use 3x # instead of 2x #
The param #{rsync.destination.host} has to be masked with a second #, so in fact you need to use # 3 times.
You could put the literal at-sign in a property:
<property name="at" value="#"/>
<echo message="${rsync.ssh.user}${at}#{rsync.destination.host}:${rsync.destination.base.dir}/"/>

How do I prevent a dependency from executing in Ant?

When debugging a build.xml file, or an Ant task, I often want to execute one task without executing its dependencies. Is there a way to do this from the command line?
For example, with this build.xml file:
<?xml version="1.0" encoding="UTF-8"?>
<project>
<target name="A" />
<target name="B" depends="A" />
</project>
is there a command that will execute task B but not task A?
You can make execution of any target conditional on a property using if or unless.
<project default="B">
<target name="A" unless="no.a">
<echo>in A</echo>
</target>
<target name="B" depends="A" >
<echo>in B</echo>
</target>
</project>
Output with no condition specified:
$ ant
Buildfile: C:\Users\sudocode\tmp\ant\build.xml
A:
[echo] in A
B:
[echo] in B
BUILD SUCCESSFUL
Total time: 0 seconds
Output with condition specified on command line:
$ ant -Dno.a=any
Buildfile: C:\Users\sudocode\tmp\ant\build.xml
A:
B:
[echo] in B
BUILD SUCCESSFUL
Total time: 0 seconds
Notes:
Ant console output will show that the target was "hit" even if entry was blocked by the condition.
The if and unless conditions do not do boolean check. They just check whether the property is defined or not.
You will have to restructure your Ant script to achieve this:
<target name="B">
<if>
<isset property="some.property"/>
<then>
<antcall target="A">
</then>
</if>
<!-- execute task B here -->
</target>
If some.property is set, then it will first execute A followed by B. Otherwise, it will skip task A and execute B by itself.

Excluding a directory and all subdirectories of it?

I have a Java application that I am packaging into a JAR. I created an Ant script to do that as I need to also add resources to the JAR (icons etc).
Now, I have libraries that I use in my project (Apache HttpClient and a JSON library). I also copy their contents into the JAR, as it is the simplest way.
My build file:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project default="create_run_jar"
name="Create Runnable Jar">
<target name="create_run_jar">
<jar destfile="C:/Users/Pietu1998/Documents/Java/Whatever.jar"
filesetmanifest="mergewithoutmain">
<manifest>
<attribute name="Main-Class"
value="net.pietu1998.whatever" />
<attribute name="Class-Path" value="." />
</manifest>
<fileset dir="C:/Users/Pietu1998/Documents/Java/Whatever/bin"
includes="*.class" />
<fileset dir="C:/Users/Pietu1998/Documents/Java/Whatever/res" />
<zipfileset excludes="META-INF/**"
src="C:/Users/Pietu1998/Documents/Java/Whatever/lib/commons-codec-1.6.jar" />
<zipfileset excludes="META-INF/**"
src="C:/Users/Pietu1998/Documents/Java/Whatever/lib/commons-logging-1.1.3.jar" />
<!-- More (like 5) JARs -->
</jar>
</target>
</project>
However, the libraries (JARs) have their own META-INF folders, and they have a lot of stuff in them; files like LICENSE, CONDITIONS and also a folder for Maven called maven.
The project is for personal use, so I just want to get rid of the unnecessary stuff. I have tried some ways to exclude all of the META-INF stuff, but something is always left behind.
excludes="META-INF" leaves everything.
excludes="META-INF/**" leaves the maven folder.
**/excludes="META-INF/**", see above.
I believe I could use a lot of include-excludes or patternsets, but it would lead to a lot of repeating.
Is there a way (not for this specific case) to exclude a folder (META-INF here) and all its contents including subdirectories, and preferably with not too much repeating (for a lot of libraries)?
Using excludes="META-INF/**"" works for me (Ant 1.9.3, Windows 7, JDK 1.7.0_51)
Some test after downloading original commons-logging-1.1.3.jar from here :
<project>
<zipfileset excludes="META-INF/**" src="c:/area51/commons-logging-1.1.3.jar" id="foobar"/>
<!-- for output line by line -->
<pathconvert property="foo" refid="foobar">
<map from="c:/area51/commons-logging-1.1.3.jar:" to="${line.separator}"/>
</pathconvert>
<echo>${foo}</echo>
</project>
output contains only classfiles under org/apache/commons/logging/.. :
[echo] commons-logging-1.1.3.jar
[echo] org/apache/commons/logging/Log.class;
[echo] org/apache/commons/logging/LogConfigurationException.class;
[echo] org/apache/commons/logging/LogFactory$1.class;
[echo] org/apache/commons/logging/LogFactory$2.class;
[echo] org/apache/commons/logging/LogFactory$3.class;
[echo] org/apache/commons/logging/LogFactory$4.class;
[echo] org/apache/commons/logging/LogFactory$5.class;
[echo] org/apache/commons/logging/LogFactory$6.class;
[echo] org/apache/commons/logging/LogFactory.class;
[echo] org/apache/commons/logging/LogSource.class;
[echo] org/apache/commons/logging/impl/AvalonLogger.class;
[echo] org/apache/commons/logging/impl/Jdk13LumberjackLogger.class;
[echo] org/apache/commons/logging/impl/Jdk14Logger.class;
[echo] org/apache/commons/logging/impl/Log4JLogger.class;
[echo] org/apache/commons/logging/impl/LogFactoryImpl$1.class;
[echo] org/apache/commons/logging/impl/LogFactoryImpl$2.class;
[echo] org/apache/commons/logging/impl/LogFactoryImpl$3.class;
[echo] org/apache/commons/logging/impl/LogFactoryImpl.class;
[echo] org/apache/commons/logging/impl/LogKitLogger.class;
[echo] org/apache/commons/logging/impl/NoOpLog.class;
[echo] org/apache/commons/logging/impl/ServletContextCleaner.class;
[echo] org/apache/commons/logging/impl/SimpleLog$1.class;
[echo] org/apache/commons/logging/impl/SimpleLog.class;
[echo] org/apache/commons/logging/impl/WeakHashtable$1.class;
[echo] org/apache/commons/logging/impl/WeakHashtable$Entry.class;
[echo] org/apache/commons/logging/impl/WeakHashtable$Referenced.class;
[echo] org/apache/commons/logging/impl/WeakHashtable$WeakKey.class;
[echo] org/apache/commons/logging/impl/WeakHashtable.class
Maybe you forgot to set the update attribute from your jar task to true :
<jar destfile=".." update="true" ..>
to overwrite some already existing jarfile with the same name !?
I found a way to do it with the stars.
<zipfileset excludes="META-INF/**/*" src="C:\library.jar" />
This excludes the META-INF folder and all its contents.
* means it excludes all files inside a folder, and
foo/**/bar means it excludes bar in any level inside foo.
So, foo/**/* excludes every file on every level inside foo.

'make -n' equivalent for ant

According to the man page of make, -n option does the following job:
Print the commands that would be executed, but do not execute them.
I am looking for an option which acts the same in Apache Ant.
Horrific, but here it is. We can hack the targets at runtime using some code inside a <script> tag*. The code in do-dry-run below sets an unless attribute on each of your targets, and then sets that property so that none of them executes. Ant still prints out the names of targets that are not executed because of an unless attribute.
*(JavaScript script tags seem to be supported in Ant 1.8+ using the Oracle, OpenJDK and IBM versions of Java.)
<?xml version="1.0" encoding="UTF-8"?>
<project default="build">
<target name="targetA"/>
<target name="targetB" depends="targetA">
<echo message="DON'T RUN ME"/>
</target>
<target name="targetC" depends="targetB"/>
<target name="build" depends="targetB"/>
<target name="dry-run">
<do-dry-run target="build"/>
</target>
<macrodef name="do-dry-run">
<attribute name="target"/>
<sequential>
<script language="javascript"><![CDATA[
var targs = project.getTargets().elements();
while( targs.hasMoreElements() ) {
var targ = targs.nextElement();
targ.setUnless( "DRY.RUN" );
}
project.setProperty( "DRY.RUN", "1" );
project.executeTarget( "#{target}" );
]]></script>
</sequential>
</macrodef>
</project>
When I run this normally, the echo happens:
$ ant
Buildfile: build.xml
targetA:
targetB:
[echo] DON'T RUN ME
build:
BUILD SUCCESSFUL
Total time: 0 seconds
But when I run dry-run, it doesn't:
$ ant dry-run
Buildfile: build.xml
dry-run:
targetA:
targetB:
build:
BUILD SUCCESSFUL
Total time: 0 seconds
Ant has no dry-run option as make or maven have. But you could run the ant file step by step it in debugging mode under eclipse.
No I belive. There is no such way by default in Ant. And many unstisfying attempts you would find on google. But I have searched once and was unsuccessful.
It would be a useful feature, but not easily implemented.
Make and ANT are architecturally quite different. ANT doesn't run external OS commands, instead, most ANT "tasks" execute within the same Java thread.
It would be possible to emulate a "dry run" as follows:
<project name="Dry run" default="step3">
<target name="step1" unless="dry.run">
<echo>1) hello world</echo>
</target>
<target name="step2" depends="step1" unless="dry.run">
<echo>2) hello world</echo>
</target>
<target name="step3" depends="step2" unless="dry.run">
<echo>3) hello world</echo>
</target>
</project>
Running ANT as follows will print the target name but won't execute the enclosed tasks:
$ ant -Ddry.run=1
Buildfile: build.xml
step1:
step2:
step3:
BUILD SUCCESSFUL
Total time: 0 seconds
Create a special target in your buildscript that does some echoing only i.e. to check whether properties, path .. are resolved correctly.
see https://stackoverflow.com/a/6724412/130683 for a similar question answered.
For checking the details of your ant installation use ant -diagnostics

Resources