diff --git a/WHATSNEW b/WHATSNEW index a26e0d129..018768dba 100644 --- a/WHATSNEW +++ b/WHATSNEW @@ -13,7 +13,7 @@ Changes that could break older environments: Other changes: -------------- -* New tasks: ear, p4counter +* New tasks: ear, p4counter, recorder * Ant now uses JAXP 1.1 diff --git a/docs/manual/CoreTasks/recorder.html b/docs/manual/CoreTasks/recorder.html new file mode 100644 index 000000000..32b5e3e35 --- /dev/null +++ b/docs/manual/CoreTasks/recorder.html @@ -0,0 +1,158 @@ + + + + +Ant User Manual + + + + +

Recorder

+

Description

+

A recorder is a listener to the current build process that records the +output to a file. + +

Several recorders can exist at the same time. Each recorder is +associated with a file. The filename is used as a unique identifier for +the recorders. The first call to the recorder task with an unused filename +will create a recorder (using the parameters provided) and add it to the +listeners of the build. All subsiquent calls to the recorder task using +this filename will modify that recorders state (recording or not) or other +properties (like logging level). + +

Some technical issues: the file's print stream is flushed for "finished" +events (buildFinished, targetFinished and taskFinished), and is closed on +a buildFinished event. + +

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
AttributeDescriptionRequired
nameThe name of the file this logger is associated with.yes
actionThis tells the logger what to do: should it start + recording or stop? The first time that the recorder task is called for + this logfile, and if this attribute is not provided, then the default + for this attribute is "start". If this attribute is not provided on + subsiquest calls, then the state remains as previous. + [Values = {start|stop}, Default = no state change]no
appendShould the recorder append to a file, or create a new + one? This is only applicable the first time this task is called for + this file. [Vaules = {yes|no}, Default=yes]no
loglevelAt what logging level should this recorder instance + record to? This is not a once only parameter (like append + is) -- you can increase or decrease the logging level as the build process + continues. [Vaules= {error|warn|info|verbose|debug}, Default = no change] + no
+ +

Examples

+

The following build.xml snippit is an example of how to use the recorder +to record just the <javac> task: +

+    ...
+    <compile >
+        <recorder name="log.txt" action="start" />
+        <javac ...
+        <recorder name="log.txt" action="stop" />
+    <compile/>
+    ...
+
+ +

The following two calls to <recorder> set up two +recorders: one to file "records-simple.log" at logging level info +(the default) and one to file "ISO.log" using logging level of +verbose. +

+    ...
+    <recorder name="records-simple.log" />
+    <recorder name="ISO.log" loglevel="verbose" />
+    ...
+
+ +

Notes

+

There is some funtionality that I would like to be able to add in the +future. They include things like the following: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
AttributeDescriptionRequired
messageprefixWhether or not to include the message prefixes (things + like the name of the tasks or targets, etc). This has the same effect as + the -emacs command line parameter does to the screen output. + [yes|no]no
listenerA classname of a build listener to use from this point + on instead of the default listener.no
includetargetA comma-separated list of targets to automaticly + record. If this value is "all", then all targets are recorded. + [Default = all]no
excludetargetno
includetaskA comma-separated list of task to automaticly + record or not. This could be difficult as it could conflict with the + includetarget/excludetarget. (e.g.: + includetarget="compile" exlcudetask="javac", what should + happen?)no
excludetaskno
actionadd greater flexability to the action attribute. Things + like close to close the print stream.no
+ + + +


Copyright © 2000,2001 Apache Software Foundation. All rights +Reserved.

+ + + + diff --git a/docs/manual/coretasklist.html b/docs/manual/coretasklist.html index 93b002468..6a6031906 100644 --- a/docs/manual/coretasklist.html +++ b/docs/manual/coretasklist.html @@ -49,6 +49,7 @@ Move
Patch
Property
+Recorder
Rename
Replace
Rmic
diff --git a/src/main/org/apache/tools/ant/taskdefs/Recorder.java b/src/main/org/apache/tools/ant/taskdefs/Recorder.java new file mode 100644 index 000000000..314033817 --- /dev/null +++ b/src/main/org/apache/tools/ant/taskdefs/Recorder.java @@ -0,0 +1,230 @@ +/* + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2001 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", "Ant", 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.taskdefs; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.DirectoryScanner; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.types.*; +import org.apache.tools.ant.util.*; +import org.apache.tools.ant.taskdefs.compilers.*; +import org.apache.tools.ant.Task; + +import java.io.*; + +import java.util.*; + +/** + * This task is the manager for RecorderEntry's. It is this class + * that holds all entries, modifies them every time the + * task is called, and addes them to the build listener process. + * @see RecorderEntry + * @author J D Glanville + * @version 0.5 + * + */ +public class Recorder extends Task { + + ////////////////////////////////////////////////////////////////////// + // ATTRIBUTES + + /** The name of the file to record to. */ + private String filename = null; + /** Whether or not to append. Need Boolean to record an unset + * state (null). + */ + private Boolean append = null; + /** Whether to start or stop recording. Need Boolean to record an + * unset state (null). + */ + private Boolean start = null; + /** What level to log? -1 means not initialized yet. */ + private int loglevel = -1; + /** The list of recorder entries. */ + private static HashMap recorderEntries = new HashMap(); + + ////////////////////////////////////////////////////////////////////// + // CONSTRUCTORS / INITIALIZERS + + ////////////////////////////////////////////////////////////////////// + // ACCESSOR METHODS + + /** + * Sets the name of the file to log to, and the name of the recorder entry. + * @param fname File name of logfile. + */ + public void setName( String fname ) { + filename = fname; + } + + /** + * Sets the action for the associated recorder entry. + * @param action The action for the entry to take: start or stop. + */ + public void setAction( ActionChoices action ) { + if ( action.getValue().equalsIgnoreCase( "start" ) ) { + start = Boolean.TRUE; + } else { + start = Boolean.FALSE; + } + } + + /** + * Whether or not the logger should append to a previous file. + */ + public void setAppend( boolean append ) { + this.append = new Boolean(append); + } + + /** + * Sets the level to which this recorder entry should log to. + * @see VerbosityLevelChoices + */ + public void setLoglevel( VerbosityLevelChoices level ){ + //I hate cascading if/elseif clauses !!! + String lev = level.getValue(); + if ( lev.equalsIgnoreCase("error") ) { + loglevel = Project.MSG_ERR; + } else if ( lev.equalsIgnoreCase("warn") ){ + loglevel = Project.MSG_WARN; + } else if ( lev.equalsIgnoreCase("info") ){ + loglevel = Project.MSG_INFO; + } else if ( lev.equalsIgnoreCase("verbose") ){ + loglevel = Project.MSG_VERBOSE; + } else if ( lev.equalsIgnoreCase("debug") ){ + loglevel = Project.MSG_DEBUG; + } + } + + ////////////////////////////////////////////////////////////////////// + // CORE / MAIN BODY + + /** + * The main execution. + */ + public void execute() throws BuildException { + if ( filename == null ) + throw new BuildException( "No filename specified" ); + + getProject().log( "setting a recorder for name " + filename, + Project.MSG_DEBUG ); + + // get the recorder entry + RecorderEntry recorder = getRecorder( filename, getProject() ); + // set the values on the recorder + recorder.setMessageOutputLevel( loglevel ); + recorder.setRecordState( start ); + } + + ////////////////////////////////////////////////////////////////////// + // INNER CLASSES + + /** + * A list of possible values for the setAction() method. + * Possible values include: start and stop. + */ + public static class ActionChoices extends EnumeratedAttribute { + private static final String[] values = {"start", "stop"}; + public String[] getValues() { + return values; + } + } + + /** + * A list of possible values for the setLoglevel() method. + * Possible values include: error, warn, info, verbose, debug. + */ + public static class VerbosityLevelChoices extends EnumeratedAttribute { + private static final String[] values = { "error", "warn", "info", + "verbose", "debug"}; + public String[] getValues() { + return values; + } + } + + /** + * Gets the recorder that's associated with the passed in name. + * If the recorder doesn't exist, then a new one is created. + */ + protected RecorderEntry getRecorder( String name, Project proj ) throws BuildException { + Object o = recorderEntries.get(name); + RecorderEntry entry; + if ( o == null ) { + // create a recorder entry + try { + entry = new RecorderEntry( name ); + PrintStream out = null; + if ( append == null ) { + out = new PrintStream( + new FileOutputStream(name)); + } else { + out = new PrintStream( + new FileOutputStream(name, append.booleanValue())); + } + entry.setErrorPrintStream(out); + entry.setOutputPrintStream(out); + } catch ( IOException ioe ) { + throw new BuildException( "Problems creating a recorder entry", + ioe ); + } + proj.addBuildListener(entry); + recorderEntries.put(name, entry); + } else { + entry = (RecorderEntry) o; + } + return entry; + } + +} diff --git a/src/main/org/apache/tools/ant/taskdefs/RecorderEntry.java b/src/main/org/apache/tools/ant/taskdefs/RecorderEntry.java new file mode 100644 index 000000000..6ca3acab8 --- /dev/null +++ b/src/main/org/apache/tools/ant/taskdefs/RecorderEntry.java @@ -0,0 +1,230 @@ +/* + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2001 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", "Ant", 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.taskdefs; + +import org.apache.tools.ant.*; +import java.io.*; +import java.util.*; + +/** + * This is a class that represents a recorder. This is the listener + * to the build process. + * @author J D Glanville + * @version 0.5 + * + */ +public class RecorderEntry implements BuildLogger { + + ////////////////////////////////////////////////////////////////////// + // ATTRIBUTES + + /** + * The name of the file associated with this recorder entry. + */ + private String filename = null; + /** + * The state of the recorder (recorder on or off). + */ + private boolean record = true; + /** + * The current verbosity level to record at. + */ + private int loglevel = Project.MSG_INFO; + /** + * The output PrintStream to record to. + */ + private PrintStream out = null; + /** + * The start time of the last know target. + */ + private long targetStartTime = 0l; + /** + * Line separator. + */ + private static String lSep = System.getProperty("line.separator"); + + ////////////////////////////////////////////////////////////////////// + // CONSTRUCTORS / INITIALIZERS + + /** + * @param name The name of this recorder (used as the filename). + * + */ + protected RecorderEntry( String name ) { + filename = name; + } + + ////////////////////////////////////////////////////////////////////// + // ACCESSOR METHODS + + /** + * @return the name of the file the output is sent to. + */ + public String getFilename() { + return filename; + } + + /** + * Turns off or on this recorder. + * @param state true for on, false for off, null for no change. + */ + public void setRecordState( Boolean state ) { + if ( state != null ) + record = state.booleanValue(); + } + + public void buildStarted(BuildEvent event) { + log( "> BUILD STARTED", Project.MSG_DEBUG ); + } + + public void buildFinished(BuildEvent event) { + log( "< BUILD FINISHED", Project.MSG_DEBUG ); + + Throwable error = event.getException(); + if (error == null) { + out.println(lSep + "BUILD SUCCESSFUL"); + } else { + out.println(lSep + "BUILD FAILED" + lSep); + error.printStackTrace(out); + } + out.flush(); + out.close(); + } + + public void targetStarted(BuildEvent event) { + log( ">> TARGET STARTED -- " + event.getTarget(), Project.MSG_DEBUG ); + log( lSep + event.getTarget().getName() + ":", Project.MSG_INFO ); + targetStartTime = System.currentTimeMillis(); + } + + public void targetFinished(BuildEvent event) { + log( "<< TARGET FINISHED -- " + event.getTarget(), Project.MSG_DEBUG ); + String time = formatTime( System.currentTimeMillis() - targetStartTime ); + log( event.getTarget() + ": duration " + time, Project.MSG_VERBOSE ); + out.flush(); + } + + public void taskStarted(BuildEvent event) { + log( ">>> TAST STARTED -- " + event.getTask(), Project.MSG_DEBUG ); + } + + public void taskFinished(BuildEvent event) { + log( "<<< TASK FINISHED -- " + event.getTask(), Project.MSG_DEBUG ); + out.flush(); + } + + public void messageLogged(BuildEvent event) { + log( "--- MESSAGE LOGGED", Project.MSG_DEBUG ); + + StringBuffer buf = new StringBuffer(); + if ( event.getTask() != null ) { + String name = "[" + event.getTask().getTaskName() + "]"; + /** @todo replace 12 with DefaultLogger.LEFT_COLUMN_SIZE */ + for ( int i = 0; i < (12 - name.length()); i++ ) { + buf.append( " " ); + } // for + buf.append( name ); + } // if + buf.append( event.getMessage() ); + + log( buf.toString(), event.getPriority() ); + } + + /** + * The thing that actually sends the information to the output. + * @param mesg The message to log. + * @param level The verbosity level of the message. + */ + private void log( String mesg, int level ) { + if ( record && (level <= loglevel) ) { + out.println(mesg); + } + } + + public void setMessageOutputLevel(int level) { + if ( level >= Project.MSG_ERR && level <= Project.MSG_DEBUG ) + loglevel = level; + } + + public void setOutputPrintStream(PrintStream output) { + out = output; + } + + public void setEmacsMode(boolean emacsMode) { + throw new java.lang.UnsupportedOperationException("Method setEmacsMode() not yet implemented."); + } + + public void setErrorPrintStream(PrintStream err) { + out = err; + } + + private static String formatTime(long millis) { + long seconds = millis / 1000; + long minutes = seconds / 60; + + + if (minutes > 0) { + return Long.toString(minutes) + " minute" + + (minutes == 1 ? " " : "s ") + + Long.toString(seconds%60) + " second" + + (seconds%60 == 1 ? "" : "s"); + } + else { + return Long.toString(seconds) + " second" + + (seconds%60 == 1 ? "" : "s"); + } + + } +} diff --git a/src/main/org/apache/tools/ant/taskdefs/defaults.properties b/src/main/org/apache/tools/ant/taskdefs/defaults.properties index 66ea759aa..db7569b10 100644 --- a/src/main/org/apache/tools/ant/taskdefs/defaults.properties +++ b/src/main/org/apache/tools/ant/taskdefs/defaults.properties @@ -43,6 +43,7 @@ fail=org.apache.tools.ant.taskdefs.Exit war=org.apache.tools.ant.taskdefs.War uptodate=org.apache.tools.ant.taskdefs.UpToDate apply=org.apache.tools.ant.taskdefs.Transform +recorder=org.apache.tools.ant.taskdefs.Recorder # optional tasks script=org.apache.tools.ant.taskdefs.optional.Script