ANT: Foreach not excluding files when target attrbute is used - ant

I have a foreach tag to execute all the files within a particular directory. As part of the fileset I have excluded two files.
But irrespective of excluding the files, the param still passes the two files to the target "runpART"
I do not want to call the target for the excluded files.
Let me know if there is any way to do it. The following does not seem to work
<foreach target="runART" param="p.schedulerFile" inheritall="true" inheritrefs="true">
<path>
<fileset dir="${p.dir}" casesensitive="no">
<!--<include name="**\*.xml"/>-->
<excludesfile name="${pART.dir}\CodeCoverage_NSR.xml"/>
<excludesfile name="${pART.dir}\CodeCoverageRegression_HLR.xml"/>
</fileset>
</path>
</foreach>

You don't want <excludesfile >, which expects a file containing exclude patterns, you want
<exclude name="CodeCoverage_NSR.xml">
<exclude name="CodeCoverageRegression_HLR.xml">
Now, that's assuming you meant to have ${p.dir} == ${pART.dir}.
Exclude patterns in a fileset have to be relative to the root dir of the fileset (here, its dir="${p.dir}").
For more info, see: http://ant.apache.org/manual/Types/fileset.html

Related

Deleting files matching a placeholders in another directory tree

I have two directory trees:
source/aaa/bbb/ccc/file01.txt
source/aaa/bbb/file02.txt
source/aaa/bbb/file03.txt
source/aaa/ddd/file03.txt
source/file01.txt
and
template/aaa/bbb/ccc/file01.txt
template/aaa/bbb/DELETE-file03.txt
template/aaa/DELETE-ddd
template/DELETE-file01.txt
Using Ant, I want to do three things. First, I want to copy any files from "template" into "source", such that all files not starting with "DELETE-" are replaced. For example, "source/aaa/bbb/ccc/file01.txt" would be replaced. This is straightforward with:
<copy todir="source" verbose="true" overwrite="true">
<fileset dir="template">
<exclude name="**/DELETE-*"/>
</fileset>
</copy>
Second, I want to delete all files in the "source" tree whose name matches a "DELETE-" file in the corresponding directory of the "template" tree. For example, both "source/aaa/bbb/file03.txt" and "source/file01.txt" will be deleted. I have been able to accomplish this with:
<delete verbose="true">
<fileset dir="source">
<present present="both" targetdir="template">
<mapper type="regexp" from="(.*[/\\])?([^/\\]+)" to="\1DELETE-\2"/>
</present>
</fileset>
</delete>
Third, I'd like to delete any directories (empty or not) whose names match in the same way. For example, "template/aaa/DELETE-ddd" and all file(s) under it would be deleted. I'm not sure how to construct a fileset that matches directories (and all the files under them) in the "source" tree where the directory has a DELETE-* file in the "template" tree.
Is this third task even possible with Ant (1.7.1)? I'd preferrably like to do this without writing any custom ant tasks/selectors.
It seems the root issue that makes this difficult is that ant drives selectors/filesets based on files found within the target directory of fileset. Normally, however, one would want to drive things from the list of DELETE-* marker files.
The best solution I've found so far does require some custom code. I chose the <groovy> task, but also could have used <script>.
The gist: create a fileset, use groovy to add a series of excludes that skip files and directories with a DELETE-* marker, then perform the copy. This accomplishes the second and third task from my question.
<fileset id="source_files" dir="source"/>
<!-- add exclude patterns to fileset that will skip any files with a DELETE-* marker -->
<groovy><![CDATA[
def excludes = []
new File( "template" ).eachFileRecurse(){ File templateFile ->
if( templateFile.name =~ /DELETE-*/ ){
// file path relative to template dir
def relativeFile = templateFile.toString().substring( "template".length() )
// filename with DELETE- prefix removed
def withoutPrefix = relativeFile.replaceFirst( "DELETE-", "")
// add wildcard to match all files under directories
def exclude = withoutPrefix + "/**"
excludes << exclude
}
}
def fileSet = project.getReference("source_files")
fileSet.appendExcludes(excludes as String[])
]]></groovy>
<!-- create a baseline copy, excluding files with DELETE-* markers in the template directories -->
<copy todir="target">
<fileset refid="source_files"/>
</copy>
To delete a directory and its contents use delete with nested fileset, i.e. :
<delete includeemptydirs="true">
<fileset dir="your/root/directory" defaultexcludes="false">
<include name="**/DELETE-*/**" />
</fileset>
</delete>
With attribute includeemptydirs="true" the directories will be deleted too.

Find all directories in which a file exists, such that the file contains a search string

I have a directory tree that I need to process as follows:
I have a certain file that needs to be copied to a select few sub directories
A sub directory of interest is one that contains a file within which I can regex match a known search string
Ideally I would like to:
Perform a regex match across all files within a directory
If the regex matches, copy the file to that directory
The trouble is that I am quite new to ANT and I'm having difficulties finding my way around. I can't find any tasks in the docs about per directory operations based on regex search. The closest thing I've found is a regex replace task (<replaceregexp>) that can search and replace patterns across files.
Is this even possible? I'd really appreciate a sample to get started with. I apologize for requesting code - I simply don't know how to begin composing the tasks together to achieve this.
Alternatively I have the option of hardcoding all the copy operations per directory, but it would mean manually keeping everything in sync as my project grows. Ideally I'd like to automate it based on the regex search/copy approach I described.
Thanks!
Your requirement is a bit non-standard, so I've solved it using a custom Groovy task.
Here's a working example:
<project name="find-files" default="copy-files">
<!--
======================
Groovy task dependency
======================
-->
<path id="build.path">
<pathelement location="jars/groovy-all-1.8.6.jar"/>
</path>
<taskdef name="groovy" classname="org.codehaus.groovy.ant.Groovy" classpathref="build.path"/>
<!--
=========================
Search for matching files
=========================
-->
<target name="search-files">
<fileset id="filesContainingSearchString" dir="src">
<include name="**/*.txt"/>
<containsregexp expression="[4-6]\.[0-9]"/>
</fileset>
</target>
<!--
===================================
Copy file into each directory found
===================================
-->
<target name="copy-files" depends="search-files">
<groovy>
project.references.filesContainingSearchString.each { file ->
def dir = new File(file.toString()).parent
ant.copy(file:"fileToBeCopied.txt", toDir:dir)
}
</groovy>
</target>
</project>
Notes:
Groovy jar can be downloaded from Maven Central
Use the copy task with a fileset and regular expression selector :
<copy todir="your/target/dir">
<fileset dir="rootdir/of/your/directorytree" includes="**/*.txt">
<containsregexp expression="[4-6]\.[0-9]"/>
</fileset>
</copy>
This example is taken from the ant manual and slightly adapted.
Means select all files with .txt extension anywhere beyond rootdir/of/your/directorytree that match the regular expression (have a 4,5 or 6 followed by a period and a number from 0 to 9) and copy them to your/target/dir.
Just adapt it for your needs.

create a fileset of all files matching a pattern, excluding files with a specific sibling file

I would like to create a fileset of files matching a specific pattern, but exclude from this set any files which have a specific other file in the same directory.
E.g., I would like a fileset which matches all ./*/file.xml files, like:
<fileset dir="${some.dir}" includes="*/file.xml" />
... but I want to exclude any file.xml files which are in the same director as an ignore.this file.
So if the dir structure is:
foo/file.xml
bar/file.xml
bar/ignore.this
... the the file.xml in foo will be selected, but bar will not.
You can use a fileset with a present selector with a mapper:
<fileset dir="${some.dir}" includes="*/file.xml">
<present targetdir="${some.dir}" present="srconly">
<mapper type="regexp" from="^(.*)/.*" to="\1/ignore.this" />
</present>
</fileset>
That is, include only files called file.xml where there is no corresponding file in the same directory called ignore.this.

Ant Copy task with Path instead of FileSet

I'm using Ant 1.7, want to copy files from different paths (they have no relationship, so i cannot use the include selector to filter them out of their root directory). I try to use the <path> inside the <copy> instead of <fileset>, because with <path> i can specify multi paths which is in <fileset> not possible. My ant script looks like this, but it doesn't work.
<target name="copytest">
<!-- copy all files in test1 and test2 into test3 -->
<copy todir="E:/test3">
<path>
<pathelement path="C:/test1;D:/test2"></pathelement>
</path>
</copy>
</target>
Anybody has idea about how to use the <path> inside <copy>? Or maybe anybody has the advise about how to copy files from different source without selector?
Btw, i don't want to hard code the source directories, they will be read from a propertiy file, so writing multi <fileset> inside <copy> should not be considered.
thanks in advance!
This only works if the flatten attribute is set to true:
<copy todir="E:/test3" flatten="true">
<path>
<pathelement path="C:/test1;D:/test2"></pathelement>
</path>
</copy>
This is documented in the Examples section of the Ant Copy task documentation.
<pathelement> generally uses it's path attribute as a reference to classpath or some other predefined location, if you want to give specific file locations outside of the classpath try with location attribute
<pathelement location="D:\lib\helper.jar"/>
The location attribute specifies a single file or directory relative
to the project's base directory (or an absolute filename), while the
path attribute accepts colon- or semicolon-separated lists of
locations. The path attribute is intended to be used with predefined
paths - in any other case, multiple elements with location attributes
should be preferred.
We have the same problem
A bit more complicated that we need to add a specified pattern set to each fileset converted from path
For example, this is the incoming data
<path id="myDirList" path="C:/test1;D:/test2" />
<patternset id="myPatterns" includes="*.html, *.css, etc, " />
We wrote a script to solve this problem
<resources id="myFilesetGroup">
<!-- mulitiple filesets to be generated here
<fileset dir="... dir1, dir2 ...">
<patternset refid="myPatterns"/>
</fileset>
-->
</resources>
<script language="javascript"><![CDATA[
(function () {
var resources = project.getReference("myFilesetGroup");
var sourceDirs = project.getReference("myDirList").list();
var patterRef = new Packages.org.apache.tools.ant.types.Reference(project, "myPatterns");
for (var i = 0; i < sourceDirs.length; i++) {
var fileSet = project.createDataType("fileset");
fileSet.dir = new java.io.File(sourceDirs[i]);
fileSet.createPatternSet().refid = patterRef;
resources.add(fileSet);
}
})();
]]></script>
now you can use this resources in you copy task
<!-- copy all files in test1 and test2 into test3 -->
<copy todir="E:/test3">
<resources refid="myFilesetGroup">
</copy>
I tried this and works fine
<fileset file="${jackson.jaxrs.lib}"/>

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