blob: 6df22a634ba38aacec80398f06ff74a336ce7a95 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.easyant.core.ant.listerners;
import org.apache.easyant.core.EasyAntMagicNames;
import org.apache.easyant.core.ant.ExecutionStatus;
import org.apache.tools.ant.BuildEvent;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.SubBuildListener;
import org.apache.tools.ant.listener.TimestampedLogger;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import static org.apache.tools.ant.util.StringUtils.LINE_SEP;
public class MultiModuleLogger extends DefaultEasyAntLogger implements SubBuildListener {
/**
* Reference key against which build execution results will be stored
*/
public static final String EXECUTION_TIMER_BUILD_RESULTS = "execution.timer.build.results";
private static final String DEMARKER = "======================================================================";
private volatile boolean subBuildStartedRaised = false;
private final Object subBuildLock = new Object();
private long buildStartTime;
/**
* 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();
}
public void targetStarted(BuildEvent event) {
maybeRaiseSubBuildStarted(event);
targetName = extractTargetName(event);
}
public void taskStarted(BuildEvent event) {
maybeRaiseSubBuildStarted(event);
super.taskStarted(event);
}
public void buildFinished(BuildEvent event) {
stopTimer(event);
printExecutionSubBuildsExecutionTimes(event.getProject());
maybeRaiseSubBuildStarted(event);
subBuildFinished(event);
super.buildFinished(event);
}
public void messageLogged(BuildEvent event) {
maybeRaiseSubBuildStarted(event);
super.messageLogged(event);
}
public void subBuildStarted(BuildEvent event) {
initTimer(event.getProject());
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(LINE_SEP + DEMARKER + LINE_SEP + "Entering project " + name
+ LINE_SEP + path + LINE_SEP + DEMARKER, out, event.getPriority());
}
@Override
public void buildStarted(BuildEvent event) {
initTimer(event.getProject());
super.buildStarted(event);
}
/**
* 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;
}
public void subBuildFinished(BuildEvent event) {
stopTimer(event);
String name = extractNameOrDefault(event);
String failed = event.getException() != null ? "failing " : "";
printMessage(LINE_SEP + DEMARKER + LINE_SEP + "Exiting " + failed + "project "
+ name + LINE_SEP + DEMARKER, out, event.getPriority());
}
private void maybeRaiseSubBuildStarted(BuildEvent event) {
// double checked locking should be OK since the flag is write-once
if (!subBuildStartedRaised) {
synchronized (subBuildLock) {
if (!subBuildStartedRaised) {
subBuildStartedRaised = true;
subBuildStarted(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;
}
}
private void initTimer(Project project) {
buildStartTime = System.currentTimeMillis();
project.addReference(EXECUTION_TIMER_BUILD_RESULTS, new ArrayList<ExecutionResult>());
}
/**
* stops the timer and stores the result as a project reference by the key 'referenceName'
*/
private void stopTimer(BuildEvent event) {
List<ExecutionResult> results = event.getProject().getReference(EXECUTION_TIMER_BUILD_RESULTS);
ExecutionStatus status = ExecutionStatus.SUCCESS;
if (event.getException() != null) {
status = ExecutionStatus.FAILED;
} else if (event.getProject().getProperty(EasyAntMagicNames.PROJECT_EXECUTED_TARGETS) == null) {
status = ExecutionStatus.SKIPPED;
}
ExecutionResult execResult = new ExecutionResult(event.getProject().getName(), System.currentTimeMillis()
- buildStartTime, status);
results.add(execResult);
}
private void printExecutionSubBuildsExecutionTimes(Project project) {
List<ExecutionResult> allSubBuildResults = project.getReference(EXECUTION_TIMER_BUILD_RESULTS);
if (allSubBuildResults != null && !allSubBuildResults.isEmpty()) {
project.log(LINE_SEP + "Project Sub-modules Summary: " + LINE_SEP + formatExecutionResults(allSubBuildResults));
}
}
private String formatExecutionResults(List<ExecutionResult> results) {
String formattedResults;
int maxUnitNameLength = 0;
int maxExecTimeLength = 0;
for (ExecutionResult result : results) {
maxUnitNameLength = result.getUnitName().length() > maxUnitNameLength ? result.getUnitName().length()
: maxUnitNameLength;
maxExecTimeLength = result.getFormattedElapsedTime().length() > maxExecTimeLength ? result
.getFormattedElapsedTime().length() : maxExecTimeLength;
}
StringBuilder sb = new StringBuilder(LINE_SEP);
for (ExecutionResult result : results) {
String moduleName = padRight(result.getUnitName(), maxUnitNameLength + 10);
sb.append(" * ").append(moduleName);
// keeping both success and failed strings of equal length
String execResult = padRight(result.getStatus().toString(), 7);
sb.append(execResult)//
.append(" [ took ")//
.append(padRight(result.getFormattedElapsedTime(), maxExecTimeLength + 1))//
.append("]")
.append(LINE_SEP);
}
return sb.toString();
}
private String padRight(String string, int nbSpace) {
return String.format("%1$-" + nbSpace + "s", string);
}
}