Ant Macro use element's content as the value of an attribute - ant

I want to use the content of the element named "soql" as an attribute in a replace filter. What I wanted to achieve is to replace a value in a file with the contents of an element. In this case I am inclined to not use an attribute as the value should be enclosed within CDATA block
<macrodef name="exportdata">
<attribute name="file"/>
<attribute name="object"/>
<element name="soql"/>
<sequential>
<echo message="Exporting #{object}"/>
<mkdir dir="data/exports"/>
<copy file="data/config/template-process-conf.xml" tofile="data/config/process-conf.xml" overwrite="true" failonerror="true"/>
<replace file="data/config/process-conf.xml">
<replacefilter token="_endpoint_" value="${sf.serverurl}"/>
<replacefilter token="_username_" value="${sf.username}"/>
<replacefilter token="_password_" value="${encryptedpassword}"/>
<replacefilter token="_object_" value="#{object}"/>
<replacefilter token="_soql_" value="#{soql}"/>
<replacefilter token="_file_" value="data/exports/#{file}.csv"/>
<replacefilter token="_keyfile_" value="data/config/key.txt"/>
</replace>
<java classname="com.salesforce.dataloader.process.ProcessRunner" classpath="lib/DataLoader.jar" failonerror="true">
<sysproperty key="salesforce.config.dir" value="data/config"/>
<arg line="process.name=export#{object}"/>
</java>
</sequential>
</macrodef>

If you have a reasonably up-to-date version of Ant (>1.7) you may be able to use a string resource to do this. Here's a simple example:
<macrodef name="element2string">
<element name="elem"/>
<sequential>
<string id="elem.as.string"><elem/></string>
<echo message="${toString:elem.as.string}"/>
</sequential>
</macrodef>
<element2string>
<elem><![CDATA[There be <dragons>]]></elem>
</element2string>
Result:
[echo] There be <dragons>

Related

Is there any way to "overload" two macrodefs in ant?

I want to implement a <macrodef/> that can be called in two ways: With a single file parameter, or with a <fileset/>:
<macrodef name="sqlplus">
<attribute name="file"/>
<sequential>
<!-- there be dragons, here -->
</sequential>
</macrodef>
<macrodef name="sqlplus">
<element name="files"/>
<sequential>
<for param="file">
<path>
<files/>
</path>
<sequential>
<!-- call the other macro -->
<sqlplus file="#{file}"/>
</sequential>
</for>
</sequential>
</macrodef>
This doesn't seem to work. Is there any trick I can use to implement "quasi"-overloading? Or do I have to rename one of the macrodefs? I know I could write this, and check if the arguments are set (e.g. using ant-contrib):
<macrodef name="sqlplus">
<attribute name="file"/>
<element name="files" optional="yes"/>
<!-- ... -->
</macrodef>
But there is no way to make an <attribute/> optional.

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 to <copy> in a <macrodef> in 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>

Resources