Source code: org/eclipse/ant/internal/ui/antsupport/InternalAntRunner.java
1 /*******************************************************************************
2 * Copyright (c) 2000, 2005 IBM Corporation and others.
3 * Portions Copyright 2000-2005 The Apache Software Foundation
4 * All rights reserved. This program and the accompanying materials are made
5 * available under the terms of the Apache Software License v2.0 which
6 * accompanies this distribution and is available at
7 * http://www.apache.org/licenses/LICENSE-2.0.
8 *
9 * Contributors:
10 * IBM Corporation - derived implementation
11 *******************************************************************************/
12
13 package org.eclipse.ant.internal.ui.antsupport;
14
15 import java.io.File;
16 import java.io.FileInputStream;
17 import java.io.FileNotFoundException;
18 import java.io.FileOutputStream;
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.io.PrintStream;
22 import java.text.MessageFormat;
23 import java.util.ArrayList;
24 import java.util.Collections;
25 import java.util.Enumeration;
26 import java.util.HashMap;
27 import java.util.Iterator;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Properties;
31 import java.util.Vector;
32
33 import org.apache.tools.ant.AntTypeDefinition;
34 import org.apache.tools.ant.BuildEvent;
35 import org.apache.tools.ant.BuildException;
36 import org.apache.tools.ant.BuildListener;
37 import org.apache.tools.ant.BuildLogger;
38 import org.apache.tools.ant.ComponentHelper;
39 import org.apache.tools.ant.DefaultLogger;
40 import org.apache.tools.ant.DemuxOutputStream;
41 import org.apache.tools.ant.Diagnostics;
42 import org.apache.tools.ant.Main;
43 import org.apache.tools.ant.Project;
44 import org.apache.tools.ant.ProjectHelper;
45 import org.apache.tools.ant.Target;
46 import org.apache.tools.ant.Task;
47 import org.apache.tools.ant.TaskAdapter;
48 import org.apache.tools.ant.util.FileUtils;
49
50 /**
51 * Eclipse application entry point into Ant in a separate VM. Derived from the original Ant Main class
52 * to ensure that the functionality is equivalent when running in the platform.
53 */
54 public class InternalAntRunner {
55
56 /**
57 * Message priority for project help messages.
58 */
59 public static final int MSG_PROJECT_HELP= Project.MSG_DEBUG + 1;
60
61 private List buildListeners;
62
63 private String buildFileLocation;
64
65 /**
66 * Targets we want to run.
67 */
68 private Vector targets;
69
70 private Map userProperties;
71
72 private Project currentProject;
73
74 private String defaultTarget;
75
76 private BuildLogger buildLogger= null;
77
78 private Map eclipseSpecifiedTasks;
79 private Map eclipseSpecifiedTypes;
80
81 /**
82 * Cache of the Ant version number when it has been loaded
83 */
84 private String antVersionNumber= null;
85
86 /** Our current message output status. Follows Project.MSG_XXX */
87 private int messageOutputLevel = Project.MSG_INFO;
88
89 /** Indicates whether output to the log is to be unadorned. */
90 private boolean emacsMode = false;
91
92 /** Indicates we should only parse and display the project help information */
93 private boolean projectHelp = false;
94
95 /** Stream that we are using for logging */
96 private PrintStream out = System.out;
97
98 /** Stream that we are using for logging error messages */
99 private PrintStream err = System.err;
100
101 /**
102 * The Ant logger class. There may be only one logger. It will have the
103 * right to use the 'out' PrintStream. The class must implement the BuildLogger
104 * interface. An empty String indicates that no logger is to be used. A <code>null</code>
105 * name indicates that the org.apache.tools.ant.DefaultLogger will be used.
106 */
107 private String loggerClassname = null;
108
109 /** Extra arguments to be parsed as command line arguments. */
110 private String[] extraArguments = null;
111
112 private boolean scriptExecuted= false;
113
114 private List propertyFiles= new ArrayList();
115
116 /**
117 * The Ant InputHandler class. There may be only one input handler.
118 */
119 private String inputHandlerClassname = null;
120
121 /**
122 * Indicates whether to execute all targets that
123 * do not depend on failed targets
124 * @since Ant 1.6.0
125 */
126 private boolean keepGoing= false;
127
128 /**
129 * Indicates whether this build is to support interactive input
130 * @since Ant 1.6.0
131 */
132 private boolean allowInput = true;
133
134 public static void main(String[] args) {
135 try {
136 new InternalAntRunner().run(getArrayList(args));
137 } catch (Throwable t) {
138 t.printStackTrace();
139 System.exit(1);
140 }
141 System.exit(0);
142 }
143
144 private void addBuildListeners(Project project) {
145 String className= null;
146 try {
147 BuildLogger logger= createLogger();
148 if (logger != null) {
149 project.addBuildListener(logger);
150 }
151 if (buildListeners != null) {
152 for (Iterator iterator = buildListeners.iterator(); iterator.hasNext();) {
153 className = (String) iterator.next();
154 Class listener = Class.forName(className);
155 project.addBuildListener((BuildListener) listener.newInstance());
156 }
157 }
158 } catch (ClassCastException e) {
159 String message = MessageFormat.format(InternalAntMessages.getString("InternalAntRunner.{0}_which_was_specified_to_be_a_build_listener_is_not_an_instance_of_org.apache.tools.ant.BuildListener._1"), new String[]{className}); //$NON-NLS-1$
160 logMessage(null, message, Project.MSG_ERR);
161 throw new BuildException(message, e);
162 } catch (BuildException e) {
163 throw e;
164 } catch (Exception e) {
165 throw new BuildException(e);
166 }
167 }
168
169 /**
170 * Parses the build file and adds necessary information into
171 * the given project.
172 * @param project The project to configure
173 */
174 private void parseBuildFile(Project project) {
175 File buildFile = new File(getBuildFileLocation());
176 if (!buildFile.exists()) {
177 throw new BuildException(MessageFormat.format(InternalAntMessages.getString("InternalAntRunner.Buildfile__{0}_does_not_exist_!_1"), //$NON-NLS-1$
178 new String[]{buildFile.getAbsolutePath()}));
179 }
180 if (!buildFile.isFile()) {
181 throw new BuildException(MessageFormat.format(InternalAntMessages.getString("InternalAntRunner.Buildfile__{0}_is_not_a_file_1"), //$NON-NLS-1$
182 new String[]{buildFile.getAbsolutePath()}));
183 }
184
185 ProjectHelper helper = ProjectHelper.getProjectHelper();
186 project.addReference("ant.projectHelper", helper); //$NON-NLS-1$
187 helper.parse(project, buildFile);
188 }
189
190 private void printArguments(Project project) {
191 if ((messageOutputLevel != Project.MSG_DEBUG) && (messageOutputLevel != Project.MSG_VERBOSE)) {
192 return;
193 }
194 StringBuffer sb = new StringBuffer();
195 for (int i = 0; i < extraArguments.length; i++) {
196 sb.append(extraArguments[i]);
197 sb.append(' ');
198 }
199 project.log(MessageFormat.format(InternalAntMessages.getString("InternalAntRunner.Arguments__{0}_2"), new String[]{sb.toString().trim()})); //$NON-NLS-1$
200 }
201
202
203 /**
204 * Logs a message with the client that lists the targets
205 * in a project
206 *
207 * @param project the project to list targets from
208 */
209 private void printTargets(Project project) {
210 //notify the logger that project help message are coming
211 //since there is no buildstarted or targetstarted to
212 //to be used to establish the connection
213 logMessage(project, "", MSG_PROJECT_HELP); //$NON-NLS-1$
214 // find the target with the longest name
215 int maxLength = 0;
216 Enumeration ptargets = project.getTargets().elements();
217 String targetName;
218 String targetDescription;
219 Target currentTarget;
220 // split the targets in top-level and sub-targets depending
221 // on the presence of a description
222 List topNames = new ArrayList();
223 List topDescriptions = new ArrayList();
224 List subNames = new ArrayList();
225
226 while (ptargets.hasMoreElements()) {
227 currentTarget = (Target) ptargets.nextElement();
228 targetName = currentTarget.getName();
229 targetDescription = currentTarget.getDescription();
230 if (targetDescription == null) {
231 subNames.add(targetName);
232 } else {
233 topNames.add(targetName);
234 topDescriptions.add(targetDescription);
235 if (targetName.length() > maxLength) {
236 maxLength = targetName.length();
237 }
238 }
239 }
240
241 Collections.sort(subNames);
242 Collections.sort(topNames);
243 Collections.sort(topDescriptions);
244
245 String defaultTargetName = project.getDefaultTarget();
246 if (defaultTargetName != null && !"".equals(defaultTargetName)) { // shouldn't need to check but... //$NON-NLS-1$
247 List defaultName = new ArrayList(1);
248 List defaultDesc = null;
249 defaultName.add(defaultTargetName);
250
251 int indexOfDefDesc = topNames.indexOf(defaultTargetName);
252 if (indexOfDefDesc >= 0) {
253 defaultDesc = new ArrayList(1);
254 defaultDesc.add(topDescriptions.get(indexOfDefDesc));
255 }
256 printTargets(project, defaultName, defaultDesc, InternalAntMessages.getString("InternalAntRunner.Default_target__3"), maxLength); //$NON-NLS-1$
257
258 }
259
260 printTargets(project, topNames, topDescriptions, InternalAntMessages.getString("InternalAntRunner.Main_targets__4"), maxLength); //$NON-NLS-1$
261 printTargets(project, subNames, null, InternalAntMessages.getString("InternalAntRunner.Subtargets__5"), 0); //$NON-NLS-1$
262 }
263
264 /**
265 * Logs a message with the client that lists the target names and optional descriptions
266 *
267 * @param project the enclosing target
268 * @param names the targets names
269 * @param descriptions the corresponding descriptions
270 * @param heading the message heading
271 * @param maxlen maximum length that can be allocated for a name
272 */
273 private void printTargets(Project project, List names, List descriptions, String heading, int maxlen) {
274 // now, start printing the targets and their descriptions
275 String lSep = System.getProperty("line.separator"); //$NON-NLS-1$
276
277 String spaces = " "; //$NON-NLS-1$
278 while (spaces.length() < maxlen) {
279 spaces += spaces;
280 }
281 StringBuffer msg = new StringBuffer();
282 msg.append(heading + lSep + lSep);
283 for (int i = 0; i < names.size(); i++) {
284 msg.append(' ');
285 msg.append(names.get(i));
286 if (descriptions != null) {
287 msg.append(spaces.substring(0, maxlen - ((String) names.get(i)).length() + 2));
288 msg.append(descriptions.get(i));
289 }
290 msg.append(lSep);
291 }
292 logMessage(project, msg.toString(), Project.MSG_INFO);
293 }
294
295 /*
296 * Note that the list passed to this method must support
297 * List#remove(Object)
298 */
299 private void run(List argList) {
300 setCurrentProject(new Project());
301 Throwable error = null;
302 PrintStream originalErr = System.err;
303 PrintStream originalOut = System.out;
304 InputStream originalIn= System.in;
305
306 SecurityManager originalSM= System.getSecurityManager();
307 scriptExecuted= true;
308 try {
309 if (argList != null && (argList.remove("-projecthelp") || argList.remove("-p"))) { //$NON-NLS-1$ //$NON-NLS-2$
310 projectHelp = true;
311 }
312 getCurrentProject().init();
313 if (argList != null) {
314 scriptExecuted= preprocessCommandLine(argList);
315
316 if (!scriptExecuted) {
317 return;
318 }
319 }
320
321 addBuildListeners(getCurrentProject());
322
323 processProperties(argList);
324
325 setProperties(getCurrentProject());
326
327 addInputHandler(getCurrentProject());
328
329 remapSystemIn();
330 System.setOut(new PrintStream(new DemuxOutputStream(getCurrentProject(), false)));
331 System.setErr(new PrintStream(new DemuxOutputStream(getCurrentProject(), true)));
332
333 if (!projectHelp) {
334 fireBuildStarted(getCurrentProject());
335 }
336
337 if (argList != null && !argList.isEmpty()) {
338 try {
339 scriptExecuted= processCommandLine(argList);
340 } catch (BuildException e) {
341 scriptExecuted= false;
342 throw e;
343 }
344 }
345 if (!scriptExecuted) {
346 return;
347 }
348
349 //we do not want to set the default input stream for the default input handler
350 //as we currently have no means for querying from System.in for input.
351 //see bug 45484
352 if (allowInput && inputHandlerClassname != null) {
353 if (isVersionCompatible("1.6")) { //$NON-NLS-1$
354 getCurrentProject().setDefaultInputStream(originalIn);
355 }
356 }
357
358 getCurrentProject().log(MessageFormat.format(InternalAntMessages.getString("InternalAntRunner.Build_file__{0}_1"), new String[]{getBuildFileLocation()})); //$NON-NLS-1$
359
360 setTasks();
361 setTypes();
362
363 if (isVersionCompatible("1.6")) { //$NON-NLS-1$
364 getCurrentProject().setKeepGoingMode(keepGoing);
365 }
366
367 parseBuildFile(getCurrentProject());
368 validateDefaultTarget();
369
370 if (projectHelp) {
371 printHelp(getCurrentProject());
372 scriptExecuted= false;
373 return;
374 }
375
376 if (extraArguments != null) {
377 printArguments(getCurrentProject());
378 }
379
380 System.setSecurityManager(new AntSecurityManager(originalSM));
381
382 if (targets != null && !targets.isEmpty()) {
383 getCurrentProject().executeTargets(targets);
384 } else {
385 getCurrentProject().executeTarget(getCurrentProject().getDefaultTarget());
386 }
387 } catch (AntSecurityException e) {
388 //expected
389 } catch (Throwable e) {
390 error = e;
391 } finally {
392 System.setErr(originalErr);
393 System.setOut(originalOut);
394 System.setIn(originalIn);
395 if (System.getSecurityManager() instanceof AntSecurityManager) {
396 System.setSecurityManager(originalSM);
397 }
398
399 if (!projectHelp) {
400 fireBuildFinished(getCurrentProject(), error);
401 }
402
403 //close any user specified build log
404 if (err != originalErr) {
405 err.close();
406 }
407 if (out != originalOut) {
408 out.close();
409 }
410 }
411 }
412
413 private void setTasks() {
414 if (eclipseSpecifiedTasks != null) {
415 Iterator itr= eclipseSpecifiedTasks.keySet().iterator();
416 String taskName;
417 String taskClassName;
418 while (itr.hasNext()) {
419 taskName= (String) itr.next();
420 taskClassName= (String) eclipseSpecifiedTasks.get(taskName);
421
422 if (isVersionCompatible("1.6")) { //$NON-NLS-1$
423 AntTypeDefinition def= new AntTypeDefinition();
424 def.setName(taskName);
425 def.setClassName(taskClassName);
426 def.setClassLoader(this.getClass().getClassLoader());
427 def.setAdaptToClass(Task.class);
428 def.setAdapterClass(TaskAdapter.class);
429 ComponentHelper.getComponentHelper(getCurrentProject()).addDataTypeDefinition(def);
430 } else {
431 try {
432 Class taskClass = Class.forName(taskClassName);
433 getCurrentProject().addTaskDefinition(taskName, taskClass);
434 } catch (ClassNotFoundException e) {
435 String message= MessageFormat.format(InternalAntMessages.getString("InternalAntRunner.161"), new String[]{taskClassName, taskName}); //$NON-NLS-1$
436 getCurrentProject().log(message, Project.MSG_WARN);
437 }
438 }
439 }
440 }
441 }
442
443 private void setTypes() {
444 if (eclipseSpecifiedTypes != null) {
445 Iterator itr= eclipseSpecifiedTypes.keySet().iterator();
446 String typeName;
447 String typeClassName;
448 while (itr.hasNext()) {
449 typeName = (String) itr.next();
450 typeClassName= (String) eclipseSpecifiedTypes.get(typeName);
451 if (isVersionCompatible("1.6")) { //$NON-NLS-1$
452 AntTypeDefinition def = new AntTypeDefinition();
453 def.setName(typeName);
454 def.setClassName(typeClassName);
455 def.setClassLoader(this.getClass().getClassLoader());
456 ComponentHelper.getComponentHelper(getCurrentProject()).addDataTypeDefinition(def);
457 } else {
458 try {
459 Class typeClass = Class.forName(typeClassName);
460 getCurrentProject().addDataTypeDefinition(typeName, typeClass);
461 } catch (ClassNotFoundException e) {
462 String message= MessageFormat.format(InternalAntMessages.getString("InternalAntRunner.162"), new String[]{typeClassName, typeName}); //$NON-NLS-1$
463 getCurrentProject().log(message, Project.MSG_WARN);
464 }
465 }
466 }
467 }
468 }
469
470 private void remapSystemIn() {
471 if (!isVersionCompatible("1.6")) { //$NON-NLS-1$
472 return;
473 }
474 DemuxInputStreamSetter setter= new DemuxInputStreamSetter();
475 setter.remapSystemIn(getCurrentProject());
476 }
477
478 private void validateDefaultTarget() {
479 defaultTarget = getCurrentProject().getDefaultTarget();
480
481 Enumeration currentTargets = getCurrentProject().getTargets().elements();
482 boolean defaultFound= false;
483 while (currentTargets.hasMoreElements()) {
484 Target target = (Target) currentTargets.nextElement();
485 if (target.getName().equals(defaultTarget)) {
486 defaultFound= true;
487 break;
488 }
489 }
490
491 if (!defaultFound) {
492 //default target must exist
493 throw new BuildException(MessageFormat.format(InternalAntMessages.getString("InternalAntRunner.Default_target_{0}{1}{2}_does_not_exist_in_this_project_1"), new String[]{"'", defaultTarget, "'"})); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
494 }
495 }
496
497 /**
498 * Creates and returns the default build logger for logging build events to the ant log.
499 *
500 * @return the default build logger for logging build events to the ant log
501 * can return <code>null</code> if no logging is to occur
502 */
503 private BuildLogger createLogger() {
504 if (loggerClassname == null) {
505 buildLogger= new DefaultLogger();
506 } else if (!"".equals(loggerClassname)) { //$NON-NLS-1$
507 try {
508 buildLogger = (BuildLogger) (Class.forName(loggerClassname).newInstance());
509 } catch (ClassCastException e) {
510 String message = MessageFormat.format(InternalAntMessages.getString("InternalAntRunner.{0}_which_was_specified_to_perform_logging_is_not_an_instance_of_org.apache.tools.ant.BuildLogger._2"), new String[]{loggerClassname}); //$NON-NLS-1$
511 logMessage(null, message, Project.MSG_ERR);
512 throw new BuildException(message, e);
513 } catch (Exception e) {
514 String message = MessageFormat.format(InternalAntMessages.getString("InternalAntRunner.Unable_to_instantiate_logger__{0}_6"), new String[]{loggerClassname}); //$NON-NLS-1$
515 logMessage(null, message, Project.MSG_ERR);
516 throw new BuildException(message, e);
517 }
518 }
519
520 if (buildLogger != null) {
521 buildLogger.setMessageOutputLevel(messageOutputLevel);
522 buildLogger.setOutputPrintStream(out);
523 buildLogger.setErrorPrintStream(err);
524 buildLogger.setEmacsMode(emacsMode);
525 }
526
527 return buildLogger;
528 }
529
530 /*
531 * We only have to do this because Project.fireBuildStarted is protected. If it becomes
532 * public we should remove this method and call the appropriate one.
533 */
534 private void fireBuildStarted(Project project) {
535 BuildEvent event = new BuildEvent(project);
536 for (Iterator iterator = project.getBuildListeners().iterator(); iterator.hasNext();) {
537 BuildListener listener = (BuildListener) iterator.next();
538 listener.buildStarted(event);
539 }
540 }
541
542 private void fireBuildFinished(Project project, Throwable error) {
543 if (error == null && scriptExecuted) {
544 logMessage(project, InternalAntMessages.getString("InternalAntRunner.BUILD_SUCCESSFUL_1"), messageOutputLevel); //$NON-NLS-1$
545 }
546 project.fireBuildFinished(error);
547 }
548
549 private void logMessage(Project project, String message, int priority) {
550 if (project != null) {
551 project.log(message, priority);
552 } else {
553 if (buildListeners != null) {
554 project = new Project();
555 BuildEvent event = new BuildEvent(project);
556 event.setMessage(message, priority);
557 //notify the build listeners that are not registered as
558 //no project existed
559 for (Iterator iterator = buildListeners.iterator(); iterator.hasNext();) {
560 try {
561 BuildListener listener = (BuildListener) iterator.next();
562 listener.messageLogged(event);
563 } catch (ClassCastException e) {
564 //ignore we could be trying to log that a build listener is the
565 //wrong type of class
566 }
567 }
568 }
569 }
570 }
571
572 /**
573 * Sets the buildFileLocation.
574 *
575 * @param buildFileLocation the file system location of the build file
576 */
577 private void setBuildFileLocation(String buildFileLocation) {
578 this.buildFileLocation = buildFileLocation;
579 if (getCurrentProject() != null) {
580 getCurrentProject().setUserProperty("ant.file", buildFileLocation); //$NON-NLS-1$
581 }
582 }
583
584 private String getBuildFileLocation() {
585 if (buildFileLocation == null) {
586 buildFileLocation = new File("build.xml").getAbsolutePath(); //$NON-NLS-1$
587 }
588 return buildFileLocation;
589 }
590
591 /**
592 * Sets the message output level. Use -1 for none.
593 * @param level The message output level
594 */
595 private void setMessageOutputLevel(int level) {
596 messageOutputLevel = level;
597 if (buildLogger != null) {
598 buildLogger.setMessageOutputLevel(level);
599 }
600 }
601
602 /*
603 * Returns a String representation of the Ant version number as specified
604 * in the version.txt file.
605 */
606 private String getAntVersionNumber() throws BuildException {
607 if (antVersionNumber == null) {
608 try {
609 Properties props = new Properties();
610 InputStream in = Main.class.getResourceAsStream("/org/apache/tools/ant/version.txt"); //$NON-NLS-1$
611 props.load(in);
612 in.close();
613 String versionNumber= props.getProperty("VERSION"); //$NON-NLS-1$
614 antVersionNumber= versionNumber;
615 } catch (IOException ioe) {
616 throw new BuildException(MessageFormat.format(InternalAntMessages.getString("InternalAntRunner.Could_not_load_the_version_information._{0}_9"), new String[]{ioe.getMessage()})); //$NON-NLS-1$
617 } catch (NullPointerException npe) {
618 throw new BuildException(InternalAntMessages.getString("InternalAntRunner.Could_not_load_the_version_information._10")); //$NON-NLS-1$
619 }
620 }
621 return antVersionNumber;
622 }
623
624 /*
625 * Returns whether the given version is compatible with the
626 * current Ant version. A version is compatible if it is less
627 * than or equal to the current version.
628 */
629 private boolean isVersionCompatible(String comparison) {
630 String version= getAntVersionNumber();
631 return version.compareTo(comparison) >= 0;
632 }
633
634 private boolean preprocessCommandLine(List commands) {
635
636 String arg = getArgument(commands, "-listener"); //$NON-NLS-1$
637 while (arg != null) {
638 if (arg.length() == 0) {
639 throw new BuildException(InternalAntMessages.getString("InternalAntRunner.You_must_specify_a_classname_when_using_the_-listener_argument_1")); //$NON-NLS-1$
640 }
641 if (buildListeners == null) {
642 buildListeners= new ArrayList(1);
643 }
644 buildListeners.add(arg);
645 arg = getArgument(commands, "-listener"); //$NON-NLS-1$
646 }
647
648 arg = getArgument(commands, "-logger"); //$NON-NLS-1$
649 if (arg != null) {
650 if (arg.length() == 0) {
651 throw new BuildException(InternalAntMessages.getString("InternalAntRunner.You_must_specify_a_classname_when_using_the_-logger_argument_2")); //$NON-NLS-1$
652 }
653 loggerClassname = arg;
654 }
655 arg = getArgument(commands, "-logger"); //$NON-NLS-1$
656 if (arg != null) {
657 throw new BuildException(InternalAntMessages.getString("InternalAntRunner.Only_one_logger_class_may_be_specified_1")); //$NON-NLS-1$
658 }
659
660 arg = getArgument(commands, "-inputhandler"); //$NON-NLS-1$
661 if (arg != null) {
662 if (!isVersionCompatible("1.5")) { //$NON-NLS-1$
663 throw new BuildException(InternalAntMessages.getString("InternalAntRunner.Specifying_an_InputHandler_is_an_Ant_1.5.*_feature._Please_update_your_Ant_classpath_to_include_an_Ant_version_greater_than_this._2")); //$NON-NLS-1$
664 }
665 if (arg.length() == 0) {
666 throw new BuildException(InternalAntMessages.getString("InternalAntRunner.You_must_specify_a_classname_when_using_the_-inputhandler_argument_1")); //$NON-NLS-1$
667 }
668 inputHandlerClassname = arg;
669 }
670 arg = getArgument(commands, "-inputhandler"); //$NON-NLS-1$
671 if (arg != null) {
672 throw new BuildException(InternalAntMessages.getString("InternalAntRunner.Only_one_input_handler_class_may_be_specified._2")); //$NON-NLS-1$
673 }
674 return true;
675 }
676
677 /*
678 * Looks for interesting command line arguments.
679 * Returns whether it is OK to run the script.
680 */
681 private boolean processCommandLine(List commands) {
682
683 if (commands.remove("-help") || commands.remove("-h")) { //$NON-NLS-1$ //$NON-NLS-2$
684 printUsage();
685 return false;
686 }
687
688 if (commands.remove("-version")) { //$NON-NLS-1$
689 printVersion();
690 return false;
691 }
692
693 if (commands.remove("-verbose") || commands.remove("-v")) { //$NON-NLS-1$ //$NON-NLS-2$
694 printVersion();
695 setMessageOutputLevel(Project.MSG_VERBOSE);
696 }
697
698 if (commands.remove("-debug") || commands.remove("-d")) { //$NON-NLS-1$ //$NON-NLS-2$
699 printVersion();
700 setMessageOutputLevel(Project.MSG_DEBUG);
701 }
702
703 if (commands.remove("-quiet") || commands.remove("-q")) { //$NON-NLS-1$ //$NON-NLS-2$
704 setMessageOutputLevel(Project.MSG_WARN);
705 }
706
707 if (commands.remove("-emacs") || commands.remove("-e")) { //$NON-NLS-1$ //$NON-NLS-2$
708 emacsMode = true;
709 if (buildLogger != null) {
710 buildLogger.setEmacsMode(true);
711 }
712 }
713
714 if (commands.remove("-diagnostics")) { //$NON-NLS-1$
715 if (!isVersionCompatible("1.5")) { //$NON-NLS-1$
716 throw new BuildException(InternalAntMessages.getString("InternalAntRunner.The_diagnositics_options_is_an_Ant_1.5.*_feature._Please_update_your_Ant_classpath_to_include_an_Ant_version_greater_than_this._4")); //$NON-NLS-1$
717 }
718 try {
719 Diagnostics.doReport(System.out);
720 } catch (NullPointerException e) {
721 logMessage(getCurrentProject(), InternalAntMessages.getString("InternalAntRunner.ANT_HOME_must_be_set_to_use_Ant_diagnostics_2"), Project.MSG_ERR); //$NON-NLS-1$
722 }
723 return false;
724 }
725
726 String arg = getArgument(commands, "-logfile"); //$NON-NLS-1$
727 if (arg == null) {
728 arg = getArgument(commands, "-l"); //$NON-NLS-1$
729 }
730 if (arg != null) {
731 if (arg.length() == 0) {
732 String message= InternalAntMessages.getString("InternalAntRunner.You_must_specify_a_log_file_when_using_the_-log_argument_3"); //$NON-NLS-1$
733 logMessage(currentProject, message, Project.MSG_ERR);
734 throw new BuildException(message);
735 }
736 try {
737 createLogFile(arg);
738 } catch (IOException e) {
739 // just log message and ignore exception
740 logMessage(getCurrentProject(), MessageFormat.format(InternalAntMessages.getString("InternalAntRunner.Could_not_write_to_the_specified_log_file__{0}._Make_sure_the_path_exists_and_you_have_write_permissions._2"), new String[]{arg}), Project.MSG_ERR); //$NON-NLS-1$
741 return false;
742 }
743
744 }
745
746 arg = getArgument(commands, "-buildfile"); //$NON-NLS-1$
747 if (arg == null) {
748 arg = getArgument(commands, "-file"); //$NON-NLS-1$
749 if (arg == null) {
750 arg = getArgument(commands, "-f"); //$NON-NLS-1$
751 }
752 }
753
754 if (arg != null) {
755 if (arg.length() == 0) {
756 String message= InternalAntMessages.getString("InternalAntRunner.You_must_specify_a_buildfile_when_using_the_-buildfile_argument_4"); //$NON-NLS-1$
757 logMessage(currentProject, message, Project.MSG_ERR);
758 throw new BuildException(message);
759 }
760 setBuildFileLocation(arg);
761 }
762
763 if (isVersionCompatible("1.6")) { //$NON-NLS-1$
764 if (commands.remove("-k") || commands.remove("-keep-going")) { //$NON-NLS-1$ //$NON-NLS-2$
765 keepGoing= true;
766 }
767 if (commands.remove("-noinput")) { //$NON-NLS-1$
768 allowInput= false;
769 }
770 arg= getArgument(commands, "-lib"); //$NON-NLS-1$
771 if (arg != null) {
772 logMessage(currentProject, InternalAntMessages.getString("InternalAntRunner.157"), Project.MSG_ERR); //$NON-NLS-1$
773 return false;
774 }
775 }
776
777 arg= getArgument(commands, "-find"); //$NON-NLS-1$
778 if (arg == null) {
779 arg= getArgument(commands, "-s"); //$NON-NLS-1$
780 }
781 if (arg != null) {
782 logMessage(currentProject, InternalAntMessages.getString("InternalAntRunner.-find_not_supported"), Project.MSG_ERR); //$NON-NLS-1$
783 return false;
784 }
785
786 processTasksAndTypes(commands);
787
788 if ((commands != null) && (!commands.isEmpty())) {
789 processUnrecognizedCommands(commands);
790 }
791
792 if ((commands != null) && (!commands.isEmpty())) {
793 processTargets(commands);
794 }
795
796 return true;
797 }
798
799 private void processTasksAndTypes(List commands) {
800 String arg = getArgument(commands, "-eclipseTask"); //$NON-NLS-1$
801 while (arg != null) {
802 if (eclipseSpecifiedTasks == null) {
803 eclipseSpecifiedTasks= new HashMap();
804 }
805 int index= arg.indexOf(',');
806 if (index != -1) {
807 String name= arg.substring(0, index);
808 String className= arg.substring(index + 1);
809 eclipseSpecifiedTasks.put(name, className);
810 }
811 arg = getArgument(commands, "-eclipseTask"); //$NON-NLS-1$
812 }
813
814 arg = getArgument(commands, "-eclipseType"); //$NON-NLS-1$
815 while (arg != null) {
816 if (eclipseSpecifiedTypes == null) {
817 eclipseSpecifiedTypes= new HashMap();
818 }
819 int index= arg.indexOf(',');
820 if (index != -1) {
821 String name= arg.substring(0, index);
822 String className= arg.substring(index + 1);
823 eclipseSpecifiedTypes.put(name, className);
824 }
825 arg = getArgument(commands, "-eclipseType"); //$NON-NLS-1$
826 }
827 }
828
829 /*
830 * Checks for unrecognized arguments on the command line.
831 * Since there is no syntactic way to distingush between
832 * ant -foo target1 target2
833 * ant -foo fooarg target
834 * we remove everything up to the last argument that
835 * begins with a '-'. In the latter case, above, that
836 * means that there will be an extra target, 'fooarg',
837 * left lying around.
838 */
839 private void processUnrecognizedCommands(List commands) {
840 int p = -1;
841
842 // find the last arg that begins with '-'
843 for (int i = commands.size() - 1; i >= 0; i--) {
844 if (((String) commands.get(0)).startsWith("-")) { //$NON-NLS-1$
845 p = i;
846 break;
847 }
848 }
849 if (p < 0) { return; }
850
851 // remove everything preceding that last '-arg'
852 String s = ""; //$NON-NLS-1$
853 for (int i = 0; i <= p; i++) {
854 s += " " + ((String) commands.get(0)); //$NON-NLS-1$
855 commands.remove(0);
856 }
857
858 // warn of ignored commands
859 String message = MessageFormat.format(InternalAntMessages.getString("InternalAntRunner.Unknown_argument__{0}_2"), new Object[]{ s.substring(1) }); //$NON-NLS-1$
860 logMessage(currentProject, message, Project.MSG_WARN);
861 }
862
863
864 /*
865 * Checks for targets specified at the command line.
866 */
867 private void processTargets(List commands) {
868 if (targets == null) {
869 targets = new Vector(commands.size());
870 }
871 for (Iterator iter = commands.iterator(); iter.hasNext();) {
872 targets.add(iter.next());
873 }
874 }
875
876 /*
877 * Creates the log file with the name specified by the user.
878 * If the fileName is not absolute, the file will be created in the
879 * working directory if specified or in the same directory as the location
880 * of the build file.
881 */
882 private void createLogFile(String fileName) throws FileNotFoundException, IOException {
883 File logFile = getFileRelativeToBaseDir(fileName);
884
885 //this stream is closed in the finally block of run(list)
886 out = new PrintStream(new FileOutputStream(logFile));
887 err = out;
888 logMessage(getCurrentProject(), MessageFormat.format(InternalAntMessages.getString("InternalAntRunner.Using_{0}_file_as_build_log._1"), new String[]{logFile.getCanonicalPath()}), Project.MSG_INFO); //$NON-NLS-1$
889 if (buildLogger != null) {
890 buildLogger.setErrorPrintStream(err);
891 buildLogger.setOutputPrintStream(out);
892 }
893 }
894
895 private File getFileRelativeToBaseDir(String fileName) {
896 File parentFile= null;
897
898 String base= getCurrentProject().getUserProperty("basedir"); //$NON-NLS-1$
899 if (base != null) {
900 parentFile= new File(base);
901 } else {
902 //relative to the build file location
903 parentFile= new File(getBuildFileLocation()).getParentFile();
904 }
905
906 return FileUtils.newFileUtils().resolveFile(parentFile, fileName);
907 }
908
909 /*
910 * Processes cmd line properties and adds the user properties
911 * Any user properties that have been explicitly set are set as well.
912 * Ensures that -D properties take precedence.
913 */
914 private void processProperties(List commands) {
915 //MULTIPLE property files are allowed
916 String arg= getArgument(commands, "-propertyfile"); //$NON-NLS-1$
917 while (arg != null) {
918 if (!isVersionCompatible("1.5")) { //$NON-NLS-1$
919 logMessage(currentProject, InternalAntMessages.getString("InternalAntRunner.Specifying_property_files_is_a_Ant_1.5.*_feature._Please_update_your_Ant_classpath._6"), Project.MSG_ERR); //$NON-NLS-1$
920 break;
921 }
922 if (arg.length() == 0) {
923 String message= InternalAntMessages.getString("InternalAntRunner.You_must_specify_a_property_filename_when_using_the_-propertyfile_argument_3"); //$NON-NLS-1$
924 logMessage(currentProject, message, Project.MSG_ERR);
925 throw new BuildException(message);
926 }
927
928 propertyFiles.add(arg);
929 arg= getArgument(commands, "-propertyfile"); //$NON-NLS-1$
930 }
931
932 if (propertyFiles != null && !propertyFiles.isEmpty()) {
933 loadPropertyFiles();
934 }
935
936 if (commands == null) {
937 return;
938 }
939 processMinusDProperties(commands);
940 }
941
942 private void processMinusDProperties(List commands) {
943 String[] args = (String[]) commands.toArray(new String[commands.size()]);
944 for (int i = 0; i < args.length; i++) {
945 String arg = args[i];
946 if (arg.startsWith("-D")) { //$NON-NLS-1$
947 String name = arg.substring(2, arg.length());
948 String value = null;
949 int posEq = name.indexOf("="); //$NON-NLS-1$
950 if (posEq == 0) {
951 value= name.substring(1);
952 name= ""; //$NON-NLS-1$
953 } else if (posEq > 0 && posEq != name.length() - 1) {
954 value = name.substring(posEq + 1).trim();
955 name = name.substring(0, posEq);
956 }
957
958 if (value == null) {
959 //the user has specified something like "-Debug"
960 continue;
961 }
962 if (userProperties == null) {
963 userProperties= new HashMap();
964 }
965 userProperties.put(name, value);
966 commands.remove(args[i]);
967 }
968 }
969 }
970
971 private void setProperties(Project project) {
972 setBuiltInProperties(project);
973 if (userProperties != null) {
974 for (Iterator iterator = userProperties.entrySet().iterator(); iterator.hasNext();) {
975 Map.Entry entry = (Map.Entry) iterator.next();
976 project.setUserProperty((String) entry.getKey(), (String) entry.getValue());
977 }
978 }
979 }
980
981 private void setBuiltInProperties(Project project) {
982 project.setUserProperty("ant.file", getBuildFileLocation()); //$NON-NLS-1$
983 project.setUserProperty("ant.version", Main.getAntVersion()); //$NON-NLS-1$
984 }
985
986 /*
987 * Print the project description, if any
988 */
989 private void printHelp(Project project) {
990 if (project.getDescription() != null) {
991 logMessage(project, project.getDescription(), Project.MSG_INFO);
992 }
993 printTargets(project);
994 }
995
996 /*
997 * Logs a message with the client indicating the version of <b>Ant</b> that this class
998 * fronts.
999 */
1000 private void printVersion() {
1001 logMessage(getCurrentProject(), Main.getAntVersion(), Project.MSG_INFO);
1002 }
1003
1004 /*
1005 * Logs a message with the client outlining the usage of <b>Ant</b>.
1006 */
1007 private void printUsage() {
1008 String lSep = System.getProperty("line.separator"); //$NON-NLS-1$
1009 StringBuffer msg = new StringBuffer();
1010 msg.append("ant ["); //$NON-NLS-1$
1011 msg.append(InternalAntMessages.getString("InternalAntRunner.options_13")); //$NON-NLS-1$
1012 msg.append("] ["); //$NON-NLS-1$
1013 msg.append(InternalAntMessages.getString("InternalAntRunner.target_15")); //$NON-NLS-1$
1014 msg.append(" ["); //$NON-NLS-1$
1015 msg.append(InternalAntMessages.getString("InternalAntRunner.target_15")); //$NON-NLS-1$
1016 msg.append("2 ["); //$NON-NLS-1$
1017 msg.append(InternalAntMessages.getString("InternalAntRunner.target_15")); //$NON-NLS-1$
1018 msg.append("3] ...]]"); //$NON-NLS-1$
1019 msg.append(lSep);
1020 msg.append(InternalAntMessages.getString("InternalAntRunner.Options___21")); //$NON-NLS-1$
1021 msg.append(lSep);
1022 msg.append("\t-help, -h\t\t\t\t"); //$NON-NLS-1$
1023 msg.append(InternalAntMessages.getString("InternalAntRunner.print_this_message_23")); //$NON-NLS-1$
1024 msg.append(lSep);
1025 msg.append("\t-projecthelp, -p\t\t"); //$NON-NLS-1$
1026 msg.append(InternalAntMessages.getString("InternalAntRunner.print_project_help_information_25")); //$NON-NLS-1$
1027 msg.append(lSep);
1028 msg.append("\t-version\t\t\t\t"); //$NON-NLS-1$
1029 msg.append(InternalAntMessages.getString("InternalAntRunner.print_the_version_information_and_exit_27")); //$NON-NLS-1$
1030 msg.append(lSep);
1031 msg.append("\t-diagnostics\t\t\t"); //$NON-NLS-1$
1032 msg.append(InternalAntMessages.getString("InternalAntRunner.12")); //$NON-NLS-1$
1033 msg.append(lSep);
1034 msg.append(InternalAntMessages.getString("InternalAntRunner.13")); //$NON-NLS-1$
1035 msg.append(lSep);
1036 msg.append("\t-quiet, -q\t\t\t"); //$NON-NLS-1$
1037 msg.append(InternalAntMessages.getString("InternalAntRunner.be_extra_quiet_29")); //$NON-NLS-1$
1038 msg.append(lSep);
1039 msg.append("\t-verbose, -v\t\t\t"); //$NON-NLS-1$
1040 msg.append(InternalAntMessages.getString("InternalAntRunner.be_extra_verbose_31")); //$NON-NLS-1$
1041 msg.append(lSep);
1042 msg.append("\t-debug, -d\t\t\t"); //$NON-NLS-1$
1043 msg.append(InternalAntMessages.getString("InternalAntRunner.print_debugging_information_33")); //$NON-NLS-1$
1044 msg.append(lSep);
1045 msg.append("\t-emacs, -e\t\t\t"); //$NON-NLS-1$
1046 msg.append(InternalAntMessages.getString("InternalAntRunner.produce_logging_information_without_adornments_35")); //$NON-NLS-1$
1047 msg.append(lSep);
1048 msg.append("\t-logfile\t<file>\t\t"); //$NON-NLS-1$
1049 msg.append(InternalAntMessages.getString("InternalAntRunner.use_given_file_for_log_37")); //$NON-NLS-1$
1050 msg.append(lSep);
1051 msg.append("\t\t-l\t<file>"); //$NON-NLS-1$
1052 msg.append(InternalAntMessages.getString("InternalAntRunner.1")); //$NON-NLS-1$ //$NON-NLS-2$
1053 msg.append(lSep);
1054 msg.append("\t-logger <classname>\t\t"); //$NON-NLS-1$
1055 msg.append(InternalAntMessages.getString("InternalAntRunner.the_class_which_is_to_perform_logging_39")); //$NON-NLS-1$
1056 msg.append(lSep);
1057 msg.append("\t-listener <classname>\t"); //$NON-NLS-1$
1058 msg.append(InternalAntMessages.getString("InternalAntRunner.add_an_instance_of_class_as_a_project_listener_41")); //$NON-NLS-1$
1059 msg.append(lSep);
1060 msg.append("\t-noinput\t"); //$NON-NLS-1$
1061 msg.append(InternalAntMessages.getString("InternalAntRunner.158")); //$NON-NLS-1$
1062 msg.append(lSep);
1063 msg.append("\t-buildfile\t<file>\t"); //$NON-NLS-1$
1064 msg.append(InternalAntMessages.getString("InternalAntRunner.use_given_buildfile_43")); //$NON-NLS-1$
1065 msg.append(lSep);
1066 msg.append("\t\t-file\t<file>"); //$NON-NLS-1$
1067 msg.append(InternalAntMessages.getString("InternalAntRunner.1")); //$NON-NLS-1$
1068 msg.append(lSep);
1069 msg.append("\t\t-f\t\t<file>"); //$NON-NLS-1$
1070 msg.append(InternalAntMessages.getString("InternalAntRunner.1")); //$NON-NLS-1$
1071 msg.append(lSep);
1072 msg.append("\t-D<property>=<value>\t"); //$NON-NLS-1$
1073 msg.append(InternalAntMessages.getString("InternalAntRunner.use_value_for_given_property_45")); //$NON-NLS-1$
1074 msg.append(lSep);
1075 msg.append("\t-keep-going, -k"); //$NON-NLS-1$
1076 msg.append(InternalAntMessages.getString("InternalAntRunner.159")); //$NON-NLS-1$
1077 msg.append(lSep);
1078 msg.append(InternalAntMessages.getString("InternalAntRunner.160")); //$NON-NLS-1$
1079 msg.append(lSep);
1080 msg.append("\t-propertyfile <name>\t"); //$NON-NLS-1$
1081 msg.append(InternalAntMessages.getString("InternalAntRunner.19")); //$NON-NLS-1$
1082 msg.append(lSep);
1083 msg.append(InternalAntMessages.getString("InternalAntRunner.20")); //$NON-NLS-1$
1084 msg.append(lSep);
1085 msg.append("\t-inputhandler <class>\t"); //$NON-NLS-1$
1086 msg.append(InternalAntMessages.getString("InternalAntRunner.22")); //$NON-NLS-1$
1087 msg.append(lSep);
1088
1089 logMessage(getCurrentProject(), msg.toString(), Project.MSG_INFO);
1090 }
1091
1092 /*
1093 * From a command line list, return the argument for the given parameter.
1094 * The parameter and its argument are removed from the list.
1095 *
1096 * @return <code>null</code> if the parameter is not found
1097 * or an empty String if no arguments are found
1098 */
1099 private String getArgument(List commands, String param) {
1100 if (commands == null) {
1101 return null;
1102 }
1103 int index = commands.indexOf(param);
1104 if (index == -1) {
1105 return null;
1106 }
1107 commands.remove(index);
1108 if (index == commands.size()) {// if this is the last command
1109 return ""; //$NON-NLS-1$
1110 }
1111
1112 String command = (String) commands.get(index);
1113 if (command.startsWith("-")) { //new parameter //$NON-NLS-1$
1114 return ""; //$NON-NLS-1$
1115 }
1116
1117 commands.remove(index);
1118 return command;
1119 }
1120
1121 /*
1122 * Helper method to ensure an array is converted into an ArrayList.
1123 */
1124 private static ArrayList getArrayList(String[] args) {
1125 if (args == null) {
1126 return null;
1127 }
1128 // We could be using Arrays.asList() here, but it does not specify
1129 // what kind of list it will return. We need a list that
1130 // implements the method List.remove(Object) and ArrayList does.
1131 ArrayList result = new ArrayList(args.length);
1132 for (int i = 0; i < args.length; i++) {
1133 result.add(args[i]);
1134 }
1135 return result;
1136 }
1137
1138 private Project getCurrentProject() {
1139 return currentProject;
1140 }
1141
1142 private void setCurrentProject(Project currentProject) {
1143 this.currentProject = currentProject;
1144 }
1145
1146 /**
1147 * Load all properties from the files
1148 * specified by -propertyfile.
1149 */
1150 private void loadPropertyFiles() {
1151 Iterator itr= propertyFiles.iterator();
1152 while (itr.hasNext()) {
1153 String filename= (String) itr.next();
1154 File file= getFileRelativeToBaseDir(filename);
1155 Properties props = new Properties();
1156 FileInputStream fis = null;
1157 try {
1158 fis = new FileInputStream(file);
1159 props.load(fis);
1160 } catch (IOException e) {
1161 String msg= MessageFormat.format(InternalAntMessages.getString("InternalAntRunner.Could_not_load_property_file_{0}__{1}_4"), new String[]{filename, e.getMessage()}); //$NON-NLS-1$
1162 logMessage(getCurrentProject(), msg, Project.MSG_ERR);
1163 } finally {
1164 if (fis != null) {
1165 try {
1166 fis.close();
1167 } catch (IOException e){
1168 }
1169 }
1170 }
1171
1172 if (userProperties == null) {
1173 userProperties= new HashMap();
1174 }
1175 Enumeration propertyNames = props.propertyNames();
1176 while (propertyNames.hasMoreElements()) {
1177 String name = (String) propertyNames.nextElement();
1178 //most specific to global
1179 //do not overwrite specific with a global property
1180 if (userProperties.get(name) == null) {
1181 userProperties.put(name, props.getProperty(name));
1182 }
1183 }
1184 }
1185 }
1186
1187 /*
1188 * Creates the InputHandler and adds it to the project.
1189 *
1190 * @exception BuildException if a specified InputHandler
1191 * implementation could not be loaded.
1192 */
1193 private void addInputHandler(Project project) {
1194 if (!isVersionCompatible("1.5")) { //$NON-NLS-1$
1195 return;
1196 }
1197 InputHandlerSetter setter= new InputHandlerSetter();
1198 setter.setInputHandler(project, inputHandlerClassname);
1199 }
1200}