Concat files in Apache ANT to variable? - ant

I know that I can use ANT's concat to merge several files into one file. But what about merging files into a variable?
Can this be done?

concat can be used as a resource collection, so you can use it in a loadresource:
<loadresource property="the.property">
<concat>
<file file="foo.txt" />
<file file="another_file.txt />
<string>You can put any resource in here, not just files!</string>
</concat>
</loadresource>
Now the the.property property will contain the concatenation of all the resources inside the concat. (I'm assuming you mean an ant property when you say variable.)

Related

How to use a glob pattern for a location attribute of a property?

I want to set a property to a filename, for which I know a pattern that will match a unique file. For example, I have the file:
plugins/doc.en_20110608.zip
I define in my ant file:
<property name="doc.zip" location="plugins/doc.en_*.zip" />
I know the pattern will match only one file. The problem is that ant doesn't try to match any pattern at this point, and fails because there is no file named plugins/doc.en_*.zip.
If I'm not using a fileset, it's because the property can be substituted where a fileset is not allowed, like destfile attribute of zip task.
<zip destfile="${doc.zip}" update="true"> ... </zip>
The answer is no I think, but you can use reference/path shortcuts to 'stringify' a fileset into a property. Something like:
<fileset id="doc.zip.fs" dir="plugins" includes="doc.en_*.zip"/>
<property name="doc.zip" value="${toString:doc.zip.fs}" />

concat as resource collection in zip not working?

I'm trying to use <concat> as a resource collection in a <zip> task and, according to the documentation, this should work. I'd like to do this because some of the files I want to include in the zip need to have some properties expanded, so I will also add a <filterchain> to the <concat> to do this. I'd prefer to do it directly rather than copying to a temp location (with property substitution) and including the copy in the zip file.
However, I can't seem to get <zip> to correctly use the <concat> element.
A simplified example of what I have so far:
<zip destfile="target/dist.zip">
<concat>
<fileset file="CHANGES.txt" />
</concat>
</zip>
This creates a zip file containing several directories all named concat (C: (obviously this is on a Windows machine).
What am I missing?
A colleague and I came up with the answer by looking through the <zip> and <concat> source. There are really two answers:
<concat>'s implementation of the ResourceCollection interface is odd, but we understand why.
There's a way around that.
For #1, while <concat> is a ResourceCollection (like FileSet), under the hood it returns as the name of single Resource it contains a hard-coded value:
"concat (" + String.valueOf(c) + ")";
Look familiar?
The name of resources is normally ignored--except by <zip> and its related tasks, which uses the resource name as the ZipEntry name. Since <concat> returns the odd-looking name, that's what we get in the zip file.
I haven't quite figured out why I get multiple entries, but it doesn't matter: the observation leads to a convoluted solution.
Since I know the name of the ZipEntry I want to create, I can use a <mapper> to give the <concat> resource a name. Here's what I came up with in all its glory:
<zip destfile="target/distribution.zip">
<fileset dir=".">
<exclude name="target/**" />
<exclude name="CHANGES.txt" />
</fileset>
<mappedresources>
<concat>
<fileset file="CHANGES.txt" />
<filterchain>
<expandproperties />
</filterchain>
</concat>
<mergemapper to="CHANGES.txt" />
</mappedresources>
</zip>
As my colleague says "In Ant every problem can be solved using a mapper."
This only works for Ant 1.8+ because <mappedresources> was added in that release.
I'm going to post some comments to the Ant mailing list and suggest a couple of enhancements:
Allow a resource name to be specified as an attribute on <concat> when it's being used as a ResourceCollection.
Throw an exception (and don't create a synthetic value) if getName() is called without having a value specified.
Finally, though not directly related, I do wish <expandproperties> could take a <propertyset> so I can control which properties get substituted.
Do you want the final zip to contain a single file or multiple files? As far as I can see, using concat (when done successfully, which isn't done above) would produce a single file, the result of concatenation of all files in the resource collection.
If you want multiple files rather than concatenation, I think intermediate copy is what you'll need.
From Ant manual for the concat task:
Since Apache Ant 1.7.1, this task can
be used as a Resource Collection that
will return exactly one resource.

Access Antlib Resources From Within Apache Ant Macros

Is it possible to access resources from within Apache Ant macros defined in an Antlib?
For instance, within my antlib.xml, I have a macro that performs some XSLT. Ideally I would like the XSLT file to be packaged in the same JAR as the antlib.xml, but I have no idea how to specify the location of the XSLT.
Here is the (simplified) code:
<antlib xmlns:tibant="antlib:org.windyroad.tibant">
<macrodef name="configure-ear">
<attribute name="xml" />
<attribute name="out" />
<sequential>
<xslt in="#{xml}"
out="#{out}"
style="...what to put here...">
</xslt>
</sequential>
</macrodef>
</antlib>
The problem is that whatever I put in the style attribute is relative to the basedir for the project using the antlib and I can't find any way to specify a path relative to the antlib.xml.
Any ideas?
I can ship the XSLT as a separate file, but then I would need to give users some way to specify the location of the XSLT, which is not ideal (e.g. setting a tibant.home property). I could also use echoxml to write out the XSLT to a temp file, but IMO that's a hack.
Instead of using the style attribute, try a nested <style> element, which will allow you to specify a javaresource as the style sheet. You can then put the stylesheet next to your antlib.xml in the jar, and it will be available on the classpath.
<xslt in="#{xml}"
out="#{out}">
<style>
<javaresource name="your/package/structure/style.xslt" />
</style>
</xslt>
The first thing I would look at is to load XSL from the classloader as a resource. You should be able to accomplish this with LoadResource task (http://ant.apache.org/manual/Tasks/loadresource.html). The next I would look at options that XSLT task gives you for the specifying style. It doesn't look like it has any ability to take literal contents of XSLT. You can work around this by writing out the XSLT content to a temporary file and then giving the path to the temp file to the XSLT task.
So...
Load XSLT text from the classloader.
Acquire a temporary file using Tempfile task (http://ant.apache.org/manual/Tasks/tempfile.html).
Write out XSLT text to the temp file using Echo task.
Invoke XSLT with reference to the temp file.

How do I make a fileset from a comma-separated list of directories in Ant?

In an Ant target I get a property, containing the list of directories to be included in further action (copying, filtering, etc.). It looks like this:
directories=dir1, dir2, dir3
I need a way to convert this list to a fileset or patternset that selects all the files in these directories.
I know I can use a script to generate pattern strings and then use it in the "include" or "exclude", but is there are a way to avoid scripts?
Note that as of Ant 1.9.4, there is a new construct <multirootfileset> that provides that functionality, even if the dirs are not siblings:
<multirootfileset basedirs="${directories}" includes="**/*">
How about using the antcontrib propertyregex task to convert the comma-separated list into wildcards suitable for a fileset?
<property name="directories" value="dir1, dir2, dir3" />
<property name="wildcard" value="${file.separator}**${file.separator}*" />
<propertyregex property="my_pattern"
input="${directories}"
regexp=", "
replace="${wildcard}," />
At this point we now have:
my_pattern=dir1/**/*,dir2/**/*,dir3
That can be used with a further suffixed wildcard to get the full fileset:
<fileset dir="." id="my_fileset" includes="${my_pattern}${wildcard}" />
(The fiddly ${wildcard} is to ensure portability between unix and windows filesystems, you could use /**/* if you're pure unix.)
Something like this should work:
<dirset includes="${directories}"/>
Yes, dirset isn't fileset. However, it may be enough, or else you can probably use a for or foreach from ant-contrib to iterate over the directories in your target. You might also be able to define a ResourceCollection based around the dirset. It might help to know what the "further action" is expected to be.
However, this feels like too much work ...

Filter a fileset referenced using a refid

I have a fileset (which is returned from the Maven Ant task), and it contains all the jars I need to repack. This fileset is referenced by a refid. I only want to include our own jars, so I would like to filter that. But Ant filesets don't support any further attributes or nested tags if a refid is used.
For example, if the fileset is:
org.foo.1.jar
org.foo.2.jar
log4j.jar
and I want to have a fileset which contains only
org.foo*.jar
How would I do that?
Try using a restrict resource collection, which you can use like a fileset in any task that uses resource collections to select the groups of files to operate on.
For example, for a fileset returned from your Maven task referenced via an id called dependency.fileset you can declare a restrict resource collection like so:
<restrict id="filtered.dependencies">
<fileset refid="dependency.fileset"/>
<rsel:name name="org.foo*.jar"/>
</restrict>
Note you'll have to declare the resource selector namespace as it isn't part of the built-in Ant namespace:
<project xmlns:rsel="antlib:org.apache.tools.ant.types.resources.selectors">
...
</project>
From here you can reference your restrict resource collection in a similar fashion to how you would reference your fileset. For example, to create backups of your filtered set of files:
<copy todir=".">
<restrict refid="filtered.dependencies"/>
<globmapper from="*" to="*.bak"/>
</copy>
Of course you can inline your restrict resource collection if you so desire:
<copy todir=".">
<restrict>
<fileset refid="dependency.fileset"/>
<rsel:name name="org.foo*.jar"/>
</restrict>
<globmapper from="*" to="*.bak"/>
</copy>
Have a look at the Ant documentation on resource collections for further information.
I think you'll need to write an ant task for that. They're pretty easy to write though.
See http://ant.apache.org/manual/develop.html#writingowntask
In your task, you'll need to call getProject() and ask it to give you the fileset, walk through it, and create a new one.
I 'm using Ant with Ivy. With the help of Ivy it is possible to filter dependencies for retrieval, with the following code in ivy.xml:
<dependency name="Project1" rev="latest.integration" transitive="true" conf="modlibs">
<exclude name="${exclusionRegEx}" matcher="regexp" />
</dependency>
<dependency name="Project2" rev="latest.integration" transitive="false" conf="modules"/>
Maybe a quick look at the Ivy source 'll help?
If you are using a sufficiently recent version of Ant and the JDK, for example, Ant 1.7 and JDK 6, then you can use the optional script task to do what you want. (Earlier versions may also work.) The page I linked to, if you scroll down to the text "The goal is to list the filesizes" then you'll see a sample script that creates a Fileset.
This isn't for the faint of heart, and a custom ant task you write yourself will probably be more flexible. But I wanted to point out the option.

Resources