Let's say I have a directory structure like this:
animals/dog/details
animals/cat/details
animals/frog/details
animals/horse/details
Using ant, I would like to rename all sub-directories under animals called details to now be named new. So the result would be this:
animals/dog/new
animals/cat/new
animals/frog/new
animals/horse/new
I've tried something like this:
<move tofile="new">
<path id="directories.to.rename">
<dirset dir="animals">
<include name="**/details"/>
</dirset>
</path>
</move>
But get this error:
Cannot concatenate multiple files into a single file.
You can carry out the rename you describe by means of a mapper. For example:
<move todir="animals">
<dirset dir="animals" includes="**/details" />
<globmapper from="*/details" to="*/new"/>
</move>
(There's a similar example at the end of the move task docs.)
The error you saw arose because you've mixed the single-file mode of the move task (tofile) with multiple-file mode.
There's no need to nest the dirset in a path as the move task accepts any file-based resource collection, including dirset.
Use Ant-Contrib's for task and propertyregex task.
<target name="test">
<for param="detailsDir">
<dirset dir="animals">
<include name="**/details"/>
</dirset>
<sequential>
<propertyregex property="output.dir" input="#{detailsDir}" regexp="(.*)/details" replace="\1" />
<move file="#{detailsDir}" toFile="${output.dir}/new" />
</sequential>
</for>
</target>
Related
I have an ANT target that unzips every jar with a "for" task, but I want to exclude a specific jar called Neo.jar. This is what I have so far, but it is unzipping every single jar that is available to it.
<target name="unzipjars">
<for param="jar">
<sequential>
<unzip dest="${expanded.dirs}" src="#{jar}">
<exclude name="Neo.jar/**"/>
</unzip>
</sequential>
</for>
</target>
I was looking at trying to exclude something from "for param="jar"" but I don't think anything like that exists. The "exclude name" for Neo.jar doesn't seem to work because I believe it doesn't think it is a directory because it is a jar
As you can see here, it's quietly easy.
All you need is to add the tag patternset to your configuration (and you don't need to iterate by the way...). Like this:
<target name="unzipjars">
<unzip dest="${expanded.dirs}">
<patternset>
<exclude name="**/Neo.jar"/>
</patternset>
<fileset dir="${jar}">
<include name="**/*.*"/>
</fileset>
</unzip>
</target>
You can use the if task (http://ant-contrib.sourceforge.net/tasks/tasks/if.html) to filter out the Neo.jar file. For example
<for param="jar">
<sequential>
<if>
<not><equals arg1="#{jar}" arg2="Neo.jar" /></not>
<then><!-- unzip the jar --></then>
</if>
</sequential>
</for>
in our project we use Ant to distribute files. There is hierarchy of files, and they overwrite files from previous level. Like this: default - level1 - level2. Currently it is made the way:
<copy todir="...">
<fileset dir="${root}/default" includes="**/*" excludes="file1" />
<fileset dir="${root}/level1" includes="**/*" />
<fileset dir="${root}/level2" includes="**/*" excludes="file2"/>
</copy>
So we expected that all the folders contain file with the same name, it will be taken from level2 directory.
Not long ago we moved to a new build box with another version of Java and we discovered that the order of filesets is broken.
Is there a way to fix this issue without modifying ant config files? We have a big number of it. If there is no way, how can I got it off cheap? Thank you.
I don't know if the order of filesets is quaranteed, but the order of copy tasks is.
So, following the suggestion of this answer, it might be a good idea to write several copy tasks with overwrite="true":
<copy todir="...">
<fileset dir="${root}/default" includes="**/*" excludes="file1" />
</copy>
<copy todir="...">
<fileset dir="${root}/level1" includes="**/*" overwrite="true" />
</copy>
<copy todir="...">
<fileset dir="${root}/level2" includes="**/*" excludes="file2" overwrite="true" />
</copy>
You can wrap a sort resource collection around your filesets and ensure the order used by copy this way. Sort itself only likes a single resource collection as child element, so you need to wrap the fileset inside a union or resources container.
See http://ant.apache.org/manual/Types/resources.html#sort
Something like
<sort>
<resources>
<fileset dir="${root}/default" includes="**/*" excludes="file1" />
<fileset dir="${root}/level1" includes="**/*" />
<fileset dir="${root}/level2" includes="**/*" excludes="file2"/>
</resources>
<name/>
</sort>
to sort the files by name.
I'm looking for a way to load properties from a file in ant script. Specifically, I want to loop through a list of properties files, and on each loop load the properties from the current file and do something with it. Something like this:
<for param="file">
<path>
<fileset containing my properties files.../>
</path>
<sequential>
<property file="#{file}" prefix="fromFile"/>
<echo message="current file: #{file}"/>
<echo message="property1 from file: ${fromFile.property1}"/>
</sequential>
</for>
The code above results in only the first properties file from being read, even though each loop does go through each properties file name. I know property is immutable, and I can get around it by using local task or variable task from ant-contrib. However, I don't know how to apply them here, or if they even contribute to a solution in this case.
Here I used Antcontrib and two property files in the same directory as the build.xml.
p1.properties:
property1=from p1
p2.properties:
property1=from p2
The trick is to use antcall inside the for loop to call another target. Properties set in the called target at not propagated back to the caller.
build.xml:
<project name="test" default="read.property.files">
<taskdef resource="net/sf/antcontrib/antcontrib.properties">
<classpath>
<pathelement location="ant-contrib/ant-contrib-1.0b3.jar"/>
</classpath>
</taskdef>
<target name="read.property.files">
<for param="file">
<path>
<fileset dir="." includes="*.properties"/>
</path>
<sequential>
<antcall target="read.one.property.file">
<param name="propfile" value="#{file}"/>
</antcall>
</sequential>
</for>
</target>
<target name="read.one.property.file">
<property file="${propfile}" />
<echo message="current file: ${propfile}" />
<echo message="property1 from file: ${property1}"/>
</target>
</project>
The output is:
Buildfile: /home/vanje/anttest/build.xml
read.property.files:
read.one.property.file:
[echo] current file: /home/vanje/anttest/p1.properties
[echo] property1 from file: from p1
read.one.property.file:
[echo] current file: /home/vanje/anttest/p2.properties
[echo] property1 from file: from p2
BUILD SUCCESSFUL
Total time: 0 seconds
In my original question I had trouble loading entire properties file from within for loop (from ant-contrib). Turns out inside for loops, ant-contrib's own var task works just the same as property task from pure ant. All I have to do is replace <property file="#{file}" prefix="fromFile"/> with <var file="#{file}"/>. The properties loaded will be overwritten with the latest values and I don't even need prefix attribute to keep track of which loop I'm currently on.
When I use a path reference ID, Ant seems to evaluate any variables inside the definition before any tasks run. For example, ${common.dist} and ${common.lib} below seem to be evaluated before any tasks run.
<path id="compile.classpath">
<fileset dir="lib">
<include name="*.jar" />
</fileset>
<fileset dir="${common.dist}">
<include name="*.jar" />
</fileset>
<fileset dir="${common.lib}">
<include name="*.jar" />
</fileset>
</path>
In the Ant output I see something like this:
Adding reference: compile.classpath
Property "common.dist" has not been set
Property "common.lib" has not been set
...
Build sequence for target(s) `package' is [...]
Complete build sequence is [...]
This makes it seem like the path reference is being processed before any targets are run.
I have a compile target like this:
<target name="compile" depends="init,common">
<javac destdir="build/classes" debug="true" deprecation="true" optimize="true">
<src path="src/java" />
<classpath>
<path refid="compile.classpath" />
</classpath>
</javac>
</target>
If I copy the guts of the path reference into the classpath element inside the compile target, things seem to work fine.
Any tasks outside a target are executed on every build, in order of appearance in the build.xml, before any targets are run. If you want to use properties in a <path> defined outside a target then you need to put the <property> task that defines the properties also outside a target, and before the <path>. If you need to load the properties within a target then you'll have to put the <path> definition inside a target too (either the same one or one that runs after the one defining the properties).
See this question (and my answer) for more details.
The answer is in the Ant manual - path like structures:
By default a path like structure will re-evaluate all nested resource
collections whenever it is used, which may lead to unnecessary
re-scanning of the filesystem ...
I think you maybe forgot to set the ${common.dist} and ${common.lib} properties. They should be outside any target:
<property name="common.dist" location="dist"/>
<property name="common.lib" location="lib"/>
I'm using an Ant build script to collate my Eclipse-based application for distribution.
One step of the build is to check that the correct libraries are present in the build folders. I currently use the Ant command for this. Unfortunately, I have to amend the script each time I switch to a new Eclipse build (since the version numbers will have updated).
I don't need to check the version numbers, I just need to check that the file's there.
So, how do I check for:
org.eclipse.rcp_3.5.0.*
instead of:
org.eclipse.rcp_3.5.0.v20090519-9SA0FwxFv6x089WEf-TWh11
using Ant?
cheers,
Ian
You mean, something like (based on the pathconvert task, after this idea):
<target name="checkEclipseRcp">
<pathconvert property="foundRcp" setonempty="false" pathsep=" ">
<path>
<fileset dir="/folder/folder/eclipse"
includes="org.eclipse.rcp_3.5.0.*" />
</path>
</pathconvert>
</target>
<target name="process" depends="checkEclipseRcp" if="foundRcp">
<!-- do something -->
</target>
A slightly shorter and more straightforward approach with resourcecount condition:
<target name="checkEclipseRcp">
<condition property="foundRcp">
<resourcecount when="greater" count="0">
<fileset file="/folder/folder/eclipse/org.eclipse.rcp_3.5.0.*"/>
</resourcecount>
</condition>
</target>
<target name="process" depends="checkEclipseRcp" if="foundRcp">
<!-- do something -->
</target>
The pathconvert task is probably the preferred way to go in most cases. But it creates a little problem when the directory tree is very large and one uses the echoproperties task. With a very large directory tree, the string generated by pathconvert can be huge. Then echoproperties sprays the huge string, making the output more difficult to work with. I use a macrodef on Linux that creates a property set to "1" if there are files in the directory:
<macrodef name="chkDirContents" >
<attribute name="propertyName" />
<attribute name="dirPath" />
<attribute name="propertyFile" />
<sequential>
<exec executable="sh" dir="." failonerror="false" >
<arg value="-c" />
<arg value='fyles=`ls -1 #{dirPath} | head -1` ; if [ "$fyles" != "" ] ; then echo #{propertyName}=1 > #{propertyFile} ; fi' />
</exec>
</sequential>
</macrodef>
<target name="test" >
<tempfile destdir="." property="temp.file" deleteonexit="true" />
<chkDirContents propertyName="files.exist" dirPath="./target_dir" propertyFile="${temp.file}" />
<property file="${temp.file}" />
<echoproperties/>
</target>
Executing the "test" target will generate the following echoproperties line if there are files in the ./target_dir/ directory:
[echoproperties] files.exist=1
What "test" does:
It generates a temporary filename, ${temp.file}, that can later be used as a property file.
It then executes the macrodef, which calls the shell to check the contents of the dirPath directory. If there are any files or directories in dirPath, it assigns the propertyName property a value of 1 in the temporary file. It then reads the file and sets the property given in the file. If the file is empty, no property is defined.
Note that the temporary file could be reused for subsequent calls of the macrodef if desired. On the other hand, of course, once a property is set, it is immutable.