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

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