Clover coverage report - Ant Coverage
Coverage timestamp: Tue Apr 8 2003 20:43:55 EST
file stats: LOC: 475   Methods: 33
NCLOC: 220   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
ExecTask.java 36.8% 55.3% 51.5% 50.3%
 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.File;
 58   
 import java.io.IOException;
 59   
 import org.apache.tools.ant.BuildException;
 60   
 import org.apache.tools.ant.Project;
 61   
 import org.apache.tools.ant.Task;
 62   
 import org.apache.tools.ant.types.Commandline;
 63   
 import org.apache.tools.ant.types.Environment;
 64   
 import org.apache.tools.ant.util.FileUtils;
 65   
 
 66   
 /**
 67   
  * Executes a given command if the os platform is appropriate.
 68   
  *
 69   
  * @author duncan@x180.com
 70   
  * @author rubys@us.ibm.com
 71   
  * @author thomas.haas@softwired-inc.com
 72   
  * @author Stefan Bodewig
 73   
  * @author <a href="mailto:mariusz@rakiura.org">Mariusz Nowostawski</a>
 74   
  *
 75   
  * @since Ant 1.2
 76   
  *
 77   
  * @ant.task category="control"
 78   
  */
 79   
 public class ExecTask extends Task {
 80   
 
 81   
     private String os;
 82   
     
 83   
     private File dir;
 84   
     protected boolean failOnError = false;
 85   
     protected boolean newEnvironment = false;
 86   
     private Long timeout = null;
 87   
     private Environment env = new Environment();
 88   
     protected Commandline cmdl = new Commandline();
 89   
     private String resultProperty;
 90   
     private boolean failIfExecFails = true;
 91   
     private String executable;
 92   
     private boolean resolveExecutable = false;
 93   
 
 94   
     private Redirector redirector = new Redirector(this);
 95   
     
 96   
     /**
 97   
      * Controls whether the VM (1.3 and above) is used to execute the
 98   
      * command
 99   
      */
 100   
     private boolean vmLauncher = true;
 101   
 
 102   
     /**
 103   
      * Timeout in milliseconds after which the process will be killed.
 104   
      *
 105   
      * @since Ant 1.5
 106   
      */
 107  0
     public void setTimeout(Long value) {
 108  0
         timeout = value;
 109   
     }
 110   
 
 111   
     /**
 112   
      * Timeout in milliseconds after which the process will be killed.
 113   
      */
 114  0
     public void setTimeout(Integer value) {
 115  0
         if (value == null) {
 116  0
             timeout = null;
 117   
         } else {
 118  0
             setTimeout(new Long(value.intValue()));
 119   
         }
 120   
     }
 121   
 
 122   
     /**
 123   
      * The command to execute.
 124   
      */
 125  6
     public void setExecutable(String value) {
 126  6
         this.executable = value;
 127  6
         cmdl.setExecutable(value);
 128   
     }
 129   
 
 130   
     /**
 131   
      * The working directory of the process.
 132   
      */
 133  0
     public void setDir(File d) {
 134  0
         this.dir = d;
 135   
     }
 136   
 
 137   
     /**
 138   
      * List of operating systems on which the command may be executed.
 139   
      */
 140  1
     public void setOs(String os) {
 141  1
         this.os = os;
 142   
     }
 143   
 
 144   
     /**
 145   
      * @ant.attribute ignore="true"
 146   
      */
 147  0
     public void setCommand(Commandline cmdl) {
 148  0
         log("The command attribute is deprecated. " +
 149   
             "Please use the executable attribute and nested arg elements.",
 150   
             Project.MSG_WARN);
 151  0
         this.cmdl = cmdl;
 152   
     }
 153   
 
 154   
     /**
 155   
      * File the output of the process is redirected to. If error is not 
 156   
      * redirected, it too will appear in the output
 157   
      */
 158  0
     public void setOutput(File out) {
 159  0
         redirector.setOutput(out);
 160   
     }
 161   
 
 162   
     /**
 163   
      * Set the input to use for the task
 164   
      */
 165  0
     public void setInput(File input) {
 166  0
         redirector.setInput(input);
 167   
     }
 168   
 
 169   
     /**
 170   
      * Set the string to use as input
 171   
      *
 172   
      * @param inputString the string which is used as the input source
 173   
      */
 174  0
     public void setInputString(String inputString) {
 175  0
         redirector.setInputString(inputString);
 176   
     }
 177   
     
 178   
     /**
 179   
      * Controls whether error output of exec is logged. This is only useful
 180   
      * when output is being redirected and error output is desired in the
 181   
      * Ant log
 182   
      */
 183  0
     public void setLogError(boolean logError) {
 184  0
         redirector.setLogError(logError);
 185   
     }
 186   
     
 187   
     /**
 188   
      * File the error stream of the process is redirected to.
 189   
      *
 190   
      * @since ant 1.6
 191   
      */
 192  0
     public void setError(File error) {
 193  0
         redirector.setError(error);
 194   
     }
 195   
 
 196   
     /**
 197   
      * Property name whose value should be set to the output of
 198   
      * the process.
 199   
      */
 200  1
     public void setOutputproperty(String outputProp) {
 201  1
         redirector.setOutputProperty(outputProp);
 202   
     }
 203   
 
 204   
     /**
 205   
      * Property name whose value should be set to the error of
 206   
      * the process.
 207   
      *
 208   
      * @since ant 1.6
 209   
      */
 210  0
     public void setErrorProperty(String errorProperty) {
 211  0
         redirector.setErrorProperty(errorProperty);
 212   
     }
 213   
 
 214   
     /**
 215   
      * Fail if the command exits with a non-zero return code.
 216   
      */
 217  5
     public void setFailonerror(boolean fail) {
 218  5
         failOnError = fail;
 219   
     }
 220   
 
 221   
     /**
 222   
      * Do not propagate old environment when new environment variables are specified.
 223   
      */
 224  0
     public void setNewenvironment(boolean newenv) {
 225  0
         newEnvironment = newenv;
 226   
     }
 227   
 
 228   
     /**
 229   
      * Attempt to resolve the executable to a file
 230   
      */
 231  0
     public void setResolveExecutable(boolean resolveExecutable) {
 232  0
         this.resolveExecutable = resolveExecutable;
 233   
     }
 234   
     
 235   
     /**
 236   
      * Add an environment variable to the launched process.
 237   
      */
 238  0
     public void addEnv(Environment.Variable var) {
 239  0
         env.addVariable(var);
 240   
     }
 241   
 
 242   
     /**
 243   
      * Adds a command-line argument.
 244   
      */
 245  36
     public Commandline.Argument createArg() {
 246  36
         return cmdl.createArgument();
 247   
     }
 248   
 
 249   
     /**
 250   
      * The name of a property in which the return code of the
 251   
      * command should be stored. Only of interest if failonerror=false.
 252   
      *
 253   
      * @since Ant 1.5
 254   
      */
 255  1
     public void setResultProperty(String resultProperty) {
 256  1
         this.resultProperty = resultProperty;
 257   
     }
 258   
 
 259   
     /**
 260   
      * helper method to set result property to the
 261   
      * passed in value if appropriate
 262   
      */
 263  5
     protected void maybeSetResultPropertyValue(int result) {
 264  5
         String res = Integer.toString(result);
 265  5
         if (resultProperty != null) {
 266  0
             getProject().setNewProperty(resultProperty, res);
 267   
         }
 268   
     }
 269   
 
 270   
     /**
 271   
      * Stop the build if program cannot be started. Defaults to true.
 272   
      *
 273   
      * @since Ant 1.5
 274   
      */
 275  0
     public void setFailIfExecutionFails(boolean flag) {
 276  0
         failIfExecFails = flag;
 277   
     }
 278   
 
 279   
     /**
 280   
      * Whether output should be appended to or overwrite an existing file.
 281   
      * Defaults to false.
 282   
      *
 283   
      * @since 1.30, Ant 1.5
 284   
      */
 285  0
     public void setAppend(boolean append) {
 286  0
         redirector.setAppend(append);
 287   
     }
 288   
 
 289   
     
 290   
     
 291   
     /**
 292   
      * Attempt to figure out where the executable is so that we can feed 
 293   
      * the full path - first try basedir, then the exec dir and then
 294   
      * fallback to the straight executable name (i.e. on ther path)
 295   
      *
 296   
      * @return the executable as a full path if it can be determined.
 297   
      */
 298  6
     private String resolveExecutable() {
 299  6
         if (!resolveExecutable) {
 300  6
             return executable;
 301   
         }
 302   
         
 303   
         // try to find the executable
 304  0
         File executableFile = getProject().resolveFile(executable);
 305  0
         if (executableFile.exists()) {
 306  0
             return executableFile.getAbsolutePath();
 307   
         }
 308   
         
 309   
         // now try to resolve against the dir if given
 310  0
         if (dir != null) {
 311  0
             FileUtils fileUtils = FileUtils.newFileUtils();
 312  0
             executableFile = fileUtils.resolveFile(dir, executable);
 313  0
             if (executableFile.exists()) {
 314  0
                 return executableFile.getAbsolutePath();
 315   
             }
 316   
         }
 317   
 
 318   
         // couldn't find it - must be on path
 319  0
         return executable;            
 320   
     }
 321   
     
 322   
     /**
 323   
      * Do the work.
 324   
      */
 325  6
     public void execute() throws BuildException {
 326  6
         File savedDir = dir; // possibly altered in prepareExec
 327  6
         cmdl.setExecutable(resolveExecutable());
 328  6
         checkConfiguration();
 329  6
         if (isValidOs()) {
 330  5
             try {
 331  5
                 runExec(prepareExec());
 332   
             } finally {
 333  5
                 dir = savedDir;
 334   
             }
 335   
         }
 336   
     }
 337   
 
 338   
     /**
 339   
      * Has the user set all necessary attributes?
 340   
      */
 341  6
     protected void checkConfiguration() throws BuildException {
 342  6
         if (cmdl.getExecutable() == null) {
 343  0
             throw new BuildException("no executable specified", getLocation());
 344   
         }
 345  6
         if (dir != null && !dir.exists()) {
 346  0
             throw new BuildException("The directory you specified does not "
 347   
                                      + "exist");
 348   
         }
 349  6
         if (dir != null && !dir.isDirectory()) {
 350  0
             throw new BuildException("The directory you specified is not a "
 351   
                                      + "directory");
 352   
         }
 353   
     }
 354   
 
 355   
     /**
 356   
      * Is this the OS the user wanted?
 357   
      */
 358  6
     protected boolean isValidOs() {
 359   
         // test if os match
 360  6
         String myos = System.getProperty("os.name");
 361  6
         log("Current OS is " + myos, Project.MSG_VERBOSE);
 362  6
         if ((os != null) && (os.indexOf(myos) < 0)){
 363   
             // this command will be executed only on the specified OS
 364  1
             log("This OS, " + myos
 365   
                 + " was not found in the specified list of valid OSes: " + os,
 366   
                 Project.MSG_VERBOSE);
 367  1
             return false;
 368   
         }
 369  5
         return true;
 370   
     }
 371   
 
 372   
     /**
 373   
      * If true, launch new process with VM, otherwise use the OS's shell.
 374   
      */
 375  0
     public void setVMLauncher(boolean vmLauncher) {    
 376  0
         this.vmLauncher = vmLauncher;
 377   
     }
 378   
 
 379   
     /**
 380   
      * Create an Execute instance with the correct working directory set.
 381   
      */
 382  5
     protected Execute prepareExec() throws BuildException {
 383   
         // default directory to the project's base directory
 384  5
         if (dir == null) {
 385  5
             dir = getProject().getBaseDir();
 386   
         }
 387  5
         Execute exe = new Execute(createHandler(), createWatchdog());
 388  5
         exe.setAntRun(getProject());
 389  5
         exe.setWorkingDirectory(dir);
 390  5
         exe.setVMLauncher(vmLauncher);
 391  5
         String[] environment = env.getVariables();
 392  5
         if (environment != null) {
 393  0
             for (int i = 0; i < environment.length; i++) {
 394  0
                 log("Setting environment variable: " + environment[i],
 395   
                     Project.MSG_VERBOSE);
 396   
             }
 397   
         }
 398  5
         exe.setNewenvironment(newEnvironment);
 399  5
         exe.setEnvironment(environment);
 400  5
         return exe;
 401   
     }
 402   
 
 403   
     /**
 404   
      * A Utility method for this classes and subclasses to run an
 405   
      * Execute instance (an external command).
 406   
      */
 407  5
     protected final void runExecute(Execute exe) throws IOException {
 408  5
         int returnCode = -1; // assume the worst
 409   
 
 410  5
         returnCode = exe.execute();
 411   
         //test for and handle a forced process death
 412  5
         if (exe.killedProcess()) {
 413  0
             log("Timeout: killed the sub-process", Project.MSG_WARN);
 414   
         }
 415  5
         maybeSetResultPropertyValue(returnCode);
 416  5
         if (returnCode != 0) {
 417  0
             if (failOnError) {
 418  0
                 throw new BuildException(getTaskType() + " returned: " 
 419   
                     + returnCode, getLocation());
 420   
             } else {
 421  0
                 log("Result: " + returnCode, Project.MSG_ERR);
 422   
             }
 423   
         }
 424  5
         redirector.complete();
 425   
     }
 426   
 
 427   
     /**
 428   
      * Run the command using the given Execute instance. This may be
 429   
      * overidden by subclasses
 430   
      */
 431  5
     protected void runExec(Execute exe) throws BuildException {
 432   
         // show the command
 433  5
         log(cmdl.describeCommand(), Project.MSG_VERBOSE);
 434   
 
 435  5
         exe.setCommandline(cmdl.getCommandline());
 436  5
         try {
 437  5
             runExecute(exe);
 438   
         } catch (IOException e) {
 439  0
             if (failIfExecFails) {
 440  0
                 throw new BuildException("Execute failed: " + e.toString(), e,
 441   
                                          getLocation());
 442   
             } else {
 443  0
                 log("Execute failed: " + e.toString(), Project.MSG_ERR);
 444   
             }
 445   
         } finally {
 446   
             // close the output file if required
 447  5
             logFlush();
 448   
         }
 449   
     }
 450   
 
 451   
     /**
 452   
      * Create the StreamHandler to use with our Execute instance.
 453   
      */
 454  5
     protected ExecuteStreamHandler createHandler() throws BuildException {
 455  5
         return redirector.createHandler();
 456   
     }
 457   
 
 458   
     /**
 459   
      * Create the Watchdog to kill a runaway process.
 460   
      */
 461  5
     protected ExecuteWatchdog createWatchdog() throws BuildException {
 462  5
         if (timeout == null) {
 463  5
             return null;
 464   
         }
 465  0
         return new ExecuteWatchdog(timeout.longValue());
 466   
     }
 467   
 
 468   
     /**
 469   
      * Flush the output stream - if there is one.
 470   
      */
 471  5
     protected void logFlush() {
 472   
     }
 473   
 
 474   
 }
 475