diff --git a/src/main/org/apache/tools/ant/BuildEvent.java b/src/main/org/apache/tools/ant/BuildEvent.java new file mode 100644 index 000000000..4f58fee20 --- /dev/null +++ b/src/main/org/apache/tools/ant/BuildEvent.java @@ -0,0 +1,153 @@ +/* + * The Apache Software License, Version 1.1 + * + * Copyright (c) 1999 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ + +package org.apache.tools.ant; + +import java.util.EventObject; + +public class BuildEvent extends EventObject { + private Project project; + private Target target; + private Task task; + private String message; + private int priority; + private Throwable exception; + + /** + * Constructs a new build event. Fields that are not relevant + * can be set to null, except for the project field which is + * required. + */ + public BuildEvent( + Project project, + Target target, + Task task, + String message, + int priority, + Throwable exception) { + + super(getSource(project, target, task)); + + this.project = project; + this.target = target; + this.task = task; + this.message = message; + this.priority = priority; + this.exception = exception; + } + + /** + * Returns the project that fired this event. + */ + public Project getProject() { + return project; + } + + /** + * Returns the target that fired this event. + */ + public Target getTarget() { + return target; + } + + /** + * Returns the task that fired this event. + */ + public Task getTask() { + return task; + } + + /** + * Returns the logging message. This field will only be set + * for "messageLogged" events. + * + * @see BuildListener#messageLogged(BuildEvent) + */ + public String getMessage() { + return message; + } + + /** + * Returns the priority of the logging message. This field will only + * be set for "messageLogged" events. + * + * @see BuildListener#messageLogged(BuildEvent) + */ + public int getPriority(){ + return priority; + } + + /** + * Returns the exception that was thrown, if any. This field will only + * be set for "taskFinished", "targetFinished", and "buildFinished" events. + * + * @see BuildListener#taskFinished(BuildEvent) + * @see BuildListener#targetFinished(BuildEvent) + * @see BuildListener#buildFinished(BuildEvent) + */ + public Throwable getException() { + return exception; + } + + /** + * Returns the object that fired this event. + */ + private static Object getSource(Project project, Target target, Task task) { + if (task != null) return task; + if (target != null) return target; + if (project != null) return project; + + throw new IllegalArgumentException("Project field cannot be null"); + } +} \ No newline at end of file diff --git a/src/main/org/apache/tools/ant/BuildListener.java b/src/main/org/apache/tools/ant/BuildListener.java new file mode 100644 index 000000000..170f3a6fd --- /dev/null +++ b/src/main/org/apache/tools/ant/BuildListener.java @@ -0,0 +1,119 @@ +/* + * The Apache Software License, Version 1.1 + * + * Copyright (c) 1999 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ + +package org.apache.tools.ant; + +import java.util.EventListener; + +/** + * Classes that implement this interface will be notified when + * things happend during a build. + * + * @see BuildEvent + * @see Project#addBuildListener(BuildListener) + */ +public interface BuildListener extends EventListener { + + /** + * Fired before any targets are started. + */ + public void buildStarted(BuildEvent event); + + /** + * Fired after the last target has finished. This event + * will still be thrown if an error occured during the build. + * + * @see BuildEvent.getException() + */ + public void buildFinished(BuildEvent event); + + /** + * Fired when a target is started. + * + * @see BuildEvent#getTarget() + */ + public void targetStarted(BuildEvent event); + + /** + * Fired when a target has finished. This event will + * still be thrown if an error occured during the build. + * + * @see BuildEvent.getException() + */ + public void targetFinished(BuildEvent event); + + /** + * Fired when a task is started. + * + * @see BuildEvent#getTask() + */ + public void taskStarted(BuildEvent event); + + /** + * Fired when a task has finished. This event will still + * be throw if an error occured during the build. + * + * @see BuildEvent#getException() + */ + public void taskFinished(BuildEvent event); + + /** + * Fired whenever a message is logged. + * + * @see BuildEvent#getMessage() + * @see BuildEvent#getPriority() + */ + public void messageLogged(BuildEvent event); + +} \ No newline at end of file diff --git a/src/main/org/apache/tools/ant/DefaultLogger.java b/src/main/org/apache/tools/ant/DefaultLogger.java new file mode 100644 index 000000000..bdcb22b1e --- /dev/null +++ b/src/main/org/apache/tools/ant/DefaultLogger.java @@ -0,0 +1,111 @@ +/* + * The Apache Software License, Version 1.1 + * + * Copyright (c) 1999 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ + +package org.apache.tools.ant; + +import java.io.*; + +/** + * Writes build event to a PrintStream. Currently, it + * only writes which targets are being executed, and + * any messages that get logged. + */ +public class DefaultLogger implements BuildListener { + private PrintStream out; + private int msgOutputLevel; + + /** + * Constructs a new logger which will write to the specified + * PrintStream. Messages with a priority lower (higher?) than + * msgOutputLevel will be ignored. + */ + public DefaultLogger(PrintStream out, int msgOutputLevel) { + this.out = out; + this.msgOutputLevel = msgOutputLevel; + } + + public void buildStarted(BuildEvent event) {} + public void buildFinished(BuildEvent event) {} + + public void targetStarted(BuildEvent event) { + if (msgOutputLevel <= Project.MSG_INFO) { + out.println("Executing Target: " + event.getTarget().getName()); + } + } + + public void targetFinished(BuildEvent event) {} + + public void taskStarted(BuildEvent event) {} + public void taskFinished(BuildEvent event) {} + + public void messageLogged(BuildEvent event) { + + // Filter out messages based on priority + if (event.getPriority() <= msgOutputLevel) { + + // Print out the name of the task if we're in one + if (event.getTask() != null) { + String name = event.getTask().getClass().getName(); + int pos = name.lastIndexOf("."); + if (pos != -1) { + name = name.substring(pos + 1); + } + out.print("[" + name + "] "); + } + + // Print the message + out.println(event.getMessage()); + } + } +} \ No newline at end of file diff --git a/src/main/org/apache/tools/ant/Main.java b/src/main/org/apache/tools/ant/Main.java index b8b8621ed..e803118ac 100644 --- a/src/main/org/apache/tools/ant/Main.java +++ b/src/main/org/apache/tools/ant/Main.java @@ -73,19 +73,27 @@ import java.util.*; public class Main { /** Our current message output status. Follows Project.MSG_XXX */ - private static int msgOutputLevel = Project.MSG_INFO; + private int msgOutputLevel = Project.MSG_INFO; /** File that we are using for configuration */ - private static File buildFile = new File("build.xml"); + private File buildFile = new File("build.xml"); /** Stream that we are using for logging */ - private static PrintStream out = System.out; + private PrintStream out = System.out; /** The build targets */ - private static Vector targets = new Vector(5); + private Vector targets = new Vector(5); /** Set of properties that can be used by tasks */ - private static Properties definedProps = new Properties(); + private Properties definedProps = new Properties(); + + /** Names of classes to add as listeners to project */ + private Vector listeners = new Vector(5); + + /** + * Indicates if this ant should be run. + */ + private boolean readyToRun = false; /** * Command line entry point. This method kicks off the building @@ -94,8 +102,11 @@ public class Main { * * @param args Command line args. */ - public static void main(String[] args) { + new Main(args).runBuild(); + } + + protected Main(String[] args) { // cycle through given args @@ -137,6 +148,16 @@ public class Main { System.out.println(msg); return; } + } else if (arg.equals("-listener")) { + try { + listeners.addElement(args[i+1]); + i++; + } catch (ArrayIndexOutOfBoundsException aioobe) { + String msg = "You must specify a classname when " + + "using the -listener argument"; + System.out.println(msg); + return; + } } else if (arg.startsWith("-D")) { /* Interestingly enough, we get to here when a user @@ -170,6 +191,7 @@ public class Main { // if it's no other arg, it may be the target targets.addElement(arg); } + } // make sure buildfile exists @@ -187,18 +209,19 @@ public class Main { return; } - // ok, so if we've made it here, let's run the damn build allready - runBuild(); - - return; + readyToRun = true; } /** * Executes the build. */ - private static void runBuild() { + private void runBuild() { + if (!readyToRun) { + return; + } + // track when we started long startTime = System.currentTimeMillis(); @@ -206,7 +229,10 @@ public class Main { System.out.println("Buildfile: " + buildFile); } - Project project = new Project(out, msgOutputLevel); + Project project = new Project(); + addBuildListeners(project); + project.fireBuildStarted(); + project.init(); // set user-define properties Enumeration e = definedProps.keys(); @@ -215,7 +241,8 @@ public class Main { String value = (String)definedProps.get(arg); project.setUserProperty(arg, value); } - project.setUserProperty( "ant.file" , buildFile.getAbsolutePath() ); + + project.setUserProperty( "ant.file" , buildFile.getAbsolutePath() ); // first use the ProjectHelper to create the project object // from the given build file. @@ -248,10 +275,7 @@ public class Main { // actually do some work try { - Enumeration en = targets.elements(); - while (en.hasMoreElements()) { - project.executeTarget((String) en.nextElement()); - } + project.executeTargets(targets); } catch (BuildException be) { String msg = "\nBUILD FATAL ERROR\n\n"; System.out.println(msg + be.toString()); @@ -270,6 +294,31 @@ public class Main { } } + protected void addBuildListeners(Project project) { + + // Add the default listener + project.addBuildListener(createDefaultBuildListener()); + + for (int i = 0; i < listeners.size(); i++) { + String className = (String) listeners.elementAt(i); + try { + BuildListener listener = + (BuildListener) Class.forName(className).newInstance(); + project.addBuildListener(listener); + } + catch(Exception exc) { + throw new BuildException("Unable to instantiate " + className, exc); + } + } + } + + /** + * Creates the default build listener for displaying output to the screen. + */ + private BuildListener createDefaultBuildListener() { + return new DefaultLogger(out, msgOutputLevel); + } + /** * Prints the usage of how to use this class to System.out */ @@ -282,6 +331,7 @@ public class Main { msg.append(" -quiet be extra quiet" + lSep); msg.append(" -verbose be extra verbose" + lSep); msg.append(" -logfile use given file for log" + lSep); + msg.append(" -listener add an instance of class as a project listener" + lSep); msg.append(" -buildfile use given buildfile" + lSep); msg.append(" -D= use value for given property" + lSep); System.out.println(msg.toString()); diff --git a/src/main/org/apache/tools/ant/Project.java b/src/main/org/apache/tools/ant/Project.java index f60b5b90e..538b1f16d 100644 --- a/src/main/org/apache/tools/ant/Project.java +++ b/src/main/org/apache/tools/ant/Project.java @@ -93,8 +93,6 @@ public class Project { public static final String TOKEN_END = "@"; private String name; - private PrintStream out; - private int msgOutputLevel = MSG_INFO; private Hashtable properties = new Hashtable(); private Hashtable userProperties = new Hashtable(); @@ -105,11 +103,20 @@ public class Project { private Hashtable filters = new Hashtable(); private File baseDir; - public Project(PrintStream out, int msgOutputLevel) { + private Vector listeners = new Vector(); + protected Target currentTarget = null; + protected Task currentTask = null; - this.out = out; - this.msgOutputLevel = msgOutputLevel; + public Project() { + } + /** + * Initialise the project. + * + * This involves setting the default task definitions and loading the + * system properties. + */ + public void init() { detectJavaVersion(); String defs = "/org/apache/tools/ant/taskdefs/defaults.properties"; @@ -148,16 +155,16 @@ public class Project { } } - public PrintStream getOutput() { - return this.out; + public void addBuildListener(BuildListener listener) { + listeners.addElement(listener); } - public void setOutput(PrintStream out) { - this.out=out; + public void removeBuildListener(BuildListener listener) { + listeners.removeElement(listener); } - public int getOutputLevel() { - return this.msgOutputLevel; + public Vector getBuildListeners() { + return listeners; } public void log(String msg) { @@ -165,15 +172,11 @@ public class Project { } public void log(String msg, int msgLevel) { - if (msgLevel <= msgOutputLevel) { - out.println(msg); - } + fireMessageLogged(msg, msgLevel); } public void log(String msg, String tag, int msgLevel) { - if (msgLevel <= msgOutputLevel) { - out.println("[" + tag + "] " + msg); - } + fireMessageLogged(msg, msgLevel); } public void setProperty(String name, String value) { @@ -403,6 +406,23 @@ public class Project { } } + public void executeTargets(Vector targetNames) throws BuildException { + Throwable error = null; + + try { + for (int i = 0; i < targetNames.size(); i++) { + executeTarget((String)targetNames.elementAt(i)); + } + } + catch(RuntimeException exc) { + error = exc; + throw exc; + } + finally { + fireBuildFinished(error); + } + } + public void executeTarget(String targetName) throws BuildException { // sanity check ourselves, if we've been asked to build nothing @@ -432,7 +452,7 @@ public class Project { public File resolveFile(String fileName) { // deal with absolute files if (fileName.startsWith("/")) return new File( fileName ); - if (fileName.startsWith(System.getProperty("file.separator"))) + if (fileName.startsWith(System.getProperty("file.separator"))) return new File( fileName ); // Eliminate consecutive slashes after the drive spec @@ -672,13 +692,25 @@ public class Project { // Target and execute it. private final void runTarget(String target, Hashtable targets) throws BuildException { - Target t = (Target)targets.get(target); - if (t == null) { + + currentTarget = (Target)targets.get(target); + if (currentTarget == null) { throw new RuntimeException("Unexpected missing target `"+target+ "' in this project."); } - log("Executing Target: "+target, MSG_INFO); - t.execute(); + + try { + fireTargetStarted(); + currentTarget.execute(); + fireTargetFinished(null); + } + catch(RuntimeException exc) { + fireTargetFinished(exc); + throw exc; + } + finally { + currentTarget = null; + } } /** @@ -804,4 +836,72 @@ public class Project { public Hashtable getReferences() { return references; } + + protected void fireBuildStarted() { + BuildEvent event = createBuildEvent(); + for (int i = 0; i < listeners.size(); i++) { + BuildListener listener = (BuildListener) listeners.elementAt(i); + listener.buildStarted(event); + } + } + + protected void fireBuildFinished(Throwable exception) { + BuildEvent event = createBuildEvent(exception); + for (int i = 0; i < listeners.size(); i++) { + BuildListener listener = (BuildListener) listeners.elementAt(i); + listener.buildFinished(event); + } + } + + protected void fireTargetStarted() { + BuildEvent event = createBuildEvent(); + for (int i = 0; i < listeners.size(); i++) { + BuildListener listener = (BuildListener) listeners.elementAt(i); + listener.targetStarted(event); + } + } + + protected void fireTargetFinished(Throwable exception) { + BuildEvent event = createBuildEvent(exception); + for (int i = 0; i < listeners.size(); i++) { + BuildListener listener = (BuildListener) listeners.elementAt(i); + listener.targetFinished(event); + } + } + + protected void fireTaskStarted() { + BuildEvent event = createBuildEvent(); + for (int i = 0; i < listeners.size(); i++) { + BuildListener listener = (BuildListener) listeners.elementAt(i); + listener.taskStarted(event); + } + } + + protected void fireTaskFinished(Throwable exception) { + BuildEvent event = createBuildEvent(exception); + for (int i = 0; i < listeners.size(); i++) { + BuildListener listener = (BuildListener) listeners.elementAt(i); + listener.taskFinished(event); + } + } + + protected void fireMessageLogged(String message, int priority) { + BuildEvent event = createBuildEvent(message, priority); + for (int i = 0; i < listeners.size(); i++) { + BuildListener listener = (BuildListener) listeners.elementAt(i); + listener.messageLogged(event); + } + } + + public BuildEvent createBuildEvent() { + return new BuildEvent(this, currentTarget, currentTask, null, MSG_VERBOSE, null); + } + + public BuildEvent createBuildEvent(String msg, int priority) { + return new BuildEvent(this, currentTarget, currentTask, msg, priority, null); + } + + public BuildEvent createBuildEvent(Throwable exception) { + return new BuildEvent(this, currentTarget, currentTask, null, MSG_VERBOSE, exception); + } } diff --git a/src/main/org/apache/tools/ant/ProjectHelper.java b/src/main/org/apache/tools/ant/ProjectHelper.java index 6192ee897..5321199de 100644 --- a/src/main/org/apache/tools/ant/ProjectHelper.java +++ b/src/main/org/apache/tools/ant/ProjectHelper.java @@ -299,7 +299,7 @@ public class ProjectHelper { // take care of dependencies if (depends.length() > 0) { - StringTokenizer tok = + StringTokenizer tok = new StringTokenizer(depends, ",", false); while (tok.hasMoreTokens()) { target.addDependency(tok.nextToken().trim()); diff --git a/src/main/org/apache/tools/ant/Target.java b/src/main/org/apache/tools/ant/Target.java index ddc11bb37..acd9b42d5 100644 --- a/src/main/org/apache/tools/ant/Target.java +++ b/src/main/org/apache/tools/ant/Target.java @@ -119,11 +119,21 @@ public class Target { Task task = (Task) enum.nextElement(); try { + project.currentTask = task; + project.fireTaskStarted(); task.execute(); - } catch(BuildException exc) { - exc.setLocation(task.getLocation()); - throw exc; + project.fireTaskFinished(null); } + catch(RuntimeException exc) { + if (exc instanceof BuildException) { + ((BuildException)exc).setLocation(task.getLocation()); + } + project.fireTaskFinished(exc); + throw exc; + } + finally { + project.currentTask = null; + } } } else { project.log("Skipped because property '" + this.condition + "' not set.", this.name, Project.MSG_VERBOSE); diff --git a/src/main/org/apache/tools/ant/XmlLogger.java b/src/main/org/apache/tools/ant/XmlLogger.java new file mode 100644 index 000000000..72348b695 --- /dev/null +++ b/src/main/org/apache/tools/ant/XmlLogger.java @@ -0,0 +1,269 @@ +/* + * The Apache Software License, Version 1.1 + * + * Copyright (c) 1999 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ + +package org.apache.tools.ant; + +import java.io.*; +import javax.xml.parsers.*; +import org.w3c.dom.*; + +/** + * Generates a "log.xml" file in the current directory with + * an XML description of what happened during a build. + * + * @see Project#addBuildListener() + */ +public class XmlLogger implements BuildListener { + + private static final DocumentBuilder builder = getDocumentBuilder(); + + private static DocumentBuilder getDocumentBuilder() { + try { + return DocumentBuilderFactory.newInstance().newDocumentBuilder(); + } + catch(Exception exc) { + throw new ExceptionInInitializerError(exc); + } + } + + // XML constants for tag names and attribute names + private static final String BUILD_TAG = "build"; + private static final String TARGET_TAG = "target"; + private static final String TASK_TAG = "task"; + private static final String MESSAGE_TAG = "message"; + private static final String NAME_ATTR = "name"; + private static final String TIME_ATTR = "time"; + private static final String PRIORITY_ATTR = "priority"; + private static final String LOCATION_ATTR = "location"; + private static final String ERROR_ATTR = "error"; + + private Document doc; + private Element buildElement; + private Element targetElement; + private Element taskElement; + + private long buildStartTime; + private long targetStartTime; + private long taskStartTime; + + /** + * Constructs a new BuildListener that logs build events to an XML file. + */ + public XmlLogger() { + } + + public void buildStarted(BuildEvent event) { + buildStartTime = System.currentTimeMillis(); + + doc = builder.newDocument(); + buildElement = doc.createElement(BUILD_TAG); + } + + public void buildFinished(BuildEvent event) { + long totalTime = System.currentTimeMillis() - buildStartTime; + buildElement.setAttribute(TIME_ATTR, formatTime(totalTime)); + + if (event.getException() != null) { + buildElement.setAttribute(ERROR_ATTR, event.getException().toString()); + } + + try { + Writer out = new FileWriter("log.xml"); + out.write("\n\n"); + write(buildElement, out, 0); + out.flush(); + out.close(); + + } + catch(IOException exc) { + throw new BuildException("Unable to close log file", exc); + } + } + + public void targetStarted(BuildEvent event) { + targetStartTime = System.currentTimeMillis(); + targetElement = doc.createElement(TARGET_TAG); + targetElement.setAttribute(NAME_ATTR, event.getTarget().getName()); + } + + public void targetFinished(BuildEvent event) { + long totalTime = System.currentTimeMillis() - targetStartTime; + targetElement.setAttribute(TIME_ATTR, formatTime(totalTime)); + buildElement.appendChild(targetElement); + + targetElement = null; + } + + public void taskStarted(BuildEvent event) { + taskStartTime = System.currentTimeMillis(); + taskElement = doc.createElement(TASK_TAG); + + String name = event.getTask().getClass().getName(); + int pos = name.lastIndexOf("."); + if (pos != -1) { + name = name.substring(pos + 1); + } + taskElement.setAttribute(NAME_ATTR, name); + + taskElement.setAttribute(LOCATION_ATTR, event.getTask().getLocation().toString()); + } + + public void taskFinished(BuildEvent event) { + long totalTime = System.currentTimeMillis() - taskStartTime; + taskElement.setAttribute(TIME_ATTR, formatTime(totalTime)); + targetElement.appendChild(taskElement); + + taskElement = null; + } + + public void messageLogged(BuildEvent event) { + Element messageElement = doc.createElement(MESSAGE_TAG); + + String name = "debug"; + switch(event.getPriority()) { + case Project.MSG_ERR: name = "error"; break; + case Project.MSG_WARN: name = "warn"; break; + case Project.MSG_INFO: name = "info"; break; + default: name = "debug"; break; + } + messageElement.setAttribute(PRIORITY_ATTR, name); + + Text messageText = doc.createTextNode(event.getMessage()); + messageElement.appendChild(messageText); + + if (taskElement != null) { + taskElement.appendChild(messageElement); + } + else if (targetElement != null) { + targetElement.appendChild(messageElement); + } + else { + buildElement.appendChild(messageElement); + } + } + + /** + * Writes a DOM element to a file. + */ + private static void write(Element element, Writer out, int indent) throws IOException { + + // Write indent characters + for (int i = 0; i < indent; i++) { + out.write("\t"); + } + + // Write element + out.write("<"); + out.write(element.getTagName()); + + // Write attributes + NamedNodeMap attrs = element.getAttributes(); + for (int i = 0; i < attrs.getLength(); i++) { + Attr attr = (Attr) attrs.item(i); + out.write(" "); + out.write(attr.getName()); + out.write("=\""); + out.write(attr.getValue()); + out.write("\""); + } + out.write(">"); + + // Write child attributes and text + boolean hasChildren = false; + NodeList children = element.getChildNodes(); + for (int i = 0; i < children.getLength(); i++) { + Node child = children.item(i); + + if (child.getNodeType() == Node.ELEMENT_NODE) { + if (!hasChildren) { + out.write("\n"); + hasChildren = true; + } + write((Element)child, out, indent + 1); + } + + if (child.getNodeType() == Node.TEXT_NODE) { + out.write(((Text)child).getData()); + } + } + + // If we had child elements, we need to indent before we close + // the element, otherwise we're on the same line and don't need + // to indent + if (hasChildren) { + for (int i = 0; i < indent; i++) { + out.write("\t"); + } + } + + // Write element close + out.write("\n"); + } + + private static String formatTime(long millis) { + long seconds = millis / 1000; + long minutes = seconds / 60; + + + if (minutes > 0) { + return Long.toString(minutes) + " minutes " + Long.toString(seconds%60) + " seconds"; + } + else { + return Long.toString(seconds) + " seconds"; + } + + } +} \ No newline at end of file diff --git a/src/main/org/apache/tools/ant/taskdefs/Ant.java b/src/main/org/apache/tools/ant/taskdefs/Ant.java index 68a0ef893..7ee143031 100644 --- a/src/main/org/apache/tools/ant/taskdefs/Ant.java +++ b/src/main/org/apache/tools/ant/taskdefs/Ant.java @@ -86,9 +86,25 @@ public class Ant extends Task { Vector properties=new Vector(); Project p1; - + public void init() { - p1 = new Project(project.getOutput(), project.getOutputLevel()); + p1 = new Project(); + Vector listeners = project.getBuildListeners(); + for (int i = 0; i < listeners.size(); i++) { + p1.addBuildListener((BuildListener)listeners.elementAt(i)); + } + + if (output != null) { + try { + PrintStream out = new PrintStream(new FileOutputStream(output)); + p1.addBuildListener(new DefaultLogger(out, Project.MSG_INFO)); + } + catch( IOException ex ) { + project.log( "Ant: Can't set output to " + output ); + } + } + + p1.init(); // set user-define properties Hashtable prop1 = project.getProperties(); @@ -99,36 +115,27 @@ public class Ant extends Task { p1.setProperty(arg, value); } } - + /** * Do the execution. */ public void execute() throws BuildException { if( dir==null) dir="."; - - if( output != null ) { - try { - PrintStream out=new PrintStream(new FileOutputStream(output)); - p1.setOutput( out ); - } catch( IOException ex ) { - project.log( "Ant: Can't set output to " + output ); - } - } - - p1.setBasedir(dir); + + p1.setBasedir(dir); p1.setUserProperty("basedir" , dir); - // Override with local-defined properties - Enumeration e = properties.elements(); + // Override with local-defined properties + Enumeration e = properties.elements(); while (e.hasMoreElements()) { Property p=(Property) e.nextElement(); - // System.out.println("Setting " + p.getName()+ " " + p.getValue()); - p.init(); + // System.out.println("Setting " + p.getName()+ " " + p.getValue()); + p.init(); } - if (antFile == null) antFile = dir + "/build.xml"; + if (antFile == null) antFile = dir + "/build.xml"; - p1.setUserProperty( "ant.file" , antFile ); + p1.setUserProperty( "ant.file" , antFile ); ProjectHelper.configureProject(p1, new File(antFile)); if (target == null) { diff --git a/src/main/org/apache/tools/ant/taskdefs/Javac.java b/src/main/org/apache/tools/ant/taskdefs/Javac.java index 5d41801ff..1328452c7 100644 --- a/src/main/org/apache/tools/ant/taskdefs/Javac.java +++ b/src/main/org/apache/tools/ant/taskdefs/Javac.java @@ -275,7 +275,7 @@ public class Javac extends MatchingTask { + ".class"); if (srcFile.lastModified() > now) { - project.log("Warning: file modified in the future: " + + project.log("Warning: file modified in the future: " + files[i], project.MSG_WARN); } @@ -423,14 +423,19 @@ public class Javac extends MatchingTask { // XXX // provide the compiler a different message sink - namely our own - JavacOutputStream jos = new JavacOutputStream(project); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + sun.tools.javac.Main compiler = new sun.tools.javac.Main(out, "javac"); - sun.tools.javac.Main compiler = - new sun.tools.javac.Main(jos, "javac"); - compiler.compile(args); - if (jos.getErrorFlag()) { - String msg = "Compile failed, messages should have been provided."; - throw new BuildException(msg); + if (compiler.compile(args)) { + String output = out.toString().trim(); + if (output.length() > 0) { + project.log(output, Project.MSG_WARN); + } + } + else { + project.log(out.toString().trim(), Project.MSG_ERR); + + throw new BuildException("Compile failed"); } }