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

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}"/>

Related

How can I add files in separate folders to a ZIP archive?

I am trying to add 3 diferent TXT files to one ZIP file.
Those 3 TXT files are in separate folders but I have their path.
In the end, I just want a ZIP file with those 3 files in it.
Also, if one of them does not exist, will it include the 2 other ones or will it throw an exception and create no ZIP file?
<zip destfile="your.zip">
<fileset dir="path1" includes="file1.txt"/>
<fileset dir="path2" includes="file2.txt"/>
<fileset dir="path3" includes="file3.txt"/>
</zip>
You can include multiple filesets into a single zip file.
If the path doesn't exist, you'll get an error. If a file doesn't exist, it doesn't care. If none of the files exist, you'll get a warning.

How do you extract from a binary file created with Ant <concat>?

I have a binary file that was created with the Ant <concat> task:
<target name="bun.create">
<concat destfile="final.bun" binary="yes">
<filelist dir="scripts/" files="script.sh"/>
<filelist dir="working/" files="files.tgz"/>
</concat>
<chmod file="final.bun" perm="+x"/>
</target>
I have the final.bun file and am wondering how I can extract the script.sh and files.tgz from it, without executing it? I know the unzip command doesn't work.
I don't think there's any way to do this - at least not easily. I'm sure there are some old Unis heads who can use the dd utility to do this, but that is still a hack. What you have done is create a file format that doesn't really exist. It's like concating together a Word document with an Excel document.
In fact, I'm not even sure if the <concat> task wouldn't shred the binary file into an incomprehensible mess as it was trying to concatenate it.
If you must put both files together, use the <zip/>, or <tar/> task to combine both files together. Yes, I know that files.tgz is already a compressed archive, but this would be the best way to put those two files together.

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.

How do I copy a directory from one location to another using Ant?

I am looking to copy a directory from one location to another. However, after looking at "copy" and "copyDir" (which is deprecated) it seems that Ant, by default, only copies the content of one location to another, not the actual directory (and everything in it).
So, as an example I have the following:
./Foo/test.txt
And I apply the following piece of Ant:
<copy todir="./build">
<fileset dir="./Foo"/>
</copy>
The result looks like this:
./build/test.txt
Whereas I would like it to be:
./build/Foo/test.txt
Hope that makes sense. How can I do that?
What about this:
<copy todir="./build">
<fileset dir=".">
<include name="Foo/**"/>
</fileset>
</copy>
To preserve the structure you would need to be copying the parent folder. There is a flatten option in the copy command, but it defaults to false. You can also make the directory structure you want, then copy at the file level straight into your fresh folder.
Here is the relevant line from the reference:
When a <fileset> is used to select files to copy,
the todir attribute must be set. Files that are
located under the base directory of the <fileset>
will be copied to a directory under the destination
directory matching the path relative to the base
directory of the <fileset>, unless the flatten
attribute is set to true.
The problem lies in that you are picking up the Foo folder, so you only get the files under it, you need to pick the folder above Foo to get the fileset where the ~/Foo/file.txt structure exists.

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

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.

Resources