Submitted by: Jay Glanville <dickon@nortelnetworks.com> git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@268914 13f79535-47bb-0310-9956-ffa450edef68master
@@ -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 | |||
@@ -0,0 +1,158 @@ | |||
<html> | |||
<head> | |||
<meta http-equiv="Content-Language" content="en-us"> | |||
<title>Ant User Manual</title> | |||
</head> | |||
<body> | |||
<h2><a name="log">Recorder</a></h2> | |||
<h3>Description</h3> | |||
<p>A recorder is a listener to the current build process that records the | |||
output to a file. | |||
<p>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). | |||
<p>Some technical issues: the file's print stream is flushed for "finished" | |||
events (buildFinished, targetFinished and taskFinished), and is closed on | |||
a buildFinished event. | |||
<h3>Parameters</h3> | |||
<table border="1" cellpadding="2" cellspacing="0"> | |||
<tr> | |||
<td valign="top"><b>Attribute</b></td> | |||
<td valign="top"><b>Description</b></td> | |||
<td align="center" valign="top"><b>Required</b></td> | |||
</tr> | |||
<tr> | |||
<td valign="top">name</td> | |||
<td valign="top">The name of the file this logger is associated with.</td> | |||
<td align="center" valign="middle">yes</td> | |||
</tr> | |||
<tr> | |||
<td valign="top">action</td> | |||
<td valign="top">This 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]</td> | |||
<td align="center" valign="middle">no</td> | |||
</tr> | |||
<tr> | |||
<td valign="top">append</td> | |||
<td valign="top">Should 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]</td> | |||
<td align="center" valign="middle">no</td> | |||
</tr> | |||
<tr> | |||
<td valign="top">loglevel</td> | |||
<td valign="top">At what logging level should this recorder instance | |||
record to? This is not a once only parameter (like <code>append</code> | |||
is) -- you can increase or decrease the logging level as the build process | |||
continues. [Vaules= {error|warn|info|verbose|debug}, Default = no change] | |||
</td> | |||
<td align="center" valign="middle">no</td> | |||
</tr> | |||
</table> | |||
<h3>Examples</h3> | |||
<p>The following build.xml snippit is an example of how to use the recorder | |||
to record just the <code><javac></code> task: | |||
<pre> | |||
... | |||
<compile > | |||
<recorder name="log.txt" action="start" /> | |||
<javac ... | |||
<recorder name="log.txt" action="stop" /> | |||
<compile/> | |||
... | |||
</pre> | |||
<p>The following two calls to <code><recorder></code> set up two | |||
recorders: one to file "records-simple.log" at logging level <code>info</code> | |||
(the default) and one to file "ISO.log" using logging level of | |||
<code>verbose</code>. | |||
<pre> | |||
... | |||
<recorder name="records-simple.log" /> | |||
<recorder name="ISO.log" loglevel="verbose" /> | |||
... | |||
</pre> | |||
<h3>Notes</h3> | |||
<p>There is some funtionality that I would like to be able to add in the | |||
future. They include things like the following: | |||
<table border="1" cellpadding="2" cellspacing="0"> | |||
<tr> | |||
<td valign="top"><b>Attribute</b></td> | |||
<td valign="top"><b>Description</b></td> | |||
<td align="center" valign="top"><b>Required</b></td> | |||
</tr> | |||
<tr> | |||
<td valign="top">messageprefix</td> | |||
<td valign="top">Whether or not to include the message prefixes (things | |||
like the name of the tasks or targets, etc). This has the same effect as | |||
the <code>-emacs</code> command line parameter does to the screen output. | |||
[yes|no]</td> | |||
<td align="center" valign="middle">no</td> | |||
</tr> | |||
<tr> | |||
<td valign="top">listener</td> | |||
<td valign="top">A classname of a build listener to use from this point | |||
on instead of the default listener.</td> | |||
<td align="center" valign="middle">no</td> | |||
</tr> | |||
<tr> | |||
<td valign="top">includetarget</td> | |||
<td valign="top" rowspan=2>A comma-separated list of targets to automaticly | |||
record. If this value is "all", then all targets are recorded. | |||
[Default = all]</td> | |||
<td align="center" valign="middle">no</td> | |||
</tr> | |||
<tr> | |||
<td valign="top">excludetarget</td> | |||
<td align="center" valign="middle">no</td> | |||
</tr> | |||
<tr> | |||
<td valign="top">includetask</td> | |||
<td valign="top" rowspan=2>A comma-separated list of task to automaticly | |||
record or not. This could be difficult as it could conflict with the | |||
<code>includetarget/excludetarget</code>. (e.g.: | |||
<code>includetarget="compile" exlcudetask="javac"</code>, what should | |||
happen?)</td> | |||
<td align="center" valign="middle">no</td> | |||
</tr> | |||
<tr> | |||
<td valign="top">excludetask</td> | |||
<td align="center" valign="middle">no</td> | |||
</tr> | |||
<tr> | |||
<td valign="top">action</td> | |||
<td valign="top">add greater flexability to the action attribute. Things | |||
like <code>close</code> to close the print stream.</td> | |||
<td align="center" valign="top">no</td> | |||
</tr> | |||
<tr> | |||
<td valign="top"></td> | |||
<td valign="top"></td> | |||
<td align="center" valign="top"></td> | |||
</tr> | |||
</table> | |||
<hr><p align="center">Copyright © 2000,2001 Apache Software Foundation. All rights | |||
Reserved.</p> | |||
</body> | |||
</html> | |||
@@ -49,6 +49,7 @@ | |||
<a href="CoreTasks/move.html">Move</a><br> | |||
<a href="CoreTasks/patch.html">Patch</a><br> | |||
<a href="CoreTasks/property.html">Property</a><br> | |||
<a href="CoreTasks/recorder.html">Recorder</a><br> | |||
<a href="CoreTasks/rename.html"><i>Rename</i></a><br> | |||
<a href="CoreTasks/replace.html">Replace</a><br> | |||
<a href="CoreTasks/rmic.html">Rmic</a><br> | |||
@@ -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 | |||
* <http://www.apache.org/>. | |||
*/ | |||
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 <recorder> | |||
* task is called, and addes them to the build listener process. | |||
* @see RecorderEntry | |||
* @author <a href="mailto:jayglanville@home.com">J D Glanville</a> | |||
* @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 <code>setAction()</code> 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 <code>setLoglevel()</code> 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; | |||
} | |||
} |
@@ -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 | |||
* <http://www.apache.org/>. | |||
*/ | |||
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 <a href="mailto:jayglanville@home.com">J D Glanville</a> | |||
* @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"); | |||
} | |||
} | |||
} |
@@ -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 | |||