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 37 kB

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