Check multiple properties are set and not blank (ant) - ant

I'm in a situation that involves running an ant build with optional parameters that are always specified but but not always defined, like so
ant -DBUILD_ENVIRONMENT=test -Dusername_ext= -Dconf.dir_ext= -Dcgi-dir_ext=
If the parameters are not given values on the command line they will be by loading a .properties file. I have the following code that will check if the property isset and is not blank.
<if>
<bool>
<and>
<isset property="username_ext"/>
<not>
<equals arg1="${username_ext}" arg2="" />
</not>
</and>
</bool>
<then>
<property name="username" value="${username_ext}" />
</then>
</if>
<property file="${BUILD_ENVIRONMENT}.properties" />
Since there are multiple properties it seems like I should write a target that will do the same actions for each property rather than repeat that code every time.
<antcall target="checkexists">
<property name="propname" value="username"/>
<property name="paramname" value="username_ext"/>
</antcall>
<antcall target="checkexists">
<property name="propname" value="conf.dir"/>
<property name="paramname" value="conf.dir_ext"/>
</antcall>
But AFAIK an antcall will not set a global property. How then can I write a target that will accept the name of a parameter it needs to check is set and is not blank, and then copy that in to a parameter that other targets can use?

Rather than using a target you could use a macro to conditionally set properties based on whether or not another property is set to a non-empty value.
<macrodef name="set-property">
<attribute name="name" />
<attribute name="if-property-isset" />
<attribute name="value" default="${#{if-property-isset}}" />
<sequential>
<condition property="#{name}" value="#{value}">
<and>
<isset property="#{if-property-isset}" />
<not>
<equals arg1="${#{if-property-isset}}" arg2="" />
</not>
</and>
</condition>
</sequential>
</macrodef>
<target name="test-macro">
<set-property name="username" if-property-isset="username_ext" />
<set-property name="conf.dir" if-property-isset="conf.dir_ext" />
<property name="conf.dir" value="default conf directory" />
<echo message="username = ${username}" />
<echo message="conf.dir = ${conf.dir}" />
</target>
Output
$ ant test-macro -Dusername_ext=jsmith -Dconf.dir_ext=
Buildfile: /your/project/build.xml
test-macro:
[echo] username = jsmith
[echo] conf.dir = default conf directory
BUILD SUCCESSFUL
Total time: 1 second
Alternate Property Value
This macro also allows you set the property to a different value than the one provided on the command line.
<target name="test-macro">
<set-property name="username" if-property-isset="username_ext"
value="It worked!" />
<set-property name="conf.dir" if-property-isset="conf.dir_ext" />
<property name="conf.dir" value="default conf directory" />
<echo message="username = ${username}" />
<echo message="conf.dir = ${conf.dir}" />
</target>
Output
$ ant test-macro -Dusername_ext=jsmith -Dconf.dir_ext=
Buildfile: /your/project/build.xml
test-macro:
[echo] username = It worked!
[echo] conf.dir = default conf directory
BUILD SUCCESSFUL
Total time: 1 second

Related

How to validate the parameter is uppercase in Jenkins using ANT

I have a Jenkins build and in that I am providing a build parameter with uppercase value (Build parameter should be uppercase always), by chance if a user is providing a lower case value, the build should gets failed.
Kindly provide inputs on how to check the provided Jenkins build parameter provided is uppercase or a lowercase using ANT. Thanks!
This can be accomplished using Ant's matches condition. A nested regexp is used in the below example to avoid repetition.
<target name="check-uppercase">
<property name="test1" value="lowercase" />
<property name="test2" value="MixedCase" />
<property name="test3" value="UPPERCASE" />
<regexp id="all.uppercase" pattern="^[A-Z]+$" />
<condition property="test1.isUppercase" value="true" else="false">
<matches string="${test1}">
<regexp refid="all.uppercase" />
</matches>
</condition>
<condition property="test2.isUppercase" value="true" else="false">
<matches string="${test2}">
<regexp refid="all.uppercase" />
</matches>
</condition>
<condition property="test3.isUppercase" value="true" else="false">
<matches string="${test3}">
<regexp refid="all.uppercase" />
</matches>
</condition>
<echo message="test1 uppercase? ${test1.isUppercase}" />
<echo message="test2 uppercase? ${test2.isUppercase}" />
<echo message="test3 uppercase? ${test3.isUppercase}" />
</target>

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>

ant: condition command does not find pattern when pattern is a file path

I am using the following code with the idea of finding a file in a directory that is also part of a list in a file:
<loadfile property="ReportFileContent" srcFile="${ReportFile}"/>
<for param="file">
<path>
<fileset dir="${MainDir}" includes="**/**"/>
</path>
<sequential>
<basename file="#{file}" property="#{file}" />
<condition property="found-file${index2}">
<matches pattern="#{file}" string="${ReportFileContent}"/>
</condition>
<if>
<isset property="found-file${index2}"/>
<then>
<echo message=" Found file #{file}" level="warning" />
</then>
<else>
<echo message="Not Found file #{file}" level="warning" />
</else>
</if>
<math result="index2" operand1="${index2}" operation="+" operand2="1" datatype="int" />
</sequential>
</for>
The command is not working though as it is not finding the file that is available in ${ReportFileContent}.
The content of the ReportFileContent property is the following:
c:\___tools\test\file1.txt;2
c:\___tools\test\file2.txt;2
c:\___tools\test\file3.txt;2
Any idea why the condition is not working correctly?
Thanks
Tony

How to check if a property has value in Ant

I have an Ant XML file which I use for build.
I have 3 properties. I want to break the build if these properties does not contain any value. Also I want to break the build if the value is empty.
How can I do this in Ant?
I a using Ant and not Ant-contrib.
You can use conditions using the <fail> task:
<fail message="Property "foo" needs to be set to a value">
<condition>
<or>
<equals arg1="${foo}" arg2=""/>
<not>
<isset property="foo"/>
</not>
</or>
</condition>
This is equivalent to saying if (not set ${foo} or ${foo} = "") is pseudocode. You have to read the XML conditions from the inside out.
You could have used the <unless> clause on the <fail> task if you only cared whether or not the variable was set, and not whether it has an actual value.
<fail message="Property "foo" needs to be set"
unless="foo"/>
However, this won't fail if the property is set, but has no value.
There's a trick that can make this simpler
<!-- Won't change the value of `${foo}` if it's already defined -->
<property name="foo" value=""/>
<fail message="Property "foo" has no value">
<condition>
<equals arg1="${foo}" arg2=""/>
</condition>
</fail>
Remember that I can't reset a property! If ${foo} already has a value, the <property> task above won't do anything. This way, I can eliminate the <isset> condition. It might be nice since you have three properties:
<property name="foo" value=""/>
<property name="bar" value=""/>
<property name="fubar" value=""/>
<fail message="You broke the build, you dufus">
<condition>
<or>
<equals arg1="${foo}" arg2=""/>
<equals arg1="${bar}" arg2=""/>
<equals arg1="${fubar}" arg2=""/>
</or>
</condition>
</fail>
Building on the other answers, this is my preferred form, as a Macro:
<!-- Macro to require a property is not blank -->
<macrodef name="prop-require">
<attribute name="prop"/>
<sequential>
<fail message="Property "#{prop}" must be set">
<condition>
<not>
<isset property="#{prop}"/>
</not>
</condition>
</fail>
<fail message="Property "#{prop}" must not be empty">
<condition>
<equals arg1="${#{prop}}" arg2=""/>
</condition>
</fail>
</sequential>
</macrodef>
To Be used as:
<target name="deploy.war" description="Do the war deployment ;)">
<prop-require prop="target.vm" />
<prop-require prop="target.vip" />
<!-- ... -->
For brevity you can collapse the two fail elements into one by using an <or>, but I prefer my error messages to treat me like I cannot think for myself ;)
You could try using conditions... or creating a target with unless
With Ant addon Flaka you may use patterns like :
<property name="foo" value="bar"/>
...
<fl:unless test="has.property.foo">
...
</fl:unless>
...
<fl:when test="has.property.foo">
...
</fl:when>
Concrete check for emptyness :
<fl:when test=" empty '${foo}' ">
<fail message="Houston we have a problem!!"/>
</fl:when>
A complete example, also using some equals check with 'eq' (opposite would be 'neq'):
<project xmlns:fl="antlib:it.haefelinger.flaka">
<!-- some if/then/else construct -->
<fl:choose>
<!-- if -->
<when test=" '${buildtype}' eq 'prod' ">
<!-- then -->
<echo>..starting ProductionBuild</echo>
</when>
<when test=" '${buildtype}' eq 'test' ">
<!-- then -->
<echo>..starting TestBuild</echo>
</when>
<!-- else -->
<otherwise>
<fl:unless test="has.property.dummybuild">
<fail message="No valid buildtype !, found => '${buildtype}'"/>
</fl:unless>
<echo>.. is DummyBuild</echo>
</otherwise>
</fl:choose>
</project>
output with ant -f build.xml -Dbuildtype=prod or
ant -f build.xml -Dbuildtype=prod -Ddummybuild=whatever
[echo] ..starting ProductionBuild
output with typo => ant - build.xml -Dbuildtype=testt
BUILD FAILED
/home/rosebud/workspace/AntTest/build.xml:21: No valid buildtype !, found => 'testt'
output with ant -f build.xml -Ddummybuild=whatever
[echo] .. is DummyBuild
I'm on an older version of Ant, so isset wasn't available. Instead I used the following notation with the double $ in the equals.
<target name="xxx">
<echo message="${contextRoot}" />
<if>
<!-- check if the contextRoot property is defined. -->
<equals arg1="${contextRoot}" arg2="$${contextRoot}" />
<then>
<!-- it isn't set, set a default -->
<property name="contextRoot" value="/WebAppCtx" />
</then>
</if>
<echo message="${contextRoot}" />
</target>
Try this.
<condition property="isPropertySet" value="true" else="false">
<and>
<isset property="my_property"/>
<length string="${my_property}" trim="true" when="greater" length="0"/>
</and>
</condition>
<fail unless="isPropertySet" message="The property my_property is not set."/>
Since Ant 1.9.1, it is possible to add if and unless attributes on all
tasks and nested elements using special namespaces.
In order to use this feature you need to add the following namespace declarations
xmlns:if="ant:if"
xmlns:unless="ant:unless"
The if and unless namespaces support the following conditions:
true - true if the value of the attribute evaluates to true
blank - true if the value of the attribute is null or empty
set - true if the specified property is set
Sample:
<project name="tryit"
xmlns:if="ant:if"
xmlns:unless="ant:unless">
<exec executable="java">
<arg line="-X" if:true="${showextendedparams}"/>
<arg line="-version" unless:true="${showextendedparams}"/>
</exec>
<condition property="onmac">
<os family="mac"/>
</condition>
<echo if:set="onmac">running on MacOS</echo>
<echo unless:set="onmac">not running on MacOS</echo>
</project>
From Ant manual "If And Unless"

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