diff options
Diffstat (limited to 'framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/ExecTask.java')
-rw-r--r-- | framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/ExecTask.java | 726 |
1 files changed, 726 insertions, 0 deletions
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/ExecTask.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/ExecTask.java new file mode 100644 index 00000000..dd93978f --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/ExecTask.java @@ -0,0 +1,726 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.tools.ant.taskdefs; + +import java.io.File; +import java.io.IOException; +import java.util.Locale; +import java.util.Map; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.taskdefs.condition.Os; +import org.apache.tools.ant.types.Commandline; +import org.apache.tools.ant.types.Environment; +import org.apache.tools.ant.types.Path; +import org.apache.tools.ant.types.RedirectorElement; +import org.apache.tools.ant.util.FileUtils; + +/** + * Executes a given command if the os platform is appropriate. + * + * @since Ant 1.2 + * + * @ant.task category="control" + */ +public class ExecTask extends Task { + + private static final FileUtils FILE_UTILS = FileUtils.getFileUtils(); + + private String os; + private String osFamily; + + private File dir; + // CheckStyle:VisibilityModifier OFF - bc + protected boolean failOnError = false; + protected boolean newEnvironment = false; + private Long timeout = null; + private Environment env = new Environment(); + protected Commandline cmdl = new Commandline(); + private String resultProperty; + private boolean failIfExecFails = true; + private String executable; + private boolean resolveExecutable = false; + private boolean searchPath = false; + private boolean spawn = false; + private boolean incompatibleWithSpawn = false; + + //include locally for screening purposes + private String inputString; + private File input; + private File output; + private File error; + + protected Redirector redirector = new Redirector(this); + protected RedirectorElement redirectorElement; + // CheckStyle:VisibilityModifier ON + + /** + * Controls whether the VM (1.3 and above) is used to execute the + * command + */ + private boolean vmLauncher = true; + + + /** + * Create an instance. + * Needs to be configured by binding to a project. + */ + public ExecTask() { + } + + /** + * create an instance that is helping another task. + * Project, OwningTarget, TaskName and description are all + * pulled out + * @param owner task that we belong to + */ + public ExecTask(Task owner) { + bindToOwner(owner); + } + + /** + * Set whether or not you want the process to be spawned. + * Default is false. + * @param spawn if true you do not want Ant to wait for the end of the process. + * @since Ant 1.6 + */ + public void setSpawn(boolean spawn) { + this.spawn = spawn; + } + + /** + * Set the timeout in milliseconds after which the process will be killed. + * + * @param value timeout in milliseconds. + * + * @since Ant 1.5 + */ + public void setTimeout(Long value) { + timeout = value; + incompatibleWithSpawn |= timeout != null; + } + + /** + * Set the timeout in milliseconds after which the process will be killed. + * + * @param value timeout in milliseconds. + */ + public void setTimeout(Integer value) { + setTimeout( + (Long) ((value == null) ? null : new Long(value.intValue()))); + } + + /** + * Set the name of the executable program. + * @param value the name of the executable program. + */ + public void setExecutable(String value) { + this.executable = value; + cmdl.setExecutable(value); + } + + /** + * Set the working directory of the process. + * @param d the working directory of the process. + */ + public void setDir(File d) { + this.dir = d; + } + + /** + * List of operating systems on which the command may be executed. + * @param os list of operating systems on which the command may be executed. + */ + public void setOs(String os) { + this.os = os; + } + + /** + * List of operating systems on which the command may be executed. + * @since Ant 1.8.0 + */ + public final String getOs() { + return os; + } + + /** + * Sets a command line. + * @param cmdl command line. + * @ant.attribute ignore="true" + */ + public void setCommand(Commandline cmdl) { + log("The command attribute is deprecated.\n" + + "Please use the executable attribute and nested arg elements.", + Project.MSG_WARN); + this.cmdl = cmdl; + } + + /** + * File the output of the process is redirected to. If error is not + * redirected, it too will appear in the output. + * + * @param out name of a file to which output should be sent. + */ + public void setOutput(File out) { + this.output = out; + incompatibleWithSpawn = true; + } + + /** + * Set the input file to use for the task. + * + * @param input name of a file from which to get input. + */ + public void setInput(File input) { + if (inputString != null) { + throw new BuildException("The \"input\" and \"inputstring\" " + + "attributes cannot both be specified"); + } + this.input = input; + incompatibleWithSpawn = true; + } + + /** + * Set the string to use as input. + * + * @param inputString the string which is used as the input source. + */ + public void setInputString(String inputString) { + if (input != null) { + throw new BuildException("The \"input\" and \"inputstring\" " + + "attributes cannot both be specified"); + } + this.inputString = inputString; + incompatibleWithSpawn = true; + } + + /** + * Controls whether error output of exec is logged. This is only useful when + * output is being redirected and error output is desired in the Ant log. + * + * @param logError set to true to log error output in the normal ant log. + */ + public void setLogError(boolean logError) { + redirector.setLogError(logError); + incompatibleWithSpawn |= logError; + } + + /** + * Set the File to which the error stream of the process should be redirected. + * + * @param error a file to which stderr should be sent. + * + * @since Ant 1.6 + */ + public void setError(File error) { + this.error = error; + incompatibleWithSpawn = true; + } + + /** + * Sets the property name whose value should be set to the output of + * the process. + * + * @param outputProp name of property. + */ + public void setOutputproperty(String outputProp) { + redirector.setOutputProperty(outputProp); + incompatibleWithSpawn = true; + } + + /** + * Sets the name of the property whose value should be set to the error of + * the process. + * + * @param errorProperty name of property. + * + * @since Ant 1.6 + */ + public void setErrorProperty(String errorProperty) { + redirector.setErrorProperty(errorProperty); + incompatibleWithSpawn = true; + } + + /** + * Fail if the command exits with a non-zero return code. + * + * @param fail if true fail the command on non-zero return code. + */ + public void setFailonerror(boolean fail) { + failOnError = fail; + incompatibleWithSpawn |= fail; + } + + /** + * Do not propagate old environment when new environment variables are specified. + * + * @param newenv if true, do not propagate old environment + * when new environment variables are specified. + */ + public void setNewenvironment(boolean newenv) { + newEnvironment = newenv; + } + + /** + * Set whether to attempt to resolve the executable to a file. + * + * @param resolveExecutable if true, attempt to resolve the + * path of the executable. + */ + public void setResolveExecutable(boolean resolveExecutable) { + this.resolveExecutable = resolveExecutable; + } + + /** + * Set whether to search nested, then + * system PATH environment variables for the executable. + * + * @param searchPath if true, search PATHs. + */ + public void setSearchPath(boolean searchPath) { + this.searchPath = searchPath; + } + + /** + * Indicates whether to attempt to resolve the executable to a + * file. + * @return the resolveExecutable flag + * + * @since Ant 1.6 + */ + public boolean getResolveExecutable() { + return resolveExecutable; + } + + /** + * Add an environment variable to the launched process. + * + * @param var new environment variable. + */ + public void addEnv(Environment.Variable var) { + env.addVariable(var); + } + + /** + * Adds a command-line argument. + * + * @return new command line argument created. + */ + public Commandline.Argument createArg() { + return cmdl.createArgument(); + } + + /** + * Sets the name of a property in which the return code of the + * command should be stored. Only of interest if failonerror=false. + * + * @since Ant 1.5 + * + * @param resultProperty name of property. + */ + public void setResultProperty(String resultProperty) { + this.resultProperty = resultProperty; + incompatibleWithSpawn = true; + } + + /** + * Helper method to set result property to the + * passed in value if appropriate. + * + * @param result value desired for the result property value. + */ + protected void maybeSetResultPropertyValue(int result) { + if (resultProperty != null) { + String res = Integer.toString(result); + getProject().setNewProperty(resultProperty, res); + } + } + + /** + * Set whether to stop the build if program cannot be started. + * Defaults to true. + * + * @param flag stop the build if program cannot be started. + * + * @since Ant 1.5 + */ + public void setFailIfExecutionFails(boolean flag) { + failIfExecFails = flag; + incompatibleWithSpawn |= flag; + } + + /** + * Set whether output should be appended to or overwrite an existing file. + * Defaults to false. + * + * @param append if true append is desired. + * + * @since 1.30, Ant 1.5 + */ + public void setAppend(boolean append) { + redirector.setAppend(append); + incompatibleWithSpawn |= append; + } + + /** + * Add a <code>RedirectorElement</code> to this task. + * + * @param redirectorElement <code>RedirectorElement</code>. + * @since Ant 1.6.2 + */ + public void addConfiguredRedirector(RedirectorElement redirectorElement) { + if (this.redirectorElement != null) { + throw new BuildException("cannot have > 1 nested <redirector>s"); + } + this.redirectorElement = redirectorElement; + incompatibleWithSpawn = true; + } + + + /** + * Restrict this execution to a single OS Family + * @param osFamily the family to restrict to. + */ + public void setOsFamily(String osFamily) { + this.osFamily = osFamily.toLowerCase(Locale.ENGLISH); + } + + /** + * Restrict this execution to a single OS Family + * @since Ant 1.8.0 + */ + public final String getOsFamily() { + return osFamily; + } + + /** + * The method attempts to figure out where the executable is so that we can feed + * the full path. We first try basedir, then the exec dir, and then + * fallback to the straight executable name (i.e. on the path). + * + * @param exec the name of the executable. + * @param mustSearchPath if true, the executable will be looked up in + * the PATH environment and the absolute path is returned. + * + * @return the executable as a full path if it can be determined. + * + * @since Ant 1.6 + */ + protected String resolveExecutable(String exec, boolean mustSearchPath) { + if (!resolveExecutable) { + return exec; + } + // try to find the executable + File executableFile = getProject().resolveFile(exec); + if (executableFile.exists()) { + return executableFile.getAbsolutePath(); + } + // now try to resolve against the dir if given + if (dir != null) { + executableFile = FILE_UTILS.resolveFile(dir, exec); + if (executableFile.exists()) { + return executableFile.getAbsolutePath(); + } + } + // couldn't find it - must be on path + if (mustSearchPath) { + Path p = null; + String[] environment = env.getVariables(); + if (environment != null) { + for (int i = 0; i < environment.length; i++) { + if (isPath(environment[i])) { + p = new Path(getProject(), getPath(environment[i])); + break; + } + } + } + if (p == null) { + String path = getPath(Execute.getEnvironmentVariables()); + if (path != null) { + p = new Path(getProject(), path); + } + } + if (p != null) { + String[] dirs = p.list(); + for (int i = 0; i < dirs.length; i++) { + executableFile + = FILE_UTILS.resolveFile(new File(dirs[i]), exec); + if (executableFile.exists()) { + return executableFile.getAbsolutePath(); + } + } + } + } + // mustSearchPath is false, or no PATH or not found - keep our + // fingers crossed. + return exec; + } + + /** + * Do the work. + * + * @throws BuildException in a number of circumstances: + * <ul> + * <li>if failIfExecFails is set to true and the process cannot be started</li> + * <li>the java13command launcher can send build exceptions</li> + * <li>this list is not exhaustive or limitative</li> + * </ul> + */ + public void execute() throws BuildException { + // Quick fail if this is not a valid OS for the command + if (!isValidOs()) { + return; + } + File savedDir = dir; // possibly altered in prepareExec + cmdl.setExecutable(resolveExecutable(executable, searchPath)); + checkConfiguration(); + try { + runExec(prepareExec()); + } finally { + dir = savedDir; + } + } + + /** + * Has the user set all necessary attributes? + * @throws BuildException if there are missing required parameters. + */ + protected void checkConfiguration() throws BuildException { + if (cmdl.getExecutable() == null) { + throw new BuildException("no executable specified", getLocation()); + } + if (dir != null && !dir.exists()) { + throw new BuildException("The directory " + dir + " does not exist"); + } + if (dir != null && !dir.isDirectory()) { + throw new BuildException(dir + " is not a directory"); + } + if (spawn && incompatibleWithSpawn) { + getProject().log("spawn does not allow attributes related to input, " + + "output, error, result", Project.MSG_ERR); + getProject().log("spawn also does not allow timeout", Project.MSG_ERR); + getProject().log("finally, spawn is not compatible " + + "with a nested I/O <redirector>", Project.MSG_ERR); + throw new BuildException("You have used an attribute " + + "or nested element which is not compatible with spawn"); + } + setupRedirector(); + } + + /** + * Set up properties on the redirector that we needed to store locally. + */ + protected void setupRedirector() { + redirector.setInput(input); + redirector.setInputString(inputString); + redirector.setOutput(output); + redirector.setError(error); + } + + /** + * Is this the OS the user wanted? + * @return boolean. + * <ul> + * <li> + * <li><code>true</code> if the os and osfamily attributes are null.</li> + * <li><code>true</code> if osfamily is set, and the os family and must match + * that of the current OS, according to the logic of + * {@link Os#isOs(String, String, String, String)}, and the result of the + * <code>os</code> attribute must also evaluate true. + * </li> + * <li> + * <code>true</code> if os is set, and the system.property os.name + * is found in the os attribute,</li> + * <li><code>false</code> otherwise.</li> + * </ul> + */ + protected boolean isValidOs() { + //hand osfamily off to Os class, if set + if (osFamily != null && !Os.isFamily(osFamily)) { + return false; + } + //the Exec OS check is different from Os.isOs(), which + //probes for a specific OS. Instead it searches the os field + //for the current os.name + String myos = System.getProperty("os.name"); + log("Current OS is " + myos, Project.MSG_VERBOSE); + if ((os != null) && (os.indexOf(myos) < 0)) { + // this command will be executed only on the specified OS + log("This OS, " + myos + + " was not found in the specified list of valid OSes: " + os, + Project.MSG_VERBOSE); + return false; + } + return true; + } + + /** + * Set whether to launch new process with VM, otherwise use the OS's shell. + * Default value is true. + * @param vmLauncher true if we want to launch new process with VM, + * false if we want to use the OS's shell. + */ + public void setVMLauncher(boolean vmLauncher) { + this.vmLauncher = vmLauncher; + } + + /** + * Create an Execute instance with the correct working directory set. + * + * @return an instance of the Execute class. + * + * @throws BuildException under unknown circumstances. + */ + protected Execute prepareExec() throws BuildException { + // default directory to the project's base directory + if (dir == null) { + dir = getProject().getBaseDir(); + } + if (redirectorElement != null) { + redirectorElement.configure(redirector); + } + Execute exe = new Execute(createHandler(), createWatchdog()); + exe.setAntRun(getProject()); + exe.setWorkingDirectory(dir); + exe.setVMLauncher(vmLauncher); + String[] environment = env.getVariables(); + if (environment != null) { + for (int i = 0; i < environment.length; i++) { + log("Setting environment variable: " + environment[i], + Project.MSG_VERBOSE); + } + } + exe.setNewenvironment(newEnvironment); + exe.setEnvironment(environment); + return exe; + } + + /** + * A Utility method for this classes and subclasses to run an + * Execute instance (an external command). + * + * @param exe instance of the execute class. + * + * @throws IOException in case of problem to attach to the stdin/stdout/stderr + * streams of the process. + */ + protected final void runExecute(Execute exe) throws IOException { + int returnCode = -1; // assume the worst + + if (!spawn) { + returnCode = exe.execute(); + + //test for and handle a forced process death + if (exe.killedProcess()) { + String msg = "Timeout: killed the sub-process"; + if (failOnError) { + throw new BuildException(msg); + } else { + log(msg, Project.MSG_WARN); + } + } + maybeSetResultPropertyValue(returnCode); + redirector.complete(); + if (Execute.isFailure(returnCode)) { + if (failOnError) { + throw new BuildException(getTaskType() + " returned: " + + returnCode, getLocation()); + } else { + log("Result: " + returnCode, Project.MSG_ERR); + } + } + } else { + exe.spawn(); + } + } + + /** + * Run the command using the given Execute instance. This may be + * overridden by subclasses. + * + * @param exe instance of Execute to run. + * + * @throws BuildException if the new process could not be started + * only if failIfExecFails is set to true (the default). + */ + protected void runExec(Execute exe) throws BuildException { + // show the command + log(cmdl.describeCommand(), Project.MSG_VERBOSE); + + exe.setCommandline(cmdl.getCommandline()); + try { + runExecute(exe); + } catch (IOException e) { + if (failIfExecFails) { + throw new BuildException("Execute failed: " + e.toString(), e, + getLocation()); + } else { + log("Execute failed: " + e.toString(), Project.MSG_ERR); + } + } finally { + // close the output file if required + logFlush(); + } + } + + /** + * Create the StreamHandler to use with our Execute instance. + * + * @return instance of ExecuteStreamHandler. + * + * @throws BuildException under unknown circumstances. + */ + protected ExecuteStreamHandler createHandler() throws BuildException { + return redirector.createHandler(); + } + + /** + * Create the Watchdog to kill a runaway process. + * + * @return instance of ExecuteWatchdog. + * + * @throws BuildException under unknown circumstances. + */ + protected ExecuteWatchdog createWatchdog() throws BuildException { + return (timeout == null) + ? null : new ExecuteWatchdog(timeout.longValue()); + } + + /** + * Flush the output stream - if there is one. + */ + protected void logFlush() { + } + + private boolean isPath(String line) { + return line.startsWith("PATH=") + || line.startsWith("Path="); + } + + private String getPath(String line) { + return line.substring("PATH=".length()); + } + + private String getPath(Map<String, String> map) { + String p = map.get("PATH"); + return p != null ? p : map.get("Path"); + } +} |