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