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.
Related
I am new in ant, so I wasn't able to find an approach to make my buld file a bit more elegant. I believe there is an approach to substitute repetitive block of code into my build. So here is build file:
<project basedir="../../../" name="do-report" default="zip-all">
<xmlproperty keeproot="false" file="implementation/xml/ant/properties.xml"/>
<!-- -->
<taskdef resource="net/sf/antcontrib/antcontrib.properties">
<classpath>
<pathelement location="${infrastructure-base-dir}/apache-ant-1.9.6/lib/ant-contrib-0.3.jar"/>
</classpath>
</taskdef>
<!-- -->
<target name="clean">
<delete dir="${dita-odt.path.odt-unzipped-base-dir}" includeemptydirs="true" failonerror="no"/>
<delete dir="examples/intermediate/odt-files" includeemptydirs="true" failonerror="no"/>
</target>
<!-- -->
<target name="unzip-writing-odt-file" depends="clean">
<unzip src="${dita-odt.path.writing-odt}" dest="${dita-odt.path.writing-odt-unzipped}"/>
</target>
<!-- -->
<target name="extract-common-paths" depends="unzip-writing-odt-file">
<foreach target="copy-text-path" param="file">
<path>
<fileset dir="${dita-odt.path.text-xml-base-dir}">
<include name="**/content.xml"/>
</fileset>
</path>
</foreach>
</target>
<!-- -->
<target name="copy-text-path" description="copy text-xml path relative to text-xml-base-dir">
<dirname property="text-absolute-dir-path" file="${file}"/>
<property name="absolute-path-text-base-dir" location="${dita-odt.path.text-xml-base-dir}"/>
<pathconvert property="common-path" dirsep="/">
<path location="${text-absolute-dir-path}"/>
<map from="${absolute-path-text-base-dir}/" to=""/>
</pathconvert>
<antcall target="copy-writing-unzipped">
<param name="common-path" value="${common-path}"/>
</antcall>
</target>
<!-- -->
<target name="copy-writing-unzipped">
<echo>${common-path}</echo>
<copy todir="${dita-odt.path.odt-unzipped-base-dir}/${common-path}">
<fileset dir="${dita-odt.path.writing-odt-unzipped}">
<include name="**/*"/>
</fileset>
</copy>
</target>
<!-- -->
<target name="transform-all" depends="extract-common-paths">
<foreach target="transform" param="file">
<path>
<fileset dir="${dita-odt.path.text-xml-base-dir}">
<include name="**/content.xml"/>
</fileset>
</path>
</foreach>
</target>
<!-- -->
<target name="transform">
<basename property="file-base-name" file="${file}"/>
<dirname property="file-dir-absolute-path" file="${file}"/>
<property name="text-xml-base-dir-absolute-path" location="${dita-odt.path.text-xml-base-dir}"/>
<pathconvert property="common-path" dirsep="/">
<path location="${file-dir-absolute-path}"/>
<map from="${text-xml-base-dir-absolute-path}/" to=""/>
</pathconvert>
<!--Substitutes backslashes with forword slashes. Basedir is a reserved property that returns absolute path with separator symbols of the current OS.-->
<pathconvert dirsep="/" property="base-dir-unix">
<path location="${basedir}"/>
</pathconvert>
<echo>TRANSFORM TO: ${dita-odt.path.odt-unzipped-base-dir}/${common-path}/${file-base-name}</echo>
<xslt in="${file}" out="${dita-odt.path.odt-unzipped-base-dir}/${common-path}/${file-base-name}" style="${dita-odt.path.text-odt-xsl}" extension=".xml" force="true">
<param name="dir-path-styles-xml" expression="${dita-odt.path.odt-unzipped-base-dir}/${common-path}"/>
<param name="project-base-dir-absolute-path" expression="${base-dir-unix}"/>
<classpath location="${infrastructure-base-dir}/${dita-odt.text-odt-xsl.processor}"/>
</xslt>
</target>
<!-- -->
<target name="zip-all" depends="transform-all" description="Turns all unzipped text folders into ODT files">
<foreach target="zip-odt" param="file">
<path>
<fileset dir="${dita-odt.path.odt-unzipped-base-dir}" includes="**/content.xml" excludes="writing/**"/>
</path>
</foreach>
</target>
<!-- -->
<target name="zip-odt">
<basename property="file-base-name" file="${file}"/>
<dirname property="file-dir-absolute-path" file="${file}"/>
<!--This property will be used to provided name for the produced ODT file. The document will have the same name as the folder that contains it.-->
<basename property="odt-doc-name" file="${file-dir-absolute-path}.odt"/>
<property name="odt-unzipped-base-dir-absolute-path" location="${dita-odt.path.odt-unzipped-base-dir}"/>
<pathconvert property="common-path" dirsep="/">
<path location="${file-dir-absolute-path}"/>
<map from="${odt-unzipped-base-dir-absolute-path}/" to=""/>
</pathconvert>
<echo>COMMON PATH: ${common-path}</echo>
<zip destfile="examples/intermediate/odt-files/${common-path}/${odt-doc-name}" basedir="${dita-odt.path.odt-unzipped-base-dir}/${common-path}" update="true"/>
</target>
<!-- -->
</project>
So this part of the script does pretty much the same, but shared among almost all the target in the project:
<dirname property="file-dir-absolute-path" file="${file}"/>
<property name="text-xml-base-dir-absolute-path" location="${dita-odt.path.text-xml-base-dir}"/>
<pathconvert property="common-path" dirsep="/">
<path location="${file-dir-absolute-path}"/>
<map from="${text-xml-base-dir-absolute-path}/" to=""/>
</pathconvert>
This part does nothing but to obtain part of a path. For example if ${file} stands for /folder/subfolder1/subfolder2 then take the path after /folder namely subfolder1/subfolder2 and assign it to a property. I this case that property is named common-path that holds same path for all the target. I examined MacroDef Task, but as far as I understand it doesn't return, only accepts some parameters in form of attributes. Anyway, any help would be much appreciated.
You are on the right track in considering <macrodef> to reduce repetitive code.
While it's true that <macrodef> doesn't return anything, <macrodef> can be given the name of a property to set. For example...
<macrodef name="my-hello">
<attribute name="person"/>
<attribute name="output-property"/>
<sequential>
<property name="#{output-property}" value="Hello, #{person}!"/>
</sequential>
</macrodef>
<my-hello person="Riko" output-property="say-hi-to-riko"/>
<echo>my-hello said: ${say-hi-to-riko}</echo>
...outputs...
[echo] my-hello said: Hello, Riko!
In this example, the caller of <my-hello> tells the macrodef to "return" its results in the say-hi-to-riko property.
Knowing this, several of the <target>s in your script can be converted to <macrodef>s that set properties...
<project name="ant-macrodef-pathconvert" default="extract-common-paths">
<taskdef resource="net/sf/antcontrib/antlib.xml" />
<property name="dita-odt.path.text-xml-base-dir" value="C:\temp\dita-odt"/>
<macrodef name="my-pathconvert">
<attribute name="file"/>
<attribute name="common-path-property"/>
<sequential>
<!-- <local> allows multiple calls to a macrodef. -->
<local name="file-dir-absolute-path"/>
<echo>In my-pathconvert for #{file}</echo>
<dirname property="file-dir-absolute-path" file="#{file}"/>
<property name="text-xml-base-dir-absolute-path"
location="${dita-odt.path.text-xml-base-dir}"/>
<pathconvert property="#{common-path-property}" dirsep="/">
<path location="${file-dir-absolute-path}"/>
<map from="${file-dir-absolute-path}/" to=""/>
</pathconvert>
</sequential>
</macrodef>
<macrodef name="copy-text-path"
description="copy text-xml path relative to text-xml-base-dir">
<attribute name="file"/>
<sequential>
<local name="common-path"/>
<echo>In copy-text-path for #{file}</echo>
<my-pathconvert file="#{file}" common-path-property="common-path"/>
<copy-writing-unzipped common-path="${common-path}"/>
</sequential>
</macrodef>
<macrodef name="copy-writing-unzipped">
<attribute name="common-path"/>
<sequential>
<echo>In copy-writing-unzipped for #{common-path}</echo>
<echo>copy task goes here.</echo>
</sequential>
</macrodef>
<target name="extract-common-paths">
<for param="file">
<path>
<fileset dir="${dita-odt.path.text-xml-base-dir}">
<include name="**/content.xml"/>
</fileset>
</path>
<sequential>
<copy-text-path file="#{file}"/>
</sequential>
</for>
</target>
</project>
In general, it's better to prefer calling <macrodef>s over calling <target>s directly. In the above example, <foreach> is replaced with <for> because <for> lets us call <macrodef>s.
Output
[echo] In copy-text-path for C:\temp\dita-odt\dir1\content.xml
[echo] In my-pathconvert for C:\temp\dita-odt\dir1\content.xml
[echo] In copy-writing-unzipped for C:/temp/dita-odt/dir1
[echo] copy task goes here.
[echo] In copy-text-path for C:\temp\dita-odt\dir2\content.xml
[echo] In my-pathconvert for C:\temp\dita-odt\dir2\content.xml
[echo] In copy-writing-unzipped for C:/temp/dita-odt/dir2
[echo] copy task goes here.
I got a requirement to loop through some XML files, replace the environment specific values in it and create new set of XML files. The environment specific values are to be taken from property file. I am able to loop through a directory to read all the files and replace some specific value using xmltask as below.
<target name="updateConfig" description="update the configuration" depends="init">
<xmltask todir="${ConfigDestDirectory}" report="false" failwithoutmatch="true">
<fileset dir="${ConfigSourceDirectory}">
<include name="*.xml"/>
</fileset>
<replace path="/:application/:NVPairs/:NameValuePair[:name='Connections/HTTP/HostName']/:value/text()" withXml="localhost"/>
</xmltask>
<echo>Replaced Successfully</echo>
</target>
But I would like to read through a property file and get the path/value from it.
I tried using property selector,property,var as different options for this case and manage to get the path but not the value. Below are the snippet of property file and the target that I am using.
#DEV.properties
HostName.xpath=/:application/:NVPairs/:NameValuePair[:name='Connections/HTTP/HostName']/:value/text()
HostName.value=localhost
<project name="TestBuild" default="ReadPropertyFile" basedir=".">
<target name="init">
<property file="DEV.properties"/>
<taskdef name="xmltask" classname="com.oopsconsultancy.xmltask.ant.XmlTask" classpath="${xmltaskPath}"/>
<taskdef resource="net/sf/antcontrib/antlib.xml" classpath="${antcontribPath}"/>
<taskdef resource="net/sf/antcontrib/antcontrib.properties"/>
</target>
<target name="ReadPropertyFile" description="update the configuration" depends="init">
<property file="DEV.properties" prefix="x"/>
<propertyselector property="propertyList" delimiter="," select="\0" match="([^\.]*)\.xpath" casesensitive="true" distinct="true"/>
<for list="${propertyList}" param="sequence">
<sequential>
<propertyregex property="destproperty" input="#{sequence}" regexp="([^\.]*)\." select="\1" />
<property name="tempname" value="${destproperty}.value" />
<var name="localprop" value="${tempname}"/>
<echo> #{sequence} </echo>
<echo> ${x.#{sequence}} </echo>
<echo>destproperty --> ${destproperty}</echo>
<echo>tempname --> ${tempname}</echo>
<echo> localprop --> ${localprop}</echo>
<echo>${x.${localprop}} </echo> <!--This is not working -->
</sequential>
</for>
</target>
It would be really helpful if you guys can throw some light.
Thanks,
Venkat
Would this work better ?
I think you got yourself confused with the "x." prefix.
<project name="TestBuild" default="ReadPropertyFile" basedir=".">
<target name="init">
<property file="DEV.properties"/>
<taskdef name="xmltask" classname="com.oopsconsultancy.xmltask.ant.XmlTask" classpath="${xmltaskPath}"/>
<taskdef resource="net/sf/antcontrib/antlib.xml" classpath="${antcontribPath}"/>
<taskdef resource="net/sf/antcontrib/antcontrib.properties"/>
</target>
<target name="ReadPropertyFile" description="update the configuration" depends="init">
<property file="DEV.properties" prefix="x"/>
<local name="propertyList"/>
<propertyselector property="propertyList" delimiter="," select="\1" match="x\.([^\.]*)\.xpath" casesensitive="true" distinct="true"/>
<for list="${propertyList}" param="sequence">
<sequential>
<echo> #{sequence} </echo>
<echo> #{sequence}.xpath = ${x.#{sequence}.xpath} </echo>
<echo> #{sequence}.value = ${x.#{sequence}.value} </echo>
</sequential>
</for>
</target>
</project>
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>
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 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