How can I prepend filenames to the files themselves in Ant? - ant

Is there any way to use Ant to prepend each file's name to it? So foo.txt would go from
bar
to
// foo.txt
bar
This needs to work on a set of files that cannot be hard-coded into the ant script.

Since you want to dynamically determine the files to work with, I recommend that you get the ant-contrib.jar and utilize its for task. I should point out that this will write out the full path to the file in the comment.
<!-- Sample usage -->
<target name="run">
<prepend>
<!-- Assuming you are interested in *.txt files in the resource directory -->
<fileset dir="resource">
<include name="**/*.txt"/>
</fileset>
</prepend>
</target>
<!-- Import the definitions from ant-contrib -->
<taskdef resource="net/sf/antcontrib/antlib.xml">
<classpath>
<pathelement location="../ant-contrib*.jar"/>
</classpath>
</taskdef>
<!-- Create the prepend task -->
<macrodef name="prepend">
<!-- Declare that it contains an element named "files". Implicit means it needn't be named -->
<element name="files" implicit="yes"/>
<sequential>
<!-- For loop, assigning the value of each iteration to "file" -->
<for param="file">
<!-- Give the for loop the files -->
<files/>
<sequential>
<!-- Load the contents of the file into a property -->
<loadfile property="#{file}-content" srcfile="#{file}"/>
<!-- Echo the header you want into the file -->
<echo message="// #{file}${line.separator}" file="#{file}"/>
<!-- Append the original contents to the file -->
<echo message="${#{file}-content}" append="true" file="#{file}"/>
</sequential>
</for>
</sequential>
</macrodef>

Related

How to find substring in apache ant property value?

I have multiple files under a directory. All these files have similar names e.g. com.xyz.projname1.xml, com.xyz.projname2.xml, etc.
When I try to get the filename in a for loop, it returns the absolute path of com.xyz.projname1.xml.
I just want to check if the absolute path contains 'projname1'.
<target name="update-proj-version-and-name">
<for param="file" >
<path>
<fileset dir="${project.dir.target}/artifact/" casesensitive="no">
<include name="*.xml"/>
</fileset>
</path>
<sequential>
<property name="proj.file.name" value="#{file}"/>
<echo>proj name - ${proj.file.name}</echo>
<!-- Here I need to check if ${proj.file.name} contains 'projname1' then invoke one of our macrodef -->
</sequential>
</for>
</target>
Can you please help?
Thanks,
Ranjeet

macrodef'd <jar> task not behaving like an implicit fileset?

Using standard Ant 1.9.7 to assemble a bunch of jar files. We've made a macro to help cut down on the verbosity of the XML:
<!-- All 'description' bits snipped for SO posting. -->
<macrodef name="buildjar">
... bunch of <attribute> ...
<element name="also"/>
<sequential>
<jar ....>
<manifest> ... </manifest>
<fileset what="stuff in every jar file" />
<fileset what="and this stuff too" />
<mappedresources if:true="beauty">
<fileset for when truth is beauty/>
<globmapper from="ugly" to="beauty"/>
</mappedresoruces>
<!-- Anything else for this specific jar. -->
<also/>
</jar>
</sequential>
</macrodef>
This works:
<buildjar .........>
<also>
<fileset file="/some/path/somewhere/a_single_file"/>
</also>
</buildjar>
But this does not:
<buildjar .........>
<also>
<include name="/some/path/somewhere/a_single_file"/>
</also>
</buildjar>
There are no errors. Looking at ant -d output, there's no mention of the additional entry at all, where in the first example there is a line for fileset: Setup scanner in dir /some/path/somewhere with patternSet{ includes: [a_single_file] excludes: [] }
Ditto for multiple files. This works:
<buildjar .........>
<also>
<fileset dir="/some/path/somewhere">
<include name="one_file" />
<include name="foo**" />
</fileset>
</also>
</buildjar>
but this does not:
<buildjar .........>
<also>
<include name="/some/path/somewhere/one_file"/>
<include name="/some/path/somewhere/foo**"/>
</also>
</buildjar>
According to the Ant manual's page for <jar>,
This task forms an implicit FileSet and supports most attributes of <fileset> (dir becomes basedir) as well as the nested <include>, <exclude> and <patternset> elements.
So in theory, shouldn't an <include> simply be enough, and become a nested element of the macro'd <jar>?
Obviously, in practice this isn't a problem (we slap a bigass comment in the build files telling people to not leave out the explicit <fileset>). And we can't put the <fileset> into the macro definition like this:
<macrodef name="buildjar">
<element name="also"/>
<sequential>
<jar ....>
.....
<!-- Anything else for this specific jar. -->
>> <fileset dir="some_generic_base_path">
<also/>
>> </fileset>
</jar>
</sequential>
</macrodef>
because then when the calling code does a buildjar without any also blocks, the unrestricted fileset will include the entire some_generic_base_path tree.
Is this simply some interaction between macrodefs and filesets that has taken us by surprise?
Short answer is no - it's not a macrodef problem. To use the implicit fileset you must specify the basedir attribute for the <jar> task. Here's an example that illustrates this:
<jar destfile="my.jar">
<include name="a/b" />
</jar>
<zip destfile="my.zip" >
<include name="a/b" />
</zip>
In the example the <jar> task will succeed and create a manifest-only jar file. But the <zip> task will fail saying:
BUILD FAILED
build.xml:8: basedir attribute must be set, or at least one resource collection must be given!
The jar task is based on the zip task and inherits its check for resources. Because the jar task has some - the manifest - the error is not thrown.
If you want to use the implicit fileset, specify a basedir.

Replacing all tokens based on properties file with ANT

I'm pretty sure this is a simple question to answer and ive seen it asked before just no solid answers.
I have several properties files that are used for different environments, i.e xxxx-dev, xxxx-test, xxxx-live
The properties files contain something like:
server.name=dummy_server_name
server.ip=127.0.0.1
The template files im using look something like:
<...>
<server name="#server.name#" ip="#server.ip#"/>
</...>
The above is a really primitive example, but im wondering if there is a way to just tell ANT to replace all tokens based on the properties file, rather than having to hardcode a token line for each... i.e
<replacetokens>
<token key="server.name" value="${server.name}"/>
<token key="server.ip" value="${server.ip}"/>
</replacetokens>
Any help would be great!
You can specify the properties file from which to read the list of tokens for the 'replace' task using replacefilterfile:
<replace file="input.txt" replacefilterfile="properties.txt"/>
Similarly, in a filter chain, you can use 'replacetokens' propertyfile:
This will treat each properties file
entry in sample.properties as a
token/key pair:
<loadfile srcfile="${src.file}" property="${src.file.replaced}">
<filterchain>
<filterreader classname="org.apache.tools.ant.filters.ReplaceTokens">
<param type="propertiesfile" value="sample.properties"/>
</filterreader>
</filterchain>
</loadfile>
With the replace task by itself I missed the # delimiters around tokens so I came up with the following solution. You can use any ant property in the template file
<project name="replace" default="replace">
<property file="build.properties" />
<target name="replace">
<!-- create temp file with properties -->
<tempfile property="temp.replace" suffix=".properties"/>
<echoproperties destfile="${temp.replace}" />
<!-- replace name=value with #name#=value -->
<replaceregexp file="${temp.replace}" match="([^=]*)=" replace="#\1#=" byline="true" />
<!-- copy template and replace properties -->
<copy file="template.txt" tofile="replaced.txt" />
<replace file="replaced.txt" replacefilterfile="${temp.replace}" />
</target>
with a template
ANT home #ant.home#
ANT version #ant.java.version#
server name #server.name# ip #server.ip#
this results in
ANT home /usr/share/ant
ANT version 1.7
server name dummy_server_name ip 127.0.0.1
Using fileset form ant-contrib you can read the tokens form properties file and replace multiple tokens over multiple files.
<project name="MyProject" default="replaceToklens" basedir=".">
<property name="profilesProperties" value="${basedir}/environment.properties" />
<property name="build.dir" location="build"/>
<!-- File to Load/ Accessable -->
<property file="${profilesProperties}" />
<target name="replaceToklens">
<taskdef resource="net/sf/antcontrib/antlib.xml">
<classpath>
<pathelement location="${basedir}/ant-contrib-1.0b3.jar" />
</classpath>
</taskdef>
<mkdir dir="${build.dir}"/>
<filter filtersfile="${profilesProperties}" />
<copy todir="${build.dir}" filtering="true" overwrite="true">
<fileset dir="${basedir}"> <!-- target/MyProject -->
<include name="*.xml" />
<exclude name="build.xml" />
</fileset>
</copy>
</target>
</project>
folder structure:
ANT
\_ build.xml
\_ environment.properties
\_ server.xml
\_ build
\_ server.xml [replaced with token value]
In order to replace single toke use the following:
<replace file="build/server.xml" token="#keyName#" value="${keyValue}" />

How to access filters in web.xml file set by a .properties file

I am using WAS6.1 as the server(but I guess this should not matter).I have a filters.properties file. It has key value pair (e.g. config.file.name=/usr/home/config.xml). These values are being used in web.xml as shown below:
<context-param>
<param-name>config.file</param-name>
<param-value>#config.file.name#</param-value>
</context-param>
So I have defined a build.xml which uses filterset task from ant to define all those filters but when I try to access the home page it says that not able to find location #config.file.name#. Obviously, these filters are not being loaded properly. Here is my build.xml code which defines the filters during the compilation. What do you think I am missing?
<target name="compile">
<property name="compile.target" location="${project.build.dir}/WEB-INF/classes" />
<property name="project.build.dir.lib" location="${project.build.dir}/WEB-INF/lib" />
<mkdir dir="${compile.target}" />
<mkdir dir="${project.build.dir.lib}" />
<!-- copy the web content into the build location -->
<copy todir="${project.build.dir}">
<fileset dir="${web.project.webcontent.dir}" excludes="**/classes/**" />
**<filterset>
<filtersfile file="${web.project.src.dir}/filters/${file.filter.name}" />
</filterset>**
</copy>
<!-- compile the java source and put it in the classes directory -->
<javac classpathref="classpath" srcdir="${web.project.src.dir}" destdir="${compile.target}" debug="${javac.debug}" deprecation="${javac.deprecation}" fork="${javac.fork}" memoryMaximumSize="${javac.memoryMaximumSize}" nowarn="${javac.nowarn}" failonerror="${javac.failonerror}">
</javac>
<!-- copy all the non-java resources (properties, etc) into the classes directory-->
<copy todir="${compile.target}">
<fileset dir="${web.project.src.dir}">
<exclude name="**/*.java" />
<exclude name="filters/**" />
</fileset>
</copy>
<!-- Create a jar file from the ${compile.target} folder -->
<jar jarfile="${project.build.dir.lib}/${ant.jar.file}.jar" excludes="filters/**" basedir="${compile.target}" />
</target>
Your feedback is highly appreciated.
Thanks for looking at my post. It is working fine. The main issue was that the path to the properties file was not correct.

How do I "expand" an ant path (accessed with refId=..) to all files in the path except some?

I am trying to get ant4eclipse to work and I have used ant a bit, but not much above a simple scripting language. We have multiple source folders in our Eclipse projects so the example in the ant4eclipse documentation needs adapting:
Currently I have the following:
<target name="build">
<!-- resolve the eclipse output location -->
<getOutputpath property="classes.dir" workspace="${workspace}" projectName="${project.name}" />
<!-- init output location -->
<delete dir="${classes.dir}" />
<mkdir dir="${classes.dir}" />
<!-- resolve the eclipse source location -->
<getSourcepath pathId="source.path" project="." allowMultipleFolders='true'/>
<!-- read the eclipse classpath -->
<getEclipseClasspath pathId="build.classpath"
workspace="${workspace}" projectName="${project.name}" />
<!-- compile -->
<javac destdir="${classes.dir}" classpathref="build.classpath" verbose="false" encoding="iso-8859-1">
<src refid="source.path" />
</javac>
<!-- copy resources from src to bin -->
<copy todir="${classes.dir}" preservelastmodified="true">
<fileset refid="source.path">
<include name="**/*"/>
<!--
patternset refid="not.java.files"/>
-->
</fileset>
</copy>
</target>
The task runs successfully, but I cannot get the to work - it is supposed to copy all non-java files over too to emulate the behaviour of eclipse.
So, I have a pathId named source.path which contains multiple directories, which I somehow needs to massage into something the copy-task like. I have tried nesting which is not valid, and some other wild guesses.
How can I do this - thanks in advance.
You might consider using pathconvert to build a pattern that fileset includes can work with.
<pathconvert pathsep="/**/*," refid="source.path" property="my_fileset_pattern">
<filtermapper>
<replacestring from="${basedir}/" to="" />
</filtermapper>
</pathconvert>
That will populate ${my_fileset_pattern} with a string like:
1/**/*,2/**/*,3
if source.path consisted of the three directories 1, 2, and 3 under the basedir. We're using the pathsep to insert wildcards that will expand to the full set of files later.
The property can now be used to generate a fileset of all the files. Note that an extra trailing /**/* is needed to expand out the last directory in the set. Exclusion can be applied at this point.
<fileset dir="." id="my_fileset" includes="${my_fileset_pattern}/**/*">
<exclude name="**/*.java" />
</fileset>
The copy of all the non-java files then becomes:
<copy todir="${classes.dir}" preservelastmodified="true">
<fileset refid="my_fileset" />
</copy>
That will copy the source files over retaining the source directory structure under todir. If needed, the flatten attribute of the copy task can be set to instead make all the source files copy directly to todir.
Note that the pathconvert example here is for a unix fileseystem, rather than windows. If something portable is needed, then the file.separator property should be used to build up the pattern:
<property name="wildcard" value="${file.separator}**${file.separator}*" />
<pathconvert pathsep="${wildcard}," refid="source.path" property="my_fileset">
...
You could use the foreach task from the ant-contrib library:
<target name="build">
...
<!-- copy resources from src to bin -->
<foreach target="copy.resources" param="resource.dir">
<path refid="source.path"/>
</foreach>
</target>
<target name="copy.resources">
<copy todir="${classes.dir}" preservelastmodified="true">
<fileset dir="${resource.dir}" exclude="**/*.java">
</copy>
</target>
If your source.path contains file paths as well then you could the if task (also from ant-contrib) to prevent attempting to copy files for a file path, e.g.
<target name="copy.resources">
<if>
<available file="${classes.dir}" type="dir"/>
<then>
<copy todir="${classes.dir}" preservelastmodified="true">
<fileset dir="${resource.dir}" exclude="**/*.java">
</copy>
</then>
</if>
</target>

Resources