diff --git a/WHATSNEW b/WHATSNEW index 6d42b1ffa..a8676b3a4 100644 --- a/WHATSNEW +++ b/WHATSNEW @@ -25,6 +25,12 @@ Changes that could break older environments: * XML namespaces are now enabled in the XML parser, meaning XML namespace declarations no longer cause errors. +* The task and friends have been heavily modified, almost every + method signature of the Zip class has changed. If you have subclassed + Zip (or one of its subclasses), your class will most likely not + compile against the current code base. If it still compiles, it will + probably not work as in Ant 1.5.1. + Fixed bugs: ----------- * was not ignoring comment lines. diff --git a/docs/manual/CoreTasks/jar.html b/docs/manual/CoreTasks/jar.html index 9f6eaf648..ff8f2575f 100644 --- a/docs/manual/CoreTasks/jar.html +++ b/docs/manual/CoreTasks/jar.html @@ -32,11 +32,16 @@ The extended fileset and groupfileset attributes from the zip task are also available in the jar task. See the Zip task for more details and examples.

If the manifest is omitted, a simple one will be supplied by Ant.

-

The update parameter controls what happens if the -JAR file already exists. When set to yes, the JAR file is + +

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.

+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.

+

(The Jar task is a shortcut for specifying the manifest file of a JAR file. The same thing can be accomplished by using the fullpath attribute of a zipfileset in a Zip task. The one difference is that if the diff --git a/src/etc/testcases/taskdefs/zip.xml b/src/etc/testcases/taskdefs/zip.xml index a05f8a782..0163bc564 100644 --- a/src/etc/testcases/taskdefs/zip.xml +++ b/src/etc/testcases/taskdefs/zip.xml @@ -19,11 +19,12 @@ + when target file does not exist before the zip task is run + --> diff --git a/src/main/org/apache/tools/ant/DirectoryScanner.java b/src/main/org/apache/tools/ant/DirectoryScanner.java index 88ec8e7b9..2d6ff86bd 100644 --- a/src/main/org/apache/tools/ant/DirectoryScanner.java +++ b/src/main/org/apache/tools/ant/DirectoryScanner.java @@ -203,11 +203,6 @@ public class DirectoryScanner implements ResourceScanner, SelectorScanner { */ protected Vector filesIncluded; - /** - * the same as filesIncluded, but in terms of Resource - */ - private Vector filesIncludedR; - /** The files which did not match any includes or selectors. */ protected Vector filesNotIncluded; @@ -221,10 +216,6 @@ public class DirectoryScanner implements ResourceScanner, SelectorScanner { * and were selected. */ protected Vector dirsIncluded; - /** The directories which matched at least one include and no excludes - * and were selected, as resources - */ - private Vector dirsIncludedR; /** The directories which were found and did not match any includes. */ protected Vector dirsNotIncluded; @@ -556,12 +547,10 @@ public class DirectoryScanner implements ResourceScanner, SelectorScanner { } filesIncluded = new Vector(); - filesIncludedR = new Vector(); filesNotIncluded = new Vector(); filesExcluded = new Vector(); filesDeselected = new Vector(); dirsIncluded = new Vector(); - dirsIncludedR = new Vector(); dirsNotIncluded = new Vector(); dirsExcluded = new Vector(); dirsDeselected = new Vector(); @@ -570,10 +559,6 @@ public class DirectoryScanner implements ResourceScanner, SelectorScanner { if (!isExcluded("")) { if (isSelected("",basedir)) { dirsIncluded.addElement(""); - dirsIncludedR.addElement(new Resource("", true, - basedir - .lastModified(), - true)); } else { dirsDeselected.addElement(""); } @@ -692,11 +677,6 @@ public class DirectoryScanner implements ResourceScanner, SelectorScanner { if (!isExcluded(name)) { if (isSelected(name,file)) { dirsIncluded.addElement(name); - dirsIncludedR.addElement(new Resource(name, - true, - file - .lastModified(), - true)); if (fast) { scandir(file, name + File.separator, fast); } @@ -730,11 +710,6 @@ public class DirectoryScanner implements ResourceScanner, SelectorScanner { if (!isExcluded(name)) { if (isSelected(name,file)) { filesIncluded.addElement(name); - filesIncludedR.addElement(new Resource(name, - true, - file - .lastModified(), - false)); } else { everythingIncluded = false; filesDeselected.addElement(name); @@ -830,13 +805,11 @@ public class DirectoryScanner implements ResourceScanner, SelectorScanner { * include patterns and none of the exclude patterns. */ public String[] getIncludedFiles() { - int count = filesIncluded.size(); - String[] files = new String[count]; - for (int i = 0; i < count; i++) { - files[i] = (String)filesIncluded.elementAt(i); - } + String[] files = new String[filesIncluded.size()]; + filesIncluded.copyInto(files); return files; } + /** * Returns the resources of the files which matched at least one * of the include patterns and none of the exclude patterns. The @@ -849,11 +822,11 @@ public class DirectoryScanner implements ResourceScanner, SelectorScanner { * @since Ant 1.5.2 */ public Resource[] getIncludedFileResources() { - int count = filesIncludedR.size(); + String[] names = getIncludedFiles(); + int count = names.length; Resource[] resources = new Resource[count]; for (int i = 0; i < count; i++) { - resources[i] = - (Resource) ((Resource) filesIncludedR.elementAt(i)).clone(); + resources[i] = getResource(names[i]); } return resources; } @@ -870,11 +843,8 @@ public class DirectoryScanner implements ResourceScanner, SelectorScanner { */ public String[] getNotIncludedFiles() { slowScan(); - int count = filesNotIncluded.size(); - String[] files = new String[count]; - for (int i = 0; i < count; i++) { - files[i] = (String)filesNotIncluded.elementAt(i); - } + String[] files = new String[filesNotIncluded.size()]; + filesNotIncluded.copyInto(files); return files; } @@ -891,11 +861,8 @@ public class DirectoryScanner implements ResourceScanner, SelectorScanner { */ public String[] getExcludedFiles() { slowScan(); - int count = filesExcluded.size(); - String[] files = new String[count]; - for (int i = 0; i < count; i++) { - files[i] = (String)filesExcluded.elementAt(i); - } + String[] files = new String[filesExcluded.size()]; + filesExcluded.copyInto(files); return files; } @@ -912,11 +879,8 @@ public class DirectoryScanner implements ResourceScanner, SelectorScanner { */ public String[] getDeselectedFiles() { slowScan(); - int count = filesDeselected.size(); - String[] files = new String[count]; - for (int i = 0; i < count; i++) { - files[i] = (String)filesDeselected.elementAt(i); - } + String[] files = new String[filesDeselected.size()]; + filesDeselected.copyInto(files); return files; } @@ -929,11 +893,8 @@ public class DirectoryScanner implements ResourceScanner, SelectorScanner { * include patterns and none of the exclude patterns. */ public String[] getIncludedDirectories() { - int count = dirsIncluded.size(); - String[] directories = new String[count]; - for (int i = 0; i < count; i++) { - directories[i] = (String)dirsIncluded.elementAt(i); - } + String[] directories = new String[dirsIncluded.size()]; + dirsIncluded.copyInto(directories); return directories; } @@ -947,15 +908,16 @@ public class DirectoryScanner implements ResourceScanner, SelectorScanner { * * @since Ant 1.5.2 */ - public Resource[] getIncludedDirectoryResources() { - int count = dirsIncludedR.size(); - Resource[] directories = new Resource[count]; - for (int i = 0; i < count; i++) { - directories[i] = - (Resource) ((Resource) dirsIncludedR.elementAt(i)).clone(); - } - return directories; - } + public Resource[] getIncludedDirectoryResources() { + String[] names = getIncludedDirectories(); + int count = names.length; + Resource[] resources = new Resource[count]; + for (int i = 0; i < count; i++) { + resources[i] = getResource(names[i]); + } + return resources; + } + /** * Returns the names of the directories which matched none of the include * patterns. The names are relative to the base directory. This involves @@ -968,11 +930,8 @@ public class DirectoryScanner implements ResourceScanner, SelectorScanner { */ public String[] getNotIncludedDirectories() { slowScan(); - int count = dirsNotIncluded.size(); - String[] directories = new String[count]; - for (int i = 0; i < count; i++) { - directories[i] = (String)dirsNotIncluded.elementAt(i); - } + String[] directories = new String[dirsNotIncluded.size()]; + dirsNotIncluded.copyInto(directories); return directories; } @@ -989,11 +948,8 @@ public class DirectoryScanner implements ResourceScanner, SelectorScanner { */ public String[] getExcludedDirectories() { slowScan(); - int count = dirsExcluded.size(); - String[] directories = new String[count]; - for (int i = 0; i < count; i++) { - directories[i] = (String)dirsExcluded.elementAt(i); - } + String[] directories = new String[dirsExcluded.size()]; + dirsExcluded.copyInto(directories); return directories; } @@ -1010,11 +966,8 @@ public class DirectoryScanner implements ResourceScanner, SelectorScanner { */ public String[] getDeselectedDirectories() { slowScan(); - int count = dirsDeselected.size(); - String[] directories = new String[count]; - for (int i = 0; i < count; i++) { - directories[i] = (String)dirsDeselected.elementAt(i); - } + String[] directories = new String[dirsDeselected.size()]; + dirsDeselected.copyInto(directories); return directories; } diff --git a/src/main/org/apache/tools/ant/taskdefs/Ear.java b/src/main/org/apache/tools/ant/taskdefs/Ear.java index 2c1ee87be..4b818b658 100644 --- a/src/main/org/apache/tools/ant/taskdefs/Ear.java +++ b/src/main/org/apache/tools/ant/taskdefs/Ear.java @@ -1,7 +1,7 @@ /* * The Apache Software License, Version 1.1 * - * Copyright (c) 2001-2002 The Apache Software Foundation. All rights + * Copyright (c) 2001-2003 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without @@ -104,8 +104,7 @@ public class Ear extends Jar { // Create a ZipFileSet for this file, and pass it up. ZipFileSet fs = new ZipFileSet(); - fs.setDir(new File(deploymentDescriptor.getParent())); - fs.setIncludes(deploymentDescriptor.getName()); + fs.setFile(deploymentDescriptor); fs.setFullpath("META-INF/application.xml"); super.addFileset(fs); } @@ -134,7 +133,10 @@ public class Ear extends Jar { super.initZipOutputStream(zOut); } - protected void zipFile(File file, ZipOutputStream zOut, String vPath, + /** + * Overriden from Zip class to deal with application.xml + */ + protected void zipFile(File file, ZipOutputStream zOut, String vPath, int mode) throws IOException { // If the file being added is META-INF/application.xml, we diff --git a/src/main/org/apache/tools/ant/taskdefs/Jar.java b/src/main/org/apache/tools/ant/taskdefs/Jar.java index 7e9ed483d..314c0ad32 100644 --- a/src/main/org/apache/tools/ant/taskdefs/Jar.java +++ b/src/main/org/apache/tools/ant/taskdefs/Jar.java @@ -72,6 +72,7 @@ import org.apache.tools.ant.Project; import org.apache.tools.ant.ResourceScanner; import org.apache.tools.ant.types.EnumeratedAttribute; import org.apache.tools.ant.types.FileSet; +import org.apache.tools.ant.types.Resource; import org.apache.tools.ant.types.ZipFileSet; import org.apache.tools.zip.ZipOutputStream; @@ -190,7 +191,6 @@ public class Jar extends Zip { } } } - } } @@ -374,7 +374,7 @@ public class Jar extends Zip { ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); super.zipFile(bais, zOut, "META-INF/MANIFEST.MF", - System.currentTimeMillis(), null, + System.currentTimeMillis(), null, ZipFileSet.DEFAULT_FILE_MODE); super.initZipOutputStream(zOut); } @@ -442,33 +442,18 @@ public class Jar extends Zip { ZipFileSet.DEFAULT_FILE_MODE); } - /** - * Overriden from Zip class to deal with manifests - */ - protected void zipFile(File file, ZipOutputStream zOut, String vPath, - int mode) - throws IOException { - if ("META-INF/MANIFEST.MF".equalsIgnoreCase(vPath)) { - if (! doubleFilePass || (doubleFilePass && skipWriting)) { - filesetManifest(file, null); - } - } else { - super.zipFile(file, zOut, vPath, mode); - } - } - /** * Overriden from Zip class to deal with manifests */ protected void zipFile(InputStream is, ZipOutputStream zOut, String vPath, - long lastModified, File file, int mode) + long lastModified, File fromArchive, int mode) throws IOException { if ("META-INF/MANIFEST.MF".equalsIgnoreCase(vPath)) { if (! doubleFilePass || (doubleFilePass && skipWriting)) { - filesetManifest(file, is); + filesetManifest(fromArchive, is); } } else { - super.zipFile(is, zOut, vPath, lastModified, null, mode); + super.zipFile(is, zOut, vPath, lastModified, fromArchive, mode); } } @@ -525,16 +510,31 @@ public class Jar extends Zip { } /** - * Check whether the archive is up-to-date; - * @param scanners list of prepared scanners containing files to archive + * Collect the resources that are newer than the corresponding + * entries (or missing) in the original archive. + * + *

If we are going to recreate the archive instead of updating + * it, all resources should be considered as new, if a single one + * is. Because of this, subclasses overriding this method must + * call super.getResourcesToAdd and indicate with the + * third arg if they already know that the archive is + * out-of-date.

+ * + * @param filesets The filesets to grab resources from * @param zipFile intended archive file (may or may not exist) - * @return true if nothing need be done (may have done something - * already); false if archive creation should proceed + * @param needsUpdate whether we already know that the archive is + * out-of-date. Subclasses overriding this method are supposed to + * set this value correctly in their call to + * super.getResourcesToAdd. + * @return an array of resources to add for each fileset passed in. + * * @exception BuildException if it likes */ - protected boolean isUpToDate(ResourceScanner[] scanners, - FileSet[] fss, File zipFile) + protected Resource[][] getResourcesToAdd(FileSet[] filesets, + File zipFile, + boolean needsUpdate) throws BuildException { + // need to handle manifest as a special check if (configuredManifest != null || manifestFile == null) { java.util.zip.ZipFile theZipFile = null; @@ -545,23 +545,24 @@ public class Jar extends Zip { if (entry == null) { log("Updating jar since the current jar has no manifest", Project.MSG_VERBOSE); - return false; - } - Manifest currentManifest = - new Manifest(new InputStreamReader(theZipFile - .getInputStream(entry))); - Manifest newManifest = createManifest(); - if (!currentManifest.equals(newManifest)) { - log("Updating jar since jar manifest has changed", - Project.MSG_VERBOSE); - return false; + needsUpdate = true; + } else { + Manifest currentManifest = + new Manifest(new InputStreamReader(theZipFile + .getInputStream(entry))); + Manifest newManifest = createManifest(); + if (!currentManifest.equals(newManifest)) { + log("Updating jar since jar manifest has changed", + Project.MSG_VERBOSE); + needsUpdate = true; + } } } catch (Exception e) { // any problems and we will rebuild log("Updating jar since cannot read current jar manifest: " + e.getClass().getName() + " - " + e.getMessage(), Project.MSG_VERBOSE); - return false; + needsUpdate = true; } finally { if (theZipFile != null) { try { @@ -572,9 +573,33 @@ public class Jar extends Zip { } } } else if (manifestFile.lastModified() > zipFile.lastModified()) { - return false; + log("Updating jar since manifestFile is newer than the archive", + Project.MSG_VERBOSE); + needsUpdate = true; + } + + Resource[][] fromZip = + super.getResourcesToAdd(filesets, zipFile, needsUpdate); + if (needsUpdate && isEmpty(fromZip)) { + // archive doesn't have any content apart from the manifest + + /* + * OK, this is a hack. + * + * Zip doesn't care if the array we return is longer than + * the array of filesets, so we can savely append an + * additional non-empty array. This will make Zip think + * that there are resources out-of-date and at the same + * time add nothing. + * + * The whole manifest handling happens in initZipOutputStream. + */ + Resource[][] tmp = new Resource[fromZip.length + 1][]; + System.arraycopy(fromZip, 0, tmp, 0, fromZip.length); + tmp[fromZip.length] = new Resource[] {new Resource("")}; + fromZip = tmp; } - return super.isUpToDate(scanners, fss, zipFile); + return fromZip; } protected boolean createEmptyZip(File zipFile) { diff --git a/src/main/org/apache/tools/ant/taskdefs/MatchingTask.java b/src/main/org/apache/tools/ant/taskdefs/MatchingTask.java index 2c843df7f..ae72cb714 100644 --- a/src/main/org/apache/tools/ant/taskdefs/MatchingTask.java +++ b/src/main/org/apache/tools/ant/taskdefs/MatchingTask.java @@ -1,7 +1,7 @@ /* * The Apache Software License, Version 1.1 * - * Copyright (c) 2000-2002 The Apache Software Foundation. All rights + * Copyright (c) 2000-2003 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without @@ -409,4 +409,13 @@ public abstract class MatchingTask extends Task implements SelectorContainer { public void addDepend(DependSelector selector) { fileset.addDepend(selector); } + + /** + * Accessor for the implict fileset. + * + * @since Ant 1.5.2 + */ + protected final FileSet getImplicitFileSet() { + return fileset; + } } diff --git a/src/main/org/apache/tools/ant/taskdefs/War.java b/src/main/org/apache/tools/ant/taskdefs/War.java index bd778e38f..27912cf56 100644 --- a/src/main/org/apache/tools/ant/taskdefs/War.java +++ b/src/main/org/apache/tools/ant/taskdefs/War.java @@ -1,7 +1,7 @@ /* * The Apache Software License, Version 1.1 * - * Copyright (c) 2000-2002 The Apache Software Foundation. All rights + * Copyright (c) 2000-2003 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without @@ -121,8 +121,7 @@ public class War extends Jar { // Create a ZipFileSet for this file, and pass it up. ZipFileSet fs = new ZipFileSet(); - fs.setDir(new File(deploymentDescriptor.getParent())); - fs.setIncludes(deploymentDescriptor.getName()); + fs.setFile(deploymentDescriptor); fs.setFullpath("WEB-INF/web.xml"); super.addFileset(fs); } @@ -170,9 +169,9 @@ public class War extends Jar { } /** - * add another file to the stream + * Overriden from Zip class to deal with web.xml */ - protected void zipFile(File file, ZipOutputStream zOut, String vPath, + protected void zipFile(File file, ZipOutputStream zOut, String vPath, int mode) throws IOException { // If the file being added is WEB-INF/web.xml, we warn if it's diff --git a/src/main/org/apache/tools/ant/taskdefs/Zip.java b/src/main/org/apache/tools/ant/taskdefs/Zip.java index 9caede3fc..a569158a7 100644 --- a/src/main/org/apache/tools/ant/taskdefs/Zip.java +++ b/src/main/org/apache/tools/ant/taskdefs/Zip.java @@ -66,6 +66,7 @@ import java.util.Hashtable; import java.util.Stack; import java.util.Vector; import java.util.zip.CRC32; +import java.util.zip.ZipFile; import java.util.zip.ZipInputStream; import org.apache.tools.ant.BuildException; @@ -127,6 +128,7 @@ public class Zip extends MatchingTask { protected boolean doubleFilePass = false; protected boolean skipWriting = false; + private static FileUtils fileUtils = FileUtils.newFileUtils(); /** * true when we are adding new files into the Zip file, as opposed @@ -338,35 +340,31 @@ public class Zip extends MatchingTask { } } - // Create the scanners to pass to isUpToDate(). - Vector dss = new Vector(); + // collect filesets to pass them to getResourcesToAdd Vector vfss = new Vector(); if (baseDir != null) { - dss.addElement(getDirectoryScanner(baseDir)); - FileSet fs = new FileSet(); + FileSet fs = (FileSet) getImplicitFileSet().clone(); fs.setDir(baseDir); vfss.addElement(fs); } for (int i = 0; i < filesets.size(); i++) { FileSet fs = (FileSet) filesets.elementAt(i); - dss.addElement (fs.getDirectoryScanner(getProject())); vfss.addElement(fs); } - int dssSize = dss.size(); - ResourceScanner[] scanners = new ResourceScanner[dssSize]; - dss.copyInto(scanners); - FileSet [] fss = new FileSet[dssSize]; + + FileSet[] fss = new FileSet[vfss.size()]; vfss.copyInto(fss); boolean success = false; try { + Resource[][] addThem = getResourcesToAdd(fss, zipFile, false); + // quick exit if the target is up to date // can also handle empty archives - if (isUpToDate(scanners, fss, zipFile)) { + if (isEmpty(addThem)) { return; } if (doUpdate) { - FileUtils fileUtils = FileUtils.newFileUtils(); renamedFile = fileUtils.createTempFile("zip", ".tmp", fileUtils.getParentFile(zipFile)); @@ -401,14 +399,13 @@ public class Zip extends MatchingTask { } initZipOutputStream(zOut); - // Add the implicit fileset to the archive. - if (baseDir != null) { - addFiles(getDirectoryScanner(baseDir), zOut, "", "", - ZipFileSet.DEFAULT_DIR_MODE, - ZipFileSet.DEFAULT_FILE_MODE); - } // Add the explicit filesets to the archive. - addFiles(filesets, zOut); + for (int i = 0; i < fss.length; i++) { + if (addThem[i].length != 0) { + addResources(fss[i], addThem[i], zOut); + } + } + if (doUpdate) { addingNewFiles = false; ZipFileSet oldFiles = new ZipFileSet(); @@ -418,9 +415,10 @@ public class Zip extends MatchingTask { PatternSet.NameEntry ne = oldFiles.createExclude(); ne.setName((String) addedFiles.elementAt(i)); } - Vector tmp = new Vector(1); - tmp.addElement(oldFiles); - addFiles(tmp, zOut); + addResources(oldFiles, + oldFiles.getDirectoryScanner(getProject()) + .getIncludedFileResources(), + zOut); } finalizeZipOutputStream(zOut); @@ -481,127 +479,101 @@ public class Zip extends MatchingTask { * Indicates if the task is adding new files into the archive as opposed to * copying back unchanged files from the backup copy */ - protected boolean isAddingNewFiles() { + protected final boolean isAddingNewFiles() { return addingNewFiles; } /** - * Add all files of the given FileScanner to the ZipOutputStream - * prependig the given prefix to each filename. - * - *

Ensure parent directories have been added as well. - * - * @deprecated use six-arg version instead. - */ - protected void addFiles(FileScanner scanner, ZipOutputStream zOut, - String prefix, String fullpath) - throws IOException { - addFiles(scanner, zOut, prefix, fullpath, ZipFileSet.DEFAULT_DIR_MODE, - ZipFileSet.DEFAULT_FILE_MODE); - } - - /** - * Add all files of the given FileScanner to the ZipOutputStream - * prependig the given prefix to each filename. + * Add the given resources. * - *

Ensure parent directories have been added as well. + * @param fileset may give additional information like fullpath or + * permissions. + * @param resources the resources to add + * @param zOut the stream to write to * * @since Ant 1.6 */ - protected void addFiles(FileScanner scanner, ZipOutputStream zOut, - String prefix, String fullpath, int dirMode, - int fileMode) + protected final void addResources(FileSet fileset, Resource[] resources, + ZipOutputStream zOut) throws IOException { + String prefix = ""; + String fullpath = ""; + int dirMode = ZipFileSet.DEFAULT_DIR_MODE; + int fileMode = ZipFileSet.DEFAULT_FILE_MODE; + + ZipFileSet zfs = null; + if (fileset instanceof ZipFileSet) { + zfs = (ZipFileSet) fileset; + prefix = zfs.getPrefix(); + fullpath = zfs.getFullpath(); + dirMode = zfs.getDirMode(); + fileMode = zfs.getDirMode(); + } + if (prefix.length() > 0 && fullpath.length() > 0) { throw new BuildException("Both prefix and fullpath attributes must" + " not be set on the same fileset."); } - File thisBaseDir = scanner.getBasedir(); - - // directories that matched include patterns - String[] dirs = scanner.getIncludedDirectories(); - if (dirs.length > 0 && fullpath.length() > 0) { + if (resources.length != 1 && fullpath.length() > 0) { throw new BuildException("fullpath attribute may only be specified" + " for filesets that specify a single" + " file."); } - for (int i = 0; i < dirs.length; i++) { - if ("".equals(dirs[i])) { - continue; - } - String name = dirs[i].replace(File.separatorChar, '/'); - if (!name.endsWith("/")) { - name += "/"; - } - addParentDirs(thisBaseDir, name, zOut, prefix, dirMode); - } - // files that matched include patterns - String[] files = scanner.getIncludedFiles(); - if (files.length > 1 && fullpath.length() > 0) { - throw new BuildException("fullpath attribute may only be specified" - + " for filesets that specify a single" - + "file."); - } - for (int i = 0; i < files.length; i++) { - File f = new File(thisBaseDir, files[i]); - if (fullpath.length() > 0) { - // Add this file at the specified location. - addParentDirs(null, fullpath, zOut, "", dirMode); - zipFile(f, zOut, fullpath, fileMode); - } else { - // Add this file with the specified prefix. - String name = files[i].replace(File.separatorChar, '/'); - addParentDirs(thisBaseDir, name, zOut, prefix, dirMode); - zipFile(f, zOut, prefix + name, fileMode); - } + if (prefix.length() > 0 + && !prefix.endsWith("/") + && !prefix.endsWith("\\")) { + prefix += "/"; } - } - - protected void addZipEntries(ZipFileSet fs, DirectoryScanner ds, - ZipOutputStream zOut, String prefix, - String fullpath) - throws IOException { - log("adding zip entries: " + fullpath, Project.MSG_VERBOSE); - if (prefix.length() > 0 && fullpath.length() > 0) { - throw new BuildException("Both prefix and fullpath attributes must" - + " not be set on the same fileset."); - } - - ZipScanner zipScanner = (ZipScanner) ds; - File zipSrc = fs.getSrc(); - - ZipEntry entry; - java.util.zip.ZipEntry origEntry; - ZipInputStream in = null; + ZipFile zf = null; try { - in = new ZipInputStream(new FileInputStream(zipSrc)); - - while ((origEntry = in.getNextEntry()) != null) { - entry = new ZipEntry(origEntry); - String vPath = entry.getName(); - if (zipScanner.match(vPath)) { - if (fullpath.length() > 0) { - addParentDirs(null, fullpath, zOut, "", - fs.getDirMode()); - zipFile(in, zOut, fullpath, entry.getTime(), zipSrc, - fs.getFileMode()); - } else { - addParentDirs(null, vPath, zOut, prefix, - fs.getDirMode()); - if (!entry.isDirectory()) { - zipFile(in, zOut, prefix + vPath, entry.getTime(), - zipSrc, fs.getFileMode()); - } + boolean dealingWithFiles = false; + File base = null; + + if (zfs == null || zfs.getSrc() == null) { + dealingWithFiles = true; + base = fileset.getDir(getProject()); + } else { + zf = new ZipFile(zfs.getSrc()); + } + + for (int i = 0; i < resources.length; i++) { + String name = null; + if (fullpath.length() > 0) { + name = fullpath; + } else { + name = resources[i].getName(); + } + name = name.replace(File.separatorChar, '/'); + + if ("".equals(name)) { + continue; + } + if (resources[i].isDirectory() && ! name.endsWith("/")) { + name = name + "/"; + } + + addParentDirs(base, name, zOut, prefix, dirMode); + + if (!resources[i].isDirectory() && dealingWithFiles) { + File f = fileUtils.resolveFile(base, + resources[i].getName()); + zipFile(f, zOut, prefix + name, fileMode); + } else if (!resources[i].isDirectory()) { + java.util.zip.ZipEntry ze = + zf.getEntry(resources[i].getName()); + if (ze != null) { + zipFile(zf.getInputStream(ze), zOut, prefix + name, + ze.getTime(), zfs.getSrc(), fileMode); } } } } finally { - if (in != null) { - in.close(); + if (zf != null) { + zf.close(); } } } @@ -623,7 +595,7 @@ public class Zip extends MatchingTask { /** * Create an empty zip file * - * @return true if the file is then considered up to date. + * @return true for historic reasons */ protected boolean createEmptyZip(File zipFile) { // In this case using java.util.zip will not work @@ -657,9 +629,12 @@ public class Zip extends MatchingTask { return true; } + /** + * @since Ant 1.6 + */ private synchronized ZipScanner getZipScanner() { if (zs == null) { - zs=new ZipScanner(); + zs = new ZipScanner(); // set the task of the zip scanner so that it can log properly zs.setTask(this); zs.setSrc(zipFile); @@ -668,144 +643,149 @@ public class Zip extends MatchingTask { } /** - * Check whether the archive is up-to-date; and handle behavior - * for empty archives. - * @param scanners list of prepared scanners containing files to archive + * Collect the resources that are newer than the corresponding + * entries (or missing) in the original archive. + * + *

If we are going to recreate the archive instead of updating + * it, all resources should be considered as new, if a single one + * is. Because of this, subclasses overriding this method must + * call super.getResourcesToAdd and indicate with the + * third arg if they already know that the archive is + * out-of-date.

+ * + * @param filesets The filesets to grab resources from * @param zipFile intended archive file (may or may not exist) - * @return true if nothing need be done (may have done something - * already); false if archive creation should proceed + * @param needsUpdate whether we already know that the archive is + * out-of-date. Subclasses overriding this method are supposed to + * set this value correctly in their call to + * super.getResourcesToAdd. + * @return an array of resources to add for each fileset passed in. + * * @exception BuildException if it likes */ - protected boolean isUpToDate(ResourceScanner[] scanners, - FileSet[] fss, File zipFile) + protected Resource[][] getResourcesToAdd(FileSet[] filesets, + File zipFile, + boolean needsUpdate) throws BuildException { - Resource[][] resourceNames = grabResources(scanners); - for (int counter = 0;counter < scanners.length; counter++){ - for (int j=0; j < resourceNames[counter].length;j++) { - log("resource from scanner " + counter + " " + j + " name : " - + resourceNames[counter][j].getName(), Project.MSG_DEBUG); - } - } - String[][] fileNames = grabFileNames(scanners); - File[] files = grabFiles(scanners, fileNames); - if (files.length == 0) { + Resource[][] initialResources = grabResources(filesets); + if (isEmpty(initialResources)) { if (emptyBehavior.equals("skip")) { - log("Warning: skipping " + archiveType + " archive " + zipFile + - " because no files were included.", Project.MSG_WARN); - return true; + log("Warning: skipping " + archiveType + " archive " + + zipFile + " because no files were included.", + Project.MSG_WARN); } else if (emptyBehavior.equals("fail")) { throw new BuildException("Cannot create " + archiveType + " archive " + zipFile + - ": no files were included.", getLocation()); + ": no files were included.", + getLocation()); } else { // Create. - return createEmptyZip(zipFile); - } - } else { - for (int i = 0; i < files.length; ++i) { - if (files[i].equals(zipFile)) { - throw new BuildException("A zip file cannot include " - + "itself", getLocation()); - } + createEmptyZip(zipFile); } + return initialResources; + } - if (!zipFile.exists()) { - return false; - } + if (!zipFile.exists()) { + return initialResources; + } + + if (needsUpdate && !doUpdate) { + // we are recreating the archive, need all resources + return initialResources; + } - for (int i = 0; i < scanners.length; i++) { - boolean result=false; - FileNameMapper myMapper = new IdentityMapper(); - if (fss[i] instanceof ZipFileSet) { - ZipFileSet zfs = (ZipFileSet) fss[i]; - if (zfs.getFullpath() != null - && !zfs.getFullpath().equals("") ) { - // in this case all files from origin map to - // the fullPath attribute of the zipfileset at - // destination - MergingMapper fm = new MergingMapper(); - fm.setTo(zfs.getFullpath()); - myMapper = fm; - - } else if (zfs.getPrefix() != null - && !zfs.getPrefix().equals("")) { - GlobPatternMapper gm=new GlobPatternMapper(); - gm.setFrom("*"); - gm.setTo(zfs.getPrefix() + "*"); - myMapper = gm; + Resource[][] newerResources = new Resource[filesets.length][]; + + for (int i = 0; i < filesets.length; i++) { + if (!(fileset instanceof ZipFileSet) + || ((ZipFileSet) fileset).getSrc() == null) { + File base = filesets[i].getDir(getProject()); + + for (int j = 0; j < initialResources[i].length; j++) { + File resourceAsFile = + fileUtils.resolveFile(base, + initialResources[i][j].getName()); + if (resourceAsFile.equals(zipFile)) { + throw new BuildException("A zip file cannot include " + + "itself", getLocation()); } } - Resource[] newerSources = - SourceSelector.selectOutOfDateSources(this, - resourceNames[i], - myMapper, - getZipScanner()); - result = (newerSources.length == 0); - if (!result) { - return result; - } } } - return true; - } - protected static File[] grabFiles(FileScanner[] scanners) { - return grabFiles(scanners, grabFileNames(scanners)); - } - - protected static File[] grabFiles(FileScanner[] scanners, - String[][] fileNames) { - Vector files = new Vector(); - for (int i = 0; i < fileNames.length; i++) { - File thisBaseDir = scanners[i].getBasedir(); - for (int j = 0; j < fileNames[i].length; j++) { - files.addElement(new File(thisBaseDir, fileNames[i][j])); + for (int i = 0; i < filesets.length; i++) { + if (initialResources[i].length == 0) { + continue; + } + + FileNameMapper myMapper = new IdentityMapper(); + if (filesets[i] instanceof ZipFileSet) { + ZipFileSet zfs = (ZipFileSet) filesets[i]; + if (zfs.getFullpath() != null + && !zfs.getFullpath().equals("") ) { + // in this case all files from origin map to + // the fullPath attribute of the zipfileset at + // destination + MergingMapper fm = new MergingMapper(); + fm.setTo(zfs.getFullpath()); + myMapper = fm; + + } else if (zfs.getPrefix() != null + && !zfs.getPrefix().equals("")) { + GlobPatternMapper gm=new GlobPatternMapper(); + gm.setFrom("*"); + String prefix = zfs.getPrefix(); + if (!prefix.endsWith("/") && !prefix.endsWith("\\")) { + prefix += "/"; + } + gm.setTo(prefix + "*"); + myMapper = gm; + } + } + newerResources[i] = + SourceSelector.selectOutOfDateSources(this, + initialResources[i], + myMapper, + getZipScanner()); + needsUpdate = needsUpdate || (newerResources[i].length > 0); + + if (needsUpdate && !doUpdate) { + // we will return initialResources anyway, no reason + // to scan further. + break; } } - File[] toret = new File[files.size()]; - files.copyInto(toret); - return toret; - } - protected static String[][] grabFileNames(FileScanner[] scanners) { - String[][] result = new String[scanners.length][]; - for (int i = 0; i < scanners.length; i++) { - String[] files = scanners[i].getIncludedFiles(); - String[] dirs = scanners[i].getIncludedDirectories(); - result[i] = new String[files.length + dirs.length]; - System.arraycopy(files, 0, result[i], 0, files.length); - System.arraycopy(dirs, 0, result[i], files.length, dirs.length); + if (needsUpdate && !doUpdate) { + // we are recreating the archive, need all resources + return initialResources; } - return result; + + return newerResources; } + /** + * Fetch all included and not excluded resources from the sets. + * + *

Included directories will preceede included files.

* - * @param scanners here are expected ResourceScanner arguments - * @return double dimensional array of resources + * @since Ant 1.6 */ - protected static Resource[][] grabResources(ResourceScanner[] scanners) { - Resource[][] result = new Resource[scanners.length][]; - for (int i = 0; i < scanners.length; i++) { - Resource[] files = scanners[i].getIncludedFileResources(); - Resource[] directories = - scanners[i].getIncludedDirectoryResources(); + protected Resource[][] grabResources(FileSet[] filesets) { + Resource[][] result = new Resource[filesets.length][]; + for (int i = 0; i < filesets.length; i++) { + ResourceScanner rs = filesets[i].getDirectoryScanner(getProject()); + Resource[] files = rs.getIncludedFileResources(); + Resource[] directories = rs.getIncludedDirectoryResources(); result[i] = new Resource[files.length + directories.length]; - System.arraycopy(files, 0, result[i], 0, files.length); - System.arraycopy(directories, 0, result[i], files.length, - directories.length); + System.arraycopy(directories, 0, result[i], 0, directories.length); + System.arraycopy(files, 0, result[i], directories.length, + files.length); } return result; } - /** - * @deprecated use four-arg version instead. - */ - protected void zipDir(File dir, ZipOutputStream zOut, String vPath) - throws IOException { - zipDir(dir, zOut, vPath, ZipFileSet.DEFAULT_DIR_MODE); - } - /** * @since Ant 1.6 */ @@ -841,20 +821,20 @@ public class Zip extends MatchingTask { } /** - * @deprecated use six-arg version instead. - */ - protected void zipFile(InputStream in, ZipOutputStream zOut, String vPath, - long lastModified, File file) - throws IOException { - zipFile(in, zOut, vPath, lastModified, file, - ZipFileSet.DEFAULT_FILE_MODE); - } - - /** + * Adds a new entry to the archive, takes care of duplicates as well. + * + * @param in the stream to read data for the entry from. + * @param zOut the stream to write to. + * @param vPath the name this entry shall have in the archive. + * @param lastModified last modification time for the entry. + * @param fromArchive the original archive we are copying this + * entry from, will be null if we are not copying from an archive. + * @param mode the Unix permissions to set. + * * @since Ant 1.6 */ protected void zipFile(InputStream in, ZipOutputStream zOut, String vPath, - long lastModified, File file, int mode) + long lastModified, File fromArchive, int mode) throws IOException { if (entries.contains(vPath)) { @@ -939,17 +919,18 @@ public class Zip extends MatchingTask { } /** - * @deprecated use six-arg version instead. - */ - protected void zipFile(File file, ZipOutputStream zOut, String vPath) - throws IOException { - zipFile(file, zOut, vPath, ZipFileSet.DEFAULT_FILE_MODE); - } - - /** + * Method that gets called when adding from java.io.File instances. + * + *

This implementation delegates to the six-arg version.

+ * + * @param file the file to add to the archive + * @param zOut the stream to write to + * @param vPath the name this entry shall have in the archive + * @param mode the Unix permissions to set. + * * @since Ant 1.6 */ - protected void zipFile(File file, ZipOutputStream zOut, String vPath, + protected void zipFile(File file, ZipOutputStream zOut, String vPath, int mode) throws IOException { if (file.equals(zipFile)) { @@ -966,24 +947,14 @@ public class Zip extends MatchingTask { } } - /** - * @deprecated use five-arg version instead. - */ - protected void addParentDirs(File baseDir, String entry, - ZipOutputStream zOut, String prefix) - throws IOException { - addParentDirs(baseDir, entry, zOut, prefix, - ZipFileSet.DEFAULT_DIR_MODE); - } - /** * Ensure all parent dirs of a given entry have been added. * * @since Ant 1.6 */ - protected void addParentDirs(File baseDir, String entry, - ZipOutputStream zOut, String prefix, - int dirMode) + protected final void addParentDirs(File baseDir, String entry, + ZipOutputStream zOut, String prefix, + int dirMode) throws IOException { if (!doFilesonly) { Stack directories = new Stack(); @@ -1010,55 +981,6 @@ public class Zip extends MatchingTask { } } - /** - * Iterate over the given Vector of (zip)filesets and add - * all files to the ZipOutputStream using the given prefix - * or fullpath. - */ - protected void addFiles(Vector filesets, ZipOutputStream zOut) - throws IOException { - // Add each fileset in the Vector. - for (int i = 0; i < filesets.size(); i++) { - FileSet fs = (FileSet) filesets.elementAt(i); - DirectoryScanner ds = fs.getDirectoryScanner(getProject()); - - String prefix = ""; - String fullpath = ""; - int fileMode = ZipFileSet.DEFAULT_FILE_MODE; - int dirMode = ZipFileSet.DEFAULT_DIR_MODE; - if (fs instanceof ZipFileSet) { - ZipFileSet zfs = (ZipFileSet) fs; - prefix = zfs.getPrefix(); - fullpath = zfs.getFullpath(); - fileMode = zfs.getFileMode(); - dirMode = zfs.getDirMode(); - } - - if (prefix.length() > 0 - && !prefix.endsWith("/") - && !prefix.endsWith("\\")) { - prefix += "/"; - } - - // Need to manually add either fullpath's parent directory, or - // the prefix directory, to the archive. - if (prefix.length() > 0) { - addParentDirs(null, prefix, zOut, "", dirMode); - zipDir(null, zOut, prefix, dirMode); - } else if (fullpath.length() > 0) { - addParentDirs(null, fullpath, zOut, "", dirMode); - } - - if (fs instanceof ZipFileSet - && ((ZipFileSet) fs).getSrc() != null) { - addZipEntries((ZipFileSet) fs, ds, zOut, prefix, fullpath); - } else { - // Add the fileset. - addFiles(ds, zOut, prefix, fullpath, dirMode, fileMode); - } - } - } - /** * Do any clean up necessary to allow this instance to be used again. * @@ -1109,6 +1031,20 @@ public class Zip extends MatchingTask { encoding = null; } + /** + * @return true if all individual arrays are empty + * + * @since Ant 1.6 + */ + protected final static boolean isEmpty(Resource[][] r) { + for (int i = 0; i < r.length; i++) { + if (r[i].length > 0) { + return false; + } + } + return true; + } + /** * Possible behaviors when a duplicate file is added: * "add", "preserve" or "fail" diff --git a/src/main/org/apache/tools/ant/types/ZipScanner.java b/src/main/org/apache/tools/ant/types/ZipScanner.java index 407d178ac..082be987b 100644 --- a/src/main/org/apache/tools/ant/types/ZipScanner.java +++ b/src/main/org/apache/tools/ant/types/ZipScanner.java @@ -127,18 +127,22 @@ public class ZipScanner extends DirectoryScanner { * include patterns and none of the exclude patterns. */ public String[] getIncludedFiles() { - Vector myvector = new Vector(); - // first check if the archive needs to be scanned again - scanme(); - for (Enumeration e = myentries.elements() ; e.hasMoreElements() ;) { - Resource myresource= (Resource) e.nextElement(); - if (!myresource.isDirectory() && match(myresource.getName())) { - myvector.addElement(myresource.getName()); + if (srcFile != null) { + Vector myvector = new Vector(); + // first check if the archive needs to be scanned again + scanme(); + for (Enumeration e = myentries.elements(); e.hasMoreElements() ;) { + Resource myresource= (Resource) e.nextElement(); + if (!myresource.isDirectory() && match(myresource.getName())) { + myvector.addElement(myresource.getName()); + } } + String[] files = new String[myvector.size()]; + myvector.copyInto(files); + return files; + } else { + return super.getIncludedFiles(); } - String[] files = new String[myvector.size()]; - myvector.copyInto(files); - return files; } /** @@ -150,18 +154,22 @@ public class ZipScanner extends DirectoryScanner { * include patterns and none of the exclude patterns. */ public String[] getIncludedDirectories() { - Vector myvector=new Vector(); - // first check if the archive needs to be scanned again - scanme(); - for (Enumeration e = myentries.elements() ; e.hasMoreElements() ;) { - Resource myresource= (Resource) e.nextElement(); - if (myresource.isDirectory() && match(myresource.getName())) { - myvector.addElement(myresource.getName()); + if (srcFile != null) { + Vector myvector=new Vector(); + // first check if the archive needs to be scanned again + scanme(); + for (Enumeration e = myentries.elements(); e.hasMoreElements() ;) { + Resource myresource= (Resource) e.nextElement(); + if (myresource.isDirectory() && match(myresource.getName())) { + myvector.addElement(myresource.getName()); + } } + String[] files = new String[myvector.size()]; + myvector.copyInto(files); + return files; + } else { + return super.getIncludedDirectories(); } - String[] files = new String[myvector.size()]; - myvector.copyInto(files); - return files; } /** @@ -205,18 +213,22 @@ public class ZipScanner extends DirectoryScanner { * @since Ant 1.5.2 */ public Resource[] getIncludedFileResources() { - Vector myvector = new Vector(); - // first check if the archive needs to be scanned again - scanme(); - for (Enumeration e = myentries.elements() ; e.hasMoreElements() ;) { - Resource myresource= (Resource) e.nextElement(); - if (!myresource.isDirectory() && match(myresource.getName())) { - myvector.addElement(myresource.clone()); + if (srcFile != null) { + Vector myvector = new Vector(); + // first check if the archive needs to be scanned again + scanme(); + for (Enumeration e = myentries.elements(); e.hasMoreElements() ;) { + Resource myresource= (Resource) e.nextElement(); + if (!myresource.isDirectory() && match(myresource.getName())) { + myvector.addElement(myresource.clone()); + } } + Resource[] resources = new Resource[myvector.size()]; + myvector.copyInto(resources); + return resources; + } else { + return super.getIncludedFileResources(); } - Resource[] resources = new Resource[myvector.size()]; - myvector.copyInto(resources); - return resources; } /** @@ -231,18 +243,22 @@ public class ZipScanner extends DirectoryScanner { * @since Ant 1.5.2 */ public Resource[] getIncludedDirectoryResources() { - Vector myvector = new Vector(); - // first check if the archive needs to be scanned again - scanme(); - for (Enumeration e = myentries.elements() ; e.hasMoreElements() ;) { - Resource myresource= (Resource) e.nextElement(); - if (myresource.isDirectory() && match(myresource.getName())) { - myvector.addElement(myresource.clone()); + if (srcFile != null) { + Vector myvector = new Vector(); + // first check if the archive needs to be scanned again + scanme(); + for (Enumeration e = myentries.elements(); e.hasMoreElements() ;) { + Resource myresource= (Resource) e.nextElement(); + if (myresource.isDirectory() && match(myresource.getName())) { + myvector.addElement(myresource.clone()); + } } + Resource[] resources = new Resource[myvector.size()]; + myvector.copyInto(resources); + return resources; + } else { + return super.getIncludedDirectoryResources(); } - Resource[] resources = new Resource[myvector.size()]; - myvector.copyInto(resources); - return resources; } /** diff --git a/src/testcases/org/apache/tools/ant/DirectoryScannerTest.java b/src/testcases/org/apache/tools/ant/DirectoryScannerTest.java index 6f47ca8ce..2e1dbc249 100644 --- a/src/testcases/org/apache/tools/ant/DirectoryScannerTest.java +++ b/src/testcases/org/apache/tools/ant/DirectoryScannerTest.java @@ -1,7 +1,7 @@ /* * The Apache Software License, Version 1.1 * - * Copyright (c) 2001-2002 The Apache Software Foundation. All rights + * Copyright (c) 2001-2003 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without @@ -55,6 +55,7 @@ package org.apache.tools.ant; import org.apache.tools.ant.taskdefs.condition.Os; +import org.apache.tools.ant.types.Resource; import org.apache.tools.ant.util.JavaEnvUtils; import junit.framework.TestCase; @@ -186,6 +187,20 @@ public class DirectoryScannerTest extends TestCase { assertTrue("(1) zip package included", haveZipPackage); assertTrue("(1) taskdefs package not included", !haveTaskdefsPackage); + haveZipPackage = false; + Resource[] includedResources = ds.getIncludedDirectoryResources(); + for (int i=0; i