diff options
Diffstat (limited to 'framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/Main.java')
-rw-r--r-- | framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/Main.java | 1317 |
1 files changed, 1317 insertions, 0 deletions
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/Main.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/Main.java new file mode 100644 index 00000000..a9c23a53 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/Main.java @@ -0,0 +1,1317 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.tools.ant; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Properties; +import java.util.Set; +import java.util.Vector; + +import org.apache.tools.ant.input.DefaultInputHandler; +import org.apache.tools.ant.input.InputHandler; +import org.apache.tools.ant.launch.AntMain; +import org.apache.tools.ant.listener.SilentLogger; +import org.apache.tools.ant.property.GetProperty; +import org.apache.tools.ant.property.ResolvePropertyMap; +import org.apache.tools.ant.util.ClasspathUtils; +import org.apache.tools.ant.util.FileUtils; +import org.apache.tools.ant.util.ProxySetup; + + +/** + * Command line entry point into Ant. This class is entered via the + * canonical `public static void main` entry point and reads the + * command line arguments. It then assembles and executes an Ant + * project. + * <p> + * If you integrating Ant into some other tool, this is not the class + * to use as an entry point. Please see the source code of this + * class to see how it manipulates the Ant project classes. + * + */ +public class Main implements AntMain { + + /** + * A Set of args that are handled by the launcher and should + * not be seen by Main. + */ + private static final Set<String> LAUNCH_COMMANDS = Collections + .unmodifiableSet(new HashSet<String>(Arrays.asList("-lib", "-cp", "-noclasspath", + "--noclasspath", "-nouserlib", "-main"))); + + /** The default build file name. {@value} */ + public static final String DEFAULT_BUILD_FILENAME = "build.xml"; + + /** Our current message output status. Follows Project.MSG_XXX. */ + private int msgOutputLevel = Project.MSG_INFO; + + /** File that we are using for configuration. */ + private File buildFile; /* null */ + + /** Stream to use for logging. */ + private static PrintStream out = System.out; + + /** Stream that we are using for logging error messages. */ + private static PrintStream err = System.err; + + /** The build targets. */ + private final Vector<String> targets = new Vector<String>(); + + /** Set of properties that can be used by tasks. */ + private final Properties definedProps = new Properties(); + + /** Names of classes to add as listeners to project. */ + private final Vector<String> listeners = new Vector<String>(1); + + /** File names of property files to load on startup. */ + private final Vector<String> propertyFiles = new Vector<String>(1); + + /** Indicates whether this build is to support interactive input */ + private boolean allowInput = true; + + /** keep going mode */ + private boolean keepGoingMode = false; + + /** + * The Ant logger class. There may be only one logger. It will have + * the right to use the 'out' PrintStream. The class must implements the + * BuildLogger interface. + */ + private String loggerClassname = null; + + /** + * The Ant InputHandler class. There may be only one input + * handler. + */ + private String inputHandlerClassname = null; + + /** + * Whether or not output to the log is to be unadorned. + */ + private boolean emacsMode = false; + + /** + * Whether or not log output should be reduced to the minimum + */ + private boolean silent = false; + + /** + * Whether or not this instance has successfully been + * constructed and is ready to run. + */ + private boolean readyToRun = false; + + /** + * Whether or not we should only parse and display the project help + * information. + */ + private boolean projectHelp = false; + + /** + * Whether or not a logfile is being used. This is used to + * check if the output streams must be closed. + */ + private static boolean isLogFileUsed = false; + + /** + * optional thread priority + */ + private Integer threadPriority = null; + + /** + * proxy flag: default is false + */ + private boolean proxy = false; + + private final Map<Class<?>, List<String>> extraArguments = new HashMap<Class<?>, List<String>>(); + + private static final GetProperty NOPROPERTIES = new GetProperty() { + public Object getProperty(final String aName) { + // No existing property takes precedence + return null; + } + }; + + + + + /** + * Prints the message of the Throwable if it (the message) is not + * <code>null</code>. + * + * @param t Throwable to print the message of. + * Must not be <code>null</code>. + */ + private static void printMessage(final Throwable t) { + final String message = t.getMessage(); + if (message != null) { + System.err.println(message); + } + } + + /** + * Creates a new instance of this class using the + * arguments specified, gives it any extra user properties which have been + * specified, and then runs the build using the classloader provided. + * + * @param args Command line arguments. Must not be <code>null</code>. + * @param additionalUserProperties Any extra properties to use in this + * build. May be <code>null</code>, which is the equivalent to + * passing in an empty set of properties. + * @param coreLoader Classloader used for core classes. May be + * <code>null</code> in which case the system classloader is used. + */ + public static void start(final String[] args, final Properties additionalUserProperties, + final ClassLoader coreLoader) { + final Main m = new Main(); + m.startAnt(args, additionalUserProperties, coreLoader); + } + + /** + * Start Ant + * @param args command line args + * @param additionalUserProperties properties to set beyond those that + * may be specified on the args list + * @param coreLoader - not used + * + * @since Ant 1.6 + */ + public void startAnt(final String[] args, final Properties additionalUserProperties, + final ClassLoader coreLoader) { + + try { + processArgs(args); + } catch (final Throwable exc) { + handleLogfile(); + printMessage(exc); + exit(1); + return; + } + + if (additionalUserProperties != null) { + for (final Enumeration<?> e = additionalUserProperties.keys(); + e.hasMoreElements();) { + final String key = (String) e.nextElement(); + final String property = additionalUserProperties.getProperty(key); + definedProps.put(key, property); + } + } + + // expect the worst + int exitCode = 1; + try { + try { + runBuild(coreLoader); + exitCode = 0; + } catch (final ExitStatusException ese) { + exitCode = ese.getStatus(); + if (exitCode != 0) { + throw ese; + } + } + } catch (final BuildException be) { + if (err != System.err) { + printMessage(be); + } + } catch (final Throwable exc) { + exc.printStackTrace(); + printMessage(exc); + } finally { + handleLogfile(); + } + exit(exitCode); + } + + /** + * This operation is expected to call {@link System#exit(int)}, which + * is what the base version does. + * However, it is possible to do something else. + * @param exitCode code to exit with + */ + protected void exit(final int exitCode) { + System.exit(exitCode); + } + + /** + * Close logfiles, if we have been writing to them. + * + * @since Ant 1.6 + */ + private static void handleLogfile() { + if (isLogFileUsed) { + FileUtils.close(out); + FileUtils.close(err); + } + } + + /** + * Command line entry point. This method kicks off the building + * of a project object and executes a build using either a given + * target or the default target. + * + * @param args Command line arguments. Must not be <code>null</code>. + */ + public static void main(final String[] args) { + start(args, null, null); + } + + /** + * Constructor used when creating Main for later arg processing + * and startup + */ + public Main() { + } + + /** + * Sole constructor, which parses and deals with command line + * arguments. + * + * @param args Command line arguments. Must not be <code>null</code>. + * + * @exception BuildException if the specified build file doesn't exist + * or is a directory. + * + * @deprecated since 1.6.x + */ + @Deprecated + protected Main(final String[] args) throws BuildException { + processArgs(args); + } + + /** + * Process command line arguments. + * When ant is started from Launcher, launcher-only arguments do not get + * passed through to this routine. + * + * @param args the command line arguments. + * + * @since Ant 1.6 + */ + private void processArgs(final String[] args) { + String searchForThis = null; + boolean searchForFile = false; + PrintStream logTo = null; + + // cycle through given args + + boolean justPrintUsage = false; + boolean justPrintVersion = false; + boolean justPrintDiagnostics = false; + + final ArgumentProcessorRegistry processorRegistry = ArgumentProcessorRegistry.getInstance(); + + for (int i = 0; i < args.length; i++) { + final String arg = args[i]; + + if (arg.equals("-help") || arg.equals("-h")) { + justPrintUsage = true; + } else if (arg.equals("-version")) { + justPrintVersion = true; + } else if (arg.equals("-diagnostics")) { + justPrintDiagnostics = true; + } else if (arg.equals("-quiet") || arg.equals("-q")) { + msgOutputLevel = Project.MSG_WARN; + } else if (arg.equals("-verbose") || arg.equals("-v")) { + msgOutputLevel = Project.MSG_VERBOSE; + } else if (arg.equals("-debug") || arg.equals("-d")) { + msgOutputLevel = Project.MSG_DEBUG; + } else if (arg.equals("-silent") || arg.equals("-S")) { + silent = true; + } else if (arg.equals("-noinput")) { + allowInput = false; + } else if (arg.equals("-logfile") || arg.equals("-l")) { + try { + final File logFile = new File(args[i + 1]); + i++; + logTo = new PrintStream(new FileOutputStream(logFile)); + isLogFileUsed = true; + } catch (final IOException ioe) { + final String msg = "Cannot write on the specified log file. " + + "Make sure the path exists and you have write " + + "permissions."; + throw new BuildException(msg); + } catch (final ArrayIndexOutOfBoundsException aioobe) { + final String msg = "You must specify a log file when " + + "using the -log argument"; + throw new BuildException(msg); + } + } else if (arg.equals("-buildfile") || arg.equals("-file") + || arg.equals("-f")) { + i = handleArgBuildFile(args, i); + } else if (arg.equals("-listener")) { + i = handleArgListener(args, i); + } else if (arg.startsWith("-D")) { + i = handleArgDefine(args, i); + } else if (arg.equals("-logger")) { + i = handleArgLogger(args, i); + } else if (arg.equals("-inputhandler")) { + i = handleArgInputHandler(args, i); + } else if (arg.equals("-emacs") || arg.equals("-e")) { + emacsMode = true; + } else if (arg.equals("-projecthelp") || arg.equals("-p")) { + // set the flag to display the targets and quit + projectHelp = true; + } else if (arg.equals("-find") || arg.equals("-s")) { + searchForFile = true; + // eat up next arg if present, default to build.xml + if (i < args.length - 1) { + searchForThis = args[++i]; + } + } else if (arg.startsWith("-propertyfile")) { + i = handleArgPropertyFile(args, i); + } else if (arg.equals("-k") || arg.equals("-keep-going")) { + keepGoingMode = true; + } else if (arg.equals("-nice")) { + i = handleArgNice(args, i); + } else if (LAUNCH_COMMANDS.contains(arg)) { + //catch script/ant mismatch with a meaningful message + //we could ignore it, but there are likely to be other + //version problems, so we stamp down on the configuration now + final String msg = "Ant's Main method is being handed " + + "an option " + arg + " that is only for the launcher class." + + "\nThis can be caused by a version mismatch between " + + "the ant script/.bat file and Ant itself."; + throw new BuildException(msg); + } else if (arg.equals("-autoproxy")) { + proxy = true; + } else if (arg.startsWith("-")) { + boolean processed = false; + for (final ArgumentProcessor processor : processorRegistry.getProcessors()) { + final int newI = processor.readArguments(args, i); + if (newI != -1) { + List<String> extraArgs = extraArguments.get(processor.getClass()); + if (extraArgs == null) { + extraArgs = new ArrayList<String>(); + extraArguments.put(processor.getClass(), extraArgs); + } + for (; i < newI && i < args.length; i++) { + extraArgs.add(args[i]); + } + processed = true; + break; + } + } + if (!processed) { + // we don't have any more args to recognize! + final String msg = "Unknown argument: " + arg; + System.err.println(msg); + printUsage(); + throw new BuildException(""); + } + } else { + // if it's no other arg, it may be the target + targets.addElement(arg); + } + } + + if (msgOutputLevel >= Project.MSG_VERBOSE || justPrintVersion) { + printVersion(msgOutputLevel); + } + + if (justPrintUsage || justPrintVersion || justPrintDiagnostics) { + if (justPrintUsage) { + printUsage(); + } + if (justPrintDiagnostics) { + Diagnostics.doReport(System.out, msgOutputLevel); + } + return; + } + + // if buildFile was not specified on the command line, + if (buildFile == null) { + // but -find then search for it + if (searchForFile) { + if (searchForThis != null) { + buildFile = findBuildFile(System.getProperty("user.dir"), searchForThis); + if (buildFile == null) { + throw new BuildException("Could not locate a build file!"); + } + } else { + // no search file specified: so search an existing default file + final Iterator<ProjectHelper> it = ProjectHelperRepository.getInstance().getHelpers(); + do { + final ProjectHelper helper = it.next(); + searchForThis = helper.getDefaultBuildFile(); + if (msgOutputLevel >= Project.MSG_VERBOSE) { + System.out.println("Searching the default build file: " + searchForThis); + } + buildFile = findBuildFile(System.getProperty("user.dir"), searchForThis); + } while (buildFile == null && it.hasNext()); + if (buildFile == null) { + throw new BuildException("Could not locate a build file!"); + } + } + } else { + // no build file specified: so search an existing default file + final Iterator<ProjectHelper> it = ProjectHelperRepository.getInstance().getHelpers(); + do { + final ProjectHelper helper = it.next(); + buildFile = new File(helper.getDefaultBuildFile()); + if (msgOutputLevel >= Project.MSG_VERBOSE) { + System.out.println("Trying the default build file: " + buildFile); + } + } while (!buildFile.exists() && it.hasNext()); + } + } + + // make sure buildfile exists + if (!buildFile.exists()) { + System.out.println("Buildfile: " + buildFile + " does not exist!"); + throw new BuildException("Build failed"); + } + + if (buildFile.isDirectory()) { + final File whatYouMeant = new File(buildFile, "build.xml"); + if (whatYouMeant.isFile()) { + buildFile = whatYouMeant; + } else { + System.out.println("What? Buildfile: " + buildFile + " is a dir!"); + throw new BuildException("Build failed"); + } + } + + // Normalize buildFile for re-import detection + buildFile = + FileUtils.getFileUtils().normalize(buildFile.getAbsolutePath()); + + // Load the property files specified by -propertyfile + loadPropertyFiles(); + + if (msgOutputLevel >= Project.MSG_INFO) { + System.out.println("Buildfile: " + buildFile); + } + + if (logTo != null) { + out = logTo; + err = logTo; + System.setOut(out); + System.setErr(err); + } + readyToRun = true; + } + + // -------------------------------------------------------- + // Methods for handling the command line arguments + // -------------------------------------------------------- + + /** Handle the -buildfile, -file, -f argument */ + private int handleArgBuildFile(final String[] args, int pos) { + try { + buildFile = new File( + args[++pos].replace('/', File.separatorChar)); + } catch (final ArrayIndexOutOfBoundsException aioobe) { + throw new BuildException( + "You must specify a buildfile when using the -buildfile argument"); + } + return pos; + } + + /** Handle -listener argument */ + private int handleArgListener(final String[] args, int pos) { + try { + listeners.addElement(args[pos + 1]); + pos++; + } catch (final ArrayIndexOutOfBoundsException aioobe) { + final String msg = "You must specify a classname when " + + "using the -listener argument"; + throw new BuildException(msg); + } + return pos; + } + + /** Handler -D argument */ + private int handleArgDefine(final String[] args, int argPos) { + /* Interestingly enough, we get to here when a user + * uses -Dname=value. However, in some cases, the OS + * goes ahead and parses this out to args + * {"-Dname", "value"} + * so instead of parsing on "=", we just make the "-D" + * characters go away and skip one argument forward. + * + * I don't know how to predict when the JDK is going + * to help or not, so we simply look for the equals sign. + */ + final String arg = args[argPos]; + String name = arg.substring(2, arg.length()); + String value = null; + final int posEq = name.indexOf("="); + if (posEq > 0) { + value = name.substring(posEq + 1); + name = name.substring(0, posEq); + } else if (argPos < args.length - 1) { + value = args[++argPos]; + } else { + throw new BuildException("Missing value for property " + + name); + } + definedProps.put(name, value); + return argPos; + } + + /** Handle the -logger argument. */ + private int handleArgLogger(final String[] args, int pos) { + if (loggerClassname != null) { + throw new BuildException( + "Only one logger class may be specified."); + } + try { + loggerClassname = args[++pos]; + } catch (final ArrayIndexOutOfBoundsException aioobe) { + throw new BuildException( + "You must specify a classname when using the -logger argument"); + } + return pos; + } + + /** Handle the -inputhandler argument. */ + private int handleArgInputHandler(final String[] args, int pos) { + if (inputHandlerClassname != null) { + throw new BuildException("Only one input handler class may " + + "be specified."); + } + try { + inputHandlerClassname = args[++pos]; + } catch (final ArrayIndexOutOfBoundsException aioobe) { + throw new BuildException("You must specify a classname when" + + " using the -inputhandler" + + " argument"); + } + return pos; + } + + /** Handle the -propertyfile argument. */ + private int handleArgPropertyFile(final String[] args, int pos) { + try { + propertyFiles.addElement(args[++pos]); + } catch (final ArrayIndexOutOfBoundsException aioobe) { + final String msg = "You must specify a property filename when " + + "using the -propertyfile argument"; + throw new BuildException(msg); + } + return pos; + } + + /** Handle the -nice argument. */ + private int handleArgNice(final String[] args, int pos) { + try { + threadPriority = Integer.decode(args[++pos]); + } catch (final ArrayIndexOutOfBoundsException aioobe) { + throw new BuildException( + "You must supply a niceness value (1-10)" + + " after the -nice option"); + } catch (final NumberFormatException e) { + throw new BuildException("Unrecognized niceness value: " + + args[pos]); + } + + if (threadPriority.intValue() < Thread.MIN_PRIORITY + || threadPriority.intValue() > Thread.MAX_PRIORITY) { + throw new BuildException( + "Niceness value is out of the range 1-10"); + } + return pos; + } + + // -------------------------------------------------------- + // other methods + // -------------------------------------------------------- + + /** Load the property files specified by -propertyfile */ + private void loadPropertyFiles() { + for (final String filename : propertyFiles) { + final Properties props = new Properties(); + FileInputStream fis = null; + try { + fis = new FileInputStream(filename); + props.load(fis); + } catch (final IOException e) { + System.out.println("Could not load property file " + + filename + ": " + e.getMessage()); + } finally { + FileUtils.close(fis); + } + + // ensure that -D properties take precedence + final Enumeration<?> propertyNames = props.propertyNames(); + while (propertyNames.hasMoreElements()) { + final String name = (String) propertyNames.nextElement(); + if (definedProps.getProperty(name) == null) { + definedProps.put(name, props.getProperty(name)); + } + } + } + } + + /** + * Helper to get the parent file for a given file. + * <p> + * Added to simulate File.getParentFile() from JDK 1.2. + * @deprecated since 1.6.x + * + * @param file File to find parent of. Must not be <code>null</code>. + * @return Parent file or null if none + */ + @Deprecated + private File getParentFile(final File file) { + final File parent = file.getParentFile(); + + if (parent != null && msgOutputLevel >= Project.MSG_VERBOSE) { + System.out.println("Searching in " + parent.getAbsolutePath()); + } + + return parent; + } + + /** + * Search parent directories for the build file. + * <p> + * Takes the given target as a suffix to append to each + * parent directory in search of a build file. Once the + * root of the file-system has been reached <code>null</code> + * is returned. + * + * @param start Leaf directory of search. + * Must not be <code>null</code>. + * @param suffix Suffix filename to look for in parents. + * Must not be <code>null</code>. + * + * @return A handle to the build file if one is found, <code>null</code> if not + */ + private File findBuildFile(final String start, final String suffix) { + if (msgOutputLevel >= Project.MSG_INFO) { + System.out.println("Searching for " + suffix + " ..."); + } + + File parent = new File(new File(start).getAbsolutePath()); + File file = new File(parent, suffix); + + // check if the target file exists in the current directory + while (!file.exists()) { + // change to parent directory + parent = getParentFile(parent); + + // if parent is null, then we are at the root of the fs, + // complain that we can't find the build file. + if (parent == null) { + return null; + } + + // refresh our file handle + file = new File(parent, suffix); + } + + return file; + } + + /** + * Executes the build. If the constructor for this instance failed + * (e.g. returned after issuing a warning), this method returns + * immediately. + * + * @param coreLoader The classloader to use to find core classes. + * May be <code>null</code>, in which case the + * system classloader is used. + * + * @exception BuildException if the build fails + */ + private void runBuild(final ClassLoader coreLoader) throws BuildException { + + if (!readyToRun) { + return; + } + + final ArgumentProcessorRegistry processorRegistry = ArgumentProcessorRegistry.getInstance(); + + for (final ArgumentProcessor processor : processorRegistry.getProcessors()) { + final List<String> extraArgs = extraArguments.get(processor.getClass()); + if (extraArgs != null) { + if (processor.handleArg(extraArgs)) { + return; + } + } + } + + final Project project = new Project(); + project.setCoreLoader(coreLoader); + + Throwable error = null; + + try { + addBuildListeners(project); + addInputHandler(project); + + final PrintStream savedErr = System.err; + final PrintStream savedOut = System.out; + final InputStream savedIn = System.in; + + // use a system manager that prevents from System.exit() + SecurityManager oldsm = null; + oldsm = System.getSecurityManager(); + + //SecurityManager can not be installed here for backwards + //compatibility reasons (PD). Needs to be loaded prior to + //ant class if we are going to implement it. + //System.setSecurityManager(new NoExitSecurityManager()); + try { + if (allowInput) { + project.setDefaultInputStream(System.in); + } + System.setIn(new DemuxInputStream(project)); + System.setOut(new PrintStream(new DemuxOutputStream(project, false))); + System.setErr(new PrintStream(new DemuxOutputStream(project, true))); + + + if (!projectHelp) { + project.fireBuildStarted(); + } + + // set the thread priorities + if (threadPriority != null) { + try { + project.log("Setting Ant's thread priority to " + + threadPriority, Project.MSG_VERBOSE); + Thread.currentThread().setPriority(threadPriority.intValue()); + } catch (final SecurityException swallowed) { + //we cannot set the priority here. + project.log("A security manager refused to set the -nice value"); + } + } + + setProperties(project); + + project.setKeepGoingMode(keepGoingMode); + if (proxy) { + //proxy setup if enabled + final ProxySetup proxySetup = new ProxySetup(project); + proxySetup.enableProxies(); + } + + for (final ArgumentProcessor processor : processorRegistry.getProcessors()) { + final List<String> extraArgs = extraArguments.get(processor.getClass()); + if (extraArgs != null) { + processor.prepareConfigure(project, extraArgs); + } + } + + ProjectHelper.configureProject(project, buildFile); + + for (final ArgumentProcessor processor : processorRegistry.getProcessors()) { + final List<String> extraArgs = extraArguments.get(processor.getClass()); + if (extraArgs != null) { + if (processor.handleArg(project, extraArgs)) { + return; + } + } + } + + if (projectHelp) { + printDescription(project); + printTargets(project, msgOutputLevel > Project.MSG_INFO, + msgOutputLevel > Project.MSG_VERBOSE); + return; + } + + // make sure that we have a target to execute + if (targets.size() == 0) { + if (project.getDefaultTarget() != null) { + targets.addElement(project.getDefaultTarget()); + } + } + + project.executeTargets(targets); + } finally { + // put back the original security manager + //The following will never eval to true. (PD) + if (oldsm != null) { + System.setSecurityManager(oldsm); + } + + System.setOut(savedOut); + System.setErr(savedErr); + System.setIn(savedIn); + } + } catch (final RuntimeException exc) { + error = exc; + throw exc; + } catch (final Error e) { + error = e; + throw e; + } finally { + if (!projectHelp) { + try { + project.fireBuildFinished(error); + } catch (final Throwable t) { + // yes, I know it is bad style to catch Throwable, + // but if we don't, we lose valuable information + System.err.println("Caught an exception while logging the" + + " end of the build. Exception was:"); + t.printStackTrace(); + if (error != null) { + System.err.println("There has been an error prior to" + + " that:"); + error.printStackTrace(); + } + throw new BuildException(t); + } + } else if (error != null) { + project.log(error.toString(), Project.MSG_ERR); + } + } + } + + private void setProperties(final Project project) { + + project.init(); + + // resolve properties + final PropertyHelper propertyHelper = PropertyHelper.getPropertyHelper(project); + @SuppressWarnings({ "rawtypes", "unchecked" }) + final Map raw = new HashMap(definedProps); + @SuppressWarnings("unchecked") + final Map<String, Object> props = raw; + + final ResolvePropertyMap resolver = new ResolvePropertyMap(project, + NOPROPERTIES, propertyHelper.getExpanders()); + resolver.resolveAllProperties(props, null, false); + + // set user-define properties + for (final Entry<String, Object> ent : props.entrySet()) { + final String arg = ent.getKey(); + final Object value = ent.getValue(); + project.setUserProperty(arg, String.valueOf(value)); + } + + project.setUserProperty(MagicNames.ANT_FILE, + buildFile.getAbsolutePath()); + project.setUserProperty(MagicNames.ANT_FILE_TYPE, + MagicNames.ANT_FILE_TYPE_FILE); + } + + /** + * Adds the listeners specified in the command line arguments, + * along with the default listener, to the specified project. + * + * @param project The project to add listeners to. + * Must not be <code>null</code>. + */ + protected void addBuildListeners(final Project project) { + + // Add the default listener + project.addBuildListener(createLogger()); + + final int count = listeners.size(); + for (int i = 0; i < count; i++) { + final String className = listeners.elementAt(i); + final BuildListener listener = + (BuildListener) ClasspathUtils.newInstance(className, + Main.class.getClassLoader(), BuildListener.class); + project.setProjectReference(listener); + + project.addBuildListener(listener); + } + } + + /** + * Creates the InputHandler and adds it to the project. + * + * @param project the project instance. + * + * @exception BuildException if a specified InputHandler + * implementation could not be loaded. + */ + private void addInputHandler(final Project project) throws BuildException { + InputHandler handler = null; + if (inputHandlerClassname == null) { + handler = new DefaultInputHandler(); + } else { + handler = (InputHandler) ClasspathUtils.newInstance( + inputHandlerClassname, Main.class.getClassLoader(), + InputHandler.class); + project.setProjectReference(handler); + } + project.setInputHandler(handler); + } + + // TODO: (Jon Skeet) Any reason for writing a message and then using a bare + // RuntimeException rather than just using a BuildException here? Is it + // in case the message could end up being written to no loggers (as the + // loggers could have failed to be created due to this failure)? + /** + * Creates the default build logger for sending build events to the ant + * log. + * + * @return the logger instance for this build. + */ + private BuildLogger createLogger() { + BuildLogger logger = null; + if (silent) { + logger = new SilentLogger(); + msgOutputLevel = Project.MSG_WARN; + emacsMode = true; + } else if (loggerClassname != null) { + try { + logger = (BuildLogger) ClasspathUtils.newInstance( + loggerClassname, Main.class.getClassLoader(), + BuildLogger.class); + } catch (final BuildException e) { + System.err.println("The specified logger class " + + loggerClassname + + " could not be used because " + e.getMessage()); + throw new RuntimeException(); + } + } else { + logger = new DefaultLogger(); + } + + logger.setMessageOutputLevel(msgOutputLevel); + logger.setOutputPrintStream(out); + logger.setErrorPrintStream(err); + logger.setEmacsMode(emacsMode); + + return logger; + } + + /** + * Prints the usage information for this class to <code>System.out</code>. + */ + private static void printUsage() { + System.out.println("ant [options] [target [target2 [target3] ...]]"); + System.out.println("Options: "); + System.out.println(" -help, -h print this message and exit"); + System.out.println(" -projecthelp, -p print project help information and exit"); + System.out.println(" -version print the version information and exit"); + System.out.println(" -diagnostics print information that might be helpful to"); + System.out.println(" diagnose or report problems and exit"); + System.out.println(" -quiet, -q be extra quiet"); + System.out.println(" -silent, -S print nothing but task outputs and build failures"); + System.out.println(" -verbose, -v be extra verbose"); + System.out.println(" -debug, -d print debugging information"); + System.out.println(" -emacs, -e produce logging information without adornments"); + System.out.println(" -lib <path> specifies a path to search for jars and classes"); + System.out.println(" -logfile <file> use given file for log"); + System.out.println(" -l <file> ''"); + System.out.println(" -logger <classname> the class which is to perform logging"); + System.out.println(" -listener <classname> add an instance of class as a project listener"); + System.out.println(" -noinput do not allow interactive input"); + System.out.println(" -buildfile <file> use given buildfile"); + System.out.println(" -file <file> ''"); + System.out.println(" -f <file> ''"); + System.out.println(" -D<property>=<value> use value for given property"); + System.out.println(" -keep-going, -k execute all targets that do not depend"); + System.out.println(" on failed target(s)"); + System.out.println(" -propertyfile <name> load all properties from file with -D"); + System.out.println(" properties taking precedence"); + System.out.println(" -inputhandler <class> the class which will handle input requests"); + System.out.println(" -find <file> (s)earch for buildfile towards the root of"); + System.out.println(" -s <file> the filesystem and use it"); + System.out.println(" -nice number A niceness value for the main thread:" + + " 1 (lowest) to 10 (highest); 5 is the default"); + System.out.println(" -nouserlib Run ant without using the jar files from" + + " ${user.home}/.ant/lib"); + System.out.println(" -noclasspath Run ant without using CLASSPATH"); + System.out.println(" -autoproxy Java1.5+: use the OS proxy settings"); + System.out.println(" -main <class> override Ant's normal entry point"); + for (final ArgumentProcessor processor : ArgumentProcessorRegistry.getInstance().getProcessors()) { + processor.printUsage(System.out); + } + } + + /** + * Prints the Ant version information to <code>System.out</code>. + * + * @exception BuildException if the version information is unavailable + */ + private static void printVersion(final int logLevel) throws BuildException { + System.out.println(getAntVersion()); + } + + /** + * Cache of the Ant version information when it has been loaded. + */ + private static String antVersion = null; + + /** + * Cache of the short Ant version information when it has been loaded. + */ + private static String shortAntVersion = null; + + /** + * Returns the Ant version information, if available. Once the information + * has been loaded once, it's cached and returned from the cache on future + * calls. + * + * @return the Ant version information as a String + * (always non-<code>null</code>) + * + * @exception BuildException if the version information is unavailable + */ + public static synchronized String getAntVersion() throws BuildException { + if (antVersion == null) { + try { + final Properties props = new Properties(); + final InputStream in = + Main.class.getResourceAsStream("/org/apache/tools/ant/version.txt"); + props.load(in); + in.close(); + shortAntVersion = props.getProperty("VERSION"); + + final StringBuffer msg = new StringBuffer(); + msg.append("Apache Ant(TM) version "); + msg.append(shortAntVersion); + msg.append(" compiled on "); + msg.append(props.getProperty("DATE")); + antVersion = msg.toString(); + } catch (final IOException ioe) { + throw new BuildException("Could not load the version information:" + + ioe.getMessage()); + } catch (final NullPointerException npe) { + throw new BuildException("Could not load the version information."); + } + } + return antVersion; + } + + /** + * Returns the short Ant version information, if available. Once the information + * has been loaded once, it's cached and returned from the cache on future + * calls. + * + * @return the short Ant version information as a String + * (always non-<code>null</code>) + * + * @throws BuildException BuildException if the version information is unavailable + * @since Ant 1.9.3 + */ + public static String getShortAntVersion() throws BuildException { + if (shortAntVersion == null) { + getAntVersion(); + } + return shortAntVersion; + } + + /** + * Prints the description of a project (if there is one) to + * <code>System.out</code>. + * + * @param project The project to display a description of. + * Must not be <code>null</code>. + */ + private static void printDescription(final Project project) { + if (project.getDescription() != null) { + project.log(project.getDescription()); + } + } + + /** + * Targets in imported files with a project name + * and not overloaded by the main build file will + * be in the target map twice. This method + * removes the duplicate target. + * @param targets the targets to filter. + * @return the filtered targets. + */ + private static Map<String, Target> removeDuplicateTargets(final Map<String, Target> targets) { + final Map<Location, Target> locationMap = new HashMap<Location, Target>(); + for (final Entry<String, Target> entry : targets.entrySet()) { + final String name = entry.getKey(); + final Target target = entry.getValue(); + final Target otherTarget = locationMap.get(target.getLocation()); + // Place this entry in the location map if + // a) location is not in the map + // b) location is in map, but its name is longer + // (an imported target will have a name. prefix) + if (otherTarget == null + || otherTarget.getName().length() > name.length()) { + locationMap.put( + target.getLocation(), target); // Smallest name wins + } + } + final Map<String, Target> ret = new HashMap<String, Target>(); + for (final Target target : locationMap.values()) { + ret.put(target.getName(), target); + } + return ret; + } + + /** + * Prints a list of all targets in the specified project to + * <code>System.out</code>, optionally including subtargets. + * + * @param project The project to display a description of. + * Must not be <code>null</code>. + * @param printSubTargets Whether or not subtarget names should also be + * printed. + */ + private static void printTargets(final Project project, boolean printSubTargets, + final boolean printDependencies) { + // find the target with the longest name + int maxLength = 0; + final Map<String, Target> ptargets = removeDuplicateTargets(project.getTargets()); + // split the targets in top-level and sub-targets depending + // on the presence of a description + final Vector<String> topNames = new Vector<String>(); + final Vector<String> topDescriptions = new Vector<String>(); + final Vector<Enumeration<String>> topDependencies = new Vector<Enumeration<String>>(); + final Vector<String> subNames = new Vector<String>(); + final Vector<Enumeration<String>> subDependencies = new Vector<Enumeration<String>>(); + + for (final Target currentTarget : ptargets.values()) { + final String targetName = currentTarget.getName(); + if (targetName.equals("")) { + continue; + } + final String targetDescription = currentTarget.getDescription(); + // maintain a sorted list of targets + if (targetDescription == null) { + final int pos = findTargetPosition(subNames, targetName); + subNames.insertElementAt(targetName, pos); + if (printDependencies) { + subDependencies.insertElementAt(currentTarget.getDependencies(), pos); + } + } else { + final int pos = findTargetPosition(topNames, targetName); + topNames.insertElementAt(targetName, pos); + topDescriptions.insertElementAt(targetDescription, pos); + if (targetName.length() > maxLength) { + maxLength = targetName.length(); + } + if (printDependencies) { + topDependencies.insertElementAt(currentTarget.getDependencies(), pos); + } + } + } + + printTargets(project, topNames, topDescriptions, topDependencies, + "Main targets:", maxLength); + //if there were no main targets, we list all subtargets + //as it means nothing has a description + if (topNames.size() == 0) { + printSubTargets = true; + } + if (printSubTargets) { + printTargets(project, subNames, null, subDependencies, "Other targets:", 0); + } + + final String defaultTarget = project.getDefaultTarget(); + if (defaultTarget != null && !"".equals(defaultTarget)) { + // shouldn't need to check but... + project.log("Default target: " + defaultTarget); + } + } + + /** + * Searches for the correct place to insert a name into a list so as + * to keep the list sorted alphabetically. + * + * @param names The current list of names. Must not be <code>null</code>. + * @param name The name to find a place for. + * Must not be <code>null</code>. + * + * @return the correct place in the list for the given name + */ + private static int findTargetPosition(final Vector<String> names, final String name) { + final int size = names.size(); + int res = size; + for (int i = 0; i < size && res == size; i++) { + if (name.compareTo(names.elementAt(i)) < 0) { + res = i; + } + } + return res; + } + + /** + * Writes a formatted list of target names to <code>System.out</code> + * with an optional description. + * + * + * @param project the project instance. + * @param names The names to be printed. + * Must not be <code>null</code>. + * @param descriptions The associated target descriptions. + * May be <code>null</code>, in which case + * no descriptions are displayed. + * If non-<code>null</code>, this should have + * as many elements as <code>names</code>. + * @param topDependencies The list of dependencies for each target. + * The dependencies are listed as a non null + * enumeration of String. + * @param heading The heading to display. + * Should not be <code>null</code>. + * @param maxlen The maximum length of the names of the targets. + * If descriptions are given, they are padded to this + * position so they line up (so long as the names really + * <i>are</i> shorter than this). + */ + private static void printTargets(final Project project, final Vector<String> names, + final Vector<String> descriptions, final Vector<Enumeration<String>> dependencies, + final String heading, + final int maxlen) { + // now, start printing the targets and their descriptions + final String lSep = System.getProperty("line.separator"); + // got a bit annoyed that I couldn't find a pad function + String spaces = " "; + while (spaces.length() <= maxlen) { + spaces += spaces; + } + final StringBuilder msg = new StringBuilder(); + msg.append(heading + lSep + lSep); + final int size = names.size(); + for (int i = 0; i < size; i++) { + msg.append(" "); + msg.append(names.elementAt(i)); + if (descriptions != null) { + msg.append( + spaces.substring(0, maxlen - names.elementAt(i).length() + 2)); + msg.append(descriptions.elementAt(i)); + } + msg.append(lSep); + if (!dependencies.isEmpty()) { + final Enumeration<String> deps = dependencies.elementAt(i); + if (deps.hasMoreElements()) { + msg.append(" depends on: "); + while (deps.hasMoreElements()) { + msg.append(deps.nextElement()); + if (deps.hasMoreElements()) { + msg.append(", "); + } + } + msg.append(lSep); + } + } + } + project.log(msg.toString(), Project.MSG_WARN); + } +} |