Ant: Create directory containing file if it doesn't already exist? - ant

Basically, I get a path like "C:\test\subfolder1\subfolder2\subfolder3\myfile.txt", but it's possible that subfolders 1-3 don't exist already, which means I'd get an exception if I try to write to the file.
Is there a way to create the directory structure the target file is in, either by using some task that creates the structure when it outputs to the file and then deleting the file, or by parsing the directory part of the path and using the mkdir task first?

Ant will create the full tree of directories for you when you use the <mkdir> task. So you just need to use the <dirname> task to get the directory name from the file name.
<dirname property="directoryProperty" file="${filePathProperty}"/>
<mkdir dir="${directoryProperty}" />
The first line extracts the directory portion of your file path and stores it in the directoryProperty property. The second line creates the directory (and any parent directories that don't exist).

This task works well
<mkdir dir="${file}/../"/>
Sometimes we could have an alternate choice, using touch task
<touch file="${file}" mkdirs="true" verbose="true"/>
This task should do the job but would have a side effect to create the file with zero size

Just make failonerror=false to avoid the error to stop the whole logic.
<delete includeemptydirs="true" failonerror="false">
<fileset dir="${builder-base.dir}" includes="**/*"/>
</delete>

Using the
<mkdir dir="${dir}"/ >
inside your <target> tag should work, but I am not sure what else you want to do along with mkdir?

I'm not 100% sure it'll work but you might be able to do something like the following to make the parent directory you're after:
<mkdir dir="${file}/../"/>
If that doesn't work straight off then it might be worth defining a property using the location syntax before creating a directory with the new property:
<property name="dir" location="${file}/../" />
<mkdir dir="${dir}" />

Well-behaved Ant tasks are generally expected to create any necessary directory structures unless there is a good reason not to.
Are you writing a task? If so you should add the directory creation logic to your task. If you are getting the task from a third party you should point this fact out to them and have them fix their task. Failing that Dan's solution should work.

Related

Sync Task's Overwrite Attribute Copies Identical Files

I've come across an issue with Ant's Sync task where files are being copied unnecessarily. The goal is to update everything in the ${destination} directory with the contents of the ${source} directory, even if the file in the ${destination} is newer. Based on Ant's documentation, I've added an overwrite attribute to to ensure the ${destination} is overwritten.
<target name="test">
<sync todir="${destination}" overwrite="true" granularity="5000">
<fileset dir="${source}">
</fileset>
</sync>
</target>
This task correctly overwrites the ${destination}, but the file is always copied, even when the source and destination are identical. This leads to a lot of unnecessary traffic.
Based on the documentation, I attempted to configure the granularity attribute, but this doesn't appear to have any effect. I'm also running this test between two directories on the same machine, so I wouldn't expect timestamp differences (certainly not of more than 5 seconds).
Any thoughts about why the Sync task and overwrite attribute function in this way? Are there any solutions using the default set of Ant tasks to prevent the unnecessary file copying?
If you use the sync task with overwrite="true", you will get this behavior.
You could use it with overwrite="false", and then follow up with a copy task that only copy the files that are existing but different, with the different selector, e.g.:
<copy todir="${destination}">
<fileset dir="${source}">
<different targetdir="${destination}" ignoreFileTimes="true"/>
</fileset>
</copy>

Zip task in Ant - incuding empty and non-empty subdirectories

Alright, I've been going slightly insane with this one - what bugs me more is that I'm sure a few years back I'd done something similar to what I'm trying to do now.
Let me say right off the bat, I am using ANT version 1.8.2, so I don't have the 1.6.2 issues with zip and empty folders. Most of my searching seems to bring up the 1.6.2 issue.
So, here's my build.xml snippet:
<target name="zip" depends="jar">
<echo>Creating zip file...</echo>
<zip destfile="${dist}/${zipfilename}">
<zipfileset dir="${dist}" includes="${jarfilename}" />
</zip>
<echo>...complete</echo>
</target>
This does what it's supposed to do, take the jar file from the {dist} directory, and put it in the zip so that the {dist} directory is NOT maintained - ie: the jar file gets extracted to wherever the zip file is.
HOWEVER, I would like to know how to do the following:
include a {config} directory and ALL the files within it except for one called "test-config.xml"
include two separate empty directories - say {subdir1} and {subdir2}. They may or may not have files in them, but I do NOT want ANY files in those directories when they get zipped.
For that second part with the empty directories, I'm perfectly content having the zip task create those in the zip itself without necessarily creating them on the local file system. In fact, the ideal would be if they'd be in the zip file and NOT be on the local file system - but I'll take whatever method I can get that works.
So, my local file system will look like so:
dist/MyJar.jar
config/config1.xml
config/config2.xml
config/test-config.xml
subdir1/ (may or may not exist and may or may not have files in it)
subdir2/ (may or may not exist and may or may not have files in it)
I want the ZIP file, when unzipped, to result in:
MyJar.jar
config/config1.xml
config/config2.xml
subdir1/
subdir2/
How do I accomplish this? I've tried variants of fileset, dirset, zipfileset, and am failing at every attempt. The best I've managed is to get the files in the {config} directory to get extracted as if the {config} directory doesn't exist... the hierarchy for that is NOT maintained, but I WANT it to be.
Here's one way you can use zipfileset to handle the config directory:
<zipfileset dir="." includes="config/*" excludes="config/test-config.xml"/>
The only way I can think of getting empty empty folders into the zip is to create them on the file system, add them using a zipfileset and then delete them once you have created the zip file.
Here is a fragment that does what you need (NOTE: I am using folder and file names based on your example; you can replace these with property references as needed):
<tempfile property="temp.file" destDir="." prefix="foo"/>
<mkdir dir="${temp.file}/subdir1"/>
<mkdir dir="${temp.file}/subdir2"/>
<zip destfile="${dist}/${zipfilename}">
<zipfileset dir="${dist}" includes="${jarfilename}"/>
<zipfileset dir="${temp.file}" includes="*"/>
<zipfileset dir="." includes="config/*" excludes="config/test-config.xml"/>
</zip>
<delete dir="${temp.file}"/>

Changing properties file inside a jar with Ant

I have a settings in a properties file located within a jar that I wish to alter at build-time using ant. Ideally if I am able to search for a specific text in the properties file and replace it easily, I would like to do that but isn't sure how.
So I was thinking I can overwrite it with another properties file that has the new settings already predefined. The jar already exists in my directory and the hierarchy of my jar is as follows:
food.jar
/com/food/donut.properties
some file...
some file...
If I had another donut.properties file with a different setting located in a different directory. How can I overwrite it with ant?
Thanks for the help, much appreciated!
EDIT:
With the following code I was able to copy the properties file into the jar. But whenever I attempt to copy the new properties file into the same directory of the old properties file, it does not get replaced. (i.e. If i change the prefix to 'com' i can see the new properties file being inserted into the jar. If the prefix is changed to com/food, nothing is replaced. What am i doing incorrectly?
<jar destfile="${dist.dir}/food.jar" update="true">
<zipfileset file="donut.xml" prefix="com/food/" />
</jar>
needs Ant 1.8.x
Step 1)
edit your propertyfile, multiple nested entry elements possible :
<propertyfile file="/path/to/propertyfile/foo.properties">
<!-- will change an existing key named 'somekey' with the value 'foo' inplace -->
<entry key="somekey" value="foo"/>
</propertyfile>
see Ant Manual propertyfile
Step 2)
update your jar with the altered propertyfile :
<jar destfile="/path/to/your/foo.jar" update="true">
<fileset dir="/path/to/propertyfile" includes="*.properties"/>
</jar>
for renaming use nested mapper like that :
<jar destfile="/path/to/your/foo.jar" update="true">
<mappedresources>
<fileset dir="." includes="*.properties"/>
<globmapper from="*.properties" to="/com/xml/*.properties"/>
</mappedresources>
</jar
The ant documentation of the jar task says:
The update parameter controls what happens if the JAR file already
exists. When set to yes, the JAR file is updated with the files
specified. When set to no (the default) the JAR file is overwritten.
An example use of this is provided in the Zip task documentation.
Please note that ZIP files store file modification times with a
granularity of two seconds. If a file is less than two seconds newer
than the entry in the archive, Ant will not consider it newer.
You might need to make sure the properties file is newer than the one in the jar file. Using the touch task could solve the problem.
Or you might just unzip the jar in the temp directory, copy the properties file with the copy task and its overwrite attribute set to true, and re-jar the contents of the temp directory.

concat as resource collection in zip not working?

I'm trying to use <concat> as a resource collection in a <zip> task and, according to the documentation, this should work. I'd like to do this because some of the files I want to include in the zip need to have some properties expanded, so I will also add a <filterchain> to the <concat> to do this. I'd prefer to do it directly rather than copying to a temp location (with property substitution) and including the copy in the zip file.
However, I can't seem to get <zip> to correctly use the <concat> element.
A simplified example of what I have so far:
<zip destfile="target/dist.zip">
<concat>
<fileset file="CHANGES.txt" />
</concat>
</zip>
This creates a zip file containing several directories all named concat (C: (obviously this is on a Windows machine).
What am I missing?
A colleague and I came up with the answer by looking through the <zip> and <concat> source. There are really two answers:
<concat>'s implementation of the ResourceCollection interface is odd, but we understand why.
There's a way around that.
For #1, while <concat> is a ResourceCollection (like FileSet), under the hood it returns as the name of single Resource it contains a hard-coded value:
"concat (" + String.valueOf(c) + ")";
Look familiar?
The name of resources is normally ignored--except by <zip> and its related tasks, which uses the resource name as the ZipEntry name. Since <concat> returns the odd-looking name, that's what we get in the zip file.
I haven't quite figured out why I get multiple entries, but it doesn't matter: the observation leads to a convoluted solution.
Since I know the name of the ZipEntry I want to create, I can use a <mapper> to give the <concat> resource a name. Here's what I came up with in all its glory:
<zip destfile="target/distribution.zip">
<fileset dir=".">
<exclude name="target/**" />
<exclude name="CHANGES.txt" />
</fileset>
<mappedresources>
<concat>
<fileset file="CHANGES.txt" />
<filterchain>
<expandproperties />
</filterchain>
</concat>
<mergemapper to="CHANGES.txt" />
</mappedresources>
</zip>
As my colleague says "In Ant every problem can be solved using a mapper."
This only works for Ant 1.8+ because <mappedresources> was added in that release.
I'm going to post some comments to the Ant mailing list and suggest a couple of enhancements:
Allow a resource name to be specified as an attribute on <concat> when it's being used as a ResourceCollection.
Throw an exception (and don't create a synthetic value) if getName() is called without having a value specified.
Finally, though not directly related, I do wish <expandproperties> could take a <propertyset> so I can control which properties get substituted.
Do you want the final zip to contain a single file or multiple files? As far as I can see, using concat (when done successfully, which isn't done above) would produce a single file, the result of concatenation of all files in the resource collection.
If you want multiple files rather than concatenation, I think intermediate copy is what you'll need.
From Ant manual for the concat task:
Since Apache Ant 1.7.1, this task can
be used as a Resource Collection that
will return exactly one resource.

ant iterate over files

I want to iterate over a list of jars (undefined number) and add them all to the jar file.
To add them I plan to use something like this:
<jar id="files" jarfile="all.jar">
<zipfileset src="first.jar" includes="**/*.java **/*.class"/>
<zipfileset src="second.jar" includes="**/*.java **/*.class"/>
</jar>
but how do I iterate over them? I don't have ant-contrib
Thanks!
Just use zipgroupfileset with the Ant Zip task
<zip destfile="out.jar">
<zipgroupfileset dir="lib" includes="*.jar"/>
</zip>
This will flatten all included jar libraries' content.
If you do not have access to ant-contrib For task, you may end up to have to define your custom Task for doing what you need...
If you have ant1.6 and above, you can also try subant (see New Ant 1.6 Features for Big Projects):
If you use <subant>'s genericantfile attribute it kind of works like <antcall> invoking a target in the same build file that contains the task.
Unlike <antcall>, <subant> takes a list or set of directories and will invoke the target once for each directory setting the project's base directory.
This is useful if you want to perform the exact same operation in an arbitrary number of directories.

Resources