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 36 kB

11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. *
  17. */
  18. package org.apache.tools.ant.taskdefs;
  19. import java.io.BufferedOutputStream;
  20. import java.io.File;
  21. import java.io.FileOutputStream;
  22. import java.io.IOException;
  23. import java.io.InputStream;
  24. import java.io.OutputStream;
  25. import java.util.Collection;
  26. import java.util.HashMap;
  27. import java.util.HashSet;
  28. import java.util.Iterator;
  29. import java.util.List;
  30. import java.util.Map;
  31. import java.util.Set;
  32. import java.util.Vector;
  33. import java.util.zip.GZIPOutputStream;
  34. import org.apache.tools.ant.BuildException;
  35. import org.apache.tools.ant.DirectoryScanner;
  36. import org.apache.tools.ant.Project;
  37. import org.apache.tools.ant.types.ArchiveFileSet;
  38. import org.apache.tools.ant.types.EnumeratedAttribute;
  39. import org.apache.tools.ant.types.FileSet;
  40. import org.apache.tools.ant.types.Resource;
  41. import org.apache.tools.ant.types.ResourceCollection;
  42. import org.apache.tools.ant.types.resources.ArchiveResource;
  43. import org.apache.tools.ant.types.resources.FileProvider;
  44. import org.apache.tools.ant.types.resources.FileResource;
  45. import org.apache.tools.ant.types.resources.TarResource;
  46. import org.apache.tools.ant.types.selectors.SelectorUtils;
  47. import org.apache.tools.ant.util.FileUtils;
  48. import org.apache.tools.ant.util.MergingMapper;
  49. import org.apache.tools.ant.util.ResourceUtils;
  50. import org.apache.tools.ant.util.SourceFileScanner;
  51. import org.apache.tools.bzip2.CBZip2OutputStream;
  52. import org.apache.tools.tar.TarConstants;
  53. import org.apache.tools.tar.TarEntry;
  54. import org.apache.tools.tar.TarOutputStream;
  55. /**
  56. * Creates a tar archive.
  57. *
  58. * @since Ant 1.1
  59. *
  60. * @ant.task category="packaging"
  61. */
  62. public class Tar extends MatchingTask {
  63. private static final int BUFFER_SIZE = 8 * 1024;
  64. /**
  65. * @deprecated since 1.5.x.
  66. * Tar.WARN is deprecated and is replaced with
  67. * Tar.TarLongFileMode.WARN
  68. */
  69. @Deprecated
  70. public static final String WARN = "warn";
  71. /**
  72. * @deprecated since 1.5.x.
  73. * Tar.FAIL is deprecated and is replaced with
  74. * Tar.TarLongFileMode.FAIL
  75. */
  76. @Deprecated
  77. public static final String FAIL = "fail";
  78. /**
  79. * @deprecated since 1.5.x.
  80. * Tar.TRUNCATE is deprecated and is replaced with
  81. * Tar.TarLongFileMode.TRUNCATE
  82. */
  83. @Deprecated
  84. public static final String TRUNCATE = "truncate";
  85. /**
  86. * @deprecated since 1.5.x.
  87. * Tar.GNU is deprecated and is replaced with
  88. * Tar.TarLongFileMode.GNU
  89. */
  90. @Deprecated
  91. public static final String GNU = "gnu";
  92. /**
  93. * @deprecated since 1.5.x.
  94. * Tar.OMIT is deprecated and is replaced with
  95. * Tar.TarLongFileMode.OMIT
  96. */
  97. @Deprecated
  98. public static final String OMIT = "omit";
  99. // CheckStyle:VisibilityModifier OFF - bc
  100. File tarFile;
  101. File baseDir;
  102. private TarLongFileMode longFileMode = new TarLongFileMode();
  103. // need to keep the package private version for backwards compatibility
  104. Vector<TarFileSet> filesets = new Vector<TarFileSet>();
  105. // we must keep two lists since other classes may modify the
  106. // filesets Vector (it is package private) without us noticing
  107. private final Vector<ResourceCollection> resourceCollections = new Vector<ResourceCollection>();
  108. // CheckStyle:VisibilityModifier ON
  109. /**
  110. * Indicates whether the user has been warned about long files already.
  111. */
  112. private boolean longWarningGiven = false;
  113. private TarCompressionMethod compression = new TarCompressionMethod();
  114. /**
  115. * Encoding to use for filenames, defaults to the platform's
  116. * default encoding.
  117. */
  118. private String encoding;
  119. /**
  120. * Add a new fileset with the option to specify permissions
  121. * @return the tar fileset to be used as the nested element.
  122. */
  123. public TarFileSet createTarFileSet() {
  124. final TarFileSet fs = new TarFileSet();
  125. fs.setProject(getProject());
  126. filesets.addElement(fs);
  127. return fs;
  128. }
  129. /**
  130. * Add a collection of resources to archive.
  131. * @param res a resource collection to archive.
  132. * @since Ant 1.7
  133. */
  134. public void add(final ResourceCollection res) {
  135. resourceCollections.add(res);
  136. }
  137. /**
  138. * Set is the name/location of where to create the tar file.
  139. * @param tarFile the location of the tar file.
  140. * @deprecated since 1.5.x.
  141. * For consistency with other tasks, please use setDestFile().
  142. */
  143. @Deprecated
  144. public void setTarfile(final File tarFile) {
  145. this.tarFile = tarFile;
  146. }
  147. /**
  148. * Set is the name/location of where to create the tar file.
  149. * @since Ant 1.5
  150. * @param destFile The output of the tar
  151. */
  152. public void setDestFile(final File destFile) {
  153. this.tarFile = destFile;
  154. }
  155. /**
  156. * This is the base directory to look in for things to tar.
  157. * @param baseDir the base directory.
  158. */
  159. public void setBasedir(final File baseDir) {
  160. this.baseDir = baseDir;
  161. }
  162. /**
  163. * Set how to handle long files, those with a path&gt;100 chars.
  164. * Optional, default=warn.
  165. * <p>
  166. * Allowable values are
  167. * <ul>
  168. * <li> truncate - paths are truncated to the maximum length
  169. * <li> fail - paths greater than the maximum cause a build exception
  170. * <li> warn - paths greater than the maximum cause a warning and GNU is used
  171. * <li> gnu - GNU extensions are used for any paths greater than the maximum.
  172. * <li> omit - paths greater than the maximum are omitted from the archive
  173. * </ul>
  174. * @param mode the mode string to handle long files.
  175. * @deprecated since 1.5.x.
  176. * setLongFile(String) is deprecated and is replaced with
  177. * setLongFile(Tar.TarLongFileMode) to make Ant's Introspection
  178. * mechanism do the work and also to encapsulate operations on
  179. * the mode in its own class.
  180. */
  181. @Deprecated
  182. public void setLongfile(final String mode) {
  183. log("DEPRECATED - The setLongfile(String) method has been deprecated."
  184. + " Use setLongfile(Tar.TarLongFileMode) instead.");
  185. this.longFileMode = new TarLongFileMode();
  186. longFileMode.setValue(mode);
  187. }
  188. /**
  189. * Set how to handle long files, those with a path&gt;100 chars.
  190. * Optional, default=warn.
  191. * <p>
  192. * Allowable values are
  193. * <ul>
  194. * <li> truncate - paths are truncated to the maximum length
  195. * <li> fail - paths greater than the maximum cause a build exception
  196. * <li> warn - paths greater than the maximum cause a warning and GNU is used
  197. * <li> gnu - extensions used by older versions of GNU tar are used for any paths greater than the maximum.
  198. * <li> posix - use POSIX PAX extension headers for any paths greater than the maximum. Supported by all modern tar implementations.
  199. * <li> omit - paths greater than the maximum are omitted from the archive
  200. * </ul>
  201. * @param mode the mode to handle long file names.
  202. */
  203. public void setLongfile(final TarLongFileMode mode) {
  204. this.longFileMode = mode;
  205. }
  206. /**
  207. * Set compression method.
  208. * Allowable values are
  209. * <ul>
  210. * <li> none - no compression
  211. * <li> gzip - Gzip compression
  212. * <li> bzip2 - Bzip2 compression
  213. * </ul>
  214. * @param mode the compression method.
  215. */
  216. public void setCompression(final TarCompressionMethod mode) {
  217. this.compression = mode;
  218. }
  219. /**
  220. * Encoding to use for filenames, defaults to the platform's
  221. * default encoding.
  222. *
  223. * <p>For a list of possible values see <a
  224. * href="http://java.sun.com/j2se/1.5.0/docs/guide/intl/encoding.doc.html">http://java.sun.com/j2se/1.5.0/docs/guide/intl/encoding.doc.html</a>.</p>
  225. * @param encoding the encoding name
  226. *
  227. * @since Ant 1.9.5
  228. */
  229. public void setEncoding(final String encoding) {
  230. this.encoding = encoding;
  231. }
  232. /**
  233. * do the business
  234. * @throws BuildException on error
  235. */
  236. @Override
  237. public void execute() throws BuildException {
  238. if (tarFile == null) {
  239. throw new BuildException("tarfile attribute must be set!",
  240. getLocation());
  241. }
  242. if (tarFile.exists() && tarFile.isDirectory()) {
  243. throw new BuildException("tarfile is a directory!",
  244. getLocation());
  245. }
  246. if (tarFile.exists() && !tarFile.canWrite()) {
  247. throw new BuildException("Can not write to the specified tarfile!",
  248. getLocation());
  249. }
  250. @SuppressWarnings("unchecked")
  251. final Vector<TarFileSet> savedFileSets = (Vector<TarFileSet>) filesets.clone();
  252. try {
  253. if (baseDir != null) {
  254. if (!baseDir.exists()) {
  255. throw new BuildException("basedir does not exist!",
  256. getLocation());
  257. }
  258. // add the main fileset to the list of filesets to process.
  259. final TarFileSet mainFileSet = new TarFileSet(fileset);
  260. mainFileSet.setDir(baseDir);
  261. filesets.addElement(mainFileSet);
  262. }
  263. if (filesets.size() == 0 && resourceCollections.size() == 0) {
  264. throw new BuildException("You must supply either a basedir "
  265. + "attribute or some nested resource"
  266. + " collections.",
  267. getLocation());
  268. }
  269. // check if tar is out of date with respect to each
  270. // fileset
  271. boolean upToDate = true;
  272. for(final TarFileSet tfs : filesets) {
  273. upToDate &= check(tfs);
  274. }
  275. for(final ResourceCollection rcol : resourceCollections) {
  276. upToDate &= check(rcol);
  277. }
  278. if (upToDate) {
  279. log("Nothing to do: " + tarFile.getAbsolutePath()
  280. + " is up to date.", Project.MSG_INFO);
  281. return;
  282. }
  283. final File parent = tarFile.getParentFile();
  284. if (parent != null && !parent.isDirectory()
  285. && !(parent.mkdirs() || parent.isDirectory())) {
  286. throw new BuildException("Failed to create missing parent"
  287. + " directory for " + tarFile);
  288. }
  289. log("Building tar: " + tarFile.getAbsolutePath(), Project.MSG_INFO);
  290. TarOutputStream tOut = null;
  291. try {
  292. tOut = new TarOutputStream(
  293. compression.compress(
  294. new BufferedOutputStream(
  295. new FileOutputStream(tarFile))),
  296. encoding);
  297. tOut.setDebug(true);
  298. if (longFileMode.isTruncateMode()) {
  299. tOut.setLongFileMode(TarOutputStream.LONGFILE_TRUNCATE);
  300. } else if (longFileMode.isFailMode()
  301. || longFileMode.isOmitMode()) {
  302. tOut.setLongFileMode(TarOutputStream.LONGFILE_ERROR);
  303. } else if (longFileMode.isPosixMode()) {
  304. tOut.setLongFileMode(TarOutputStream.LONGFILE_POSIX);
  305. } else {
  306. // warn or GNU
  307. tOut.setLongFileMode(TarOutputStream.LONGFILE_GNU);
  308. }
  309. longWarningGiven = false;
  310. for (final TarFileSet tfs : filesets) {
  311. tar(tfs, tOut);
  312. }
  313. for (final ResourceCollection rcol : resourceCollections) {
  314. tar(rcol, tOut);
  315. }
  316. } catch (final IOException ioe) {
  317. final String msg = "Problem creating TAR: " + ioe.getMessage();
  318. throw new BuildException(msg, ioe, getLocation());
  319. } finally {
  320. FileUtils.close(tOut);
  321. }
  322. } finally {
  323. filesets = savedFileSets;
  324. }
  325. }
  326. /**
  327. * tar a file
  328. * @param file the file to tar
  329. * @param tOut the output stream
  330. * @param vPath the path name of the file to tar
  331. * @param tarFileSet the fileset that the file came from.
  332. * @throws IOException on error
  333. */
  334. protected void tarFile(final File file, final TarOutputStream tOut, final String vPath,
  335. final TarFileSet tarFileSet)
  336. throws IOException {
  337. if (file.equals(tarFile)) {
  338. // If the archive is built for the first time and it is
  339. // matched by a resource collection, then it hasn't been
  340. // found in check (it hasn't been there) but will be
  341. // included now.
  342. //
  343. // for some strange reason the old code would simply skip
  344. // the entry and not fail, do the same now for backwards
  345. // compatibility reasons. Without this, the which4j build
  346. // fails in Gump
  347. return;
  348. }
  349. tarResource(new FileResource(file), tOut, vPath, tarFileSet);
  350. }
  351. /**
  352. * tar a resource
  353. * @param r the resource to tar
  354. * @param tOut the output stream
  355. * @param vPath the path name of the file to tar
  356. * @param tarFileSet the fileset that the file came from, may be null.
  357. * @throws IOException on error
  358. * @since Ant 1.7
  359. */
  360. protected void tarResource(final Resource r, final TarOutputStream tOut, String vPath,
  361. final TarFileSet tarFileSet)
  362. throws IOException {
  363. if (!r.isExists()) {
  364. return;
  365. }
  366. boolean preserveLeadingSlashes = false;
  367. if (tarFileSet != null) {
  368. final String fullpath = tarFileSet.getFullpath(this.getProject());
  369. if (fullpath.length() > 0) {
  370. vPath = fullpath;
  371. } else {
  372. // don't add "" to the archive
  373. if (vPath.length() <= 0) {
  374. return;
  375. }
  376. vPath = getCanonicalPrefix(tarFileSet, this.getProject()) + vPath;
  377. }
  378. preserveLeadingSlashes = tarFileSet.getPreserveLeadingSlashes();
  379. if (vPath.startsWith("/") && !preserveLeadingSlashes) {
  380. final int l = vPath.length();
  381. if (l <= 1) {
  382. // we would end up adding "" to the archive
  383. return;
  384. }
  385. vPath = vPath.substring(1, l);
  386. }
  387. }
  388. if (r.isDirectory() && !vPath.endsWith("/")) {
  389. vPath += "/";
  390. }
  391. if (vPath.length() >= TarConstants.NAMELEN) {
  392. if (longFileMode.isOmitMode()) {
  393. log("Omitting: " + vPath, Project.MSG_INFO);
  394. return;
  395. } else if (longFileMode.isWarnMode()) {
  396. log("Entry: " + vPath + " longer than "
  397. + TarConstants.NAMELEN + " characters.",
  398. Project.MSG_WARN);
  399. if (!longWarningGiven) {
  400. log("Resulting tar file can only be processed "
  401. + "successfully by GNU compatible tar commands",
  402. Project.MSG_WARN);
  403. longWarningGiven = true;
  404. }
  405. } else if (longFileMode.isFailMode()) {
  406. throw new BuildException("Entry: " + vPath
  407. + " longer than " + TarConstants.NAMELEN
  408. + "characters.", getLocation());
  409. }
  410. }
  411. final TarEntry te = new TarEntry(vPath, preserveLeadingSlashes);
  412. te.setModTime(r.getLastModified());
  413. // preserve permissions
  414. if (r instanceof ArchiveResource) {
  415. final ArchiveResource ar = (ArchiveResource) r;
  416. te.setMode(ar.getMode());
  417. if (r instanceof TarResource) {
  418. final TarResource tr = (TarResource) r;
  419. te.setUserName(tr.getUserName());
  420. te.setUserId(tr.getUid());
  421. te.setGroupName(tr.getGroup());
  422. te.setGroupId(tr.getGid());
  423. String linkName = tr.getLinkName();
  424. byte linkFlag = tr.getLinkFlag();
  425. if (linkFlag == TarConstants.LF_LINK &&
  426. linkName != null && linkName.length() > 0 && !linkName.startsWith("/")) {
  427. linkName = getCanonicalPrefix(tarFileSet, this.getProject()) + linkName;
  428. }
  429. te.setLinkName(linkName);
  430. te.setLinkFlag(linkFlag);
  431. }
  432. }
  433. if (!r.isDirectory()) {
  434. if (r.size() > TarConstants.MAXSIZE) {
  435. throw new BuildException(
  436. "Resource: " + r + " larger than "
  437. + TarConstants.MAXSIZE + " bytes.");
  438. }
  439. te.setSize(r.getSize());
  440. // override permissions if set explicitly
  441. if (tarFileSet != null && tarFileSet.hasFileModeBeenSet()) {
  442. te.setMode(tarFileSet.getMode());
  443. }
  444. } else if (tarFileSet != null && tarFileSet.hasDirModeBeenSet()) {
  445. // override permissions if set explicitly
  446. te.setMode(tarFileSet.getDirMode(this.getProject()));
  447. }
  448. if (tarFileSet != null) {
  449. // only override permissions if set explicitly
  450. if (tarFileSet.hasUserNameBeenSet()) {
  451. te.setUserName(tarFileSet.getUserName());
  452. }
  453. if (tarFileSet.hasGroupBeenSet()) {
  454. te.setGroupName(tarFileSet.getGroup());
  455. }
  456. if (tarFileSet.hasUserIdBeenSet()) {
  457. te.setUserId(tarFileSet.getUid());
  458. }
  459. if (tarFileSet.hasGroupIdBeenSet()) {
  460. te.setGroupId(tarFileSet.getGid());
  461. }
  462. }
  463. InputStream in = null;
  464. try {
  465. tOut.putNextEntry(te);
  466. if (!r.isDirectory()) {
  467. in = r.getInputStream();
  468. final byte[] buffer = new byte[BUFFER_SIZE];
  469. int count = 0;
  470. do {
  471. tOut.write(buffer, 0, count);
  472. count = in.read(buffer, 0, buffer.length);
  473. } while (count != -1);
  474. }
  475. tOut.closeEntry();
  476. } finally {
  477. FileUtils.close(in);
  478. }
  479. }
  480. /**
  481. * Is the archive up to date in relationship to a list of files.
  482. * @param files the files to check
  483. * @return true if the archive is up to date.
  484. * @deprecated since 1.5.x.
  485. * use the two-arg version instead.
  486. */
  487. @Deprecated
  488. protected boolean archiveIsUpToDate(final String[] files) {
  489. return archiveIsUpToDate(files, baseDir);
  490. }
  491. /**
  492. * Is the archive up to date in relationship to a list of files.
  493. * @param files the files to check
  494. * @param dir the base directory for the files.
  495. * @return true if the archive is up to date.
  496. * @since Ant 1.5.2
  497. */
  498. protected boolean archiveIsUpToDate(final String[] files, final File dir) {
  499. final SourceFileScanner sfs = new SourceFileScanner(this);
  500. final MergingMapper mm = new MergingMapper();
  501. mm.setTo(tarFile.getAbsolutePath());
  502. return sfs.restrict(files, dir, null, mm).length == 0;
  503. }
  504. /**
  505. * Is the archive up to date in relationship to a list of files.
  506. * @param r the files to check
  507. * @return true if the archive is up to date.
  508. * @since Ant 1.7
  509. */
  510. protected boolean archiveIsUpToDate(final Resource r) {
  511. return SelectorUtils.isOutOfDate(new FileResource(tarFile), r,
  512. FileUtils.getFileUtils()
  513. .getFileTimestampGranularity());
  514. }
  515. /**
  516. * Whether this task can deal with non-file resources.
  517. *
  518. * <p>This implementation returns true only if this task is
  519. * &lt;tar&gt;. Any subclass of this class that also wants to
  520. * support non-file resources needs to override this method. We
  521. * need to do so for backwards compatibility reasons since we
  522. * can't expect subclasses to support resources.</p>
  523. * @return true for this task.
  524. * @since Ant 1.7
  525. */
  526. protected boolean supportsNonFileResources() {
  527. return getClass().equals(Tar.class);
  528. }
  529. /**
  530. * Checks whether the archive is out-of-date with respect to the resources
  531. * of the given collection.
  532. *
  533. * <p>Also checks that either all collections only contain file
  534. * resources or this class supports non-file collections.</p>
  535. *
  536. * <p>And - in case of file-collections - ensures that the archive won't
  537. * contain itself.</p>
  538. *
  539. * @param rc the resource collection to check
  540. * @return whether the archive is up-to-date
  541. * @since Ant 1.7
  542. */
  543. protected boolean check(final ResourceCollection rc) {
  544. boolean upToDate = true;
  545. if (isFileFileSet(rc)) {
  546. final FileSet fs = (FileSet) rc;
  547. upToDate = check(fs.getDir(getProject()), getFileNames(fs));
  548. } else if (!rc.isFilesystemOnly() && !supportsNonFileResources()) {
  549. throw new BuildException("only filesystem resources are supported");
  550. } else if (rc.isFilesystemOnly()) {
  551. final Set<File> basedirs = new HashSet<File>();
  552. final Map<File, List<String>> basedirToFilesMap = new HashMap<File, List<String>>();
  553. for (final Resource res : rc) {
  554. final FileResource r = ResourceUtils
  555. .asFileResource(res.as(FileProvider.class));
  556. File base = r.getBaseDir();
  557. if (base == null) {
  558. base = Copy.NULL_FILE_PLACEHOLDER;
  559. }
  560. basedirs.add(base);
  561. List<String> files = basedirToFilesMap.get(base);
  562. if (files == null) {
  563. files = new Vector<String>();
  564. basedirToFilesMap.put(base, files);
  565. }
  566. if (base == Copy.NULL_FILE_PLACEHOLDER) {
  567. files.add(r.getFile().getAbsolutePath());
  568. } else {
  569. files.add(r.getName());
  570. }
  571. }
  572. for (final File base : basedirs) {
  573. final File tmpBase = base == Copy.NULL_FILE_PLACEHOLDER ? null : base;
  574. final List<String> files = basedirToFilesMap.get(base);
  575. upToDate &= check(tmpBase, files);
  576. }
  577. } else { // non-file resources
  578. final Iterator<Resource> iter = rc.iterator();
  579. while (upToDate && iter.hasNext()) {
  580. final Resource r = iter.next();
  581. upToDate = archiveIsUpToDate(r);
  582. }
  583. }
  584. return upToDate;
  585. }
  586. /**
  587. * <p>Checks whether the archive is out-of-date with respect to the
  588. * given files, ensures that the archive won't contain itself.</p>
  589. *
  590. * @param basedir base directory for file names
  591. * @param files array of relative file names
  592. * @return whether the archive is up-to-date
  593. * @since Ant 1.7
  594. */
  595. protected boolean check(final File basedir, final String[] files) {
  596. boolean upToDate = true;
  597. if (!archiveIsUpToDate(files, basedir)) {
  598. upToDate = false;
  599. }
  600. for (int i = 0; i < files.length; ++i) {
  601. if (tarFile.equals(new File(basedir, files[i]))) {
  602. throw new BuildException("A tar file cannot include "
  603. + "itself", getLocation());
  604. }
  605. }
  606. return upToDate;
  607. }
  608. /**
  609. * <p>Checks whether the archive is out-of-date with respect to the
  610. * given files, ensures that the archive won't contain itself.</p>
  611. *
  612. * @param basedir base directory for file names
  613. * @param files array of relative file names
  614. * @return whether the archive is up-to-date
  615. * @see #check(File, String[])
  616. * @since Ant 1.9.5
  617. */
  618. protected boolean check(final File basedir, final Collection<String> files) {
  619. return check(basedir, files.toArray(new String[files.size()]));
  620. }
  621. /**
  622. * Adds the resources contained in this collection to the archive.
  623. *
  624. * <p>Uses the file based methods for file resources for backwards
  625. * compatibility.</p>
  626. *
  627. * @param rc the collection containing resources to add
  628. * @param tOut stream writing to the archive.
  629. * @throws IOException on error.
  630. * @since Ant 1.7
  631. */
  632. protected void tar(final ResourceCollection rc, final TarOutputStream tOut)
  633. throws IOException {
  634. ArchiveFileSet afs = null;
  635. if (rc instanceof ArchiveFileSet) {
  636. afs = (ArchiveFileSet) rc;
  637. }
  638. if (afs != null && afs.size() > 1
  639. && afs.getFullpath(this.getProject()).length() > 0) {
  640. throw new BuildException("fullpath attribute may only "
  641. + "be specified for "
  642. + "filesets that specify a "
  643. + "single file.");
  644. }
  645. final TarFileSet tfs = asTarFileSet(afs);
  646. if (isFileFileSet(rc)) {
  647. final FileSet fs = (FileSet) rc;
  648. final String[] files = getFileNames(fs);
  649. for (int i = 0; i < files.length; i++) {
  650. final File f = new File(fs.getDir(getProject()), files[i]);
  651. final String name = files[i].replace(File.separatorChar, '/');
  652. tarFile(f, tOut, name, tfs);
  653. }
  654. } else if (rc.isFilesystemOnly()) {
  655. for (final Resource r : rc) {
  656. final File f = r.as(FileProvider.class).getFile();
  657. tarFile(f, tOut, f.getName(), tfs);
  658. }
  659. } else { // non-file resources
  660. for (final Resource r : rc) {
  661. tarResource(r, tOut, r.getName(), tfs);
  662. }
  663. }
  664. }
  665. /**
  666. * whether the given resource collection is a (subclass of)
  667. * FileSet that only contains file system resources.
  668. * @param rc the resource collection to check.
  669. * @return true if the collection is a fileset.
  670. * @since Ant 1.7
  671. */
  672. protected static boolean isFileFileSet(final ResourceCollection rc) {
  673. return rc instanceof FileSet && rc.isFilesystemOnly();
  674. }
  675. /**
  676. * Grabs all included files and directors from the FileSet and
  677. * returns them as an array of (relative) file names.
  678. * @param fs the fileset to operate on.
  679. * @return a list of the filenames.
  680. * @since Ant 1.7
  681. */
  682. protected static String[] getFileNames(final FileSet fs) {
  683. final DirectoryScanner ds = fs.getDirectoryScanner(fs.getProject());
  684. final String[] directories = ds.getIncludedDirectories();
  685. final String[] filesPerSe = ds.getIncludedFiles();
  686. final String[] files = new String [directories.length + filesPerSe.length];
  687. System.arraycopy(directories, 0, files, 0, directories.length);
  688. System.arraycopy(filesPerSe, 0, files, directories.length,
  689. filesPerSe.length);
  690. return files;
  691. }
  692. /**
  693. * Copies fullpath, prefix and permission attributes from the
  694. * ArchiveFileSet to a new TarFileSet (or returns it unchanged if
  695. * it already is a TarFileSet).
  696. *
  697. * @param archiveFileSet fileset to copy attributes from, may be null
  698. * @return a new TarFileSet.
  699. * @since Ant 1.7
  700. */
  701. protected TarFileSet asTarFileSet(final ArchiveFileSet archiveFileSet) {
  702. TarFileSet tfs = null;
  703. if (archiveFileSet != null && archiveFileSet instanceof TarFileSet) {
  704. tfs = (TarFileSet) archiveFileSet;
  705. } else {
  706. tfs = new TarFileSet();
  707. tfs.setProject(getProject());
  708. if (archiveFileSet != null) {
  709. tfs.setPrefix(archiveFileSet.getPrefix(getProject()));
  710. tfs.setFullpath(archiveFileSet.getFullpath(getProject()));
  711. if (archiveFileSet.hasFileModeBeenSet()) {
  712. tfs.integerSetFileMode(archiveFileSet
  713. .getFileMode(getProject()));
  714. }
  715. if (archiveFileSet.hasDirModeBeenSet()) {
  716. tfs.integerSetDirMode(archiveFileSet
  717. .getDirMode(getProject()));
  718. }
  719. if (archiveFileSet
  720. instanceof org.apache.tools.ant.types.TarFileSet) {
  721. final org.apache.tools.ant.types.TarFileSet t =
  722. (org.apache.tools.ant.types.TarFileSet) archiveFileSet;
  723. if (t.hasUserNameBeenSet()) {
  724. tfs.setUserName(t.getUserName());
  725. }
  726. if (t.hasGroupBeenSet()) {
  727. tfs.setGroup(t.getGroup());
  728. }
  729. if (t.hasUserIdBeenSet()) {
  730. tfs.setUid(t.getUid());
  731. }
  732. if (t.hasGroupIdBeenSet()) {
  733. tfs.setGid(t.getGid());
  734. }
  735. }
  736. }
  737. }
  738. return tfs;
  739. }
  740. private static String getCanonicalPrefix(TarFileSet tarFileSet, Project project) {
  741. String prefix = tarFileSet.getPrefix(project);
  742. // '/' is appended for compatibility with the zip task.
  743. if (prefix.isEmpty() || prefix.endsWith("/")) {
  744. return prefix;
  745. }
  746. return prefix += "/";
  747. }
  748. /**
  749. * This is a FileSet with the option to specify permissions
  750. * and other attributes.
  751. */
  752. public static class TarFileSet
  753. extends org.apache.tools.ant.types.TarFileSet {
  754. private String[] files = null;
  755. private boolean preserveLeadingSlashes = false;
  756. /**
  757. * Creates a new <code>TarFileSet</code> instance.
  758. * Using a fileset as a constructor argument.
  759. *
  760. * @param fileset a <code>FileSet</code> value
  761. */
  762. public TarFileSet(final FileSet fileset) {
  763. super(fileset);
  764. }
  765. /**
  766. * Creates a new <code>TarFileSet</code> instance.
  767. *
  768. */
  769. public TarFileSet() {
  770. super();
  771. }
  772. /**
  773. * Get a list of files and directories specified in the fileset.
  774. * @param p the current project.
  775. * @return a list of file and directory names, relative to
  776. * the baseDir for the project.
  777. */
  778. public String[] getFiles(final Project p) {
  779. if (files == null) {
  780. files = getFileNames(this);
  781. }
  782. return files;
  783. }
  784. /**
  785. * A 3 digit octal string, specify the user, group and
  786. * other modes in the standard Unix fashion;
  787. * optional, default=0644
  788. * @param octalString a 3 digit octal string.
  789. */
  790. public void setMode(final String octalString) {
  791. setFileMode(octalString);
  792. }
  793. /**
  794. * @return the current mode.
  795. */
  796. public int getMode() {
  797. return getFileMode(this.getProject());
  798. }
  799. /**
  800. * Flag to indicates whether leading `/'s should
  801. * be preserved in the file names.
  802. * Optional, default is <code>false</code>.
  803. * @param b the leading slashes flag.
  804. */
  805. public void setPreserveLeadingSlashes(final boolean b) {
  806. this.preserveLeadingSlashes = b;
  807. }
  808. /**
  809. * @return the leading slashes flag.
  810. */
  811. public boolean getPreserveLeadingSlashes() {
  812. return preserveLeadingSlashes;
  813. }
  814. }
  815. /**
  816. * Set of options for long file handling in the task.
  817. *
  818. */
  819. public static class TarLongFileMode extends EnumeratedAttribute {
  820. /** permissible values for longfile attribute */
  821. public static final String
  822. WARN = "warn",
  823. FAIL = "fail",
  824. TRUNCATE = "truncate",
  825. GNU = "gnu",
  826. POSIX = "posix",
  827. OMIT = "omit";
  828. private final String[] validModes = {
  829. WARN, FAIL, TRUNCATE, GNU, POSIX, OMIT
  830. };
  831. /** Constructor, defaults to "warn" */
  832. public TarLongFileMode() {
  833. super();
  834. setValue(WARN);
  835. }
  836. /**
  837. * @return the possible values for this enumerated type.
  838. */
  839. @Override
  840. public String[] getValues() {
  841. return validModes;
  842. }
  843. /**
  844. * @return true if value is "truncate".
  845. */
  846. public boolean isTruncateMode() {
  847. return TRUNCATE.equalsIgnoreCase(getValue());
  848. }
  849. /**
  850. * @return true if value is "warn".
  851. */
  852. public boolean isWarnMode() {
  853. return WARN.equalsIgnoreCase(getValue());
  854. }
  855. /**
  856. * @return true if value is "gnu".
  857. */
  858. public boolean isGnuMode() {
  859. return GNU.equalsIgnoreCase(getValue());
  860. }
  861. /**
  862. * @return true if value is "fail".
  863. */
  864. public boolean isFailMode() {
  865. return FAIL.equalsIgnoreCase(getValue());
  866. }
  867. /**
  868. * @return true if value is "omit".
  869. */
  870. public boolean isOmitMode() {
  871. return OMIT.equalsIgnoreCase(getValue());
  872. }
  873. /**
  874. * @return true if value is "posix".
  875. */
  876. public boolean isPosixMode() {
  877. return POSIX.equalsIgnoreCase(getValue());
  878. }
  879. }
  880. /**
  881. * Valid Modes for Compression attribute to Tar Task
  882. *
  883. */
  884. public static final class TarCompressionMethod extends EnumeratedAttribute {
  885. // permissible values for compression attribute
  886. /**
  887. * No compression
  888. */
  889. private static final String NONE = "none";
  890. /**
  891. * GZIP compression
  892. */
  893. private static final String GZIP = "gzip";
  894. /**
  895. * BZIP2 compression
  896. */
  897. private static final String BZIP2 = "bzip2";
  898. /**
  899. * Default constructor
  900. */
  901. public TarCompressionMethod() {
  902. super();
  903. setValue(NONE);
  904. }
  905. /**
  906. * Get valid enumeration values.
  907. * @return valid enumeration values
  908. */
  909. @Override
  910. public String[] getValues() {
  911. return new String[] {NONE, GZIP, BZIP2};
  912. }
  913. /**
  914. * This method wraps the output stream with the
  915. * corresponding compression method
  916. *
  917. * @param ostream output stream
  918. * @return output stream with on-the-fly compression
  919. * @exception IOException thrown if file is not writable
  920. */
  921. private OutputStream compress(final OutputStream ostream)
  922. throws IOException {
  923. final String v = getValue();
  924. if (GZIP.equals(v)) {
  925. return new GZIPOutputStream(ostream);
  926. } else {
  927. if (BZIP2.equals(v)) {
  928. ostream.write('B');
  929. ostream.write('Z');
  930. return new CBZip2OutputStream(ostream);
  931. }
  932. }
  933. return ostream;
  934. }
  935. }
  936. }