The main change is the new BigProjectLogger that makes reading the results of very big chained/nested projects manageable. Some pulling up of helper methods into DefaultLogger, and a bit of cleanup there; plus the appopriate documentation changes git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@539477 13f79535-47bb-0310-9956-ffa450edef68master
@@ -148,6 +148,8 @@ Other changes: | |||
<comment> nested element as nested text instead of using the 'value' | |||
attribute. | |||
* A new logger, BigProjectLogger, lists the project name with every target | |||
Changes from Ant 1.6.5 to Ant 1.7.0 | |||
=================================== | |||
@@ -44,13 +44,19 @@ listeners and loggers.</p> | |||
<li>message logged</li> | |||
</ul> | |||
<p> | |||
These are used internally for various recording and housekeeping operations, | |||
however new listeners may registered on the command line through the <code>-listener</code> | |||
argument. | |||
</p> | |||
<h3><a name="Loggers">Loggers</a></h3> | |||
<p>Loggers extend the capabilities of listeners and add the following features:</p> | |||
<ul> | |||
<li>Receives a handle to the standard output and error print streams and | |||
therefore can log information to the console or the -logfile specified file.</li> | |||
therefore can log information to the console or the <code>-logfile</code> specified file.</li> | |||
<li>Logging level (-quiet, -verbose, -debug) aware</li> | |||
<li>Emacs-mode aware</li> | |||
</ul> | |||
@@ -104,7 +110,11 @@ listeners and loggers.</p> | |||
<td width="33%">Prints the time that a build finished</td> | |||
<td width="34%">BuildLogger</td> | |||
</tr> | |||
<tr> | |||
<td width="33%"><code><a href="#BigProjectLogger">org.apache.tools.ant.BigProjectLogger</a></code></td> | |||
<td width="33%">Prints the project name every target</td> | |||
<td width="34%">BuildLogger</td> | |||
</tr> | |||
</table> | |||
<h3><a name="DefaultLogger">DefaultLogger</a></h3> | |||
@@ -235,7 +245,7 @@ in the console using applications like cat, more, etc.</p> | |||
color codes. It works on XTerm, ETerm, Win9x Console | |||
(with ANSI.SYS loaded.), etc.</p> | |||
<p><Strong>NOTE:</Strong> | |||
It doesn't work on WinNT even when a COMMAND.COM console loaded with | |||
It doesn't work on WinNT and successors, even when a COMMAND.COM console loaded with | |||
ANSI.SYS is used.</p> | |||
<p>If the user wishes to override the default colors | |||
with custom ones, a file containing zero or more of the | |||
@@ -318,7 +328,7 @@ corresponding Log4j level.</p> | |||
</blockquote> | |||
<p>To use Log4j you will need the Log4j jar file and a 'log4j.properties' | |||
<p>To use Log4j you will need the Log4j JAR file and a 'log4j.properties' | |||
configuration file. Both should be placed somewhere in your Ant | |||
classpath. If the log4j.properties is in your project root folder you can | |||
add this with <i>-lib</i> option:</p> | |||
@@ -386,6 +396,7 @@ is declared at all. | |||
<pre> | |||
BUILD SUCCESSFUL - at 16/08/05 16:24 | |||
</pre> | |||
<p>To use this listener, use the command:</p> | |||
<blockquote> | |||
@@ -393,6 +404,63 @@ is declared at all. | |||
</blockquote> | |||
<h3><a name="BigProjectLogger">BigProjectLogger</a></h3> | |||
<p> | |||
This logger is designed to make examining the logs of a big build easier, | |||
especially those run under continuous integration tools. It | |||
</p> | |||
<ol> | |||
<li>When entering a child project, prints its name and directory</li> | |||
<li>When exiting a child project, prints its name</li> | |||
<li>Includes the name of the project when printing a target</li> | |||
<li>Omits logging the names of all targets that have no direct task output</li> | |||
<li>Includes the build finished timestamp of the TimeStamp logger</li> | |||
</ol> | |||
<p> | |||
This is useful when using <subant> to build a large project | |||
from many smaller projects -the output shows which particular | |||
project is building. Here is an example in which "clean" is being called | |||
on all a number of child projects, only some of which perform work: | |||
</p> | |||
<pre> | |||
====================================================================== | |||
Entering project "xunit" | |||
In /home/ant/components/xunit | |||
====================================================================== | |||
xunit.clean: | |||
[delete] Deleting directory /home/ant/components/xunit/build | |||
[delete] Deleting directory /home/ant/components/xunit/dist | |||
====================================================================== | |||
Exiting project "xunit" | |||
====================================================================== | |||
====================================================================== | |||
Entering project "junit" | |||
In /home/ant/components/junit | |||
====================================================================== | |||
====================================================================== | |||
Exiting project "junit" | |||
====================================================================== | |||
</pre> | |||
<p> | |||
The entry and exit messages are very verbose in this example, but in | |||
a big project compiling or testing many child components, the messages | |||
are reduced to becoming clear delimiters of where different projects | |||
are in charge -or more importantly, which project is failing. | |||
</p> | |||
<p>To use this listener, use the command:</p> | |||
<blockquote> | |||
<code>ant -logger org.apache.tools.ant.listener.BigProjectLogger</code> | |||
</blockquote> | |||
<h2><a name="dev">Writing your own</a></h2> | |||
@@ -402,8 +470,25 @@ developers.</p> | |||
<p>Notes:</p> | |||
<ul> | |||
<li>A listener or logger should not write to standard output or error; Ant | |||
captures these internally and may cause an infinite loop.</li> | |||
<li> | |||
A listener or logger should not write to standard output or error in the <code>messageLogged() method</code>; | |||
Ant captures these internally and it will trigger an infinite loop. | |||
</li> | |||
<li> | |||
Logging is synchronous; all listeners and loggers are called one after the other, with the build blocking until | |||
the output is processed. Slow logging means a slow build. | |||
</li> | |||
<li>When a build is started, and <code>BuildListener.buildStarted(BuildEvent event)</code> is called, | |||
the project is not fully functional. The build has started, yes, and the event.getProject() method call | |||
returns the Project instance, but that project is initialized with JVM and ant properties, nor has it | |||
parsed the build file yet. You cannot call <code>Project.getProperty()</code> for property lookup, or | |||
<code>Project.getName()</code> to get the project name (it will return null). | |||
</li> | |||
<li> | |||
Classes that implement <code>org.apache.tools.ant.SubBuildListener</code> receive notifications when child projects | |||
start and stop. | |||
</li> | |||
</ul> | |||
@@ -22,9 +22,12 @@ import java.io.BufferedReader; | |||
import java.io.IOException; | |||
import java.io.PrintStream; | |||
import java.io.StringReader; | |||
import java.util.Date; | |||
import java.text.DateFormat; | |||
import org.apache.tools.ant.util.DateUtils; | |||
import org.apache.tools.ant.util.StringUtils; | |||
import org.apache.tools.ant.util.FileUtils; | |||
/** | |||
* Writes build events to a PrintStream. Currently, it | |||
@@ -251,9 +254,9 @@ public class DefaultLogger implements BuildLogger { | |||
tmp.append(label); | |||
label = tmp.toString(); | |||
BufferedReader r = null; | |||
try { | |||
BufferedReader r = | |||
new BufferedReader( | |||
r = new BufferedReader( | |||
new StringReader(event.getMessage())); | |||
String line = r.readLine(); | |||
boolean first = true; | |||
@@ -273,8 +276,14 @@ public class DefaultLogger implements BuildLogger { | |||
} catch (IOException e) { | |||
// shouldn't be possible | |||
message.append(label).append(event.getMessage()); | |||
} finally { | |||
if (r != null) { | |||
FileUtils.close(r); | |||
} | |||
} | |||
} else { | |||
//emacs mode or there is no task | |||
message.append(event.getMessage()); | |||
} | |||
Throwable ex = event.getException(); | |||
@@ -329,4 +338,27 @@ public class DefaultLogger implements BuildLogger { | |||
*/ | |||
protected void log(String message) { | |||
} | |||
/** | |||
* Get the current time. | |||
* @return the current time as a formatted string. | |||
* @since Ant1.7.1 | |||
*/ | |||
protected String getTimestamp() { | |||
Date date = new Date(System.currentTimeMillis()); | |||
DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT); | |||
String finishTime = formatter.format(date); | |||
return finishTime; | |||
} | |||
/** | |||
* Get the project name or null | |||
* @param event the event | |||
* @return the project that raised this event | |||
* @since Ant1.7.1 | |||
*/ | |||
protected String extractProjectName(BuildEvent event) { | |||
Project project = event.getProject(); | |||
return project!=null?project.getName():null; | |||
} | |||
} |
@@ -49,7 +49,17 @@ public class NoBannerLogger extends DefaultLogger { | |||
* Must not be <code>null</code>. | |||
*/ | |||
public void targetStarted(BuildEvent event) { | |||
targetName = event.getTarget().getName(); | |||
targetName = extractTargetName(event); | |||
} | |||
/** | |||
* Override point, extract the target name | |||
* @param event the event to work on | |||
* @return the target name to print | |||
* @since Ant1.7.1 | |||
*/ | |||
protected String extractTargetName(BuildEvent event) { | |||
return event.getTarget().getName(); | |||
} | |||
/** | |||
@@ -96,7 +96,7 @@ import org.apache.tools.ant.Project; | |||
* 47 -> White | |||
* | |||
*/ | |||
public final class AnsiColorLogger extends DefaultLogger { | |||
public class AnsiColorLogger extends DefaultLogger { | |||
// private static final int ATTR_NORMAL = 0; | |||
// private static final int ATTR_BRIGHT = 1; | |||
private static final int ATTR_DIM = 2; | |||
@@ -0,0 +1,171 @@ | |||
/* | |||
* Copyright 2007 The Apache Software Foundation | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||
* you may not use this file except in compliance with the License. | |||
* You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
* | |||
*/ | |||
package org.apache.tools.ant.listener; | |||
import org.apache.tools.ant.BuildEvent; | |||
import org.apache.tools.ant.NoBannerLogger; | |||
import org.apache.tools.ant.SubBuildListener; | |||
import org.apache.tools.ant.Project; | |||
import org.apache.tools.ant.util.StringUtils; | |||
import java.io.File; | |||
/** | |||
* This is a special logger that is designed to make it easier to work with big projects, those that use imports and | |||
* subant to build complex systems. | |||
* | |||
* @since Ant1.7.1 | |||
*/ | |||
public class BigProjectLogger extends NoBannerLogger implements SubBuildListener { | |||
/** | |||
* Header string for the log. | |||
* {@value} | |||
*/ | |||
public static final String HEADER="======================================================================"; | |||
/** | |||
* Footer string for the log. | |||
* {@value} | |||
*/ | |||
public static final String FOOTER=HEADER; | |||
/** | |||
* This is an override point: the message that indicates whether a build failed. Subclasses can change/enhance the | |||
* message. | |||
* | |||
* @return The classic "BUILD FAILED" plus a timestamp | |||
*/ | |||
protected String getBuildFailedMessage() { | |||
return super.getBuildFailedMessage() + TimestampedLogger.SPACER + getTimestamp(); | |||
} | |||
/** | |||
* This is an override point: the message that indicates that a build succeeded. Subclasses can change/enhance the | |||
* message. | |||
* | |||
* @return The classic "BUILD SUCCESSFUL" plus a timestamp | |||
*/ | |||
protected String getBuildSuccessfulMessage() { | |||
return super.getBuildSuccessfulMessage() + TimestampedLogger.SPACER + getTimestamp(); | |||
} | |||
/** | |||
* {@inheritDoc} | |||
* | |||
* @param event | |||
*/ | |||
public void buildStarted(BuildEvent event) { | |||
super.buildStarted(event); | |||
subBuildStarted(event); | |||
} | |||
/** | |||
* {@inheritDoc} | |||
* | |||
* @param event | |||
*/ | |||
public void buildFinished(BuildEvent event) { | |||
subBuildFinished(event); | |||
super.buildFinished(event); | |||
} | |||
/** | |||
* Override point, extract the target name | |||
* | |||
* @param event the event to work on | |||
* @return the target name -including the owning project name (if non-null) | |||
*/ | |||
protected String extractTargetName(BuildEvent event) { | |||
String targetName = event.getTarget().getName(); | |||
String projectName = extractProjectName(event); | |||
if (projectName != null && targetName != null) { | |||
return projectName + '.' + targetName; | |||
} else { | |||
return targetName; | |||
} | |||
} | |||
/** | |||
* {@inheritDoc} | |||
* | |||
* @param event An event with any relevant extra information. Must not be <code>null</code>. | |||
*/ | |||
public void subBuildStarted(BuildEvent event) { | |||
String name = extractNameOrDefault(event); | |||
Project project = event.getProject(); | |||
File base = project == null ? null : project.getBaseDir(); | |||
String path = base == null ? | |||
"With no base directory" | |||
: "In " + base.getAbsolutePath(); | |||
printMessage(StringUtils.LINE_SEP + getHeader() | |||
+ StringUtils.LINE_SEP +"Entering project " + name | |||
+ StringUtils.LINE_SEP + path | |||
+StringUtils.LINE_SEP + getFooter(), | |||
out, | |||
event.getPriority()); | |||
} | |||
/** | |||
* Get the name of an event | |||
* | |||
* @param event the event name | |||
* @return the name or a default string | |||
*/ | |||
protected String extractNameOrDefault(BuildEvent event) { | |||
String name = extractProjectName(event); | |||
if (name == null) { | |||
name = ""; | |||
} else { | |||
name = '"'+name+'"'; | |||
} | |||
return name; | |||
} | |||
/** {@inheritDoc} */ | |||
public void subBuildFinished(BuildEvent event) { | |||
String name = extractNameOrDefault(event); | |||
String failed = event.getException() != null ? "failing " : ""; | |||
printMessage(StringUtils.LINE_SEP + getHeader() | |||
+ StringUtils.LINE_SEP + "Exiting " + failed + "project " | |||
+ name | |||
+ StringUtils.LINE_SEP + getFooter(), | |||
out, | |||
event.getPriority()); | |||
} | |||
/** | |||
* Override point: return the header string for the entry/exit message | |||
* @return the header string | |||
*/ | |||
protected String getHeader() { | |||
return HEADER; | |||
} | |||
/** | |||
* Override point: return the footer string for the entry/exit message | |||
* @return the footer string | |||
*/ | |||
protected String getFooter() { | |||
return FOOTER; | |||
} | |||
} |
@@ -31,14 +31,14 @@ public class TimestampedLogger extends DefaultLogger { | |||
/** | |||
* what appears between the old message and the new | |||
*/ | |||
private static final String SPACER = " - at "; | |||
public static final String SPACER = " - at "; | |||
/** | |||
* This is an override point: the message that indicates whether a build failed. | |||
* Subclasses can change/enhance the message. | |||
* | |||
* @return The classic "BUILD FAILED" | |||
* @return The classic "BUILD FAILED" plus a timestamp | |||
*/ | |||
protected String getBuildFailedMessage() { | |||
return super.getBuildFailedMessage() + SPACER + getTimestamp(); | |||
@@ -48,20 +48,10 @@ public class TimestampedLogger extends DefaultLogger { | |||
* This is an override point: the message that indicates that a build succeeded. | |||
* Subclasses can change/enhance the message. | |||
* | |||
* @return The classic "BUILD SUCCESSFUL" | |||
* @return The classic "BUILD SUCCESSFUL" plus a timestamp | |||
*/ | |||
protected String getBuildSuccessfulMessage() { | |||
return super.getBuildSuccessfulMessage() + SPACER + getTimestamp(); | |||
} | |||
/** | |||
* Get the current time. | |||
* @return the current time as a formatted string. | |||
*/ | |||
protected String getTimestamp() { | |||
Date date = new Date(System.currentTimeMillis()); | |||
DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT); | |||
String finishTime = formatter.format(date); | |||
return finishTime; | |||
} | |||
} |