source: other-projects/trunk/gs3-release-maker/apache-ant-1.6.5/src/main/org/apache/tools/ant/taskdefs/ExecuteOn.java@ 14627

Last change on this file since 14627 was 14627, checked in by oranfry, 17 years ago

initial import of the gs3-release-maker

File size: 26.7 KB
Line 
1/*
2 * Copyright 2000-2005 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.File;
21import java.io.IOException;
22import java.util.Hashtable;
23import java.util.Vector;
24import org.apache.tools.ant.BuildException;
25import org.apache.tools.ant.DirectoryScanner;
26import org.apache.tools.ant.Project;
27import org.apache.tools.ant.types.Commandline;
28import org.apache.tools.ant.types.AbstractFileSet;
29import org.apache.tools.ant.types.DirSet;
30import org.apache.tools.ant.types.EnumeratedAttribute;
31import org.apache.tools.ant.types.FileList;
32import org.apache.tools.ant.types.FileSet;
33import org.apache.tools.ant.types.Mapper;
34import org.apache.tools.ant.util.FileNameMapper;
35import org.apache.tools.ant.util.SourceFileScanner;
36
37/**
38 * Executes a given command, supplying a set of files as arguments.
39 *
40 * @since Ant 1.2
41 *
42 * @ant.task category="control" name="apply"
43 */
44public class ExecuteOn extends ExecTask {
45
46 protected Vector filesets = new Vector(); // contains AbstractFileSet
47 // (both DirSet and FileSet)
48 private Vector filelists = new Vector();
49 private boolean relative = false;
50 private boolean parallel = false;
51 private boolean forwardSlash = false;
52 protected String type = "file";
53 protected Commandline.Marker srcFilePos = null;
54 private boolean skipEmpty = false;
55 protected Commandline.Marker targetFilePos = null;
56 protected Mapper mapperElement = null;
57 protected FileNameMapper mapper = null;
58 protected File destDir = null;
59 private int maxParallel = -1;
60 private boolean addSourceFile = true;
61 private boolean verbose = false;
62 private boolean ignoreMissing = true;
63 private boolean force = false;
64
65 /**
66 * Has <srcfile> been specified before <targetfile>
67 */
68 protected boolean srcIsFirst = true;
69
70 /**
71 * Add a set of files upon which to operate.
72 * @param set the FileSet to add.
73 */
74 public void addFileset(FileSet set) {
75 filesets.addElement(set);
76 }
77
78 /**
79 * Add a set of directories upon which to operate.
80 *
81 * @param set the DirSet to add.
82 *
83 * @since Ant 1.6
84 */
85 public void addDirset(DirSet set) {
86 filesets.addElement(set);
87 }
88
89 /**
90 * Add a list of source files upon which to operate.
91 * @param list the FileList to add.
92 */
93 public void addFilelist(FileList list) {
94 filelists.addElement(list);
95 }
96
97 /**
98 * Set whether the filenames should be passed on the command line as
99 * absolute or relative pathnames. Paths are relative to the base
100 * directory of the corresponding fileset for source files or the
101 * dest attribute for target files.
102 * @param relative whether to pass relative pathnames.
103 */
104 public void setRelative(boolean relative) {
105 this.relative = relative;
106 }
107
108
109 /**
110 * Set whether to execute in parallel mode.
111 * If true, run the command only once, appending all files as arguments.
112 * If false, command will be executed once for every file. Defaults to false.
113 * @param parallel whether to run in parallel.
114 */
115 public void setParallel(boolean parallel) {
116 this.parallel = parallel;
117 }
118
119 /**
120 * Set whether the command works only on files, directories or both.
121 * @param type a FileDirBoth EnumeratedAttribute.
122 */
123 public void setType(FileDirBoth type) {
124 this.type = type.getValue();
125 }
126
127 /**
128 * Set whether empty filesets will be skipped. If true and
129 * no source files have been found or are newer than their
130 * corresponding target files, the command will not be run.
131 * @param skip whether to skip empty filesets.
132 */
133 public void setSkipEmptyFilesets(boolean skip) {
134 skipEmpty = skip;
135 }
136
137 /**
138 * Specify the directory where target files are to be placed.
139 * @param destDir the File object representing the destination directory.
140 */
141 public void setDest(File destDir) {
142 this.destDir = destDir;
143 }
144
145 /**
146 * Set whether the source and target file names on Windows and OS/2
147 * must use the forward slash as file separator.
148 * @param forwardSlash whether the forward slash will be forced.
149 */
150 public void setForwardslash(boolean forwardSlash) {
151 this.forwardSlash = forwardSlash;
152 }
153
154 /**
155 * Limit the command line length by passing at maximum this many
156 * sourcefiles at once to the command.
157 *
158 * <p>Set to &lt;= 0 for unlimited - this is the default.</p>
159 *
160 * @param max <code>int</code> maximum number of sourcefiles
161 * passed to the executable.
162 *
163 * @since Ant 1.6
164 */
165 public void setMaxParallel(int max) {
166 maxParallel = max;
167 }
168
169 /**
170 * Set whether to send the source file name on the command line.
171 *
172 * <p>Defaults to <code>true</code>.
173 *
174 * @param b whether to add the source file to the command line.
175 *
176 * @since Ant 1.6
177 */
178 public void setAddsourcefile(boolean b) {
179 addSourceFile = b;
180 }
181
182 /**
183 * Set whether to operate in verbose mode.
184 * If true, a verbose summary will be printed after execution.
185 * @param b whether to operate in verbose mode.
186 *
187 * @since Ant 1.6
188 */
189 public void setVerbose(boolean b) {
190 verbose = b;
191 }
192
193 /**
194 * Set whether to ignore nonexistent files from filelists.
195 * @param b whether to ignore missing files.
196 *
197 * @since Ant 1.6.2
198 */
199 public void setIgnoremissing(boolean b) {
200 ignoreMissing = b;
201 }
202
203 /**
204 * Set whether to bypass timestamp comparisons for target files.
205 * @param b whether to bypass timestamp comparisons.
206 *
207 * @since Ant 1.6.3
208 */
209 public void setForce(boolean b) {
210 force = b;
211 }
212
213 /**
214 * Create a placeholder indicating where on the command line
215 * the name of the source file should be inserted.
216 * @return <code>Commandline.Marker</code>.
217 */
218 public Commandline.Marker createSrcfile() {
219 if (srcFilePos != null) {
220 throw new BuildException(getTaskType() + " doesn\'t support multiple "
221 + "srcfile elements.", getLocation());
222 }
223 srcFilePos = cmdl.createMarker();
224 return srcFilePos;
225 }
226
227 /**
228 * Create a placeholder indicating where on the command line
229 * the name of the target file should be inserted.
230 * @return <code>Commandline.Marker</code>.
231 */
232 public Commandline.Marker createTargetfile() {
233 if (targetFilePos != null) {
234 throw new BuildException(getTaskType() + " doesn\'t support multiple "
235 + "targetfile elements.", getLocation());
236 }
237 targetFilePos = cmdl.createMarker();
238 srcIsFirst = (srcFilePos != null);
239 return targetFilePos;
240 }
241
242 /**
243 * Create a nested Mapper element to use for mapping
244 * source files to target files.
245 * @return <code>Mapper</code>.
246 * @throws BuildException if more than one mapper is defined.
247 */
248 public Mapper createMapper() throws BuildException {
249 if (mapperElement != null) {
250 throw new BuildException("Cannot define more than one mapper",
251 getLocation());
252 }
253 mapperElement = new Mapper(getProject());
254 return mapperElement;
255 }
256
257 /**
258 * Add a nested FileNameMapper.
259 * @param fileNameMapper the mapper to add.
260 * @since Ant 1.6.3
261 */
262 public void add(FileNameMapper fileNameMapper) {
263 createMapper().add(fileNameMapper);
264 }
265
266 /**
267 * Check the configuration of this ExecuteOn instance.
268 */
269 protected void checkConfiguration() {
270// * @TODO using taskName here is brittle, as a user could override it.
271// * this should probably be modified to use the classname instead.
272 if ("execon".equals(getTaskName())) {
273 log("!! execon is deprecated. Use apply instead. !!");
274 }
275 super.checkConfiguration();
276 if (filesets.size() == 0 && filelists.size() == 0) {
277 throw new BuildException("no filesets and no filelists specified",
278 getLocation());
279 }
280 if (targetFilePos != null && mapperElement == null) {
281 throw new BuildException("targetfile specified without mapper",
282 getLocation());
283 }
284 if (destDir != null && mapperElement == null) {
285 throw new BuildException("dest specified without mapper",
286 getLocation());
287 }
288 if (mapperElement != null) {
289 mapper = mapperElement.getImplementation();
290 }
291 }
292
293 /**
294 * Create the ExecuteStreamHandler instance that will be used
295 * during execution.
296 * @return <code>ExecuteStreamHandler</code>.
297 * @throws BuildException on error.
298 */
299 protected ExecuteStreamHandler createHandler() throws BuildException {
300 //if we have a RedirectorElement, return a decoy
301 return (redirectorElement == null)
302 ? super.createHandler() : new PumpStreamHandler();
303 }
304
305 /**
306 * Set up the I/O Redirector.
307 */
308 protected void setupRedirector() {
309 super.setupRedirector();
310 redirector.setAppendProperties(true);
311 }
312
313 /**
314 * Run the specified Execute object.
315 * @param exe the Execute instance representing the external process.
316 * @throws BuildException on error
317 */
318 protected void runExec(Execute exe) throws BuildException {
319 int totalFiles = 0;
320 int totalDirs = 0;
321 boolean haveExecuted = false;
322 try {
323 Vector fileNames = new Vector();
324 Vector baseDirs = new Vector();
325 for (int i = 0; i < filesets.size(); i++) {
326 String currentType = type;
327 AbstractFileSet fs = (AbstractFileSet) filesets.elementAt(i);
328 if (fs instanceof DirSet) {
329 if (!"dir".equals(type)) {
330 log("Found a nested dirset but type is " + type + ". "
331 + "Temporarily switching to type=\"dir\" on the"
332 + " assumption that you really did mean"
333 + " <dirset> not <fileset>.", Project.MSG_DEBUG);
334 currentType = "dir";
335 }
336 }
337 File base = fs.getDir(getProject());
338
339 DirectoryScanner ds = fs.getDirectoryScanner(getProject());
340
341 if (!"dir".equals(currentType)) {
342 String[] s = getFiles(base, ds);
343 for (int j = 0; j < s.length; j++) {
344 totalFiles++;
345 fileNames.addElement(s[j]);
346 baseDirs.addElement(base);
347 }
348 }
349 if (!"file".equals(currentType)) {
350 String[] s = getDirs(base, ds);
351 for (int j = 0; j < s.length; j++) {
352 totalDirs++;
353 fileNames.addElement(s[j]);
354 baseDirs.addElement(base);
355 }
356 }
357 if (fileNames.size() == 0 && skipEmpty) {
358 int includedCount
359 = ((!"dir".equals(currentType))
360 ? ds.getIncludedFilesCount() : 0)
361 + ((!"file".equals(currentType))
362 ? ds.getIncludedDirsCount() : 0);
363
364 log("Skipping fileset for directory " + base + ". It is "
365 + ((includedCount > 0) ? "up to date." : "empty."),
366 Project.MSG_INFO);
367 continue;
368 }
369 if (!parallel) {
370 String[] s = new String[fileNames.size()];
371 fileNames.copyInto(s);
372 for (int j = 0; j < s.length; j++) {
373 String[] command = getCommandline(s[j], base);
374 log(Commandline.describeCommand(command),
375 Project.MSG_VERBOSE);
376 exe.setCommandline(command);
377
378 if (redirectorElement != null) {
379 setupRedirector();
380 redirectorElement.configure(redirector, s[j]);
381 }
382 if (redirectorElement != null || haveExecuted) {
383 // need to reset the stream handler to restart
384 // reading of pipes;
385 // go ahead and do it always w/ nested redirectors
386 exe.setStreamHandler(redirector.createHandler());
387 }
388 runExecute(exe);
389 haveExecuted = true;
390 }
391 fileNames.removeAllElements();
392 baseDirs.removeAllElements();
393 }
394 }
395 for (int i = 0; i < filelists.size(); i++) {
396 FileList list = (FileList) filelists.elementAt(i);
397 File base = list.getDir(getProject());
398 String[] names = getFilesAndDirs(list);
399
400 for (int j = 0; j < names.length; j++) {
401 File f = new File(base, names[j]);
402 if ((!ignoreMissing) || (f.isFile() && !"dir".equals(type))
403 || (f.isDirectory() && !"file".equals(type))) {
404
405 if (ignoreMissing || f.isFile()) {
406 totalFiles++;
407 } else {
408 totalDirs++;
409 }
410 fileNames.addElement(names[j]);
411 baseDirs.addElement(base);
412 }
413 }
414 if (fileNames.size() == 0 && skipEmpty) {
415 DirectoryScanner ds = new DirectoryScanner();
416 ds.setBasedir(base);
417 ds.setIncludes(list.getFiles(getProject()));
418 ds.scan();
419 int includedCount
420 = ds.getIncludedFilesCount() + ds.getIncludedDirsCount();
421
422 log("Skipping filelist for directory " + base + ". It is "
423 + ((includedCount > 0) ? "up to date." : "empty."),
424 Project.MSG_INFO);
425 continue;
426 }
427 if (!parallel) {
428 String[] s = new String[fileNames.size()];
429 fileNames.copyInto(s);
430 for (int j = 0; j < s.length; j++) {
431 String[] command = getCommandline(s[j], base);
432 log(Commandline.describeCommand(command),
433 Project.MSG_VERBOSE);
434 exe.setCommandline(command);
435
436 if (redirectorElement != null) {
437 setupRedirector();
438 redirectorElement.configure(redirector, s[j]);
439 }
440 if (redirectorElement != null || haveExecuted) {
441 // need to reset the stream handler to restart
442 // reading of pipes;
443 // go ahead and do it always w/ nested redirectors
444 exe.setStreamHandler(redirector.createHandler());
445 }
446 runExecute(exe);
447 haveExecuted = true;
448 }
449 fileNames.removeAllElements();
450 baseDirs.removeAllElements();
451 }
452 }
453 if (parallel && (fileNames.size() > 0 || !skipEmpty)) {
454 runParallel(exe, fileNames, baseDirs);
455 haveExecuted = true;
456 }
457 if (haveExecuted) {
458 log("Applied " + cmdl.getExecutable() + " to "
459 + totalFiles + " file"
460 + (totalFiles != 1 ? "s" : "") + " and "
461 + totalDirs + " director"
462 + (totalDirs != 1 ? "ies" : "y") + ".",
463 verbose ? Project.MSG_INFO : Project.MSG_VERBOSE);
464 }
465 } catch (IOException e) {
466 throw new BuildException("Execute failed: " + e, e, getLocation());
467 } finally {
468 // close the output file if required
469 logFlush();
470 redirector.setAppendProperties(false);
471 redirector.setProperties();
472 }
473 }
474
475 /**
476 * Construct the command line for parallel execution.
477 *
478 * @param srcFiles The filenames to add to the commandline.
479 * @param baseDirs filenames are relative to this dir.
480 * @return the command line in the form of a String[].
481 */
482 protected String[] getCommandline(String[] srcFiles, File[] baseDirs) {
483 final char fileSeparator = File.separatorChar;
484 Vector targets = new Vector();
485 if (targetFilePos != null) {
486 Hashtable addedFiles = new Hashtable();
487 for (int i = 0; i < srcFiles.length; i++) {
488 String[] subTargets = mapper.mapFileName(srcFiles[i]);
489 if (subTargets != null) {
490 for (int j = 0; j < subTargets.length; j++) {
491 String name = null;
492 if (!relative) {
493 name = (new File(destDir, subTargets[j])).getAbsolutePath();
494 } else {
495 name = subTargets[j];
496 }
497 if (forwardSlash && fileSeparator != '/') {
498 name = name.replace(fileSeparator, '/');
499 }
500 if (!addedFiles.contains(name)) {
501 targets.addElement(name);
502 addedFiles.put(name, name);
503 }
504 }
505 }
506 }
507 }
508 String[] targetFiles = new String[targets.size()];
509 targets.copyInto(targetFiles);
510
511 if (!addSourceFile) {
512 srcFiles = new String[0];
513 }
514 String[] orig = cmdl.getCommandline();
515 String[] result
516 = new String[orig.length + srcFiles.length + targetFiles.length];
517
518 int srcIndex = orig.length;
519 if (srcFilePos != null) {
520 srcIndex = srcFilePos.getPosition();
521 }
522 if (targetFilePos != null) {
523 int targetIndex = targetFilePos.getPosition();
524
525 if (srcIndex < targetIndex
526 || (srcIndex == targetIndex && srcIsFirst)) {
527
528 // 0 --> srcIndex
529 System.arraycopy(orig, 0, result, 0, srcIndex);
530
531 // srcIndex --> targetIndex
532 System.arraycopy(orig, srcIndex, result,
533 srcIndex + srcFiles.length,
534 targetIndex - srcIndex);
535
536 // targets are already absolute file names
537 System.arraycopy(targetFiles, 0, result,
538 targetIndex + srcFiles.length,
539 targetFiles.length);
540
541 // targetIndex --> end
542 System.arraycopy(orig, targetIndex, result,
543 targetIndex + srcFiles.length + targetFiles.length,
544 orig.length - targetIndex);
545 } else {
546 // 0 --> targetIndex
547 System.arraycopy(orig, 0, result, 0, targetIndex);
548
549 // targets are already absolute file names
550 System.arraycopy(targetFiles, 0, result,
551 targetIndex,
552 targetFiles.length);
553
554 // targetIndex --> srcIndex
555 System.arraycopy(orig, targetIndex, result,
556 targetIndex + targetFiles.length,
557 srcIndex - targetIndex);
558
559 // srcIndex --> end
560 System.arraycopy(orig, srcIndex, result,
561 srcIndex + srcFiles.length + targetFiles.length,
562 orig.length - srcIndex);
563 srcIndex += targetFiles.length;
564 }
565
566 } else { // no targetFilePos
567
568 // 0 --> srcIndex
569 System.arraycopy(orig, 0, result, 0, srcIndex);
570 // srcIndex --> end
571 System.arraycopy(orig, srcIndex, result,
572 srcIndex + srcFiles.length,
573 orig.length - srcIndex);
574 }
575 // fill in source file names
576 for (int i = 0; i < srcFiles.length; i++) {
577 if (!relative) {
578 result[srcIndex + i] =
579 (new File(baseDirs[i], srcFiles[i])).getAbsolutePath();
580 } else {
581 result[srcIndex + i] = srcFiles[i];
582 }
583 if (forwardSlash && fileSeparator != '/') {
584 result[srcIndex + i] =
585 result[srcIndex + i].replace(fileSeparator, '/');
586 }
587 }
588 return result;
589 }
590
591 /**
592 * Construct the command line for serial execution.
593 *
594 * @param srcFile The filename to add to the commandline.
595 * @param baseDir filename is relative to this dir.
596 * @return the command line in the form of a String[].
597 */
598 protected String[] getCommandline(String srcFile, File baseDir) {
599 return getCommandline(new String[] {srcFile}, new File[] {baseDir});
600 }
601
602 /**
603 * Return the list of files from this DirectoryScanner that should
604 * be included on the command line.
605 * @param baseDir the File base directory.
606 * @param ds the DirectoryScanner to use for file scanning.
607 * @return a String[] containing the filenames.
608 */
609 protected String[] getFiles(File baseDir, DirectoryScanner ds) {
610 return restrict(ds.getIncludedFiles(), baseDir);
611 }
612
613 /**
614 * Return the list of Directories from this DirectoryScanner that
615 * should be included on the command line.
616 * @param baseDir the File base directory.
617 * @param ds the DirectoryScanner to use for file scanning.
618 * @return a String[] containing the directory names.
619 */
620 protected String[] getDirs(File baseDir, DirectoryScanner ds) {
621 return restrict(ds.getIncludedDirectories(), baseDir);
622 }
623
624 /**
625 * Return the list of files or directories from this FileList that
626 * should be included on the command line.
627 * @param list the FileList to check.
628 * @return a String[] containing the directory names.
629 *
630 * @since Ant 1.6.2
631 */
632 protected String[] getFilesAndDirs(FileList list) {
633 return restrict(list.getFiles(getProject()), list.getDir(getProject()));
634 }
635
636 private String[] restrict(String[] s, File baseDir) {
637 return (mapper == null || force) ? s
638 : new SourceFileScanner(this).restrict(s, baseDir, destDir, mapper);
639 }
640
641 /**
642 * Run the command in "parallel" mode, making sure that at most
643 * maxParallel sourcefiles get passed on the command line.
644 * @param exe the Executable to use.
645 * @param fileNames the Vector of filenames.
646 * @param baseDirs the Vector of base directories corresponding to fileNames.
647 * @throws IOException on I/O errors.
648 * @throws BuildException on other errors.
649 * @since Ant 1.6
650 */
651 protected void runParallel(Execute exe, Vector fileNames,
652 Vector baseDirs)
653 throws IOException, BuildException {
654 String[] s = new String[fileNames.size()];
655 fileNames.copyInto(s);
656 File[] b = new File[baseDirs.size()];
657 baseDirs.copyInto(b);
658
659 if (maxParallel <= 0
660 || s.length == 0 /* this is skipEmpty == false */) {
661 String[] command = getCommandline(s, b);
662 log(Commandline.describeCommand(command), Project.MSG_VERBOSE);
663 exe.setCommandline(command);
664 runExecute(exe);
665 } else {
666 int stillToDo = fileNames.size();
667 int currentOffset = 0;
668 while (stillToDo > 0) {
669 int currentAmount = Math.min(stillToDo, maxParallel);
670 String[] cs = new String[currentAmount];
671 System.arraycopy(s, currentOffset, cs, 0, currentAmount);
672 File[] cb = new File[currentAmount];
673 System.arraycopy(b, currentOffset, cb, 0, currentAmount);
674 String[] command = getCommandline(cs, cb);
675 log(Commandline.describeCommand(command), Project.MSG_VERBOSE);
676 exe.setCommandline(command);
677 if (redirectorElement != null) {
678 setupRedirector();
679 redirectorElement.configure(redirector, null);
680 }
681 if (redirectorElement != null || currentOffset > 0) {
682 // need to reset the stream handler to restart
683 // reading of pipes;
684 // go ahead and do it always w/ nested redirectors
685 exe.setStreamHandler(redirector.createHandler());
686 }
687 runExecute(exe);
688
689 stillToDo -= currentAmount;
690 currentOffset += currentAmount;
691 }
692 }
693 }
694
695 /**
696 * Enumerated attribute with the values "file", "dir" and "both"
697 * for the type attribute.
698 */
699 public static class FileDirBoth extends EnumeratedAttribute {
700 /**
701 * @see EnumeratedAttribute#getValues
702 */
703 public String[] getValues() {
704 return new String[] {"file", "dir", "both"};
705 }
706 }
707}
Note: See TracBrowser for help on using the repository browser.