Propertyregex to read a CSV line and assign fields to variables - ant

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

Related

Use Property value as a Key to read another value from Properties file

For Ant Script I have following myBuild.properties file
p.buildpath=c:\production\build
d.buildpath=c:\development\build
I wrote following build.xml:
<?xml version="1.0"?>
<project name="Test Project" default="info">
<property file="myBuild.properties"/>
<target name="info">
<input
message="Please enter the Server Name(p: production, d: development)?"
validargs="p,d"
addproperty="do.Server"
/>
<echo>Your Server type: ${do.Server} </echo>
<property name="myserv.buildpath" value="${do.Server}.buildpath" />
<property name="newProperty" value="${myserv.buildpath}" />
<echo>New Property Value: ${newProperty}</echo>
<!-- Following is an incorrect syntax -->
<echo>Build Path: ${${newProperty}}</echo>
</target>
</project>
When I run it using:
c:\>ant
I get following Output:
Buildfile: C:\build.xml
info:
[input] Please enter the Server Name(p: production, d: development)? (p, d)
p
[echo] Your Server type : p
[echo] New Property Value: p.buildpath
[echo] Build Path: ${${newProperty}}
BUILD SUCCESSFUL
Total time: 2 seconds
I want to echo the "Build Path" value same as p.buildpath value.
How is it possible to do in above case?
Taking the macrodef advice of Ant Faq you need no Ant addons like antcontrib, it's foolproof :
<project>
<macrodef name="cp_property">
<attribute name="name"/>
<attribute name="from"/>
<sequential>
<property name="#{name}" value="${#{from}}"/>
</sequential>
</macrodef>
<property file="myBuild.properties"/>
<input
message="Please enter the Server Name(p: production, d: development)?"
validargs="p,d"
addproperty="do.Server"
/>
<echo>Your Server type: ${do.Server}</echo>
<cp_property name="myserv.buildpath" from="${do.Server}.buildpath"/>
<echo>$${myserv.buildpath} : ${myserv.buildpath}</echo>
</project>
output :
[input] Please enter the Server Name(p: production, d: development)? (p, d)
p
[echo] Your Server type: p
[echo] ${myserv.buildpath} : c:/production/build
btw. Within your propertyfile you need to change your path separator to either unix style '/' (when on windows ant will handle it correctly) or double '\\' , otherwise with :
p.buildpath=c:\production\build
d.buildpath=c:\development\build
you'll get something like :
[input] Please enter the Server Name(p: production, d: development)? (p, d)
p
[echo] Your Server type: p
[echo] ${myserv.buildpath} : c:productionbuild
Also see https://stackoverflow.com/a/25681686/130683 for a very similar problem solved with Props antlib
You can do it without additional tools this way (edited/revised version):
<project name="Test Project" default="info">
<property file="myBuild.properties"/>
<target name="info">
<input
message="Please enter the Server Name(p: production, d: development)?"
validargs="p,d"
addproperty="do.Server"
/>
<echo>Your Server type: ${do.Server} </echo>
<property name="buildstage.production" value="p.buildpath" />
<property name="myserv.buildpath" value="${do.Server}.buildpath" />
<echo>New Property Value: ${myserv.buildpath}</echo>
<condition property="isProduction">
<equals arg1="${buildstage.production}" arg2="${myserv.buildpath}" />
</condition>
<antcall target="production" />
<antcall target="development" />
</target>
<target name="production" if="${isProduction}">
<echo>Build Path: ${p.buildpath}</echo>
</target>
<target name="development" unless="${isProduction}">
<echo>Build Path: ${d.buildpath}</echo>
</target>
</project>
Core idea is using a <conditional> task for a test on the selected property key and calling two tasks named production and development, where the first runs if the property isProduction is avaluated to true, the second if not (unlesss).
The condition is checked against a property named buildstage.production which is set to the property key p.buildpath as discriminator.
With "property key" I reference to the keys in the properties file. A hole bunch of "properties" here, could be confusing :)
By the way: you need to escape the backslashs in the property values as follows, otherwise they won't be echoed:
p.buildpath=c:\\production\\build
d.buildpath=c:\\development\\build
How it says in this post (http://ant.apache.org/faq#propertyvalue-as-name-for-property), without any external help, it's tricky.
With AntContrib (external task library) you can do <propertycopy name="prop" from="${anotherprop}"/>.
Anyway, there are other alternatives in the same post.
Hope it helps you.
There is a much simpler way that does not require macrodef or antcontrib:
<property name="a" value="Hello"/>
<property name="newProperty" value="a"/>
<loadresource property="b">
<propertyresource name="${newProperty}"/>
</loadresource>
<echo message="a='${a}'"/>
<echo message="b='${b}'"/>

ant-contrib for loop task fails

To search for multiple string in a fileset I try to use the ant-contrib for .. loop without success.
The documentation has the following simple example
<project name="testing" default="main" basedir=".">
<taskdef resource="net/sf/antcontrib/antcontrib.properties">
<classpath>
<pathelement location="/usr/share/java/ant-contrib-0.3.jar"/>
</classpath>
</taskdef>
<task name=main">
<echo message="The first five letters of the alphabet are:"/>
<for list="a,b,c,d,e" param="letter">
<sequential>
<echo>Letter #{letter}</echo>
</sequential>
</for>
</task>
</project>
That fails with
xTest.xml:12: Problem: failed to create task or type for
Cause: The name is undefined.
What's wrong with this?
Not sure if I need the taskdef for ant-contrib-0.3.jar
Note: ANT is running within Eclipse and it has version: "Apache ANT 1.8.2v20110505-1300"
You need to add the taskdef for ant-contrib-0.3.jar. The ant-contrib installation page tells you how to do so:
<taskdef resource="net/sf/antcontrib/antlib.xml">
<classpath>
<pathelement location="/usr/share/java/lib/ant-contrib-version.jar"/>
</classpath>
</taskdef>
Doing this, I have the following definitions in build.xml:
<project name="sample" basedir=".">
<taskdef resource="net/sf/antcontrib/antlib.xml">
<classpath>
<pathelement location="C:\\Users\\userdomains\\Downloads\\ant-contrib\\ant-contrib-1.0b3.jar"/>
</classpath>
</taskdef>
<echo message="The first five letters of the alphabet are:"/>
<for list="a,b,c,d,e" param="letter">
<sequential>
<echo>Letter #{letter}</echo>
</sequential>
</for>
</project>
which should now give:
Buildfile: c:\build.xml
[echo] The first five letters of the alphabet are:
[echo] Letter a
[echo] Letter b
[echo] Letter c
[echo] Letter d
[echo] Letter e
BUILD SUCCESSFUL
Total time: 0 seconds
Also the basic example works, the need as described with first posting
To search for multiple strings in a fileset
isn't solved. Here is what I have right now:
<loadfile property="dtdNAMES" srcFile="_dtdNAMES_1.txt"/>
<echo>.:${dtdNAMES}:.</echo>
<target name="main">
<for list="xyz.name,xyz.version,xyz.general,xyz.generalXXX,xyz.ok" param="search4" delimiter=",">
<!--for list="${dtdNames}" param="search4" delimiter=","-->
<!-- do the stuff here with param -->
<sequential>
<fileset id="existing" dir="../src">
<patternset id="files">
<include name="content/**/*.xul"/>
</patternset>
</fileset>
<resourcecount property="count">
<fileset id="matches" dir="../src">
<patternset refid="files" />
<contains text="#{search4}" />
</fileset>
</resourcecount>
<echo message="Found '#{search4}' in files : '${count}'"/>
</sequential>
</for>
</target>
This has 2 problems:
ad 1. the use of <for list=" .. csv .." just outputs a list with all files and one result like this:
[echo] Found 'xyz.name' in files : '3'
[echo] Found 'xyz.version' in files : '3'
[echo] Found 'xyz.general' in files : '3'
[echo] Found 'xyz.generalXXX' in files : '3'
The result '3' belongs to the very first loop with 'xyz.name', and yes there are 3 matches in all files. But the other strings have other matches in all files. There is no 'xyz.generalXXX' string in any file!
Question: How to get correct results for all strings in all files?
ad 2. For flexibility a final solution needs to replace the list string with a property like
<for list="${dtdNames}" param="search4" delimiter=",">
To the property ${dtdNames} is read a string from a srcFile (see above), and yes it's comma separated, no line breaks! And checked with a echo command.
But that reference fails with
[echo] Found '${dtdNames}' in files : '0'
So it seems the <for /> doesn't allow to work with property?
How to write that correctly?

trouble using replaceregexp when replacing string DIR location

I am having trouble in doing this.
there is 1 batch file with this line:
set TEST_DIR=C:\temp\dir1
I just want to set some new value to TEST_DIR
But, when I use in my ant script, it escapes forward slashes and gives this result:
set TEST_DIR=C:homedir2
Instead, I want to give it:
set TEST_DIR=C:\home\dir2
I am using this command:
<replaceregexp file="${MT_BATCH_FILE_LOCATION}\myfile.bat" match="TEST_DIR=C:\\temp\\dir1" replace="TEST_DIR=C:\home\dir2" byline="true" />
You can get the result you want by using this replace pattern:
replace="TEST_DIR=C:\\\\home\\\\dir2"
The reason is that you must escape the backslash once for the regex and once for Java - backslash is an escape character in both those contexts.
In answer to your subsequent questions in comments...
I expect the answer will be the same. You will need to double-escape the backslash in the value of ${new_loc}, i.e. use C:\\\\my_projcode not C:\my_projcode.
If new_loc is coming in as an environment variable, you could use the propertyregex task from ant-contrib to escape backslashes in the value:
<project default="test">
<!-- import ant-contrib -->
<taskdef resource="net/sf/antcontrib/antlib.xml">
<classpath>
<pathelement location="C:/lib/ant-contrib/ant-contrib-1.0b3.jar"/>
</classpath>
</taskdef>
<target name="test">
<!-- load environment variables -->
<property environment="env"/>
<!-- escape backslashes in new_loc -->
<propertyregex property="loc" input="${env.new_loc}" regexp="\\" replace="\\\\\\\\\\\\\\\\" />
<echo message="env.new_loc: ${env.new_loc}"/>
<echo message="loc: ${loc}"/>
<!-- do the replace -->
<replaceregexp file="test.bat" match="TEST_DIR=C:\\temp\\dir1" replace="TEST_DIR=${loc}\\\\home\\\\dir2" byline="true" />
</target>
Output:
c:\tmp\ant>set new_loc=c:\foo\bar
c:\tmp\ant>ant
Buildfile: c:\tmp\ant\build.xml
test:
[echo] new_loc: c:\foo\bar
[echo] env.new_loc: c:\foo\bar
[echo] loc: c:\\\\foo\\\\bar
BUILD SUCCESSFUL
Total time: 0 seconds
c:\tmp\ant>type test.bat
set TEST_DIR=c:\foo\bar\home\dir2
I have found another simple solution use replace instead of replaceregexp.
<replace file="${MT_BATCH_FILE_LOCATION}\myfile.bat"
token='TEST_DIR=C:\temp\dir1'
value='TEST_DIR=${new_loc}\home\dir2' />

how to escape backslash in ant

I am writing Ant scripts.
I have a property which has the value:
"C\:Program Files\\test1\\test2"
Is there a method in Ant to convert it to:
C:Program Files\test1\test2
You could use : http://ant-contrib.sourceforge.net/tasks/tasks/propertyregex.html
although I am not sure if this will do what you are asking for. Are the backslashes visible when you echo your property?
In any case to use the above task you will have to have ant-contrib installed and simply write a task like this :
<project name="test" default="build">
<!--Needed for antcontrib-->
<taskdef resource="net/sf/antcontrib/antlib.xml"/>
<target name="build">
<property name="path" value="C\:Program Files\\test1\\test2"/>
<echo message="Path with slashes : ${path}"/>
<propertyregex property="removed.backslash.property"
input="${path}"
global="true"
regexp="\\(\\|:)"
replace="\1"
/>
<echo message="Path with single slashes : ${removed.backslash.property}"/>
</target>
</project>
Output :
build:
[echo] Path with slashes : C\:Program Files\\test1\\test2
[echo] Path with single slashes : C:Program Files\test1\test2
In addition you could use any of the BSF languages :
http://ant.apache.org/manual/Tasks/script.html
provided you are using jre 1.6 and above.

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.

Resources