Is there a way to esacpe "#" in ant build - ant

I'm trying to echo a list of file names to a file with 2 # appended as prefix
E.g.
##some_filename
##some_filename2
Below is what I'm doing currently which works fine and outputs as expected except its done in a really dumb way.
<fileset id="filesref" dir="some_path" includes="**/*.txt"/>
<property name="files" refid="filesref"/>
<for list="${files}" delimiter=";" param="file" >
<sequential>
<echo file="somefile" message="#####{file}${line.separator}" append="true"/>
</sequential>
</for>
Is there a way to escape # sign so my message could prob look something like below (let's say if \ escapes)
<echo file="somefile" message="\#\##{file}${line.separator}"/>
Thanks in advance.

You only need to escape the #s in the antcontrib tasks where it has a special meaning (variable sigil). You can do this to make it a bit easier on the eye:
<property name="prefix" value="##" />
<for list="${files}" delimiter=";" param="file" >
<sequential>
<echo file="somefile" append="yes" message="${prefix}#{file}${line.separator}"/>
</sequential>
</for>

Related

Iterating over all filenames in a directory in Ant

I need to iterate over all files in a directory. But I need just the name of each file, not absolute paths. Here's my attempt using ant-contrib:
<target name="store">
<for param="file">
<path>
<fileset dir="." includes="*.xqm"/>
</path>
<sequential>
<basename file="#{file}" property="name" />
<echo message="#{file}, ${name}"/>
</sequential>
</for>
</target>
The problem is that ${name} expression gets evaluated only once. Is there another approach to this problem?
From ant manual basename : "When this task executes, it will set the specified property to the value of the last path element of the specified file"
Properties once set are immutable in vanilla ant, so when using basename task within for loop, the property 'name' holds the value of the first file.
Therefore antcontrib var task with unset="true" has to be used :
<target name="store">
<for param="file">
<path>
<fileset dir="." includes="*.xqm"/>
</path>
<sequential>
<var name="name" unset="true"/>
<basename file="#{file}" property="name" />
<echo message="#{file}, ${name}"/>
</sequential>
</for>
</target>
Alternatively use local task, when using Ant 1.8.x or later :
<target name="store">
<for param="file">
<path>
<fileset dir="." includes="*.xqm"/>
</path>
<sequential>
<local name="name"/>
<basename file="#{file}" property="name" />
<echo message="#{file}, ${name}"/>
</sequential>
</for>
</target>
Finally you may use Ant Flaka instead of antcontrib :
<project xmlns:fl="antlib:it.haefelinger.flaka">
<fl:install-property-handler />
<fileset dir="." includes="*.xqm" id="foobar"/>
<!-- create real file objects and access their properties -->
<fl:for var="f" in="split('${toString:foobar}', ';')">
<echo>
#{ format('filename %s, last modified %tD, size %s bytes', f.tofile.toabs,f.tofile.mtime,f.tofile.size) }
</echo>
</fl:for>
<!-- simple echoing the basename -->
<fl:for var="f" in="split('${toString:foobar}', ';')">
<echo>#{f}</echo>
</fl:for>
</project>
If you're averse to using the var task due to Ant's standard of property immutability, there's a way to do this by taking advantage of the fact that normal property references ("${}")and iterated property references ("#{}") can be nested within one another:
<target name="store">
<for param="file">
<path>
<fileset dir="." includes="*.xqm"/>
</path>
<sequential>
<basename file="#{file}" property="#{file}" />
<echo message="#{file}, ${#{file}}"/>
</sequential>
</for>
</target>
This way, you'll be creating a new property named after each file name.

How to pass multiple parameters to a target in Ant?

I have this dummy target:
<mkdir dir="${project.stage}/release
<war destfile="${project.stage}/release/sigma.war">
...
...
</war>
What I want to do is provide two parameters say "abc" & "xyz" which will replace the word release with the values of abc and xyz parameters respectively.
For the first parameter say abc="test", the code above will create a test directory and put the war inside it.Similarly for xyz="production" it will create a folder production and put the war file inside it.
I tried this by using
<antcall target="create.war">
<param name="test" value="${test.param.name}"/>
<param name="production" value="${prod.param.name}"/>
</antcall>
in the target which depends on the dummy target provided above.
Is this the right way to do this.I guess there must be some way to pass multiple parameters and then loop through the parameters one at a time.
unfortunately ant doesn't support iteration like for or foreach loops unless you are refering to files. There is however the ant contrib tasks which solve most if not all of your iteration problems.
You will have to install the .jar first by following the instructions here : http://ant-contrib.sourceforge.net/#install
This should take about 10 seconds. After you can simply use the foreach task to iterate through you custom list. As an example you can follow the below build.xml file :
<project name="test" default="build">
<!--Needed for antcontrib-->
<taskdef resource="net/sf/antcontrib/antcontrib.properties"/>
<target name="build">
<property name="test" value="value_1"/>
<property name="production" value="value_2"/>
<!--Iterate through every token and call target with parameter dir-->
<foreach list="${test},${production}" param="dir" target="create.war"/>
</target>
<target name="create.war">
<echo message="My path is : ${dir}"/>
</target>
</project>
Output :
build:
create.war:
[echo] My path is : value_1
create.war:
[echo] My path is : value_2
BUILD SUCCESSFUL
Total time: 0 seconds
I hope it helps :)
Second solution without using ant contrib. You could encapsulate all your logic into a macrodef and simply call it twice. In any case you would need to write the two parameters at some point in your build file. I don't think there is any way to iterate through properties without using external .jars or BSF languages.
<project name="test" default="build">
<!--Needed for antcontrib-->
<macrodef name="build.war">
<attribute name="dir"/>
<attribute name="target"/>
<sequential>
<antcall target="#{target}">
<param name="path" value="#{dir}"/>
</antcall>
</sequential>
</macrodef>
<target name="build">
<property name="test" value="value_1"/>
<property name="production" value="value_2"/>
<build.war dir="${test}" target="create.war"/>
<build.war dir="${production}" target="create.war"/>
</target>
<target name="create.war">
<echo message="My path is : ${path}"/>
</target>
</project>
I admit that I don't understand the question in detail. Is ${project.stage} the same as the xyz and abc parameters? And why are there two parameters xyz and abc mentioned, when only the word "release" should be replaced?
What I know is, that macrodef (docu) is something very versatile and that it might be of good use here:
<project name="Foo" default="create.wars">
<macrodef name="createwar">
<attribute name="stage" />
<sequential>
<echo message="mkdir dir=#{stage}/release " />
<echo message="war destfile=#{stage}/release/sigma.war" />
</sequential>
</macrodef>
<target name="create.wars">
<createwar stage="test" />
<createwar stage="production" />
</target>
</project>
The output will be:
create.wars:
[echo] mkdir dir=test/release
[echo] war destfile=test/release/sigma.war
[echo] mkdir dir=production/release
[echo] war destfile=production/release/sigma.war
Perhaps we can start from here and adapt this example as required.

How can I use something like an array or list in Ant?

I have a list of strings (e.g. "piyush,kumar") in an Ant script for which I want to assign piyush to var1 like <var name="var1" value="piyush"/> and kumar to var2 like <var name="var2" value="kumar"/>.
So far, I'm using a buildfile like the following:
<?xml version="1.0"?>
<project name="cutter" default="cutter">
<target name="cutter">
<for list="piyush,kumar" param="letter">
<sequential>
<echo>var1 #{letter}</echo>
</sequential>
</for>
</target>
</project>
I'm not sure how to progress this - any suggestions?
Here's an example using an ant-contrib variable and the math task:
<var name="index" value="1"/>
<for list="piyush,kumar" param="letter">
<sequential>
<property name="var${index}" value="#{letter}" />
<math result="index" operand1="${index}" operation="+" operand2="1" datatype="int" />
</sequential>
</for>
<echoproperties prefix="var" />
Output:
[echoproperties] var1=piyush
[echoproperties] var2=kumar
This is all very un-Ant like though - once you've set these what are you going to do with them?
You might consider using an Ant script task instead for this sort of non-declarative processing.

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

How to fail Ant build if errors are detected in either of two files?

I am using Ant to do a database build by basically using the exec task to run some SQL scripts.
However, there might be errors generated during the script execution (e.g. could not properly drop a connected user, etc) so I check for this by looking through two output log files.
Here's a snippet of the relevant target:
<target name="build">
<echo message="Add foo bar baz"/>
<exec executable="${db.sqlplus}">
</exec>
<echo message="Load x y z"/>
<exec executable="${db.sqlplus}" dir="foobar">
</exec>
<!--Check the log files here-->
<antcall target="check-log-file">
<param name="file.to.check" value="${output.log.1}"/>
</antcall>
<antcall target="check-log-file">
<param name="file.to.check" value="${output.log.2}"/>
</antcall>
<antcall target="fail-if-error"/>
</target>
<!--=============================================================================
Check the file named in the property file.to.check to see if there are errors.
The way this works is to find all lines containing the text "ERROR" and put
them into a separate file. Then it checks to see if this file has non-zero
length. If so, then there are errors, and it sets the property errors.found.
Then it calls the send-email target, which doesn't execute if the
errors.found property isn't set.
-->
<target name="check-log-file"
description="Checks the file (specified in ${file.to.check}) for errors">
<property name="file.errorcount" value="${file.to.check}.errorcount"
description="The file to hold the error lines"/>
<copy file="${file.to.check}" tofile="${file.errorcount}">
<filterchain>
<linecontains>
<contains value="ERROR"/>
</linecontains>
</filterchain>
</copy>
<condition property="errors.found" value="true">
<length file="${file.errorcount}" when="gt" length="0"/>
</condition>
<antcall target="check-log-file-send-email"/>
</target>
<!--=========================================================================
If there are any errors, send an email to let someone know
-->
<target name="check-log-file-send-email" if="errors.found"
description="Sends an email out if error detected">
<resourcecount property="error.count">
<tokens><!-- default tokenizer is a line tokenizer -->
<file file="${file.to.check}.errorcount"/>
</tokens>
</resourcecount>
<echo
message="Database build (${e1.codeline} - ${error.count} errors found..."/>
<antcall target="mail">
<param name="from-address" value="build"/>
<param name="to-list" value="myemail"/>
<param name="subject"
value="Automated database build error report for ${db.host}"/>
<param name="message"
value="See attached log file, ${error.count} error(s)found..."/>
<param name="attach" value="${file.to.check}"/>
</antcall>
</target>
<!--==========================================================================
Fails the database build if errors were detected.
-->
<target name="fail-if-error" if="errors.found">
<echo message="Errors found - setting database fail flag..."/>
<fail message="Errors detected during ${codeline} database build. Check logs."/>
</target>
When there are errors the build does not fail.
I think it's because the antcall task to check the logs does not return the property error.
Found back to the build target, so when fail-if-error is called, that property is unset.
Is that right?
Is there a way to set it up to fail properly?
The antcall will set the property in the scope of its execution, so when you get to your check it is not set. Instead try using a macrodef, this will run in the current scope and set the errors-found property in that scope so the later check can read it. You'd define the macrodef something like this:
<macrodef name="check-log-file">
<attribute name="fileToCheck"/>
<!--note attributes are referenced with an "#" rather than a "$" -->
<property name="file.errorcount" value="#{fileToCheck}.errorcount"/>
<copy file="#{fileToCheck}" tofile="${file.errorcount}">
...
</macrodef>
and call it like this:
<check-log-file fileToCheck="${output.log.1}"/>
<check-log-file fileToCheck="${output.log.1}"/>
Thanks to Rich Seller, who provided the idea of using a macrodef. The macrodef needed a little cleanup (property not allowed inside a macrodef, tasks need to be wrapped in a sequential tag) so I'm providing it here in full:
<macrodef name="check-log-file">
<attribute name="file.to.check"/>
<attribute name="file.errorcount" default="#{file.to.check}.errorcount" description="The file to hold the error lines"/>
<sequential>
<copy file="#{file.to.check}" tofile="#{file.errorcount}">
<filterchain>
<linecontains>
<contains value="ERROR"/>
</linecontains>
</filterchain>
</copy>
<condition property="errors.found" value="true">
<length file="#{file.errorcount}" when="gt" length="0"/>
</condition>
<antcall target="check-log-file-send-email">
<param name="file.to.check" value="#{file.to.check}"/>
</antcall>
</sequential>
</macrodef>
General log file checking with Errors and Warnings
Here is a general macrodef that can be used to scan files for issues. As long as you can write a regexp for the issue, it can check for it...
It can fail or not if the issue is found.
It summarises the issues found, writing them to the Ant output.
The files to scan can be indicated with wildcards.
Here are example calls to check log files for Oracle errors:
Fail on "SP2-" errors
Warn on "ORA-" errors
Warn on "ERROR:" text.
<check_for_errors file.to.check.dir="${buildlogs}" file.to.check.include="*.log" error.pattern="SP2-" />
<check_for_errors file.to.check.dir="${buildlogs}" file.to.check.include="*.log" error.pattern="ORA-" error.action="warn" />
<check_for_errors file.to.check.dir="${buildlogs}" file.to.check.include="*.log" error.pattern="ERROR:" error.action="warn" />
Here are example calls to check for unreplaced tokens in generated sql files, before execution:
<check_for_errors file.to.check.dir="${distdir}" file.to.check.include="**/\*.sql"
error.name="Token" error.pattern="^(?!--).+#[^# ]+#" error.display.find=".*(#[^# ]+#).*" error.display.show=" Token = '\1'"/>
<check_for_errors file.to.check.dir="${distdir}" file.to.check.include="**/*.sql"
error.name="Token" error.pattern="^(?!--).+#\$\{[^ }]+\}" error.display.find=".*(\$\{[^ }]+\}).*" error.display.show=" Token = '\1'"/>
Here is the macrodef:
<macrodef name="check_for_errors">
<attribute name="file.to.check.dir" default="." />
<attribute name="file.to.check.include" default="*.log" />
<attribute name="file.to.check.exclude" default="" />
<attribute name="error.pattern" default="ERROR" />
<attribute name="error.name" default="ERROR" />
<attribute name="error.action" default="fail" />
<attribute name="error.display.find" default="(.+)" />
<attribute name="error.display.show" default=" \1" />
<sequential>
<echo message="Excluding file ${buildlogfile}" level="debug" />
<for param="file.to.check.name">
<fileset dir="#{file.to.check.dir}">
<include name="#{file.to.check.include}"/>
<exclude name="#{file.to.check.exclude}"/>
<exclude name="${buildlogfile}"/>
<containsregexp expression="#{error.pattern}"/>
</fileset>
<sequential>
<echo message="ERROR: #{error.name} found in file '#{file.to.check.name}' :" level="warn" />
<concat>
<fileset file="#{file.to.check.name}" />
<filterchain>
<linecontainsregexp>
<regexp pattern="#{error.pattern}" />
</linecontainsregexp>
<replaceregex pattern="#{error.display.find}" replace="#{error.display.show}" />
</filterchain>
</concat>
<property name="error.check.foundvalues" value="true" />
</sequential>
</for>
<condition property="error.check.fail">
<and>
<matches string="#{error.action}" pattern="fail" />
<isset property="error.check.foundvalues" />
</and>
</condition>
<fail message="ERROR: Fix the above errors and try again. Exiting..." if="error.check.fail"/>
</sequential>
</macrodef>

Resources