String replacement using propertyregex - ant

Need your advice on string replacement using ant propertyregex function.
I actually need to manipulate classpath file content by replacing certain section of the folder to the new folder and and remove some folder paths.
The actual content of the class path looks like:
/usr/home/folder1/com/codahale/metrics/metrics-core/3.0.2/metrics-core-3.0.2.jar:/usr/home/folder1/javax/ws/rs/javax.ws.rs-api/2.0-m10/javax.ws.rs-api-2.0-m10.jar:/etc/lib/:/usr/java/lib
which should eventually look like
/var/home/newfolder/metrics-core-3.0.2.jar:/var/home/newfolder/javax.ws.rs-api-2.0-m10.jar:/etc/lib/:/usr/java/lib
Basically, whichever string contains /usr/home/folder1, the new folder name should be prefixed to the jar file. The classpath contains 500 different groupId, artifactId with /usr/home/folder1 as the base
I am trying to replace the string in the classpath property from /usr/home/folder1/ to /var/home/newfolder/ as below. Not sure if something is wrong here, but this totally doesn't work..
<target name="classpath-generate">
<propertyregex property="compileclasspath" input="${compileclasspath}" regexp="/usr/home/folder1/" replace="/var/home/newfolder/" defaultValue="${compileclasspath}"/>
</target>
Also, need some help on how to extract just the jar filename and prefix the new foldername.
Highly appreciate your input on this !!

No need for extra libraries like antcontrib or regular expressions.
Use ant api via script task with builtin JavaScript engine (since JDK 1.6.0_06) like that :
<project>
<property name="compileclasspath" value="/usr/home/folder1/com/codahale/metrics/metrics-core/3.0.2/metrics-core-3.0.2.jar:/usr/home:/usr/home/folder1/com/codahale/metrics/metrics-core/bla.jar"/>
<property name="replacefrom" value="/usr/home/folder1/com/codahale/metrics/metrics-core"/>
<property name="replaceto" value="/var/home/newfolder"/>
<echo>
$${compileclasspath} initial :
${compileclasspath}
</echo>
<script language="javascript">
//set newProperty
project.setProperty('foobar', project.getProperty('compileclasspath').replace(project.getProperty('replacefrom'), project.getProperty('replaceto')));
// overwrite existing property
project.setProperty('compileclasspath', project.getProperty('compileclasspath').replace(project.getProperty('replacefrom'), project.getProperty('replaceto')));
</script>
<echo>
$${compileclasspath} changed :
${compileclasspath}
new Property $${foobar} :
${foobar}
</echo>
</project>
output :
[echo] ${compileclasspath} initial :
[echo] /usr/home/folder1/com/codahale/metrics/metrics-core/3.0.2/metrics-core-3.0.2.jar:/usr/home:/usr/home/folder1/com/codahale/metrics/metrics-core/bla.jar
[echo]
[echo] ${compileclasspath} changed :
[echo] /var/home/newfolder/3.0.2/metrics-core-3.0.2.jar:/usr/home:/var/home/newfolder/bla.jar
[echo] new Property ${foobar} :
[echo] /var/home/newfolder/3.0.2/metrics-core-3.0.2.jar:/usr/home:/var/home/newfolder/bla.jar
[echo]
To extract the jar names you might use something like :
<project>
<property name="compileclasspath" value="/usr/home/folder1/com/codahale/metrics/metrics-core/3.0.2/metrics-core-3.0.2.jar:/usr/home:/usr/home/folder1/com/codahale/metrics/metrics-core/bla.jar"/>
<script language="javascript">
<![CDATA[
var cpitems = project.getProperty('compileclasspath').split(':');
var jars = "";
for (i=0; i < cpitems.length; i++) {
if(cpitems[i].split('/')[(cpitems[i].split('/')).length -1].endsWith('.jar'))
{
jars += cpitems[i].split('/')[(cpitems[i].split('/')).length -1] + ','
}
}
project.setProperty('cpjars', jars.substring(0, jars.length - 1));
]]>
</script>
<echo>$${cpjars} => ${cpjars}</echo>
</project>
output :
[echo] ${cpjars} => metrics-core-3.0.2.jar,bla.jar

Below is an Ant script that uses Ant's built-in file name mapping functionality:
build.xml
<project name="ant-classpath-mapper" default="run">
<property
name="original-classpath"
value="/usr/home/folder1/com/codahale/metrics/metrics-core/3.0.2/metrics-core-3.0.2.jar:/usr/home/folder1/javax/ws/rs/javax.ws.rs-api/2.0-m10/javax.ws.rs-api-2.0-m10.jar:/etc/lib/:/usr/java/lib"
/>
<target name="run">
<pathconvert property="modified-classpath">
<path>
<pathelement path="${original-classpath}"/>
</path>
<firstmatchmapper>
<regexpmapper
from="/usr/home/folder1/.*?([^/]+\.jar)$"
to="/var/home/newfolder/\1"
/>
<identitymapper/>
</firstmatchmapper>
</pathconvert>
<echo>${modified-classpath}</echo>
</target>
</project>
Output
run:
[echo] /var/home/newfolder/metrics-core-3.0.2.jar:/var/home/newfolder/javax.ws.rs-api-2.0-m10.jar:/etc/lib:/usr/java/lib
The <regexpmapper> matches the paths that start with "/usr/home/folder1".
The <regexpmapper> won't match paths such as "/etc/lib/" and "/usr/java/lib". The <firstmatchmapper> ensures that these paths will get matched by the following <identitymapper>.

Related

In Ant, how can I dynamically build a property that references a property file?

I am using Input tasks to collect specific property values and I want to concatenate those into one property value that references my properties file.
I can generate the format of the property but at runtime it is treated as a string and not a property reference.
Example properties file:
# build.properties
# Some Server Credentials
west.1.server = TaPwxOsa
west.2.server = DQmCIizF
east.1.server = ZCTgqq9A
Example build file:
<property file="build.properties"/>
<target name="login">
<input message="Enter Location:" addproperty="loc" />
<input message="Enter Sandbox:" addproperty="box" />
<property name="token" value="\$\{${loc}.${box}.server}" />
<echo message="${token}"/>
</target>
When I call login and provide "west" and "1" for the input values, echo will print ${west.1.server} but it will not retrieve the property value from the properties file.
If I hardcode the property value in the message:
<echo message="${west.1.server}"/>
then Ant will dutifully retrieve the string from the properties file.
How can I get Ant to accept the dynamically generated property value and treat it as a property to be retrieved from the properties file?
The props antlib provides support for this but as far as I know there's no binary release available yet so you have to build it from source.
An alternative approach would be to use a macrodef:
<macrodef name="setToken">
<attribute name="loc"/>
<attribute name="box"/>
<sequential>
<property name="token" value="${#{loc}.#{box}.server}" />
</sequential>
</macrodef>
<setToken loc="${loc}" box="${box}"/>
Additional example using the Props antlib.
Needs Ant >= 1.8.0 (works fine with latest Ant version 1.9.4)
and Props antlib binaries.
The current build.xml in official Props antlib GIT Repository (or here) doesn't work out of the box :
BUILD FAILED
Target "compile" does not exist in the project "props".
Get the sources of props antlib and unpack in filesystem.
Get the sources of antlibs-common and unpack contents to ../ant-antlibs-props-master/common
Run ant antlib for building the jar :
[jar] Building jar: c:\area51\ant-antlibs-props-master\build\lib\ant-props-1.0Alpha.jar
Otherwise get the binaries from MVNRepository or here
The examples in ../antunit are quite helpful.
For nested properties look in nested-test.xml
Put the ant-props.jar on ant classpath.
<project xmlns:props="antlib:org.apache.ant.props">
<!-- Activate Props antlib -->
<propertyhelper>
<props:nested/>
</propertyhelper>
<property file="build.properties"/>
<input message="Enter Location:" addproperty="loc" />
<input message="Enter Sandbox:" addproperty="box" />
<property name="token" value="${${loc}.${box}.server}"/>
<echo message="${token}"/>
</project>
output :
Buildfile: c:\area51\ant\tryme.xml
[input] Enter Location:
west
[input] Enter Sandbox:
1
[echo] TaPwxOsa
BUILD SUCCESSFUL
Total time: 4 seconds
Solution is :
Consider problem is this, where you want to achieve this :
<property name="prop" value="${${anotherprop}}"/> (double expanding the property)?
You can use javascript:
<script language="javascript">
propname = project.getProperty("anotherprop");
project.setNewProperty("prop", propname);
</script>
I gave it a try and this is working for me.

Excluding a directory and all subdirectories of it?

I have a Java application that I am packaging into a JAR. I created an Ant script to do that as I need to also add resources to the JAR (icons etc).
Now, I have libraries that I use in my project (Apache HttpClient and a JSON library). I also copy their contents into the JAR, as it is the simplest way.
My build file:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project default="create_run_jar"
name="Create Runnable Jar">
<target name="create_run_jar">
<jar destfile="C:/Users/Pietu1998/Documents/Java/Whatever.jar"
filesetmanifest="mergewithoutmain">
<manifest>
<attribute name="Main-Class"
value="net.pietu1998.whatever" />
<attribute name="Class-Path" value="." />
</manifest>
<fileset dir="C:/Users/Pietu1998/Documents/Java/Whatever/bin"
includes="*.class" />
<fileset dir="C:/Users/Pietu1998/Documents/Java/Whatever/res" />
<zipfileset excludes="META-INF/**"
src="C:/Users/Pietu1998/Documents/Java/Whatever/lib/commons-codec-1.6.jar" />
<zipfileset excludes="META-INF/**"
src="C:/Users/Pietu1998/Documents/Java/Whatever/lib/commons-logging-1.1.3.jar" />
<!-- More (like 5) JARs -->
</jar>
</target>
</project>
However, the libraries (JARs) have their own META-INF folders, and they have a lot of stuff in them; files like LICENSE, CONDITIONS and also a folder for Maven called maven.
The project is for personal use, so I just want to get rid of the unnecessary stuff. I have tried some ways to exclude all of the META-INF stuff, but something is always left behind.
excludes="META-INF" leaves everything.
excludes="META-INF/**" leaves the maven folder.
**/excludes="META-INF/**", see above.
I believe I could use a lot of include-excludes or patternsets, but it would lead to a lot of repeating.
Is there a way (not for this specific case) to exclude a folder (META-INF here) and all its contents including subdirectories, and preferably with not too much repeating (for a lot of libraries)?
Using excludes="META-INF/**"" works for me (Ant 1.9.3, Windows 7, JDK 1.7.0_51)
Some test after downloading original commons-logging-1.1.3.jar from here :
<project>
<zipfileset excludes="META-INF/**" src="c:/area51/commons-logging-1.1.3.jar" id="foobar"/>
<!-- for output line by line -->
<pathconvert property="foo" refid="foobar">
<map from="c:/area51/commons-logging-1.1.3.jar:" to="${line.separator}"/>
</pathconvert>
<echo>${foo}</echo>
</project>
output contains only classfiles under org/apache/commons/logging/.. :
[echo] commons-logging-1.1.3.jar
[echo] org/apache/commons/logging/Log.class;
[echo] org/apache/commons/logging/LogConfigurationException.class;
[echo] org/apache/commons/logging/LogFactory$1.class;
[echo] org/apache/commons/logging/LogFactory$2.class;
[echo] org/apache/commons/logging/LogFactory$3.class;
[echo] org/apache/commons/logging/LogFactory$4.class;
[echo] org/apache/commons/logging/LogFactory$5.class;
[echo] org/apache/commons/logging/LogFactory$6.class;
[echo] org/apache/commons/logging/LogFactory.class;
[echo] org/apache/commons/logging/LogSource.class;
[echo] org/apache/commons/logging/impl/AvalonLogger.class;
[echo] org/apache/commons/logging/impl/Jdk13LumberjackLogger.class;
[echo] org/apache/commons/logging/impl/Jdk14Logger.class;
[echo] org/apache/commons/logging/impl/Log4JLogger.class;
[echo] org/apache/commons/logging/impl/LogFactoryImpl$1.class;
[echo] org/apache/commons/logging/impl/LogFactoryImpl$2.class;
[echo] org/apache/commons/logging/impl/LogFactoryImpl$3.class;
[echo] org/apache/commons/logging/impl/LogFactoryImpl.class;
[echo] org/apache/commons/logging/impl/LogKitLogger.class;
[echo] org/apache/commons/logging/impl/NoOpLog.class;
[echo] org/apache/commons/logging/impl/ServletContextCleaner.class;
[echo] org/apache/commons/logging/impl/SimpleLog$1.class;
[echo] org/apache/commons/logging/impl/SimpleLog.class;
[echo] org/apache/commons/logging/impl/WeakHashtable$1.class;
[echo] org/apache/commons/logging/impl/WeakHashtable$Entry.class;
[echo] org/apache/commons/logging/impl/WeakHashtable$Referenced.class;
[echo] org/apache/commons/logging/impl/WeakHashtable$WeakKey.class;
[echo] org/apache/commons/logging/impl/WeakHashtable.class
Maybe you forgot to set the update attribute from your jar task to true :
<jar destfile=".." update="true" ..>
to overwrite some already existing jarfile with the same name !?
I found a way to do it with the stars.
<zipfileset excludes="META-INF/**/*" src="C:\library.jar" />
This excludes the META-INF folder and all its contents.
* means it excludes all files inside a folder, and
foo/**/bar means it excludes bar in any level inside foo.
So, foo/**/* excludes every file on every level inside foo.

How to create a Java file dynamically into certain package using Ant concat task?

I have an Ant script file in which I use concat task to create a Java cource file in the specified package which is defined in a properties file.
For example, I define the package name:
ma.package=com.my.package
In Ant script, I call:
<concat destfile="./${prject.root}/${ma.package}/MyClass.java">
However, MyClass.java was created in a subfolder com.my.package, instead of folder structure com\my\package. How to fix it?
I use Eclipse Helios under Windows XP.
You can use a PathConvert with a nested UnpackageMapper to convert the package name to a path. For example:
<project default="test">
<property name="ma.package" value="com.my.package"/>
<target name="test">
<pathconvert property="ma.package.dir">
<path path="${ma.package}"/>
<unpackagemapper from="*" to="*"/>
</pathconvert>
<echo message="ma.package : ${ma.package}"/>
<echo message="ma.package.dir : ${ma.package.dir}"/>
</target>
</project>
The output is:
Buildfile: C:\tmp\ant\build.xml
test:
[echo] ma.package : com.my.package
[echo] ma.package.dir : C:\tmp\ant\com\my\package
So you could use the converted property value in your concat:
<concat destfile="${ma.package.dir}/MyClass.java">

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.

Is there any way to generate a guid in ANT?

I have an ant script to manage out build process. For WiX I need to produce a new guid when we produce a new version of the installer. Anyone have any idea how to do this in ANT? Any answer that uses built-in tasks would be preferable. But if I have to add another file, that's fine.
I'd use a scriptdef task to define simple javascript task that wraps the Java UUID class, something like this:
<scriptdef name="generateguid" language="javascript">
<attribute name="property" />
<![CDATA[
importClass( java.util.UUID );
project.setProperty( attributes.get( "property" ), UUID.randomUUID() );
]]>
</scriptdef>
<generateguid property="guid1" />
<echo message="${guid1}" />
Result:
[echo] 42dada5a-3c5d-4ace-9315-3df416b31084
If you have a reasonably up-to-date Ant install, this should work out of the box.
If you are using (or would like to use) groovy this will work nicely.
<project default="main" basedir=".">
<taskdef name="groovy" classname="org.codehaus.groovy.ant.Groovy"
classpath="lib/groovy-all-2.1.5.jar" />
<target name="main">
<groovy>
//generate uuid and place it in ants properties map
def myguid1 = UUID.randomUUID()
properties['guid1'] = myguid1
println "uuid " + properties['guid1']
</groovy>
<!--use the uuid from ant -->
<echo message="uuid ${guid1}" />
</target>
</project>
Output
Buildfile: C:\dev\anttest\build.xml
main:
[groovy] uuid d9b4a35e-4a75-454c-9f15-16b4b83bc6d0
[echo] uuid d9b4a35e-4a75-454c-9f15-16b4b83bc6d0
BUILD SUCCESSFUL
Using groovy 2.1.5 and ant 1.8

Resources