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.

Tar.java 18 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519
  1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. * Copyright (c) 2000-2002 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.File;
  56. import java.io.IOException;
  57. import java.io.FileOutputStream;
  58. import java.io.FileInputStream;
  59. import java.util.Vector;
  60. import java.util.Enumeration;
  61. import org.apache.tools.ant.BuildException;
  62. import org.apache.tools.ant.Project;
  63. import org.apache.tools.ant.DirectoryScanner;
  64. import org.apache.tools.ant.types.FileSet;
  65. import org.apache.tools.ant.types.EnumeratedAttribute;
  66. import org.apache.tools.ant.util.SourceFileScanner;
  67. import org.apache.tools.ant.util.MergingMapper;
  68. import org.apache.tools.tar.TarOutputStream;
  69. import org.apache.tools.tar.TarConstants;
  70. import org.apache.tools.tar.TarEntry;
  71. /**
  72. * Creates a TAR archive.
  73. *
  74. * @author Stefano Mazzocchi <a href="mailto:stefano@apache.org">stefano@apache.org</a>
  75. * @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a>
  76. * @author <a href="mailto:umagesh@apache.org">Magesh Umasankar</a>
  77. *
  78. * @ant:task category="packaging"
  79. */
  80. public class Tar extends MatchingTask {
  81. /**
  82. * @deprecated Tar.WARN is deprecated and is replaced with
  83. * Tar.TarLongFileMode.WARN
  84. */
  85. public final static String WARN = "warn";
  86. /**
  87. * @deprecated Tar.FAIL is deprecated and is replaced with
  88. * Tar.TarLongFileMode.FAIL
  89. */
  90. public final static String FAIL = "fail";
  91. /**
  92. * @deprecated Tar.TRUNCATE is deprecated and is replaced with
  93. * Tar.TarLongFileMode.TRUNCATE
  94. */
  95. public final static String TRUNCATE = "truncate";
  96. /**
  97. * @deprecated Tar.GNU is deprecated and is replaced with
  98. * Tar.TarLongFileMode.GNU
  99. */
  100. public final static String GNU = "gnu";
  101. /**
  102. * @deprecated Tar.OMIT is deprecated and is replaced with
  103. * Tar.TarLongFileMode.OMIT
  104. */
  105. public final static String OMIT = "omit";
  106. File tarFile;
  107. File baseDir;
  108. private TarLongFileMode longFileMode = new TarLongFileMode();
  109. Vector filesets = new Vector();
  110. Vector fileSetFiles = new Vector();
  111. /**
  112. * Indicates whether the user has been warned about long files already.
  113. */
  114. private boolean longWarningGiven = false;
  115. public TarFileSet createTarFileSet() {
  116. TarFileSet fileset = new TarFileSet();
  117. filesets.addElement(fileset);
  118. return fileset;
  119. }
  120. /**
  121. * This is the name/location of where to create the tar file.
  122. * @deprecated for consistency with other tasks, please use setDestFile()
  123. */
  124. public void setTarfile(File tarFile) {
  125. //log("DEPRECATED - The tarfile attribute is deprecated. Please use the destfile attribute instead.");
  126. this.tarFile = tarFile;
  127. }
  128. /**
  129. * Sets the destfile attribute.
  130. * @since 1.22 ant 1.5
  131. * @param destFile The output of the tar
  132. */
  133. public void setDestFile(File destFile) {
  134. this.tarFile = destFile;
  135. }
  136. /**
  137. * This is the base directory to look in for things to tar.
  138. */
  139. public void setBasedir(File baseDir) {
  140. this.baseDir = baseDir;
  141. }
  142. /**
  143. * Set how to handle long files.
  144. *
  145. * Allowable values are
  146. * truncate - paths are truncated to the maximum length
  147. * fail - paths greater than the maximim cause a build exception
  148. * warn - paths greater than the maximum cause a warning and GNU is used
  149. * gnu - GNU extensions are used for any paths greater than the maximum.
  150. * omit - paths greater than the maximum are omitted from the archive
  151. * @deprecated setLongFile(String) is deprecated and is replaced with
  152. * setLongFile(Tar.TarLongFileMode) to make Ant's Introspection
  153. * mechanism do the work and also to encapsulate operations on
  154. * the mode in its own class.
  155. */
  156. public void setLongfile(String mode) {
  157. log("DEPRECATED - The setLongfile(String) method has been deprecated."
  158. + " Use setLongfile(Tar.TarLongFileMode) instead.");
  159. this.longFileMode = new TarLongFileMode();
  160. longFileMode.setValue(mode);
  161. }
  162. /**
  163. * Set how to handle long files.
  164. *
  165. * Allowable values are
  166. * truncate - paths are truncated to the maximum length
  167. * fail - paths greater than the maximim cause a build exception
  168. * warn - paths greater than the maximum cause a warning and GNU is used
  169. * gnu - GNU extensions are used for any paths greater than the maximum.
  170. * omit - paths greater than the maximum are omitted from the archive
  171. */
  172. public void setLongfile(TarLongFileMode mode) {
  173. this.longFileMode = mode;
  174. }
  175. public void execute() throws BuildException {
  176. if (tarFile == null) {
  177. throw new BuildException("tarfile attribute must be set!",
  178. location);
  179. }
  180. if (tarFile.exists() && tarFile.isDirectory()) {
  181. throw new BuildException("tarfile is a directory!",
  182. location);
  183. }
  184. if (tarFile.exists() && !tarFile.canWrite()) {
  185. throw new BuildException("Can not write to the specified tarfile!",
  186. location);
  187. }
  188. if (baseDir != null) {
  189. if (!baseDir.exists()) {
  190. throw new BuildException("basedir does not exist!", location);
  191. }
  192. // add the main fileset to the list of filesets to process.
  193. TarFileSet mainFileSet = new TarFileSet(fileset);
  194. mainFileSet.setDir(baseDir);
  195. filesets.addElement(mainFileSet);
  196. }
  197. if (filesets.size() == 0) {
  198. throw new BuildException("You must supply either a basdir attribute or some nested filesets.",
  199. location);
  200. }
  201. // check if tr is out of date with respect to each
  202. // fileset
  203. boolean upToDate = true;
  204. for (Enumeration e = filesets.elements(); e.hasMoreElements();) {
  205. TarFileSet fs = (TarFileSet)e.nextElement();
  206. String[] files = fs.getFiles(project);
  207. if (!archiveIsUpToDate(files)) {
  208. upToDate = false;
  209. }
  210. for (int i = 0; i < files.length; ++i) {
  211. if (tarFile.equals(new File(fs.getDir(project), files[i]))) {
  212. throw new BuildException("A tar file cannot include itself", location);
  213. }
  214. }
  215. }
  216. if (upToDate) {
  217. log("Nothing to do: "+tarFile.getAbsolutePath()+" is up to date.",
  218. Project.MSG_INFO);
  219. return;
  220. }
  221. log("Building tar: "+ tarFile.getAbsolutePath(), Project.MSG_INFO);
  222. TarOutputStream tOut = null;
  223. try {
  224. tOut = new TarOutputStream(new FileOutputStream(tarFile));
  225. tOut.setDebug(true);
  226. if (longFileMode.isTruncateMode()) {
  227. tOut.setLongFileMode(TarOutputStream.LONGFILE_TRUNCATE);
  228. }
  229. else if (longFileMode.isFailMode() ||
  230. longFileMode.isOmitMode()) {
  231. tOut.setLongFileMode(TarOutputStream.LONGFILE_ERROR);
  232. }
  233. else {
  234. // warn or GNU
  235. tOut.setLongFileMode(TarOutputStream.LONGFILE_GNU);
  236. }
  237. longWarningGiven = false;
  238. for (Enumeration e = filesets.elements(); e.hasMoreElements();) {
  239. TarFileSet fs = (TarFileSet)e.nextElement();
  240. String[] files = fs.getFiles(project);
  241. if (files.length > 1 && fs.getFullpath().length() > 0) {
  242. throw new BuildException("fullpath attribute may only be specified for " +
  243. "filesets that specify a single file.");
  244. }
  245. for (int i = 0; i < files.length; i++) {
  246. File f = new File(fs.getDir(project), files[i]);
  247. String name = files[i].replace(File.separatorChar,'/');
  248. tarFile(f, tOut, name, fs);
  249. }
  250. }
  251. } catch (IOException ioe) {
  252. String msg = "Problem creating TAR: " + ioe.getMessage();
  253. throw new BuildException(msg, ioe, location);
  254. } finally {
  255. if (tOut != null) {
  256. try {
  257. // close up
  258. tOut.close();
  259. }
  260. catch (IOException e) {}
  261. }
  262. }
  263. }
  264. protected void tarFile(File file, TarOutputStream tOut, String vPath,
  265. TarFileSet tarFileSet)
  266. throws IOException
  267. {
  268. FileInputStream fIn = null;
  269. String fullpath = tarFileSet.getFullpath();
  270. if (fullpath.length() > 0) {
  271. vPath = fullpath;
  272. } else {
  273. // don't add "" to the archive
  274. if (vPath.length() <= 0) {
  275. return;
  276. }
  277. if (file.isDirectory() && !vPath.endsWith("/")) {
  278. vPath += "/";
  279. }
  280. String prefix = tarFileSet.getPrefix();
  281. // '/' is appended for compatibility with the zip task.
  282. if (prefix.length() > 0 && !prefix.endsWith("/")) {
  283. prefix = prefix + "/";
  284. }
  285. vPath = prefix + vPath;
  286. }
  287. if (vPath.startsWith("/") && !tarFileSet.getPreserveLeadingSlashes()) {
  288. int l = vPath.length();
  289. if (l <= 1) {
  290. // we would end up adding "" to the archive
  291. return;
  292. }
  293. vPath = vPath.substring(1, l);
  294. }
  295. try {
  296. if (vPath.length() >= TarConstants.NAMELEN) {
  297. if (longFileMode.isOmitMode()) {
  298. log("Omitting: "+ vPath, Project.MSG_INFO);
  299. return;
  300. } else if (longFileMode.isWarnMode()) {
  301. log("Entry: "+ vPath + " longer than " +
  302. TarConstants.NAMELEN + " characters.", Project.MSG_WARN);
  303. if (!longWarningGiven) {
  304. log("Resulting tar file can only be processed successfully"
  305. + " by GNU compatible tar commands", Project.MSG_WARN);
  306. longWarningGiven = true;
  307. }
  308. } else if (longFileMode.isFailMode()) {
  309. throw new BuildException(
  310. "Entry: "+ vPath + " longer than " +
  311. TarConstants.NAMELEN + "characters.", location);
  312. }
  313. }
  314. TarEntry te = new TarEntry(vPath);
  315. te.setModTime(file.lastModified());
  316. if (!file.isDirectory()) {
  317. te.setSize(file.length());
  318. te.setMode(tarFileSet.getMode());
  319. }
  320. te.setUserName(tarFileSet.getUserName());
  321. te.setGroupName(tarFileSet.getGroup());
  322. tOut.putNextEntry(te);
  323. if (!file.isDirectory()) {
  324. fIn = new FileInputStream(file);
  325. byte[] buffer = new byte[8 * 1024];
  326. int count = 0;
  327. do {
  328. tOut.write(buffer, 0, count);
  329. count = fIn.read(buffer, 0, buffer.length);
  330. } while (count != -1);
  331. }
  332. tOut.closeEntry();
  333. } finally {
  334. if (fIn != null) {
  335. fIn.close();
  336. }
  337. }
  338. }
  339. protected boolean archiveIsUpToDate(String[] files) {
  340. SourceFileScanner sfs = new SourceFileScanner(this);
  341. MergingMapper mm = new MergingMapper();
  342. mm.setTo(tarFile.getAbsolutePath());
  343. return sfs.restrict(files, baseDir, null, mm).length == 0;
  344. }
  345. public static class TarFileSet extends FileSet {
  346. private String[] files = null;
  347. private int mode = 0100644;
  348. private String userName = "";
  349. private String groupName = "";
  350. private String prefix = "";
  351. private String fullpath = "";
  352. private boolean preserveLeadingSlashes = false;
  353. public TarFileSet(FileSet fileset) {
  354. super(fileset);
  355. }
  356. public TarFileSet() {
  357. super();
  358. }
  359. /**
  360. * Get a list of files and directories specified in the fileset.
  361. * @return a list of file and directory names, relative to
  362. * the baseDir for the project.
  363. */
  364. public String[] getFiles(Project p) {
  365. if (files == null) {
  366. DirectoryScanner ds = getDirectoryScanner(p);
  367. String[] directories = ds.getIncludedDirectories();
  368. String[] filesPerSe = ds.getIncludedFiles();
  369. files = new String [directories.length + filesPerSe.length];
  370. System.arraycopy(directories, 0, files, 0, directories.length);
  371. System.arraycopy(filesPerSe, 0, files, directories.length,
  372. filesPerSe.length);
  373. }
  374. return files;
  375. }
  376. public void setMode(String octalString) {
  377. this.mode = 0100000 | Integer.parseInt(octalString, 8);
  378. }
  379. public int getMode() {
  380. return mode;
  381. }
  382. public void setUserName(String userName) {
  383. this.userName = userName;
  384. }
  385. public String getUserName() {
  386. return userName;
  387. }
  388. public void setGroup(String groupName) {
  389. this.groupName = groupName;
  390. }
  391. public String getGroup() {
  392. return groupName;
  393. }
  394. public void setPrefix(String prefix) {
  395. this.prefix = prefix;
  396. }
  397. public String getPrefix() {
  398. return prefix;
  399. }
  400. public void setFullpath(String fullpath) {
  401. this.fullpath = fullpath;
  402. }
  403. public String getFullpath() {
  404. return fullpath;
  405. }
  406. public void setPreserveLeadingSlashes(boolean b) {
  407. this.preserveLeadingSlashes = b;
  408. }
  409. public boolean getPreserveLeadingSlashes() {
  410. return preserveLeadingSlashes;
  411. }
  412. }
  413. /**
  414. * Valid Modes for LongFile attribute to Tar Task
  415. *
  416. * @author <a href="mailto:umagesh@apache.org">Magesh Umasankar</a>
  417. */
  418. public static class TarLongFileMode extends EnumeratedAttribute {
  419. // permissable values for longfile attribute
  420. public final static String WARN = "warn";
  421. public final static String FAIL = "fail";
  422. public final static String TRUNCATE = "truncate";
  423. public final static String GNU = "gnu";
  424. public final static String OMIT = "omit";
  425. private final String[] validModes = {WARN, FAIL, TRUNCATE, GNU, OMIT};
  426. public TarLongFileMode() {
  427. super();
  428. setValue(WARN);
  429. }
  430. public String[] getValues() {
  431. return validModes;
  432. }
  433. public boolean isTruncateMode() {
  434. return TRUNCATE.equalsIgnoreCase(getValue());
  435. }
  436. public boolean isWarnMode() {
  437. return WARN.equalsIgnoreCase(getValue());
  438. }
  439. public boolean isGnuMode() {
  440. return GNU.equalsIgnoreCase(getValue());
  441. }
  442. public boolean isFailMode() {
  443. return FAIL.equalsIgnoreCase(getValue());
  444. }
  445. public boolean isOmitMode() {
  446. return OMIT.equalsIgnoreCase(getValue());
  447. }
  448. }
  449. }