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