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

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