0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one or more
0003: * contributor license agreements. See the NOTICE file distributed with
0004: * this work for additional information regarding copyright ownership.
0005: * The ASF licenses this file to You under the Apache License, Version 2.0
0006: * (the "License"); you may not use this file except in compliance with
0007: * the License. You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: *
0017: */
0018:
0019: package org.apache.tools.ant;
0020:
0021: import java.io.File;
0022: import java.io.FileInputStream;
0023: import java.io.FileOutputStream;
0024: import java.io.IOException;
0025: import java.io.InputStream;
0026: import java.io.PrintStream;
0027: import java.util.Enumeration;
0028: import java.util.Properties;
0029: import java.util.Vector;
0030: import java.util.HashMap;
0031:
0032: import org.apache.tools.ant.input.DefaultInputHandler;
0033: import org.apache.tools.ant.input.InputHandler;
0034: import org.apache.tools.ant.launch.AntMain;
0035: import org.apache.tools.ant.util.ClasspathUtils;
0036: import org.apache.tools.ant.util.FileUtils;
0037: import org.apache.tools.ant.util.ProxySetup;
0038:
0039: /**
0040: * Command line entry point into Ant. This class is entered via the
0041: * canonical `public static void main` entry point and reads the
0042: * command line arguments. It then assembles and executes an Ant
0043: * project.
0044: * <p>
0045: * If you integrating Ant into some other tool, this is not the class
0046: * to use as an entry point. Please see the source code of this
0047: * class to see how it manipulates the Ant project classes.
0048: *
0049: */
0050: public class Main implements AntMain {
0051:
0052: /** The default build file name. {@value} */
0053: public static final String DEFAULT_BUILD_FILENAME = "build.xml";
0054:
0055: /** Our current message output status. Follows Project.MSG_XXX. */
0056: private int msgOutputLevel = Project.MSG_INFO;
0057:
0058: /** File that we are using for configuration. */
0059: private File buildFile; /* null */
0060:
0061: /** Stream to use for logging. */
0062: private static PrintStream out = System.out;
0063:
0064: /** Stream that we are using for logging error messages. */
0065: private static PrintStream err = System.err;
0066:
0067: /** The build targets. */
0068: private Vector targets = new Vector();
0069:
0070: /** Set of properties that can be used by tasks. */
0071: private Properties definedProps = new Properties();
0072:
0073: /** Names of classes to add as listeners to project. */
0074: private Vector listeners = new Vector(1);
0075:
0076: /** File names of property files to load on startup. */
0077: private Vector propertyFiles = new Vector(1);
0078:
0079: /** Indicates whether this build is to support interactive input */
0080: private boolean allowInput = true;
0081:
0082: /** keep going mode */
0083: private boolean keepGoingMode = false;
0084:
0085: /**
0086: * The Ant logger class. There may be only one logger. It will have
0087: * the right to use the 'out' PrintStream. The class must implements the
0088: * BuildLogger interface.
0089: */
0090: private String loggerClassname = null;
0091:
0092: /**
0093: * The Ant InputHandler class. There may be only one input
0094: * handler.
0095: */
0096: private String inputHandlerClassname = null;
0097:
0098: /**
0099: * Whether or not output to the log is to be unadorned.
0100: */
0101: private boolean emacsMode = false;
0102:
0103: /**
0104: * Whether or not this instance has successfully been
0105: * constructed and is ready to run.
0106: */
0107: private boolean readyToRun = false;
0108:
0109: /**
0110: * Whether or not we should only parse and display the project help
0111: * information.
0112: */
0113: private boolean projectHelp = false;
0114:
0115: /**
0116: * Whether or not a logfile is being used. This is used to
0117: * check if the output streams must be closed.
0118: */
0119: private static boolean isLogFileUsed = false;
0120:
0121: /**
0122: * optional thread priority
0123: */
0124: private Integer threadPriority = null;
0125:
0126: /**
0127: * proxy flag: default is false
0128: */
0129: private boolean proxy = false;
0130:
0131: /**
0132: * Prints the message of the Throwable if it (the message) is not
0133: * <code>null</code>.
0134: *
0135: * @param t Throwable to print the message of.
0136: * Must not be <code>null</code>.
0137: */
0138: private static void printMessage(Throwable t) {
0139: String message = t.getMessage();
0140: if (message != null) {
0141: System.err.println(message);
0142: }
0143: }
0144:
0145: /**
0146: * Creates a new instance of this class using the
0147: * arguments specified, gives it any extra user properties which have been
0148: * specified, and then runs the build using the classloader provided.
0149: *
0150: * @param args Command line arguments. Must not be <code>null</code>.
0151: * @param additionalUserProperties Any extra properties to use in this
0152: * build. May be <code>null</code>, which is the equivalent to
0153: * passing in an empty set of properties.
0154: * @param coreLoader Classloader used for core classes. May be
0155: * <code>null</code> in which case the system classloader is used.
0156: */
0157: public static void start(String[] args,
0158: Properties additionalUserProperties, ClassLoader coreLoader) {
0159: Main m = new Main();
0160: m.startAnt(args, additionalUserProperties, coreLoader);
0161: }
0162:
0163: /**
0164: * Start Ant
0165: * @param args command line args
0166: * @param additionalUserProperties properties to set beyond those that
0167: * may be specified on the args list
0168: * @param coreLoader - not used
0169: *
0170: * @since Ant 1.6
0171: */
0172: public void startAnt(String[] args,
0173: Properties additionalUserProperties, ClassLoader coreLoader) {
0174:
0175: try {
0176: Diagnostics.validateVersion();
0177: processArgs(args);
0178: } catch (Throwable exc) {
0179: handleLogfile();
0180: printMessage(exc);
0181: exit(1);
0182: return;
0183: }
0184:
0185: if (additionalUserProperties != null) {
0186: for (Enumeration e = additionalUserProperties.keys(); e
0187: .hasMoreElements();) {
0188: String key = (String) e.nextElement();
0189: String property = additionalUserProperties
0190: .getProperty(key);
0191: definedProps.put(key, property);
0192: }
0193: }
0194:
0195: // expect the worst
0196: int exitCode = 1;
0197: try {
0198: try {
0199: runBuild(coreLoader);
0200: exitCode = 0;
0201: } catch (ExitStatusException ese) {
0202: exitCode = ese.getStatus();
0203: if (exitCode != 0) {
0204: throw ese;
0205: }
0206: }
0207: } catch (BuildException be) {
0208: if (err != System.err) {
0209: printMessage(be);
0210: }
0211: } catch (Throwable exc) {
0212: exc.printStackTrace();
0213: printMessage(exc);
0214: } finally {
0215: handleLogfile();
0216: }
0217: exit(exitCode);
0218: }
0219:
0220: /**
0221: * This operation is expected to call {@link System#exit(int)}, which
0222: * is what the base version does.
0223: * However, it is possible to do something else.
0224: * @param exitCode code to exit with
0225: */
0226: protected void exit(int exitCode) {
0227: System.exit(exitCode);
0228: }
0229:
0230: /**
0231: * Close logfiles, if we have been writing to them.
0232: *
0233: * @since Ant 1.6
0234: */
0235: private static void handleLogfile() {
0236: if (isLogFileUsed) {
0237: FileUtils.close(out);
0238: FileUtils.close(err);
0239: }
0240: }
0241:
0242: /**
0243: * Command line entry point. This method kicks off the building
0244: * of a project object and executes a build using either a given
0245: * target or the default target.
0246: *
0247: * @param args Command line arguments. Must not be <code>null</code>.
0248: */
0249: public static void main(String[] args) {
0250: start(args, null, null);
0251: }
0252:
0253: /**
0254: * Constructor used when creating Main for later arg processing
0255: * and startup
0256: */
0257: public Main() {
0258: }
0259:
0260: /**
0261: * Sole constructor, which parses and deals with command line
0262: * arguments.
0263: *
0264: * @param args Command line arguments. Must not be <code>null</code>.
0265: *
0266: * @exception BuildException if the specified build file doesn't exist
0267: * or is a directory.
0268: *
0269: * @deprecated since 1.6.x
0270: */
0271: protected Main(String[] args) throws BuildException {
0272: processArgs(args);
0273: }
0274:
0275: /**
0276: * Process command line arguments.
0277: * When ant is started from Launcher, launcher-only arguments doe not get
0278: * passed through to this routine.
0279: *
0280: * @param args the command line arguments.
0281: *
0282: * @since Ant 1.6
0283: */
0284: private void processArgs(String[] args) {
0285: String searchForThis = null;
0286: PrintStream logTo = null;
0287:
0288: //this is the list of lu
0289: HashMap launchCommands = new HashMap();
0290: launchCommands.put("-lib", "");
0291: launchCommands.put("-cp", "");
0292: launchCommands.put("-noclasspath", "");
0293: launchCommands.put("--noclasspath", "");
0294: launchCommands.put("-nouserlib", "");
0295: launchCommands.put("--nouserlib", "");
0296: launchCommands.put("-main", "");
0297: // cycle through given args
0298:
0299: for (int i = 0; i < args.length; i++) {
0300: String arg = args[i];
0301:
0302: if (arg.equals("-help") || arg.equals("-h")) {
0303: printUsage();
0304: return;
0305: } else if (arg.equals("-version")) {
0306: printVersion();
0307: return;
0308: } else if (arg.equals("-diagnostics")) {
0309: Diagnostics.doReport(System.out);
0310: return;
0311: } else if (arg.equals("-quiet") || arg.equals("-q")) {
0312: msgOutputLevel = Project.MSG_WARN;
0313: } else if (arg.equals("-verbose") || arg.equals("-v")) {
0314: printVersion();
0315: msgOutputLevel = Project.MSG_VERBOSE;
0316: } else if (arg.equals("-debug") || arg.equals("-d")) {
0317: printVersion();
0318: msgOutputLevel = Project.MSG_DEBUG;
0319: } else if (arg.equals("-noinput")) {
0320: allowInput = false;
0321: } else if (arg.equals("-logfile") || arg.equals("-l")) {
0322: try {
0323: File logFile = new File(args[i + 1]);
0324: i++;
0325: logTo = new PrintStream(new FileOutputStream(
0326: logFile));
0327: isLogFileUsed = true;
0328: } catch (IOException ioe) {
0329: String msg = "Cannot write on the specified log file. "
0330: + "Make sure the path exists and you have write "
0331: + "permissions.";
0332: throw new BuildException(msg);
0333: } catch (ArrayIndexOutOfBoundsException aioobe) {
0334: String msg = "You must specify a log file when "
0335: + "using the -log argument";
0336: throw new BuildException(msg);
0337: }
0338: } else if (arg.equals("-buildfile") || arg.equals("-file")
0339: || arg.equals("-f")) {
0340: try {
0341: buildFile = new File(args[i + 1].replace('/',
0342: File.separatorChar));
0343: i++;
0344: } catch (ArrayIndexOutOfBoundsException aioobe) {
0345: String msg = "You must specify a buildfile when "
0346: + "using the -buildfile argument";
0347: throw new BuildException(msg);
0348: }
0349: } else if (arg.equals("-listener")) {
0350: try {
0351: listeners.addElement(args[i + 1]);
0352: i++;
0353: } catch (ArrayIndexOutOfBoundsException aioobe) {
0354: String msg = "You must specify a classname when "
0355: + "using the -listener argument";
0356: throw new BuildException(msg);
0357: }
0358: } else if (arg.startsWith("-D")) {
0359:
0360: /* Interestingly enough, we get to here when a user
0361: * uses -Dname=value. However, in some cases, the OS
0362: * goes ahead and parses this out to args
0363: * {"-Dname", "value"}
0364: * so instead of parsing on "=", we just make the "-D"
0365: * characters go away and skip one argument forward.
0366: *
0367: * I don't know how to predict when the JDK is going
0368: * to help or not, so we simply look for the equals sign.
0369: */
0370:
0371: String name = arg.substring(2, arg.length());
0372: String value = null;
0373: int posEq = name.indexOf("=");
0374: if (posEq > 0) {
0375: value = name.substring(posEq + 1);
0376: name = name.substring(0, posEq);
0377: } else if (i < args.length - 1) {
0378: value = args[++i];
0379: } else {
0380: throw new BuildException(
0381: "Missing value for property " + name);
0382: }
0383:
0384: definedProps.put(name, value);
0385: } else if (arg.equals("-logger")) {
0386: if (loggerClassname != null) {
0387: throw new BuildException(
0388: "Only one logger class may "
0389: + " be specified.");
0390: }
0391: try {
0392: loggerClassname = args[++i];
0393: } catch (ArrayIndexOutOfBoundsException aioobe) {
0394: throw new BuildException(
0395: "You must specify a classname when"
0396: + " using the -logger argument");
0397: }
0398: } else if (arg.equals("-inputhandler")) {
0399: if (inputHandlerClassname != null) {
0400: throw new BuildException(
0401: "Only one input handler class may "
0402: + "be specified.");
0403: }
0404: try {
0405: inputHandlerClassname = args[++i];
0406: } catch (ArrayIndexOutOfBoundsException aioobe) {
0407: throw new BuildException(
0408: "You must specify a classname when"
0409: + " using the -inputhandler"
0410: + " argument");
0411: }
0412: } else if (arg.equals("-emacs") || arg.equals("-e")) {
0413: emacsMode = true;
0414: } else if (arg.equals("-projecthelp") || arg.equals("-p")) {
0415: // set the flag to display the targets and quit
0416: projectHelp = true;
0417: } else if (arg.equals("-find") || arg.equals("-s")) {
0418: // eat up next arg if present, default to build.xml
0419: if (i < args.length - 1) {
0420: searchForThis = args[++i];
0421: } else {
0422: searchForThis = DEFAULT_BUILD_FILENAME;
0423: }
0424: } else if (arg.startsWith("-propertyfile")) {
0425: try {
0426: propertyFiles.addElement(args[i + 1]);
0427: i++;
0428: } catch (ArrayIndexOutOfBoundsException aioobe) {
0429: String msg = "You must specify a property filename when "
0430: + "using the -propertyfile argument";
0431: throw new BuildException(msg);
0432: }
0433: } else if (arg.equals("-k") || arg.equals("-keep-going")) {
0434: keepGoingMode = true;
0435: } else if (arg.equals("-nice")) {
0436: try {
0437: threadPriority = Integer.decode(args[i + 1]);
0438: } catch (ArrayIndexOutOfBoundsException aioobe) {
0439: throw new BuildException(
0440: "You must supply a niceness value (1-10)"
0441: + " after the -nice option");
0442: } catch (NumberFormatException e) {
0443: throw new BuildException(
0444: "Unrecognized niceness value: "
0445: + args[i + 1]);
0446: }
0447: i++;
0448: if (threadPriority.intValue() < Thread.MIN_PRIORITY
0449: || threadPriority.intValue() > Thread.MAX_PRIORITY) {
0450: throw new BuildException(
0451: "Niceness value is out of the range 1-10");
0452: }
0453: } else if (launchCommands.get(arg) != null) {
0454: //catch script/ant mismatch with a meaningful message
0455: //we could ignore it, but there are likely to be other
0456: //version problems, so we stamp down on the configuration now
0457: String msg = "Ant's Main method is being handed "
0458: + "an option "
0459: + arg
0460: + " that is only for the launcher class."
0461: + "\nThis can be caused by a version mismatch between "
0462: + "the ant script/.bat file and Ant itself.";
0463: throw new BuildException(msg);
0464: } else if (arg.equals("-autoproxy")) {
0465: proxy = false;
0466: } else if (arg.startsWith("-")) {
0467: // we don't have any more args to recognize!
0468: String msg = "Unknown argument: " + arg;
0469: System.err.println(msg);
0470: printUsage();
0471: throw new BuildException("");
0472: } else {
0473: // if it's no other arg, it may be the target
0474: targets.addElement(arg);
0475: }
0476: }
0477:
0478: // if buildFile was not specified on the command line,
0479: if (buildFile == null) {
0480: // but -find then search for it
0481: if (searchForThis != null) {
0482: buildFile = findBuildFile(System
0483: .getProperty("user.dir"), searchForThis);
0484: } else {
0485: buildFile = new File(DEFAULT_BUILD_FILENAME);
0486: }
0487: }
0488:
0489: // make sure buildfile exists
0490: if (!buildFile.exists()) {
0491: System.out.println("Buildfile: " + buildFile
0492: + " does not exist!");
0493: throw new BuildException("Build failed");
0494: }
0495:
0496: // make sure it's not a directory (this falls into the ultra
0497: // paranoid lets check everything category
0498:
0499: if (buildFile.isDirectory()) {
0500: System.out.println("What? Buildfile: " + buildFile
0501: + " is a dir!");
0502: throw new BuildException("Build failed");
0503: }
0504:
0505: // Load the property files specified by -propertyfile
0506: for (int propertyFileIndex = 0; propertyFileIndex < propertyFiles
0507: .size(); propertyFileIndex++) {
0508: String filename = (String) propertyFiles
0509: .elementAt(propertyFileIndex);
0510: Properties props = new Properties();
0511: FileInputStream fis = null;
0512: try {
0513: fis = new FileInputStream(filename);
0514: props.load(fis);
0515: } catch (IOException e) {
0516: System.out.println("Could not load property file "
0517: + filename + ": " + e.getMessage());
0518: } finally {
0519: FileUtils.close(fis);
0520: }
0521:
0522: // ensure that -D properties take precedence
0523: Enumeration propertyNames = props.propertyNames();
0524: while (propertyNames.hasMoreElements()) {
0525: String name = (String) propertyNames.nextElement();
0526: if (definedProps.getProperty(name) == null) {
0527: definedProps.put(name, props.getProperty(name));
0528: }
0529: }
0530: }
0531:
0532: if (msgOutputLevel >= Project.MSG_INFO) {
0533: System.out.println("Buildfile: " + buildFile);
0534: }
0535:
0536: if (logTo != null) {
0537: out = logTo;
0538: err = logTo;
0539: System.setOut(out);
0540: System.setErr(err);
0541: }
0542: readyToRun = true;
0543: }
0544:
0545: /**
0546: * Helper to get the parent file for a given file.
0547: * <p>
0548: * Added to simulate File.getParentFile() from JDK 1.2.
0549: * @deprecated since 1.6.x
0550: *
0551: * @param file File to find parent of. Must not be <code>null</code>.
0552: * @return Parent file or null if none
0553: */
0554: private File getParentFile(File file) {
0555: File parent = file.getParentFile();
0556:
0557: if (parent != null && msgOutputLevel >= Project.MSG_VERBOSE) {
0558: System.out.println("Searching in "
0559: + parent.getAbsolutePath());
0560: }
0561:
0562: return parent;
0563: }
0564:
0565: /**
0566: * Search parent directories for the build file.
0567: * <p>
0568: * Takes the given target as a suffix to append to each
0569: * parent directory in search of a build file. Once the
0570: * root of the file-system has been reached an exception
0571: * is thrown.
0572: *
0573: * @param start Leaf directory of search.
0574: * Must not be <code>null</code>.
0575: * @param suffix Suffix filename to look for in parents.
0576: * Must not be <code>null</code>.
0577: *
0578: * @return A handle to the build file if one is found
0579: *
0580: * @exception BuildException if no build file is found
0581: */
0582: private File findBuildFile(String start, String suffix)
0583: throws BuildException {
0584: if (msgOutputLevel >= Project.MSG_INFO) {
0585: System.out.println("Searching for " + suffix + " ...");
0586: }
0587:
0588: File parent = new File(new File(start).getAbsolutePath());
0589: File file = new File(parent, suffix);
0590:
0591: // check if the target file exists in the current directory
0592: while (!file.exists()) {
0593: // change to parent directory
0594: parent = getParentFile(parent);
0595:
0596: // if parent is null, then we are at the root of the fs,
0597: // complain that we can't find the build file.
0598: if (parent == null) {
0599: throw new BuildException(
0600: "Could not locate a build file!");
0601: }
0602:
0603: // refresh our file handle
0604: file = new File(parent, suffix);
0605: }
0606:
0607: return file;
0608: }
0609:
0610: /**
0611: * Executes the build. If the constructor for this instance failed
0612: * (e.g. returned after issuing a warning), this method returns
0613: * immediately.
0614: *
0615: * @param coreLoader The classloader to use to find core classes.
0616: * May be <code>null</code>, in which case the
0617: * system classloader is used.
0618: *
0619: * @exception BuildException if the build fails
0620: */
0621: private void runBuild(ClassLoader coreLoader) throws BuildException {
0622:
0623: if (!readyToRun) {
0624: return;
0625: }
0626:
0627: final Project project = new Project();
0628: project.setCoreLoader(coreLoader);
0629:
0630: Throwable error = null;
0631:
0632: try {
0633: addBuildListeners(project);
0634: addInputHandler(project);
0635:
0636: PrintStream savedErr = System.err;
0637: PrintStream savedOut = System.out;
0638: InputStream savedIn = System.in;
0639:
0640: // use a system manager that prevents from System.exit()
0641: SecurityManager oldsm = null;
0642: oldsm = System.getSecurityManager();
0643:
0644: //SecurityManager can not be installed here for backwards
0645: //compatibility reasons (PD). Needs to be loaded prior to
0646: //ant class if we are going to implement it.
0647: //System.setSecurityManager(new NoExitSecurityManager());
0648: try {
0649: if (allowInput) {
0650: project.setDefaultInputStream(System.in);
0651: }
0652: System.setIn(new DemuxInputStream(project));
0653: System.setOut(new PrintStream(new DemuxOutputStream(
0654: project, false)));
0655: System.setErr(new PrintStream(new DemuxOutputStream(
0656: project, true)));
0657:
0658: if (!projectHelp) {
0659: project.fireBuildStarted();
0660: }
0661:
0662: // set the thread priorities
0663: if (threadPriority != null) {
0664: try {
0665: project.log("Setting Ant's thread priority to "
0666: + threadPriority, Project.MSG_VERBOSE);
0667: Thread.currentThread().setPriority(
0668: threadPriority.intValue());
0669: } catch (SecurityException swallowed) {
0670: //we cannot set the priority here.
0671: project
0672: .log("A security manager refused to set the -nice value");
0673: }
0674: }
0675:
0676: project.init();
0677:
0678: // set user-define properties
0679: Enumeration e = definedProps.keys();
0680: while (e.hasMoreElements()) {
0681: String arg = (String) e.nextElement();
0682: String value = (String) definedProps.get(arg);
0683: project.setUserProperty(arg, value);
0684: }
0685:
0686: project.setUserProperty(MagicNames.ANT_FILE, buildFile
0687: .getAbsolutePath());
0688:
0689: project.setKeepGoingMode(keepGoingMode);
0690: if (proxy) {
0691: //proxy setup if enabled
0692: ProxySetup proxySetup = new ProxySetup(project);
0693: proxySetup.enableProxies();
0694: }
0695:
0696: ProjectHelper.configureProject(project, buildFile);
0697:
0698: if (projectHelp) {
0699: printDescription(project);
0700: printTargets(project,
0701: msgOutputLevel > Project.MSG_INFO);
0702: return;
0703: }
0704:
0705: // make sure that we have a target to execute
0706: if (targets.size() == 0) {
0707: if (project.getDefaultTarget() != null) {
0708: targets.addElement(project.getDefaultTarget());
0709: }
0710: }
0711:
0712: project.executeTargets(targets);
0713: } finally {
0714: // put back the original security manager
0715: //The following will never eval to true. (PD)
0716: if (oldsm != null) {
0717: System.setSecurityManager(oldsm);
0718: }
0719:
0720: System.setOut(savedOut);
0721: System.setErr(savedErr);
0722: System.setIn(savedIn);
0723: }
0724: } catch (RuntimeException exc) {
0725: error = exc;
0726: throw exc;
0727: } catch (Error e) {
0728: error = e;
0729: throw e;
0730: } finally {
0731: if (!projectHelp) {
0732: project.fireBuildFinished(error);
0733: } else if (error != null) {
0734: project.log(error.toString(), Project.MSG_ERR);
0735: }
0736: }
0737: }
0738:
0739: /**
0740: * Adds the listeners specified in the command line arguments,
0741: * along with the default listener, to the specified project.
0742: *
0743: * @param project The project to add listeners to.
0744: * Must not be <code>null</code>.
0745: */
0746: protected void addBuildListeners(Project project) {
0747:
0748: // Add the default listener
0749: project.addBuildListener(createLogger());
0750:
0751: for (int i = 0; i < listeners.size(); i++) {
0752: String className = (String) listeners.elementAt(i);
0753: BuildListener listener = (BuildListener) ClasspathUtils
0754: .newInstance(className,
0755: Main.class.getClassLoader(),
0756: BuildListener.class);
0757: if (project != null) {
0758: project.setProjectReference(listener);
0759: }
0760: project.addBuildListener(listener);
0761: }
0762: }
0763:
0764: /**
0765: * Creates the InputHandler and adds it to the project.
0766: *
0767: * @param project the project instance.
0768: *
0769: * @exception BuildException if a specified InputHandler
0770: * implementation could not be loaded.
0771: */
0772: private void addInputHandler(Project project) throws BuildException {
0773: InputHandler handler = null;
0774: if (inputHandlerClassname == null) {
0775: handler = new DefaultInputHandler();
0776: } else {
0777: handler = (InputHandler) ClasspathUtils.newInstance(
0778: inputHandlerClassname, Main.class.getClassLoader(),
0779: InputHandler.class);
0780: if (project != null) {
0781: project.setProjectReference(handler);
0782: }
0783: }
0784: project.setInputHandler(handler);
0785: }
0786:
0787: // XXX: (Jon Skeet) Any reason for writing a message and then using a bare
0788: // RuntimeException rather than just using a BuildException here? Is it
0789: // in case the message could end up being written to no loggers (as the
0790: // loggers could have failed to be created due to this failure)?
0791: /**
0792: * Creates the default build logger for sending build events to the ant
0793: * log.
0794: *
0795: * @return the logger instance for this build.
0796: */
0797: private BuildLogger createLogger() {
0798: BuildLogger logger = null;
0799: if (loggerClassname != null) {
0800: try {
0801: logger = (BuildLogger) ClasspathUtils.newInstance(
0802: loggerClassname, Main.class.getClassLoader(),
0803: BuildLogger.class);
0804: } catch (BuildException e) {
0805: System.err.println("The specified logger class "
0806: + loggerClassname
0807: + " could not be used because "
0808: + e.getMessage());
0809: throw new RuntimeException();
0810: }
0811: } else {
0812: logger = new DefaultLogger();
0813: }
0814:
0815: logger.setMessageOutputLevel(msgOutputLevel);
0816: logger.setOutputPrintStream(out);
0817: logger.setErrorPrintStream(err);
0818: logger.setEmacsMode(emacsMode);
0819:
0820: return logger;
0821: }
0822:
0823: /**
0824: * Prints the usage information for this class to <code>System.out</code>.
0825: */
0826: private static void printUsage() {
0827: String lSep = System.getProperty("line.separator");
0828: StringBuffer msg = new StringBuffer();
0829: msg.append("ant [options] [target [target2 [target3] ...]]"
0830: + lSep);
0831: msg.append("Options: " + lSep);
0832: msg
0833: .append(" -help, -h print this message"
0834: + lSep);
0835: msg
0836: .append(" -projecthelp, -p print project help information"
0837: + lSep);
0838: msg
0839: .append(" -version print the version information and exit"
0840: + lSep);
0841: msg
0842: .append(" -diagnostics print information that might be helpful to"
0843: + lSep);
0844: msg
0845: .append(" diagnose or report problems."
0846: + lSep);
0847: msg.append(" -quiet, -q be extra quiet" + lSep);
0848: msg.append(" -verbose, -v be extra verbose" + lSep);
0849: msg
0850: .append(" -debug, -d print debugging information"
0851: + lSep);
0852: msg
0853: .append(" -emacs, -e produce logging information without adornments"
0854: + lSep);
0855: msg
0856: .append(" -lib <path> specifies a path to search for jars and classes"
0857: + lSep);
0858: msg.append(" -logfile <file> use given file for log"
0859: + lSep);
0860: msg.append(" -l <file> ''" + lSep);
0861: msg
0862: .append(" -logger <classname> the class which is to perform logging"
0863: + lSep);
0864: msg
0865: .append(" -listener <classname> add an instance of class as a project listener"
0866: + lSep);
0867: msg
0868: .append(" -noinput do not allow interactive input"
0869: + lSep);
0870: msg.append(" -buildfile <file> use given buildfile"
0871: + lSep);
0872: msg.append(" -file <file> ''" + lSep);
0873: msg.append(" -f <file> ''" + lSep);
0874: msg
0875: .append(" -D<property>=<value> use value for given property"
0876: + lSep);
0877: msg
0878: .append(" -keep-going, -k execute all targets that do not depend"
0879: + lSep);
0880: msg.append(" on failed target(s)"
0881: + lSep);
0882: msg
0883: .append(" -propertyfile <name> load all properties from file with -D"
0884: + lSep);
0885: msg
0886: .append(" properties taking precedence"
0887: + lSep);
0888: msg
0889: .append(" -inputhandler <class> the class which will handle input requests"
0890: + lSep);
0891: msg
0892: .append(" -find <file> (s)earch for buildfile towards the root of"
0893: + lSep);
0894: msg.append(" -s <file> the filesystem and use it"
0895: + lSep);
0896: msg
0897: .append(" -nice number A niceness value for the main thread:"
0898: + lSep
0899: + " 1 (lowest) to 10 (highest); 5 is the default"
0900: + lSep);
0901: msg
0902: .append(" -nouserlib Run ant without using the jar files from"
0903: + lSep
0904: + " ${user.home}/.ant/lib"
0905: + lSep);
0906: msg
0907: .append(" -noclasspath Run ant without using CLASSPATH"
0908: + lSep);
0909: msg
0910: .append(" -autoproxy Java1.5+: use the OS proxy settings"
0911: + lSep);
0912: msg
0913: .append(" -main <class> override Ant's normal entry point");
0914: System.out.println(msg.toString());
0915: }
0916:
0917: /**
0918: * Prints the Ant version information to <code>System.out</code>.
0919: *
0920: * @exception BuildException if the version information is unavailable
0921: */
0922: private static void printVersion() throws BuildException {
0923: System.out.println(getAntVersion());
0924: }
0925:
0926: /**
0927: * Cache of the Ant version information when it has been loaded.
0928: */
0929: private static String antVersion = null;
0930:
0931: /**
0932: * Returns the Ant version information, if available. Once the information
0933: * has been loaded once, it's cached and returned from the cache on future
0934: * calls.
0935: *
0936: * @return the Ant version information as a String
0937: * (always non-<code>null</code>)
0938: *
0939: * @exception BuildException if the version information is unavailable
0940: */
0941: public static synchronized String getAntVersion()
0942: throws BuildException {
0943: if (antVersion == null) {
0944: try {
0945: Properties props = new Properties();
0946: InputStream in = Main.class
0947: .getResourceAsStream("/org/apache/tools/ant/version.txt");
0948: props.load(in);
0949: in.close();
0950:
0951: StringBuffer msg = new StringBuffer();
0952: msg.append("Apache Ant version ");
0953: msg.append(props.getProperty("VERSION"));
0954: msg.append(" compiled on ");
0955: msg.append(props.getProperty("DATE"));
0956: antVersion = msg.toString();
0957: } catch (IOException ioe) {
0958: throw new BuildException(
0959: "Could not load the version information:"
0960: + ioe.getMessage());
0961: } catch (NullPointerException npe) {
0962: throw new BuildException(
0963: "Could not load the version information.");
0964: }
0965: }
0966: return antVersion;
0967: }
0968:
0969: /**
0970: * Prints the description of a project (if there is one) to
0971: * <code>System.out</code>.
0972: *
0973: * @param project The project to display a description of.
0974: * Must not be <code>null</code>.
0975: */
0976: private static void printDescription(Project project) {
0977: if (project.getDescription() != null) {
0978: project.log(project.getDescription());
0979: }
0980: }
0981:
0982: /**
0983: * Prints a list of all targets in the specified project to
0984: * <code>System.out</code>, optionally including subtargets.
0985: *
0986: * @param project The project to display a description of.
0987: * Must not be <code>null</code>.
0988: * @param printSubTargets Whether or not subtarget names should also be
0989: * printed.
0990: */
0991: private static void printTargets(Project project,
0992: boolean printSubTargets) {
0993: // find the target with the longest name
0994: int maxLength = 0;
0995: Enumeration ptargets = project.getTargets().elements();
0996: String targetName;
0997: String targetDescription;
0998: Target currentTarget;
0999: // split the targets in top-level and sub-targets depending
1000: // on the presence of a description
1001: Vector topNames = new Vector();
1002: Vector topDescriptions = new Vector();
1003: Vector subNames = new Vector();
1004:
1005: while (ptargets.hasMoreElements()) {
1006: currentTarget = (Target) ptargets.nextElement();
1007: targetName = currentTarget.getName();
1008: if (targetName.equals("")) {
1009: continue;
1010: }
1011: targetDescription = currentTarget.getDescription();
1012: // maintain a sorted list of targets
1013: if (targetDescription == null) {
1014: int pos = findTargetPosition(subNames, targetName);
1015: subNames.insertElementAt(targetName, pos);
1016: } else {
1017: int pos = findTargetPosition(topNames, targetName);
1018: topNames.insertElementAt(targetName, pos);
1019: topDescriptions.insertElementAt(targetDescription, pos);
1020: if (targetName.length() > maxLength) {
1021: maxLength = targetName.length();
1022: }
1023: }
1024: }
1025:
1026: printTargets(project, topNames, topDescriptions,
1027: "Main targets:", maxLength);
1028: //if there were no main targets, we list all subtargets
1029: //as it means nothing has a description
1030: if (topNames.size() == 0) {
1031: printSubTargets = true;
1032: }
1033: if (printSubTargets) {
1034: printTargets(project, subNames, null, "Other targets:", 0);
1035: }
1036:
1037: String defaultTarget = project.getDefaultTarget();
1038: if (defaultTarget != null && !"".equals(defaultTarget)) {
1039: // shouldn't need to check but...
1040: project.log("Default target: " + defaultTarget);
1041: }
1042: }
1043:
1044: /**
1045: * Searches for the correct place to insert a name into a list so as
1046: * to keep the list sorted alphabetically.
1047: *
1048: * @param names The current list of names. Must not be <code>null</code>.
1049: * @param name The name to find a place for.
1050: * Must not be <code>null</code>.
1051: *
1052: * @return the correct place in the list for the given name
1053: */
1054: private static int findTargetPosition(Vector names, String name) {
1055: int res = names.size();
1056: for (int i = 0; i < names.size() && res == names.size(); i++) {
1057: if (name.compareTo((String) names.elementAt(i)) < 0) {
1058: res = i;
1059: }
1060: }
1061: return res;
1062: }
1063:
1064: /**
1065: * Writes a formatted list of target names to <code>System.out</code>
1066: * with an optional description.
1067: *
1068: *
1069: * @param project the project instance.
1070: * @param names The names to be printed.
1071: * Must not be <code>null</code>.
1072: * @param descriptions The associated target descriptions.
1073: * May be <code>null</code>, in which case
1074: * no descriptions are displayed.
1075: * If non-<code>null</code>, this should have
1076: * as many elements as <code>names</code>.
1077: * @param heading The heading to display.
1078: * Should not be <code>null</code>.
1079: * @param maxlen The maximum length of the names of the targets.
1080: * If descriptions are given, they are padded to this
1081: * position so they line up (so long as the names really
1082: * <i>are</i> shorter than this).
1083: */
1084: private static void printTargets(Project project, Vector names,
1085: Vector descriptions, String heading, int maxlen) {
1086: // now, start printing the targets and their descriptions
1087: String lSep = System.getProperty("line.separator");
1088: // got a bit annoyed that I couldn't find a pad function
1089: String spaces = " ";
1090: while (spaces.length() <= maxlen) {
1091: spaces += spaces;
1092: }
1093: StringBuffer msg = new StringBuffer();
1094: msg.append(heading + lSep + lSep);
1095: for (int i = 0; i < names.size(); i++) {
1096: msg.append(" ");
1097: msg.append(names.elementAt(i));
1098: if (descriptions != null) {
1099: msg.append(spaces.substring(0, maxlen
1100: - ((String) names.elementAt(i)).length() + 2));
1101: msg.append(descriptions.elementAt(i));
1102: }
1103: msg.append(lSep);
1104: }
1105: project.log(msg.toString(), Project.MSG_WARN);
1106: }
1107: }
|