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

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