source: release-kits/lirk3/resources/gs3-release-maker/apache-ant-1.6.5/src/main/org/apache/tools/ant/taskdefs/Tar.java@ 14982

Last change on this file since 14982 was 14982, checked in by oranfry, 16 years ago

initial import of LiRK3

File size: 25.2 KB
Line 
1/*
2 * Copyright 2000-2004 The Apache Software Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17
18package org.apache.tools.ant.taskdefs;
19
20import java.io.BufferedOutputStream;
21import java.io.File;
22import java.io.FileInputStream;
23import java.io.FileOutputStream;
24import java.io.IOException;
25import java.io.OutputStream;
26import java.util.Enumeration;
27import java.util.Vector;
28import java.util.zip.GZIPOutputStream;
29import org.apache.tools.ant.BuildException;
30import org.apache.tools.ant.DirectoryScanner;
31import org.apache.tools.ant.Project;
32import org.apache.tools.ant.types.EnumeratedAttribute;
33import org.apache.tools.ant.types.FileSet;
34import org.apache.tools.ant.util.MergingMapper;
35import org.apache.tools.ant.util.SourceFileScanner;
36import org.apache.tools.bzip2.CBZip2OutputStream;
37import org.apache.tools.tar.TarConstants;
38import org.apache.tools.tar.TarEntry;
39import org.apache.tools.tar.TarOutputStream;
40import org.apache.tools.zip.UnixStat;
41
42/**
43 * Creates a tar archive.
44 *
45 * @since Ant 1.1
46 *
47 * @ant.task category="packaging"
48 */
49public class Tar extends MatchingTask {
50
51 /**
52 * @deprecated Tar.WARN is deprecated and is replaced with
53 * Tar.TarLongFileMode.WARN
54 */
55 public static final String WARN = "warn";
56 /**
57 * @deprecated Tar.FAIL is deprecated and is replaced with
58 * Tar.TarLongFileMode.FAIL
59 */
60 public static final String FAIL = "fail";
61 /**
62 * @deprecated Tar.TRUNCATE is deprecated and is replaced with
63 * Tar.TarLongFileMode.TRUNCATE
64 */
65 public static final String TRUNCATE = "truncate";
66 /**
67 * @deprecated Tar.GNU is deprecated and is replaced with
68 * Tar.TarLongFileMode.GNU
69 */
70 public static final String GNU = "gnu";
71 /**
72 * @deprecated Tar.OMIT is deprecated and is replaced with
73 * Tar.TarLongFileMode.OMIT
74 */
75 public static final String OMIT = "omit";
76
77 File tarFile;
78 File baseDir;
79
80 private TarLongFileMode longFileMode = new TarLongFileMode();
81
82 Vector filesets = new Vector();
83 Vector fileSetFiles = new Vector();
84
85 /**
86 * Indicates whether the user has been warned about long files already.
87 */
88 private boolean longWarningGiven = false;
89
90 private TarCompressionMethod compression = new TarCompressionMethod();
91
92 /**
93 * Add a new fileset with the option to specify permissions
94 * @return the tar fileset to be used as the nested element.
95 */
96 public TarFileSet createTarFileSet() {
97 TarFileSet fileset = new TarFileSet();
98 filesets.addElement(fileset);
99 return fileset;
100 }
101
102
103 /**
104 * Set is the name/location of where to create the tar file.
105 * @param tarFile the location of the tar file.
106 * @deprecated for consistency with other tasks, please use setDestFile()
107 */
108 public void setTarfile(File tarFile) {
109 this.tarFile = tarFile;
110 }
111
112 /**
113 * Set is the name/location of where to create the tar file.
114 * @since Ant 1.5
115 * @param destFile The output of the tar
116 */
117 public void setDestFile(File destFile) {
118 this.tarFile = destFile;
119 }
120
121 /**
122 * This is the base directory to look in for things to tar.
123 * @param baseDir the base directory.
124 */
125 public void setBasedir(File baseDir) {
126 this.baseDir = baseDir;
127 }
128
129 /**
130 * Set how to handle long files, those with a path>100 chars.
131 * Optional, default=warn.
132 * <p>
133 * Allowable values are
134 * <ul>
135 * <li> truncate - paths are truncated to the maximum length
136 * <li> fail - paths greater than the maximum cause a build exception
137 * <li> warn - paths greater than the maximum cause a warning and GNU is used
138 * <li> gnu - GNU extensions are used for any paths greater than the maximum.
139 * <li> omit - paths greater than the maximum are omitted from the archive
140 * </ul>
141 * @param mode the mode string to handle long files.
142 * @deprecated setLongFile(String) is deprecated and is replaced with
143 * setLongFile(Tar.TarLongFileMode) to make Ant's Introspection
144 * mechanism do the work and also to encapsulate operations on
145 * the mode in its own class.
146 */
147 public void setLongfile(String mode) {
148 log("DEPRECATED - The setLongfile(String) method has been deprecated."
149 + " Use setLongfile(Tar.TarLongFileMode) instead.");
150 this.longFileMode = new TarLongFileMode();
151 longFileMode.setValue(mode);
152 }
153
154 /**
155 * Set how to handle long files, those with a path&gt;100 chars.
156 * Optional, default=warn.
157 * <p>
158 * Allowable values are
159 * <ul>
160 * <li> truncate - paths are truncated to the maximum length
161 * <li> fail - paths greater than the maximum cause a build exception
162 * <li> warn - paths greater than the maximum cause a warning and GNU is used
163 * <li> gnu - GNU extensions are used for any paths greater than the maximum.
164 * <li> omit - paths greater than the maximum are omitted from the archive
165 * </ul>
166 * @param mode the mode to handle long file names.
167 */
168 public void setLongfile(TarLongFileMode mode) {
169 this.longFileMode = mode;
170 }
171
172 /**
173 * Set compression method.
174 * Allowable values are
175 * <ul>
176 * <li> none - no compression
177 * <li> gzip - Gzip compression
178 * <li> bzip2 - Bzip2 compression
179 * </ul>
180 * @param mode the compression method.
181 */
182 public void setCompression(TarCompressionMethod mode) {
183 this.compression = mode;
184 }
185
186 /**
187 * do the business
188 * @throws BuildException on error
189 */
190 public void execute() throws BuildException {
191 if (tarFile == null) {
192 throw new BuildException("tarfile attribute must be set!",
193 getLocation());
194 }
195
196 if (tarFile.exists() && tarFile.isDirectory()) {
197 throw new BuildException("tarfile is a directory!",
198 getLocation());
199 }
200
201 if (tarFile.exists() && !tarFile.canWrite()) {
202 throw new BuildException("Can not write to the specified tarfile!",
203 getLocation());
204 }
205
206 Vector savedFileSets = (Vector) filesets.clone();
207 try {
208 if (baseDir != null) {
209 if (!baseDir.exists()) {
210 throw new BuildException("basedir does not exist!",
211 getLocation());
212 }
213
214 // add the main fileset to the list of filesets to process.
215 TarFileSet mainFileSet = new TarFileSet(fileset);
216 mainFileSet.setDir(baseDir);
217 filesets.addElement(mainFileSet);
218 }
219
220 if (filesets.size() == 0) {
221 throw new BuildException("You must supply either a basedir "
222 + "attribute or some nested filesets.",
223 getLocation());
224 }
225
226 // check if tar is out of date with respect to each
227 // fileset
228 boolean upToDate = true;
229 for (Enumeration e = filesets.elements(); e.hasMoreElements();) {
230 TarFileSet fs = (TarFileSet) e.nextElement();
231 String[] files = fs.getFiles(getProject());
232
233 if (!archiveIsUpToDate(files, fs.getDir(getProject()))) {
234 upToDate = false;
235 }
236
237 for (int i = 0; i < files.length; ++i) {
238 if (tarFile.equals(new File(fs.getDir(getProject()),
239 files[i]))) {
240 throw new BuildException("A tar file cannot include "
241 + "itself", getLocation());
242 }
243 }
244 }
245
246 if (upToDate) {
247 log("Nothing to do: " + tarFile.getAbsolutePath()
248 + " is up to date.", Project.MSG_INFO);
249 return;
250 }
251
252 log("Building tar: " + tarFile.getAbsolutePath(), Project.MSG_INFO);
253
254 TarOutputStream tOut = null;
255 try {
256 tOut = new TarOutputStream(
257 compression.compress(
258 new BufferedOutputStream(
259 new FileOutputStream(tarFile))));
260 tOut.setDebug(true);
261 if (longFileMode.isTruncateMode()) {
262 tOut.setLongFileMode(TarOutputStream.LONGFILE_TRUNCATE);
263 } else if (longFileMode.isFailMode()
264 || longFileMode.isOmitMode()) {
265 tOut.setLongFileMode(TarOutputStream.LONGFILE_ERROR);
266 } else {
267 // warn or GNU
268 tOut.setLongFileMode(TarOutputStream.LONGFILE_GNU);
269 }
270
271 longWarningGiven = false;
272 for (Enumeration e = filesets.elements();
273 e.hasMoreElements();) {
274 TarFileSet fs = (TarFileSet) e.nextElement();
275 String[] files = fs.getFiles(getProject());
276 if (files.length > 1 && fs.getFullpath().length() > 0) {
277 throw new BuildException("fullpath attribute may only "
278 + "be specified for "
279 + "filesets that specify a "
280 + "single file.");
281 }
282 for (int i = 0; i < files.length; i++) {
283 File f = new File(fs.getDir(getProject()), files[i]);
284 String name = files[i].replace(File.separatorChar, '/');
285 tarFile(f, tOut, name, fs);
286 }
287 }
288 } catch (IOException ioe) {
289 String msg = "Problem creating TAR: " + ioe.getMessage();
290 throw new BuildException(msg, ioe, getLocation());
291 } finally {
292 if (tOut != null) {
293 try {
294 // close up
295 tOut.close();
296 } catch (IOException e) {
297 // ignore
298 }
299 }
300 }
301 } finally {
302 filesets = savedFileSets;
303 }
304 }
305
306 /**
307 * tar a file
308 * @param file the file to tar
309 * @param tOut the output stream
310 * @param vPath the path name of the file to tar
311 * @param tarFileSet the fileset that the file came from.
312 * @throws IOException on error
313 */
314 protected void tarFile(File file, TarOutputStream tOut, String vPath,
315 TarFileSet tarFileSet)
316 throws IOException {
317 FileInputStream fIn = null;
318
319 String fullpath = tarFileSet.getFullpath();
320 if (fullpath.length() > 0) {
321 vPath = fullpath;
322 } else {
323 // don't add "" to the archive
324 if (vPath.length() <= 0) {
325 return;
326 }
327
328 if (file.isDirectory() && !vPath.endsWith("/")) {
329 vPath += "/";
330 }
331
332 String prefix = tarFileSet.getPrefix();
333 // '/' is appended for compatibility with the zip task.
334 if (prefix.length() > 0 && !prefix.endsWith("/")) {
335 prefix = prefix + "/";
336 }
337 vPath = prefix + vPath;
338 }
339
340 if (vPath.startsWith("/") && !tarFileSet.getPreserveLeadingSlashes()) {
341 int l = vPath.length();
342 if (l <= 1) {
343 // we would end up adding "" to the archive
344 return;
345 }
346 vPath = vPath.substring(1, l);
347 }
348
349 try {
350 if (vPath.length() >= TarConstants.NAMELEN) {
351 if (longFileMode.isOmitMode()) {
352 log("Omitting: " + vPath, Project.MSG_INFO);
353 return;
354 } else if (longFileMode.isWarnMode()) {
355 log("Entry: " + vPath + " longer than "
356 + TarConstants.NAMELEN + " characters.",
357 Project.MSG_WARN);
358 if (!longWarningGiven) {
359 log("Resulting tar file can only be processed "
360 + "successfully by GNU compatible tar commands",
361 Project.MSG_WARN);
362 longWarningGiven = true;
363 }
364 } else if (longFileMode.isFailMode()) {
365 throw new BuildException("Entry: " + vPath
366 + " longer than " + TarConstants.NAMELEN
367 + "characters.", getLocation());
368 }
369 }
370
371 TarEntry te = new TarEntry(vPath);
372 te.setModTime(file.lastModified());
373 if (!file.isDirectory()) {
374 te.setSize(file.length());
375 te.setMode(tarFileSet.getMode());
376 } else {
377 te.setMode(tarFileSet.getDirMode());
378 }
379 te.setUserName(tarFileSet.getUserName());
380 te.setGroupName(tarFileSet.getGroup());
381 te.setUserId(tarFileSet.getUid());
382 te.setGroupId(tarFileSet.getGid());
383
384 tOut.putNextEntry(te);
385
386 if (!file.isDirectory()) {
387 fIn = new FileInputStream(file);
388
389 byte[] buffer = new byte[8 * 1024];
390 int count = 0;
391 do {
392 tOut.write(buffer, 0, count);
393 count = fIn.read(buffer, 0, buffer.length);
394 } while (count != -1);
395 }
396
397 tOut.closeEntry();
398 } finally {
399 if (fIn != null) {
400 fIn.close();
401 }
402 }
403 }
404
405 /**
406 * Is the archive up to date in relationship to a list of files.
407 * @param files the files to check
408 * @return true if the archive is up to date.
409 * @deprecated use the two-arg version instead.
410 */
411 protected boolean archiveIsUpToDate(String[] files) {
412 return archiveIsUpToDate(files, baseDir);
413 }
414
415 /**
416 * Is the archive up to date in relationship to a list of files.
417 * @param files the files to check
418 * @param dir the base directory for the files.
419 * @return true if the archive is up to date.
420 * @since Ant 1.5.2
421 */
422 protected boolean archiveIsUpToDate(String[] files, File dir) {
423 SourceFileScanner sfs = new SourceFileScanner(this);
424 MergingMapper mm = new MergingMapper();
425 mm.setTo(tarFile.getAbsolutePath());
426 return sfs.restrict(files, dir, null, mm).length == 0;
427 }
428
429 /**
430 * This is a FileSet with the option to specify permissions
431 * and other attributes.
432 */
433 public static class TarFileSet extends FileSet {
434 private String[] files = null;
435
436 private int fileMode = UnixStat.FILE_FLAG | UnixStat.DEFAULT_FILE_PERM;
437 private int dirMode = UnixStat.DIR_FLAG | UnixStat.DEFAULT_DIR_PERM;
438
439 private String userName = "";
440 private String groupName = "";
441 private int uid;
442 private int gid;
443 private String prefix = "";
444 private String fullpath = "";
445 private boolean preserveLeadingSlashes = false;
446
447 /**
448 * Creates a new <code>TarFileSet</code> instance.
449 * Using a fileset as a constructor argument.
450 *
451 * @param fileset a <code>FileSet</code> value
452 */
453 public TarFileSet(FileSet fileset) {
454 super(fileset);
455 }
456
457 /**
458 * Creates a new <code>TarFileSet</code> instance.
459 *
460 */
461 public TarFileSet() {
462 super();
463 }
464
465 /**
466 * Get a list of files and directories specified in the fileset.
467 * @param p the current project.
468 * @return a list of file and directory names, relative to
469 * the baseDir for the project.
470 */
471 public String[] getFiles(Project p) {
472 if (files == null) {
473 DirectoryScanner ds = getDirectoryScanner(p);
474 String[] directories = ds.getIncludedDirectories();
475 String[] filesPerSe = ds.getIncludedFiles();
476 files = new String [directories.length + filesPerSe.length];
477 System.arraycopy(directories, 0, files, 0, directories.length);
478 System.arraycopy(filesPerSe, 0, files, directories.length,
479 filesPerSe.length);
480 }
481
482 return files;
483 }
484
485 /**
486 * A 3 digit octal string, specify the user, group and
487 * other modes in the standard Unix fashion;
488 * optional, default=0644
489 * @param octalString a 3 digit octal string.
490 */
491 public void setMode(String octalString) {
492 this.fileMode =
493 UnixStat.FILE_FLAG | Integer.parseInt(octalString, 8);
494 }
495
496 /**
497 * @return the current mode.
498 */
499 public int getMode() {
500 return fileMode;
501 }
502
503 /**
504 * A 3 digit octal string, specify the user, group and
505 * other modes in the standard Unix fashion;
506 * optional, default=0755
507 *
508 * @param octalString a 3 digit octal string.
509 * @since Ant 1.6
510 */
511 public void setDirMode(String octalString) {
512 this.dirMode =
513 UnixStat.DIR_FLAG | Integer.parseInt(octalString, 8);
514 }
515
516 /**
517 * @return the current directory mode
518 * @since Ant 1.6
519 */
520 public int getDirMode() {
521 return dirMode;
522 }
523
524 /**
525 * The username for the tar entry
526 * This is not the same as the UID.
527 * @param userName the user name for the tar entry.
528 */
529 public void setUserName(String userName) {
530 this.userName = userName;
531 }
532
533 /**
534 * @return the user name for the tar entry
535 */
536 public String getUserName() {
537 return userName;
538 }
539
540 /**
541 * The uid for the tar entry
542 * This is not the same as the User name.
543 * @param uid the id of the user for the tar entry.
544 */
545 public void setUid(int uid) {
546 this.uid = uid;
547 }
548
549 /**
550 * @return the uid for the tar entry
551 */
552 public int getUid() {
553 return uid;
554 }
555
556 /**
557 * The groupname for the tar entry; optional, default=""
558 * This is not the same as the GID.
559 * @param groupName the group name string.
560 */
561 public void setGroup(String groupName) {
562 this.groupName = groupName;
563 }
564
565 /**
566 * @return the group name string.
567 */
568 public String getGroup() {
569 return groupName;
570 }
571
572 /**
573 * The GID for the tar entry; optional, default="0"
574 * This is not the same as the group name.
575 * @param gid the group id.
576 */
577 public void setGid(int gid) {
578 this.gid = gid;
579 }
580
581 /**
582 * @return the group identifier.
583 */
584 public int getGid() {
585 return gid;
586 }
587
588 /**
589 * If the prefix attribute is set, all files in the fileset
590 * are prefixed with that path in the archive.
591 * optional.
592 * @param prefix the path prefix.
593 */
594 public void setPrefix(String prefix) {
595 this.prefix = prefix;
596 }
597
598 /**
599 * @return the path prefix for the files in the fileset.
600 */
601 public String getPrefix() {
602 return prefix;
603 }
604
605 /**
606 * If the fullpath attribute is set, the file in the fileset
607 * is written with that path in the archive. The prefix attribute,
608 * if specified, is ignored. It is an error to have more than one file specified in
609 * such a fileset.
610 * @param fullpath the path to use for the file in a fileset.
611 */
612 public void setFullpath(String fullpath) {
613 this.fullpath = fullpath;
614 }
615
616 /**
617 * @return the path to use for a single file fileset.
618 */
619 public String getFullpath() {
620 return fullpath;
621 }
622
623 /**
624 * Flag to indicates whether leading `/'s should
625 * be preserved in the file names.
626 * Optional, default is <code>false</code>.
627 * @param b the leading slashes flag.
628 */
629 public void setPreserveLeadingSlashes(boolean b) {
630 this.preserveLeadingSlashes = b;
631 }
632
633 /**
634 * @return the leading slashes flag.
635 */
636 public boolean getPreserveLeadingSlashes() {
637 return preserveLeadingSlashes;
638 }
639 }
640
641 /**
642 * Set of options for long file handling in the task.
643 *
644 */
645 public static class TarLongFileMode extends EnumeratedAttribute {
646
647 /** permissible values for longfile attribute */
648 public static final String
649 WARN = "warn",
650 FAIL = "fail",
651 TRUNCATE = "truncate",
652 GNU = "gnu",
653 OMIT = "omit";
654
655 private final String[] validModes = {WARN, FAIL, TRUNCATE, GNU, OMIT};
656
657 /** Constructor, defaults to "warn" */
658 public TarLongFileMode() {
659 super();
660 setValue(WARN);
661 }
662
663 /**
664 * @return the possible values for this enumerated type.
665 */
666 public String[] getValues() {
667 return validModes;
668 }
669
670 /**
671 * @return true if value is "truncate".
672 */
673 public boolean isTruncateMode() {
674 return TRUNCATE.equalsIgnoreCase(getValue());
675 }
676
677 /**
678 * @return true if value is "warn".
679 */
680 public boolean isWarnMode() {
681 return WARN.equalsIgnoreCase(getValue());
682 }
683
684 /**
685 * @return true if value is "gnu".
686 */
687 public boolean isGnuMode() {
688 return GNU.equalsIgnoreCase(getValue());
689 }
690
691 /**
692 * @return true if value is "fail".
693 */
694 public boolean isFailMode() {
695 return FAIL.equalsIgnoreCase(getValue());
696 }
697
698 /**
699 * @return true if value is "omit".
700 */
701 public boolean isOmitMode() {
702 return OMIT.equalsIgnoreCase(getValue());
703 }
704 }
705
706 /**
707 * Valid Modes for Compression attribute to Tar Task
708 *
709 */
710 public static final class TarCompressionMethod extends EnumeratedAttribute {
711
712 // permissible values for compression attribute
713 /**
714 * No compression
715 */
716 private static final String NONE = "none";
717 /**
718 * GZIP compression
719 */
720 private static final String GZIP = "gzip";
721 /**
722 * BZIP2 compression
723 */
724 private static final String BZIP2 = "bzip2";
725
726
727 /**
728 * Default constructor
729 */
730 public TarCompressionMethod() {
731 super();
732 setValue(NONE);
733 }
734
735 /**
736 * Get valid enumeration values.
737 * @return valid enumeration values
738 */
739 public String[] getValues() {
740 return new String[] {NONE, GZIP, BZIP2 };
741 }
742
743 /**
744 * This method wraps the output stream with the
745 * corresponding compression method
746 *
747 * @param ostream output stream
748 * @return output stream with on-the-fly compression
749 * @exception IOException thrown if file is not writable
750 */
751 private OutputStream compress(final OutputStream ostream)
752 throws IOException {
753 final String value = getValue();
754 if (GZIP.equals(value)) {
755 return new GZIPOutputStream(ostream);
756 } else {
757 if (BZIP2.equals(value)) {
758 ostream.write('B');
759 ostream.write('Z');
760 return new CBZip2OutputStream(ostream);
761 }
762 }
763 return ostream;
764 }
765 }
766}
Note: See TracBrowser for help on using the repository browser.