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
Related
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>.
My xml does not work:
When I run in command line
ant compile -Dmodules=a,b,c
My build file need to count how many modules in modules parameters, compile them one by one using for loop
<target name="count_modules">
<resourcecount property="count">
<tokens>
<concat>
<filterchain>
<tokenfilter>
<stringtokenizer delims=","/>
</tokenfilter>
</filterchain>
<propertyresource name="modules" />
</concat>
</tokens>
</resourcecount>
<echo message="count is ${count}" />
</target>
count will always return 1
[echo] count is 1
The propertyresource will return a single resource to the concat task which is designed to act on resources like files.
This complex piece of logic is best replaced by an in-line script.
<project name="myproject" default="count_modules">
<property name="modules" value="a,b,c"/>
<target name="count_modules">
<script language="javascript"><![CDATA[
modules = project.getProperty("modules");
project.setProperty("count", modules.split(",").length);
]]></script>
<echo message="Number of modules: ${count}"/>
</target>
</project>
Running sub-module builds
The for task is not part of core ant, it's part of an extension called ant-contrib. My advice is to use the subant task when invoking sub-module builds. The following answer has some simple and advanced examples of its use:
Ant Script to Automate the build process
Let me first provide the background of the problem I'm facing.
I have a directory structure as below.
c:\myDirectory
c:\myDirectory\Project1
c:\myDirectory\Scripts
Under the c:\myDirectory\Scripts there is a script that download the source code (from svn) and creates the c:\myDirectory\Project1 directory.
I have another ant scripts ( c:\myDirectory\Scripts**compile-source.xml ) that compiles the Project1
from an ant script build.xml that is downloaded to c:\myDirectory\Project1
Snippet for c:\myDirectory\Scripts\compile-source.xml
<project name="compile" default="buildAll" basedir=".">
<property file=".\build.properties">
</property>
.......
<import file="${project.home.path}/${project.name}/build.xml"/>
<target name="buildAll">
<antcall target="jar-pack"/>
</target>
</project>
Snippet for c:\myDirectory\Project1\build.xml.
<project name="CommonFeatures" default="jar-pack" basedir=".">
<description>
A build file for the Common Features project
</description>
....
</project>
Note that the basedir for the project is set as "." for both the above ant scripts.
When I execute the script c:\myDirectory\Scripts\compile-source.xml from the c:\myDirectory\Scripts directory the target "jar-pack" present in the c:\myDirectory\Project1\build.xml gets executed.
However, the problem is that basedir attribude in build.xml ( basedir="." ) is the current working directory and in this case its c:\myDirectory\Scripts. Hence the script build.xml errors out since the basedir for build.xml is expected to be c:\myDirectory\Project1. The build.xml script would have worked, if basedir="." were set to "c:\myDirectory\Project1", but unfortunately build.xml file comes from the source code that is downloaded and I'm unable to edit.
So here's my question, Is it possible to do any of the following.
Override the value of the attribude basedir="." in build.xml when the is done in c:\myDirectory\Scripts\compile-source.xml ?
Is it possible to change the basedir in build.xml by any other mechanism so that the script c:\myDirectory\Project1\build.xml is executed under directory c:\myDirectory\Project1 ?
Any other way to resolve this issue?
Any help from Ant experts to overcome this issue is highly appreciated.
You can update basedir using subant task. Check this answer
Create the following build.xml file (assuming it is in Z:/any/folder):
<?xml version="1.0" encoding="UTF-8"?>
<project name="project">
<target name="mytarget">
<subant target="debug">
<property name="basedir" value="X:/any/dir/with/project"/>
<fileset dir="Y:/any/folder/with" includes="build.xml"/>
</subant>
</target>
</project>
The you can execute ant mytarget from Z:/any/folder
You can specifically reference the location of your build file, which is described in this stack overflow thread. This would allow you to get and use the directory your build file resides in as a reference point.
For your case the usage of the subant or ant tasks may be better suited, but nevertheless...
You can (but you should know/consider the side-effects!) extend ant with the common ant-contrib task definitions and use the var task which is able to override properties. Make sure to use the latest version (> 1.0b3).
<!-- adjust to your path and include it somewhere at the beginning of your project file -->
<taskdef resource="net/sf/antcontrib/antlib.xml" classpath="lib/ant-contrib-1.0b3.jar" />
<!-- works e.g. for basedir = /foo/bar to update it to /foo/bar/.. ~ /foo -->
<var name="basedir" value="${basedir}/.." />
update: but one has to be careful, because this does not change . (current working directory) (so <property name="x" location="tmp" /> would be relative to . and not to basedir anymore ; update: setting basedir outside of ant or via <project basedir= also sets . to basedir!). Here is some test target proving the effect on both:
<target name="tst.dummy.basedir-override">
<!-- example output:
tst.dummy.basedir-override:
[echo] basedir before: basedir=D:\tst, '.'=D:\tst\.
[echo] updating it via 'var' to '..'
[echo] basedir now: basedir=D:\tst/.., '.'=D:\tst\.
-->
<property name="cur" location="." /> <!-- makes the relative path absolute -->
<echo message="basedir before: basedir=${basedir}, '.'=${cur}" />
<echo message="updating it via 'var' to '..'" />
<var name="basedir" value="${basedir}/.." />
<property name="cur2" location="." /> <!-- makes the relative path absolute -->
<echo message="basedir now: basedir=${basedir}, '.'=${cur2}" />
</target>
This question already has answers here:
How can I get the value of the current target ant?
(5 answers)
Closed 7 years ago.
How can an ANT task like this one:
<target name="mytask">
<echo>processing ${blabla}</echo>
</target>
print processing mytask ?
What should I replace blabla with ? Or this is actually even possible ?
This might work for you with vanilla Ant, if your version is recent enough to include javascript support.
<scriptdef name="currenttarget" language="javascript">
<attribute name="property"/>
<![CDATA[
importClass( java.lang.Thread );
project.setProperty(
attributes.get( "property" ),
project.getThreadTask(
Thread.currentThread( ) ).getTask( ).getOwningTarget( ).getName( ) );
]]>
</scriptdef>
<target name="foobar">
<currenttarget property="my_target" />
<echo message="${my_target}" />
</target>
The scriptdef sets up a task currenttarget that can be used to get the current target in a property, which you can then use as you see fit.
The task name is always output to console by Ant:
Here is an example:
<project default="test">
<target name="test" depends="mytask-silent, mytask-echo"/>
<target name="mytask-echo">
<echo>processing</echo>
</target>
<target name="mytask-silent"/>
</project>
Here is the output:
C:\tmp\ant>ant
Buildfile: c:\tmp\ant\build.xml
mytask-silent:
mytask-echo:
[echo] processing
test:
BUILD SUCCESSFUL
Total time: 0 seconds
The only way I know it do this is to write an ANT task or use a groovy script as follows:
<target name="mytask" depends="init">
<taskdef name="groovy" classname="org.codehaus.groovy.ant.Groovy" classpathref="build.path"/>
<groovy>
println "processing ${target}"
</groovy>
</target>
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.