mirror of
https://github.com/apache/maven-mvnd.git
synced 2025-09-19 18:04:53 +00:00
Improve terminal output
* Say 'Looking for daemon...' straight ahead to elliminate the initial lag * Present 'hidden' threads along with used/max threads instead of 'n more' * Print project name in bold even if no mojo is present in the line * Be consistent about printing project artifactIds in bold also when lines are hidden * The righ status line is present also when lines are hidden
This commit is contained in:
@@ -15,7 +15,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.jboss.fuse.mvnd.client;
|
package org.jboss.fuse.mvnd.client;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
@@ -30,7 +29,6 @@ import java.util.Map;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.function.Consumer;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import org.jboss.fuse.mvnd.common.BuildProperties;
|
import org.jboss.fuse.mvnd.common.BuildProperties;
|
||||||
import org.jboss.fuse.mvnd.common.DaemonCompatibilitySpec;
|
import org.jboss.fuse.mvnd.common.DaemonCompatibilitySpec;
|
||||||
@@ -45,6 +43,7 @@ import org.jboss.fuse.mvnd.common.DaemonStopEvent;
|
|||||||
import org.jboss.fuse.mvnd.common.Environment;
|
import org.jboss.fuse.mvnd.common.Environment;
|
||||||
import org.jboss.fuse.mvnd.common.MavenDaemon;
|
import org.jboss.fuse.mvnd.common.MavenDaemon;
|
||||||
import org.jboss.fuse.mvnd.common.Os;
|
import org.jboss.fuse.mvnd.common.Os;
|
||||||
|
import org.jboss.fuse.mvnd.common.logging.ClientOutput;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -86,10 +85,10 @@ public class DaemonConnector {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DaemonClientConnection connect(DaemonCompatibilitySpec constraint, Consumer<String> logger) {
|
public DaemonClientConnection connect(DaemonCompatibilitySpec constraint, ClientOutput output) {
|
||||||
|
output.buildStatus("Looking up daemon...");
|
||||||
Map<Boolean, List<DaemonInfo>> idleBusy = registry.getAll().stream()
|
Map<Boolean, List<DaemonInfo>> idleBusy = registry.getAll().stream()
|
||||||
.collect(Collectors.groupingBy(di -> di.getState() == DaemonState.Idle));
|
.collect(Collectors.groupingBy(di -> di.getState() == DaemonState.Idle));
|
||||||
|
|
||||||
final Collection<DaemonInfo> idleDaemons = idleBusy.getOrDefault(true, Collections.emptyList());
|
final Collection<DaemonInfo> idleDaemons = idleBusy.getOrDefault(true, Collections.emptyList());
|
||||||
final Collection<DaemonInfo> busyDaemons = idleBusy.getOrDefault(false, Collections.emptyList());
|
final Collection<DaemonInfo> busyDaemons = idleBusy.getOrDefault(false, Collections.emptyList());
|
||||||
|
|
||||||
@@ -107,7 +106,7 @@ public class DaemonConnector {
|
|||||||
|
|
||||||
// No compatible daemons available - start a new daemon
|
// No compatible daemons available - start a new daemon
|
||||||
String message = handleStopEvents(idleDaemons, busyDaemons);
|
String message = handleStopEvents(idleDaemons, busyDaemons);
|
||||||
logger.accept(message);
|
output.buildStatus(message);
|
||||||
return startDaemon(constraint);
|
return startDaemon(constraint);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,11 +150,11 @@ public class DaemonConnector {
|
|||||||
if (numStopped > 0) {
|
if (numStopped > 0) {
|
||||||
reasons.add(numStopped + " stopped");
|
reasons.add(numStopped + " stopped");
|
||||||
}
|
}
|
||||||
return "Starting a Maven Daemon, "
|
return "Starting new daemon, "
|
||||||
+ String.join(" and ", reasons) + " Daemon" + (totalUnavailableDaemons > 1 ? "s" : "")
|
+ String.join(" and ", reasons) + " daemon" + (totalUnavailableDaemons > 1 ? "s" : "")
|
||||||
+ " could not be reused, use --status for details";
|
+ " could not be reused, use --status for details";
|
||||||
} else {
|
} else {
|
||||||
return "Starting a Maven Daemon (subsequent builds will be faster)";
|
return "Starting new daemon (subsequent builds will be faster)...";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -202,8 +202,7 @@ public class DefaultClient implements Client {
|
|||||||
|
|
||||||
final DaemonConnector connector = new DaemonConnector(layout, registry, buildProperties);
|
final DaemonConnector connector = new DaemonConnector(layout, registry, buildProperties);
|
||||||
List<String> opts = new ArrayList<>();
|
List<String> opts = new ArrayList<>();
|
||||||
try (DaemonClientConnection daemon = connector.connect(new DaemonCompatibilitySpec(javaHome, opts),
|
try (DaemonClientConnection daemon = connector.connect(new DaemonCompatibilitySpec(javaHome, opts), output)) {
|
||||||
s -> output.accept(null, s))) {
|
|
||||||
|
|
||||||
daemon.dispatch(new Message.BuildRequest(
|
daemon.dispatch(new Message.BuildRequest(
|
||||||
args,
|
args,
|
||||||
|
@@ -36,4 +36,6 @@ public interface ClientOutput extends AutoCloseable {
|
|||||||
|
|
||||||
void keepAlive();
|
void keepAlive();
|
||||||
|
|
||||||
|
void buildStatus(String status);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -64,13 +64,14 @@ public class TerminalOutput implements ClientOutput {
|
|||||||
private boolean displayDone = false;
|
private boolean displayDone = false;
|
||||||
|
|
||||||
private final long start;
|
private final long start;
|
||||||
|
private String buildStatus;
|
||||||
private String name;
|
private String name;
|
||||||
private int totalProjects;
|
private int totalProjects;
|
||||||
private int doneProjects;
|
private int doneProjects;
|
||||||
private int usedCores;
|
private int maxThreads;
|
||||||
|
|
||||||
enum EventType {
|
enum EventType {
|
||||||
BUILD,
|
BUILD_STATUS,
|
||||||
PROJECT_STATE,
|
PROJECT_STATE,
|
||||||
PROJECT_FINISHED,
|
PROJECT_FINISHED,
|
||||||
LOG,
|
LOG,
|
||||||
@@ -92,6 +93,10 @@ public class TerminalOutput implements ClientOutput {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link Project} is owned by the display loop thread and is accessed only from there. Therefore it does not need
|
||||||
|
* to be immutable.
|
||||||
|
*/
|
||||||
static class Project {
|
static class Project {
|
||||||
final String id;
|
final String id;
|
||||||
String status;
|
String status;
|
||||||
@@ -125,7 +130,7 @@ public class TerminalOutput implements ClientOutput {
|
|||||||
this.name = name;
|
this.name = name;
|
||||||
this.totalProjects = projects;
|
this.totalProjects = projects;
|
||||||
this.doneProjects = 0;
|
this.doneProjects = 0;
|
||||||
this.usedCores = cores;
|
this.maxThreads = cores;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void projectStateChanged(String projectId, String task) {
|
public void projectStateChanged(String projectId, String task) {
|
||||||
@@ -182,6 +187,15 @@ public class TerminalOutput implements ClientOutput {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void buildStatus(String status) {
|
||||||
|
try {
|
||||||
|
queue.put(new Event(EventType.BUILD_STATUS, null, status));
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void readInputLoop() {
|
void readInputLoop() {
|
||||||
try {
|
try {
|
||||||
while (!closing) {
|
while (!closing) {
|
||||||
@@ -209,6 +223,10 @@ public class TerminalOutput implements ClientOutput {
|
|||||||
queue.drainTo(entries);
|
queue.drainTo(entries);
|
||||||
for (Event entry : entries) {
|
for (Event entry : entries) {
|
||||||
switch (entry.type) {
|
switch (entry.type) {
|
||||||
|
case BUILD_STATUS: {
|
||||||
|
this.buildStatus = entry.message;
|
||||||
|
break;
|
||||||
|
}
|
||||||
case END_OF_STREAM: {
|
case END_OF_STREAM: {
|
||||||
projects.values().stream().flatMap(p -> p.log.stream()).forEach(log);
|
projects.values().stream().flatMap(p -> p.log.stream()).forEach(log);
|
||||||
clearDisplay();
|
clearDisplay();
|
||||||
@@ -312,49 +330,16 @@ public class TerminalOutput implements ClientOutput {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final List<AttributedString> lines = new ArrayList<>(rows);
|
final List<AttributedString> lines = new ArrayList<>(rows);
|
||||||
int dispLines = rows - 1; // for the "Building..." line
|
int dispLines = rows - 1; // for the build status line
|
||||||
dispLines--; // there's a bug which sometimes make the cursor goes one line below, so keep one more line empty at the end
|
dispLines--; // there's a bug which sometimes make the cursor goes one line below, so keep one more line empty at the end
|
||||||
if (projects.size() <= dispLines) {
|
final int projectsCount = projects.size();
|
||||||
if (name != null) {
|
|
||||||
AttributedStringBuilder asb = new AttributedStringBuilder();
|
|
||||||
asb.append("Building ");
|
|
||||||
asb.style(AttributedStyle.BOLD);
|
|
||||||
asb.append(name);
|
|
||||||
asb.style(AttributedStyle.DEFAULT);
|
|
||||||
|
|
||||||
StringBuilder statusLine = new StringBuilder(64);
|
addStatusLine(lines, dispLines, projectsCount);
|
||||||
statusLine.append(" threads: ").append(usedCores);
|
|
||||||
|
|
||||||
statusLine.append(" time: ");
|
if (projectsCount <= dispLines) {
|
||||||
long sec = (System.currentTimeMillis() - this.start) / 1000;
|
int remLogLines = dispLines - projectsCount;
|
||||||
if (sec > 60) {
|
|
||||||
statusLine.append(sec / 60).append('m').append(String.valueOf(sec % 60)).append('s');
|
|
||||||
} else {
|
|
||||||
statusLine.append(sec).append('s');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (totalProjects > 0) {
|
|
||||||
statusLine.append(" progress: ").append(doneProjects).append('/').append(totalProjects).append(' ')
|
|
||||||
.append(doneProjects * 100 / totalProjects).append('%');
|
|
||||||
}
|
|
||||||
lines.add(asb.append(statusLine.toString()).toAttributedString());
|
|
||||||
}
|
|
||||||
int remLogLines = dispLines - projects.size();
|
|
||||||
for (Project prj : projects.values()) {
|
for (Project prj : projects.values()) {
|
||||||
String str = prj.status != null ? prj.status : ":" + prj.id + ":<unknown>";
|
addProjectLine(lines, prj);
|
||||||
int cs = str.indexOf(':');
|
|
||||||
int ce = cs >= 0 ? str.indexOf(':', cs + 1) : -1;
|
|
||||||
if (ce > 0) {
|
|
||||||
AttributedStringBuilder asb = new AttributedStringBuilder();
|
|
||||||
asb.append(str, 0, cs);
|
|
||||||
asb.style(AttributedStyle.BOLD);
|
|
||||||
asb.append(str, cs, ce);
|
|
||||||
asb.style(AttributedStyle.DEFAULT);
|
|
||||||
asb.append(str, ce, str.length());
|
|
||||||
lines.add(asb.toAttributedString());
|
|
||||||
} else {
|
|
||||||
lines.add(AttributedString.fromAnsi(str));
|
|
||||||
}
|
|
||||||
// get the last lines of the project log, taking multi-line logs into account
|
// get the last lines of the project log, taking multi-line logs into account
|
||||||
int nb = Math.min(remLogLines, linesPerProject);
|
int nb = Math.min(remLogLines, linesPerProject);
|
||||||
List<AttributedString> logs = lastN(prj.log, nb).stream()
|
List<AttributedString> logs = lastN(prj.log, nb).stream()
|
||||||
@@ -365,10 +350,14 @@ public class TerminalOutput implements ClientOutput {
|
|||||||
remLogLines -= logs.size();
|
remLogLines -= logs.size();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
lines.add(new AttributedString("Building... (" + (projects.size() - dispLines) + " more)"));
|
int skipProjects = projectsCount - dispLines;
|
||||||
lines.addAll(projects.values().stream()
|
for (Project prj : projects.values()) {
|
||||||
.map(prj -> AttributedString.fromAnsi(prj.status != null ? prj.status : "<unknown>"))
|
if (skipProjects == 0) {
|
||||||
.collect(lastN(dispLines)));
|
addProjectLine(lines, prj);
|
||||||
|
} else {
|
||||||
|
skipProjects--;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
List<AttributedString> trimmed = lines.stream()
|
List<AttributedString> trimmed = lines.stream()
|
||||||
.map(s -> s.columnSubSequence(0, cols))
|
.map(s -> s.columnSubSequence(0, cols))
|
||||||
@@ -376,6 +365,62 @@ public class TerminalOutput implements ClientOutput {
|
|||||||
display.update(trimmed, -1);
|
display.update(trimmed, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void addStatusLine(final List<AttributedString> lines, int dispLines, final int projectsCount) {
|
||||||
|
AttributedStringBuilder asb = new AttributedStringBuilder();
|
||||||
|
StringBuilder statusLine = new StringBuilder(64);
|
||||||
|
if (name == null) {
|
||||||
|
statusLine.append(buildStatus != null ? buildStatus : "Looking up daemon...");
|
||||||
|
} else {
|
||||||
|
asb.append("Building ");
|
||||||
|
asb.style(AttributedStyle.BOLD);
|
||||||
|
asb.append(name);
|
||||||
|
asb.style(AttributedStyle.DEFAULT);
|
||||||
|
if (projectsCount <= dispLines) {
|
||||||
|
statusLine.append(" threads used/max: ")
|
||||||
|
.append(projectsCount).append('/').append(maxThreads);
|
||||||
|
} else {
|
||||||
|
statusLine.append(" threads used/hidden/max: ")
|
||||||
|
.append(projectsCount).append('/').append(projectsCount - dispLines).append('/').append(maxThreads);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (totalProjects > 0) {
|
||||||
|
statusLine.append(" progress: ").append(doneProjects).append('/').append(totalProjects).append(' ')
|
||||||
|
.append(doneProjects * 100 / totalProjects).append('%');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
statusLine.append(" time: ");
|
||||||
|
long sec = (System.currentTimeMillis() - this.start) / 1000;
|
||||||
|
if (sec > 60) {
|
||||||
|
statusLine.append(sec / 60).append('m').append(String.valueOf(sec % 60)).append('s');
|
||||||
|
} else {
|
||||||
|
statusLine.append(sec).append('s');
|
||||||
|
}
|
||||||
|
|
||||||
|
asb.append(statusLine.toString());
|
||||||
|
lines.add(asb.toAttributedString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addProjectLine(final List<AttributedString> lines, Project prj) {
|
||||||
|
String str = prj.status != null ? prj.status : ":" + prj.id + ":<unknown>";
|
||||||
|
if (str.length() >= 1 && str.charAt(0) == ':') {
|
||||||
|
int ce = str.indexOf(':', 1);
|
||||||
|
final AttributedStringBuilder asb = new AttributedStringBuilder();
|
||||||
|
asb.append(":");
|
||||||
|
asb.style(AttributedStyle.BOLD);
|
||||||
|
if (ce > 0) {
|
||||||
|
asb.append(str, 1, ce);
|
||||||
|
asb.style(AttributedStyle.DEFAULT);
|
||||||
|
asb.append(str, ce, str.length());
|
||||||
|
} else {
|
||||||
|
asb.append(str, 1, str.length());
|
||||||
|
}
|
||||||
|
lines.add(asb.toAttributedString());
|
||||||
|
} else {
|
||||||
|
lines.add(AttributedString.fromAnsi(str));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static <T> List<T> lastN(List<T> list, int n) {
|
private static <T> List<T> lastN(List<T> list, int n) {
|
||||||
return list.subList(Math.max(0, list.size() - n), list.size());
|
return list.subList(Math.max(0, list.size() - n), list.size());
|
||||||
}
|
}
|
||||||
|
@@ -38,11 +38,9 @@ import java.util.Map.Entry;
|
|||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
import javax.enterprise.inject.Default;
|
import javax.enterprise.inject.Default;
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
import org.apache.maven.RepositoryUtils;
|
import org.apache.maven.RepositoryUtils;
|
||||||
import org.apache.maven.artifact.Artifact;
|
import org.apache.maven.artifact.Artifact;
|
||||||
import org.apache.maven.eventspy.AbstractEventSpy;
|
import org.apache.maven.eventspy.AbstractEventSpy;
|
||||||
|
Reference in New Issue
Block a user