git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@267998 13f79535-47bb-0310-9956-ffa450edef68master
| @@ -25,7 +25,7 @@ | |||||
| <li>Dave Walend (<a href="mailto:dwalend@cs.tufts.edu">dwalend@cs.tufts.edu</a>)</li> | <li>Dave Walend (<a href="mailto:dwalend@cs.tufts.edu">dwalend@cs.tufts.edu</a>)</li> | ||||
| </ul> | </ul> | ||||
| <p>Version 1.2 - 2000/09/14</p> | |||||
| <p>Version 1.2 - 2000/09/15</p> | |||||
| <hr> | <hr> | ||||
| <h2>Table of Contents</h2> | <h2>Table of Contents</h2> | ||||
| @@ -870,7 +870,9 @@ same patterns as the example before.</p> | |||||
| <li><a href="#tstamp">Tstamp</a></li> | <li><a href="#tstamp">Tstamp</a></li> | ||||
| <li><a href="#unzip">Unjar</a></li> | <li><a href="#unzip">Unjar</a></li> | ||||
| <li><a href="#untar">Untar</a></li> | <li><a href="#untar">Untar</a></li> | ||||
| <li><a href="#unzip">Unwar</a></li> | |||||
| <li><a href="#unzip">Unzip</a></li> | <li><a href="#unzip">Unzip</a></li> | ||||
| <li><a href="#war">War</a></li> | |||||
| <li><a href="#zip">Zip</a></li> | <li><a href="#zip">Zip</a></li> | ||||
| </ul> | </ul> | ||||
| <hr> | <hr> | ||||
| @@ -3700,9 +3702,9 @@ initialization target.</p> | |||||
| <h3>Examples</h3> | <h3>Examples</h3> | ||||
| <pre> <tstamp/></pre> | <pre> <tstamp/></pre> | ||||
| <hr> | <hr> | ||||
| <h2><a name="unzip">Unjar/Unzip</a></h2> | |||||
| <h2><a name="unzip">Unjar/Unwar/Unzip</a></h2> | |||||
| <h3>Description</h3> | <h3>Description</h3> | ||||
| <p>Unzips a zip- or jarfile.</p> | |||||
| <p>Unzips a zip-, war- or jarfile.</p> | |||||
| <p>For JDK 1.1 "last modified time" field is set to current time instead of being | <p>For JDK 1.1 "last modified time" field is set to current time instead of being | ||||
| carried from zipfile.</p> | carried from zipfile.</p> | ||||
| <p>File permissions will not be restored on extracted files.</a> | <p>File permissions will not be restored on extracted files.</a> | ||||
| @@ -3762,6 +3764,129 @@ carried from tarfile.</p> | |||||
| </code></p> | </code></p> | ||||
| </blockquote> | </blockquote> | ||||
| <hr> | <hr> | ||||
| <h2><a name="war">War</a></h2> | |||||
| <h3>Description</h3> | |||||
| <p>An extension of the <a name="#jar">Jar</a> task with special | |||||
| treatment for files that should end up in the <code>lib</code>, | |||||
| <code>classes</code> or <code>WEB-INF</code> directories of the Web | |||||
| Application Archive.</p> | |||||
| <h3>Parameters</h3> | |||||
| <table border="1" cellpadding="2" cellspacing="0"> | |||||
| <tr> | |||||
| <td valign="top"><b>Attribute</b></td> | |||||
| <td valign="top"><b>Description</b></td> | |||||
| <td align="center" valign="top"><b>Required</b></td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td valign="top">warfile</td> | |||||
| <td valign="top">the war-file to create.</td> | |||||
| <td valign="top" align="center">Yes</td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td valign="top">webxml</td> | |||||
| <td valign="top">The deployment descriptor to use (WEB-INF/web.xml).</td> | |||||
| <td valign="top" align="center">Yes</td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td valign="top">basedir</td> | |||||
| <td valign="top">the directory from which to jar the files.</td> | |||||
| <td valign="top" align="center">No</td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td valign="top">compress</td> | |||||
| <td valign="top">Not only store data but also compress them, defaults to true</td> | |||||
| <td align="center" valign="top">No</td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td valign="top">includes</td> | |||||
| <td valign="top">comma separated list of patterns of files that must be | |||||
| included. All files are included when omitted.</td> | |||||
| <td valign="top" align="center">No</td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td valign="top">includesfile</td> | |||||
| <td valign="top">the name of a file. Each line of this file is | |||||
| taken to be an include pattern</td> | |||||
| <td valign="top" align="center">No</td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td valign="top">excludes</td> | |||||
| <td valign="top">comma separated list of patterns of files that must be | |||||
| excluded. No files (except default excludes) are excluded when omitted.</td> | |||||
| <td valign="top" align="center">No</td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td valign="top">excludesfile</td> | |||||
| <td valign="top">the name of a file. Each line of this file is | |||||
| taken to be an exclude pattern</td> | |||||
| <td valign="top" align="center">No</td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td valign="top">defaultexcludes</td> | |||||
| <td valign="top">indicates whether default excludes should be used or not | |||||
| ("yes"/"no"). Default excludes are used when omitted.</td> | |||||
| <td valign="top" align="center">No</td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td valign="top">manifest</td> | |||||
| <td valign="top">the manifest file to use.</td> | |||||
| <td valign="top" align="center">No</td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td valign="top">whenempty</td> | |||||
| <td valign="top">Behavior to use if no files match.</td> | |||||
| <td valign="top" align="center">No</td> | |||||
| </tr> | |||||
| </table> | |||||
| <h3>Nested elements</h3> | |||||
| <h4>lib</h4> | |||||
| <p>The nested <code>lib</code> element specifies a <a | |||||
| href="#fileset">FileSet</a>. All files included in this fileset will | |||||
| end up in the <code>lib</code> directory of the war file.</p> | |||||
| <h4>classes</h4> | |||||
| <p>The nested <code>classes</code> element specifies a <a | |||||
| href="#fileset">FileSet</a>. All files included in this fileset will | |||||
| end up in the <code>classes</code> directory of the war file.</p> | |||||
| <h4>webinf</h4> | |||||
| <p>The nested <code>webinf</code> element specifies a <a | |||||
| href="#fileset">FileSet</a>. All files included in this fileset will | |||||
| end up in the <code>WEB-INF</code> directory of the war file. If this | |||||
| fileset includes a file named <code>web.xml</code>, the file is | |||||
| ignored and you will get a warning.</p> | |||||
| <h3>Examples</h3> | |||||
| <p>Assume the following structure in the project's base directory: | |||||
| <pre> | |||||
| thirdparty/libs/jdbc1.jar | |||||
| thirdparty/libs/jdbc2.jar | |||||
| build/main/com/myco/myapp/Servlet.class | |||||
| src/metadata/myapp.xml | |||||
| src/html/myapp/index.html | |||||
| src/jsp/myapp/front.jsp | |||||
| </pre> | |||||
| then the war file <code>myapp.war</code> created with | |||||
| <pre> | |||||
| <war warfile="myapp.war" webxml="src/metadata/myapp.xml"> | |||||
| <fileset dir="src/html/myapp" /> | |||||
| <fileset dir="src/jsp/myapp" /> | |||||
| <lib dir="thirdparty/libs"> | |||||
| <exclude name="jdbc1.jar" /> | |||||
| </lib> | |||||
| <classes dir="build/main" /> | |||||
| </war> | |||||
| </pre> | |||||
| will consist of | |||||
| <pre> | |||||
| WEB-INF/web.xml | |||||
| lib/jdbc2.jar | |||||
| classes/com/myco/myapp/Servlet.class | |||||
| META-INF/MANIFEST.MF | |||||
| index.html | |||||
| front.jsp | |||||
| </pre> | |||||
| using Ant's default manifest file. The content of | |||||
| <code>WEB-INF/web.xml</code> is identical to | |||||
| <code>src/metadata/myapp.xml</code>.</p> | |||||
| <hr> | |||||
| <h2><a name="zip">Zip</a></h2> | <h2><a name="zip">Zip</a></h2> | ||||
| <h3>Description</h3> | <h3>Description</h3> | ||||
| <p>Creates a zipfile.</p> | <p>Creates a zipfile.</p> | ||||
| @@ -69,13 +69,18 @@ public class Jar extends Zip { | |||||
| private File manifest; | private File manifest; | ||||
| public void setJarfile(String jarFilename) { | |||||
| super.setZipfile(jarFilename); | |||||
| super.archiveType = "jar"; | |||||
| public Jar() { | |||||
| super(); | |||||
| archiveType = "jar"; | |||||
| emptyBehavior = "create"; | |||||
| } | |||||
| public void setJarfile(File jarFile) { | |||||
| super.setZipfile(jarFile); | |||||
| } | } | ||||
| public void setManifest(String manifestFilename) { | |||||
| manifest = project.resolveFile(manifestFilename); | |||||
| public void setManifest(File manifestFile) { | |||||
| manifest = manifestFile; | |||||
| } | } | ||||
| protected void initZipOutputStream(ZipOutputStream zOut) | protected void initZipOutputStream(ZipOutputStream zOut) | ||||
| @@ -83,59 +88,51 @@ public class Jar extends Zip { | |||||
| { | { | ||||
| // add manifest first | // add manifest first | ||||
| if (manifest != null) { | if (manifest != null) { | ||||
| super.zipDir(new File(manifest.getParent()), zOut, "META-INF/"); | |||||
| zipDir(new File(manifest.getParent()), zOut, "META-INF/"); | |||||
| super.zipFile(manifest, zOut, "META-INF/MANIFEST.MF"); | super.zipFile(manifest, zOut, "META-INF/MANIFEST.MF"); | ||||
| } else { | } else { | ||||
| String s = "/org/apache/tools/ant/defaultManifest.mf"; | String s = "/org/apache/tools/ant/defaultManifest.mf"; | ||||
| InputStream in = this.getClass().getResourceAsStream(s); | InputStream in = this.getClass().getResourceAsStream(s); | ||||
| if ( in == null ) | if ( in == null ) | ||||
| throw new BuildException ( "Could not find: " + s ); | throw new BuildException ( "Could not find: " + s ); | ||||
| super.zipDir(null, zOut, "META-INF/"); | |||||
| zipDir(null, zOut, "META-INF/"); | |||||
| zipFile(in, zOut, "META-INF/MANIFEST.MF", System.currentTimeMillis()); | zipFile(in, zOut, "META-INF/MANIFEST.MF", System.currentTimeMillis()); | ||||
| } | } | ||||
| } | } | ||||
| protected boolean isUpToDate(FileScanner[] scanners, File zipFile) | |||||
| protected boolean isUpToDate(FileScanner[] scanners, File zipFile) throws BuildException | |||||
| { | { | ||||
| File[] files = grabFiles(scanners); | File[] files = grabFiles(scanners); | ||||
| if (emptyBehavior == null) emptyBehavior = "create"; | |||||
| if (files.length == 0) { | |||||
| if (emptyBehavior.equals("skip")) { | |||||
| log("Warning: skipping JAR archive " + zipFile + | |||||
| " because no files were included.", Project.MSG_WARN); | |||||
| return true; | |||||
| } else if (emptyBehavior.equals("fail")) { | |||||
| throw new BuildException("Cannot create JAR archive " + zipFile + | |||||
| ": no files were included.", location); | |||||
| } else { | |||||
| // create | |||||
| if (!zipFile.exists() || | |||||
| (manifest != null && | |||||
| manifest.lastModified() > zipFile.lastModified())) | |||||
| log("Note: creating empty JAR archive " + zipFile, Project.MSG_INFO); | |||||
| // and continue below... | |||||
| } | |||||
| } | |||||
| if (!zipFile.exists()) return false; | |||||
| if (manifest != null && manifest.lastModified() > zipFile.lastModified()) | |||||
| return false; | |||||
| for (int i=0; i<files.length; i++) { | |||||
| if (files[i].lastModified() > zipFile.lastModified()) { | |||||
| return false; | |||||
| if (manifest != null) { | |||||
| // just add the manifest file to the mix | |||||
| DirectoryScanner ds = new DirectoryScanner(); | |||||
| ds.setBasedir(new File(manifest.getParent())); | |||||
| ds.setIncludes(new String[] {manifest.getName()}); | |||||
| ds.scan(); | |||||
| FileScanner[] myScanners = new FileScanner[scanners.length+1]; | |||||
| System.arraycopy(scanners, 0, myScanners, 0, scanners.length); | |||||
| myScanners[scanners.length] = ds; | |||||
| boolean retval = super.isUpToDate(myScanners, zipFile); | |||||
| if (!retval && files.length == 0) { | |||||
| log("Note: creating empty "+archiveType+" archive " + zipFile, | |||||
| Project.MSG_INFO); | |||||
| } | } | ||||
| } | |||||
| return true; | |||||
| } | |||||
| return retval; | |||||
| protected void zipDir(File dir, ZipOutputStream zOut, String vPath) | |||||
| throws IOException | |||||
| { | |||||
| // First add directory to zip entry | |||||
| if(!vPath.equalsIgnoreCase("META-INF/")) { | |||||
| // we already added a META-INF | |||||
| super.zipDir(dir, zOut, vPath); | |||||
| } else if (emptyBehavior.equals("create") && files.length == 0) { | |||||
| log("Note: creating empty "+archiveType+" archive " + zipFile, | |||||
| Project.MSG_INFO); | |||||
| return false; | |||||
| } else { | |||||
| // all other cases are handled correctly by Zip's method | |||||
| return super.isUpToDate(scanners, zipFile); | |||||
| } | } | ||||
| // no warning if not, it is harmless in and of itself | |||||
| } | } | ||||
| protected void zipFile(File file, ZipOutputStream zOut, String vPath) | protected void zipFile(File file, ZipOutputStream zOut, String vPath) | ||||
| @@ -145,8 +142,9 @@ public class Jar extends Zip { | |||||
| if (!vPath.equalsIgnoreCase("META-INF/MANIFEST.MF")) { | if (!vPath.equalsIgnoreCase("META-INF/MANIFEST.MF")) { | ||||
| super.zipFile(file, zOut, vPath); | super.zipFile(file, zOut, vPath); | ||||
| } else { | } else { | ||||
| log("Warning: selected JAR files include a META-INF/MANIFEST.MF which will be ignored " + | |||||
| "(please use manifest attribute to jar task)", Project.MSG_WARN); | |||||
| log("Warning: selected "+archiveType+" files include a META-INF/MANIFEST.MF which will be ignored " + | |||||
| "(please use manifest attribute to "+archiveType+" task)", Project.MSG_WARN); | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -0,0 +1,194 @@ | |||||
| /* | |||||
| * The Apache Software License, Version 1.1 | |||||
| * | |||||
| * Copyright (c) 2000 The Apache Software Foundation. All rights | |||||
| * reserved. | |||||
| * | |||||
| * Redistribution and use in source and binary forms, with or without | |||||
| * modification, are permitted provided that the following conditions | |||||
| * are met: | |||||
| * | |||||
| * 1. Redistributions of source code must retain the above copyright | |||||
| * notice, this list of conditions and the following disclaimer. | |||||
| * | |||||
| * 2. Redistributions in binary form must reproduce the above copyright | |||||
| * notice, this list of conditions and the following disclaimer in | |||||
| * the documentation and/or other materials provided with the | |||||
| * distribution. | |||||
| * | |||||
| * 3. The end-user documentation included with the redistribution, if | |||||
| * any, must include the following acknowlegement: | |||||
| * "This product includes software developed by the | |||||
| * Apache Software Foundation (http://www.apache.org/)." | |||||
| * Alternately, this acknowlegement may appear in the software itself, | |||||
| * if and wherever such third-party acknowlegements normally appear. | |||||
| * | |||||
| * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software | |||||
| * Foundation" must not be used to endorse or promote products derived | |||||
| * from this software without prior written permission. For written | |||||
| * permission, please contact apache@apache.org. | |||||
| * | |||||
| * 5. Products derived from this software may not be called "Apache" | |||||
| * nor may "Apache" appear in their names without prior written | |||||
| * permission of the Apache Group. | |||||
| * | |||||
| * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED | |||||
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |||||
| * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||||
| * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR | |||||
| * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||||
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||||
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | |||||
| * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||||
| * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |||||
| * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | |||||
| * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||||
| * SUCH DAMAGE. | |||||
| * ==================================================================== | |||||
| * | |||||
| * This software consists of voluntary contributions made by many | |||||
| * individuals on behalf of the Apache Software Foundation. For more | |||||
| * information on the Apache Software Foundation, please see | |||||
| * <http://www.apache.org/>. | |||||
| */ | |||||
| package org.apache.tools.ant.taskdefs; | |||||
| import org.apache.tools.ant.*; | |||||
| import org.apache.tools.ant.types.FileSet; | |||||
| import java.io.*; | |||||
| import java.util.Vector; | |||||
| import java.util.zip.*; | |||||
| /** | |||||
| * Creates a WAR archive. | |||||
| * | |||||
| * @author <a href="mailto:stefan.bodewig@megabit.net">Stefan Bodewig</a> | |||||
| */ | |||||
| public class War extends Jar { | |||||
| private File deploymentDescriptor; | |||||
| private Vector libFileSets = new Vector(); | |||||
| private Vector classesFileSets = new Vector(); | |||||
| private Vector webInfFileSets = new Vector(); | |||||
| public War() { | |||||
| super(); | |||||
| archiveType = "war"; | |||||
| emptyBehavior = "create"; | |||||
| } | |||||
| public void setWarfile(File warFile) { | |||||
| super.setZipfile(warFile); | |||||
| } | |||||
| public void setWebxml(File descr) { | |||||
| deploymentDescriptor = descr; | |||||
| } | |||||
| public void addLib(FileSet fs) { | |||||
| libFileSets.addElement(fs); | |||||
| } | |||||
| public void addClasses(FileSet fs) { | |||||
| classesFileSets.addElement(fs); | |||||
| } | |||||
| public void addWebinf(FileSet fs) { | |||||
| webInfFileSets.addElement(fs); | |||||
| } | |||||
| /** | |||||
| * Add the deployment descriptor as well as all files added the | |||||
| * special way of nested lib, classes or webinf filesets. | |||||
| */ | |||||
| protected void initZipOutputStream(ZipOutputStream zOut) | |||||
| throws IOException, BuildException | |||||
| { | |||||
| // add deployment descriptor first | |||||
| if (deploymentDescriptor != null) { | |||||
| zipDir(new File(deploymentDescriptor.getParent()), zOut, | |||||
| "WEB-INF/"); | |||||
| super.zipFile(deploymentDescriptor, zOut, "WEB-INF/web.xml"); | |||||
| } else { | |||||
| throw new BuildException("webxml attribute is required", location); | |||||
| } | |||||
| addFiles(libFileSets, zOut, "lib/"); | |||||
| addFiles(classesFileSets, zOut, "classes/"); | |||||
| addFiles(webInfFileSets, zOut, "WEB-INF/"); | |||||
| super.initZipOutputStream(zOut); | |||||
| } | |||||
| protected boolean isUpToDate(FileScanner[] scanners, File zipFile) throws BuildException | |||||
| { | |||||
| if (deploymentDescriptor == null) { | |||||
| throw new BuildException("webxml attribute is required", location); | |||||
| } | |||||
| // just add some Scanners for our filesets and web.xml and let | |||||
| // Jar/Zip do the rest of the work | |||||
| FileScanner[] myScanners = new FileScanner[scanners.length | |||||
| + 1 // web.xml | |||||
| + libFileSets.size() | |||||
| + classesFileSets.size() | |||||
| + webInfFileSets.size()]; | |||||
| System.arraycopy(scanners, 0, myScanners, 0, scanners.length); | |||||
| DirectoryScanner ds = new DirectoryScanner(); | |||||
| ds.setBasedir(new File(deploymentDescriptor.getParent())); | |||||
| ds.setIncludes(new String[] {deploymentDescriptor.getName()}); | |||||
| ds.scan(); | |||||
| myScanners[scanners.length] = ds; | |||||
| addScanners(myScanners, scanners.length+1, libFileSets); | |||||
| addScanners(myScanners, scanners.length+1+libFileSets.size(), | |||||
| classesFileSets); | |||||
| addScanners(myScanners, scanners.length+1+libFileSets.size()+classesFileSets.size(), | |||||
| webInfFileSets); | |||||
| return super.isUpToDate(myScanners, zipFile); | |||||
| } | |||||
| protected void zipFile(File file, ZipOutputStream zOut, String vPath) | |||||
| throws IOException | |||||
| { | |||||
| // We already added a WEB-INF/web.xml | |||||
| if (!vPath.equalsIgnoreCase("WEB-INF/web.xml")) { | |||||
| super.zipFile(file, zOut, vPath); | |||||
| } else { | |||||
| log("Warning: selected "+archiveType+" files include a WEB-INF/web.xml which will be ignored " + | |||||
| "(please use webxml attribute to "+archiveType+" task)", Project.MSG_WARN); | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Add a DirectoryScanner for each FileSet included in fileSets to scanners | |||||
| * starting with index startIndex. | |||||
| */ | |||||
| protected void addScanners(FileScanner[] scanners, int startIndex, | |||||
| Vector fileSets) { | |||||
| for (int i=0; i<fileSets.size(); i++) { | |||||
| FileSet fs = (FileSet) fileSets.elementAt(i); | |||||
| scanners[startIndex+i] = fs.getDirectoryScanner(project); | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Iterate over the given Vector of filesets and add all files to the | |||||
| * ZipOutputStream using the given prefix. | |||||
| */ | |||||
| protected void addFiles(Vector v, ZipOutputStream zOut, String prefix) | |||||
| throws IOException { | |||||
| for (int i=0; i<v.size(); i++) { | |||||
| FileSet fs = (FileSet) v.elementAt(i); | |||||
| DirectoryScanner ds = fs.getDirectoryScanner(project); | |||||
| addFiles(ds, zOut, prefix); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -60,6 +60,7 @@ import org.apache.tools.ant.types.*; | |||||
| import java.io.*; | import java.io.*; | ||||
| import java.util.Enumeration; | import java.util.Enumeration; | ||||
| import java.util.Hashtable; | import java.util.Hashtable; | ||||
| import java.util.Stack; | |||||
| import java.util.StringTokenizer; | import java.util.StringTokenizer; | ||||
| import java.util.Vector; | import java.util.Vector; | ||||
| import java.util.zip.*; | import java.util.zip.*; | ||||
| @@ -79,15 +80,16 @@ public class Zip extends MatchingTask { | |||||
| protected String archiveType = "zip"; | protected String archiveType = "zip"; | ||||
| // For directories: | // For directories: | ||||
| private static long emptyCrc = new CRC32 ().getValue (); | private static long emptyCrc = new CRC32 ().getValue (); | ||||
| protected String emptyBehavior = null; | |||||
| protected String emptyBehavior = "skip"; | |||||
| private Vector filesets = new Vector (); | private Vector filesets = new Vector (); | ||||
| private Hashtable addedDirs = new Hashtable(); | |||||
| /** | /** | ||||
| * This is the name/location of where to | * This is the name/location of where to | ||||
| * create the .zip file. | * create the .zip file. | ||||
| */ | */ | ||||
| public void setZipfile(String zipFilename) { | |||||
| zipFile = project.resolveFile(zipFilename); | |||||
| public void setZipfile(File zipFile) { | |||||
| this.zipFile = zipFile; | |||||
| } | } | ||||
| /** | /** | ||||
| @@ -158,28 +160,9 @@ public class Zip extends MatchingTask { | |||||
| zOut.setMethod(ZipOutputStream.STORED); | zOut.setMethod(ZipOutputStream.STORED); | ||||
| } | } | ||||
| initZipOutputStream(zOut); | initZipOutputStream(zOut); | ||||
| // XXX ideally would also enter includedDirectories to the archive | |||||
| Hashtable parentDirs = new Hashtable(); | |||||
| for (int j = 0; j < scanners.length; j++) { | for (int j = 0; j < scanners.length; j++) { | ||||
| String[] files = scanners[j].getIncludedFiles(); | |||||
| File thisBaseDir = scanners[j].getBasedir(); | |||||
| for (int i = 0; i < files.length; i++) { | |||||
| File f = new File(thisBaseDir,files[i]); | |||||
| String name = files[i].replace(File.separatorChar,'/'); | |||||
| // Look for & create parent dirs as needed. | |||||
| int slashPos = -1; | |||||
| while ((slashPos = name.indexOf((int)'/', slashPos + 1)) != -1) { | |||||
| String dir = name.substring(0, slashPos); | |||||
| if (!parentDirs.contains(dir)) { | |||||
| parentDirs.put(dir, dir); | |||||
| zipDir(new File(thisBaseDir, dir.replace('/', File.separatorChar)), | |||||
| zOut, dir + '/'); | |||||
| } | |||||
| } | |||||
| zipFile(f, zOut, name); | |||||
| } | |||||
| addFiles(scanners[j], zOut, ""); | |||||
| } | } | ||||
| } finally { | } finally { | ||||
| zOut.close (); | zOut.close (); | ||||
| @@ -196,6 +179,36 @@ public class Zip extends MatchingTask { | |||||
| } | } | ||||
| } | } | ||||
| /** | |||||
| * Add all files of the given FileScanner to the ZipOutputStream | |||||
| * prependig the given prefix to each filename. | |||||
| * | |||||
| * <p>Ensure parent directories have been added as well. | |||||
| */ | |||||
| protected void addFiles(FileScanner scanner, ZipOutputStream zOut, | |||||
| String prefix) throws IOException { | |||||
| File thisBaseDir = scanner.getBasedir(); | |||||
| // directories that matched include patterns | |||||
| String[] dirs = scanner.getIncludedDirectories(); | |||||
| for (int i = 0; i < dirs.length; i++) { | |||||
| String name = dirs[i].replace(File.separatorChar,'/'); | |||||
| if (!name.endsWith("/")) { | |||||
| name += "/"; | |||||
| } | |||||
| addParentDirs(thisBaseDir, name, zOut, prefix); | |||||
| } | |||||
| // files that matched include patterns | |||||
| String[] files = scanner.getIncludedFiles(); | |||||
| for (int i = 0; i < files.length; i++) { | |||||
| File f = new File(thisBaseDir, files[i]); | |||||
| String name = files[i].replace(File.separatorChar,'/'); | |||||
| addParentDirs(thisBaseDir, name, zOut, prefix); | |||||
| zipFile(f, zOut, prefix+name); | |||||
| } | |||||
| } | |||||
| protected void initZipOutputStream(ZipOutputStream zOut) | protected void initZipOutputStream(ZipOutputStream zOut) | ||||
| throws IOException, BuildException | throws IOException, BuildException | ||||
| { | { | ||||
| @@ -211,15 +224,14 @@ public class Zip extends MatchingTask { | |||||
| */ | */ | ||||
| protected boolean isUpToDate(FileScanner[] scanners, File zipFile) throws BuildException | protected boolean isUpToDate(FileScanner[] scanners, File zipFile) throws BuildException | ||||
| { | { | ||||
| if (emptyBehavior == null) emptyBehavior = "skip"; | |||||
| File[] files = grabFiles(scanners); | File[] files = grabFiles(scanners); | ||||
| if (files.length == 0) { | if (files.length == 0) { | ||||
| if (emptyBehavior.equals("skip")) { | if (emptyBehavior.equals("skip")) { | ||||
| log("Warning: skipping ZIP archive " + zipFile + | |||||
| log("Warning: skipping "+archiveType+" archive " + zipFile + | |||||
| " because no files were included.", Project.MSG_WARN); | " because no files were included.", Project.MSG_WARN); | ||||
| return true; | return true; | ||||
| } else if (emptyBehavior.equals("fail")) { | } else if (emptyBehavior.equals("fail")) { | ||||
| throw new BuildException("Cannot create ZIP archive " + zipFile + | |||||
| throw new BuildException("Cannot create "+archiveType+" archive " + zipFile + | |||||
| ": no files were included.", location); | ": no files were included.", location); | ||||
| } else { | } else { | ||||
| // Create. | // Create. | ||||
| @@ -227,7 +239,7 @@ public class Zip extends MatchingTask { | |||||
| // In this case using java.util.zip will not work | // In this case using java.util.zip will not work | ||||
| // because it does not permit a zero-entry archive. | // because it does not permit a zero-entry archive. | ||||
| // Must create it manually. | // Must create it manually. | ||||
| log("Note: creating empty ZIP archive " + zipFile, Project.MSG_INFO); | |||||
| log("Note: creating empty "+archiveType+" archive " + zipFile, Project.MSG_INFO); | |||||
| try { | try { | ||||
| OutputStream os = new FileOutputStream(zipFile); | OutputStream os = new FileOutputStream(zipFile); | ||||
| try { | try { | ||||
| @@ -275,6 +287,13 @@ public class Zip extends MatchingTask { | |||||
| protected void zipDir(File dir, ZipOutputStream zOut, String vPath) | protected void zipDir(File dir, ZipOutputStream zOut, String vPath) | ||||
| throws IOException | throws IOException | ||||
| { | { | ||||
| if (addedDirs.get(vPath) != null) { | |||||
| // don't add directories we've already added. | |||||
| // no warning if we try, it is harmless in and of itself | |||||
| return; | |||||
| } | |||||
| addedDirs.put(vPath, vPath); | |||||
| ZipEntry ze = new ZipEntry (vPath); | ZipEntry ze = new ZipEntry (vPath); | ||||
| if (dir != null) ze.setTime (dir.lastModified ()); | if (dir != null) ze.setTime (dir.lastModified ()); | ||||
| ze.setSize (0); | ze.setSize (0); | ||||
| @@ -353,4 +372,29 @@ public class Zip extends MatchingTask { | |||||
| fIn.close(); | fIn.close(); | ||||
| } | } | ||||
| } | } | ||||
| /** | |||||
| * Ensure all parent dirs of a given entry have been added. | |||||
| */ | |||||
| protected void addParentDirs(File baseDir, String entry, | |||||
| ZipOutputStream zOut, String prefix) | |||||
| throws IOException { | |||||
| Stack directories = new Stack(); | |||||
| int slashPos = entry.length(); | |||||
| while ((slashPos = entry.lastIndexOf((int)'/', slashPos-1)) != -1) { | |||||
| String dir = entry.substring(0, slashPos+1); | |||||
| if (addedDirs.get(prefix+dir) != null) { | |||||
| break; | |||||
| } | |||||
| directories.push(dir); | |||||
| } | |||||
| while (!directories.isEmpty()) { | |||||
| String dir = (String) directories.pop(); | |||||
| File f = new File(baseDir, dir); | |||||
| zipDir(f, zOut, prefix+dir); | |||||
| } | |||||
| } | |||||
| } | } | ||||
| @@ -12,6 +12,7 @@ cvs=org.apache.tools.ant.taskdefs.Cvs | |||||
| get=org.apache.tools.ant.taskdefs.Get | get=org.apache.tools.ant.taskdefs.Get | ||||
| unzip=org.apache.tools.ant.taskdefs.Expand | unzip=org.apache.tools.ant.taskdefs.Expand | ||||
| unjar=org.apache.tools.ant.taskdefs.Expand | unjar=org.apache.tools.ant.taskdefs.Expand | ||||
| unwar=org.apache.tools.ant.taskdefs.Expand | |||||
| echo=org.apache.tools.ant.taskdefs.Echo | echo=org.apache.tools.ant.taskdefs.Echo | ||||
| javadoc=org.apache.tools.ant.taskdefs.Javadoc | javadoc=org.apache.tools.ant.taskdefs.Javadoc | ||||
| zip=org.apache.tools.ant.taskdefs.Zip | zip=org.apache.tools.ant.taskdefs.Zip | ||||
| @@ -42,6 +43,7 @@ antcall=org.apache.tools.ant.taskdefs.CallTarget | |||||
| sql=org.apache.tools.ant.taskdefs.SQLExec | sql=org.apache.tools.ant.taskdefs.SQLExec | ||||
| mail=org.apache.tools.ant.taskdefs.SendEmail | mail=org.apache.tools.ant.taskdefs.SendEmail | ||||
| fail=org.apache.tools.ant.taskdefs.Exit | fail=org.apache.tools.ant.taskdefs.Exit | ||||
| war=org.apache.tools.ant.taskdefs.War | |||||
| # optional tasks | # optional tasks | ||||
| script=org.apache.tools.ant.taskdefs.optional.Script | script=org.apache.tools.ant.taskdefs.optional.Script | ||||
| @@ -107,18 +107,12 @@ public class FileSet extends DataType { | |||||
| throw tooManyAttributes(); | throw tooManyAttributes(); | ||||
| } | } | ||||
| /* | |||||
| * XXX cannot check as long as tasks get configured at parse time. | |||||
| * | |||||
| * the build process might create the directory. | |||||
| */ | |||||
| // if (!dir.exists()) { | |||||
| // throw new BuildException(dir.getAbsolutePath()+" not found."); | |||||
| // } | |||||
| // if (!dir.isDirectory()) { | |||||
| // throw new BuildException(dir.getAbsolutePath()+" is not a directory."); | |||||
| // } | |||||
| if (!dir.exists()) { | |||||
| throw new BuildException(dir.getAbsolutePath()+" not found."); | |||||
| } | |||||
| if (!dir.isDirectory()) { | |||||
| throw new BuildException(dir.getAbsolutePath()+" is not a directory."); | |||||
| } | |||||
| this.dir = dir; | this.dir = dir; | ||||
| } | } | ||||
| @@ -239,17 +233,6 @@ public class FileSet extends DataType { | |||||
| throw new BuildException("No directory specified for fileset."); | throw new BuildException("No directory specified for fileset."); | ||||
| } | } | ||||
| /* | |||||
| * XXX remove the check here and enable the one in setDir as soon | |||||
| * as we configure tasks at execution time. | |||||
| */ | |||||
| if (!dir.exists()) { | |||||
| throw new BuildException(dir.getAbsolutePath()+" not found."); | |||||
| } | |||||
| if (!dir.isDirectory()) { | |||||
| throw new BuildException(dir.getAbsolutePath()+" is not a directory."); | |||||
| } | |||||
| DirectoryScanner ds = new DirectoryScanner(); | DirectoryScanner ds = new DirectoryScanner(); | ||||
| setupDirectoryScanner(ds, p); | setupDirectoryScanner(ds, p); | ||||
| ds.scan(); | ds.scan(); | ||||