blob: 339c9c76231e92bd49ee031713d0250ff0b17dab [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2017 IBM Corporation and others.
* Portions Copyright 2000-2005 The Apache Software Foundation
*
* This program and the accompanying materials are made
* available under the terms of the Apache Software License v2.0 which
* accompanies this distribution and is available at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Contributors:
* IBM Corporation - derived implementation
* Rob Stryker - Bug 459188 - InternalAntRunner tries to set null user property
*******************************************************************************/
package org.eclipse.ant.internal.launching.remote;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.Vector;
import org.apache.tools.ant.AntTypeDefinition;
import org.apache.tools.ant.BuildEvent;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.BuildListener;
import org.apache.tools.ant.BuildLogger;
import org.apache.tools.ant.ComponentHelper;
import org.apache.tools.ant.DefaultLogger;
import org.apache.tools.ant.DemuxOutputStream;
import org.apache.tools.ant.Diagnostics;
import org.apache.tools.ant.Main;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.ProjectHelper;
import org.apache.tools.ant.Target;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.TaskAdapter;
import org.apache.tools.ant.util.FileUtils;
import org.eclipse.ant.internal.launching.remote.logger.RemoteAntBuildLogger;
/**
* Eclipse application entry point into Ant in a separate VM. Derived from the original Ant Main class to ensure that the functionality is equivalent
* when running in the platform.
*/
public class InternalAntRunner {
/**
* Message priority for project help messages.
*/
public static final int MSG_PROJECT_HELP = Project.MSG_DEBUG + 1;
private List<String> buildListeners;
private String buildFileLocation;
/**
* Targets we want to run.
*/
private Vector<String> targets;
private Map<String, String> userProperties;
private Project currentProject;
private BuildLogger buildLogger = null;
private Map<String, String> eclipseSpecifiedTasks;
private Map<String, String> eclipseSpecifiedTypes;
/**
* Cache of the Ant version number when it has been loaded
*/
private String antVersionNumber = null;
/** Our current message output status. Follows Project.MSG_XXX */
private int messageOutputLevel = Project.MSG_INFO;
/** Indicates whether output to the log is to be unadorned. */
private boolean emacsMode = false;
/** Indicates we should only parse and display the project help information */
private boolean projectHelp = false;
/** Stream that we are using for logging */
private PrintStream out = System.out;
/** Stream that we are using for logging error messages */
private PrintStream err = System.err;
/**
* The Ant logger class. There may be only one logger. It will have the right to use the 'out' PrintStream. The class must implement the
* BuildLogger interface. An empty String indicates that no logger is to be used. A <code>null</code> name indicates that the
* <code>org.apache.tools.ant.DefaultLogger</code> will be used.
*/
private String loggerClassname = null;
/** Extra arguments to be parsed as command line arguments. */
private String[] extraArguments = null;
private boolean scriptExecuted = false;
@SuppressWarnings("unused")
private List<String> propertyFiles = new ArrayList<String>();
/**
* The Ant InputHandler class. There may be only one input handler.
*/
private String inputHandlerClassname = null;
/**
* Indicates whether to execute all targets that do not depend on failed targets
*
* @since Ant 1.6.0
*/
private boolean keepGoing = false;
/**
* Indicates whether this build is to support interactive input
*
* @since Ant 1.6.0
*/
private boolean allowInput = true;
private String fEarlyErrorMessage = null;
public static void main(String[] args) {
try {
new InternalAntRunner().run(getArrayList(args));
}
catch (Throwable t) {
t.printStackTrace();
System.exit(1);
}
System.exit(0);
}
/*
* Helper method to ensure an array is converted into an ArrayList.
*/
@SuppressWarnings("unused")
static ArrayList<String> getArrayList(String[] args) {
if (args == null) {
return null;
}
// We could be using Arrays.asList() here, but it does not specify
// what kind of list it will return. We need a list that
// implements the method List.remove(Object) and ArrayList does.
ArrayList<String> result = new ArrayList<String>(args.length);
for (int i = 0; i < args.length; i++) {
result.add(args[i]);
}
return result;
}
private void addBuildListeners(Project project) {
String clazz = null;
try {
BuildLogger logger = createLogger();
if (logger != null) {
project.addBuildListener(logger);
}
if (buildListeners != null) {
for (String className : buildListeners) {
clazz = className;
Class<?> listener = Class.forName(className);
project.addBuildListener((BuildListener) listener.newInstance());
}
}
}
catch (ClassCastException e) {
String message = MessageFormat.format(RemoteAntMessages.getString("InternalAntRunner.{0}_which_was_specified_to_be_a_build_listener_is_not_an_instance_of_org.apache.tools.ant.BuildListener._1"), new Object[] { //$NON-NLS-1$
clazz });
logMessage(null, message, Project.MSG_ERR);
throw new BuildException(message, e);
}
catch (BuildException e) {
throw e;
}
catch (Exception e) {
throw new BuildException(e);
}
}
/**
* Parses the build file and adds necessary information into the given project.
*
* @param project
* The project to configure
*/
private void parseBuildFile(Project project) {
File buildFile = new File(getBuildFileLocation());
if (!buildFile.exists()) {
throw new BuildException(MessageFormat.format(RemoteAntMessages.getString("InternalAntRunner.Buildfile__{0}_does_not_exist_!_1"), //$NON-NLS-1$
new Object[] { buildFile.getAbsolutePath() }));
}
if (!buildFile.isFile()) {
throw new BuildException(MessageFormat.format(RemoteAntMessages.getString("InternalAntRunner.Buildfile__{0}_is_not_a_file_1"), //$NON-NLS-1$
new Object[] { buildFile.getAbsolutePath() }));
}
if (!isVersionCompatible("1.5")) { //$NON-NLS-1$
parseBuildFile(project, buildFile);
} else {
ProjectHelper helper = ProjectHelper.getProjectHelper();
project.addReference("ant.projectHelper", helper); //$NON-NLS-1$
helper.parse(project, buildFile);
}
}
/**
* @deprecated support for Ant older than 1.5
*/
@Deprecated
private void parseBuildFile(Project project, File buildFile) {
ProjectHelper.configureProject(project, buildFile);
}
private void printArguments(Project project) {
if ((messageOutputLevel != Project.MSG_DEBUG) && (messageOutputLevel != Project.MSG_VERBOSE)) {
return;
}
StringBuffer sb = new StringBuffer();
for (int i = 0; i < extraArguments.length; i++) {
sb.append(extraArguments[i]);
sb.append(' ');
}
project.log(MessageFormat.format(RemoteAntMessages.getString("InternalAntRunner.Arguments__{0}_2"), new Object[] { sb.toString().trim() })); //$NON-NLS-1$
}
/**
* Logs a message with the client that lists the targets in a project
*
* @param project
* the project to list targets from
*/
@SuppressWarnings("unused")
private void printTargets(Project project) {
// notify the logger that project help message are coming
// since there is no buildstarted or targetstarted to
// to be used to establish the connection
logMessage(project, IAntCoreConstants.EMPTY_STRING, MSG_PROJECT_HELP);
// find the target with the longest name
int maxLength = 0;
Enumeration<Target> ptargets = project.getTargets().elements();
String targetName;
String targetDescription;
Target currentTarget;
// split the targets in top-level and sub-targets depending
// on the presence of a description
List<String> topNames = new ArrayList<String>();
List<String> topDescriptions = new ArrayList<String>();
List<String> subNames = new ArrayList<String>();
while (ptargets.hasMoreElements()) {
currentTarget = ptargets.nextElement();
targetName = currentTarget.getName();
targetDescription = currentTarget.getDescription();
if (targetDescription == null) {
subNames.add(targetName);
} else {
topNames.add(targetName);
topDescriptions.add(targetDescription);
if (targetName.length() > maxLength) {
maxLength = targetName.length();
}
}
}
Collections.sort(subNames);
Collections.sort(topNames);
Collections.sort(topDescriptions);
String defaultTargetName = project.getDefaultTarget();
if (defaultTargetName != null && !IAntCoreConstants.EMPTY_STRING.equals(defaultTargetName)) { // shouldn't need to check but...
List<String> defaultName = new ArrayList<String>(1);
List<String> defaultDesc = null;
defaultName.add(defaultTargetName);
int indexOfDefDesc = topNames.indexOf(defaultTargetName);
if (indexOfDefDesc >= 0) {
defaultDesc = new ArrayList<String>(1);
defaultDesc.add(topDescriptions.get(indexOfDefDesc));
}
printTargets(project, defaultName, defaultDesc, RemoteAntMessages.getString("InternalAntRunner.Default_target__3"), maxLength); //$NON-NLS-1$
}
printTargets(project, topNames, topDescriptions, RemoteAntMessages.getString("InternalAntRunner.Main_targets__4"), maxLength); //$NON-NLS-1$
printTargets(project, subNames, null, RemoteAntMessages.getString("InternalAntRunner.Subtargets__5"), 0); //$NON-NLS-1$
}
/**
* Logs a message with the client that lists the target names and optional descriptions
*
* @param project
* the enclosing target
* @param names
* the targets names
* @param descriptions
* the corresponding descriptions
* @param heading
* the message heading
* @param maxlen
* maximum length that can be allocated for a name
*/
private void printTargets(Project project, List<String> names, List<String> descriptions, String heading, int maxlen) {
// now, start printing the targets and their descriptions
String lSep = System.getProperty("line.separator"); //$NON-NLS-1$
String spaces = " "; //$NON-NLS-1$
while (spaces.length() < maxlen) {
spaces += spaces;
}
StringBuffer msg = new StringBuffer();
msg.append(heading + lSep + lSep);
for (int i = 0; i < names.size(); i++) {
msg.append(' ');
msg.append(names.get(i));
if (descriptions != null) {
msg.append(spaces.substring(0, maxlen - names.get(i).length() + 2));
msg.append(descriptions.get(i));
}
msg.append(lSep);
}
logMessage(project, msg.toString(), Project.MSG_INFO);
}
/*
* Note that the list passed to this method must support List#remove(Object)
*/
@SuppressWarnings("unused")
private void run(List<String> argList) {
setCurrentProject(new Project());
if (isVersionCompatible("1.6.3")) { //$NON-NLS-1$
new ExecutorSetter().setExecutor(getCurrentProject());
}
Throwable error = null;
PrintStream originalErr = System.err;
PrintStream originalOut = System.out;
InputStream originalIn = System.in;
SecurityManager originalSM = System.getSecurityManager();
scriptExecuted = true;
try {
if (argList != null && (argList.remove("-projecthelp") || argList.remove("-p"))) { //$NON-NLS-1$ //$NON-NLS-2$
projectHelp = true;
}
getCurrentProject().init();
if (argList != null) {
scriptExecuted = preprocessCommandLine(argList);
if (!scriptExecuted) {
return;
}
}
boolean exceptionState = processProperties(argList);
addBuildListeners(getCurrentProject());
addInputHandler(getCurrentProject());
remapSystemIn();
System.setOut(new PrintStream(new DemuxOutputStream(getCurrentProject(), false)));
System.setErr(new PrintStream(new DemuxOutputStream(getCurrentProject(), true)));
if (!projectHelp) {
fireBuildStarted(getCurrentProject());
}
if (fEarlyErrorMessage != null) {
// an error occurred processing the properties
// build started has fired and we have
// listeners/loggers to report the error
logMessage(getCurrentProject(), fEarlyErrorMessage, Project.MSG_ERR);
if (exceptionState) {
throw new BuildException(fEarlyErrorMessage);
}
}
// properties can only be set after buildStarted as some listeners/loggers
// depend on this (e.g. XMLLogger)
setProperties(getCurrentProject());
if (argList != null && !argList.isEmpty()) {
try {
scriptExecuted = processCommandLine(argList);
}
catch (BuildException e) {
scriptExecuted = false;
throw e;
}
}
if (!scriptExecuted) {
return;
}
// needs to occur after processCommandLine(List)
if (allowInput && (inputHandlerClassname != null && inputHandlerClassname.length() > 0)) {
if (isVersionCompatible("1.6")) { //$NON-NLS-1$
getCurrentProject().setDefaultInputStream(originalIn);
}
} else {
// set the system property that any input handler
// can check to see if handling input is allowed
System.setProperty("eclipse.ant.noInput", "true"); //$NON-NLS-1$//$NON-NLS-2$
if (isVersionCompatible("1.5") && (inputHandlerClassname == null || inputHandlerClassname.length() == 0)) { //$NON-NLS-1$
InputHandlerSetter setter = new InputHandlerSetter();
setter.setInputHandler(getCurrentProject(), "org.eclipse.ant.internal.launching.remote.inputhandler.FailInputHandler"); //$NON-NLS-1$
}
}
getCurrentProject().log(MessageFormat.format(RemoteAntMessages.getString("InternalAntRunner.Build_file__{0}_1"), new Object[] { //$NON-NLS-1$
getBuildFileLocation() }));
setTasks();
setTypes();
if (isVersionCompatible("1.6")) { //$NON-NLS-1$
getCurrentProject().setKeepGoingMode(keepGoing);
}
parseBuildFile(getCurrentProject());
if (projectHelp) {
printHelp(getCurrentProject());
scriptExecuted = false;
return;
}
if (extraArguments != null) {
printArguments(getCurrentProject());
}
System.setSecurityManager(new AntSecurityManager(originalSM, Thread.currentThread()));
if (targets == null) {
targets = new Vector<String>(1);
}
if (targets.isEmpty() && getCurrentProject().getDefaultTarget() != null) {
targets.add(getCurrentProject().getDefaultTarget());
}
if (!isVersionCompatible("1.6.3")) { //$NON-NLS-1$
getCurrentProject().addReference(IAntCoreConstants.TARGET_VECTOR_NAME, targets);
}
getCurrentProject().executeTargets(targets);
}
catch (AntSecurityException e) {
// expected
}
catch (Throwable e) {
error = e;
}
finally {
System.setErr(originalErr);
System.setOut(originalOut);
System.setIn(originalIn);
if (System.getSecurityManager() instanceof AntSecurityManager) {
System.setSecurityManager(originalSM);
}
if (!projectHelp) {
fireBuildFinished(getCurrentProject(), error);
}
// close any user specified build log
if (err != originalErr) {
err.close();
}
if (out != originalOut) {
out.close();
}
}
}
private void setTasks() {
if (eclipseSpecifiedTasks != null) {
String taskName;
String taskClassName;
for (Entry<String, String> entry : eclipseSpecifiedTasks.entrySet()) {
taskName = entry.getKey();
taskClassName = entry.getValue();
if (isVersionCompatible("1.6")) { //$NON-NLS-1$
AntTypeDefinition def = new AntTypeDefinition();
def.setName(taskName);
def.setClassName(taskClassName);
def.setClassLoader(this.getClass().getClassLoader());
def.setAdaptToClass(Task.class);
def.setAdapterClass(TaskAdapter.class);
ComponentHelper.getComponentHelper(getCurrentProject()).addDataTypeDefinition(def);
} else {
try {
Class<?> taskClass = Class.forName(taskClassName);
getCurrentProject().addTaskDefinition(taskName, taskClass);
}
catch (ClassNotFoundException e) {
String message = MessageFormat.format(RemoteAntMessages.getString("InternalAntRunner.161"), new Object[] { taskClassName, //$NON-NLS-1$
taskName });
getCurrentProject().log(message, Project.MSG_WARN);
}
}
}
}
}
private void setTypes() {
if (eclipseSpecifiedTypes != null) {
String typeName;
String typeClassName;
for (Entry<String, String> entry : eclipseSpecifiedTypes.entrySet()) {
typeName = entry.getKey();
typeClassName = entry.getValue();
if (isVersionCompatible("1.6")) { //$NON-NLS-1$
AntTypeDefinition def = new AntTypeDefinition();
def.setName(typeName);
def.setClassName(typeClassName);
def.setClassLoader(this.getClass().getClassLoader());
ComponentHelper.getComponentHelper(getCurrentProject()).addDataTypeDefinition(def);
} else {
try {
Class<?> typeClass = Class.forName(typeClassName);
getCurrentProject().addDataTypeDefinition(typeName, typeClass);
}
catch (ClassNotFoundException e) {
String message = MessageFormat.format(RemoteAntMessages.getString("InternalAntRunner.162"), new Object[] { typeClassName, //$NON-NLS-1$
typeName });
getCurrentProject().log(message, Project.MSG_WARN);
}
}
}
}
}
private void remapSystemIn() {
if (!isVersionCompatible("1.6")) { //$NON-NLS-1$
return;
}
DemuxInputStreamSetter setter = new DemuxInputStreamSetter();
setter.remapSystemIn(getCurrentProject());
}
/**
* Creates and returns the default build logger for logging build events to the ant log.
*
* @return the default build logger for logging build events to the ant log can return <code>null</code> if no logging is to occur
*/
private BuildLogger createLogger() {
if (loggerClassname == null) {
buildLogger = new DefaultLogger();
} else if (!IAntCoreConstants.EMPTY_STRING.equals(loggerClassname)) {
try {
buildLogger = (BuildLogger) (Class.forName(loggerClassname).newInstance());
}
catch (ClassCastException e) {
String message = MessageFormat.format(RemoteAntMessages.getString("InternalAntRunner.{0}_which_was_specified_to_perform_logging_is_not_an_instance_of_org.apache.tools.ant.BuildLogger._2"), new Object[] { //$NON-NLS-1$
loggerClassname });
logMessage(null, message, Project.MSG_ERR);
throw new BuildException(message, e);
}
catch (Exception e) {
String message = MessageFormat.format(RemoteAntMessages.getString("InternalAntRunner.Unable_to_instantiate_logger__{0}_6"), new Object[] { //$NON-NLS-1$
loggerClassname });
logMessage(null, message, Project.MSG_ERR);
throw new BuildException(message, e);
}
}
if (buildLogger != null) {
buildLogger.setMessageOutputLevel(messageOutputLevel);
buildLogger.setOutputPrintStream(out);
buildLogger.setErrorPrintStream(err);
buildLogger.setEmacsMode(emacsMode);
if (buildLogger instanceof RemoteAntBuildLogger) {
((RemoteAntBuildLogger) buildLogger).configure(userProperties);
}
}
return buildLogger;
}
/**
* Project.fireBuildStarted is protected in Ant earlier than 1.5.*. Provides backwards compatibility with old Ant installs.
*/
private void fireBuildStarted(Project project) {
if (!isVersionCompatible("1.5")) { //$NON-NLS-1$
BuildEvent event = new BuildEvent(project);
for (Iterator<BuildListener> iterator = project.getBuildListeners().iterator(); iterator.hasNext();) {
BuildListener listener = iterator.next();
listener.buildStarted(event);
}
} else {
project.fireBuildStarted();
}
}
private void fireBuildFinished(Project project, Throwable error) {
if (error == null && scriptExecuted) {
logMessage(project, RemoteAntMessages.getString("InternalAntRunner.BUILD_SUCCESSFUL_1"), messageOutputLevel); //$NON-NLS-1$
}
if (!isVersionCompatible("1.5")) { //$NON-NLS-1$
BuildEvent event = new BuildEvent(project);
event.setException(error);
Iterator<BuildListener> iter = project.getBuildListeners().iterator();
while (iter.hasNext()) {
BuildListener listener = iter.next();
listener.buildFinished(event);
}
} else {
project.fireBuildFinished(error);
}
}
private void logMessage(Project project, String message, int priority) {
if (project != null) {
project.log(message, priority);
}
}
/**
* Sets the buildFileLocation.
*
* @param buildFileLocation
* the file system location of the build file
*/
private void setBuildFileLocation(String buildFileLocation) {
this.buildFileLocation = buildFileLocation;
if (getCurrentProject() != null) {
getCurrentProject().setUserProperty("ant.file", buildFileLocation); //$NON-NLS-1$
}
}
private String getBuildFileLocation() {
if (buildFileLocation == null) {
buildFileLocation = new File("build.xml").getAbsolutePath(); //$NON-NLS-1$
}
return buildFileLocation;
}
/**
* Sets the message output level. Use -1 for none.
*
* @param level
* The message output level
*/
private void setMessageOutputLevel(int level) {
messageOutputLevel = level;
if (buildLogger != null) {
buildLogger.setMessageOutputLevel(level);
}
}
/**
* @return a String representation of the Ant version number as specified in the version.txt file
*/
private String getAntVersionNumber() throws BuildException {
if (antVersionNumber == null) {
try {
InputStream in = Main.class.getResourceAsStream("/org/apache/tools/ant/version.txt");//$NON-NLS-1$
Properties props = new Properties();
props.load(in);
String versionNumber = props.getProperty("VERSION"); //$NON-NLS-1$
antVersionNumber = versionNumber;
}
catch (IOException ioe) {
throw new BuildException(MessageFormat.format(RemoteAntMessages.getString("InternalAntRunner.Could_not_load_the_version_information._{0}_9"), new Object[] { //$NON-NLS-1$
ioe.getMessage() }), ioe);
}
catch (NullPointerException npe) {
throw new BuildException(RemoteAntMessages.getString("InternalAntRunner.Could_not_load_the_version_information._10"), npe); //$NON-NLS-1$
}
}
return antVersionNumber;
}
/**
* Returns whether the given version is compatible with the current Ant version. A version is compatible if it is less than or equal to the
* current version.
*/
private boolean isVersionCompatible(String comparison) {
String version = getAntVersionNumber();
Version osgiVersion = new Version(version);
Version osgiComparison = new Version(comparison);
return osgiVersion.compareTo(osgiComparison) >= 0;
}
/**
* Copy of org.osgi.framework.Version.
* <p>
* We can't know how Ant version numbers will evolve in the future, but the OSGi version number format looks like a good bet.
*/
private static class Version {
private final int major;
private final int minor;
private final int micro;
private final String qualifier;
private static final String SEPARATOR = "."; //$NON-NLS-1$
public int compareTo(Version other) {
if (other == this) { // quick test
return 0;
}
int result = major - other.major;
if (result != 0) {
return result;
}
result = minor - other.minor;
if (result != 0) {
return result;
}
result = micro - other.micro;
if (result != 0) {
return result;
}
return qualifier.compareTo(other.qualifier);
}
public Version(String version) {
int maj = 0;
int min = 0;
int mic = 0;
String qual = ""; //$NON-NLS-1$
try {
StringTokenizer st = new StringTokenizer(version, SEPARATOR, true);
maj = parseInt(st.nextToken(), version);
if (st.hasMoreTokens()) { // minor
st.nextToken(); // consume delimiter
min = parseInt(st.nextToken(), version);
if (st.hasMoreTokens()) { // micro
st.nextToken(); // consume delimiter
mic = parseInt(st.nextToken(), version);
if (st.hasMoreTokens()) { // qualifier separator
st.nextToken(); // consume delimiter
qual = st.nextToken(""); // remaining string //$NON-NLS-1$
if (st.hasMoreTokens()) { // fail safe
throw new IllegalArgumentException("invalid version \"" + version + "\": invalid format"); //$NON-NLS-1$ //$NON-NLS-2$
}
}
}
}
}
catch (NoSuchElementException e) {
IllegalArgumentException iae = new IllegalArgumentException("invalid version \"" + version + "\": invalid format");//$NON-NLS-1$ //$NON-NLS-2$
iae.initCause(e);
throw iae;
}
major = maj;
minor = min;
micro = mic;
qualifier = qual;
// validate();
}
private int parseInt(String value, String version) {
try {
return Integer.parseInt(value);
}
catch (NumberFormatException e) {
IllegalArgumentException iae = new IllegalArgumentException("invalid version \"" + version + "\": non-numeric \"" + value + "\""); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$
iae.initCause(e);
throw iae;
}
}
}
@SuppressWarnings("unused")
private boolean preprocessCommandLine(List<String> commands) {
String arg = getArgument(commands, "-listener"); //$NON-NLS-1$
while (arg != null) {
if (arg.length() == 0) {
throw new BuildException(RemoteAntMessages.getString("InternalAntRunner.You_must_specify_a_classname_when_using_the_-listener_argument_1")); //$NON-NLS-1$
}
if (buildListeners == null) {
buildListeners = new ArrayList<String>(1);
}
buildListeners.add(arg);
arg = getArgument(commands, "-listener"); //$NON-NLS-1$
}
arg = getArgument(commands, "-logger"); //$NON-NLS-1$
if (arg != null) {
// allow empty string to mean no logger
loggerClassname = arg;
}
arg = getArgument(commands, "-logger"); //$NON-NLS-1$
if (arg != null) {
throw new BuildException(RemoteAntMessages.getString("InternalAntRunner.Only_one_logger_class_may_be_specified_1")); //$NON-NLS-1$
}
arg = getArgument(commands, "-inputhandler"); //$NON-NLS-1$
if (arg != null) {
if (arg.length() == 0) {
throw new BuildException(RemoteAntMessages.getString("InternalAntRunner.You_must_specify_a_classname_when_using_the_-inputhandler_argument_1")); //$NON-NLS-1$
}
inputHandlerClassname = arg;
}
arg = getArgument(commands, "-inputhandler"); //$NON-NLS-1$
if (arg != null) {
throw new BuildException(RemoteAntMessages.getString("InternalAntRunner.Only_one_input_handler_class_may_be_specified._2")); //$NON-NLS-1$
}
return true;
}
/*
* Looks for interesting command line arguments. Returns whether it is OK to run the script.
*/
private boolean processCommandLine(List<String> commands) {
if (commands.remove("-help") || commands.remove("-h")) { //$NON-NLS-1$ //$NON-NLS-2$
printUsage();
return false;
}
if (commands.remove("-version")) { //$NON-NLS-1$
printVersion();
return false;
}
if (commands.remove("-verbose") || commands.remove("-v")) { //$NON-NLS-1$ //$NON-NLS-2$
printVersion();
setMessageOutputLevel(Project.MSG_VERBOSE);
}
if (commands.remove("-debug") || commands.remove("-d")) { //$NON-NLS-1$ //$NON-NLS-2$
printVersion();
setMessageOutputLevel(Project.MSG_DEBUG);
}
if (commands.remove("-quiet") || commands.remove("-q")) { //$NON-NLS-1$ //$NON-NLS-2$
setMessageOutputLevel(Project.MSG_WARN);
}
if (commands.remove("-emacs") || commands.remove("-e")) { //$NON-NLS-1$ //$NON-NLS-2$
emacsMode = true;
if (buildLogger != null) {
buildLogger.setEmacsMode(true);
}
}
if (commands.remove("-diagnostics")) { //$NON-NLS-1$
if (!isVersionCompatible("1.5")) { //$NON-NLS-1$
throw new BuildException(RemoteAntMessages.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$
}
try {
Diagnostics.doReport(System.out);
}
catch (NullPointerException e) {
logMessage(getCurrentProject(), RemoteAntMessages.getString("InternalAntRunner.ANT_HOME_must_be_set_to_use_Ant_diagnostics_2"), Project.MSG_ERR); //$NON-NLS-1$
}
return false;
}
String arg = getArgument(commands, "-logfile"); //$NON-NLS-1$
if (arg == null) {
arg = getArgument(commands, "-l"); //$NON-NLS-1$
}
if (arg != null) {
if (arg.length() == 0) {
String message = RemoteAntMessages.getString("InternalAntRunner.You_must_specify_a_log_file_when_using_the_-log_argument_3"); //$NON-NLS-1$
logMessage(currentProject, message, Project.MSG_ERR);
throw new BuildException(message);
}
try {
createLogFile(arg);
}
catch (IOException e) {
// just log message and ignore exception
logMessage(getCurrentProject(), MessageFormat.format(RemoteAntMessages.getString("InternalAntRunner.Could_not_write_to_the_specified_log_file__{0}._Make_sure_the_path_exists_and_you_have_write_permissions._2"), new Object[] { //$NON-NLS-1$
arg }), Project.MSG_ERR);
return false;
}
}
arg = getArgument(commands, "-buildfile"); //$NON-NLS-1$
if (arg == null) {
arg = getArgument(commands, "-file"); //$NON-NLS-1$
if (arg == null) {
arg = getArgument(commands, "-f"); //$NON-NLS-1$
}
}
if (arg != null) {
if (arg.length() == 0) {
String message = RemoteAntMessages.getString("InternalAntRunner.You_must_specify_a_buildfile_when_using_the_-buildfile_argument_4"); //$NON-NLS-1$
logMessage(currentProject, message, Project.MSG_ERR);
throw new BuildException(message);
}
setBuildFileLocation(arg);
}
if (isVersionCompatible("1.6")) { //$NON-NLS-1$
if (commands.remove("-k") || commands.remove("-keep-going")) { //$NON-NLS-1$ //$NON-NLS-2$
keepGoing = true;
}
if (commands.remove("-noinput")) { //$NON-NLS-1$
allowInput = false;
}
arg = getArgument(commands, "-lib"); //$NON-NLS-1$
if (arg != null) {
logMessage(currentProject, RemoteAntMessages.getString("InternalAntRunner.157"), Project.MSG_ERR); //$NON-NLS-1$
return false;
}
}
arg = getArgument(commands, "-find"); //$NON-NLS-1$
if (arg == null) {
arg = getArgument(commands, "-s"); //$NON-NLS-1$
}
if (arg != null) {
logMessage(currentProject, RemoteAntMessages.getString("InternalAntRunner.-find_not_supported"), Project.MSG_ERR); //$NON-NLS-1$
return false;
}
processTasksAndTypes(commands);
if (!commands.isEmpty()) {
processUnrecognizedCommands(commands);
}
if (!commands.isEmpty()) {
processTargets(commands);
}
return true;
}
@SuppressWarnings("unused")
private void processTasksAndTypes(List<String> commands) {
String arg = getArgument(commands, "-eclipseTask"); //$NON-NLS-1$
while (arg != null) {
if (eclipseSpecifiedTasks == null) {
eclipseSpecifiedTasks = new HashMap<String, String>();
}
int index = arg.indexOf(',');
if (index != -1) {
String name = arg.substring(0, index);
String className = arg.substring(index + 1);
eclipseSpecifiedTasks.put(name, className);
}
arg = getArgument(commands, "-eclipseTask"); //$NON-NLS-1$
}
arg = getArgument(commands, "-eclipseType"); //$NON-NLS-1$
while (arg != null) {
if (eclipseSpecifiedTypes == null) {
eclipseSpecifiedTypes = new HashMap<String, String>();
}
int index = arg.indexOf(',');
if (index != -1) {
String name = arg.substring(0, index);
String className = arg.substring(index + 1);
eclipseSpecifiedTypes.put(name, className);
}
arg = getArgument(commands, "-eclipseType"); //$NON-NLS-1$
}
}
/*
* Checks for unrecognized arguments on the command line. Since there is no syntactic way to distingush between ant -foo target1 target2 ant -foo
* fooarg target we remove everything up to the last argument that begins with a '-'. In the latter case, above, that means that there will be an
* extra target, 'fooarg', left lying around.
*/
private void processUnrecognizedCommands(List<String> commands) {
int p = -1;
// find the last arg that begins with '-'
for (int i = commands.size() - 1; i >= 0; i--) {
if (commands.get(0).startsWith("-")) { //$NON-NLS-1$
p = i;
break;
}
}
if (p < 0) {
return;
}
// remove everything preceding that last '-arg'
String s = IAntCoreConstants.EMPTY_STRING;
for (int i = 0; i <= p; i++) {
s += " " + (commands.get(0)); //$NON-NLS-1$
commands.remove(0);
}
// warn of ignored commands
String message = MessageFormat.format(RemoteAntMessages.getString("InternalAntRunner.Unknown_argument__{0}_2"), new Object[] { //$NON-NLS-1$
s.substring(1) });
logMessage(currentProject, message, Project.MSG_WARN);
}
/*
* Checks for targets specified at the command line.
*/
@SuppressWarnings("unused")
private void processTargets(List<String> commands) {
if (targets == null) {
targets = new Vector<String>(commands.size());
}
for (String string : commands) {
targets.add(string);
}
}
/*
* Creates the log file with the name specified by the user. If the fileName is not absolute, the file will be created in the working directory if
* specified or in the same directory as the location of the build file.
*/
private void createLogFile(String fileName) throws FileNotFoundException, IOException {
File logFile = getFileRelativeToBaseDir(fileName);
// this stream is closed in the finally block of run(list)
out = new PrintStream(new FileOutputStream(logFile));
err = out;
logMessage(getCurrentProject(), MessageFormat.format(RemoteAntMessages.getString("InternalAntRunner.Using_{0}_file_as_build_log._1"), new Object[] { //$NON-NLS-1$
logFile.getCanonicalPath() }), Project.MSG_INFO);
if (buildLogger != null) {
buildLogger.setErrorPrintStream(err);
buildLogger.setOutputPrintStream(out);
}
}
private File getFileRelativeToBaseDir(String fileName) {
File parentFile = null;
String base = getCurrentProject().getUserProperty("basedir"); //$NON-NLS-1$
if (base != null) {
parentFile = new File(base);
} else {
// relative to the build file location
parentFile = new File(getBuildFileLocation()).getParentFile();
}
// remain backwards compatible for older Ant usage
return FileUtils.getFileUtils().resolveFile(parentFile, fileName);
}
/*
* Processes cmd line properties and adds the user properties Any user properties that have been explicitly set are set as well. Ensures that -D
* properties take precedence.
*/
private boolean processProperties(List<String> commands) {
boolean exceptionToBeThrown = false;
// MULTIPLE property files are allowed
String arg = getArgument(commands, "-propertyfile"); //$NON-NLS-1$
while (arg != null) {
if (!isVersionCompatible("1.5")) { //$NON-NLS-1$
fEarlyErrorMessage = RemoteAntMessages.getString("InternalAntRunner.Specifying_property_files_is_a_Ant_1.5.*_feature._Please_update_your_Ant_classpath._6"); //$NON-NLS-1$
break;
}
if (arg.length() == 0) {
fEarlyErrorMessage = RemoteAntMessages.getString("InternalAntRunner.You_must_specify_a_property_filename_when_using_the_-propertyfile_argument_3"); //$NON-NLS-1$
exceptionToBeThrown = true;
break;
}
propertyFiles.add(arg);
arg = getArgument(commands, "-propertyfile"); //$NON-NLS-1$
}
if (propertyFiles != null && !propertyFiles.isEmpty()) {
loadPropertyFiles();
}
if (commands != null) {
processMinusDProperties(commands);
}
return exceptionToBeThrown;
}
@SuppressWarnings("unused")
private void processMinusDProperties(List<String> commands) {
String[] args = commands.toArray(new String[commands.size()]);
for (int i = 0; i < args.length; i++) {
String arg = args[i];
if (arg.startsWith("-D")) { //$NON-NLS-1$
String name = arg.substring(2, arg.length());
String value = null;
int posEq = name.indexOf("="); //$NON-NLS-1$
if (posEq == 0) {
value = name.substring(1);
name = IAntCoreConstants.EMPTY_STRING;
} else if (posEq > 0 && posEq != name.length() - 1) {
value = name.substring(posEq + 1).trim();
name = name.substring(0, posEq);
}
if (value == null) {
// the user has specified something like "-Debug"
continue;
}
if (userProperties == null) {
userProperties = new HashMap<String, String>();
}
userProperties.put(name, value);
commands.remove(args[i]);
}
}
}
private void setProperties(Project project) {
setBuiltInProperties(project);
if (userProperties != null) {
for (Entry<String, String> entry : userProperties.entrySet()) {
if (entry.getValue() != null)
project.setUserProperty(entry.getKey(), entry.getValue());
}
}
}
private void setBuiltInProperties(Project project) {
project.setUserProperty("ant.file", getBuildFileLocation()); //$NON-NLS-1$
project.setUserProperty("ant.version", Main.getAntVersion()); //$NON-NLS-1$
}
/*
* Print the project description, if any
*/
private void printHelp(Project project) {
if (project.getDescription() != null) {
logMessage(project, project.getDescription(), Project.MSG_INFO);
}
printTargets(project);
}
/*
* Logs a message with the client indicating the version of <b>Ant</b> that this class fronts.
*/
private void printVersion() {
logMessage(getCurrentProject(), Main.getAntVersion(), Project.MSG_INFO);
}
/*
* Logs a message with the client outlining the usage of <b>Ant</b>.
*/
private void printUsage() {
String lSep = System.getProperty("line.separator"); //$NON-NLS-1$
StringBuffer msg = new StringBuffer();
msg.append("ant ["); //$NON-NLS-1$
msg.append(RemoteAntMessages.getString("InternalAntRunner.options_13")); //$NON-NLS-1$
msg.append("] ["); //$NON-NLS-1$
msg.append(RemoteAntMessages.getString("InternalAntRunner.target_15")); //$NON-NLS-1$
msg.append(" ["); //$NON-NLS-1$
msg.append(RemoteAntMessages.getString("InternalAntRunner.target_15")); //$NON-NLS-1$
msg.append("2 ["); //$NON-NLS-1$
msg.append(RemoteAntMessages.getString("InternalAntRunner.target_15")); //$NON-NLS-1$
msg.append("3] ...]]"); //$NON-NLS-1$
msg.append(lSep);
msg.append(RemoteAntMessages.getString("InternalAntRunner.Options___21")); //$NON-NLS-1$
msg.append(lSep);
msg.append("\t-help, -h\t\t\t\t"); //$NON-NLS-1$
msg.append(RemoteAntMessages.getString("InternalAntRunner.print_this_message_23")); //$NON-NLS-1$
msg.append(lSep);
msg.append("\t-projecthelp, -p\t\t"); //$NON-NLS-1$
msg.append(RemoteAntMessages.getString("InternalAntRunner.print_project_help_information_25")); //$NON-NLS-1$
msg.append(lSep);
msg.append("\t-version\t\t\t\t"); //$NON-NLS-1$
msg.append(RemoteAntMessages.getString("InternalAntRunner.print_the_version_information_and_exit_27")); //$NON-NLS-1$
msg.append(lSep);
msg.append("\t-diagnostics\t\t\t"); //$NON-NLS-1$
msg.append(RemoteAntMessages.getString("InternalAntRunner.12")); //$NON-NLS-1$
msg.append(lSep);
msg.append(RemoteAntMessages.getString("InternalAntRunner.13")); //$NON-NLS-1$
msg.append(lSep);
msg.append("\t-quiet, -q\t\t\t"); //$NON-NLS-1$
msg.append(RemoteAntMessages.getString("InternalAntRunner.be_extra_quiet_29")); //$NON-NLS-1$
msg.append(lSep);
msg.append("\t-verbose, -v\t\t\t"); //$NON-NLS-1$
msg.append(RemoteAntMessages.getString("InternalAntRunner.be_extra_verbose_31")); //$NON-NLS-1$
msg.append(lSep);
msg.append("\t-debug, -d\t\t\t"); //$NON-NLS-1$
msg.append(RemoteAntMessages.getString("InternalAntRunner.print_debugging_information_33")); //$NON-NLS-1$
msg.append(lSep);
msg.append("\t-emacs, -e\t\t\t"); //$NON-NLS-1$
msg.append(RemoteAntMessages.getString("InternalAntRunner.produce_logging_information_without_adornments_35")); //$NON-NLS-1$
msg.append(lSep);
msg.append("\t-logfile\t<file>\t\t"); //$NON-NLS-1$
msg.append(RemoteAntMessages.getString("InternalAntRunner.use_given_file_for_log_37")); //$NON-NLS-1$
msg.append(lSep);
msg.append("\t\t-l\t<file>"); //$NON-NLS-1$
msg.append(RemoteAntMessages.getString("InternalAntRunner.1")); //$NON-NLS-1$
msg.append(lSep);
msg.append("\t-logger <classname>\t\t"); //$NON-NLS-1$
msg.append(RemoteAntMessages.getString("InternalAntRunner.the_class_which_is_to_perform_logging_39")); //$NON-NLS-1$
msg.append(lSep);
msg.append("\t-listener <classname>\t"); //$NON-NLS-1$
msg.append(RemoteAntMessages.getString("InternalAntRunner.add_an_instance_of_class_as_a_project_listener_41")); //$NON-NLS-1$
msg.append(lSep);
msg.append("\t-noinput\t"); //$NON-NLS-1$
msg.append(RemoteAntMessages.getString("InternalAntRunner.158")); //$NON-NLS-1$
msg.append(lSep);
msg.append("\t-buildfile\t<file>\t"); //$NON-NLS-1$
msg.append(RemoteAntMessages.getString("InternalAntRunner.use_given_buildfile_43")); //$NON-NLS-1$
msg.append(lSep);
msg.append("\t\t-file\t<file>"); //$NON-NLS-1$
msg.append(RemoteAntMessages.getString("InternalAntRunner.1")); //$NON-NLS-1$
msg.append(lSep);
msg.append("\t\t-f\t\t<file>"); //$NON-NLS-1$
msg.append(RemoteAntMessages.getString("InternalAntRunner.1")); //$NON-NLS-1$
msg.append(lSep);
msg.append("\t-D<property>=<value>\t"); //$NON-NLS-1$
msg.append(RemoteAntMessages.getString("InternalAntRunner.use_value_for_given_property_45")); //$NON-NLS-1$
msg.append(lSep);
msg.append("\t-keep-going, -k"); //$NON-NLS-1$
msg.append(RemoteAntMessages.getString("InternalAntRunner.159")); //$NON-NLS-1$
msg.append(lSep);
msg.append(RemoteAntMessages.getString("InternalAntRunner.160")); //$NON-NLS-1$
msg.append(lSep);
msg.append("\t-propertyfile <name>\t"); //$NON-NLS-1$
msg.append(RemoteAntMessages.getString("InternalAntRunner.19")); //$NON-NLS-1$
msg.append(lSep);
msg.append(RemoteAntMessages.getString("InternalAntRunner.20")); //$NON-NLS-1$
msg.append(lSep);
msg.append("\t-inputhandler <class>\t"); //$NON-NLS-1$
msg.append(RemoteAntMessages.getString("InternalAntRunner.22")); //$NON-NLS-1$
msg.append(lSep);
logMessage(getCurrentProject(), msg.toString(), Project.MSG_INFO);
}
/*
* From a command line list, return the argument for the given parameter. The parameter and its argument are removed from the list.
*
* @return <code>null</code> if the parameter is not found or an empty String if no arguments are found
*/
private String getArgument(List<String> commands, String param) {
if (commands == null) {
return null;
}
int index = commands.indexOf(param);
if (index == -1) {
return null;
}
commands.remove(index);
if (index == commands.size()) {// if this is the last command
return IAntCoreConstants.EMPTY_STRING;
}
String command = commands.get(index);
if (command.startsWith("-")) { // new parameter //$NON-NLS-1$
return IAntCoreConstants.EMPTY_STRING;
}
commands.remove(index);
return command;
}
private Project getCurrentProject() {
return currentProject;
}
private void setCurrentProject(Project currentProject) {
this.currentProject = currentProject;
}
/**
* Load all properties from the files specified by -propertyfile.
*/
@SuppressWarnings("unused")
private void loadPropertyFiles() {
for (String filename : propertyFiles) {
File file = getFileRelativeToBaseDir(filename);
Properties props = new Properties();
try {
FileInputStream fis = new FileInputStream(file);
props.load(fis);
}
catch (IOException e) {
fEarlyErrorMessage = MessageFormat.format(RemoteAntMessages.getString("InternalAntRunner.Could_not_load_property_file_{0}__{1}_4"), new Object[] { //$NON-NLS-1$
filename, e.getMessage() });
}
if (userProperties == null) {
userProperties = new HashMap<String, String>();
}
Enumeration<?> propertyNames = props.propertyNames();
while (propertyNames.hasMoreElements()) {
String name = (String) propertyNames.nextElement();
// most specific to global
// do not overwrite specific with a global property
if (userProperties.get(name) == null) {
userProperties.put(name, props.getProperty(name));
}
}
}
}
/*
* Creates the InputHandler and adds it to the project.
*
* @exception BuildException if a specified InputHandler implementation could not be loaded.
*/
private void addInputHandler(Project project) {
if (!isVersionCompatible("1.5")) { //$NON-NLS-1$
return;
}
InputHandlerSetter setter = new InputHandlerSetter();
setter.setInputHandler(project, inputHandlerClassname);
}
}