Structure ant projects - ant

I am currently writing an ant project xml file and I am looking for some hints and tips to improve the structure and readability of the project.
<target name="eatnutsOnClient" >
<monkey.eatnuts clientName="${clientName}" label="${nutLabel}" />
<if><not> <equals arg1="${returnCode}" arg2="0"/> </not><then>
<echo message="eatnuts-[${nutlabel}]_[${returnCode}]${line.separator}" file="${reachedFile}" append="true" />
</then></if>
</target>
<target name="eatnuts" depends="createClient,eatnutsOnClient,destroyClient"/>
In order to manage the return codes I would like to have the possibility to replace the full if section that I need to replicate over quite some targets by a sort of function which I can call to handle the returncode logic. I guess one option would be to create a target which only contains the if section and add it to the depend list of each task? Are there better ways?

An Ant <macrodef> provides a function-like way to share code:
<project name="ant-macrodef-echo" default="run">
<taskdef resource="net/sf/antcontrib/antlib.xml" />
<macrodef name="echo-macrodef">
<attribute name="returnCode"/>
<sequential>
<if>
<not>
<equals arg1="#{returnCode}" arg2="0"/>
</not>
<then>
<echo message="#{returnCode}" />
</then>
</if>
</sequential>
</macrodef>
<target name="run">
<echo-macrodef returnCode="42"/>
<echo-macrodef returnCode="0"/>
<echo-macrodef returnCode="-9"/>
</target>
</project>
Results:
run:
[echo] 42
[echo] -9

Related

how to ignore a failure ant task?

I have this ant script that is reading from a parameter a list of components and running other ant tasks (build.xml's):
<for list="${components.locations}" param="component" failonany="false">
<sequential>
<property name="#{component}" value="true"/>
<if>
<and>
<available file="${repository.location}/#{component}"/>
<available file="${repository.location}/${jars.location}"/>
</and>
<then>
<ant inheritAll="false" antfile="${repository.location}/#{component}/build.xml">
<!-- failonerror="false" -->
<property name="copy.libs" value="${copy.libs}"/>
<property name="repository.location" value="${repository.location}"/>
<property name="jars.location" value="${repository.location}/${jars.location}"/>
</ant>
</then>
</if>
</sequential>
</for>
The problem is if one component is failing, the script doesn't continue to next one.
I tried running with -k (-keep-going) argument but it doesn't help.
I found this property failonerror="false" but it's valid for "exec" tasks and couldn't integrate it with "ant" tasks or inside a "target".
Other direction was "failonany" property of the "for" but I didn't manage setting it up explicitly.
Can you please advice...
Thanks.
First of all, I would suggest deleting ant-contrib.jar and never looking back. Believe me, you will be doing yourself a favor.
You can use the subant task to iterate Ant builds on a set of directories or files. Simply define a dirset and pass any extra properties you need.
Instead of using ant-contrib's <if> block, use the standard target's if attribute to switch the entire target on or off. This is much safer and better practice.
<property name="repository.location" location="repository_location" />
<property name="jars.location" location="${repository.location}/jars" />
<property name="components" value="dir1,dir2,dir3" />
<target name="init">
<condition property="jars.available">
<available file="${jars.location}" />
</condition>
</target>
<target name="default" depends="init" if="jars.available">
<subant inheritall="false" failonerror="false">
<dirset id="components.dirs" dir="${repository.location}" includes="${components}" />
<property name="copy.libs" value="${copy.libs}" />
<property name="repository.location" value="${repository.location}" />
<property name="jars.location" value="${jars.location}" />
</subant>
</target>

How to check two properties in an if condition of an Ant task?

It is possible to execute an Ant target conditionally by specifying an if or unless clause. As far as I can see this clause accepts only one property. How can I check for two properties?
This is an example:
<project default="test">
<property name="a" value="true"/>
<property name="b" value="true"/>
<target name="test-a" if="a">
<echo>a</echo>
</target>
<target name="test-b" if="b">
<echo>b</echo>
</target>
<target name="test-ab" if="a,b">
<echo>a and b</echo>
</target>
<target name="test" depends="test-a,test-b,test-ab"/>
</project>
If I run it, the test-ab target generates no output:
$ ant -f target-if.xml
Buildfile: target-if.xml
test-a:
[echo] a
test-b:
[echo] b
test-ab:
test:
BUILD SUCCESSFUL
Total time: 0 seconds
How to specify an and expression for the two properties?
Unfortunately, no. From the ant Targets manual:
Only one propertyname can be specified in the if/unless clause. If you
want to check multiple conditions, you can use a dependend target for
computing the result for the check:
<target name="myTarget" depends="myTarget.check" if="myTarget.run">
<echo>Files foo.txt and bar.txt are present.</echo>
</target>
<target name="myTarget.check">
<condition property="myTarget.run">
<and>
<available file="foo.txt"/>
<available file="bar.txt"/>
</and>
</condition>
</target>
This is my example with the use of the condition element:
<project default="test">
<property name="a" value="true"/>
<property name="b" value="true"/>
<target name="test-a" if="a">
<echo>a</echo>
</target>
<target name="test-b" if="b">
<echo>b</echo>
</target>
<condition property="a-and-b">
<and>
<equals arg1="${a}" arg2="true"/>
<equals arg1="${b}" arg2="true"/>
</and>
</condition>
<target name="test-ab" if="a-and-b">
<echo>a and b</echo>
</target>
<target name="test" depends="test-a,test-b,test-ab"/>
</project>

how to write ant script to check a given string is a file or directory

how to use write ant script to check a file is a file or directory???
Given a string "C:\test\application\Services\Test"
I need to know this string
"C:\test\application\Services\Test"
is a file or directory,
I use following script to check, looks like cannot decide it is s a file or directory
<if>
<available file="${sourceFile}" />
<then>
<echo> It is a File ${sourceFile}</echo>
<copyfile src="${sourceFile}" dest="${targetFile}" />
<echo> Single file copied: sourceFile = ${sourceFile}</echo>
</then>
<else>
<if>
<available file="${dir}" type="dir" />
<then>
<echo> It is a Directory: ${sourceFile}</echo>
<copy todir="${targetFile}">
<fileset dir="${sourceFile}" />
</copy>
<echo> Single dir copied: sourceFile = ${sourceFile}</echo>
</then>
</if>
</else>
</if>
How to use ant to do it???
Thanks
The if/else tasks are not part of standard ANT (requires 3rd party jar)
Conditional execution of targets is performed as follows:
<project name="demo" default="process">
<property name="sourceFile" location="C:\test\application\Services\Test"/>
<available property="sourceFile.is.a.file" file="${sourceFile}" type="file"/>
<available property="sourceFile.is.a.dir" file="${sourceFile}" type="dir"/>
<target name="process" depends="process-file,process-dir"/>
<target name="process-dir" if="sourceFile.is.a.dir">
<echo message="${sourceFile} is a dir"/>
</target>
<target name="process-file" if="sourceFile.is.a.file">
<echo message="${sourceFile} is a file"/>
</target>
</project>

how to have an excludes list in ant build script?

I'm trying to add an excludes list to my ant build script. I have a property (lets call it build.excludes) which looks something like this:
build.excludes=MyApp,AnotherApp
in my build script I have an if statement similar to the following:
<for list="${appsList}" delimiter="" trim="true" param="currentApp" keepgoing="yes">
<sequential>
<if>
<!-- check if my current build item is in the build.excludes list -->
<then>
<!-- build of a project happens here -->
</then>
</if>
</sequential>
</for>
The only way I can think of doing it is to have a for loop to iterate over my build.excludes list and then do something (but I don't know where to put this for loop... perhaps in a macro?).
Thanks!
EDIT: Ant 1.6.5 and can't upgrade.
Looks like you're using the ant-contrib for task. if supports the same elements as the ant condition task, which has been around long enough to be in version 1.6.5.
Here's an example:
<property name="build.excludes" value="MyApp,AnotherApp" />
<property name="appsList" value="MyApp,ExtraApp" />
<for list="${appsList}" delimiter="," trim="true" param="currentApp" keepgoing="yes">
<sequential>
<echo message="Checking #{currentApp}" />
<if>
<not>
<contains string=",${build.excludes}," substring=",#{currentApp}," />
</not>
<then>
<echo message="Building #{currentApp}" />
</then>
<else>
<echo message="Not Building #{currentApp} - excluded" />
</else>
</if>
</sequential>
</for>
Gives:
[echo] Checking MyApp
[echo] Not Building MyApp - excluded
[echo] Checking ExtraApp
[echo] Building ExtraApp

Testing Macrodef Attribute without IF

I am attempting to remove all lines that begin with log if a macrodef attribute is set to prod (example below). I plan on using replaceregexp to remove all lines beginning with log. However, I am not sure how to test if an attribute is set to a specific value, besides using the if task. I would like to not introduce any non-core Ant tasks to perform this, but I can't come up with any other solutions. Do I have any other options besides using the if-task?
Thanks
<macrodef name="setBuildstamp">
<attribute name="platform" />
<sequential>
<if>
<equals arg1="platform" arg2="prod" />
<then>
<replaceregexp match="^log\(.*" value="" />
</then>
</if>
</sequential>
</macrodef>
You should use a reference to a parameter, like this #{platform}.
Also, your replaceregexp task is missing a few parameters.
I think that in your particular case it is better to use linecontainsregexp filter reader. Here is modified code (note negate argument to linecontainsregexp).
<macrodef name="setBuildstamp">
<attribute name="platform" />
<sequential>
<if>
<equals arg1="#{platform}" arg2="prod" />
<then>
<copy todir="dest-dir">
<fileset dir="src-dir"/>
<filterchain>
<linecontainsregexp
regexp="^log\(.*"
negate="true"
/>
</filterchain>
</copy>
</then>
</if>
</sequential>
</macrodef>
They may be a couple of ways to solve this, but none are as straightforward as using the ant-contrib element. I'm not sure if this will get you what you need for your application, but you could try the following:
Using conditional targets. If you can replace your macrodef with a target to call, this may work for you. Note that this will set the property globally, so it might not work for your application.
<target name="default">
<condition property="platformIsProd">
<equals arg1="${platform}" arg2="prod" />
</condition>
<antcall target="do-buildstamp" />
</target>
<target name="do-buildstamp" if="platformIsProd">
<echo>doing prod stuff...</echo>
</target>
Handle the 'else' case. If you need to handle an alternate case, you'll need to provide a few targets...
<target name="default">
<property name="platform" value="prod" />
<antcall target="do-buildstamp" />
</target>
<target name="do-buildstamp">
<condition property="platformIsProd">
<equals arg1="${platform}" arg2="prod" />
</condition>
<antcall target="do-buildstamp-prod" />
<antcall target="do-buildstamp-other" />
</target>
<target name="do-buildstamp-prod" if="platformIsProd">
<echo>doing internal prod stuff...</echo>
</target>
<target name="do-buildstamp-other" unless="platformIsProd">
<echo>doing internal non-prod stuff...</echo>
</target>
Using an external build file. If you need to make multiple calls with different values for your property, you could isolate this in another build file within the same project. This creates a bit of a performance hit, but you would not need the additional library.
in build.xml:
<target name="default">
<ant antfile="buildstamp.xml" target="do-buildstamp" />
<ant antfile="buildstamp.xml" target="do-buildstamp">
<property name="platform" value="prod" />
</ant>
<ant antfile="buildstamp.xml" target="do-buildstamp">
<property name="platform" value="nonprod" />
</ant>
</target>
in buildstamp.xml:
<condition property="platformIsProd">
<equals arg1="${platform}" arg2="prod" />
</condition>
<target name="do-buildstamp">
<antcall target="do-buildstamp-prod" />
<antcall target="do-buildstamp-other" />
</target>
<target name="do-buildstamp-prod" if="platformIsProd">
<echo>doing external prod stuff...</echo>
</target>
<target name="do-buildstamp-other" unless="platformIsProd">
<echo>doing external non-prod stuff...</echo>
</target>
Add ant-contrib to your project. Of course, if you can add a file to your project, the easiest thing would be to just add the ant-contrib.jar file. You could put it under a "tools" folder and pull it in using a taskdef:
<taskdef resource="net/sf/antcontrib/antlib.xml" classpath="${basedir}/tools/ant-contrib.jar" />
It looks like when you are building your project specifically for your Production environment - you are stripping out code you don't want to run in Production. Thus you are creating a different binary than what will run in your Dev or Testing environment.
How about using an environment variable or property file at run-time instead of build-time which determines whether or not logging happens? This way when you're having trouble in Production and you want to use the same exact binary (instead of determining the revision, checking out the code, rebuilding with a different environment flag) you just re-deploy it to your Dev or Test environment and turn on debugging in a properties file or environment variable?

Resources