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; | |||
| } | |||
| } | |||