Ant strings comparison - ant

I have a script in ant, and I need to compare 2 strings lexicographically.
something like:
"1.2.3".compareTo("1.2.4")
I can't find a way to do so... Any ideas? I'm using ant 1.8, and ant-contrib.
Thanks

For this solution to work you will need to have your JAVA_HOME pointing to JRE 1.6 or later.
<project name="test" default="test">
<scriptdef name="compare" language="javascript">
<attribute name="lhs" />
<attribute name="rhs" />
<attribute name="property" />
<![CDATA[
var lhs = attributes.get("lhs");
var rhs = attributes.get("rhs");
project.setProperty(attributes.get("property"), lhs > rhs);
]]>
</scriptdef>
<target name="test">
<compare lhs="1.2.3" rhs="1.2.4" property="result"/>
<echo message="Result is : ${result}"/>
</target>
</project>
Output :
test:
[echo] Result is : false

Related

Remove last three character from ant property

I have an ant property which has value of the abcdefghijklmn
I want to remove the last three character from the property to abcdefghijk.
Please help.
Example
build:
[echo] x = abcdefghijklmn
[echo] y = abcdefghijk
build.xml
<project name="demo" default="build">
<property name="x" value="abcdefghijklmn"/>
<target name="build">
<script language="javascript">
project.setProperty("y", project.getProperty("x").substr(0,11));
</script>
<echo message="x = ${x}"/>
<echo message="y = ${y}"/>
</target>
</project>

How to omit a manifest attribute in Ant, if the value is empty?

This defines a manifest in Ant:
<manifest>
<attribute name="Main-Class" value="${main-class}"/>
</manifest>
How can I omit an empty main class entry in the manifest, when the property contains an empty string?
The only clean solution is to write the ant task twice: with and without manifest.
But if you don't want to do this, you could use following trick. Create two manifest files: one with main class and one without. The main class in the first manifest should be replaced with a placeholder (token) (e. g. #main-class#). Then you can do
<condition
property="manifest-file"
value="manifest-without-main-class.mf"
else="manifest-with-main-class.mf"
>
<length string="${main-class}" trim="true" length="0" />
</condition>
<copy file="manifest-template.mf" tofile="manifest-with-main-class.mf"/>
<replace
file="manifest-with-main-class.mf"
token="#main-class#"
value="${manifest-file}"
/>
<jar manifest="${manifest-file}" ...>
...
</jar>
With manifest-template.mf:
Manifest-Version: 1.0
Created-By: ...
Built-By: ...
Main-Class: #main-class#
And manifest-without-main-class.mf:
Manifest-Version: 1.0
Created-By: ...
Built-By: ...
You could also optionally update your manifest using 'update' mode:
<ac:if>
<length string="${module.dependencies}" trim="true" when="greater" length="0" />
<then>
<echo>Adding Dependencies (${module.dependencies}) to manifest file ${manifest-file}</echo>
<manifest file="${manifest-file}" mode="update">
<attribute name="Dependencies" value="${module.dependencies}" />
</manifest>
</then>
</ac:if>
This will only add the attribute you need when it's fill-in.
One option is to make use of the <macrodef> task's ability to pass arbitrary elements into it.
build.xml
The following script uses the third-party Ant-Contrib library.
<project name="ant-macrodef-with-optional-element" default="test" basedir=".">
<taskdef resource="net/sf/antcontrib/antlib.xml" />
<macrodef name="create-manifest">
<attribute name="file"/>
<attribute name="main-class"/>
<sequential>
<if>
<length string="#{main-class}" length="0"/>
<then>
<create-manifest-internal file="#{file}"/>
</then>
<else>
<create-manifest-internal file="#{file}">
<main-class-attribute>
<attribute name="Main-Class" value="#{main-class}"/>
</main-class-attribute>
</create-manifest-internal>
</else>
</if>
</sequential>
</macrodef>
<macrodef name="create-manifest-internal">
<attribute name="file"/>
<element name="main-class-attribute" optional="yes" />
<sequential>
<manifest file="#{file}">
<main-class-attribute/>
</manifest>
</sequential>
</macrodef>
<target name="test">
<create-manifest file="manifest-1-out.txt" main-class="com.example.Main"/>
<loadfile property="manifest-1-out-result" srcFile="manifest-1-out.txt"/>
<echo>manifest-1-out.txt:</echo>
<echo>${manifest-1-out-result}</echo>
<create-manifest file="manifest-2-out.txt" main-class=""/>
<loadfile property="manifest-2-out-result" srcFile="manifest-2-out.txt"/>
<echo>manifest-2-out.txt:</echo>
<echo>${manifest-2-out-result}</echo>
</target>
</project>
Output
test:
[echo] manifest-1-out.txt:
[echo] Manifest-Version: 1.0
[echo] Ant-Version: Apache Ant 1.8.2
[echo] Created-By: 1.7.0_40-b43 (Oracle Corporation)
[echo] Main-Class: com.example.Main
[echo]
[echo] manifest-2-out.txt:
[echo] Manifest-Version: 1.0
[echo] Ant-Version: Apache Ant 1.8.2
[echo] Created-By: 1.7.0_40-b43 (Oracle Corporation)
[echo]
The main drawback of this approach is the duplication of attributes needed to pass values from the caller to the outer macrodef and then to the inner macrodef.
My Ant skills are not great but I solved it this way. It's bit of a hack but it does use only one jar task. It puts in the Main-Class if required, otherwise adds a bland Comment entry.
build.xml
<project name="Builder">
<target name="default">
<build-me name="FooBar" main-class="foo.bar.Main"/>
<build-me name="BarFoo" />
</target>
<macrodef name="build-me">
<attribute name="name" />
<attribute name="main-class" default="" />
<sequential>
<tstamp>
<format property="now" pattern="E d MMM yyyy HH:mm:ss zzz" locale="en,UK"/>
</tstamp>
<!-- Compile and other steps ommited -->
<condition property="optional-main" value="Main-class" else="Comments">
<and>
<not>
<equals arg1="" arg2="#{main-class}" />
</not>
</and>
</condition>
<condition property="main-class" value="#{main-class}" else="no main class">
<and>
<not>
<equals arg1="" arg2="#{main-class}" />
</not>
</and>
</condition>
<jar destfile="#{name}.jar" basedir="build/#{name}">
<manifest>
<attribute name="Build-Date" value="${now}" />
<attribute name="${optional-main}" value="${main-class}" />
</manifest>
</jar>
</sequential>
</macrodef>
</project>
Result:
FooBar
MANIFEST.MF
Manifest-Version: 1.0
Ant-Version: Apache Ant 1.9.4
Build-Date: Tue 2 Aug 2016 11:02:46 BST
Main-class: foo.bar.Main
BarFoo
MANIFEST.MF
Manifest-Version: 1.0
Ant-Version: Apache Ant 1.9.4
Build-Date: Tue 2 Aug 2016 11:02:46 BST
Comments: no main class

Can one access the "name" of the enclosing target inside an ant macrodef?

It would be fantastic if I could do something like:
<macrodef name="process-target">
<attribute name="target" default="?enclosing.target.name?" />
...
</macrodef>
<target name="myTarget>
<process-target/>
</target>
Need to use a script to access the target name as follows:
<project name="demo" default="run1">
<macrodef name="process-target">
<sequential>
<script language="javascript">
project.setProperty("enclosing.target.name", self.getOwningTarget());
</script>
<echo message="${enclosing.target.name}"/>
</sequential>
</macrodef>
<target name="run1">
<process-target/>
</target>
<target name="run2">
<process-target/>
</target>
</project>
I am putting this here because I wanted to have an optional attribute in core ant.
<macrodef name="process-target">
<attribute name="target" default="_not_set_" />
<sequential>
<property name="_target_" value="#{target}" />
<script language="javascript">
if(project.getProperty("_target_") == "_not_set_") {
project.setProperty("_target_", self.getOwningTarget());
}
</script>
<echo message="${_target_}"/>
</sequential>
</macrodef>

Pretty-printing comma-separated list of strings in ant

I have a comma-separated string in an ant property, like this:
<property name="prop" value="a,b,c"/>
I would like to be able to print or log it like this:
Line 1: a
Line 2: b
Line 3: c
Doesn't sound like it should be too difficult, but I cannot figure out which ant components I should be putting together.
You can do this by using loadresource specifying the property value as a string resource. Now you can use a replaceregex filter to convert comma to newline.
<project default="test">
<property name="prop" value="a,b,c"/>
<target name="test">
<loadresource property="prop.fmt">
<string value="${prop}"/>
<filterchain>
<tokenfilter>
<replaceregex pattern="," replace="${line.separator}" flags="g"/>
</tokenfilter>
</filterchain>
</loadresource>
<echo message="${prop.fmt}"/>
</target>
</project>
The output is:
test:
[echo] a
[echo] b
[echo] c
The sample using Ant-Contrib Tasks
<taskdef resource="net/sf/antcontrib/antlib.xml"/>
<target name="test_split">
<property name="prop" value="a,b,c"/>
<for list="${prop}" param="letter">
<sequential>
<echo>#{letter}</echo>
</sequential>
</for>
</target>
The output is:
a
b
c
Another solution from here:
<scriptdef name="split" language="javascript">
<attribute name="value"/>
<attribute name="delimiter"/>
<attribute name="prefix"/>
<![CDATA[
values = attributes.get("value").split(attributes.get("delimiter"));
for(i=0; i<values.length; i++) {
project.setNewProperty(attributes.get("prefix")+i, values[i]);
}
]]>
</scriptdef>
<target name="test_split2">
<property name="prop" value="a,b,c"/>
<property name="prefix_str" value="Line_"/>
<split value="${prop}" delimiter="," prefix="${prefix_str}"/>
<echoproperties prefix="${prefix_str}"/>
</target>
The output is:
Ant properties
Tue Nov 22 17:12:55 2011
Line_0=a
Line_1=b
Line_2=c

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.

Resources