You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

Main.java 36 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924
  1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. * Copyright (c) 2000-2002 The Apache Software Foundation. All rights
  5. * reserved.
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions
  9. * are met:
  10. *
  11. * 1. Redistributions of source code must retain the above copyright
  12. * notice, this list of conditions and the following disclaimer.
  13. *
  14. * 2. Redistributions in binary form must reproduce the above copyright
  15. * notice, this list of conditions and the following disclaimer in
  16. * the documentation and/or other materials provided with the
  17. * distribution.
  18. *
  19. * 3. The end-user documentation included with the redistribution, if
  20. * any, must include the following acknowlegement:
  21. * "This product includes software developed by the
  22. * Apache Software Foundation (http://www.apache.org/)."
  23. * Alternately, this acknowlegement may appear in the software itself,
  24. * if and wherever such third-party acknowlegements normally appear.
  25. *
  26. * 4. The names "The Jakarta Project", "Ant", and "Apache Software
  27. * Foundation" must not be used to endorse or promote products derived
  28. * from this software without prior written permission. For written
  29. * permission, please contact apache@apache.org.
  30. *
  31. * 5. Products derived from this software may not be called "Apache"
  32. * nor may "Apache" appear in their names without prior written
  33. * permission of the Apache Group.
  34. *
  35. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  36. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  37. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  38. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  39. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  40. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  41. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  42. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  43. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  44. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  45. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  46. * SUCH DAMAGE.
  47. * ====================================================================
  48. *
  49. * This software consists of voluntary contributions made by many
  50. * individuals on behalf of the Apache Software Foundation. For more
  51. * information on the Apache Software Foundation, please see
  52. * <http://www.apache.org/>.
  53. */
  54. package org.apache.tools.ant;
  55. import org.apache.tools.ant.input.DefaultInputHandler;
  56. import org.apache.tools.ant.input.InputHandler;
  57. import java.io.File;
  58. import java.io.FileInputStream;
  59. import java.io.PrintStream;
  60. import java.io.FileOutputStream;
  61. import java.io.IOException;
  62. import java.io.InputStream;
  63. import java.util.Vector;
  64. import java.util.Properties;
  65. import java.util.Enumeration;
  66. /**
  67. * Command line entry point into Ant. This class is entered via the
  68. * cannonical `public static void main` entry point and reads the
  69. * command line arguments. It then assembles and executes an Ant
  70. * project.
  71. * <p>
  72. * If you integrating Ant into some other tool, this is not the class
  73. * to use as an entry point. Please see the source code of this
  74. * class to see how it manipulates the Ant project classes.
  75. *
  76. * @author duncan@x180.com
  77. */
  78. public class Main {
  79. /** The default build file name. */
  80. public static final String DEFAULT_BUILD_FILENAME = "build.xml";
  81. /** Our current message output status. Follows Project.MSG_XXX. */
  82. private int msgOutputLevel = Project.MSG_INFO;
  83. /** File that we are using for configuration. */
  84. private File buildFile; /* null */
  85. /** Stream to use for logging. */
  86. private static PrintStream out = System.out;
  87. /** Stream that we are using for logging error messages. */
  88. private static PrintStream err = System.err;
  89. /** The build targets. */
  90. private Vector targets = new Vector(5);
  91. /** Set of properties that can be used by tasks. */
  92. private Properties definedProps = new Properties();
  93. /** Names of classes to add as listeners to project. */
  94. private Vector listeners = new Vector(5);
  95. /** File names of property files to load on startup. */
  96. private Vector propertyFiles = new Vector(5);
  97. /**
  98. * The Ant logger class. There may be only one logger. It will have
  99. * the right to use the 'out' PrintStream. The class must implements the
  100. * BuildLogger interface.
  101. */
  102. private String loggerClassname = null;
  103. /**
  104. * The Ant InputHandler class. There may be only one input
  105. * handler.
  106. */
  107. private String inputHandlerClassname = null;
  108. /**
  109. * Whether or not output to the log is to be unadorned.
  110. */
  111. private boolean emacsMode = false;
  112. /**
  113. * Whether or not this instance has successfully been
  114. * constructed and is ready to run.
  115. */
  116. private boolean readyToRun = false;
  117. /**
  118. * Whether or not we should only parse and display the project help
  119. * information.
  120. */
  121. private boolean projectHelp = false;
  122. /**
  123. * Is a logfile being used? This is used to
  124. * check if the output streams must be closed.
  125. */
  126. private static boolean isLogFileUsed = false;
  127. /**
  128. * Prints the message of the Throwable if it (the message) is not
  129. * <code>null</code>.
  130. *
  131. * @param t Throwable to print the message of.
  132. * Must not be <code>null</code>.
  133. */
  134. private static void printMessage(Throwable t) {
  135. String message = t.getMessage();
  136. if (message != null) {
  137. System.err.println(message);
  138. }
  139. }
  140. /**
  141. * Creates a new instance of this class using the
  142. * arguments specified, gives it any extra user properties which have been
  143. * specified, and then runs the build using the classloader provided.
  144. *
  145. * @param args Command line arguments. Must not be <code>null</code>.
  146. * @param additionalUserProperties Any extra properties to use in this
  147. * build. May be <code>null</code>, which is the equivalent to
  148. * passing in an empty set of properties.
  149. * @param coreLoader Classloader used for core classes. May be
  150. * <code>null</code> in which case the system classloader is used.
  151. */
  152. public static void start(String[] args, Properties additionalUserProperties,
  153. ClassLoader coreLoader) {
  154. Main m = null;
  155. try {
  156. m = new Main(args);
  157. } catch (Throwable exc) {
  158. printMessage(exc);
  159. System.exit(1);
  160. }
  161. if (additionalUserProperties != null) {
  162. for (Enumeration e = additionalUserProperties.keys();
  163. e.hasMoreElements();) {
  164. String key = (String) e.nextElement();
  165. String property = additionalUserProperties.getProperty(key);
  166. m.definedProps.put(key, property);
  167. }
  168. }
  169. try {
  170. m.runBuild(coreLoader);
  171. System.exit(0);
  172. } catch (BuildException be) {
  173. if (m.err != System.err) {
  174. printMessage(be);
  175. }
  176. System.exit(1);
  177. } catch (Throwable exc) {
  178. exc.printStackTrace();
  179. printMessage(exc);
  180. System.exit(1);
  181. } finally {
  182. if (isLogFileUsed) {
  183. if (out != null) {
  184. try {
  185. out.close();
  186. } catch (final Exception e) {
  187. //ignore
  188. }
  189. }
  190. if (err != null) {
  191. try {
  192. err.close();
  193. } catch (final Exception e) {
  194. //ignore
  195. }
  196. }
  197. }
  198. }
  199. }
  200. /**
  201. * Command line entry point. This method kicks off the building
  202. * of a project object and executes a build using either a given
  203. * target or the default target.
  204. *
  205. * @param args Command line arguments. Must not be <code>null</code>.
  206. */
  207. public static void main(String[] args) {
  208. start(args, null, null);
  209. }
  210. // XXX: (Jon Skeet) Error handling appears to be inconsistent here.
  211. // Sometimes there's just a return statement, and sometimes a
  212. // BuildException is thrown. What's the rationale for when to do
  213. // what?
  214. /**
  215. * Sole constructor, which parses and deals with command line
  216. * arguments.
  217. *
  218. * @param args Command line arguments. Must not be <code>null</code>.
  219. *
  220. * @exception BuildException if the specified build file doesn't exist
  221. * or is a directory.
  222. */
  223. protected Main(String[] args) throws BuildException {
  224. String searchForThis = null;
  225. // cycle through given args
  226. for (int i = 0; i < args.length; i++) {
  227. String arg = args[i];
  228. if (arg.equals("-help")) {
  229. printUsage();
  230. return;
  231. } else if (arg.equals("-version")) {
  232. printVersion();
  233. return;
  234. } else if (arg.equals("-quiet") || arg.equals("-q")) {
  235. msgOutputLevel = Project.MSG_WARN;
  236. } else if (arg.equals("-verbose") || arg.equals("-v")) {
  237. printVersion();
  238. msgOutputLevel = Project.MSG_VERBOSE;
  239. } else if (arg.equals("-debug")) {
  240. printVersion();
  241. msgOutputLevel = Project.MSG_DEBUG;
  242. } else if (arg.equals("-logfile") || arg.equals("-l")) {
  243. try {
  244. File logFile = new File(args[i + 1]);
  245. i++;
  246. out = new PrintStream(new FileOutputStream(logFile));
  247. err = out;
  248. System.setOut(out);
  249. System.setErr(out);
  250. isLogFileUsed = true;
  251. } catch (IOException ioe) {
  252. String msg = "Cannot write on the specified log file. "
  253. + "Make sure the path exists and you have write "
  254. + "permissions.";
  255. System.out.println(msg);
  256. return;
  257. } catch (ArrayIndexOutOfBoundsException aioobe) {
  258. String msg = "You must specify a log file when " +
  259. "using the -log argument";
  260. System.out.println(msg);
  261. return;
  262. }
  263. } else if (arg.equals("-buildfile") || arg.equals("-file")
  264. || arg.equals("-f")) {
  265. try {
  266. buildFile = new File(args[i + 1]);
  267. i++;
  268. } catch (ArrayIndexOutOfBoundsException aioobe) {
  269. String msg = "You must specify a buildfile when " +
  270. "using the -buildfile argument";
  271. System.out.println(msg);
  272. return;
  273. }
  274. } else if (arg.equals("-listener")) {
  275. try {
  276. listeners.addElement(args[i + 1]);
  277. i++;
  278. } catch (ArrayIndexOutOfBoundsException aioobe) {
  279. String msg = "You must specify a classname when " +
  280. "using the -listener argument";
  281. System.out.println(msg);
  282. return;
  283. }
  284. } else if (arg.startsWith("-D")) {
  285. /* Interestingly enough, we get to here when a user
  286. * uses -Dname=value. However, in some cases, the JDK
  287. * goes ahead and parses this out to args
  288. * {"-Dname", "value"}
  289. * so instead of parsing on "=", we just make the "-D"
  290. * characters go away and skip one argument forward.
  291. *
  292. * I don't know how to predict when the JDK is going
  293. * to help or not, so we simply look for the equals sign.
  294. */
  295. String name = arg.substring(2, arg.length());
  296. String value = null;
  297. int posEq = name.indexOf("=");
  298. if (posEq > 0) {
  299. value = name.substring(posEq + 1);
  300. name = name.substring(0, posEq);
  301. } else if (i < args.length - 1) {
  302. value = args[++i];
  303. }
  304. definedProps.put(name, value);
  305. } else if (arg.equals("-logger")) {
  306. if (loggerClassname != null) {
  307. System.out.println("Only one logger class may "
  308. + " be specified.");
  309. return;
  310. }
  311. try {
  312. loggerClassname = args[++i];
  313. } catch (ArrayIndexOutOfBoundsException aioobe) {
  314. System.out.println("You must specify a classname when " +
  315. "using the -logger argument");
  316. return;
  317. }
  318. } else if (arg.equals("-inputhandler")) {
  319. if (inputHandlerClassname != null) {
  320. System.out.println("Only one input handler class may " +
  321. "be specified.");
  322. return;
  323. }
  324. try {
  325. inputHandlerClassname = args[++i];
  326. } catch (ArrayIndexOutOfBoundsException aioobe) {
  327. System.out.println("You must specify a classname when " +
  328. "using the -inputhandler argument");
  329. return;
  330. }
  331. } else if (arg.equals("-emacs")) {
  332. emacsMode = true;
  333. } else if (arg.equals("-projecthelp")) {
  334. // set the flag to display the targets and quit
  335. projectHelp = true;
  336. } else if (arg.equals("-find")) {
  337. // eat up next arg if present, default to build.xml
  338. if (i < args.length - 1) {
  339. searchForThis = args[++i];
  340. } else {
  341. searchForThis = DEFAULT_BUILD_FILENAME;
  342. }
  343. } else if (arg.startsWith("-propertyfile")) {
  344. try {
  345. propertyFiles.addElement(args[i + 1]);
  346. i++;
  347. } catch (ArrayIndexOutOfBoundsException aioobe) {
  348. String msg = "You must specify a property filename when " +
  349. "using the -propertyfile argument";
  350. System.out.println(msg);
  351. return;
  352. }
  353. } else if (arg.startsWith("-")) {
  354. // we don't have any more args to recognize!
  355. String msg = "Unknown argument: " + arg;
  356. System.out.println(msg);
  357. printUsage();
  358. return;
  359. } else {
  360. // if it's no other arg, it may be the target
  361. targets.addElement(arg);
  362. }
  363. }
  364. // if buildFile was not specified on the command line,
  365. if (buildFile == null) {
  366. // but -find then search for it
  367. if (searchForThis != null) {
  368. buildFile = findBuildFile(System.getProperty("user.dir"),
  369. searchForThis);
  370. } else {
  371. buildFile = new File(DEFAULT_BUILD_FILENAME);
  372. }
  373. }
  374. // make sure buildfile exists
  375. if (!buildFile.exists()) {
  376. System.out.println("Buildfile: " + buildFile + " does not exist!");
  377. throw new BuildException("Build failed");
  378. }
  379. // make sure it's not a directory (this falls into the ultra
  380. // paranoid lets check everything catagory
  381. if (buildFile.isDirectory()) {
  382. System.out.println("What? Buildfile: " + buildFile + " is a dir!");
  383. throw new BuildException("Build failed");
  384. }
  385. // Load the property files specified by -propertyfile
  386. for (int propertyFileIndex = 0;
  387. propertyFileIndex < propertyFiles.size();
  388. propertyFileIndex++) {
  389. String filename
  390. = (String) propertyFiles.elementAt(propertyFileIndex);
  391. Properties props = new Properties();
  392. FileInputStream fis = null;
  393. try {
  394. fis = new FileInputStream(filename);
  395. props.load(fis);
  396. } catch (IOException e) {
  397. System.out.println("Could not load property file "
  398. + filename + ": " + e.getMessage());
  399. } finally {
  400. if (fis != null){
  401. try {
  402. fis.close();
  403. } catch (IOException e){
  404. }
  405. }
  406. }
  407. // ensure that -D properties take precedence
  408. Enumeration propertyNames = props.propertyNames();
  409. while (propertyNames.hasMoreElements()) {
  410. String name = (String) propertyNames.nextElement();
  411. if (definedProps.getProperty(name) == null) {
  412. definedProps.put(name, props.getProperty(name));
  413. }
  414. }
  415. }
  416. readyToRun = true;
  417. }
  418. /**
  419. * Helper to get the parent file for a given file.
  420. * <p>
  421. * Added to simulate File.getParentFile() from JDK 1.2.
  422. *
  423. * @param file File to find parent of. Must not be <code>null</code>.
  424. * @return Parent file or null if none
  425. */
  426. private File getParentFile(File file) {
  427. String filename = file.getAbsolutePath();
  428. file = new File(filename);
  429. filename = file.getParent();
  430. if (filename != null && msgOutputLevel >= Project.MSG_VERBOSE) {
  431. System.out.println("Searching in " + filename);
  432. }
  433. return (filename == null) ? null : new File(filename);
  434. }
  435. /**
  436. * Search parent directories for the build file.
  437. * <p>
  438. * Takes the given target as a suffix to append to each
  439. * parent directory in seach of a build file. Once the
  440. * root of the file-system has been reached an exception
  441. * is thrown.
  442. *
  443. * @param start Leaf directory of search.
  444. * Must not be <code>null</code>.
  445. * @param suffix Suffix filename to look for in parents.
  446. * Must not be <code>null</code>.
  447. *
  448. * @return A handle to the build file if one is found
  449. *
  450. * @exception BuildException if no build file is found
  451. */
  452. private File findBuildFile(String start, String suffix)
  453. throws BuildException {
  454. if (msgOutputLevel >= Project.MSG_INFO) {
  455. System.out.println("Searching for " + suffix + " ...");
  456. }
  457. File parent = new File(new File(start).getAbsolutePath());
  458. File file = new File(parent, suffix);
  459. // check if the target file exists in the current directory
  460. while (!file.exists()) {
  461. // change to parent directory
  462. parent = getParentFile(parent);
  463. // if parent is null, then we are at the root of the fs,
  464. // complain that we can't find the build file.
  465. if (parent == null) {
  466. throw new BuildException("Could not locate a build file!");
  467. }
  468. // refresh our file handle
  469. file = new File(parent, suffix);
  470. }
  471. return file;
  472. }
  473. /**
  474. * Executes the build. If the constructor for this instance failed
  475. * (e.g. returned after issuing a warning), this method returns
  476. * immediately.
  477. *
  478. * @param coreLoader The classloader to use to find core classes.
  479. * May be <code>null</code>, in which case the
  480. * system classloader is used.
  481. *
  482. * @exception BuildException if the build fails
  483. */
  484. private void runBuild(ClassLoader coreLoader) throws BuildException {
  485. if (!readyToRun) {
  486. return;
  487. }
  488. // track when we started
  489. if (msgOutputLevel >= Project.MSG_INFO) {
  490. System.out.println("Buildfile: " + buildFile);
  491. }
  492. final Project project = new Project();
  493. project.setCoreLoader(coreLoader);
  494. Throwable error = null;
  495. try {
  496. addBuildListeners(project);
  497. addInputHandler(project);
  498. PrintStream err = System.err;
  499. PrintStream out = System.out;
  500. // use a system manager that prevents from System.exit()
  501. // only in JDK > 1.1
  502. SecurityManager oldsm = null;
  503. if (!Project.JAVA_1_0.equals(Project.getJavaVersion()) &&
  504. !Project.JAVA_1_1.equals(Project.getJavaVersion())){
  505. oldsm = System.getSecurityManager();
  506. //SecurityManager can not be installed here for backwards
  507. //compatability reasons (PD). Needs to be loaded prior to
  508. //ant class if we are going to implement it.
  509. //System.setSecurityManager(new NoExitSecurityManager());
  510. }
  511. try {
  512. System.setOut(new PrintStream(new DemuxOutputStream(project, false)));
  513. System.setErr(new PrintStream(new DemuxOutputStream(project, true)));
  514. if (!projectHelp) {
  515. project.fireBuildStarted();
  516. }
  517. project.init();
  518. project.setUserProperty("ant.version", getAntVersion());
  519. // set user-define properties
  520. Enumeration e = definedProps.keys();
  521. while (e.hasMoreElements()) {
  522. String arg = (String) e.nextElement();
  523. String value = (String) definedProps.get(arg);
  524. project.setUserProperty(arg, value);
  525. }
  526. project.setUserProperty("ant.file",
  527. buildFile.getAbsolutePath());
  528. ProjectHelper.configureProject(project, buildFile);
  529. if (projectHelp) {
  530. printDescription(project);
  531. printTargets(project, msgOutputLevel > Project.MSG_INFO);
  532. return;
  533. }
  534. // make sure that we have a target to execute
  535. if (targets.size() == 0) {
  536. targets.addElement(project.getDefaultTarget());
  537. }
  538. project.executeTargets(targets);
  539. } finally {
  540. // put back the original security manager
  541. //The following will never eval to true. (PD)
  542. if (oldsm != null){
  543. System.setSecurityManager(oldsm);
  544. }
  545. System.setOut(out);
  546. System.setErr(err);
  547. }
  548. } catch (RuntimeException exc) {
  549. error = exc;
  550. throw exc;
  551. } catch (Error err) {
  552. error = err;
  553. throw err;
  554. } finally {
  555. if (!projectHelp) {
  556. project.fireBuildFinished(error);
  557. }
  558. }
  559. }
  560. /**
  561. * Adds the listeners specified in the command line arguments,
  562. * along with the default listener, to the specified project.
  563. *
  564. * @param project The project to add listeners to.
  565. * Must not be <code>null</code>.
  566. */
  567. protected void addBuildListeners(Project project) {
  568. // Add the default listener
  569. project.addBuildListener(createLogger());
  570. for (int i = 0; i < listeners.size(); i++) {
  571. String className = (String) listeners.elementAt(i);
  572. try {
  573. BuildListener listener =
  574. (BuildListener) Class.forName(className).newInstance();
  575. project.addBuildListener(listener);
  576. } catch (Throwable exc) {
  577. throw new BuildException("Unable to instantiate listener "
  578. + className, exc);
  579. }
  580. }
  581. }
  582. /**
  583. * Creates the InputHandler and adds it to the project.
  584. *
  585. * @exception BuildException if a specified InputHandler
  586. * implementation could not be loaded.
  587. */
  588. private void addInputHandler(Project project) {
  589. InputHandler handler = null;
  590. if (inputHandlerClassname == null) {
  591. handler = new DefaultInputHandler();
  592. } else {
  593. try {
  594. handler = (InputHandler)
  595. (Class.forName(inputHandlerClassname).newInstance());
  596. } catch (ClassCastException e) {
  597. String msg = "The specified input handler class "
  598. + inputHandlerClassname
  599. + " does not implement the InputHandler interface";
  600. throw new BuildException(msg);
  601. }
  602. catch (Exception e) {
  603. String msg = "Unable to instantiate specified input handler "
  604. + "class " + inputHandlerClassname + " : "
  605. + e.getClass().getName();
  606. throw new BuildException(msg);
  607. }
  608. }
  609. project.setInputHandler(handler);
  610. }
  611. // XXX: (Jon Skeet) Any reason for writing a message and then using a bare
  612. // RuntimeException rather than just using a BuildException here? Is it
  613. // in case the message could end up being written to no loggers (as the
  614. // loggers could have failed to be created due to this failure)?
  615. /**
  616. * Creates the default build logger for sending build events to the ant
  617. * log.
  618. *
  619. * @return the logger instance for this build.
  620. */
  621. private BuildLogger createLogger() {
  622. BuildLogger logger = null;
  623. if (loggerClassname != null) {
  624. try {
  625. logger = (BuildLogger) (Class.forName(loggerClassname).newInstance());
  626. } catch (ClassCastException e) {
  627. System.err.println("The specified logger class "
  628. + loggerClassname
  629. + " does not implement the BuildLogger interface");
  630. throw new RuntimeException();
  631. } catch (Exception e) {
  632. System.err.println("Unable to instantiate specified logger "
  633. + "class " + loggerClassname + " : "
  634. + e.getClass().getName());
  635. throw new RuntimeException();
  636. }
  637. } else {
  638. logger = new DefaultLogger();
  639. }
  640. logger.setMessageOutputLevel(msgOutputLevel);
  641. logger.setOutputPrintStream(out);
  642. logger.setErrorPrintStream(err);
  643. logger.setEmacsMode(emacsMode);
  644. return logger;
  645. }
  646. /**
  647. * Prints the usage information for this class to <code>System.out</code>.
  648. */
  649. private static void printUsage() {
  650. String lSep = System.getProperty("line.separator");
  651. StringBuffer msg = new StringBuffer();
  652. msg.append("ant [options] [target [target2 [target3] ...]]" + lSep);
  653. msg.append("Options: " + lSep);
  654. msg.append(" -help print this message" + lSep);
  655. msg.append(" -projecthelp print project help information" + lSep);
  656. msg.append(" -version print the version information and exit" + lSep);
  657. msg.append(" -quiet be extra quiet" + lSep);
  658. msg.append(" -verbose be extra verbose" + lSep);
  659. msg.append(" -debug print debugging information" + lSep);
  660. msg.append(" -emacs produce logging information without adornments" + lSep);
  661. msg.append(" -logfile <file> use given file for log" + lSep);
  662. msg.append(" -logger <classname> the class which is to perform logging" + lSep);
  663. msg.append(" -listener <classname> add an instance of class as a project listener" + lSep);
  664. msg.append(" -buildfile <file> use given buildfile" + lSep);
  665. msg.append(" -D<property>=<value> use value for given property" + lSep);
  666. msg.append(" -propertyfile <name> load all properties from file with -D" + lSep);
  667. msg.append(" properties taking precedence" + lSep);
  668. msg.append(" -find <file> search for buildfile towards the root of the" + lSep);
  669. msg.append(" filesystem and use it" + lSep);
  670. System.out.println(msg.toString());
  671. }
  672. /**
  673. * Prints the Ant version information to <code>System.out</code>.
  674. *
  675. * @exception BuildException if the version information is unavailable
  676. */
  677. private static void printVersion() throws BuildException {
  678. System.out.println(getAntVersion());
  679. }
  680. /**
  681. * Cache of the Ant version information when it has been loaded.
  682. */
  683. private static String antVersion = null;
  684. /**
  685. * Returns the Ant version information, if available. Once the information
  686. * has been loaded once, it's cached and returned from the cache on future
  687. * calls.
  688. *
  689. * @return the Ant version information as a String
  690. * (always non-<code>null</code>)
  691. *
  692. * @exception BuildException if the version information is unavailable
  693. */
  694. public static synchronized String getAntVersion() throws BuildException {
  695. if (antVersion == null) {
  696. try {
  697. Properties props = new Properties();
  698. InputStream in =
  699. Main.class.getResourceAsStream("/org/apache/tools/ant/version.txt");
  700. props.load(in);
  701. in.close();
  702. String lSep = System.getProperty("line.separator");
  703. StringBuffer msg = new StringBuffer();
  704. msg.append("Apache Ant version ");
  705. msg.append(props.getProperty("VERSION"));
  706. msg.append(" compiled on ");
  707. msg.append(props.getProperty("DATE"));
  708. antVersion = msg.toString();
  709. } catch (IOException ioe) {
  710. throw new BuildException("Could not load the version information:"
  711. + ioe.getMessage());
  712. } catch (NullPointerException npe) {
  713. throw new BuildException("Could not load the version information.");
  714. }
  715. }
  716. return antVersion;
  717. }
  718. /**
  719. * Prints the description of a project (if there is one) to
  720. * <code>System.out</code>.
  721. *
  722. * @param project The project to display a description of.
  723. * Must not be <code>null</code>.
  724. */
  725. private static void printDescription(Project project) {
  726. if (project.getDescription() != null) {
  727. System.out.println(project.getDescription());
  728. }
  729. }
  730. /**
  731. * Prints a list of all targets in the specified project to
  732. * <code>System.out</code>, optionally including subtargets.
  733. *
  734. * @param project The project to display a description of.
  735. * Must not be <code>null</code>.
  736. * @param printSubTargets Whether or not subtarget names should also be
  737. * printed.
  738. */
  739. private static void printTargets(Project project, boolean printSubTargets) {
  740. // find the target with the longest name
  741. int maxLength = 0;
  742. Enumeration ptargets = project.getTargets().elements();
  743. String targetName;
  744. String targetDescription;
  745. Target currentTarget;
  746. // split the targets in top-level and sub-targets depending
  747. // on the presence of a description
  748. Vector topNames = new Vector();
  749. Vector topDescriptions = new Vector();
  750. Vector subNames = new Vector();
  751. while (ptargets.hasMoreElements()) {
  752. currentTarget = (Target) ptargets.nextElement();
  753. targetName = currentTarget.getName();
  754. targetDescription = currentTarget.getDescription();
  755. // maintain a sorted list of targets
  756. if (targetDescription == null) {
  757. int pos = findTargetPosition(subNames, targetName);
  758. subNames.insertElementAt(targetName, pos);
  759. } else {
  760. int pos = findTargetPosition(topNames, targetName);
  761. topNames.insertElementAt(targetName, pos);
  762. topDescriptions.insertElementAt(targetDescription, pos);
  763. if (targetName.length() > maxLength) {
  764. maxLength = targetName.length();
  765. }
  766. }
  767. }
  768. printTargets(topNames, topDescriptions, "Main targets:", maxLength);
  769. if (printSubTargets) {
  770. printTargets(subNames, null, "Subtargets:", 0);
  771. }
  772. String defaultTarget = project.getDefaultTarget();
  773. if (defaultTarget != null && !"".equals(defaultTarget)) {
  774. // shouldn't need to check but...
  775. System.out.println("Default target: " + defaultTarget);
  776. }
  777. }
  778. /**
  779. * Searches for the correct place to insert a name into a list so as
  780. * to keep the list sorted alphabetically.
  781. *
  782. * @param names The current list of names. Must not be <code>null</code>.
  783. * @param name The name to find a place for.
  784. * Must not be <code>null</code>.
  785. *
  786. * @return the correct place in the list for the given name
  787. */
  788. private static int findTargetPosition(Vector names, String name) {
  789. int res = names.size();
  790. for (int i = 0; i < names.size() && res == names.size(); i++) {
  791. if (name.compareTo((String) names.elementAt(i)) < 0) {
  792. res = i;
  793. }
  794. }
  795. return res;
  796. }
  797. /**
  798. * Writes a formatted list of target names to <code>System.out</code>
  799. * with an optional description.
  800. *
  801. * @param names The names to be printed.
  802. * Must not be <code>null</code>.
  803. * @param descriptions The associated target descriptions.
  804. * May be <code>null</code>, in which case
  805. * no descriptions are displayed.
  806. * If non-<code>null</code>, this should have
  807. * as many elements as <code>names</code>.
  808. * @param heading The heading to display.
  809. * Should not be <code>null</code>.
  810. * @param maxlen The maximum length of the names of the targets.
  811. * If descriptions are given, they are padded to this
  812. * position so they line up (so long as the names really
  813. * <i>are</i> shorter than this).
  814. */
  815. private static void printTargets(Vector names, Vector descriptions,
  816. String heading, int maxlen) {
  817. // now, start printing the targets and their descriptions
  818. String lSep = System.getProperty("line.separator");
  819. // got a bit annoyed that I couldn't find a pad function
  820. String spaces = " ";
  821. while (spaces.length() < maxlen) {
  822. spaces += spaces;
  823. }
  824. StringBuffer msg = new StringBuffer();
  825. msg.append(heading + lSep + lSep);
  826. for (int i = 0; i < names.size(); i++) {
  827. msg.append(" ");
  828. msg.append(names.elementAt(i));
  829. if (descriptions != null) {
  830. msg.append(spaces.substring(0, maxlen - ((String) names.elementAt(i)).length() + 2));
  831. msg.append(descriptions.elementAt(i));
  832. }
  833. msg.append(lSep);
  834. }
  835. System.out.println(msg.toString());
  836. }
  837. }