How to <copy> in a <macrodef> in ant? - ant

I'm trying to copy files in a macro, like so:
<project name="why" default="go">
<macrodef name="copy-some-stuff">
<attribute name="file.name" />
<copy todir="/var/tmp">
<fileset file="${file.name}" />
</copy>
</macrodef>
<target name="go">
<copy-some-stuff file.name="/etc/hosts" />
</target>
</project>
but I get the following
BUILD FAILED
b.xml:3: macrodef doesn't support the nested "copy" element.
Any ideas, other than "yes, indeeed, macrodef doesn't support the nested "copy" element." I got that much. I'm looking for why this limitation is here and a possible workaround (without using antcall).

Try surrounding the <copy> element with <sequential>:
<macrodef name="copy-some-stuff">
<attribute name="file.name" />
<sequential>
<copy todir="/var/tmp">
<fileset file="#{file.name}" />
</copy>
</sequential>
</macrodef>

Related

Iterating over all filenames in a directory in Ant

I need to iterate over all files in a directory. But I need just the name of each file, not absolute paths. Here's my attempt using ant-contrib:
<target name="store">
<for param="file">
<path>
<fileset dir="." includes="*.xqm"/>
</path>
<sequential>
<basename file="#{file}" property="name" />
<echo message="#{file}, ${name}"/>
</sequential>
</for>
</target>
The problem is that ${name} expression gets evaluated only once. Is there another approach to this problem?
From ant manual basename : "When this task executes, it will set the specified property to the value of the last path element of the specified file"
Properties once set are immutable in vanilla ant, so when using basename task within for loop, the property 'name' holds the value of the first file.
Therefore antcontrib var task with unset="true" has to be used :
<target name="store">
<for param="file">
<path>
<fileset dir="." includes="*.xqm"/>
</path>
<sequential>
<var name="name" unset="true"/>
<basename file="#{file}" property="name" />
<echo message="#{file}, ${name}"/>
</sequential>
</for>
</target>
Alternatively use local task, when using Ant 1.8.x or later :
<target name="store">
<for param="file">
<path>
<fileset dir="." includes="*.xqm"/>
</path>
<sequential>
<local name="name"/>
<basename file="#{file}" property="name" />
<echo message="#{file}, ${name}"/>
</sequential>
</for>
</target>
Finally you may use Ant Flaka instead of antcontrib :
<project xmlns:fl="antlib:it.haefelinger.flaka">
<fl:install-property-handler />
<fileset dir="." includes="*.xqm" id="foobar"/>
<!-- create real file objects and access their properties -->
<fl:for var="f" in="split('${toString:foobar}', ';')">
<echo>
#{ format('filename %s, last modified %tD, size %s bytes', f.tofile.toabs,f.tofile.mtime,f.tofile.size) }
</echo>
</fl:for>
<!-- simple echoing the basename -->
<fl:for var="f" in="split('${toString:foobar}', ';')">
<echo>#{f}</echo>
</fl:for>
</project>
If you're averse to using the var task due to Ant's standard of property immutability, there's a way to do this by taking advantage of the fact that normal property references ("${}")and iterated property references ("#{}") can be nested within one another:
<target name="store">
<for param="file">
<path>
<fileset dir="." includes="*.xqm"/>
</path>
<sequential>
<basename file="#{file}" property="#{file}" />
<echo message="#{file}, ${#{file}}"/>
</sequential>
</for>
</target>
This way, you'll be creating a new property named after each file name.

how to use fileset in ant macrodef

I would like to use the fileset in below macrodef.
I wish to change attribute jar to dir so that all jar files in dir can be processed.
<macrodef name="unjartemp">
<attribute name="jar" />
<sequential>
<!-- Remove any existing signatures from a JAR file. -->
<tempfile prefix="unjar-"
destdir="${java.io.tmpdir}" property="temp.file" />
<echo message="Removing signatures from JAR: #{jar}" />
<mkdir dir="${temp.file}" />
<unjar src="#{jar}" dest="${temp.file}" />
<delete file="#{jar}" failonerror="true" />
</sequential>
</macrodef>
To keep it flexible you may use macrodef with nested element attribute for 1-n filesets, f.e.
a macrodef that creates a dirlisting in xmlformat for nested filesets :
<macrodef name="dir2xml">
<attribute name="file"
description="xmlfile for filelisting"/>
<attribute name="roottag"
description="xml root tag"/>
<attribute name="entrytag"
description="xml tag for entry"/>
<element name="fs"
description="nested filesets for listing"/>
<sequential>
<pathconvert
property="files.xml"
dirsep="/"
pathsep="</#{entrytag}>${line.separator} <#{entrytag}>"
>
<!-- 1-n nested fileset(s) -->
<fs/>
</pathconvert>
<!-- create xmlfile -->
<echo message="<#{roottag}>${line.separator} <#{entrytag}>${files.xml}</#{entrytag}>${line.separator}</#{roottag}>" file="#{file}"/>
</sequential>
</macrodef>
Usage :
<dir2xml file="filelistant.xml" entrytag="antfile" roottag="antfilelist">
<fs>
<fileset dir="." includes="**/*.xml"/>
<fileset dir="../ant_xml" includes="**/*.xml"/>
</fs>
</dir2xml>
Add your fileset:
<fileset dir="${jars.dir}" id="jars_to_unjar">
<include name="**/*.jar"/>
</fileset>
call you macros:
<unjartemp filesetref="jars_to_unjar"/>
And you can try this modified macros:
<macrodef name="unjartemp">
<attribute name="filesetref" />
<sequential>
<for param="file">
<fileset refid="#{filesetref}"/>
<sequential>
<!-- Remove any existing signatures from a JAR file. -->
<tempfile prefix="unjar-"
destdir="${java.io.tmpdir}" property="temp.file" />
<echo message="Removing signatures from JAR: #{file}" />
<mkdir dir="${temp.file}" />
<unjar src="#{file}" dest="${temp.file}" />
<delete file="#{file}" failonerror="true" />
</sequential>
</for>
</sequential>
</macrodef>

Split the value from property using ANT

I have the following code:
<dirset id="aa" dir="FOLDER" includes="example*" excludes=".*">
</dirset>
<pathconvert pathsep="," property="bb" refid="aa">
<mapper type="flatten"/>
</pathconvert>
<echo message="LIST:${bb}"/>
and the output is for example 'examle.aa,example.bb'
And I would like to call another target for each example.*.. Can you help me please?
A macrodef would work for this, you could invoke it with custom attributes or elements based on you current property:
macrodef
Try antcontrib for the loop functionality. Here's an example
<project>
<target name="test">
<taskdef resource="net/sf/antcontrib/antlib.xml" classpath="./lib/ant-contrib-1.0.jar" />
<for param="file">
<dirset dir="." />
<sequential>
<task dir="#{file}" />
</sequential>
</for>
</target>
<macrodef name="task">
<attribute name="dir" />
<sequential>
<echo>#{dir}</echo>
</sequential>
</macrodef>
</project>

How do I select the first element of a filelist in ant?

How do I take only the first element of an ant <filelist> and also <echo> that filename as a string?
Here's a solution that doesn't require external tasks
<project name="demo" default="run">
<path id="files.path">
<first>
<filelist dir="dir1" files="foo.jar,file1.jar,file2.jar"/>
</first>
</path>
<target name="run">
<pathconvert property="path.output" refid="files.path"/>
<echo message="Output: ${path.output}"/>
</target>
</project>
You will need ant-contrib and the trick used in the code is the fact that properties cannot be changed once they are set.
<for param="file">
<path>
<filelist
id="docfiles"
dir="toto"
files="foo.xml
bar.xml"/>
</path>
<sequential>
<basename property="package" file="#{file}"/>
</sequential>
</for>
<echo>${package}</echo>
EDIT:
An alternative solution is using the break task from Antelope

Update fileset using ant dynamically

I would like to create a jar file dynamically depending on selected java modules
Here is the part of the ant script which does that.
<property name="modules.selected" value="A,C,F" />
<for list="${modules.selected}" param="module">
<sequential>
<echo>Module chosen ${basedir}/#{module}/src</echo>
<copy todir="${build.dir.src}" overwrite="true">
<fileset dir="${basedir}/#{module}/src">
<include name="**/*.${src.valid.exts}" />
</fileset>
</copy>
</sequential>
</for>
In the above script I am selecting a module and then constructing a directory and copying all the modules present in the directory to a location (build/src).
But I really dont like the logic mentioned above would like to change to include all the required modules in a fileset and use the populated fileset to copy.
Here is the logic I am looking for
<fileset id="required-modules" dir="${basedir}/#{module}/src">
<for list="${modules.selected}" param="module">
<sequential>
<echo>Module chosen ${basedir}/#{module}/src</echo>
<include name="**/*.${src.valid.exts}" />
</sequential>
</for>
</fileset>
<copy todir="${build.dir.src}" overwrite="true">
<fileset refid="required-modules" />
</copy>
Could anyone update the above script to make it work.

Resources