mirror of
https://github.com/apache/maven-mvnd.git
synced 2025-09-24 21:35:15 +00:00
Provide an early display of build failures (fixes #361)
Also fix ordering of messages when projects are skipped at the end
This commit is contained in:
@@ -55,6 +55,7 @@ public abstract class Message {
|
||||
public static final int TRANSFER_CORRUPTED = 21;
|
||||
public static final int TRANSFER_SUCCEEDED = 22;
|
||||
public static final int TRANSFER_FAILED = 23;
|
||||
public static final int EXECUTION_FAILURE = 24;
|
||||
|
||||
final int type;
|
||||
|
||||
@@ -103,6 +104,8 @@ public abstract class Message {
|
||||
case TRANSFER_SUCCEEDED:
|
||||
case TRANSFER_FAILED:
|
||||
return TransferEvent.read(type, input);
|
||||
case EXECUTION_FAILURE:
|
||||
return ExecutionFailureEvent.read(input);
|
||||
}
|
||||
throw new IllegalStateException("Unexpected message type: " + type);
|
||||
}
|
||||
@@ -821,6 +824,56 @@ public abstract class Message {
|
||||
}
|
||||
}
|
||||
|
||||
public static class ExecutionFailureEvent extends Message {
|
||||
|
||||
final String projectId;
|
||||
final boolean halted;
|
||||
final String exception;
|
||||
|
||||
private ExecutionFailureEvent(String projectId, boolean halted, String exception) {
|
||||
super(EXECUTION_FAILURE);
|
||||
this.projectId = projectId;
|
||||
this.halted = halted;
|
||||
this.exception = exception;
|
||||
}
|
||||
|
||||
public String getProjectId() {
|
||||
return projectId;
|
||||
}
|
||||
|
||||
public boolean isHalted() {
|
||||
return halted;
|
||||
}
|
||||
|
||||
public String getException() {
|
||||
return exception;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ExecutionFailure{" +
|
||||
"projectId='" + projectId + '\'' +
|
||||
", halted=" + halted +
|
||||
", exception='" + exception + '\'' +
|
||||
'}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(DataOutputStream output) throws IOException {
|
||||
super.write(output);
|
||||
writeUTF(output, projectId);
|
||||
output.writeBoolean(halted);
|
||||
writeUTF(output, exception);
|
||||
}
|
||||
|
||||
public static ExecutionFailureEvent read(DataInputStream input) throws IOException {
|
||||
String projectId = readUTF(input);
|
||||
boolean halted = input.readBoolean();
|
||||
String exception = readUTF(input);
|
||||
return new ExecutionFailureEvent(projectId, halted, exception);
|
||||
}
|
||||
}
|
||||
|
||||
public static class TransferEvent extends Message {
|
||||
|
||||
public static final int INITIATED = 0;
|
||||
@@ -982,6 +1035,10 @@ public abstract class Message {
|
||||
return new StringMessage(PROJECT_STOPPED, projectId);
|
||||
}
|
||||
|
||||
public static ExecutionFailureEvent executionFailure(String projectId, boolean halted, String exception) {
|
||||
return new ExecutionFailureEvent(projectId, halted, exception);
|
||||
}
|
||||
|
||||
public static Message mojoStarted(String artifactId, String pluginGroupId, String pluginArtifactId,
|
||||
String pluginVersion, String mojo, String executionId) {
|
||||
return new MojoStartedEvent(artifactId, pluginGroupId, pluginArtifactId, pluginVersion, mojo, executionId);
|
||||
|
@@ -46,6 +46,7 @@ import org.jline.utils.Display;
|
||||
import org.mvndaemon.mvnd.common.Message;
|
||||
import org.mvndaemon.mvnd.common.Message.BuildException;
|
||||
import org.mvndaemon.mvnd.common.Message.BuildStarted;
|
||||
import org.mvndaemon.mvnd.common.Message.ExecutionFailureEvent;
|
||||
import org.mvndaemon.mvnd.common.Message.MojoStartedEvent;
|
||||
import org.mvndaemon.mvnd.common.Message.ProjectEvent;
|
||||
import org.mvndaemon.mvnd.common.Message.StringMessage;
|
||||
@@ -93,6 +94,7 @@ public class TerminalOutput implements ClientOutput {
|
||||
private final Terminal.SignalHandler previousIntHandler;
|
||||
private final Display display;
|
||||
private final Map<String, Map<String, TransferEvent>> transfers = new LinkedHashMap<>();
|
||||
private final ArrayList<ExecutionFailureEvent> failures = new ArrayList<>();
|
||||
private final LinkedHashMap<String, Project> projects = new LinkedHashMap<>();
|
||||
private final ClientLog log;
|
||||
private final Thread reader;
|
||||
@@ -409,6 +411,11 @@ public class TerminalOutput implements ClientOutput {
|
||||
.remove(te.getResourceName());
|
||||
break;
|
||||
}
|
||||
case Message.EXECUTION_FAILURE: {
|
||||
final ExecutionFailureEvent efe = (ExecutionFailureEvent) entry;
|
||||
failures.add(efe);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new IllegalStateException("Unexpected message " + entry);
|
||||
}
|
||||
@@ -521,12 +528,18 @@ public class TerminalOutput implements ClientOutput {
|
||||
// so keep one more line empty at the end
|
||||
dispLines--;
|
||||
|
||||
AttributedString globalTransfer = formatTransfers("");
|
||||
dispLines -= globalTransfer != null ? 1 : 0;
|
||||
|
||||
addStatusLine(lines, dispLines, projectsCount);
|
||||
|
||||
AttributedString globalFailure = formatFailures();
|
||||
if (globalFailure != null) {
|
||||
lines.add(globalFailure);
|
||||
dispLines--;
|
||||
}
|
||||
|
||||
AttributedString globalTransfer = formatTransfers("");
|
||||
if (globalTransfer != null) {
|
||||
lines.add(globalTransfer);
|
||||
dispLines--;
|
||||
}
|
||||
|
||||
if (projectsCount <= dispLines) {
|
||||
@@ -561,6 +574,43 @@ public class TerminalOutput implements ClientOutput {
|
||||
display.update(trimmed, -1);
|
||||
}
|
||||
|
||||
private AttributedString formatFailures() {
|
||||
if (failures.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
AttributedStringBuilder asb = new AttributedStringBuilder();
|
||||
asb.style(AttributedStyle.DEFAULT.foreground(AttributedStyle.RED).bold());
|
||||
if (failures.stream().anyMatch(ExecutionFailureEvent::isHalted)) {
|
||||
asb.append("ABORTING ");
|
||||
}
|
||||
asb.append("FAILURE: ");
|
||||
asb.style(AttributedStyle.DEFAULT.foreground(AttributedStyle.RED));
|
||||
if (failures.size() == 1) {
|
||||
ExecutionFailureEvent efe = failures.iterator().next();
|
||||
asb.append(efe.getProjectId());
|
||||
String exception = efe.getException();
|
||||
if (exception != null) {
|
||||
if (exception.startsWith("org.apache.maven.lifecycle.LifecycleExecutionException: ")) {
|
||||
exception = exception
|
||||
.substring("org.apache.maven.lifecycle.LifecycleExecutionException: ".length());
|
||||
}
|
||||
asb.append(": ").append(exception);
|
||||
}
|
||||
} else {
|
||||
asb.append(String.valueOf(failures.size())).append(" projects failed: ");
|
||||
asb.append(failures.stream().map(ExecutionFailureEvent::getProjectId).collect(Collectors.joining(", ")));
|
||||
}
|
||||
AttributedString as = asb.toAttributedString();
|
||||
if (as.columnLength() >= getTerminalWidth() - 1) {
|
||||
asb = new AttributedStringBuilder();
|
||||
asb.append(as.columnSubSequence(0, getTerminalWidth() - 2));
|
||||
asb.style(AttributedStyle.DEFAULT);
|
||||
asb.append("…");
|
||||
as = asb.toAttributedString();
|
||||
}
|
||||
return as;
|
||||
}
|
||||
|
||||
private AttributedString formatTransfers(String projectId) {
|
||||
Collection<TransferEvent> transfers = this.transfers.getOrDefault(projectId, Collections.emptyMap()).values();
|
||||
if (transfers.isEmpty()) {
|
||||
|
@@ -66,7 +66,6 @@ import org.apache.maven.eventspy.internal.EventSpyDispatcher;
|
||||
import org.apache.maven.exception.DefaultExceptionHandler;
|
||||
import org.apache.maven.exception.ExceptionHandler;
|
||||
import org.apache.maven.exception.ExceptionSummary;
|
||||
import org.apache.maven.execution.ExecutionListener;
|
||||
import org.apache.maven.execution.MavenExecutionRequest;
|
||||
import org.apache.maven.execution.MavenExecutionRequestPopulationException;
|
||||
import org.apache.maven.execution.MavenExecutionRequestPopulator;
|
||||
@@ -177,6 +176,8 @@ public class DaemonMavenCli {
|
||||
|
||||
private final Map<String, ConfigurationProcessor> configurationProcessors;
|
||||
|
||||
private final LoggingExecutionListener executionListener;
|
||||
|
||||
/** Non-volatile, assuming that it is accessed only from the main thread */
|
||||
private BuildEventListener buildEventListener = BuildEventListener.dummy();
|
||||
|
||||
@@ -197,6 +198,7 @@ public class DaemonMavenCli {
|
||||
configurationProcessors = container.lookupMap(ConfigurationProcessor.class);
|
||||
toolchainsBuilder = container.lookup(ToolchainsBuilder.class);
|
||||
dispatcher = (DefaultSecDispatcher) container.lookup(SecDispatcher.class, "maven");
|
||||
executionListener = container.lookup(LoggingExecutionListener.class);
|
||||
|
||||
}
|
||||
|
||||
@@ -579,8 +581,10 @@ public class DaemonMavenCli {
|
||||
final EventSpyDispatcher eventSpyDispatcher = container.lookup(EventSpyDispatcher.class);
|
||||
properties(cliRequest);
|
||||
configure(cliRequest, eventSpyDispatcher, configurationProcessors);
|
||||
LoggingExecutionListener executionListener = container.lookup(LoggingExecutionListener.class);
|
||||
populateRequest(cliRequest, cliRequest.request, slf4jLogger, eventSpyDispatcher,
|
||||
container.lookup(ModelProcessor.class), createTransferListener(cliRequest), buildEventListener);
|
||||
container.lookup(ModelProcessor.class), createTransferListener(cliRequest), buildEventListener,
|
||||
executionListener);
|
||||
executionRequestPopulator.populateDefaults(cliRequest.request);
|
||||
BootstrapCoreExtensionManager resolver = container.lookup(BootstrapCoreExtensionManager.class);
|
||||
return Collections
|
||||
@@ -1110,7 +1114,7 @@ public class DaemonMavenCli {
|
||||
|
||||
private void populateRequest(CliRequest cliRequest) {
|
||||
populateRequest(cliRequest, cliRequest.request, slf4jLogger, eventSpyDispatcher, modelProcessor,
|
||||
createTransferListener(cliRequest), buildEventListener);
|
||||
createTransferListener(cliRequest), buildEventListener, executionListener);
|
||||
}
|
||||
|
||||
private static void populateRequest(
|
||||
@@ -1120,7 +1124,8 @@ public class DaemonMavenCli {
|
||||
EventSpyDispatcher eventSpyDispatcher,
|
||||
ModelProcessor modelProcessor,
|
||||
TransferListener transferListener,
|
||||
BuildEventListener buildEventListener) {
|
||||
BuildEventListener buildEventListener,
|
||||
LoggingExecutionListener executionListener) {
|
||||
CommandLine commandLine = cliRequest.commandLine;
|
||||
String workingDirectory = cliRequest.workingDirectory;
|
||||
boolean showErrors = cliRequest.showErrors;
|
||||
@@ -1219,12 +1224,10 @@ public class DaemonMavenCli {
|
||||
}
|
||||
}
|
||||
|
||||
ExecutionListener executionListener = new ExecutionEventLogger();
|
||||
if (eventSpyDispatcher != null) {
|
||||
executionListener = new LoggingExecutionListener(
|
||||
eventSpyDispatcher.chainListener(executionListener),
|
||||
buildEventListener);
|
||||
}
|
||||
ExecutionEventLogger executionEventLogger = new ExecutionEventLogger();
|
||||
executionListener.init(
|
||||
eventSpyDispatcher.chainListener(executionEventLogger),
|
||||
buildEventListener);
|
||||
|
||||
String alternatePomFile = null;
|
||||
if (commandLine.hasOption(CLIManager.ALTERNATE_POM_FILE)) {
|
||||
|
@@ -92,6 +92,8 @@ public class SmartBuilder implements Builder {
|
||||
ProjectBuildList projectBuilds, final List<TaskSegment> taskSegments,
|
||||
ReactorBuildStatus reactorBuildStatus) throws ExecutionException, InterruptedException {
|
||||
|
||||
session.getRepositorySession().getData().set(ReactorBuildStatus.class, reactorBuildStatus);
|
||||
|
||||
DependencyGraph<MavenProject> graph = (DependencyGraph<MavenProject>) session.getRequest().getData()
|
||||
.get(DependencyGraph.class.getName());
|
||||
|
||||
|
@@ -101,6 +101,11 @@ public class ClientDispatcher extends BuildEventListener {
|
||||
queue.add(Message.projectStopped(projectId));
|
||||
}
|
||||
|
||||
public void executionFailure(String projectId, boolean halted, String exception) {
|
||||
projects.put(projectId, Boolean.FALSE);
|
||||
queue.add(Message.executionFailure(projectId, halted, exception));
|
||||
}
|
||||
|
||||
public void mojoStarted(ExecutionEvent event) {
|
||||
final MojoExecution execution = event.getMojoExecution();
|
||||
queue.add(Message.mojoStarted(
|
||||
|
@@ -36,6 +36,9 @@ public abstract class BuildEventListener {
|
||||
public void projectFinished(String projectId) {
|
||||
}
|
||||
|
||||
public void executionFailure(String projectId, boolean halted, String exception) {
|
||||
}
|
||||
|
||||
public void mojoStarted(ExecutionEvent event) {
|
||||
}
|
||||
|
||||
@@ -70,6 +73,8 @@ public abstract class BuildEventListener {
|
||||
|
||||
public abstract void projectFinished(String projectId);
|
||||
|
||||
public abstract void executionFailure(String projectId, boolean halted, String exception);
|
||||
|
||||
public abstract void mojoStarted(ExecutionEvent event);
|
||||
|
||||
public abstract void finish(int exitCode) throws Exception;
|
||||
|
@@ -15,19 +15,52 @@
|
||||
*/
|
||||
package org.mvndaemon.mvnd.logging.smart;
|
||||
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
import org.apache.maven.execution.ExecutionEvent;
|
||||
import org.apache.maven.execution.ExecutionListener;
|
||||
import org.apache.maven.execution.MavenSession;
|
||||
import org.apache.maven.execution.ProjectExecutionEvent;
|
||||
import org.apache.maven.execution.ProjectExecutionListener;
|
||||
import org.apache.maven.lifecycle.LifecycleExecutionException;
|
||||
import org.apache.maven.lifecycle.internal.ReactorBuildStatus;
|
||||
import org.eclipse.sisu.Typed;
|
||||
|
||||
public class LoggingExecutionListener implements ExecutionListener {
|
||||
@Singleton
|
||||
@Named
|
||||
@Typed({ LoggingExecutionListener.class, ExecutionListener.class, ProjectExecutionListener.class })
|
||||
public class LoggingExecutionListener implements ExecutionListener, ProjectExecutionListener {
|
||||
|
||||
private final ExecutionListener delegate;
|
||||
private final BuildEventListener buildEventListener;
|
||||
private ExecutionListener delegate;
|
||||
private BuildEventListener buildEventListener;
|
||||
|
||||
public LoggingExecutionListener(ExecutionListener delegate, BuildEventListener buildEventListener) {
|
||||
public void init(ExecutionListener delegate, BuildEventListener buildEventListener) {
|
||||
this.delegate = delegate;
|
||||
this.buildEventListener = buildEventListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeProjectExecution(ProjectExecutionEvent projectExecutionEvent) throws LifecycleExecutionException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeProjectLifecycleExecution(ProjectExecutionEvent projectExecutionEvent)
|
||||
throws LifecycleExecutionException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterProjectExecutionSuccess(ProjectExecutionEvent projectExecutionEvent) throws LifecycleExecutionException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterProjectExecutionFailure(ProjectExecutionEvent projectExecutionEvent) {
|
||||
MavenSession session = projectExecutionEvent.getSession();
|
||||
ReactorBuildStatus status = (ReactorBuildStatus) session.getRepositorySession().getData().get(ReactorBuildStatus.class);
|
||||
Throwable cause = projectExecutionEvent.getCause();
|
||||
buildEventListener.executionFailure(projectExecutionEvent.getProject().getArtifactId(),
|
||||
status.isHalted(), cause != null ? cause.toString() : null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void projectDiscoveryStarted(ExecutionEvent event) {
|
||||
setMdc(event);
|
||||
@@ -71,6 +104,7 @@ public class LoggingExecutionListener implements ExecutionListener {
|
||||
@Override
|
||||
public void projectSkipped(ExecutionEvent event) {
|
||||
setMdc(event);
|
||||
buildEventListener.projectStarted(event.getProject().getArtifactId());
|
||||
delegate.projectSkipped(event);
|
||||
buildEventListener.projectFinished(event.getProject().getArtifactId());
|
||||
}
|
||||
|
Reference in New Issue
Block a user