Clover coverage report - Ant Coverage
Coverage timestamp: Tue Apr 8 2003 20:43:55 EST
file stats: LOC: 915   Methods: 39
NCLOC: 499   Classes: 10
 
 Source file Conditionals Statements Methods TOTAL
Execute.java 24.5% 36.3% 61.5% 35.6%
 1   
 /*
 2   
  * The Apache Software License, Version 1.1
 3   
  *
 4   
  * Copyright (c) 2000-2003 The Apache Software Foundation.  All rights
 5   
  * reserved.
 6   
  *
 7   
  * Redistribution and use in source and binary forms, with or without
 8   
  * modification, are permitted provided that the following conditions
 9   
  * are met:
 10   
  *
 11   
  * 1. Redistributions of source code must retain the above copyright
 12   
  *    notice, this list of conditions and the following disclaimer.
 13   
  *
 14   
  * 2. Redistributions in binary form must reproduce the above copyright
 15   
  *    notice, this list of conditions and the following disclaimer in
 16   
  *    the documentation and/or other materials provided with the
 17   
  *    distribution.
 18   
  *
 19   
  * 3. The end-user documentation included with the redistribution, if
 20   
  *    any, must include the following acknowlegement:
 21   
  *       "This product includes software developed by the
 22   
  *        Apache Software Foundation (http://www.apache.org/)."
 23   
  *    Alternately, this acknowlegement may appear in the software itself,
 24   
  *    if and wherever such third-party acknowlegements normally appear.
 25   
  *
 26   
  * 4. The names "Ant" and "Apache Software
 27   
  *    Foundation" must not be used to endorse or promote products derived
 28   
  *    from this software without prior written permission. For written
 29   
  *    permission, please contact apache@apache.org.
 30   
  *
 31   
  * 5. Products derived from this software may not be called "Apache"
 32   
  *    nor may "Apache" appear in their names without prior written
 33   
  *    permission of the Apache Group.
 34   
  *
 35   
  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 36   
  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 37   
  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 38   
  * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 39   
  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 40   
  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 41   
  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 42   
  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 43   
  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 44   
  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 45   
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 46   
  * SUCH DAMAGE.
 47   
  * ====================================================================
 48   
  *
 49   
  * This software consists of voluntary contributions made by many
 50   
  * individuals on behalf of the Apache Software Foundation.  For more
 51   
  * information on the Apache Software Foundation, please see
 52   
  * <http://www.apache.org/>.
 53   
  */
 54   
 
 55   
 package org.apache.tools.ant.taskdefs;
 56   
 
 57   
 import java.io.BufferedReader;
 58   
 import java.io.ByteArrayOutputStream;
 59   
 import java.io.File;
 60   
 import java.io.IOException;
 61   
 import java.io.StringReader;
 62   
 import java.lang.reflect.InvocationTargetException;
 63   
 import java.lang.reflect.Method;
 64   
 import java.util.Vector;
 65   
 import org.apache.tools.ant.BuildException;
 66   
 import org.apache.tools.ant.Project;
 67   
 import org.apache.tools.ant.Task;
 68   
 import org.apache.tools.ant.taskdefs.condition.Os;
 69   
 import org.apache.tools.ant.types.Commandline;
 70   
 
 71   
 /**
 72   
  * Runs an external program.
 73   
  *
 74   
  * @author thomas.haas@softwired-inc.com
 75   
  * @author <a href="mailto:jtulley@novell.com">Jeff Tulley</a>
 76   
  *
 77   
  * @since Ant 1.2
 78   
  *
 79   
  * @version $Revision: 1.53 $
 80   
  */
 81   
 public class Execute {
 82   
 
 83   
     /** Invalid exit code. **/
 84   
     public static final int INVALID = Integer.MAX_VALUE;
 85   
 
 86   
     private String[] cmdl = null;
 87   
     private String[] env = null;
 88   
     private int exitValue = INVALID;
 89   
     private ExecuteStreamHandler streamHandler;
 90   
     private ExecuteWatchdog watchdog;
 91   
     private File workingDirectory = null;
 92   
     private Project project = null;
 93   
     private boolean newEnvironment = false;
 94   
 
 95   
     /** Controls whether the VM is used to launch commands, where possible */
 96   
     private boolean useVMLauncher = true;
 97   
 
 98   
     private static String antWorkingDirectory = System.getProperty("user.dir");
 99   
     private static CommandLauncher vmLauncher = null;
 100   
     private static CommandLauncher shellLauncher = null;
 101   
     private static Vector procEnvironment = null;
 102   
 
 103   
     /** Used to destroy processes when the VM exits. */
 104   
     private static ProcessDestroyer processDestroyer = new ProcessDestroyer();
 105   
 
 106   
     /**
 107   
      * Builds a command launcher for the OS and JVM we are running under
 108   
      */
 109   
     static {
 110   
         // Try using a JDK 1.3 launcher
 111  1
         try {
 112  1
             vmLauncher = new Java13CommandLauncher();
 113   
         } catch (NoSuchMethodException exc) {
 114   
             // Ignore and keep trying
 115   
         }
 116   
 
 117  1
         if (Os.isFamily("mac")) {
 118   
             // Mac
 119  0
             shellLauncher = new MacCommandLauncher(new CommandLauncher());
 120  1
         } else if (Os.isFamily("os/2")) {
 121   
             // OS/2
 122  0
             shellLauncher = new OS2CommandLauncher(new CommandLauncher());
 123  1
         } else if (Os.isFamily("windows")) {
 124   
             // Windows.  Need to determine which JDK we're running in
 125   
 
 126  0
             CommandLauncher baseLauncher;
 127  0
             if (System.getProperty("java.version").startsWith("1.1")) {
 128   
                 // JDK 1.1
 129  0
                 baseLauncher = new Java11CommandLauncher();
 130   
             } else {
 131   
                 // JDK 1.2
 132  0
                 baseLauncher = new CommandLauncher();
 133   
             }
 134   
 
 135  0
             if (!Os.isFamily("win9x")) {
 136   
                 // Windows XP/2000/NT
 137  0
                 shellLauncher = new WinNTCommandLauncher(baseLauncher);
 138   
             } else {
 139   
                 // Windows 98/95 - need to use an auxiliary script
 140  0
                 shellLauncher
 141   
                     = new ScriptCommandLauncher("bin/antRun.bat", baseLauncher);
 142   
             }
 143  1
         } else if (Os.isFamily("netware")) {
 144   
             // NetWare.  Need to determine which JDK we're running in
 145  0
             CommandLauncher baseLauncher;
 146  0
             if (System.getProperty("java.version").startsWith("1.1")) {
 147   
                 // JDK 1.1
 148  0
                 baseLauncher = new Java11CommandLauncher();
 149   
             } else {
 150   
                 // JDK 1.2
 151  0
                 baseLauncher = new CommandLauncher();
 152   
             }
 153   
 
 154  0
             shellLauncher
 155   
                 = new PerlScriptCommandLauncher("bin/antRun.pl", baseLauncher);
 156   
         } else {
 157   
             // Generic
 158  1
             shellLauncher = new ScriptCommandLauncher("bin/antRun",
 159   
                 new CommandLauncher());
 160   
         }
 161   
     }
 162   
 
 163   
     /**
 164   
      * Find the list of environment variables for this process.
 165   
      */
 166  2
     public static synchronized Vector getProcEnvironment() {
 167  2
         if (procEnvironment != null) {
 168  1
             return procEnvironment;
 169   
         }
 170   
 
 171  1
         procEnvironment = new Vector();
 172  1
         try {
 173  1
             ByteArrayOutputStream out = new ByteArrayOutputStream();
 174  1
             Execute exe = new Execute(new PumpStreamHandler(out));
 175  1
             exe.setCommandline(getProcEnvCommand());
 176   
             // Make sure we do not recurse forever
 177  1
             exe.setNewenvironment(true);
 178  1
             int retval = exe.execute();
 179  1
             if (retval != 0) {
 180   
                 // Just try to use what we got
 181   
             }
 182   
 
 183  1
             BufferedReader in =
 184   
                 new BufferedReader(new StringReader(toString(out)));
 185   
 
 186  1
             String var = null;
 187  1
             String line, lineSep = System.getProperty("line.separator");
 188  ?
             while ((line = in.readLine()) != null) {
 189  41
                 if (line.indexOf('=') == -1) {
 190   
                     // Chunk part of previous env var (UNIX env vars can
 191   
                     // contain embedded new lines).
 192  0
                     if (var == null) {
 193  0
                         var = lineSep + line;
 194   
                     } else {
 195  0
                         var += lineSep + line;
 196   
                     }
 197   
                 } else {
 198   
                     // New env var...append the previous one if we have it.
 199  41
                     if (var != null) {
 200  40
                         procEnvironment.addElement(var);
 201   
                     }
 202  41
                     var = line;
 203   
                 }
 204   
             }
 205   
             // Since we "look ahead" before adding, there's one last env var.
 206  1
             if (var != null) {
 207  1
                 procEnvironment.addElement(var);
 208   
             }
 209   
         } catch (java.io.IOException exc) {
 210  0
             exc.printStackTrace();
 211   
             // Just try to see how much we got
 212   
         }
 213  1
         return procEnvironment;
 214   
     }
 215   
 
 216  1
     private static String[] getProcEnvCommand() {
 217  1
         if (Os.isFamily("os/2")) {
 218   
             // OS/2 - use same mechanism as Windows 2000
 219  0
             String[] cmd = {"cmd", "/c", "set" };
 220  0
             return cmd;
 221  1
         } else if (Os.isFamily("windows")) {
 222   
             // Determine if we're running under XP/2000/NT or 98/95
 223  0
             if (!Os.isFamily("win9x")) {
 224   
                 // Windows XP/2000/NT
 225  0
                 String[] cmd = {"cmd", "/c", "set" };
 226  0
                 return cmd;
 227   
             } else {
 228   
                 // Windows 98/95
 229  0
                 String[] cmd = {"command.com", "/c", "set" };
 230  0
                 return cmd;
 231   
             }
 232  1
         } else if (Os.isFamily("z/os") || Os.isFamily("tandem") 
 233   
                    || Os.isFamily("unix")) {
 234   
             // On most systems one could use: /bin/sh -c env
 235   
 
 236   
             // Some systems have /bin/env, others /usr/bin/env, just try
 237  1
             String[] cmd = new String[1];
 238  1
             if (new File("/bin/env").canRead()) {
 239  1
                 cmd[0] = "/bin/env";
 240  0
             } else if (new File("/usr/bin/env").canRead()) {
 241  0
                 cmd[0] = "/usr/bin/env";
 242   
             } else {
 243   
                 // rely on PATH
 244  0
                 cmd[0] = "env";
 245   
             }
 246  1
             return cmd;
 247  0
         } else if (Os.isFamily("netware") || Os.isFamily("os/400")) {
 248   
             // rely on PATH
 249  0
             String[] cmd = {"env"};
 250  0
             return cmd;
 251   
         } else {
 252   
             // MAC OS 9 and previous
 253   
             // TODO: I have no idea how to get it, someone must fix it
 254  0
             String[] cmd = null;
 255  0
             return cmd;
 256   
         }
 257   
     }
 258   
 
 259   
     /**
 260   
      * ByteArrayOutputStream#toString doesn't seem to work reliably on
 261   
      * OS/390, at least not the way we use it in the execution
 262   
      * context.
 263   
      *
 264   
      * @since Ant 1.5
 265   
      */
 266  1
     public static String toString(ByteArrayOutputStream bos) {
 267  1
         if (Os.isFamily("z/os")) {
 268  0
             try {
 269  0
                 return bos.toString("Cp1047");
 270   
             } catch (java.io.UnsupportedEncodingException e) {
 271   
             }
 272  1
         } else if (Os.isFamily("os/400")) {
 273  0
             try {
 274  0
                 return bos.toString("Cp500");
 275   
             } catch (java.io.UnsupportedEncodingException e) {
 276   
             }
 277   
         }
 278  1
         return bos.toString();
 279   
     }
 280   
 
 281   
     /**
 282   
      * Creates a new execute object using <code>PumpStreamHandler</code> for
 283   
      * stream handling.
 284   
      */
 285  0
     public Execute() {
 286  0
         this(new PumpStreamHandler(), null);
 287   
     }
 288   
 
 289   
 
 290   
     /**
 291   
      * Creates a new execute object.
 292   
      *
 293   
      * @param streamHandler the stream handler used to handle the input and
 294   
      *        output streams of the subprocess.
 295   
      */
 296  15
     public Execute(ExecuteStreamHandler streamHandler) {
 297  15
         this(streamHandler, null);
 298   
     }
 299   
 
 300   
     /**
 301   
      * Creates a new execute object.
 302   
      *
 303   
      * @param streamHandler the stream handler used to handle the input and
 304   
      *        output streams of the subprocess.
 305   
      * @param watchdog a watchdog for the subprocess or <code>null</code> to
 306   
      *        to disable a timeout for the subprocess.
 307   
      */
 308  30
     public Execute(ExecuteStreamHandler streamHandler,
 309   
                    ExecuteWatchdog watchdog) {
 310  30
         this.streamHandler = streamHandler;
 311  30
         this.watchdog = watchdog;
 312   
     }
 313   
 
 314   
 
 315   
     /**
 316   
      * Returns the commandline used to create a subprocess.
 317   
      *
 318   
      * @return the commandline used to create a subprocess
 319   
      */
 320  30
     public String[] getCommandline() {
 321  30
         return cmdl;
 322   
     }
 323   
 
 324   
 
 325   
     /**
 326   
      * Sets the commandline of the subprocess to launch.
 327   
      *
 328   
      * @param commandline the commandline of the subprocess to launch
 329   
      */
 330  30
     public void setCommandline(String[] commandline) {
 331  30
         cmdl = commandline;
 332   
     }
 333   
 
 334   
     /**
 335   
      * Set whether to propagate the default environment or not.
 336   
      *
 337   
      * @param newenv whether to propagate the process environment.
 338   
      */
 339  16
     public void setNewenvironment(boolean newenv) {
 340  16
         newEnvironment = newenv;
 341   
     }
 342   
 
 343   
     /**
 344   
      * Returns the environment used to create a subprocess.
 345   
      *
 346   
      * @return the environment used to create a subprocess
 347   
      */
 348  30
     public String[] getEnvironment() {
 349  30
         if (env == null || newEnvironment) {
 350  30
             return env;
 351   
         }
 352  0
         return patchEnvironment();
 353   
     }
 354   
 
 355   
 
 356   
     /**
 357   
      * Sets the environment variables for the subprocess to launch.
 358   
      *
 359   
      * @param env array of Strings, each element of which has
 360   
      * an environment variable settings in format <em>key=value</em>
 361   
      */
 362  15
     public void setEnvironment(String[] env) {
 363  15
         this.env = env;
 364   
     }
 365   
 
 366   
     /**
 367   
      * Sets the working directory of the process to execute.
 368   
      *
 369   
      * <p>This is emulated using the antRun scripts unless the OS is
 370   
      * Windows NT in which case a cmd.exe is spawned,
 371   
      * or MRJ and setting user.dir works, or JDK 1.3 and there is
 372   
      * official support in java.lang.Runtime.
 373   
      *
 374   
      * @param wd the working directory of the process.
 375   
      */
 376  15
     public void setWorkingDirectory(File wd) {
 377  15
         if (wd == null || wd.getAbsolutePath().equals(antWorkingDirectory)) {
 378  0
             workingDirectory = null;
 379   
         } else {
 380  15
             workingDirectory = wd;
 381   
         }
 382   
     }
 383   
 
 384   
     /**
 385   
      * Set the name of the antRun script using the project's value.
 386   
      *
 387   
      * @param project the current project.
 388   
      */
 389  29
     public void setAntRun(Project project) throws BuildException {
 390  29
         this.project = project;
 391   
     }
 392   
 
 393   
     /**
 394   
      * Launch this execution through the VM, where possible, rather than through
 395   
      * the OS's shell. In some cases and operating systems using the shell will
 396   
      * allow the shell to perform additional processing such as associating an
 397   
      * executable with a script, etc
 398   
      *
 399   
      * @param useVMLauncher true if exec should launch through thge VM,
 400   
      *                   false if the shell should be used to launch the
 401   
      *                   command.
 402   
      */
 403  5
     public void setVMLauncher(boolean useVMLauncher) {
 404  5
         this.useVMLauncher = useVMLauncher;
 405   
     }
 406   
 
 407   
     /**
 408   
      * Creates a process that runs a command.
 409   
      *
 410   
      * @param project the Project, only used for logging purposes, may be null.
 411   
      * @param command the command to run
 412   
      * @param env the environment for the command
 413   
      * @param dir the working directory for the command
 414   
      * @param useVM use the built-in exec command for JDK 1.3 if available.
 415   
      *
 416   
      * @since Ant 1.5
 417   
      */
 418  30
     public static Process launch(Project project, String[] command,
 419   
                                  String[] env, File dir, boolean useVM)
 420   
         throws IOException {
 421  30
         CommandLauncher launcher
 422   
             = vmLauncher != null ? vmLauncher : shellLauncher;
 423  30
         if (!useVM) {
 424  0
             launcher = shellLauncher;
 425   
         }
 426   
 
 427  30
         return launcher.exec(project, command, env, dir);
 428   
     }
 429   
 
 430   
     /**
 431   
      * Runs a process defined by the command line and returns its exit status.
 432   
      *
 433   
      * @return the exit status of the subprocess or <code>INVALID</code>
 434   
      * @exception java.io.IOException The exception is thrown, if launching
 435   
      *            of the subprocess failed
 436   
      */
 437  30
     public int execute() throws IOException {
 438  30
         final Process process = launch(project, getCommandline(),
 439   
                                        getEnvironment(), workingDirectory,
 440   
                                        useVMLauncher);
 441   
 
 442  30
         try {
 443  30
             streamHandler.setProcessInputStream(process.getOutputStream());
 444  30
             streamHandler.setProcessOutputStream(process.getInputStream());
 445  30
             streamHandler.setProcessErrorStream(process.getErrorStream());
 446   
         } catch (IOException e) {
 447  0
             process.destroy();
 448  0
             throw e;
 449   
         }
 450  30
         streamHandler.start();
 451   
 
 452   
         // add the process to the list of those to destroy if the VM exits
 453   
         //
 454  30
         processDestroyer.add(process);
 455   
 
 456  30
         if (watchdog != null) {
 457  0
             watchdog.start(process);
 458   
         }
 459  30
         waitFor(process);
 460   
 
 461   
         // remove the process to the list of those to destroy if the VM exits
 462   
         //
 463  30
         processDestroyer.remove(process);
 464   
 
 465  30
         if (watchdog != null) {
 466  0
             watchdog.stop();
 467   
         }
 468  30
         streamHandler.stop();
 469  30
         if (watchdog != null) {
 470  0
             watchdog.checkException();
 471   
         }
 472  30
         return getExitValue();
 473   
     }
 474   
 
 475  30
     protected void waitFor(Process process) {
 476  30
         try {
 477  30
             process.waitFor();
 478  30
             setExitValue(process.exitValue());
 479   
         } catch (InterruptedException e) {
 480  0
             process.destroy();
 481   
         }
 482   
     }
 483   
 
 484  30
     protected void setExitValue(int value) {
 485  30
         exitValue = value;
 486   
     }
 487   
 
 488   
     /**
 489   
      * query the exit value of the process.
 490   
      * @return the exit value, 1 if the process was killed,
 491   
      * or Project.INVALID if no exit value has been received
 492   
      */
 493  30
     public int getExitValue() {
 494  30
         return exitValue;
 495   
     }
 496   
 
 497   
     /**
 498   
      * test for an untimely death of the process
 499   
      * @return true iff a watchdog had to kill the process
 500   
      * @since Ant 1.5
 501   
      */
 502  15
     public boolean killedProcess() {
 503  15
         return watchdog != null && watchdog.killedProcess();
 504   
     }
 505   
 
 506   
     /**
 507   
      * Patch the current environment with the new values from the user.
 508   
      * @return the patched environment
 509   
      */
 510  0
     private String[] patchEnvironment() {
 511  0
         Vector osEnv = (Vector) getProcEnvironment().clone();
 512  0
         for (int i = 0; i < env.length; i++) {
 513  0
             int pos = env[i].indexOf('=');
 514   
             // Get key including "="
 515  0
             String key = env[i].substring(0, pos + 1);
 516  0
             int size = osEnv.size();
 517  0
             for (int j = 0; j < size; j++) {
 518  0
                 if (((String) osEnv.elementAt(j)).startsWith(key)) {
 519  0
                     osEnv.removeElementAt(j);
 520  0
                     break;
 521   
                 }
 522   
             }
 523  0
             osEnv.addElement(env[i]);
 524   
         }
 525  0
         String[] result = new String[osEnv.size()];
 526  0
         osEnv.copyInto(result);
 527  0
         return result;
 528   
     }
 529   
 
 530   
     /**
 531   
      * A utility method that runs an external command.  Writes the output and
 532   
      * error streams of the command to the project log.
 533   
      *
 534   
      * @param task      The task that the command is part of.  Used for logging
 535   
      * @param cmdline   The command to execute.
 536   
      *
 537   
      * @throws BuildException if the command does not return 0.
 538   
      */
 539  14
     public static void runCommand(Task task, String[] cmdline)
 540   
         throws BuildException {
 541  14
         try {
 542  14
             task.log(Commandline.describeCommand(cmdline),
 543   
                      Project.MSG_VERBOSE);
 544  14
             Execute exe = new Execute(new LogStreamHandler(task,
 545   
                                                            Project.MSG_INFO,
 546   
                                                            Project.MSG_ERR));
 547  14
             exe.setAntRun(task.getProject());
 548  14
             exe.setCommandline(cmdline);
 549  14
             int retval = exe.execute();
 550  14
             if (retval != 0) {
 551  0
                 throw new BuildException(cmdline[0]
 552   
                     + " failed with return code " + retval, task.getLocation());
 553   
             }
 554   
         } catch (java.io.IOException exc) {
 555  0
             throw new BuildException("Could not launch " + cmdline[0] + ": "
 556   
                 + exc, task.getLocation());
 557   
         }
 558   
     }
 559   
 
 560   
     /**
 561   
      * A command launcher for a particular JVM/OS platform.  This class is
 562   
      * a general purpose command launcher which can only launch commands in
 563   
      * the current working directory.
 564   
      */
 565   
     private static class CommandLauncher {
 566   
         /**
 567   
          * Launches the given command in a new process.
 568   
          *
 569   
          * @param project       The project that the command is part of
 570   
          * @param cmd           The command to execute
 571   
          * @param env           The environment for the new process.  If null,
 572   
          *                      the environment of the current proccess is used.
 573   
          */
 574  0
         public Process exec(Project project, String[] cmd, String[] env)
 575   
              throws IOException {
 576  0
             if (project != null) {
 577  0
                 project.log("Execute:CommandLauncher: " +
 578   
                             Commandline.describeCommand(cmd),
 579   
                             Project.MSG_DEBUG);
 580   
             }
 581  0
             return Runtime.getRuntime().exec(cmd, env);
 582   
         }
 583   
 
 584   
         /**
 585   
          * Launches the given command in a new process, in the given working
 586   
          * directory.
 587   
          *
 588   
          * @param project       The project that the command is part of
 589   
          * @param cmd           The command to execute
 590   
          * @param env           The environment for the new process.  If null,
 591   
          *                      the environment of the current proccess is used.
 592   
          * @param workingDir    The directory to start the command in.  If null,
 593   
          *                      the current directory is used
 594   
          */
 595  0
         public Process exec(Project project, String[] cmd, String[] env,
 596   
                             File workingDir) throws IOException {
 597  0
             if (workingDir == null) {
 598  0
                 return exec(project, cmd, env);
 599   
             }
 600  0
             throw new IOException("Cannot execute a process in different "
 601   
                 + "directory under this JVM");
 602   
         }
 603   
     }
 604   
 
 605   
     /**
 606   
      * A command launcher for JDK/JRE 1.1 under Windows.  Fixes quoting problems
 607   
      * in Runtime.exec().  Can only launch commands in the current working
 608   
      * directory
 609   
      */
 610   
     private static class Java11CommandLauncher extends CommandLauncher {
 611   
         /**
 612   
          * Launches the given command in a new process.  Needs to quote
 613   
          * arguments
 614   
          */
 615  0
         public Process exec(Project project, String[] cmd, String[] env)
 616   
              throws IOException {
 617   
             // Need to quote arguments with spaces, and to escape
 618   
             // quote characters
 619  0
             String[] newcmd = new String[cmd.length];
 620  0
             for (int i = 0; i < cmd.length; i++) {
 621  0
                 newcmd[i] = Commandline.quoteArgument(cmd[i]);
 622   
             }
 623  0
             if (project != null) {
 624  0
                 project.log("Execute:Java11CommandLauncher: " +
 625   
                             Commandline.describeCommand(newcmd),
 626   
                             Project.MSG_DEBUG);
 627   
             }
 628  0
             return Runtime.getRuntime().exec(newcmd, env);
 629   
         }
 630   
     }
 631   
 
 632   
     /**
 633   
      * A command launcher for JDK/JRE 1.3 (and higher).  Uses the built-in
 634   
      * Runtime.exec() command
 635   
      */
 636   
     private static class Java13CommandLauncher extends CommandLauncher {
 637  1
         public Java13CommandLauncher() throws NoSuchMethodException {
 638   
             // Locate method Runtime.exec(String[] cmdarray,
 639   
             //                            String[] envp, File dir)
 640  1
             _execWithCWD = Runtime.class.getMethod("exec",
 641   
                 new Class[] {String[].class, String[].class, File.class});
 642   
         }
 643   
 
 644   
         /**
 645   
          * Launches the given command in a new process, in the given working
 646   
          * directory
 647   
          */
 648  30
         public Process exec(Project project, String[] cmd, String[] env,
 649   
                             File workingDir) throws IOException {
 650  30
             try {
 651  30
                 if (project != null) {
 652  29
                     project.log("Execute:Java13CommandLauncher: " +
 653   
                                 Commandline.describeCommand(cmd),
 654   
                                 Project.MSG_DEBUG);
 655   
                 }
 656  30
                 Object[] arguments = { cmd, env, workingDir };
 657  30
                 return (Process) _execWithCWD.invoke(Runtime.getRuntime(),
 658   
                                                      arguments);
 659   
             } catch (InvocationTargetException exc) {
 660  0
                 Throwable realexc = exc.getTargetException();
 661  0
                 if (realexc instanceof ThreadDeath) {
 662  0
                     throw (ThreadDeath) realexc;
 663  0
                 } else if (realexc instanceof IOException) {
 664  0
                     throw (IOException) realexc;
 665   
                 } else {
 666  0
                     throw new BuildException("Unable to execute command",
 667   
                                              realexc);
 668   
                 }
 669   
             } catch (Exception exc) {
 670   
                 // IllegalAccess, IllegalArgument, ClassCast
 671  0
                 throw new BuildException("Unable to execute command", exc);
 672   
             }
 673   
         }
 674   
 
 675   
         private Method _execWithCWD;
 676   
     }
 677   
 
 678   
     /**
 679   
      * A command launcher that proxies another command launcher.
 680   
      *
 681   
      * Sub-classes override exec(args, env, workdir)
 682   
      */
 683   
     private static class CommandLauncherProxy extends CommandLauncher {
 684  1
         CommandLauncherProxy(CommandLauncher launcher) {
 685  1
             _launcher = launcher;
 686   
         }
 687   
 
 688   
         /**
 689   
          * Launches the given command in a new process.  Delegates this
 690   
          * method to the proxied launcher
 691   
          */
 692  0
         public Process exec(Project project, String[] cmd, String[] env)
 693   
             throws IOException {
 694  0
             return _launcher.exec(project, cmd, env);
 695   
         }
 696   
 
 697   
         private CommandLauncher _launcher;
 698   
     }
 699   
 
 700   
     /**
 701   
      * A command launcher for OS/2 that uses 'cmd.exe' when launching
 702   
      * commands in directories other than the current working
 703   
      * directory.
 704   
      *
 705   
      * <p>Unlike Windows NT and friends, OS/2's cd doesn't support the
 706   
      * /d switch to change drives and directories in one go.</p>
 707   
      */
 708   
     private static class OS2CommandLauncher extends CommandLauncherProxy {
 709  0
         OS2CommandLauncher(CommandLauncher launcher) {
 710  0
             super(launcher);
 711   
         }
 712   
 
 713   
         /**
 714   
          * Launches the given command in a new process, in the given working
 715   
          * directory.
 716   
          */
 717  0
         public Process exec(Project project, String[] cmd, String[] env,
 718   
                             File workingDir) throws IOException {
 719  0
             File commandDir = workingDir;
 720  0
             if (workingDir == null) {
 721  0
                 if (project != null) {
 722  0
                     commandDir = project.getBaseDir();
 723   
                 } else {
 724  0
                     return exec(project, cmd, env);
 725   
                 }
 726   
             }
 727   
 
 728   
             // Use cmd.exe to change to the specified drive and
 729   
             // directory before running the command
 730  0
             final int preCmdLength = 7;
 731  0
             final String cmdDir = commandDir.getAbsolutePath();
 732  0
             String[] newcmd = new String[cmd.length + preCmdLength];
 733  0
             newcmd[0] = "cmd";
 734  0
             newcmd[1] = "/c";
 735  0
             newcmd[2] = cmdDir.substring(0, 2);
 736  0
             newcmd[3] = "&&";
 737  0
             newcmd[4] = "cd";
 738  0
             newcmd[5] = cmdDir.substring(2);
 739  0
             newcmd[6] = "&&";
 740  0
             System.arraycopy(cmd, 0, newcmd, preCmdLength, cmd.length);
 741   
 
 742  0
             return exec(project, newcmd, env);
 743   
         }
 744   
     }
 745   
 
 746   
     /**
 747   
      * A command launcher for Windows XP/2000/NT that uses 'cmd.exe' when
 748   
      * launching commands in directories other than the current working
 749   
      * directory.
 750   
      */
 751   
     private static class WinNTCommandLauncher extends CommandLauncherProxy {
 752  0
         WinNTCommandLauncher(CommandLauncher launcher) {
 753  0
             super(launcher);
 754   
         }
 755   
 
 756   
         /**
 757   
          * Launches the given command in a new process, in the given working
 758   
          * directory.
 759   
          */
 760  0
         public Process exec(Project project, String[] cmd, String[] env,
 761   
                             File workingDir) throws IOException {
 762  0
             File commandDir = workingDir;
 763  0
             if (workingDir == null) {
 764  0
                 if (project != null) {
 765  0
                     commandDir = project.getBaseDir();
 766   
                 } else {
 767  0
                     return exec(project, cmd, env);
 768   
                 }
 769   
             }
 770   
 
 771   
             // Use cmd.exe to change to the specified directory before running
 772   
             // the command
 773  0
             final int preCmdLength = 6;
 774  0
             String[] newcmd = new String[cmd.length + preCmdLength];
 775  0
             newcmd[0] = "cmd";
 776  0
             newcmd[1] = "/c";
 777  0
             newcmd[2] = "cd";
 778  0
             newcmd[3] = "/d";
 779  0
             newcmd[4] = commandDir.getAbsolutePath();
 780  0
             newcmd[5] = "&&";
 781  0
             System.arraycopy(cmd, 0, newcmd, preCmdLength, cmd.length);
 782   
 
 783  0
             return exec(project, newcmd, env);
 784   
         }
 785   
     }
 786   
 
 787   
     /**
 788   
      * A command launcher for Mac that uses a dodgy mechanism to change
 789   
      * working directory before launching commands.
 790   
      */
 791   
     private static class MacCommandLauncher extends CommandLauncherProxy {
 792  0
         MacCommandLauncher(CommandLauncher launcher) {
 793  0
             super(launcher);
 794   
         }
 795   
 
 796   
         /**
 797   
          * Launches the given command in a new process, in the given working
 798   
          * directory
 799   
          */
 800  0
         public Process exec(Project project, String[] cmd, String[] env,
 801   
                             File workingDir) throws IOException {
 802  0
             if (workingDir == null) {
 803  0
                 return exec(project, cmd, env);
 804   
             }
 805   
 
 806  0
             System.getProperties().put("user.dir", workingDir.getAbsolutePath());
 807  0
             try {
 808  0
                 return exec(project, cmd, env);
 809   
             } finally {
 810  0
                 System.getProperties().put("user.dir", antWorkingDirectory);
 811   
             }
 812   
         }
 813   
     }
 814   
 
 815   
     /**
 816   
      * A command launcher that uses an auxiliary script to launch commands
 817   
      * in directories other than the current working directory.
 818   
      */
 819   
     private static class ScriptCommandLauncher extends CommandLauncherProxy {
 820  1
         ScriptCommandLauncher(String script, CommandLauncher launcher) {
 821  1
             super(launcher);
 822  1
             _script = script;
 823   
         }
 824   
 
 825   
         /**
 826   
          * Launches the given command in a new process, in the given working
 827   
          * directory
 828   
          */
 829  0
         public Process exec(Project project, String[] cmd, String[] env,
 830   
                             File workingDir) throws IOException {
 831  0
             if (project == null) {
 832  0
                 if (workingDir == null) {
 833  0
                     return exec(project, cmd, env);
 834   
                 }
 835  0
                 throw new IOException("Cannot locate antRun script: "
 836   
                     + "No project provided");
 837   
             }
 838   
 
 839   
             // Locate the auxiliary script
 840  0
             String antHome = project.getProperty("ant.home");
 841  0
             if (antHome == null) {
 842  0
                 throw new IOException("Cannot locate antRun script: "
 843   
                     + "Property 'ant.home' not found");
 844   
             }
 845  0
             String antRun = project.resolveFile(antHome + File.separator + _script).toString();
 846   
 
 847   
             // Build the command
 848  0
             File commandDir = workingDir;
 849  0
             if (workingDir == null && project != null) {
 850  0
                 commandDir = project.getBaseDir();
 851   
             }
 852   
 
 853  0
             String[] newcmd = new String[cmd.length + 2];
 854  0
             newcmd[0] = antRun;
 855  0
             newcmd[1] = commandDir.getAbsolutePath();
 856  0
             System.arraycopy(cmd, 0, newcmd, 2, cmd.length);
 857   
 
 858  0
             return exec(project, newcmd, env);
 859   
         }
 860   
 
 861   
         private String _script;
 862   
     }
 863   
 
 864   
     /**
 865   
      * A command launcher that uses an auxiliary perl script to launch commands
 866   
      * in directories other than the current working directory.
 867   
      */
 868   
     private static class PerlScriptCommandLauncher
 869   
         extends CommandLauncherProxy {
 870  0
         PerlScriptCommandLauncher(String script, CommandLauncher launcher) {
 871  0
             super(launcher);
 872  0
             _script = script;
 873   
         }
 874   
 
 875   
         /**
 876   
          * Launches the given command in a new process, in the given working
 877   
          * directory
 878   
          */
 879  0
         public Process exec(Project project, String[] cmd, String[] env,
 880   
                             File workingDir) throws IOException {
 881  0
             if (project == null) {
 882  0
                 if (workingDir == null) {
 883  0
                     return exec(project, cmd, env);
 884   
                 }
 885  0
                 throw new IOException("Cannot locate antRun script: "
 886   
                     + "No project provided");
 887   
             }
 888   
 
 889   
             // Locate the auxiliary script
 890  0
             String antHome = project.getProperty("ant.home");
 891  0
             if (antHome == null) {
 892  0
                 throw new IOException("Cannot locate antRun script: "
 893   
                     + "Property 'ant.home' not found");
 894   
             }
 895  0
             String antRun = project.resolveFile(antHome + File.separator + _script).toString();
 896   
 
 897   
             // Build the command
 898  0
             File commandDir = workingDir;
 899  0
             if (workingDir == null && project != null) {
 900  0
                 commandDir = project.getBaseDir();
 901   
             }
 902   
 
 903  0
             String[] newcmd = new String[cmd.length + 3];
 904  0
             newcmd[0] = "perl";
 905  0
             newcmd[1] = antRun;
 906  0
             newcmd[2] = commandDir.getAbsolutePath();
 907  0
             System.arraycopy(cmd, 0, newcmd, 3, cmd.length);
 908   
 
 909  0
             return exec(project, newcmd, env);
 910   
         }
 911   
 
 912   
         private String _script;
 913   
     }
 914   
 }
 915