| /* |
| * 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.BufferedReader; |
| import java.io.ByteArrayOutputStream; |
| import java.io.File; |
| import java.io.FileWriter; |
| import java.io.IOException; |
| import java.io.OutputStream; |
| import java.io.PrintWriter; |
| import java.io.StringReader; |
| import java.lang.reflect.InvocationTargetException; |
| import java.lang.reflect.Method; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.Vector; |
| |
| import org.apache.tools.ant.BuildException; |
| import org.apache.tools.ant.MagicNames; |
| 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.util.FileUtils; |
| import org.apache.tools.ant.util.StringUtils; |
| |
| /** |
| * Runs an external program. |
| * |
| * @since Ant 1.2 |
| * |
| */ |
| public class Execute { |
| |
| private static final int ONE_SECOND = 1000; |
| |
| /** Invalid exit code. |
| * set to {@link Integer#MAX_VALUE} |
| */ |
| public static final int INVALID = Integer.MAX_VALUE; |
| |
| private static final FileUtils FILE_UTILS = FileUtils.getFileUtils(); |
| |
| private String[] cmdl = null; |
| private String[] env = null; |
| private int exitValue = INVALID; |
| private ExecuteStreamHandler streamHandler; |
| private ExecuteWatchdog watchdog; |
| private File workingDirectory = null; |
| private Project project = null; |
| private boolean newEnvironment = false; |
| //TODO: nothing appears to read this but is set using a public setter. |
| private boolean spawn = false; |
| |
| |
| /** Controls whether the VM is used to launch commands, where possible. */ |
| private boolean useVMLauncher = true; |
| |
| private static String antWorkingDirectory = System.getProperty("user.dir"); |
| private static CommandLauncher vmLauncher = null; |
| private static CommandLauncher shellLauncher = null; |
| private static Vector procEnvironment = null; |
| |
| /** Used to destroy processes when the VM exits. */ |
| private static ProcessDestroyer processDestroyer = new ProcessDestroyer(); |
| |
| /** Used for replacing env variables */ |
| private static boolean environmentCaseInSensitive = false; |
| |
| /* |
| * Builds a command launcher for the OS and JVM we are running under. |
| */ |
| static { |
| // Try using a JDK 1.3 launcher |
| try { |
| if (!Os.isFamily("os/2")) { |
| vmLauncher = new Java13CommandLauncher(); |
| } |
| } catch (NoSuchMethodException exc) { |
| // Ignore and keep trying |
| } |
| if (Os.isFamily("mac") && !Os.isFamily("unix")) { |
| // Mac |
| shellLauncher = new MacCommandLauncher(new CommandLauncher()); |
| } else if (Os.isFamily("os/2")) { |
| // OS/2 |
| shellLauncher = new OS2CommandLauncher(new CommandLauncher()); |
| } else if (Os.isFamily("windows")) { |
| environmentCaseInSensitive = true; |
| CommandLauncher baseLauncher = new CommandLauncher(); |
| |
| if (!Os.isFamily("win9x")) { |
| // Windows XP/2000/NT |
| shellLauncher = new WinNTCommandLauncher(baseLauncher); |
| } else { |
| // Windows 98/95 - need to use an auxiliary script |
| shellLauncher |
| = new ScriptCommandLauncher("bin/antRun.bat", baseLauncher); |
| } |
| } else if (Os.isFamily("netware")) { |
| |
| CommandLauncher baseLauncher = new CommandLauncher(); |
| |
| shellLauncher |
| = new PerlScriptCommandLauncher("bin/antRun.pl", baseLauncher); |
| } else if (Os.isFamily("openvms")) { |
| // OpenVMS |
| try { |
| shellLauncher = new VmsCommandLauncher(); |
| } catch (NoSuchMethodException exc) { |
| // Ignore and keep trying |
| } |
| } else { |
| // Generic |
| shellLauncher = new ScriptCommandLauncher("bin/antRun", |
| new CommandLauncher()); |
| } |
| } |
| |
| /** |
| * Set whether or not you want the process to be spawned. |
| * Default is not spawned. |
| * |
| * @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; |
| } |
| |
| /** |
| * Find the list of environment variables for this process. |
| * |
| * @return a vector containing the environment variables. |
| * The vector elements are strings formatted like variable = value. |
| */ |
| public static synchronized Vector getProcEnvironment() { |
| if (procEnvironment != null) { |
| return procEnvironment; |
| } |
| procEnvironment = new Vector(); |
| try { |
| ByteArrayOutputStream out = new ByteArrayOutputStream(); |
| Execute exe = new Execute(new PumpStreamHandler(out)); |
| exe.setCommandline(getProcEnvCommand()); |
| // Make sure we do not recurse forever |
| exe.setNewenvironment(true); |
| int retval = exe.execute(); |
| if (retval != 0) { |
| // Just try to use what we got |
| } |
| BufferedReader in = |
| new BufferedReader(new StringReader(toString(out))); |
| |
| if (Os.isFamily("openvms")) { |
| procEnvironment = addVMSLogicals(procEnvironment, in); |
| return procEnvironment; |
| } |
| String var = null; |
| String line, lineSep = StringUtils.LINE_SEP; |
| while ((line = in.readLine()) != null) { |
| if (line.indexOf('=') == -1) { |
| // Chunk part of previous env var (UNIX env vars can |
| // contain embedded new lines). |
| if (var == null) { |
| var = lineSep + line; |
| } else { |
| var += lineSep + line; |
| } |
| } else { |
| // New env var...append the previous one if we have it. |
| if (var != null) { |
| procEnvironment.addElement(var); |
| } |
| var = line; |
| } |
| } |
| // Since we "look ahead" before adding, there's one last env var. |
| if (var != null) { |
| procEnvironment.addElement(var); |
| } |
| } catch (java.io.IOException exc) { |
| exc.printStackTrace(); |
| // Just try to see how much we got |
| } |
| return procEnvironment; |
| } |
| |
| /** |
| * This is the operation to get our environment. |
| * It is a notorious troublespot pre-Java1.5, and should be approached |
| * with extreme caution. |
| * @return |
| */ |
| private static String[] getProcEnvCommand() { |
| if (Os.isFamily("os/2")) { |
| // OS/2 - use same mechanism as Windows 2000 |
| return new String[] {"cmd", "/c", "set" }; |
| } else if (Os.isFamily("windows")) { |
| // Determine if we're running under XP/2000/NT or 98/95 |
| if (Os.isFamily("win9x")) { |
| // Windows 98/95 |
| return new String[] {"command.com", "/c", "set" }; |
| } else { |
| // Windows XP/2000/NT/2003 |
| return new String[] {"cmd", "/c", "set" }; |
| } |
| } else if (Os.isFamily("z/os") || Os.isFamily("unix")) { |
| // On most systems one could use: /bin/sh -c env |
| |
| // Some systems have /bin/env, others /usr/bin/env, just try |
| String[] cmd = new String[1]; |
| if (new File("/bin/env").canRead()) { |
| cmd[0] = "/bin/env"; |
| } else if (new File("/usr/bin/env").canRead()) { |
| cmd[0] = "/usr/bin/env"; |
| } else { |
| // rely on PATH |
| cmd[0] = "env"; |
| } |
| return cmd; |
| } else if (Os.isFamily("netware") || Os.isFamily("os/400")) { |
| // rely on PATH |
| return new String[] {"env"}; |
| } else if (Os.isFamily("openvms")) { |
| return new String[] {"show", "logical"}; |
| } else { |
| // MAC OS 9 and previous |
| //TODO: I have no idea how to get it, someone must fix it |
| return null; |
| } |
| } |
| |
| /** |
| * ByteArrayOutputStream#toString doesn't seem to work reliably on |
| * OS/390, at least not the way we use it in the execution |
| * context. |
| * |
| * @param bos the output stream that one wants to read. |
| * @return the output stream as a string, read with |
| * special encodings in the case of z/os and os/400. |
| * |
| * @since Ant 1.5 |
| */ |
| public static String toString(ByteArrayOutputStream bos) { |
| if (Os.isFamily("z/os")) { |
| try { |
| return bos.toString("Cp1047"); |
| } catch (java.io.UnsupportedEncodingException e) { |
| //noop default encoding used |
| } |
| } else if (Os.isFamily("os/400")) { |
| try { |
| return bos.toString("Cp500"); |
| } catch (java.io.UnsupportedEncodingException e) { |
| //noop default encoding used |
| } |
| } |
| return bos.toString(); |
| } |
| |
| /** |
| * Creates a new execute object using <code>PumpStreamHandler</code> for |
| * stream handling. |
| */ |
| public Execute() { |
| this(new PumpStreamHandler(), null); |
| } |
| |
| /** |
| * Creates a new execute object. |
| * |
| * @param streamHandler the stream handler used to handle the input and |
| * output streams of the subprocess. |
| */ |
| public Execute(ExecuteStreamHandler streamHandler) { |
| this(streamHandler, null); |
| } |
| |
| /** |
| * Creates a new execute object. |
| * |
| * @param streamHandler the stream handler used to handle the input and |
| * output streams of the subprocess. |
| * @param watchdog a watchdog for the subprocess or <code>null</code> to |
| * to disable a timeout for the subprocess. |
| */ |
| public Execute(ExecuteStreamHandler streamHandler, |
| ExecuteWatchdog watchdog) { |
| setStreamHandler(streamHandler); |
| this.watchdog = watchdog; |
| //By default, use the shell launcher for VMS |
| // |
| if (Os.isFamily("openvms")) { |
| useVMLauncher = false; |
| } |
| } |
| |
| /** |
| * Set the stream handler to use. |
| * @param streamHandler ExecuteStreamHandler. |
| * @since Ant 1.6 |
| */ |
| public void setStreamHandler(ExecuteStreamHandler streamHandler) { |
| this.streamHandler = streamHandler; |
| } |
| |
| /** |
| * Returns the commandline used to create a subprocess. |
| * |
| * @return the commandline used to create a subprocess. |
| */ |
| public String[] getCommandline() { |
| return cmdl; |
| } |
| |
| /** |
| * Sets the commandline of the subprocess to launch. |
| * |
| * @param commandline the commandline of the subprocess to launch. |
| */ |
| public void setCommandline(String[] commandline) { |
| cmdl = commandline; |
| } |
| |
| /** |
| * Set whether to propagate the default environment or not. |
| * |
| * @param newenv whether to propagate the process environment. |
| */ |
| public void setNewenvironment(boolean newenv) { |
| newEnvironment = newenv; |
| } |
| |
| /** |
| * Returns the environment used to create a subprocess. |
| * |
| * @return the environment used to create a subprocess. |
| */ |
| public String[] getEnvironment() { |
| return (env == null || newEnvironment) |
| ? env : patchEnvironment(); |
| } |
| |
| /** |
| * Sets the environment variables for the subprocess to launch. |
| * |
| * @param env array of Strings, each element of which has |
| * an environment variable settings in format <em>key=value</em>. |
| */ |
| public void setEnvironment(String[] env) { |
| this.env = env; |
| } |
| |
| /** |
| * Sets the working directory of the process to execute. |
| * |
| * <p>This is emulated using the antRun scripts unless the OS is |
| * Windows NT in which case a cmd.exe is spawned, |
| * or MRJ and setting user.dir works, or JDK 1.3 and there is |
| * official support in java.lang.Runtime. |
| * |
| * @param wd the working directory of the process. |
| */ |
| public void setWorkingDirectory(File wd) { |
| workingDirectory = |
| (wd == null || wd.getAbsolutePath().equals(antWorkingDirectory)) |
| ? null : wd; |
| } |
| |
| /** |
| * Return the working directory. |
| * @return the directory as a File. |
| * @since Ant 1.7 |
| */ |
| public File getWorkingDirectory() { |
| return workingDirectory == null ? new File(antWorkingDirectory) |
| : workingDirectory; |
| } |
| |
| /** |
| * Set the name of the antRun script using the project's value. |
| * |
| * @param project the current project. |
| * |
| * @throws BuildException not clear when it is going to throw an exception, but |
| * it is the method's signature. |
| */ |
| public void setAntRun(Project project) throws BuildException { |
| this.project = project; |
| } |
| |
| /** |
| * Launch this execution through the VM, where possible, rather than through |
| * the OS's shell. In some cases and operating systems using the shell will |
| * allow the shell to perform additional processing such as associating an |
| * executable with a script, etc. |
| * |
| * @param useVMLauncher true if exec should launch through the VM, |
| * false if the shell should be used to launch the |
| * command. |
| */ |
| public void setVMLauncher(boolean useVMLauncher) { |
| this.useVMLauncher = useVMLauncher; |
| } |
| |
| /** |
| * Creates a process that runs a command. |
| * |
| * @param project the Project, only used for logging purposes, may be null. |
| * @param command the command to run. |
| * @param env the environment for the command. |
| * @param dir the working directory for the command. |
| * @param useVM use the built-in exec command for JDK 1.3 if available. |
| * @return the process started. |
| * @throws IOException forwarded from the particular launcher used. |
| * |
| * @since Ant 1.5 |
| */ |
| public static Process launch(Project project, String[] command, |
| String[] env, File dir, boolean useVM) |
| throws IOException { |
| if (dir != null && !dir.exists()) { |
| throw new BuildException(dir + " doesn't exist."); |
| } |
| CommandLauncher launcher |
| = ((useVM && vmLauncher != null) ? vmLauncher : shellLauncher); |
| return launcher.exec(project, command, env, dir); |
| } |
| |
| /** |
| * Runs a process defined by the command line and returns its exit status. |
| * |
| * @return the exit status of the subprocess or <code>INVALID</code>. |
| * @exception java.io.IOException The exception is thrown, if launching |
| * of the subprocess failed. |
| */ |
| public int execute() throws IOException { |
| if (workingDirectory != null && !workingDirectory.exists()) { |
| throw new BuildException(workingDirectory + " doesn't exist."); |
| } |
| final Process process = launch(project, getCommandline(), |
| getEnvironment(), workingDirectory, |
| useVMLauncher); |
| try { |
| streamHandler.setProcessInputStream(process.getOutputStream()); |
| streamHandler.setProcessOutputStream(process.getInputStream()); |
| streamHandler.setProcessErrorStream(process.getErrorStream()); |
| } catch (IOException e) { |
| process.destroy(); |
| throw e; |
| } |
| streamHandler.start(); |
| |
| try { |
| // add the process to the list of those to destroy if the VM exits |
| // |
| processDestroyer.add(process); |
| |
| if (watchdog != null) { |
| watchdog.start(process); |
| } |
| waitFor(process); |
| |
| if (watchdog != null) { |
| watchdog.stop(); |
| } |
| streamHandler.stop(); |
| closeStreams(process); |
| |
| if (watchdog != null) { |
| watchdog.checkException(); |
| } |
| return getExitValue(); |
| } catch (ThreadDeath t) { |
| // #31928: forcibly kill it before continuing. |
| process.destroy(); |
| throw t; |
| } finally { |
| // remove the process to the list of those to destroy if |
| // the VM exits |
| // |
| processDestroyer.remove(process); |
| } |
| } |
| |
| /** |
| * Starts a process defined by the command line. |
| * Ant will not wait for this process, nor log its output. |
| * |
| * @throws java.io.IOException The exception is thrown, if launching |
| * of the subprocess failed. |
| * @since Ant 1.6 |
| */ |
| public void spawn() throws IOException { |
| if (workingDirectory != null && !workingDirectory.exists()) { |
| throw new BuildException(workingDirectory + " doesn't exist."); |
| } |
| final Process process = launch(project, getCommandline(), |
| getEnvironment(), workingDirectory, |
| useVMLauncher); |
| if (Os.isFamily("windows")) { |
| try { |
| Thread.sleep(ONE_SECOND); |
| } catch (InterruptedException e) { |
| project.log("interruption in the sleep after having spawned a" |
| + " process", Project.MSG_VERBOSE); |
| } |
| } |
| OutputStream dummyOut = new OutputStream() { |
| public void write(int b) throws IOException { |
| } |
| }; |
| |
| ExecuteStreamHandler handler = new PumpStreamHandler(dummyOut); |
| handler.setProcessErrorStream(process.getErrorStream()); |
| handler.setProcessOutputStream(process.getInputStream()); |
| handler.start(); |
| process.getOutputStream().close(); |
| |
| project.log("spawned process " + process.toString(), |
| Project.MSG_VERBOSE); |
| } |
| |
| /** |
| * Wait for a given process. |
| * |
| * @param process the process one wants to wait for. |
| */ |
| protected void waitFor(Process process) { |
| try { |
| process.waitFor(); |
| setExitValue(process.exitValue()); |
| } catch (InterruptedException e) { |
| process.destroy(); |
| } |
| } |
| |
| /** |
| * Set the exit value. |
| * |
| * @param value exit value of the process. |
| */ |
| protected void setExitValue(int value) { |
| exitValue = value; |
| } |
| |
| /** |
| * Query the exit value of the process. |
| * @return the exit value or Execute.INVALID if no exit value has |
| * been received. |
| */ |
| public int getExitValue() { |
| return exitValue; |
| } |
| |
| /** |
| * Checks whether <code>exitValue</code> signals a failure on the current |
| * system (OS specific). |
| * |
| * <p><b>Note</b> that this method relies on the conventions of |
| * the OS, it will return false results if the application you are |
| * running doesn't follow these conventions. One notable |
| * exception is the Java VM provided by HP for OpenVMS - it will |
| * return 0 if successful (like on any other platform), but this |
| * signals a failure on OpenVMS. So if you execute a new Java VM |
| * on OpenVMS, you cannot trust this method.</p> |
| * |
| * @param exitValue the exit value (return code) to be checked. |
| * @return <code>true</code> if <code>exitValue</code> signals a failure. |
| */ |
| public static boolean isFailure(int exitValue) { |
| //on openvms even exit value signals failure; |
| // for other platforms nonzero exit value signals failure |
| return Os.isFamily("openvms") |
| ? (exitValue % 2 == 0) : (exitValue != 0); |
| } |
| |
| /** |
| * Did this execute return in a failure. |
| * @see #isFailure(int) |
| * @return true if and only if the exit code is interpreted as a failure |
| * @since Ant1.7 |
| */ |
| public boolean isFailure() { |
| return isFailure(getExitValue()); |
| } |
| |
| /** |
| * Test for an untimely death of the process. |
| * @return true if a watchdog had to kill the process. |
| * @since Ant 1.5 |
| */ |
| public boolean killedProcess() { |
| return watchdog != null && watchdog.killedProcess(); |
| } |
| |
| /** |
| * Patch the current environment with the new values from the user. |
| * @return the patched environment. |
| */ |
| private String[] patchEnvironment() { |
| // On OpenVMS Runtime#exec() doesn't support the environment array, |
| // so we only return the new values which then will be set in |
| // the generated DCL script, inheriting the parent process environment |
| if (Os.isFamily("openvms")) { |
| return env; |
| } |
| Vector osEnv = (Vector) getProcEnvironment().clone(); |
| for (int i = 0; i < env.length; i++) { |
| String keyValue = env[i]; |
| // Get key including "=" |
| String key = keyValue.substring(0, keyValue.indexOf('=') + 1); |
| if (environmentCaseInSensitive) { |
| // Nb: using default locale as key is a env name |
| key = key.toLowerCase(); |
| } |
| int size = osEnv.size(); |
| // Find the key in the current enviroment copy |
| // and remove it. |
| for (int j = 0; j < size; j++) { |
| String osEnvItem = (String) osEnv.elementAt(j); |
| String convertedItem = environmentCaseInSensitive |
| ? osEnvItem.toLowerCase() : osEnvItem; |
| if (convertedItem.startsWith(key)) { |
| osEnv.removeElementAt(j); |
| if (environmentCaseInSensitive) { |
| // Use the original casiness of the key |
| keyValue = osEnvItem.substring(0, key.length()) |
| + keyValue.substring(key.length()); |
| } |
| break; |
| } |
| } |
| // Add the key to the enviromnent copy |
| osEnv.addElement(keyValue); |
| } |
| return (String[]) (osEnv.toArray(new String[osEnv.size()])); |
| } |
| |
| /** |
| * A utility method that runs an external command. Writes the output and |
| * error streams of the command to the project log. |
| * |
| * @param task The task that the command is part of. Used for logging |
| * @param cmdline The command to execute. |
| * |
| * @throws BuildException if the command does not exit successfully. |
| */ |
| public static void runCommand(Task task, String[] cmdline) |
| throws BuildException { |
| try { |
| task.log(Commandline.describeCommand(cmdline), |
| Project.MSG_VERBOSE); |
| Execute exe = new Execute( |
| new LogStreamHandler(task, Project.MSG_INFO, Project.MSG_ERR)); |
| exe.setAntRun(task.getProject()); |
| exe.setCommandline(cmdline); |
| int retval = exe.execute(); |
| if (isFailure(retval)) { |
| throw new BuildException(cmdline[0] |
| + " failed with return code " + retval, task.getLocation()); |
| } |
| } catch (java.io.IOException exc) { |
| throw new BuildException("Could not launch " + cmdline[0] + ": " |
| + exc, task.getLocation()); |
| } |
| } |
| |
| /** |
| * Close the streams belonging to the given Process. |
| * @param process the <code>Process</code>. |
| */ |
| public static void closeStreams(Process process) { |
| FileUtils.close(process.getInputStream()); |
| FileUtils.close(process.getOutputStream()); |
| FileUtils.close(process.getErrorStream()); |
| } |
| |
| /** |
| * This method is VMS specific and used by getProcEnvironment(). |
| * |
| * Parses VMS logicals from <code>in</code> and adds them to |
| * <code>environment</code>. <code>in</code> is expected to be the |
| * output of "SHOW LOGICAL". The method takes care of parsing the output |
| * correctly as well as making sure that a logical defined in multiple |
| * tables only gets added from the highest order table. Logicals with |
| * multiple equivalence names are mapped to a variable with multiple |
| * values separated by a comma (,). |
| */ |
| private static Vector addVMSLogicals(Vector environment, BufferedReader in) |
| throws IOException { |
| HashMap logicals = new HashMap(); |
| String logName = null, logValue = null, newLogName; |
| String line = null; |
| // CheckStyle:MagicNumber OFF |
| while ((line = in.readLine()) != null) { |
| // parse the VMS logicals into required format ("VAR=VAL[,VAL2]") |
| if (line.startsWith("\t=")) { |
| // further equivalence name of previous logical |
| if (logName != null) { |
| logValue += "," + line.substring(4, line.length() - 1); |
| } |
| } else if (line.startsWith(" \"")) { |
| // new logical? |
| if (logName != null) { |
| logicals.put(logName, logValue); |
| } |
| int eqIndex = line.indexOf('='); |
| newLogName = line.substring(3, eqIndex - 2); |
| if (logicals.containsKey(newLogName)) { |
| // already got this logical from a higher order table |
| logName = null; |
| } else { |
| logName = newLogName; |
| logValue = line.substring(eqIndex + 3, line.length() - 1); |
| } |
| } |
| } |
| // CheckStyle:MagicNumber ON |
| // Since we "look ahead" before adding, there's one last env var. |
| if (logName != null) { |
| logicals.put(logName, logValue); |
| } |
| for (Iterator i = logicals.keySet().iterator(); i.hasNext();) { |
| String logical = (String) i.next(); |
| environment.add(logical + "=" + logicals.get(logical)); |
| } |
| return environment; |
| } |
| |
| /** |
| * A command launcher for a particular JVM/OS platform. This class is |
| * a general purpose command launcher which can only launch commands in |
| * the current working directory. |
| */ |
| private static class CommandLauncher { |
| /** |
| * Launches the given command in a new process. |
| * |
| * @param project The project that the command is part of. |
| * @param cmd The command to execute. |
| * @param env The environment for the new process. If null, |
| * the environment of the current process is used. |
| * @return the created Process. |
| * @throws IOException if attempting to run a command in a |
| * specific directory. |
| */ |
| public Process exec(Project project, String[] cmd, String[] env) |
| throws IOException { |
| if (project != null) { |
| project.log("Execute:CommandLauncher: " |
| + Commandline.describeCommand(cmd), Project.MSG_DEBUG); |
| } |
| return Runtime.getRuntime().exec(cmd, env); |
| } |
| |
| /** |
| * Launches the given command in a new process, in the given working |
| * directory. |
| * |
| * @param project The project that the command is part of. |
| * @param cmd The command to execute. |
| * @param env The environment for the new process. If null, |
| * the environment of the current process is used. |
| * @param workingDir The directory to start the command in. If null, |
| * the current directory is used. |
| * @return the created Process. |
| * @throws IOException if trying to change directory. |
| */ |
| public Process exec(Project project, String[] cmd, String[] env, |
| File workingDir) throws IOException { |
| if (workingDir == null) { |
| return exec(project, cmd, env); |
| } |
| throw new IOException("Cannot execute a process in different " |
| + "directory under this JVM"); |
| } |
| } |
| |
| /** |
| * A command launcher for JDK/JRE 1.3 (and higher). Uses the built-in |
| * Runtime.exec() command. |
| */ |
| private static class Java13CommandLauncher extends CommandLauncher { |
| private Method myExecWithCWD; |
| |
| public Java13CommandLauncher() throws NoSuchMethodException { |
| // Locate method Runtime.exec(String[] cmdarray, |
| // String[] envp, File dir) |
| myExecWithCWD = Runtime.class.getMethod("exec", |
| new Class[] {String[].class, String[].class, File.class}); |
| } |
| |
| /** |
| * Launches the given command in a new process, in the given working |
| * directory. |
| * @param project the Ant project. |
| * @param cmd the command line to execute as an array of strings. |
| * @param env the environment to set as an array of strings. |
| * @param workingDir the working directory where the command |
| * should run. |
| * @return the created Process. |
| * @throws IOException probably forwarded from Runtime#exec. |
| */ |
| public Process exec(Project project, String[] cmd, String[] env, |
| File workingDir) throws IOException { |
| try { |
| if (project != null) { |
| project.log("Execute:Java13CommandLauncher: " |
| + Commandline.describeCommand(cmd), Project.MSG_DEBUG); |
| } |
| return (Process) myExecWithCWD.invoke(Runtime.getRuntime(), |
| /* the arguments: */ new Object[] {cmd, env, workingDir}); |
| } catch (InvocationTargetException exc) { |
| Throwable realexc = exc.getTargetException(); |
| if (realexc instanceof ThreadDeath) { |
| throw (ThreadDeath) realexc; |
| } else if (realexc instanceof IOException) { |
| throw (IOException) realexc; |
| } else { |
| throw new BuildException("Unable to execute command", |
| realexc); |
| } |
| } catch (Exception exc) { |
| // IllegalAccess, IllegalArgument, ClassCast |
| throw new BuildException("Unable to execute command", exc); |
| } |
| } |
| } |
| |
| /** |
| * A command launcher that proxies another command launcher. |
| * |
| * Sub-classes override exec(args, env, workdir). |
| */ |
| private static class CommandLauncherProxy extends CommandLauncher { |
| private CommandLauncher myLauncher; |
| |
| CommandLauncherProxy(CommandLauncher launcher) { |
| myLauncher = launcher; |
| } |
| |
| /** |
| * Launches the given command in a new process. Delegates this |
| * method to the proxied launcher. |
| * @param project the Ant project. |
| * @param cmd the command line to execute as an array of strings. |
| * @param env the environment to set as an array of strings. |
| * @return the created Process. |
| * @throws IOException forwarded from the exec method of the |
| * command launcher. |
| */ |
| public Process exec(Project project, String[] cmd, String[] env) |
| throws IOException { |
| return myLauncher.exec(project, cmd, env); |
| } |
| } |
| |
| /** |
| * A command launcher for OS/2 that uses 'cmd.exe' when launching |
| * commands in directories other than the current working |
| * directory. |
| * |
| * <p>Unlike Windows NT and friends, OS/2's cd doesn't support the |
| * /d switch to change drives and directories in one go.</p> |
| */ |
| private static class OS2CommandLauncher extends CommandLauncherProxy { |
| OS2CommandLauncher(CommandLauncher launcher) { |
| super(launcher); |
| } |
| |
| /** |
| * Launches the given command in a new process, in the given working |
| * directory. |
| * @param project the Ant project. |
| * @param cmd the command line to execute as an array of strings. |
| * @param env the environment to set as an array of strings. |
| * @param workingDir working directory where the command should run. |
| * @return the created Process. |
| * @throws IOException forwarded from the exec method of the |
| * command launcher. |
| */ |
| public Process exec(Project project, String[] cmd, String[] env, |
| File workingDir) throws IOException { |
| File commandDir = workingDir; |
| if (workingDir == null) { |
| if (project != null) { |
| commandDir = project.getBaseDir(); |
| } else { |
| return exec(project, cmd, env); |
| } |
| } |
| // Use cmd.exe to change to the specified drive and |
| // directory before running the command |
| final int preCmdLength = 7; |
| final String cmdDir = commandDir.getAbsolutePath(); |
| String[] newcmd = new String[cmd.length + preCmdLength]; |
| // CheckStyle:MagicNumber OFF - do not bother |
| newcmd[0] = "cmd"; |
| newcmd[1] = "/c"; |
| newcmd[2] = cmdDir.substring(0, 2); |
| newcmd[3] = "&&"; |
| newcmd[4] = "cd"; |
| newcmd[5] = cmdDir.substring(2); |
| newcmd[6] = "&&"; |
| // CheckStyle:MagicNumber ON |
| System.arraycopy(cmd, 0, newcmd, preCmdLength, cmd.length); |
| |
| return exec(project, newcmd, env); |
| } |
| } |
| |
| /** |
| * A command launcher for Windows XP/2000/NT that uses 'cmd.exe' when |
| * launching commands in directories other than the current working |
| * directory. |
| */ |
| private static class WinNTCommandLauncher extends CommandLauncherProxy { |
| WinNTCommandLauncher(CommandLauncher launcher) { |
| super(launcher); |
| } |
| |
| /** |
| * Launches the given command in a new process, in the given working |
| * directory. |
| * @param project the Ant project. |
| * @param cmd the command line to execute as an array of strings. |
| * @param env the environment to set as an array of strings. |
| * @param workingDir working directory where the command should run. |
| * @return the created Process. |
| * @throws IOException forwarded from the exec method of the |
| * command launcher. |
| */ |
| public Process exec(Project project, String[] cmd, String[] env, |
| File workingDir) throws IOException { |
| File commandDir = workingDir; |
| if (workingDir == null) { |
| if (project != null) { |
| commandDir = project.getBaseDir(); |
| } else { |
| return exec(project, cmd, env); |
| } |
| } |
| // Use cmd.exe to change to the specified directory before running |
| // the command |
| final int preCmdLength = 6; |
| String[] newcmd = new String[cmd.length + preCmdLength]; |
| // CheckStyle:MagicNumber OFF - do not bother |
| newcmd[0] = "cmd"; |
| newcmd[1] = "/c"; |
| newcmd[2] = "cd"; |
| newcmd[3] = "/d"; |
| newcmd[4] = commandDir.getAbsolutePath(); |
| newcmd[5] = "&&"; |
| // CheckStyle:MagicNumber ON |
| System.arraycopy(cmd, 0, newcmd, preCmdLength, cmd.length); |
| |
| return exec(project, newcmd, env); |
| } |
| } |
| |
| /** |
| * A command launcher for Mac that uses a dodgy mechanism to change |
| * working directory before launching commands. |
| */ |
| private static class MacCommandLauncher extends CommandLauncherProxy { |
| MacCommandLauncher(CommandLauncher launcher) { |
| super(launcher); |
| } |
| |
| /** |
| * Launches the given command in a new process, in the given working |
| * directory. |
| * @param project the Ant project. |
| * @param cmd the command line to execute as an array of strings. |
| * @param env the environment to set as an array of strings. |
| * @param workingDir working directory where the command should run. |
| * @return the created Process. |
| * @throws IOException forwarded from the exec method of the |
| * command launcher. |
| */ |
| public Process exec(Project project, String[] cmd, String[] env, |
| File workingDir) throws IOException { |
| if (workingDir == null) { |
| return exec(project, cmd, env); |
| } |
| System.getProperties().put("user.dir", workingDir.getAbsolutePath()); |
| try { |
| return exec(project, cmd, env); |
| } finally { |
| System.getProperties().put("user.dir", antWorkingDirectory); |
| } |
| } |
| } |
| |
| /** |
| * A command launcher that uses an auxiliary script to launch commands |
| * in directories other than the current working directory. |
| */ |
| private static class ScriptCommandLauncher extends CommandLauncherProxy { |
| ScriptCommandLauncher(String script, CommandLauncher launcher) { |
| super(launcher); |
| myScript = script; |
| } |
| |
| /** |
| * Launches the given command in a new process, in the given working |
| * directory. |
| * @param project the Ant project. |
| * @param cmd the command line to execute as an array of strings. |
| * @param env the environment to set as an array of strings. |
| * @param workingDir working directory where the command should run. |
| * @return the created Process. |
| * @throws IOException forwarded from the exec method of the |
| * command launcher. |
| */ |
| public Process exec(Project project, String[] cmd, String[] env, |
| File workingDir) throws IOException { |
| if (project == null) { |
| if (workingDir == null) { |
| return exec(project, cmd, env); |
| } |
| throw new IOException("Cannot locate antRun script: " |
| + "No project provided"); |
| } |
| // Locate the auxiliary script |
| String antHome = project.getProperty(MagicNames.ANT_HOME); |
| if (antHome == null) { |
| throw new IOException("Cannot locate antRun script: " |
| + "Property '" + MagicNames.ANT_HOME + "' not found"); |
| } |
| String antRun = |
| FILE_UTILS.resolveFile(project.getBaseDir(), |
| antHome + File.separator + myScript).toString(); |
| |
| // Build the command |
| File commandDir = workingDir; |
| if (workingDir == null) { |
| commandDir = project.getBaseDir(); |
| } |
| String[] newcmd = new String[cmd.length + 2]; |
| newcmd[0] = antRun; |
| newcmd[1] = commandDir.getAbsolutePath(); |
| System.arraycopy(cmd, 0, newcmd, 2, cmd.length); |
| |
| return exec(project, newcmd, env); |
| } |
| |
| private String myScript; |
| } |
| |
| /** |
| * A command launcher that uses an auxiliary perl script to launch commands |
| * in directories other than the current working directory. |
| */ |
| private static class PerlScriptCommandLauncher |
| extends CommandLauncherProxy { |
| private String myScript; |
| |
| PerlScriptCommandLauncher(String script, CommandLauncher launcher) { |
| super(launcher); |
| myScript = script; |
| } |
| |
| /** |
| * Launches the given command in a new process, in the given working |
| * directory. |
| * @param project the Ant project. |
| * @param cmd the command line to execute as an array of strings. |
| * @param env the environment to set as an array of strings. |
| * @param workingDir working directory where the command should run. |
| * @return the created Process. |
| * @throws IOException forwarded from the exec method of the |
| * command launcher. |
| */ |
| public Process exec(Project project, String[] cmd, String[] env, |
| File workingDir) throws IOException { |
| if (project == null) { |
| if (workingDir == null) { |
| return exec(project, cmd, env); |
| } |
| throw new IOException("Cannot locate antRun script: " |
| + "No project provided"); |
| } |
| // Locate the auxiliary script |
| String antHome = project.getProperty(MagicNames.ANT_HOME); |
| if (antHome == null) { |
| throw new IOException("Cannot locate antRun script: " |
| + "Property '" + MagicNames.ANT_HOME + "' not found"); |
| } |
| String antRun = |
| FILE_UTILS.resolveFile(project.getBaseDir(), |
| antHome + File.separator + myScript).toString(); |
| |
| // Build the command |
| File commandDir = workingDir; |
| if (workingDir == null) { |
| commandDir = project.getBaseDir(); |
| } |
| // CheckStyle:MagicNumber OFF |
| String[] newcmd = new String[cmd.length + 3]; |
| newcmd[0] = "perl"; |
| newcmd[1] = antRun; |
| newcmd[2] = commandDir.getAbsolutePath(); |
| System.arraycopy(cmd, 0, newcmd, 3, cmd.length); |
| // CheckStyle:MagicNumber ON |
| |
| return exec(project, newcmd, env); |
| } |
| } |
| |
| /** |
| * A command launcher for VMS that writes the command to a temporary DCL |
| * script before launching commands. This is due to limitations of both |
| * the DCL interpreter and the Java VM implementation. |
| */ |
| private static class VmsCommandLauncher extends Java13CommandLauncher { |
| |
| public VmsCommandLauncher() throws NoSuchMethodException { |
| super(); |
| } |
| |
| /** |
| * Launches the given command in a new process. |
| * @param project the Ant project. |
| * @param cmd the command line to execute as an array of strings. |
| * @param env the environment to set as an array of strings. |
| * @return the created Process. |
| * @throws IOException forwarded from the exec method of the |
| * command launcher. |
| */ |
| public Process exec(Project project, String[] cmd, String[] env) |
| throws IOException { |
| File cmdFile = createCommandFile(cmd, env); |
| Process p |
| = super.exec(project, new String[] {cmdFile.getPath()}, env); |
| deleteAfter(cmdFile, p); |
| return p; |
| } |
| |
| /** |
| * Launches the given command in a new process, in the given working |
| * directory. Note that under Java 1.3.1, 1.4.0 and 1.4.1 on VMS this |
| * method only works if <code>workingDir</code> is null or the logical |
| * JAVA$FORK_SUPPORT_CHDIR needs to be set to TRUE. |
| * @param project the Ant project. |
| * @param cmd the command line to execute as an array of strings. |
| * @param env the environment to set as an array of strings. |
| * @param workingDir working directory where the command should run. |
| * @return the created Process. |
| * @throws IOException forwarded from the exec method of the |
| * command launcher. |
| */ |
| public Process exec(Project project, String[] cmd, String[] env, |
| File workingDir) throws IOException { |
| File cmdFile = createCommandFile(cmd, env); |
| Process p = super.exec(project, new String[] {cmdFile.getPath()}, |
| env, workingDir); |
| deleteAfter(cmdFile, p); |
| return p; |
| } |
| |
| /* |
| * Writes the command into a temporary DCL script and returns the |
| * corresponding File object. The script will be deleted on exit. |
| * @param cmd the command line to execute as an array of strings. |
| * @param env the environment to set as an array of strings. |
| * @return the command File. |
| * @throws IOException if errors are encountered creating the file. |
| */ |
| private File createCommandFile(String[] cmd, String[] env) |
| throws IOException { |
| File script = FILE_UTILS.createTempFile("ANT", ".COM", null, true, true); |
| PrintWriter out = null; |
| try { |
| out = new PrintWriter(new FileWriter(script)); |
| |
| // add the environment as logicals to the DCL script |
| if (env != null) { |
| int eqIndex; |
| for (int i = 0; i < env.length; i++) { |
| eqIndex = env[i].indexOf('='); |
| if (eqIndex != -1) { |
| out.print("$ DEFINE/NOLOG "); |
| out.print(env[i].substring(0, eqIndex)); |
| out.print(" \""); |
| out.print(env[i].substring(eqIndex + 1)); |
| out.println('\"'); |
| } |
| } |
| } |
| out.print("$ " + cmd[0]); |
| for (int i = 1; i < cmd.length; i++) { |
| out.println(" -"); |
| out.print(cmd[i]); |
| } |
| } finally { |
| if (out != null) { |
| out.close(); |
| } |
| } |
| return script; |
| } |
| |
| private void deleteAfter(final File f, final Process p) { |
| new Thread() { |
| public void run() { |
| try { |
| p.waitFor(); |
| } catch (InterruptedException e) { |
| //ignore |
| } |
| FileUtils.delete(f); |
| } |
| } |
| .start(); |
| } |
| } |
| } |