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

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