Converting ../ in paths in Ant - ant

I have an Ant build that has this line in it:
WORKSPACE.dir = ${basedir}/../
I then have:
CORE_PROJECT.dir= ${WORKSPACE.dir}/UUI_Core
which means that I end up with paths like this:
C:\dev\workspaces\RTC\UUI_Core_ANT/..//UUI_Core
This works fine in almost all cases but I am trying to build a list of classes to be used in a build. At the moment I have this code:
<pathconvert
property="coreClasses"
pathsep=" "
dirsep="."
refid="coreSources">
<map from="C:\dev\workspaces\RTC\UUI_Core\src\" to="" />
<mapper>
<chainedmapper>
<globmapper from="*.mxml" to="*"/>
</chainedmapper>
<chainedmapper>
<globmapper from="*.as" to="*"/>
</chainedmapper>
</mapper>
</pathconvert>
Which does the job of removing the file location and jsut leaving the package structure. It is not very flexible though. I should be able to use CORE_PROJECT.dir here.
So, how can I convert
C:\dev\workspaces\RTC\UUI_Core_ANT/..//UUI_Core
to
C:\dev\workspaces\RTC\UUI_Core

WORKSPACE.dir = ${basedir}/../
This is not a valid Ant syntax.
To convert .. you should use location attribute of the <property> task instead of value. location substitutes the attribute value with an absolute path. In your case:
<property name="WORKSPACE.dir" location="${basedir}/.."/>
EDIT: I should add, always use location attribute when setting path-like properties.

Related

How to run shell script in Ant while doing search and replace?

I am trying to achieve following with Ant.
I have a property file where we have list of tokens and their values.
This file is passed to Ant and it works great and does all the normal string replacement.
<copy todir="${target_direcotry}" overwrite="true">
<fileset dir="config">
<include name="*.change_me"/>
</fileset>
<filterset begintoken="<" endtoken=">">
<filtersfile file="${property_file}"/>
</filterset>
<mapper type="glob" from="*.change_me" to="*"/>
</copy>
Now If i have one of the token-value pair as follows :
TOKEN_VALUE1=`./run_me.ksh` in property file.
Target file test.xml.change_me has content :
You have <TOKEN_VALUE1> entries present !!!
With above code in build.xml and this new token in property file i am getting content of test.xml after running ant is :
You have `./run_me.ksh` entries present !!!
Output of script run_me.ksh will decide the value this token and give me output as follows :
Scenario 1 :
Run_me.ksh output: 10
Required content of file test.xml after execution :
"You have 10 entries present !!!"
Scenario 1 :
Run_me.ksh output: 20
Required content of file test.xml after execution :
"You have 20 entries present !!!"
Can i achieve this with Ant function/commands to run such shell script during replacements and how ?
It is technically possible to achieve this with Ant using FilterChain and FilterReader (specifically this one: ScriptFilter). However this is not so trivial and may have some portability issues. You may consider either writing your own task (or a specific FilterReader), or simply define a target that executes your shell script and store its result in a property and the use that property as value for token replacement.
Hereafter a snippet of how to launch a script in your token replacement task :
<copy todir="${target.dir}" overwrite="true">
<fileset dir="${config.dir}">
<include name="*.change_me" />
</fileset>
<filterchain>
<!-- replace token with value in the property file (i.e. `run_me.sh`) -->
<filterreader classname="org.apache.tools.ant.filters.ReplaceTokens">
<param type="propertiesfile" value="${token.properties}" />
</filterreader>
<tokenfilter>
<!-- split result along a specific pattern ('`' character for instance). -->
<stringtokenizer delims="`" delimsaretokens="false" />
<!-- apply a script filter to execute shell if token is script name -->
<scriptfilter language="javascript">
<![CDATA[
if( self.getToken().indexOf(".sh") != -1 ){
<!-- rely on ant Exec task to run script -->
exectask = project.createTask("exec");
exectask.setExecutable("sh");
exectask.createArg().setValue(self.getToken());
<!-- store output in an ant property : -->
exectask.setOutputproperty("result");
exectask.perform();
<!-- retrieve the ant property and use it to replace current token -->
self.setToken(project.getProperty("result"));
}
]]>
</scriptfilter>
</tokenfilter>
</filterchain>
<mapper type="glob" from="*.change_me" to="*" />
</copy>

How to identify if a file is copied from a particular archive?

I am new to ANT.
I have a very specific scenario to handle in this:
STEP-1: I need to look for the pattern of filenames in certain ear files. If the pattern matches then I need to extract those files.
STEP-2: And if any file is extracted from a certain ear (similar to zip-file) file, then I need to search for another set of files, and copy those set of files too.
The case to handle is "How to identify if a file is copied from a particular archive" if found then proceed to step 2, else move to next archive.
I have achieved STEP-1 but no idea how to achieve step-2.
STEP-1
<!-- Set via arguments passed -->
<patternset id="pattern.needtocopy" includes="${needtocopyfile.pattern}" excludes="${ignore.pattern}">
</patternset>
<target name="get-binaries-from-baseline">
<for param="binary">
<path>
<fileset dir="${baseline.dir}/target/aaa/bbb/ccc" includes="*.ear" />
</path>
<sequential>
<basename file="#{binary}" property="#{binary}.basename" />
<unzip src="#{binary}" dest="${baseline.dir}">
<patternset refid="pattern.needtocopy" />
<mapper type="flatten" />
</unzip>
</sequential>
</for>
</target>
STEP-2:
????
Need help in this.
Thanks.
Well I resolved the same, using a groovy script based on the resources I could find.
<target name="findJars">
<zipfileset id="found" src="${ear-name}">
<patternset refid="${patternsetref}" />
</zipfileset>
<groovy>
project.references.found.each {
println it.name
println project.properties.'ear-name'
println project.properties.'dest.dir'
}
</groovy>
</target>
And then I added another task which takes this filename and ear-file-name as input and extracts the related jars based on file to search pattern.

Ant: Convert comma-delimited list of relative paths to path

I have a comma-delimited list of directories:
foo,bar,baz,qux
I want to convert it to an Ant path containing (something like) the following:
${basedir}/build/foo/classes
${basedir}/build/bar/classes
${basedir}/build/baz/classes
${basedir}/build/qux/classes
It seems like there should be a way to do this with <pathconvert>, but it's not obvious to me what it would be. Suggestions?
You might be able to use a dirset to hold your list of directories, then feed that into pathconvert. Something like:
<property name="dirs" value="foo,bar,baz,qux" />
<dirset id="dir_list" dir="${basedir}" includes="${dirs}" />
<pathconvert refid="dir_list" property="dirs_prop">
<regexpmapper from="(${basedir})/(.*)" to="\1/build/\2/classes" />
</pathconvert>
Then the property ${dirs_prop} will hold the path you want... or almost. The problem with dirset is that the order of directories is not defined. To retain the order of the original list, use a filelist in place of the dirlist:
<filelist id="dir_list" dir="${basedir}" files="${dirs}" />

Ant Fileset to Comma Separated List

I'm trying to pass a fileset to a macrodef, and have the macro generate a comma separated list of the classes. More over, I also need to change the list to contain java package & class names instead of "/" delimmited names.
We're using Ant, OSGi, and bnd and what I'm ultimately trying to do is create an entry in the Manifest that contains the fully qualified class name of each entry of the fileset.
End Goal example:
Manifest-Entry: org.foo.bar.ClassOne, org.foo.bar.ClassTo
You could do this using the Ant pathconvert task with a nested mapper, for example:
<property name="classes" location="classes" />
<fileset dir="${classes}" id="classes" />
<pathconvert dirsep="." refid="classes" property="manifest.entry" pathsep=", ">
<mapper type="regexp" from="${classes}/(.*).class" to="\1" />
</pathconvert>
<echo message="Manifest-Entry: ${manifest.entry}" />
Since you are using bnd, you could also try using the ${classes} macro in the bnd file.

Can I send Ant 'replace' task output to a new file?

The Ant replace task does an in-place replacement without creating a new file.
The below snippet replaces tokens in any of the '*.xml' files with the corresponding values from the 'my.properties' file.
<replace dir="${projects.prj.dir}/config"
replacefilterfile="${projects.prj.dir}/my.properties"
includes="*.xml" summary="true" />
I want those files that had their tokens replaced to be created named after a pattern (e.g.) '*.xml.filtered', and keep the original files.
Is this possible in Ant with some smart combination of tasks?
There are a couple of ways to get close to what you want without copying to a temporary directory and copying back.
Filtersets
If the source files can be changed so that the parts to be replaced can be delimited with begin and end tokens, as in #date# (# is the default token, but it can be changed) then you can use the copy task with a globmapper and a filterset:
<copy todir="config">
<fileset dir="config" includes="*.xml" />
<globmapper from="*.xml" to="*.xml.filtered" />
<filterset filtersfile="replace.properties" />
</copy>
If replace.properties contains FOO = bar, then any occurrence of #FOO# in a source xml file file be replaced with bar in the target.
Note that the source and target directories are the same, the globmapper means the target files and named with the suffix .filtered. It's possible (and more usual) to copy files into a different target directory)
Filterchains
If the source file can't be changed to add begin and end tokens, a possible alternative would be to use a filterchain with one or more replacestring filters instead of the filterset:
<copy todir="config">
<fileset dir="config" includes="*.xml" />
<globmapper from="*.xml" to="*.xml.filtered" />
<filterchain>
<tokenfilter>
<replacestring from="foo" to="bar" />
<!-- extra replacestring elements here as required -->
</tokenfilter>
</filterchain>
</copy>
This will replace any occurrence of foo with bar, anywhere in the file, which is more like the behaviour of the replace task. Unfortunately this way means you need to include all your replacements in the build file itself, you can't have them in a separate properties file.
In both cases the copy task will only copy source files that are newer than the target files, so unnecessary work won't be done.
Copy then replace
A third possibility (that has just occured to me whilst writing up the other two) would be to perform the copy first to the renamed files, then run the replace task specifying the renamed files:
<copy todir="config">
<fileset dir="config" includes="*.xml" />
<globmapper from="*.xml" to="*.xml.filtered" />
</copy>
<replace dir="config" replacefilterfile="replace.properties" summary="true"
includes="*.xml.filtered" />
This might be the closest solution to the original requirement. The downside is that the replace task will be run each time on the renamed files. This could be a problem for some replacement patterns (admittedly they would be odd ones like foo=foofoo, but they would be okay with the first two methods) and you will be doing unnecessary work when the dependencies don't change.
The replace task doesn't observe dependencies, instead it carries out the replacement by writing a temporary file for each input file. If the temporary file is the same as the input file, it is discarded. A temporary file that differs from the input file is renamed to replace that input. This means all the files are processed, even if none of them need be - hence it can be inefficient.
The original solution to this question was to carry out a copy-replace-copy. The second copy isn't needed though, as a mapper can be used in the first. In the copy, dependencies can be used to restrict processing to just the files that have changed - by means of a depend selector in an explicit fileset:
<copy todir="${projects.prj.dir}">
<fileset dir="${projects.prj.dir}">
<include name="*.xml" />
<depend targetdir="${projects.prj.dir}">
<mapper type="glob" from="*.xml" to="*.xml.filtered" />
</depend>
</fileset>
<mapper type="glob" from="*.xml" to="*.xml.filtered" />
</copy>
That will restrict the copy fileset to just those files that have changed. An alternative syntax for the mappers is:
<globmapper from="*.xml" to="*.xml.filtered" />
The simplest replace would then be:
<replace dir="${projects.prj.dir}"
replacefilterfile="my.properties"
includes="*.xml.filtered" />
That will still process all the files though, even if none of them need undergo replacements. The replace task has an implicit fileset and can operate on an explicit fileset, but unlike similar tasks the implicit fileset is not optional, hence to take advantage of selectors in an explicit fileset you must make the implicit one 'do nothing' - hence the .dummy file here:
<replace dir="${projects.prj.dir}"
replacefilterfile="my.properties">
includes=".dummy" />
<fileset dir="${projects.prj.dir}" includes="*.xml.filtered">
<not>
<different targetdir="${projects.prj.dir}">
<globmapper from="*.xml.filtered" to="*.xml" />
</different>
</not>
</fileset>
</replace>
That will prevent the replace task from needlessly processing files that have previously undergone substitution. It doesn't, however, prevent processing of files that haven't changed and don't need substitution.
Beyond that, I'm not sure there is a way to 'code golf' this problem to reduce the number of steps to one.
There isn't a multiple string replacement filter that can be used in a copy task to achieve the same affect as replace, which is a shame because that feels like it would be the right solution.
One other approach would be to generate the xml for a series of replace string filters and then have Ant execute that. But that will be more complex than the existing solution, and prone to problems with replacement strings that, if pasted into an xml fragment will result in something that can't be parsed.
Yet another approach would be to write a custom task or script task to do the work. If there are many files and the copy-replace solution is judged to be too slow, then this might be the way to go. But again, that approach is less simple than the existing solution.
If the requirement is to minimise the work done in the processing, rather than to come up with the shortest Ant solution, then this approach might do.
Make a fileset containing a list of inputs that have changed.
From that fileset create a comma-separated list of corresponding filtered files.
Carry out the copy on the fileset.
Carry out the replace on the comma-separated list.
A wrinkle here is that the implicit fileset in the replace task will fall back to processing everything if no files have changed. To overcome this we insert a dummy file name.
<fileset id="changed" dir="${projects.prj.dir}" includes="*.xml">
<depend targetdir="${projects.prj.dir}">
<globmapper from="*.xml" to="*.xml.filtered" />
</depend>
</fileset>
<pathconvert property="replace.includes" refid="changed">
<map from=".xml" to=".xml.filtered" />
</pathconvert>
<copy todir="${projects.prj.dir}" preservelastmodified="true">
<fileset refid="changed" />
<globmapper from="*.xml" to="*.xml.filtered" />
</copy>
<replace dir="${projects.prj.dir}"
replacefilterfile="my.properties"
includes=".dummy,${replace.includes}" summary="true" />

Resources