source: other-projects/trunk/gs3-release-maker/apache-ant-1.6.5/src/main/org/apache/tools/ant/taskdefs/ExecTask.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: 21.2 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.Enumeration;
23import java.util.Vector;
24import org.apache.tools.ant.BuildException;
25import org.apache.tools.ant.Project;
26import org.apache.tools.ant.Task;
27import org.apache.tools.ant.types.Commandline;
28import org.apache.tools.ant.types.Environment;
29import org.apache.tools.ant.types.Path;
30import org.apache.tools.ant.types.RedirectorElement;
31import org.apache.tools.ant.util.FileUtils;
32
33/**
34 * Executes a given command if the os platform is appropriate.
35 *
36 * @since Ant 1.2
37 *
38 * @ant.task category="control"
39 */
40public class ExecTask extends Task {
41
42 private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
43
44 private String os;
45
46 private File dir;
47 protected boolean failOnError = false;
48 protected boolean newEnvironment = false;
49 private Long timeout = null;
50 private Environment env = new Environment();
51 protected Commandline cmdl = new Commandline();
52 private String resultProperty;
53 private boolean failIfExecFails = true;
54 private String executable;
55 private boolean resolveExecutable = false;
56 private boolean searchPath = false;
57 private boolean spawn = false;
58 private boolean incompatibleWithSpawn = false;
59
60 //include locally for screening purposes
61 private String inputString;
62 private File input;
63 private File output;
64 private File error;
65
66 protected Redirector redirector = new Redirector(this);
67 protected RedirectorElement redirectorElement;
68
69 /**
70 * Controls whether the VM (1.3 and above) is used to execute the
71 * command
72 */
73 private boolean vmLauncher = true;
74
75 /**
76 * Set whether or not you want the process to be spawned.
77 * Default is false.
78 * @param spawn if true you do not want Ant to wait for the end of the process.
79 * @since Ant 1.6
80 */
81 public void setSpawn(boolean spawn) {
82 this.spawn = spawn;
83 }
84
85 /**
86 * Set the timeout in milliseconds after which the process will be killed.
87 *
88 * @param value timeout in milliseconds.
89 *
90 * @since Ant 1.5
91 */
92 public void setTimeout(Long value) {
93 timeout = value;
94 incompatibleWithSpawn = true;
95 }
96
97 /**
98 * Set the timeout in milliseconds after which the process will be killed.
99 *
100 * @param value timeout in milliseconds.
101 */
102 public void setTimeout(Integer value) {
103 setTimeout(
104 (Long) ((value == null) ? null : new Long(value.intValue())));
105 }
106
107 /**
108 * Set the name of the executable program.
109 * @param value the name of the executable program.
110 */
111 public void setExecutable(String value) {
112 this.executable = value;
113 cmdl.setExecutable(value);
114 }
115
116 /**
117 * Set the working directory of the process.
118 * @param d the working directory of the process.
119 */
120 public void setDir(File d) {
121 this.dir = d;
122 }
123
124 /**
125 * List of operating systems on which the command may be executed.
126 * @param os list of operating systems on which the command may be executed.
127 */
128 public void setOs(String os) {
129 this.os = os;
130 }
131
132 /**
133 * Sets a command line.
134 * @param cmdl command line.
135 * @ant.attribute ignore="true"
136 */
137 public void setCommand(Commandline cmdl) {
138 log("The command attribute is deprecated. "
139 + "Please use the executable attribute and nested arg elements.",
140 Project.MSG_WARN);
141 this.cmdl = cmdl;
142 }
143
144 /**
145 * File the output of the process is redirected to. If error is not
146 * redirected, it too will appear in the output.
147 *
148 * @param out name of a file to which output should be sent.
149 */
150 public void setOutput(File out) {
151 this.output = out;
152 incompatibleWithSpawn = true;
153 }
154
155 /**
156 * Set the input file to use for the task.
157 *
158 * @param input name of a file from which to get input.
159 */
160 public void setInput(File input) {
161 if (inputString != null) {
162 throw new BuildException("The \"input\" and \"inputstring\" "
163 + "attributes cannot both be specified");
164 }
165 this.input = input;
166 incompatibleWithSpawn = true;
167 }
168
169 /**
170 * Set the string to use as input.
171 *
172 * @param inputString the string which is used as the input source.
173 */
174 public void setInputString(String inputString) {
175 if (input != null) {
176 throw new BuildException("The \"input\" and \"inputstring\" "
177 + "attributes cannot both be specified");
178 }
179 this.inputString = inputString;
180 incompatibleWithSpawn = true;
181 }
182
183 /**
184 * Controls whether error output of exec is logged. This is only useful when
185 * output is being redirected and error output is desired in the Ant log.
186 *
187 * @param logError set to true to log error output in the normal ant log.
188 */
189 public void setLogError(boolean logError) {
190 redirector.setLogError(logError);
191 incompatibleWithSpawn |= logError;
192 }
193
194 /**
195 * Set the File to which the error stream of the process should be redirected.
196 *
197 * @param error a file to which stderr should be sent.
198 *
199 * @since Ant 1.6
200 */
201 public void setError(File error) {
202 this.error = error;
203 incompatibleWithSpawn = true;
204 }
205
206 /**
207 * Sets the property name whose value should be set to the output of
208 * the process.
209 *
210 * @param outputProp name of property.
211 */
212 public void setOutputproperty(String outputProp) {
213 redirector.setOutputProperty(outputProp);
214 incompatibleWithSpawn = true;
215 }
216
217 /**
218 * Sets the name of the property whose value should be set to the error of
219 * the process.
220 *
221 * @param errorProperty name of property.
222 *
223 * @since Ant 1.6
224 */
225 public void setErrorProperty(String errorProperty) {
226 redirector.setErrorProperty(errorProperty);
227 incompatibleWithSpawn = true;
228 }
229
230 /**
231 * Fail if the command exits with a non-zero return code.
232 *
233 * @param fail if true fail the command on non-zero return code.
234 */
235 public void setFailonerror(boolean fail) {
236 failOnError = fail;
237 incompatibleWithSpawn |= fail;
238 }
239
240 /**
241 * Do not propagate old environment when new environment variables are specified.
242 *
243 * @param newenv if true, do not propagate old environment
244 * when new environment variables are specified.
245 */
246 public void setNewenvironment(boolean newenv) {
247 newEnvironment = newenv;
248 }
249
250 /**
251 * Set whether to attempt to resolve the executable to a file.
252 *
253 * @param resolveExecutable if true, attempt to resolve the
254 * path of the executable.
255 */
256 public void setResolveExecutable(boolean resolveExecutable) {
257 this.resolveExecutable = resolveExecutable;
258 }
259
260 /**
261 * Set whether to search nested, then
262 * system PATH environment variables for the executable.
263 *
264 * @param searchPath if true, search PATHs.
265 */
266 public void setSearchPath(boolean searchPath) {
267 this.searchPath = searchPath;
268 }
269
270 /**
271 * Indicates whether to attempt to resolve the executable to a
272 * file.
273 *
274 * @since Ant 1.6
275 */
276 public boolean getResolveExecutable() {
277 return resolveExecutable;
278 }
279
280 /**
281 * Add an environment variable to the launched process.
282 *
283 * @param var new environment variable.
284 */
285 public void addEnv(Environment.Variable var) {
286 env.addVariable(var);
287 }
288
289 /**
290 * Adds a command-line argument.
291 *
292 * @return new command line argument created.
293 */
294 public Commandline.Argument createArg() {
295 return cmdl.createArgument();
296 }
297
298 /**
299 * Sets the name of a property in which the return code of the
300 * command should be stored. Only of interest if failonerror=false.
301 *
302 * @since Ant 1.5
303 *
304 * @param resultProperty name of property.
305 */
306 public void setResultProperty(String resultProperty) {
307 this.resultProperty = resultProperty;
308 incompatibleWithSpawn = true;
309 }
310
311 /**
312 * Helper method to set result property to the
313 * passed in value if appropriate.
314 *
315 * @param result value desired for the result property value.
316 */
317 protected void maybeSetResultPropertyValue(int result) {
318 if (resultProperty != null) {
319 String res = Integer.toString(result);
320 getProject().setNewProperty(resultProperty, res);
321 }
322 }
323
324 /**
325 * Set whether to stop the build if program cannot be started.
326 * Defaults to true.
327 *
328 * @param flag stop the build if program cannot be started.
329 *
330 * @since Ant 1.5
331 */
332 public void setFailIfExecutionFails(boolean flag) {
333 failIfExecFails = flag;
334 incompatibleWithSpawn = true;
335 }
336
337 /**
338 * Set whether output should be appended to or overwrite an existing file.
339 * Defaults to false.
340 *
341 * @param append if true append is desired.
342 *
343 * @since 1.30, Ant 1.5
344 */
345 public void setAppend(boolean append) {
346 redirector.setAppend(append);
347 incompatibleWithSpawn = true;
348 }
349
350 /**
351 * Add a <code>RedirectorElement</code> to this task.
352 *
353 * @param redirectorElement <code>RedirectorElement</code>.
354 * @since Ant 1.6.2
355 */
356 public void addConfiguredRedirector(RedirectorElement redirectorElement) {
357 if (this.redirectorElement != null) {
358 throw new BuildException("cannot have > 1 nested <redirector>s");
359 }
360 this.redirectorElement = redirectorElement;
361 incompatibleWithSpawn = true;
362 }
363
364
365 /**
366 * The method attempts to figure out where the executable is so that we can feed
367 * the full path. We first try basedir, then the exec dir, and then
368 * fallback to the straight executable name (i.e. on the path).
369 *
370 * @param exec the name of the executable.
371 * @param mustSearchPath if true, the executable will be looked up in
372 * the PATH environment and the absolute path is returned.
373 *
374 * @return the executable as a full path if it can be determined.
375 *
376 * @since Ant 1.6
377 */
378 protected String resolveExecutable(String exec, boolean mustSearchPath) {
379 if (!resolveExecutable) {
380 return exec;
381 }
382 // try to find the executable
383 File executableFile = getProject().resolveFile(exec);
384 if (executableFile.exists()) {
385 return executableFile.getAbsolutePath();
386 }
387 // now try to resolve against the dir if given
388 if (dir != null) {
389 executableFile = FILE_UTILS.resolveFile(dir, exec);
390 if (executableFile.exists()) {
391 return executableFile.getAbsolutePath();
392 }
393 }
394 // couldn't find it - must be on path
395 if (mustSearchPath) {
396 Path p = null;
397 String[] environment = env.getVariables();
398 if (environment != null) {
399 for (int i = 0; i < environment.length; i++) {
400 if (isPath(environment[i])) {
401 p = new Path(getProject(), environment[i].substring(5));
402 break;
403 }
404 }
405 }
406 if (p == null) {
407 Vector envVars = Execute.getProcEnvironment();
408 Enumeration e = envVars.elements();
409 while (e.hasMoreElements()) {
410 String line = (String) e.nextElement();
411 if (isPath(line)) {
412 p = new Path(getProject(), line.substring(5));
413 break;
414 }
415 }
416 }
417 if (p != null) {
418 String[] dirs = p.list();
419 for (int i = 0; i < dirs.length; i++) {
420 executableFile
421 = FILE_UTILS.resolveFile(new File(dirs[i]), exec);
422 if (executableFile.exists()) {
423 return executableFile.getAbsolutePath();
424 }
425 }
426 }
427 }
428 // mustSearchPath is false, or no PATH or not found - keep our
429 // fingers crossed.
430 return exec;
431 }
432
433 /**
434 * Do the work.
435 *
436 * @throws BuildException in a number of circumstances:
437 * <ul>
438 * <li>if failIfExecFails is set to true and the process cannot be started</li>
439 * <li>the java13command launcher can send build exceptions</li>
440 * <li>this list is not exhaustive or limitative</li>
441 * </ul>
442 */
443 public void execute() throws BuildException {
444 // Quick fail if this is not a valid OS for the command
445 if (!isValidOs()) {
446 return;
447 }
448 File savedDir = dir; // possibly altered in prepareExec
449 cmdl.setExecutable(resolveExecutable(executable, searchPath));
450 checkConfiguration();
451 try {
452 runExec(prepareExec());
453 } finally {
454 dir = savedDir;
455 }
456 }
457
458 /**
459 * Has the user set all necessary attributes?
460 * @throws BuildException if there are missing required parameters.
461 */
462 protected void checkConfiguration() throws BuildException {
463 if (cmdl.getExecutable() == null) {
464 throw new BuildException("no executable specified", getLocation());
465 }
466 if (dir != null && !dir.exists()) {
467 throw new BuildException("The directory you specified does not "
468 + "exist");
469 }
470 if (dir != null && !dir.isDirectory()) {
471 throw new BuildException("The directory you specified is not a "
472 + "directory");
473 }
474 if (spawn && incompatibleWithSpawn) {
475 getProject().log("spawn does not allow attributes related to input, "
476 + "output, error, result", Project.MSG_ERR);
477 getProject().log("spawn also does not allow timeout", Project.MSG_ERR);
478 getProject().log("finally, spawn is not compatible "
479 + "with a nested I/O <redirector>", Project.MSG_ERR);
480 throw new BuildException("You have used an attribute "
481 + "or nested element which is not compatible with spawn");
482 }
483 setupRedirector();
484 }
485
486 /**
487 * Set up properties on the redirector that we needed to store locally.
488 */
489 protected void setupRedirector() {
490 redirector.setInput(input);
491 redirector.setInputString(inputString);
492 redirector.setOutput(output);
493 redirector.setError(error);
494 }
495
496 /**
497 * Is this the OS the user wanted?
498 * @return boolean.
499 * <ul>
500 * <li>
501 * <code>true</code> if the os under which Ant is running is
502 * matches one os in the os attribute
503 * or if the os attribute is null</li>
504 * <li><code>false</code> otherwise.</li>
505 * </ul>
506 */
507 protected boolean isValidOs() {
508 // test if os match
509 String myos = System.getProperty("os.name");
510 log("Current OS is " + myos, Project.MSG_VERBOSE);
511 if ((os != null) && (os.indexOf(myos) < 0)) {
512 // this command will be executed only on the specified OS
513 log("This OS, " + myos
514 + " was not found in the specified list of valid OSes: " + os,
515 Project.MSG_VERBOSE);
516 return false;
517 }
518 return true;
519 }
520
521 /**
522 * Set whether to launch new process with VM, otherwise use the OS's shell.
523 * Default value is true.
524 * @param vmLauncher true if we want to launch new process with VM,
525 * false if we want to use the OS's shell.
526 */
527 public void setVMLauncher(boolean vmLauncher) {
528 this.vmLauncher = vmLauncher;
529 }
530
531 /**
532 * Create an Execute instance with the correct working directory set.
533 *
534 * @return an instance of the Execute class.
535 *
536 * @throws BuildException under unknown circumstances.
537 */
538 protected Execute prepareExec() throws BuildException {
539 // default directory to the project's base directory
540 if (dir == null) {
541 dir = getProject().getBaseDir();
542 }
543 if (redirectorElement != null) {
544 redirectorElement.configure(redirector);
545 }
546 Execute exe = new Execute(createHandler(), createWatchdog());
547 exe.setAntRun(getProject());
548 exe.setWorkingDirectory(dir);
549 exe.setVMLauncher(vmLauncher);
550 exe.setSpawn(spawn);
551 String[] environment = env.getVariables();
552 if (environment != null) {
553 for (int i = 0; i < environment.length; i++) {
554 log("Setting environment variable: " + environment[i],
555 Project.MSG_VERBOSE);
556 }
557 }
558 exe.setNewenvironment(newEnvironment);
559 exe.setEnvironment(environment);
560 return exe;
561 }
562
563 /**
564 * A Utility method for this classes and subclasses to run an
565 * Execute instance (an external command).
566 *
567 * @param exe instance of the execute class.
568 *
569 * @throws IOException in case of problem to attach to the stdin/stdout/stderr
570 * streams of the process.
571 */
572 protected final void runExecute(Execute exe) throws IOException {
573 int returnCode = -1; // assume the worst
574
575 if (!spawn) {
576 returnCode = exe.execute();
577
578 //test for and handle a forced process death
579 if (exe.killedProcess()) {
580 String msg = "Timeout: killed the sub-process";
581 if (failOnError) {
582 throw new BuildException(msg);
583 } else {
584 log(msg, Project.MSG_WARN);
585 }
586 }
587 maybeSetResultPropertyValue(returnCode);
588 redirector.complete();
589 if (Execute.isFailure(returnCode)) {
590 if (failOnError) {
591 throw new BuildException(getTaskType() + " returned: "
592 + returnCode, getLocation());
593 } else {
594 log("Result: " + returnCode, Project.MSG_ERR);
595 }
596 }
597 } else {
598 exe.spawn();
599 }
600 }
601
602 /**
603 * Run the command using the given Execute instance. This may be
604 * overridden by subclasses.
605 *
606 * @param exe instance of Execute to run.
607 *
608 * @throws BuildException if the new process could not be started
609 * only if failIfExecFails is set to true (the default).
610 */
611 protected void runExec(Execute exe) throws BuildException {
612 // show the command
613 log(cmdl.describeCommand(), Project.MSG_VERBOSE);
614
615 exe.setCommandline(cmdl.getCommandline());
616 try {
617 runExecute(exe);
618 } catch (IOException e) {
619 if (failIfExecFails) {
620 throw new BuildException("Execute failed: " + e.toString(), e,
621 getLocation());
622 } else {
623 log("Execute failed: " + e.toString(), Project.MSG_ERR);
624 }
625 } finally {
626 // close the output file if required
627 logFlush();
628 }
629 }
630
631 /**
632 * Create the StreamHandler to use with our Execute instance.
633 *
634 * @return instance of ExecuteStreamHandler.
635 *
636 * @throws BuildException under unknown circumstances.
637 */
638 protected ExecuteStreamHandler createHandler() throws BuildException {
639 return redirector.createHandler();
640 }
641
642 /**
643 * Create the Watchdog to kill a runaway process.
644 *
645 * @return instance of ExecuteWatchdog.
646 *
647 * @throws BuildException under unknown circumstances.
648 */
649 protected ExecuteWatchdog createWatchdog() throws BuildException {
650 return (timeout == null)
651 ? null : new ExecuteWatchdog(timeout.longValue());
652 }
653
654 /**
655 * Flush the output stream - if there is one.
656 */
657 protected void logFlush() {
658 }
659
660 private boolean isPath(String line) {
661 return line.startsWith("PATH=") || line.startsWith("Path=");
662 }
663
664}
Note: See TracBrowser for help on using the repository browser.