Save This Page
Home » apache-ant-1.8.1 » org.apache.tools » ant » [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;
   20   
   21   import java.io.File;
   22   import java.io.FileInputStream;
   23   import java.io.FileNotFoundException;
   24   import java.io.FileOutputStream;
   25   import java.io.IOException;
   26   import java.io.InputStream;
   27   import java.io.PrintStream;
   28   import java.util.Enumeration;
   29   import java.util.HashMap;
   30   import java.util.HashSet;
   31   import java.util.Iterator;
   32   import java.util.Map;
   33   import java.util.Properties;
   34   import java.util.Set;
   35   import java.util.Vector;
   36   
   37   import org.apache.tools.ant.input.DefaultInputHandler;
   38   import org.apache.tools.ant.input.InputHandler;
   39   import org.apache.tools.ant.launch.AntMain;
   40   import org.apache.tools.ant.util.ClasspathUtils;
   41   import org.apache.tools.ant.util.FileUtils;
   42   import org.apache.tools.ant.util.ProxySetup;
   43   
   44   
   45   /**
   46    * Command line entry point into Ant. This class is entered via the
   47    * canonical `public static void main` entry point and reads the
   48    * command line arguments. It then assembles and executes an Ant
   49    * project.
   50    * <p>
   51    * If you integrating Ant into some other tool, this is not the class
   52    * to use as an entry point. Please see the source code of this
   53    * class to see how it manipulates the Ant project classes.
   54    *
   55    */
   56   public class Main implements AntMain {
   57   
   58       /**
   59        * A Set of args are are handled by the launcher and should
   60        * not be seen by Main.
   61        */
   62       private static final Set LAUNCH_COMMANDS = new HashSet();
   63       static {
   64           LAUNCH_COMMANDS.add("-lib");
   65           LAUNCH_COMMANDS.add("-cp");
   66           LAUNCH_COMMANDS.add("-noclasspath");
   67           LAUNCH_COMMANDS.add("--noclasspath");
   68           LAUNCH_COMMANDS.add("-nouserlib");
   69           LAUNCH_COMMANDS.add("-main");
   70       }
   71   
   72       /** The default build file name. {@value} */
   73       public static final String DEFAULT_BUILD_FILENAME = "build.xml";
   74   
   75       /** Our current message output status. Follows Project.MSG_XXX. */
   76       private int msgOutputLevel = Project.MSG_INFO;
   77   
   78       /** File that we are using for configuration. */
   79       private File buildFile; /* null */
   80   
   81       /** Stream to use for logging. */
   82       private static PrintStream out = System.out;
   83   
   84       /** Stream that we are using for logging error messages. */
   85       private static PrintStream err = System.err;
   86   
   87       /** The build targets. */
   88       private Vector targets = new Vector();
   89   
   90       /** Set of properties that can be used by tasks. */
   91       private Properties definedProps = new Properties();
   92   
   93       /** Names of classes to add as listeners to project. */
   94       private Vector listeners = new Vector(1);
   95   
   96       /** File names of property files to load on startup. */
   97       private Vector propertyFiles = new Vector(1);
   98   
   99       /** Indicates whether this build is to support interactive input */
  100       private boolean allowInput = true;
  101   
  102       /** keep going mode */
  103       private boolean keepGoingMode = false;
  104   
  105       /**
  106        * The Ant logger class. There may be only one logger. It will have
  107        * the right to use the 'out' PrintStream. The class must implements the
  108        * BuildLogger interface.
  109        */
  110       private String loggerClassname = null;
  111   
  112       /**
  113        * The Ant InputHandler class.  There may be only one input
  114        * handler.
  115        */
  116       private String inputHandlerClassname = null;
  117   
  118       /**
  119        * Whether or not output to the log is to be unadorned.
  120        */
  121       private boolean emacsMode = false;
  122   
  123       /**
  124        * Whether or not this instance has successfully been
  125        * constructed and is ready to run.
  126        */
  127       private boolean readyToRun = false;
  128   
  129       /**
  130        * Whether or not we should only parse and display the project help
  131        * information.
  132        */
  133       private boolean projectHelp = false;
  134   
  135       /**
  136        * Whether or not a logfile is being used. This is used to
  137        * check if the output streams must be closed.
  138        */
  139       private static boolean isLogFileUsed = false;
  140   
  141       /**
  142        * optional thread priority
  143        */
  144       private Integer threadPriority = null;
  145   
  146       /**
  147        * proxy flag: default is false
  148        */
  149       private boolean proxy = false;
  150   
  151       /**
  152        * Prints the message of the Throwable if it (the message) is not
  153        * <code>null</code>.
  154        *
  155        * @param t Throwable to print the message of.
  156        *          Must not be <code>null</code>.
  157        */
  158       private static void printMessage(Throwable t) {
  159           String message = t.getMessage();
  160           if (message != null) {
  161               System.err.println(message);
  162           }
  163       }
  164   
  165       /**
  166        * Creates a new instance of this class using the
  167        * arguments specified, gives it any extra user properties which have been
  168        * specified, and then runs the build using the classloader provided.
  169        *
  170        * @param args Command line arguments. Must not be <code>null</code>.
  171        * @param additionalUserProperties Any extra properties to use in this
  172        *        build. May be <code>null</code>, which is the equivalent to
  173        *        passing in an empty set of properties.
  174        * @param coreLoader Classloader used for core classes. May be
  175        *        <code>null</code> in which case the system classloader is used.
  176        */
  177       public static void start(String[] args, Properties additionalUserProperties,
  178                                ClassLoader coreLoader) {
  179           Main m = new Main();
  180           m.startAnt(args, additionalUserProperties, coreLoader);
  181       }
  182   
  183       /**
  184        * Start Ant
  185        * @param args command line args
  186        * @param additionalUserProperties properties to set beyond those that
  187        *        may be specified on the args list
  188        * @param coreLoader - not used
  189        *
  190        * @since Ant 1.6
  191        */
  192       public void startAnt(String[] args, Properties additionalUserProperties,
  193                            ClassLoader coreLoader) {
  194   
  195           try {
  196               Diagnostics.validateVersion();
  197               processArgs(args);
  198           } catch (Throwable exc) {
  199               handleLogfile();
  200               printMessage(exc);
  201               exit(1);
  202               return;
  203           }
  204   
  205           if (additionalUserProperties != null) {
  206               for (Enumeration e = additionalUserProperties.keys();
  207                       e.hasMoreElements();) {
  208                   String key = (String) e.nextElement();
  209                   String property = additionalUserProperties.getProperty(key);
  210                   definedProps.put(key, property);
  211               }
  212           }
  213   
  214           // expect the worst
  215           int exitCode = 1;
  216           try {
  217               try {
  218                   runBuild(coreLoader);
  219                   exitCode = 0;
  220               } catch (ExitStatusException ese) {
  221                   exitCode = ese.getStatus();
  222                   if (exitCode != 0) {
  223                       throw ese;
  224                   }
  225               }
  226           } catch (BuildException be) {
  227               if (err != System.err) {
  228                   printMessage(be);
  229               }
  230           } catch (Throwable exc) {
  231               exc.printStackTrace();
  232               printMessage(exc);
  233           } finally {
  234               handleLogfile();
  235           }
  236           exit(exitCode);
  237       }
  238   
  239       /**
  240        * This operation is expected to call {@link System#exit(int)}, which
  241        * is what the base version does.
  242        * However, it is possible to do something else.
  243        * @param exitCode code to exit with
  244        */
  245       protected void exit(int exitCode) {
  246           System.exit(exitCode);
  247       }
  248   
  249       /**
  250        * Close logfiles, if we have been writing to them.
  251        *
  252        * @since Ant 1.6
  253        */
  254       private static void handleLogfile() {
  255           if (isLogFileUsed) {
  256               FileUtils.close(out);
  257               FileUtils.close(err);
  258           }
  259       }
  260   
  261       /**
  262        * Command line entry point. This method kicks off the building
  263        * of a project object and executes a build using either a given
  264        * target or the default target.
  265        *
  266        * @param args Command line arguments. Must not be <code>null</code>.
  267        */
  268       public static void main(String[] args) {
  269           start(args, null, null);
  270       }
  271   
  272       /**
  273        * Constructor used when creating Main for later arg processing
  274        * and startup
  275        */
  276       public Main() {
  277       }
  278   
  279       /**
  280        * Sole constructor, which parses and deals with command line
  281        * arguments.
  282        *
  283        * @param args Command line arguments. Must not be <code>null</code>.
  284        *
  285        * @exception BuildException if the specified build file doesn't exist
  286        *                           or is a directory.
  287        *
  288        * @deprecated since 1.6.x
  289        */
  290       protected Main(String[] args) throws BuildException {
  291           processArgs(args);
  292       }
  293   
  294       /**
  295        * Process command line arguments.
  296        * When ant is started from Launcher, launcher-only arguments do not get
  297        * passed through to this routine.
  298        *
  299        * @param args the command line arguments.
  300        *
  301        * @since Ant 1.6
  302        */
  303       private void processArgs(String[] args) {
  304           String searchForThis = null;
  305           boolean searchForFile = false;
  306           PrintStream logTo = null;
  307   
  308           // cycle through given args
  309   
  310           boolean justPrintUsage = false;
  311           boolean justPrintVersion = false;
  312           boolean justPrintDiagnostics = false;
  313   
  314           for (int i = 0; i < args.length; i++) {
  315               String arg = args[i];
  316   
  317               if (arg.equals("-help") || arg.equals("-h")) {
  318                   justPrintUsage = true;
  319               } else if (arg.equals("-version")) {
  320                   justPrintVersion = true;
  321               } else if (arg.equals("-diagnostics")) {
  322                   justPrintDiagnostics = true;
  323               } else if (arg.equals("-quiet") || arg.equals("-q")) {
  324                   msgOutputLevel = Project.MSG_WARN;
  325               } else if (arg.equals("-verbose") || arg.equals("-v")) {
  326                   msgOutputLevel = Project.MSG_VERBOSE;
  327               } else if (arg.equals("-debug") || arg.equals("-d")) {
  328                   msgOutputLevel = Project.MSG_DEBUG;
  329               } else if (arg.equals("-noinput")) {
  330                   allowInput = false;
  331               } else if (arg.equals("-logfile") || arg.equals("-l")) {
  332                   try {
  333                       File logFile = new File(args[i + 1]);
  334                       i++;
  335                       logTo = new PrintStream(new FileOutputStream(logFile));
  336                       isLogFileUsed = true;
  337                   } catch (IOException ioe) {
  338                       String msg = "Cannot write on the specified log file. "
  339                           + "Make sure the path exists and you have write "
  340                           + "permissions.";
  341                       throw new BuildException(msg);
  342                   } catch (ArrayIndexOutOfBoundsException aioobe) {
  343                       String msg = "You must specify a log file when "
  344                           + "using the -log argument";
  345                       throw new BuildException(msg);
  346                   }
  347               } else if (arg.equals("-buildfile") || arg.equals("-file")
  348                          || arg.equals("-f")) {
  349                   i = handleArgBuildFile(args, i);
  350               } else if (arg.equals("-listener")) {
  351                   i = handleArgListener(args, i);
  352               } else if (arg.startsWith("-D")) {
  353                   i = handleArgDefine(args, i);
  354               } else if (arg.equals("-logger")) {
  355                   i = handleArgLogger(args, i);
  356               } else if (arg.equals("-inputhandler")) {
  357                   i = handleArgInputHandler(args, i);
  358               } else if (arg.equals("-emacs") || arg.equals("-e")) {
  359                   emacsMode = true;
  360               } else if (arg.equals("-projecthelp") || arg.equals("-p")) {
  361                   // set the flag to display the targets and quit
  362                   projectHelp = true;
  363               } else if (arg.equals("-find") || arg.equals("-s")) {
  364                   searchForFile = true;
  365                   // eat up next arg if present, default to build.xml
  366                   if (i < args.length - 1) {
  367                       searchForThis = args[++i];
  368                   }
  369               } else if (arg.startsWith("-propertyfile")) {
  370                   i = handleArgPropertyFile(args, i);
  371               } else if (arg.equals("-k") || arg.equals("-keep-going")) {
  372                   keepGoingMode = true;
  373               } else if (arg.equals("-nice")) {
  374                   i = handleArgNice(args, i);
  375               } else if (LAUNCH_COMMANDS.contains(arg)) {
  376                   //catch script/ant mismatch with a meaningful message
  377                   //we could ignore it, but there are likely to be other
  378                   //version problems, so we stamp down on the configuration now
  379                   String msg = "Ant's Main method is being handed "
  380                           + "an option " + arg + " that is only for the launcher class."
  381                           + "\nThis can be caused by a version mismatch between "
  382                           + "the ant script/.bat file and Ant itself.";
  383                   throw new BuildException(msg);
  384               } else if (arg.equals("-autoproxy")) {
  385                   proxy = true;
  386               } else if (arg.startsWith("-")) {
  387                   // we don't have any more args to recognize!
  388                   String msg = "Unknown argument: " + arg;
  389                   System.err.println(msg);
  390                   printUsage();
  391                   throw new BuildException("");
  392               } else {
  393                   // if it's no other arg, it may be the target
  394                   targets.addElement(arg);
  395               }
  396           }
  397   
  398           if (msgOutputLevel >= Project.MSG_VERBOSE || justPrintVersion) {
  399               printVersion(msgOutputLevel);
  400           }
  401   
  402           if (justPrintUsage || justPrintVersion || justPrintDiagnostics) {
  403               if (justPrintUsage) {
  404                   printUsage();
  405               }
  406               if (justPrintDiagnostics) {
  407                   Diagnostics.doReport(System.out, msgOutputLevel);
  408               }
  409               return;
  410           }
  411   
  412           // if buildFile was not specified on the command line,
  413           if (buildFile == null) {
  414               // but -find then search for it
  415               if (searchForFile) {
  416                   if (searchForThis != null) {
  417                       buildFile = findBuildFile(System.getProperty("user.dir"), searchForThis);
  418                       if (buildFile == null) {
  419                           throw new BuildException("Could not locate a build file!");
  420                       }
  421                   } else {
  422                       // no search file specified: so search an existing default file
  423                       Iterator it = ProjectHelperRepository.getInstance().getHelpers();
  424                       do {
  425                           ProjectHelper helper = (ProjectHelper) it.next();
  426                           searchForThis = helper.getDefaultBuildFile();
  427                           if (msgOutputLevel >= Project.MSG_VERBOSE) {
  428                               System.out.println("Searching the default build file: " + searchForThis);
  429                           }
  430                           buildFile = findBuildFile(System.getProperty("user.dir"), searchForThis);
  431                       } while (buildFile == null && it.hasNext());
  432                       if (buildFile == null) {
  433                           throw new BuildException("Could not locate a build file!");
  434                       }
  435                   }
  436               } else {
  437                   // no build file specified: so search an existing default file
  438                   Iterator it = ProjectHelperRepository.getInstance().getHelpers();
  439                   do {
  440                       ProjectHelper helper = (ProjectHelper) it.next();
  441                       buildFile = new File(helper.getDefaultBuildFile());
  442                       if (msgOutputLevel >= Project.MSG_VERBOSE) {
  443                           System.out.println("Trying the default build file: " + buildFile);
  444                       }
  445                   } while (!buildFile.exists() && it.hasNext());
  446               }
  447           }
  448   
  449           // make sure buildfile exists
  450           if (!buildFile.exists()) {
  451               System.out.println("Buildfile: " + buildFile + " does not exist!");
  452               throw new BuildException("Build failed");
  453           }
  454   
  455           // make sure it's not a directory (this falls into the ultra
  456           // paranoid lets check everything category
  457   
  458           if (buildFile.isDirectory()) {
  459               System.out.println("What? Buildfile: " + buildFile + " is a dir!");
  460               throw new BuildException("Build failed");
  461           }
  462   
  463           // Normalize buildFile for re-import detection
  464           buildFile =
  465               FileUtils.getFileUtils().normalize(buildFile.getAbsolutePath());
  466   
  467           // Load the property files specified by -propertyfile
  468           loadPropertyFiles();
  469   
  470           if (msgOutputLevel >= Project.MSG_INFO) {
  471               System.out.println("Buildfile: " + buildFile);
  472           }
  473   
  474           if (logTo != null) {
  475               out = logTo;
  476               err = logTo;
  477               System.setOut(out);
  478               System.setErr(err);
  479           }
  480           readyToRun = true;
  481       }
  482   
  483       // --------------------------------------------------------
  484       //    Methods for handling the command line arguments
  485       // --------------------------------------------------------
  486   
  487       /** Handle the -buildfile, -file, -f argument */
  488       private int handleArgBuildFile(String[] args, int pos) {
  489           try {
  490               buildFile = new File(
  491                   args[++pos].replace('/', File.separatorChar));
  492           } catch (ArrayIndexOutOfBoundsException aioobe) {
  493               throw new BuildException(
  494                   "You must specify a buildfile when using the -buildfile argument");
  495           }
  496           return pos;
  497       }
  498   
  499       /** Handle -listener argument */
  500       private int handleArgListener(String[] args, int pos) {
  501           try {
  502               listeners.addElement(args[pos + 1]);
  503               pos++;
  504           } catch (ArrayIndexOutOfBoundsException aioobe) {
  505               String msg = "You must specify a classname when "
  506                   + "using the -listener argument";
  507               throw new BuildException(msg);
  508           }
  509           return pos;
  510       }
  511   
  512       /** Handler -D argument */
  513       private int handleArgDefine(String[] args, int argPos) {
  514           /* Interestingly enough, we get to here when a user
  515            * uses -Dname=value. However, in some cases, the OS
  516            * goes ahead and parses this out to args
  517            *   {"-Dname", "value"}
  518            * so instead of parsing on "=", we just make the "-D"
  519            * characters go away and skip one argument forward.
  520            *
  521            * I don't know how to predict when the JDK is going
  522            * to help or not, so we simply look for the equals sign.
  523            */
  524           String arg = args[argPos];
  525           String name = arg.substring(2, arg.length());
  526           String value = null;
  527           int posEq = name.indexOf("=");
  528           if (posEq > 0) {
  529               value = name.substring(posEq + 1);
  530               name = name.substring(0, posEq);
  531           } else if (argPos < args.length - 1) {
  532               value = args[++argPos];
  533           } else {
  534               throw new BuildException("Missing value for property "
  535                                        + name);
  536           }
  537           definedProps.put(name, value);
  538           return argPos;
  539       }
  540   
  541       /** Handle the -logger argument. */
  542       private int handleArgLogger(String[] args, int pos) {
  543           if (loggerClassname != null) {
  544               throw new BuildException(
  545                   "Only one logger class may be specified.");
  546           }
  547           try {
  548               loggerClassname = args[++pos];
  549           } catch (ArrayIndexOutOfBoundsException aioobe) {
  550               throw new BuildException(
  551                   "You must specify a classname when using the -logger argument");
  552           }
  553           return pos;
  554       }
  555   
  556       /** Handle the -inputhandler argument. */
  557       private int handleArgInputHandler(String[] args, int pos) {
  558           if (inputHandlerClassname != null) {
  559               throw new BuildException("Only one input handler class may "
  560                                        + "be specified.");
  561           }
  562           try {
  563               inputHandlerClassname = args[++pos];
  564           } catch (ArrayIndexOutOfBoundsException aioobe) {
  565               throw new BuildException("You must specify a classname when"
  566                                        + " using the -inputhandler"
  567                                        + " argument");
  568           }
  569           return pos;
  570       }
  571   
  572       /** Handle the -propertyfile argument. */
  573       private int handleArgPropertyFile(String[] args, int pos) {
  574           try {
  575               propertyFiles.addElement(args[++pos]);
  576           } catch (ArrayIndexOutOfBoundsException aioobe) {
  577               String msg = "You must specify a property filename when "
  578                   + "using the -propertyfile argument";
  579               throw new BuildException(msg);
  580           }
  581           return pos;
  582       }
  583   
  584       /** Handle the -nice argument. */
  585       private int handleArgNice(String[] args, int pos) {
  586           try {
  587               threadPriority = Integer.decode(args[++pos]);
  588           } catch (ArrayIndexOutOfBoundsException aioobe) {
  589               throw new BuildException(
  590                   "You must supply a niceness value (1-10)"
  591                   + " after the -nice option");
  592           } catch (NumberFormatException e) {
  593               throw new BuildException("Unrecognized niceness value: "
  594                                        + args[pos]);
  595           }
  596   
  597           if (threadPriority.intValue() < Thread.MIN_PRIORITY
  598               || threadPriority.intValue() > Thread.MAX_PRIORITY) {
  599               throw new BuildException(
  600                   "Niceness value is out of the range 1-10");
  601           }
  602           return pos;
  603       }
  604   
  605       // --------------------------------------------------------
  606       //    other methods
  607       // --------------------------------------------------------
  608   
  609       /** Load the property files specified by -propertyfile */
  610       private void loadPropertyFiles() {
  611           for (int propertyFileIndex = 0;
  612                propertyFileIndex < propertyFiles.size();
  613                propertyFileIndex++) {
  614               String filename
  615                   = (String) propertyFiles.elementAt(propertyFileIndex);
  616               Properties props = new Properties();
  617               FileInputStream fis = null;
  618               try {
  619                   fis = new FileInputStream(filename);
  620                   props.load(fis);
  621               } catch (IOException e) {
  622                   System.out.println("Could not load property file "
  623                                      + filename + ": " + e.getMessage());
  624               } finally {
  625                   FileUtils.close(fis);
  626               }
  627   
  628               // ensure that -D properties take precedence
  629               Enumeration propertyNames = props.propertyNames();
  630               while (propertyNames.hasMoreElements()) {
  631                   String name = (String) propertyNames.nextElement();
  632                   if (definedProps.getProperty(name) == null) {
  633                       definedProps.put(name, props.getProperty(name));
  634                   }
  635               }
  636           }
  637       }
  638   
  639       /**
  640        * Helper to get the parent file for a given file.
  641        * <p>
  642        * Added to simulate File.getParentFile() from JDK 1.2.
  643        * @deprecated since 1.6.x
  644        *
  645        * @param file   File to find parent of. Must not be <code>null</code>.
  646        * @return       Parent file or null if none
  647        */
  648       private File getParentFile(File file) {
  649           File parent = file.getParentFile();
  650   
  651           if (parent != null && msgOutputLevel >= Project.MSG_VERBOSE) {
  652               System.out.println("Searching in " + parent.getAbsolutePath());
  653           }
  654   
  655           return parent;
  656       }
  657   
  658       /**
  659        * Search parent directories for the build file.
  660        * <p>
  661        * Takes the given target as a suffix to append to each
  662        * parent directory in search of a build file.  Once the
  663        * root of the file-system has been reached <code>null</code>
  664        * is returned.
  665        *
  666        * @param start  Leaf directory of search.
  667        *               Must not be <code>null</code>.
  668        * @param suffix  Suffix filename to look for in parents.
  669        *                Must not be <code>null</code>.
  670        *
  671        * @return A handle to the build file if one is found, <code>null</code> if not
  672        */
  673       private File findBuildFile(String start, String suffix) {
  674           if (msgOutputLevel >= Project.MSG_INFO) {
  675               System.out.println("Searching for " + suffix + " ...");
  676           }
  677   
  678           File parent = new File(new File(start).getAbsolutePath());
  679           File file = new File(parent, suffix);
  680   
  681           // check if the target file exists in the current directory
  682           while (!file.exists()) {
  683               // change to parent directory
  684               parent = getParentFile(parent);
  685   
  686               // if parent is null, then we are at the root of the fs,
  687               // complain that we can't find the build file.
  688               if (parent == null) {
  689                   return null;
  690               }
  691   
  692               // refresh our file handle
  693               file = new File(parent, suffix);
  694           }
  695   
  696           return file;
  697       }
  698   
  699       /**
  700        * Executes the build. If the constructor for this instance failed
  701        * (e.g. returned after issuing a warning), this method returns
  702        * immediately.
  703        *
  704        * @param coreLoader The classloader to use to find core classes.
  705        *                   May be <code>null</code>, in which case the
  706        *                   system classloader is used.
  707        *
  708        * @exception BuildException if the build fails
  709        */
  710       private void runBuild(ClassLoader coreLoader) throws BuildException {
  711   
  712           if (!readyToRun) {
  713               return;
  714           }
  715   
  716           final Project project = new Project();
  717           project.setCoreLoader(coreLoader);
  718   
  719           Throwable error = null;
  720   
  721           try {
  722               addBuildListeners(project);
  723               addInputHandler(project);
  724   
  725               PrintStream savedErr = System.err;
  726               PrintStream savedOut = System.out;
  727               InputStream savedIn = System.in;
  728   
  729               // use a system manager that prevents from System.exit()
  730               SecurityManager oldsm = null;
  731               oldsm = System.getSecurityManager();
  732   
  733                   //SecurityManager can not be installed here for backwards
  734                   //compatibility reasons (PD). Needs to be loaded prior to
  735                   //ant class if we are going to implement it.
  736                   //System.setSecurityManager(new NoExitSecurityManager());
  737               try {
  738                   if (allowInput) {
  739                       project.setDefaultInputStream(System.in);
  740                   }
  741                   System.setIn(new DemuxInputStream(project));
  742                   System.setOut(new PrintStream(new DemuxOutputStream(project, false)));
  743                   System.setErr(new PrintStream(new DemuxOutputStream(project, true)));
  744   
  745   
  746                   if (!projectHelp) {
  747                       project.fireBuildStarted();
  748                   }
  749   
  750                   // set the thread priorities
  751                   if (threadPriority != null) {
  752                       try {
  753                           project.log("Setting Ant's thread priority to "
  754                                   + threadPriority, Project.MSG_VERBOSE);
  755                           Thread.currentThread().setPriority(threadPriority.intValue());
  756                       } catch (SecurityException swallowed) {
  757                           //we cannot set the priority here.
  758                           project.log("A security manager refused to set the -nice value");
  759                       }
  760                   }
  761   
  762   
  763   
  764                   project.init();
  765   
  766                   // set user-define properties
  767                   Enumeration e = definedProps.keys();
  768                   while (e.hasMoreElements()) {
  769                       String arg = (String) e.nextElement();
  770                       String value = (String) definedProps.get(arg);
  771                       project.setUserProperty(arg, value);
  772                   }
  773   
  774                   project.setUserProperty(MagicNames.ANT_FILE,
  775                                           buildFile.getAbsolutePath());
  776                   project.setUserProperty(MagicNames.ANT_FILE_TYPE,
  777                                           MagicNames.ANT_FILE_TYPE_FILE);
  778   
  779                   project.setKeepGoingMode(keepGoingMode);
  780                   if (proxy) {
  781                       //proxy setup if enabled
  782                       ProxySetup proxySetup = new ProxySetup(project);
  783                       proxySetup.enableProxies();
  784                   }
  785   
  786                   ProjectHelper.configureProject(project, buildFile);
  787   
  788                   if (projectHelp) {
  789                       printDescription(project);
  790                       printTargets(project, msgOutputLevel > Project.MSG_INFO);
  791                       return;
  792                   }
  793   
  794                   // make sure that we have a target to execute
  795                   if (targets.size() == 0) {
  796                       if (project.getDefaultTarget() != null) {
  797                           targets.addElement(project.getDefaultTarget());
  798                       }
  799                   }
  800   
  801                   project.executeTargets(targets);
  802               } finally {
  803                   // put back the original security manager
  804                   //The following will never eval to true. (PD)
  805                   if (oldsm != null) {
  806                       System.setSecurityManager(oldsm);
  807                   }
  808   
  809                   System.setOut(savedOut);
  810                   System.setErr(savedErr);
  811                   System.setIn(savedIn);
  812               }
  813           } catch (RuntimeException exc) {
  814               error = exc;
  815               throw exc;
  816           } catch (Error e) {
  817               error = e;
  818               throw e;
  819           } finally {
  820               if (!projectHelp) {
  821                   try {
  822                       project.fireBuildFinished(error);
  823                   } catch (Throwable t) {
  824                       // yes, I know it is bad style to catch Throwable,
  825                       // but if we don't, we lose valuable information
  826                       System.err.println("Caught an exception while logging the"
  827                                          + " end of the build.  Exception was:");
  828                       t.printStackTrace();
  829                       if (error != null) {
  830                           System.err.println("There has been an error prior to"
  831                                              + " that:");
  832                           error.printStackTrace();
  833                       }
  834                       throw new BuildException(t);
  835                   }
  836               } else if (error != null) {
  837                   project.log(error.toString(), Project.MSG_ERR);
  838               }
  839           }
  840       }
  841   
  842       /**
  843        * Adds the listeners specified in the command line arguments,
  844        * along with the default listener, to the specified project.
  845        *
  846        * @param project The project to add listeners to.
  847        *                Must not be <code>null</code>.
  848        */
  849       protected void addBuildListeners(Project project) {
  850   
  851           // Add the default listener
  852           project.addBuildListener(createLogger());
  853   
  854           for (int i = 0; i < listeners.size(); i++) {
  855               String className = (String) listeners.elementAt(i);
  856               BuildListener listener =
  857                       (BuildListener) ClasspathUtils.newInstance(className,
  858                               Main.class.getClassLoader(), BuildListener.class);
  859               project.setProjectReference(listener);
  860   
  861               project.addBuildListener(listener);
  862           }
  863       }
  864   
  865       /**
  866        * Creates the InputHandler and adds it to the project.
  867        *
  868        * @param project the project instance.
  869        *
  870        * @exception BuildException if a specified InputHandler
  871        *                           implementation could not be loaded.
  872        */
  873       private void addInputHandler(Project project) throws BuildException {
  874           InputHandler handler = null;
  875           if (inputHandlerClassname == null) {
  876               handler = new DefaultInputHandler();
  877           } else {
  878               handler = (InputHandler) ClasspathUtils.newInstance(
  879                       inputHandlerClassname, Main.class.getClassLoader(),
  880                       InputHandler.class);
  881               project.setProjectReference(handler);
  882           }
  883           project.setInputHandler(handler);
  884       }
  885   
  886       // XXX: (Jon Skeet) Any reason for writing a message and then using a bare
  887       // RuntimeException rather than just using a BuildException here? Is it
  888       // in case the message could end up being written to no loggers (as the
  889       // loggers could have failed to be created due to this failure)?
  890       /**
  891        * Creates the default build logger for sending build events to the ant
  892        * log.
  893        *
  894        * @return the logger instance for this build.
  895        */
  896       private BuildLogger createLogger() {
  897           BuildLogger logger = null;
  898           if (loggerClassname != null) {
  899               try {
  900                   logger = (BuildLogger) ClasspathUtils.newInstance(
  901                           loggerClassname, Main.class.getClassLoader(),
  902                           BuildLogger.class);
  903               } catch (BuildException e) {
  904                   System.err.println("The specified logger class "
  905                       + loggerClassname
  906                       + " could not be used because " + e.getMessage());
  907                   throw new RuntimeException();
  908               }
  909           } else {
  910               logger = new DefaultLogger();
  911           }
  912   
  913           logger.setMessageOutputLevel(msgOutputLevel);
  914           logger.setOutputPrintStream(out);
  915           logger.setErrorPrintStream(err);
  916           logger.setEmacsMode(emacsMode);
  917   
  918           return logger;
  919       }
  920   
  921       /**
  922        * Prints the usage information for this class to <code>System.out</code>.
  923        */
  924       private static void printUsage() {
  925           String lSep = System.getProperty("line.separator");
  926           StringBuffer msg = new StringBuffer();
  927           msg.append("ant [options] [target [target2 [target3] ...]]" + lSep);
  928           msg.append("Options: " + lSep);
  929           msg.append("  -help, -h              print this message" + lSep);
  930           msg.append("  -projecthelp, -p       print project help information" + lSep);
  931           msg.append("  -version               print the version information and exit" + lSep);
  932           msg.append("  -diagnostics           print information that might be helpful to" + lSep);
  933           msg.append("                         diagnose or report problems." + lSep);
  934           msg.append("  -quiet, -q             be extra quiet" + lSep);
  935           msg.append("  -verbose, -v           be extra verbose" + lSep);
  936           msg.append("  -debug, -d             print debugging information" + lSep);
  937           msg.append("  -emacs, -e             produce logging information without adornments"
  938                      + lSep);
  939           msg.append("  -lib <path>            specifies a path to search for jars and classes"
  940                      + lSep);
  941           msg.append("  -logfile <file>        use given file for log" + lSep);
  942           msg.append("    -l     <file>                ''" + lSep);
  943           msg.append("  -logger <classname>    the class which is to perform logging" + lSep);
  944           msg.append("  -listener <classname>  add an instance of class as a project listener"
  945                      + lSep);
  946           msg.append("  -noinput               do not allow interactive input" + lSep);
  947           msg.append("  -buildfile <file>      use given buildfile" + lSep);
  948           msg.append("    -file    <file>              ''" + lSep);
  949           msg.append("    -f       <file>              ''" + lSep);
  950           msg.append("  -D<property>=<value>   use value for given property" + lSep);
  951           msg.append("  -keep-going, -k        execute all targets that do not depend" + lSep);
  952           msg.append("                         on failed target(s)" + lSep);
  953           msg.append("  -propertyfile <name>   load all properties from file with -D" + lSep);
  954           msg.append("                         properties taking precedence" + lSep);
  955           msg.append("  -inputhandler <class>  the class which will handle input requests" + lSep);
  956           msg.append("  -find <file>           (s)earch for buildfile towards the root of" + lSep);
  957           msg.append("    -s  <file>           the filesystem and use it" + lSep);
  958           msg.append("  -nice  number          A niceness value for the main thread:" + lSep
  959                      + "                         1 (lowest) to 10 (highest); 5 is the default"
  960                      + lSep);
  961           msg.append("  -nouserlib             Run ant without using the jar files from" + lSep
  962                      + "                         ${user.home}/.ant/lib" + lSep);
  963           msg.append("  -noclasspath           Run ant without using CLASSPATH" + lSep);
  964           msg.append("  -autoproxy             Java1.5+: use the OS proxy settings"
  965                   + lSep);
  966           msg.append("  -main <class>          override Ant's normal entry point");
  967           System.out.println(msg.toString());
  968       }
  969   
  970       /**
  971        * Prints the Ant version information to <code>System.out</code>.
  972        *
  973        * @exception BuildException if the version information is unavailable
  974        */
  975       private static void printVersion(int logLevel) throws BuildException {
  976           System.out.println(getAntVersion());
  977       }
  978   
  979       /**
  980        * Cache of the Ant version information when it has been loaded.
  981        */
  982       private static String antVersion = null;
  983   
  984       /**
  985        * Returns the Ant version information, if available. Once the information
  986        * has been loaded once, it's cached and returned from the cache on future
  987        * calls.
  988        *
  989        * @return the Ant version information as a String
  990        *         (always non-<code>null</code>)
  991        *
  992        * @exception BuildException if the version information is unavailable
  993        */
  994       public static synchronized String getAntVersion() throws BuildException {
  995           if (antVersion == null) {
  996               try {
  997                   Properties props = new Properties();
  998                   InputStream in =
  999                       Main.class.getResourceAsStream("/org/apache/tools/ant/version.txt");
 1000                   props.load(in);
 1001                   in.close();
 1002   
 1003                   StringBuffer msg = new StringBuffer();
 1004                   msg.append("Apache Ant version ");
 1005                   msg.append(props.getProperty("VERSION"));
 1006                   msg.append(" compiled on ");
 1007                   msg.append(props.getProperty("DATE"));
 1008                   antVersion = msg.toString();
 1009               } catch (IOException ioe) {
 1010                   throw new BuildException("Could not load the version information:"
 1011                                            + ioe.getMessage());
 1012               } catch (NullPointerException npe) {
 1013                   throw new BuildException("Could not load the version information.");
 1014               }
 1015           }
 1016           return antVersion;
 1017       }
 1018   
 1019        /**
 1020         * Prints the description of a project (if there is one) to
 1021         * <code>System.out</code>.
 1022         *
 1023         * @param project The project to display a description of.
 1024         *                Must not be <code>null</code>.
 1025         */
 1026       private static void printDescription(Project project) {
 1027          if (project.getDescription() != null) {
 1028             project.log(project.getDescription());
 1029          }
 1030       }
 1031   
 1032       /**
 1033        * Targets in imported files with a project name
 1034        * and not overloaded by the main build file will
 1035        * be in the target map twice. This method
 1036        * removes the duplicate target.
 1037        * @param targets the targets to filter.
 1038        * @return the filtered targets.
 1039        */
 1040       private static Map removeDuplicateTargets(Map targets) {
 1041           Map locationMap = new HashMap();
 1042           for (Iterator i = targets.entrySet().iterator(); i.hasNext();) {
 1043               Map.Entry entry = (Map.Entry) i.next();
 1044               String name = (String) entry.getKey();
 1045               Target target = (Target) entry.getValue();
 1046               Target otherTarget =
 1047                   (Target) locationMap.get(target.getLocation());
 1048               // Place this entry in the location map if
 1049               //  a) location is not in the map
 1050               //  b) location is in map, but it's name is longer
 1051               //     (an imported target will have a name. prefix)
 1052               if (otherTarget == null
 1053                   || otherTarget.getName().length() > name.length()) {
 1054                   locationMap.put(
 1055                       target.getLocation(), target); // Smallest name wins
 1056               }
 1057           }
 1058           Map ret = new HashMap();
 1059           for (Iterator i = locationMap.values().iterator(); i.hasNext();) {
 1060               Target target = (Target) i.next();
 1061               ret.put(target.getName(), target);
 1062           }
 1063           return ret;
 1064       }
 1065   
 1066       /**
 1067        * Prints a list of all targets in the specified project to
 1068        * <code>System.out</code>, optionally including subtargets.
 1069        *
 1070        * @param project The project to display a description of.
 1071        *                Must not be <code>null</code>.
 1072        * @param printSubTargets Whether or not subtarget names should also be
 1073        *                        printed.
 1074        */
 1075       private static void printTargets(Project project, boolean printSubTargets) {
 1076           // find the target with the longest name
 1077           int maxLength = 0;
 1078           Map ptargets = removeDuplicateTargets(project.getTargets());
 1079           String targetName;
 1080           String targetDescription;
 1081           Target currentTarget;
 1082           // split the targets in top-level and sub-targets depending
 1083           // on the presence of a description
 1084           Vector topNames = new Vector();
 1085           Vector topDescriptions = new Vector();
 1086           Vector subNames = new Vector();
 1087   
 1088           for (Iterator i = ptargets.values().iterator(); i.hasNext();) {
 1089               currentTarget = (Target) i.next();
 1090               targetName = currentTarget.getName();
 1091               if (targetName.equals("")) {
 1092                   continue;
 1093               }
 1094               targetDescription = currentTarget.getDescription();
 1095               // maintain a sorted list of targets
 1096               if (targetDescription == null) {
 1097                   int pos = findTargetPosition(subNames, targetName);
 1098                   subNames.insertElementAt(targetName, pos);
 1099               } else {
 1100                   int pos = findTargetPosition(topNames, targetName);
 1101                   topNames.insertElementAt(targetName, pos);
 1102                   topDescriptions.insertElementAt(targetDescription, pos);
 1103                   if (targetName.length() > maxLength) {
 1104                       maxLength = targetName.length();
 1105                   }
 1106               }
 1107           }
 1108   
 1109           printTargets(project, topNames, topDescriptions, "Main targets:",
 1110                        maxLength);
 1111           //if there were no main targets, we list all subtargets
 1112           //as it means nothing has a description
 1113           if (topNames.size() == 0) {
 1114               printSubTargets = true;
 1115           }
 1116           if (printSubTargets) {
 1117               printTargets(project, subNames, null, "Other targets:", 0);
 1118           }
 1119   
 1120           String defaultTarget = project.getDefaultTarget();
 1121           if (defaultTarget != null && !"".equals(defaultTarget)) {
 1122               // shouldn't need to check but...
 1123               project.log("Default target: " + defaultTarget);
 1124           }
 1125       }
 1126   
 1127       /**
 1128        * Searches for the correct place to insert a name into a list so as
 1129        * to keep the list sorted alphabetically.
 1130        *
 1131        * @param names The current list of names. Must not be <code>null</code>.
 1132        * @param name  The name to find a place for.
 1133        *              Must not be <code>null</code>.
 1134        *
 1135        * @return the correct place in the list for the given name
 1136        */
 1137       private static int findTargetPosition(Vector names, String name) {
 1138           int res = names.size();
 1139           for (int i = 0; i < names.size() && res == names.size(); i++) {
 1140               if (name.compareTo((String) names.elementAt(i)) < 0) {
 1141                   res = i;
 1142               }
 1143           }
 1144           return res;
 1145       }
 1146   
 1147       /**
 1148        * Writes a formatted list of target names to <code>System.out</code>
 1149        * with an optional description.
 1150        *
 1151        *
 1152        * @param project the project instance.
 1153        * @param names The names to be printed.
 1154        *              Must not be <code>null</code>.
 1155        * @param descriptions The associated target descriptions.
 1156        *                     May be <code>null</code>, in which case
 1157        *                     no descriptions are displayed.
 1158        *                     If non-<code>null</code>, this should have
 1159        *                     as many elements as <code>names</code>.
 1160        * @param heading The heading to display.
 1161        *                Should not be <code>null</code>.
 1162        * @param maxlen The maximum length of the names of the targets.
 1163        *               If descriptions are given, they are padded to this
 1164        *               position so they line up (so long as the names really
 1165        *               <i>are</i> shorter than this).
 1166        */
 1167       private static void printTargets(Project project, Vector names,
 1168                                        Vector descriptions, String heading,
 1169                                        int maxlen) {
 1170           // now, start printing the targets and their descriptions
 1171           String lSep = System.getProperty("line.separator");
 1172           // got a bit annoyed that I couldn't find a pad function
 1173           String spaces = "    ";
 1174           while (spaces.length() <= maxlen) {
 1175               spaces += spaces;
 1176           }
 1177           StringBuffer msg = new StringBuffer();
 1178           msg.append(heading + lSep + lSep);
 1179           for (int i = 0; i < names.size(); i++) {
 1180               msg.append(" ");
 1181               msg.append(names.elementAt(i));
 1182               if (descriptions != null) {
 1183                   msg.append(
 1184                       spaces.substring(0, maxlen - ((String) names.elementAt(i)).length() + 2));
 1185                   msg.append(descriptions.elementAt(i));
 1186               }
 1187               msg.append(lSep);
 1188           }
 1189           project.log(msg.toString(), Project.MSG_WARN);
 1190       }
 1191   }

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