You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

Zip.java 26 kB

Addition of ZipFileset facilities. Descibed by the author --- With these patches, Zip (and derivative tasks such as Jar and War) can merge the entries of multiple zip files into a single output zip file. The contents of an input zip file may be selectively extracted based on include/exclude patterns. An included zip file is specified using a <fileset> with a "src" attribute, as in: <target name="jartest"> <jar jarfile="utils.jar"> <fileset src="weblogic.jar" includes="weblogic/utils/" excludes="weblogic/utils/jars/,**/reflect/" /> </jar> </target> In this example, a subset of the "weblogic/utils" directory is extracted from weblogic.jar, into utils.jar. The fileset may also contain "prefix" and "fullpath" attributes (the functionality of PrefixedFileSet has been retained in the new class ZipFileSet). Prefixes apply to directory-based and zip-based filesets. The fullpath attributes applies only to a single file in a directory-based fileset. The War task may extract entries from a zip file for all of its filesets (including the files in "classes" and "lib"). The motivation for this change is: 1) There is significant overlap between "jlink" and "zip", and it seemed better to combine them. 2) "jlink" does not support include/exclude patterns which are extremely useful for writing packaging-type tasks such as Zip/Jar/War. This was my main motivation. 3) By adding this functionality to the base task, it can also be used in derivative tasks such as Jar and War. --- Submitted By: Don Ferguson <don@bea.com> git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@268458 13f79535-47bb-0310-9956-ffa450edef68
25 years ago
Addition of ZipFileset facilities. Descibed by the author --- With these patches, Zip (and derivative tasks such as Jar and War) can merge the entries of multiple zip files into a single output zip file. The contents of an input zip file may be selectively extracted based on include/exclude patterns. An included zip file is specified using a <fileset> with a "src" attribute, as in: <target name="jartest"> <jar jarfile="utils.jar"> <fileset src="weblogic.jar" includes="weblogic/utils/" excludes="weblogic/utils/jars/,**/reflect/" /> </jar> </target> In this example, a subset of the "weblogic/utils" directory is extracted from weblogic.jar, into utils.jar. The fileset may also contain "prefix" and "fullpath" attributes (the functionality of PrefixedFileSet has been retained in the new class ZipFileSet). Prefixes apply to directory-based and zip-based filesets. The fullpath attributes applies only to a single file in a directory-based fileset. The War task may extract entries from a zip file for all of its filesets (including the files in "classes" and "lib"). The motivation for this change is: 1) There is significant overlap between "jlink" and "zip", and it seemed better to combine them. 2) "jlink" does not support include/exclude patterns which are extremely useful for writing packaging-type tasks such as Zip/Jar/War. This was my main motivation. 3) By adding this functionality to the base task, it can also be used in derivative tasks such as Jar and War. --- Submitted By: Don Ferguson <don@bea.com> git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@268458 13f79535-47bb-0310-9956-ffa450edef68
25 years ago
Addition of ZipFileset facilities. Descibed by the author --- With these patches, Zip (and derivative tasks such as Jar and War) can merge the entries of multiple zip files into a single output zip file. The contents of an input zip file may be selectively extracted based on include/exclude patterns. An included zip file is specified using a <fileset> with a "src" attribute, as in: <target name="jartest"> <jar jarfile="utils.jar"> <fileset src="weblogic.jar" includes="weblogic/utils/" excludes="weblogic/utils/jars/,**/reflect/" /> </jar> </target> In this example, a subset of the "weblogic/utils" directory is extracted from weblogic.jar, into utils.jar. The fileset may also contain "prefix" and "fullpath" attributes (the functionality of PrefixedFileSet has been retained in the new class ZipFileSet). Prefixes apply to directory-based and zip-based filesets. The fullpath attributes applies only to a single file in a directory-based fileset. The War task may extract entries from a zip file for all of its filesets (including the files in "classes" and "lib"). The motivation for this change is: 1) There is significant overlap between "jlink" and "zip", and it seemed better to combine them. 2) "jlink" does not support include/exclude patterns which are extremely useful for writing packaging-type tasks such as Zip/Jar/War. This was my main motivation. 3) By adding this functionality to the base task, it can also be used in derivative tasks such as Jar and War. --- Submitted By: Don Ferguson <don@bea.com> git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@268458 13f79535-47bb-0310-9956-ffa450edef68
25 years ago
Addition of ZipFileset facilities. Descibed by the author --- With these patches, Zip (and derivative tasks such as Jar and War) can merge the entries of multiple zip files into a single output zip file. The contents of an input zip file may be selectively extracted based on include/exclude patterns. An included zip file is specified using a <fileset> with a "src" attribute, as in: <target name="jartest"> <jar jarfile="utils.jar"> <fileset src="weblogic.jar" includes="weblogic/utils/" excludes="weblogic/utils/jars/,**/reflect/" /> </jar> </target> In this example, a subset of the "weblogic/utils" directory is extracted from weblogic.jar, into utils.jar. The fileset may also contain "prefix" and "fullpath" attributes (the functionality of PrefixedFileSet has been retained in the new class ZipFileSet). Prefixes apply to directory-based and zip-based filesets. The fullpath attributes applies only to a single file in a directory-based fileset. The War task may extract entries from a zip file for all of its filesets (including the files in "classes" and "lib"). The motivation for this change is: 1) There is significant overlap between "jlink" and "zip", and it seemed better to combine them. 2) "jlink" does not support include/exclude patterns which are extremely useful for writing packaging-type tasks such as Zip/Jar/War. This was my main motivation. 3) By adding this functionality to the base task, it can also be used in derivative tasks such as Jar and War. --- Submitted By: Don Ferguson <don@bea.com> git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@268458 13f79535-47bb-0310-9956-ffa450edef68
25 years ago
Addition of ZipFileset facilities. Descibed by the author --- With these patches, Zip (and derivative tasks such as Jar and War) can merge the entries of multiple zip files into a single output zip file. The contents of an input zip file may be selectively extracted based on include/exclude patterns. An included zip file is specified using a <fileset> with a "src" attribute, as in: <target name="jartest"> <jar jarfile="utils.jar"> <fileset src="weblogic.jar" includes="weblogic/utils/" excludes="weblogic/utils/jars/,**/reflect/" /> </jar> </target> In this example, a subset of the "weblogic/utils" directory is extracted from weblogic.jar, into utils.jar. The fileset may also contain "prefix" and "fullpath" attributes (the functionality of PrefixedFileSet has been retained in the new class ZipFileSet). Prefixes apply to directory-based and zip-based filesets. The fullpath attributes applies only to a single file in a directory-based fileset. The War task may extract entries from a zip file for all of its filesets (including the files in "classes" and "lib"). The motivation for this change is: 1) There is significant overlap between "jlink" and "zip", and it seemed better to combine them. 2) "jlink" does not support include/exclude patterns which are extremely useful for writing packaging-type tasks such as Zip/Jar/War. This was my main motivation. 3) By adding this functionality to the base task, it can also be used in derivative tasks such as Jar and War. --- Submitted By: Don Ferguson <don@bea.com> git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@268458 13f79535-47bb-0310-9956-ffa450edef68
25 years ago
Addition of ZipFileset facilities. Descibed by the author --- With these patches, Zip (and derivative tasks such as Jar and War) can merge the entries of multiple zip files into a single output zip file. The contents of an input zip file may be selectively extracted based on include/exclude patterns. An included zip file is specified using a <fileset> with a "src" attribute, as in: <target name="jartest"> <jar jarfile="utils.jar"> <fileset src="weblogic.jar" includes="weblogic/utils/" excludes="weblogic/utils/jars/,**/reflect/" /> </jar> </target> In this example, a subset of the "weblogic/utils" directory is extracted from weblogic.jar, into utils.jar. The fileset may also contain "prefix" and "fullpath" attributes (the functionality of PrefixedFileSet has been retained in the new class ZipFileSet). Prefixes apply to directory-based and zip-based filesets. The fullpath attributes applies only to a single file in a directory-based fileset. The War task may extract entries from a zip file for all of its filesets (including the files in "classes" and "lib"). The motivation for this change is: 1) There is significant overlap between "jlink" and "zip", and it seemed better to combine them. 2) "jlink" does not support include/exclude patterns which are extremely useful for writing packaging-type tasks such as Zip/Jar/War. This was my main motivation. 3) By adding this functionality to the base task, it can also be used in derivative tasks such as Jar and War. --- Submitted By: Don Ferguson <don@bea.com> git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@268458 13f79535-47bb-0310-9956-ffa450edef68
25 years ago
Addition of ZipFileset facilities. Descibed by the author --- With these patches, Zip (and derivative tasks such as Jar and War) can merge the entries of multiple zip files into a single output zip file. The contents of an input zip file may be selectively extracted based on include/exclude patterns. An included zip file is specified using a <fileset> with a "src" attribute, as in: <target name="jartest"> <jar jarfile="utils.jar"> <fileset src="weblogic.jar" includes="weblogic/utils/" excludes="weblogic/utils/jars/,**/reflect/" /> </jar> </target> In this example, a subset of the "weblogic/utils" directory is extracted from weblogic.jar, into utils.jar. The fileset may also contain "prefix" and "fullpath" attributes (the functionality of PrefixedFileSet has been retained in the new class ZipFileSet). Prefixes apply to directory-based and zip-based filesets. The fullpath attributes applies only to a single file in a directory-based fileset. The War task may extract entries from a zip file for all of its filesets (including the files in "classes" and "lib"). The motivation for this change is: 1) There is significant overlap between "jlink" and "zip", and it seemed better to combine them. 2) "jlink" does not support include/exclude patterns which are extremely useful for writing packaging-type tasks such as Zip/Jar/War. This was my main motivation. 3) By adding this functionality to the base task, it can also be used in derivative tasks such as Jar and War. --- Submitted By: Don Ferguson <don@bea.com> git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@268458 13f79535-47bb-0310-9956-ffa450edef68
25 years ago
Addition of ZipFileset facilities. Descibed by the author --- With these patches, Zip (and derivative tasks such as Jar and War) can merge the entries of multiple zip files into a single output zip file. The contents of an input zip file may be selectively extracted based on include/exclude patterns. An included zip file is specified using a <fileset> with a "src" attribute, as in: <target name="jartest"> <jar jarfile="utils.jar"> <fileset src="weblogic.jar" includes="weblogic/utils/" excludes="weblogic/utils/jars/,**/reflect/" /> </jar> </target> In this example, a subset of the "weblogic/utils" directory is extracted from weblogic.jar, into utils.jar. The fileset may also contain "prefix" and "fullpath" attributes (the functionality of PrefixedFileSet has been retained in the new class ZipFileSet). Prefixes apply to directory-based and zip-based filesets. The fullpath attributes applies only to a single file in a directory-based fileset. The War task may extract entries from a zip file for all of its filesets (including the files in "classes" and "lib"). The motivation for this change is: 1) There is significant overlap between "jlink" and "zip", and it seemed better to combine them. 2) "jlink" does not support include/exclude patterns which are extremely useful for writing packaging-type tasks such as Zip/Jar/War. This was my main motivation. 3) By adding this functionality to the base task, it can also be used in derivative tasks such as Jar and War. --- Submitted By: Don Ferguson <don@bea.com> git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@268458 13f79535-47bb-0310-9956-ffa450edef68
25 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695
  1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. * Copyright (c) 1999 The Apache Software Foundation. All rights
  5. * reserved.
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions
  9. * are met:
  10. *
  11. * 1. Redistributions of source code must retain the above copyright
  12. * notice, this list of conditions and the following disclaimer.
  13. *
  14. * 2. Redistributions in binary form must reproduce the above copyright
  15. * notice, this list of conditions and the following disclaimer in
  16. * the documentation and/or other materials provided with the
  17. * distribution.
  18. *
  19. * 3. The end-user documentation included with the redistribution, if
  20. * any, must include the following acknowlegement:
  21. * "This product includes software developed by the
  22. * Apache Software Foundation (http://www.apache.org/)."
  23. * Alternately, this acknowlegement may appear in the software itself,
  24. * if and wherever such third-party acknowlegements normally appear.
  25. *
  26. * 4. The names "The Jakarta Project", "Ant", and "Apache Software
  27. * Foundation" must not be used to endorse or promote products derived
  28. * from this software without prior written permission. For written
  29. * permission, please contact apache@apache.org.
  30. *
  31. * 5. Products derived from this software may not be called "Apache"
  32. * nor may "Apache" appear in their names without prior written
  33. * permission of the Apache Group.
  34. *
  35. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  36. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  37. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  38. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  39. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  40. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  41. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  42. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  43. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  44. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  45. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  46. * SUCH DAMAGE.
  47. * ====================================================================
  48. *
  49. * This software consists of voluntary contributions made by many
  50. * individuals on behalf of the Apache Software Foundation. For more
  51. * information on the Apache Software Foundation, please see
  52. * <http://www.apache.org/>.
  53. */
  54. package org.apache.tools.ant.taskdefs;
  55. import java.io.*;
  56. import java.util.Enumeration;
  57. import java.util.Hashtable;
  58. import java.util.Stack;
  59. import java.util.StringTokenizer;
  60. import java.util.Vector;
  61. import java.util.zip.CRC32;
  62. import java.util.zip.ZipInputStream;
  63. import org.apache.tools.ant.*;
  64. import org.apache.tools.ant.types.*;
  65. import org.apache.tools.ant.util.*;
  66. import org.apache.tools.zip.*;
  67. /**
  68. * Create a ZIP archive.
  69. *
  70. * @author James Davidson <a href="mailto:duncan@x180.com">duncan@x180.com</a>
  71. * @author Jon S. Stevens <a href="mailto:jon@clearink.com">jon@clearink.com</a>
  72. * @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a>
  73. */
  74. public class Zip extends MatchingTask {
  75. private File zipFile;
  76. private File baseDir;
  77. private boolean doCompress = true;
  78. private boolean doUpdate = true;
  79. private boolean doFilesonly = false;
  80. protected String archiveType = "zip";
  81. // For directories:
  82. private static long emptyCrc = new CRC32 ().getValue ();
  83. protected String emptyBehavior = "skip";
  84. private Vector filesets = new Vector ();
  85. private Hashtable addedDirs = new Hashtable();
  86. private Vector addedFiles = new Vector();
  87. /**
  88. * Encoding to use for filenames, defaults to the platform's
  89. * default encoding.
  90. */
  91. private String encoding = null;
  92. /**
  93. * This is the name/location of where to
  94. * create the .zip file.
  95. */
  96. public void setZipfile(File zipFile) {
  97. this.zipFile = zipFile;
  98. }
  99. /**
  100. * This is the base directory to look in for
  101. * things to zip.
  102. */
  103. public void setBasedir(File baseDir) {
  104. this.baseDir = baseDir;
  105. }
  106. /**
  107. * Sets whether we want to compress the files or only store them.
  108. */
  109. public void setCompress(boolean c) {
  110. doCompress = c;
  111. }
  112. /**
  113. * Emulate Sun's jar utility by not adding parent dirs
  114. */
  115. public void setFilesonly(boolean f) {
  116. doFilesonly = f;
  117. }
  118. /**
  119. * Sets whether we want to update the file (if it exists)
  120. * or create a new one.
  121. */
  122. public void setUpdate(boolean c) {
  123. doUpdate = c;
  124. }
  125. /**
  126. * Adds a set of files (nested fileset attribute).
  127. */
  128. public void addFileset(FileSet set) {
  129. filesets.addElement(set);
  130. }
  131. /**
  132. * Adds a set of files (nested zipfileset attribute) that can be
  133. * read from an archive and be given a prefix/fullpath.
  134. */
  135. public void addZipfileset(ZipFileSet set) {
  136. filesets.addElement(set);
  137. }
  138. /** Possible behaviors when there are no matching files for the task. */
  139. public static class WhenEmpty extends EnumeratedAttribute {
  140. public String[] getValues() {
  141. return new String[] {"fail", "skip", "create"};
  142. }
  143. }
  144. /**
  145. * Sets behavior of the task when no files match.
  146. * Possible values are: <code>fail</code> (throw an exception
  147. * and halt the build); <code>skip</code> (do not create
  148. * any archive, but issue a warning); <code>create</code>
  149. * (make an archive with no entries).
  150. * Default for zip tasks is <code>skip</code>;
  151. * for jar tasks, <code>create</code>.
  152. */
  153. public void setWhenempty(WhenEmpty we) {
  154. emptyBehavior = we.getValue();
  155. }
  156. /**
  157. * Encoding to use for filenames, defaults to the platform's
  158. * default encoding.
  159. *
  160. * <p>For a list of possible values see <a
  161. * href="http://java.sun.com/products/jdk/1.2/docs/guide/internat/encoding.doc.html">http://java.sun.com/products/jdk/1.2/docs/guide/internat/encoding.doc.html</a>.</p>
  162. */
  163. public void setEncoding(String encoding) {
  164. this.encoding = encoding;
  165. }
  166. public void execute() throws BuildException {
  167. if (baseDir == null && filesets.size() == 0 && "zip".equals(archiveType)) {
  168. throw new BuildException( "basedir attribute must be set, or at least " +
  169. "one fileset must be given!" );
  170. }
  171. if (zipFile == null) {
  172. throw new BuildException("You must specify the " + archiveType + " file to create!");
  173. }
  174. // Renamed version of original file, if it exists
  175. File renamedFile=null;
  176. // Whether or not an actual update is required -
  177. // we don't need to update if the original file doesn't exist
  178. boolean reallyDoUpdate=false;
  179. if (doUpdate && zipFile.exists())
  180. {
  181. reallyDoUpdate=true;
  182. int i;
  183. for (i=0; i < 1000; i++)
  184. {
  185. renamedFile = new File (zipFile.getParent(), "tmp."+i);
  186. if (!renamedFile.exists())
  187. break;
  188. }
  189. if (i==1000)
  190. throw new BuildException
  191. ("Can't find temporary filename to rename old file to.");
  192. try
  193. {
  194. if (!zipFile.renameTo (renamedFile))
  195. throw new BuildException
  196. ("Unable to rename old file to temporary file");
  197. }
  198. catch (SecurityException e)
  199. {
  200. throw new BuildException
  201. ("Not allowed to rename old file to temporary file");
  202. }
  203. }
  204. // Create the scanners to pass to isUpToDate().
  205. Vector dss = new Vector ();
  206. if (baseDir != null)
  207. dss.addElement(getDirectoryScanner(baseDir));
  208. for (int i=0; i<filesets.size(); i++) {
  209. FileSet fs = (FileSet) filesets.elementAt(i);
  210. dss.addElement (fs.getDirectoryScanner(project));
  211. }
  212. int dssSize = dss.size();
  213. FileScanner[] scanners = new FileScanner[dssSize];
  214. dss.copyInto(scanners);
  215. // quick exit if the target is up to date
  216. // can also handle empty archives
  217. if (isUpToDate(scanners, zipFile)) return;
  218. String action=reallyDoUpdate ? "Updating " : "Building ";
  219. log(action + archiveType +": "+ zipFile.getAbsolutePath());
  220. boolean success = false;
  221. try {
  222. ZipOutputStream zOut =
  223. new ZipOutputStream(new FileOutputStream(zipFile));
  224. zOut.setEncoding(encoding);
  225. try {
  226. if (doCompress) {
  227. zOut.setMethod(ZipOutputStream.DEFLATED);
  228. } else {
  229. zOut.setMethod(ZipOutputStream.STORED);
  230. }
  231. initZipOutputStream(zOut);
  232. // Add the implicit fileset to the archive.
  233. if (baseDir != null)
  234. addFiles(getDirectoryScanner(baseDir), zOut, "", "");
  235. // Add the explicit filesets to the archive.
  236. addFiles(filesets, zOut);
  237. if (reallyDoUpdate)
  238. {
  239. ZipFileSet oldFiles = new ZipFileSet ();
  240. oldFiles.setSrc (renamedFile);
  241. StringBuffer exclusionPattern=new StringBuffer();
  242. for (int i=0; i < addedFiles.size(); i++)
  243. {
  244. if (i != 0)
  245. exclusionPattern.append (",");
  246. exclusionPattern.append
  247. ((String) addedFiles.elementAt(i));
  248. }
  249. oldFiles.setExcludes (exclusionPattern.toString());
  250. Vector tmp = new Vector();
  251. tmp.addElement (oldFiles);
  252. addFiles (tmp, zOut);
  253. }
  254. success = true;
  255. } finally {
  256. // Close the output stream.
  257. try {
  258. if (zOut != null)
  259. zOut.close ();
  260. } catch(IOException ex) {
  261. // If we're in this finally clause because of an exception, we don't
  262. // really care if there's an exception when closing the stream. E.g. if it
  263. // throws "ZIP file must have at least one entry", because an exception happened
  264. // before we added any files, then we must swallow this exception. Otherwise,
  265. // the error that's reported will be the close() error, which is not the real
  266. // cause of the problem.
  267. if (success)
  268. throw ex;
  269. }
  270. }
  271. } catch (IOException ioe) {
  272. String msg = "Problem creating " + archiveType + ": " + ioe.getMessage();
  273. // delete a bogus ZIP file
  274. if (!zipFile.delete()) {
  275. msg += " (and the archive is probably corrupt but I could not delete it)";
  276. }
  277. if (reallyDoUpdate) {
  278. if (!renamedFile.renameTo (zipFile)) {
  279. msg+=" (and I couldn't rename the temporary file "+
  280. renamedFile.getName()+" back)";
  281. }
  282. }
  283. throw new BuildException(msg, ioe, location);
  284. } finally {
  285. cleanUp();
  286. }
  287. // If we've been successful on an update, delete the temporary file
  288. if (success && reallyDoUpdate)
  289. if (!renamedFile.delete())
  290. log ("Warning: unable to delete temporary file "+
  291. renamedFile.getName(), Project.MSG_WARN);
  292. }
  293. /**
  294. * Add all files of the given FileScanner to the ZipOutputStream
  295. * prependig the given prefix to each filename.
  296. *
  297. * <p>Ensure parent directories have been added as well.
  298. */
  299. protected void addFiles(FileScanner scanner, ZipOutputStream zOut,
  300. String prefix, String fullpath) throws IOException {
  301. if (prefix.length() > 0 && fullpath.length() > 0)
  302. throw new BuildException("Both prefix and fullpath attributes may not be set on the same fileset.");
  303. File thisBaseDir = scanner.getBasedir();
  304. // directories that matched include patterns
  305. String[] dirs = scanner.getIncludedDirectories();
  306. if (dirs.length > 0 && fullpath.length() > 0)
  307. throw new BuildException("fullpath attribute may only be specified for filesets that specify a single file.");
  308. for (int i = 0; i < dirs.length; i++) {
  309. if ("".equals(dirs[i])) {
  310. continue;
  311. }
  312. String name = dirs[i].replace(File.separatorChar,'/');
  313. if (!name.endsWith("/")) {
  314. name += "/";
  315. }
  316. addParentDirs(thisBaseDir, name, zOut, prefix);
  317. }
  318. // files that matched include patterns
  319. String[] files = scanner.getIncludedFiles();
  320. if (files.length > 1 && fullpath.length() > 0)
  321. throw new BuildException("fullpath attribute may only be specified for filesets that specify a single file.");
  322. for (int i = 0; i < files.length; i++) {
  323. File f = new File(thisBaseDir, files[i]);
  324. if (fullpath.length() > 0)
  325. {
  326. // Add this file at the specified location.
  327. addParentDirs(null, fullpath, zOut, "");
  328. zipFile(f, zOut, fullpath);
  329. }
  330. else
  331. {
  332. // Add this file with the specified prefix.
  333. String name = files[i].replace(File.separatorChar,'/');
  334. addParentDirs(thisBaseDir, name, zOut, prefix);
  335. zipFile(f, zOut, prefix+name);
  336. }
  337. }
  338. }
  339. protected void addZipEntries(ZipFileSet fs, DirectoryScanner ds,
  340. ZipOutputStream zOut, String prefix)
  341. throws IOException
  342. {
  343. ZipScanner zipScanner = (ZipScanner) ds;
  344. File zipSrc = fs.getSrc();
  345. ZipEntry entry;
  346. java.util.zip.ZipEntry origEntry;
  347. ZipInputStream in = null;
  348. try {
  349. in = new ZipInputStream(new FileInputStream(zipSrc));
  350. while ((origEntry = in.getNextEntry()) != null) {
  351. entry = new ZipEntry(origEntry);
  352. String vPath = entry.getName();
  353. if (zipScanner.match(vPath)) {
  354. addParentDirs(null, vPath, zOut, prefix);
  355. if (! entry.isDirectory()) {
  356. zipFile(in, zOut, prefix+vPath, entry.getTime());
  357. }
  358. }
  359. }
  360. } finally {
  361. if (in != null) {
  362. in.close();
  363. }
  364. }
  365. }
  366. protected void initZipOutputStream(ZipOutputStream zOut)
  367. throws IOException, BuildException
  368. {
  369. }
  370. /**
  371. * Check whether the archive is up-to-date; and handle behavior for empty archives.
  372. * @param scanners list of prepared scanners containing files to archive
  373. * @param zipFile intended archive file (may or may not exist)
  374. * @return true if nothing need be done (may have done something already); false if
  375. * archive creation should proceed
  376. * @exception BuildException if it likes
  377. */
  378. protected boolean isUpToDate(FileScanner[] scanners, File zipFile) throws BuildException
  379. {
  380. String[][] fileNames = grabFileNames(scanners);
  381. File[] files = grabFiles(scanners, fileNames);
  382. if (files.length == 0) {
  383. if (emptyBehavior.equals("skip")) {
  384. log("Warning: skipping "+archiveType+" archive " + zipFile +
  385. " because no files were included.", Project.MSG_WARN);
  386. return true;
  387. } else if (emptyBehavior.equals("fail")) {
  388. throw new BuildException("Cannot create "+archiveType+" archive " + zipFile +
  389. ": no files were included.", location);
  390. } else {
  391. // Create.
  392. if (zipFile.exists()) return true;
  393. // In this case using java.util.zip will not work
  394. // because it does not permit a zero-entry archive.
  395. // Must create it manually.
  396. log("Note: creating empty "+archiveType+" archive " + zipFile, Project.MSG_INFO);
  397. try {
  398. OutputStream os = new FileOutputStream(zipFile);
  399. try {
  400. // Cf. PKZIP specification.
  401. byte[] empty = new byte[22];
  402. empty[0] = 80; // P
  403. empty[1] = 75; // K
  404. empty[2] = 5;
  405. empty[3] = 6;
  406. // remainder zeros
  407. os.write(empty);
  408. } finally {
  409. os.close();
  410. }
  411. } catch (IOException ioe) {
  412. throw new BuildException("Could not create empty ZIP archive", ioe, location);
  413. }
  414. return true;
  415. }
  416. } else {
  417. for (int i = 0; i < files.length; ++i) {
  418. if (files[i].equals(zipFile)) {
  419. throw new BuildException("A zip file cannot include itself", location);
  420. }
  421. }
  422. if (!zipFile.exists()) return false;
  423. SourceFileScanner sfs = new SourceFileScanner(this);
  424. MergingMapper mm = new MergingMapper();
  425. mm.setTo(zipFile.getAbsolutePath());
  426. for (int i=0; i<scanners.length; i++) {
  427. if (sfs.restrict(fileNames[i], scanners[i].getBasedir(), null,
  428. mm).length > 0) {
  429. return false;
  430. }
  431. }
  432. return true;
  433. }
  434. }
  435. protected static File[] grabFiles(FileScanner[] scanners) {
  436. return grabFiles(scanners, grabFileNames(scanners));
  437. }
  438. protected static File[] grabFiles(FileScanner[] scanners,
  439. String[][] fileNames) {
  440. Vector files = new Vector();
  441. for (int i = 0; i < fileNames.length; i++) {
  442. File thisBaseDir = scanners[i].getBasedir();
  443. for (int j = 0; j < fileNames[i].length; j++)
  444. files.addElement(new File(thisBaseDir, fileNames[i][j]));
  445. }
  446. File[] toret = new File[files.size()];
  447. files.copyInto(toret);
  448. return toret;
  449. }
  450. protected static String[][] grabFileNames(FileScanner[] scanners) {
  451. String[][] result = new String[scanners.length][];
  452. for (int i=0; i<scanners.length; i++) {
  453. String[] files = scanners[i].getIncludedFiles();
  454. String[] dirs = scanners[i].getIncludedDirectories();
  455. result[i] = new String[files.length + dirs.length];
  456. System.arraycopy(files, 0, result[i], 0, files.length);
  457. System.arraycopy(dirs, 0, result[i], files.length, dirs.length);
  458. }
  459. return result;
  460. }
  461. protected void zipDir(File dir, ZipOutputStream zOut, String vPath)
  462. throws IOException
  463. {
  464. if (addedDirs.get(vPath) != null) {
  465. // don't add directories we've already added.
  466. // no warning if we try, it is harmless in and of itself
  467. return;
  468. }
  469. addedDirs.put(vPath, vPath);
  470. ZipEntry ze = new ZipEntry (vPath);
  471. if (dir != null && dir.exists()) {
  472. ze.setTime(dir.lastModified());
  473. } else {
  474. ze.setTime(System.currentTimeMillis());
  475. }
  476. ze.setSize (0);
  477. ze.setMethod (ZipEntry.STORED);
  478. // This is faintly ridiculous:
  479. ze.setCrc (emptyCrc);
  480. // this is 040775 | MS-DOS directory flag in reverse byte order
  481. ze.setExternalAttributes(0x41FD0010L);
  482. zOut.putNextEntry (ze);
  483. }
  484. protected void zipFile(InputStream in, ZipOutputStream zOut, String vPath,
  485. long lastModified)
  486. throws IOException
  487. {
  488. ZipEntry ze = new ZipEntry(vPath);
  489. ze.setTime(lastModified);
  490. /*
  491. * XXX ZipOutputStream.putEntry expects the ZipEntry to know its
  492. * size and the CRC sum before you start writing the data when using
  493. * STORED mode.
  494. *
  495. * This forces us to process the data twice.
  496. *
  497. * I couldn't find any documentation on this, just found out by try
  498. * and error.
  499. */
  500. if (!doCompress) {
  501. long size = 0;
  502. CRC32 cal = new CRC32();
  503. if (!in.markSupported()) {
  504. // Store data into a byte[]
  505. ByteArrayOutputStream bos = new ByteArrayOutputStream();
  506. byte[] buffer = new byte[8 * 1024];
  507. int count = 0;
  508. do {
  509. size += count;
  510. cal.update(buffer, 0, count);
  511. bos.write(buffer, 0, count);
  512. count = in.read(buffer, 0, buffer.length);
  513. } while (count != -1);
  514. in = new ByteArrayInputStream(bos.toByteArray());
  515. } else {
  516. in.mark(Integer.MAX_VALUE);
  517. byte[] buffer = new byte[8 * 1024];
  518. int count = 0;
  519. do {
  520. size += count;
  521. cal.update(buffer, 0, count);
  522. count = in.read(buffer, 0, buffer.length);
  523. } while (count != -1);
  524. in.reset();
  525. }
  526. ze.setSize(size);
  527. ze.setCrc(cal.getValue());
  528. }
  529. zOut.putNextEntry(ze);
  530. byte[] buffer = new byte[8 * 1024];
  531. int count = 0;
  532. do {
  533. if (count != 0) {
  534. zOut.write(buffer, 0, count);
  535. }
  536. count = in.read(buffer, 0, buffer.length);
  537. } while (count != -1);
  538. addedFiles.addElement (vPath);
  539. }
  540. protected void zipFile(File file, ZipOutputStream zOut, String vPath)
  541. throws IOException
  542. {
  543. if (file.equals(zipFile)) {
  544. throw new BuildException("A zip file cannot include itself", location);
  545. }
  546. FileInputStream fIn = new FileInputStream(file);
  547. try {
  548. zipFile(fIn, zOut, vPath, file.lastModified());
  549. } finally {
  550. fIn.close();
  551. }
  552. }
  553. /**
  554. * Ensure all parent dirs of a given entry have been added.
  555. */
  556. protected void addParentDirs(File baseDir, String entry,
  557. ZipOutputStream zOut, String prefix)
  558. throws IOException {
  559. if( !doFilesonly ) {
  560. Stack directories = new Stack();
  561. int slashPos = entry.length();
  562. while ((slashPos = entry.lastIndexOf((int)'/', slashPos-1)) != -1) {
  563. String dir = entry.substring(0, slashPos+1);
  564. if (addedDirs.get(prefix+dir) != null) {
  565. break;
  566. }
  567. directories.push(dir);
  568. }
  569. while (!directories.isEmpty()) {
  570. String dir = (String) directories.pop();
  571. File f = null;
  572. if (baseDir != null) {
  573. f = new File(baseDir, dir);
  574. } else {
  575. f = new File(dir);
  576. }
  577. zipDir(f, zOut, prefix+dir);
  578. }
  579. }
  580. }
  581. /**
  582. * Iterate over the given Vector of (zip)filesets and add
  583. * all files to the ZipOutputStream using the given prefix.
  584. */
  585. protected void addFiles(Vector filesets, ZipOutputStream zOut)
  586. throws IOException {
  587. // Add each fileset in the Vector.
  588. for (int i = 0; i<filesets.size(); i++) {
  589. FileSet fs = (FileSet) filesets.elementAt(i);
  590. DirectoryScanner ds = fs.getDirectoryScanner(project);
  591. String prefix = "";
  592. String fullpath = "";
  593. if (fs instanceof ZipFileSet) {
  594. ZipFileSet zfs = (ZipFileSet) fs;
  595. prefix = zfs.getPrefix();
  596. fullpath = zfs.getFullpath();
  597. }
  598. if (prefix.length() > 0
  599. && !prefix.endsWith("/")
  600. && !prefix.endsWith("\\")) {
  601. prefix += "/";
  602. }
  603. // Need to manually add either fullpath's parent directory, or
  604. // the prefix directory, to the archive.
  605. if (prefix.length() > 0) {
  606. addParentDirs(null, prefix, zOut, "");
  607. zipDir(null, zOut, prefix);
  608. } else if (fullpath.length() > 0) {
  609. addParentDirs(null, fullpath, zOut, "");
  610. }
  611. if (fs instanceof ZipFileSet
  612. && ((ZipFileSet) fs).getSrc() != null) {
  613. addZipEntries((ZipFileSet) fs, ds, zOut, prefix);
  614. } else {
  615. // Add the fileset.
  616. addFiles(ds, zOut, prefix, fullpath);
  617. }
  618. }
  619. }
  620. /**
  621. * Do any clean up necessary to allow this instance to be used again.
  622. *
  623. * <p>When we get here, the Zip file has been closed and all we
  624. * need to do is to reset some globals.</p>
  625. */
  626. protected void cleanUp() {
  627. addedDirs = new Hashtable();
  628. addedFiles = new Vector();
  629. }
  630. }