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