Ant: Iterating through an XML file's nodes - ant

I have the following XML structure:
<servers>
<hostname>ABC01</hostname>
<hostname>ABC02</hostname>
</servers>
I need to retrieve a file from each server, from a folder I know, attach it to an email and then send it.
What would be the approach for this?
Thanks.

Use the xmlproperty task to load an XML file into properties.
Then use the for task from ant-contrib to act upon each of the matched properties.
Something like:
<target name="funtimes">
<xmlproperty file="the.xml" delimiter=","/>
<for list="${servers.hostname}" param="hostname">
<sequential>
<echo>Doing things with #{hostname}</echo>
</sequential>
</for>
</target>
Fetching files depends upon how you are planning to access them. The scp task might help.
For sending email you can use the mail task.

Related

Renaming files with dir values using Ant?

I have the following simple thing to do with Ant but did not find how to do that:
move build/xxx/file.ext to dest/xxxfile.ext
I'm no Ant Guru .
file.ext is constant in this particular case
Nota : xxx can take many values so I want to apply to all these values
You need to use a mapper element to generate the destination file names. This is derived from the Ant mapper docs:
<move todir="dest">
<fileset dir="build" includes="*/*.ext" />
<mapper type="regexp" from="^([^/]*)/([^/]*)" to="\1\2"/>
</move>
When in doubt, an exec will do the job for you, but it's not always the best way.
Try the move task.
<move file="build/xxx/file.ext" tofile="dest/xxxfile.ext"/>

How can I iterate over properties from a file?

All my projects and their versions are defined in a properties file like this:
ProjectNameA=0.0.1
ProjectNameB=1.4.2
I'd like to iterate over all the projects, and use their names and versions in an Ant script.
At present I read the entire file using the property task, then iterate over a given list in a for loop like this:
<for list="ProjectNameA,ProjectNameB" param="project">
<sequential>
<echo message="#{project} has version ${#{project}}" />
</sequential>
</for>
How can I avoid the hard-coding of the project names in the for loop?
Basically iterate over each line and extract the name and the version of a project as I go.
Seeing as you're already using antcontrib for, how about making use of the propertyselector task:
<property file="properties.txt" prefix="projects."/>
<propertyselector property="projects" match="projects\.(.*)" select="\1"/>
<property file="properties.txt" />
<for list="${projects}" param="project">
...
</for>
The idea here is to read the properties once with the projects prefix, and use the resulting set of properties to build a comma-separated list of projects with the propertyselector task. Then the properties are re-read without the prefix, so that your for loop can proceed as before.
Something you want to keep in mind, if you are reading additional .property files (besides build.properties) is scoping. If you read an additional file (via the property file="foo.property") tag, ant will show that the file was read, and the properties loaded. However, when you goto reference them, they come up un-defined.

Ant: Create directory containing file if it doesn't already exist?

Basically, I get a path like "C:\test\subfolder1\subfolder2\subfolder3\myfile.txt", but it's possible that subfolders 1-3 don't exist already, which means I'd get an exception if I try to write to the file.
Is there a way to create the directory structure the target file is in, either by using some task that creates the structure when it outputs to the file and then deleting the file, or by parsing the directory part of the path and using the mkdir task first?
Ant will create the full tree of directories for you when you use the <mkdir> task. So you just need to use the <dirname> task to get the directory name from the file name.
<dirname property="directoryProperty" file="${filePathProperty}"/>
<mkdir dir="${directoryProperty}" />
The first line extracts the directory portion of your file path and stores it in the directoryProperty property. The second line creates the directory (and any parent directories that don't exist).
This task works well
<mkdir dir="${file}/../"/>
Sometimes we could have an alternate choice, using touch task
<touch file="${file}" mkdirs="true" verbose="true"/>
This task should do the job but would have a side effect to create the file with zero size
Just make failonerror=false to avoid the error to stop the whole logic.
<delete includeemptydirs="true" failonerror="false">
<fileset dir="${builder-base.dir}" includes="**/*"/>
</delete>
Using the
<mkdir dir="${dir}"/ >
inside your <target> tag should work, but I am not sure what else you want to do along with mkdir?
I'm not 100% sure it'll work but you might be able to do something like the following to make the parent directory you're after:
<mkdir dir="${file}/../"/>
If that doesn't work straight off then it might be worth defining a property using the location syntax before creating a directory with the new property:
<property name="dir" location="${file}/../" />
<mkdir dir="${dir}" />
Well-behaved Ant tasks are generally expected to create any necessary directory structures unless there is a good reason not to.
Are you writing a task? If so you should add the directory creation logic to your task. If you are getting the task from a third party you should point this fact out to them and have them fix their task. Failing that Dan's solution should work.

ant iterate over files

I want to iterate over a list of jars (undefined number) and add them all to the jar file.
To add them I plan to use something like this:
<jar id="files" jarfile="all.jar">
<zipfileset src="first.jar" includes="**/*.java **/*.class"/>
<zipfileset src="second.jar" includes="**/*.java **/*.class"/>
</jar>
but how do I iterate over them? I don't have ant-contrib
Thanks!
Just use zipgroupfileset with the Ant Zip task
<zip destfile="out.jar">
<zipgroupfileset dir="lib" includes="*.jar"/>
</zip>
This will flatten all included jar libraries' content.
If you do not have access to ant-contrib For task, you may end up to have to define your custom Task for doing what you need...
If you have ant1.6 and above, you can also try subant (see New Ant 1.6 Features for Big Projects):
If you use <subant>'s genericantfile attribute it kind of works like <antcall> invoking a target in the same build file that contains the task.
Unlike <antcall>, <subant> takes a list or set of directories and will invoke the target once for each directory setting the project's base directory.
This is useful if you want to perform the exact same operation in an arbitrary number of directories.

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