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

Save This Page
Home » apache-ant-1.7.1-src » org.apache.tools » ant » taskdefs » [javadoc | source]