mirror of
https://github.com/apache/maven-mvnd.git
synced 2025-09-13 06:29:18 +00:00
Maven-like rolling output when the build happens to be linear #269
This commit is contained in:
@@ -173,6 +173,11 @@ public class TerminalOutput implements ClientOutput {
|
|||||||
this.maxThreads = bs.getMaxThreads();
|
this.maxThreads = bs.getMaxThreads();
|
||||||
final int maxThreadsDigits = (int) (Math.log10(maxThreads) + 1);
|
final int maxThreadsDigits = (int) (Math.log10(maxThreads) + 1);
|
||||||
this.threadsFormat = "%" + (maxThreadsDigits * 3 + 2) + "s";
|
this.threadsFormat = "%" + (maxThreadsDigits * 3 + 2) + "s";
|
||||||
|
if (maxThreads <= 1 || totalProjects <= 1) {
|
||||||
|
this.noBuffering = true;
|
||||||
|
display.update(Collections.emptyList(), 0);
|
||||||
|
applyNoBuffering();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Message.CANCEL_BUILD: {
|
case Message.CANCEL_BUILD: {
|
||||||
@@ -322,8 +327,7 @@ public class TerminalOutput implements ClientOutput {
|
|||||||
case CTRL_B:
|
case CTRL_B:
|
||||||
noBuffering = !noBuffering;
|
noBuffering = !noBuffering;
|
||||||
if (noBuffering) {
|
if (noBuffering) {
|
||||||
projects.values().stream().flatMap(p -> p.log.stream()).forEach(log);
|
applyNoBuffering();
|
||||||
projects.clear();
|
|
||||||
} else {
|
} else {
|
||||||
clearDisplay();
|
clearDisplay();
|
||||||
}
|
}
|
||||||
@@ -345,6 +349,11 @@ public class TerminalOutput implements ClientOutput {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void applyNoBuffering() {
|
||||||
|
projects.values().stream().flatMap(p -> p.log.stream()).forEach(log);
|
||||||
|
projects.clear();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void describeTerminal() {
|
public void describeTerminal() {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
|
@@ -15,28 +15,151 @@
|
|||||||
*/
|
*/
|
||||||
package org.mvndaemon.mvnd.builder;
|
package org.mvndaemon.mvnd.builder;
|
||||||
|
|
||||||
|
import groovy.lang.Binding;
|
||||||
|
import groovy.lang.GroovyShell;
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.Writer;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.function.Function;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
import org.apache.maven.execution.MavenSession;
|
||||||
import org.apache.maven.execution.ProjectDependencyGraph;
|
import org.apache.maven.execution.ProjectDependencyGraph;
|
||||||
import org.apache.maven.project.MavenProject;
|
import org.apache.maven.project.MavenProject;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* File origin:
|
* File origin:
|
||||||
* https://github.com/takari/takari-smart-builder/blob/takari-smart-builder-0.6.1/src/main/java/io/takari/maven/builder/smart/DependencyGraph.java
|
* https://github.com/takari/takari-smart-builder/blob/takari-smart-builder-0.6.1/src/main/java/io/takari/maven/builder/smart/DependencyGraph.java
|
||||||
*/
|
*/
|
||||||
interface DependencyGraph<K> {
|
public class DependencyGraph<K> {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(DependencyGraph.class);
|
||||||
|
static final Pattern mvndRuleSanitizerPattern = Pattern.compile("[,\\s]+");
|
||||||
|
|
||||||
|
private final List<K> projects;
|
||||||
|
private final Map<K, List<K>> upstreams;
|
||||||
|
private final Map<K, List<K>> downstreams;
|
||||||
|
|
||||||
|
public static DependencyGraph<MavenProject> fromMaven(MavenSession session) {
|
||||||
|
|
||||||
|
final ProjectDependencyGraph graph = session.getProjectDependencyGraph();
|
||||||
|
final List<MavenProject> projects = graph.getSortedProjects();
|
||||||
|
return fromMaven(graph, getRules(projects, session));
|
||||||
|
}
|
||||||
|
|
||||||
|
static String getRules(List<MavenProject> projects, MavenSession session) {
|
||||||
|
List<String> list = new ArrayList<>();
|
||||||
|
|
||||||
|
String providerScript = null;
|
||||||
|
final MavenProject topLevelProject = projects.get(0);
|
||||||
|
String providerUrl = topLevelProject.getProperties()
|
||||||
|
.getProperty(SmartBuilder.MVND_BUILDER_RULES_PROVIDER_URL);
|
||||||
|
if (providerUrl != null) {
|
||||||
|
logger.warn(SmartBuilder.MVND_BUILDER_RULES_PROVIDER_URL
|
||||||
|
+ " property is deprecated and the support for it will be removed in mvnd 0.3. See https://github.com/mvndaemon/mvnd/issues/264");
|
||||||
|
|
||||||
|
URL url;
|
||||||
|
try {
|
||||||
|
url = new URL(providerUrl);
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
try {
|
||||||
|
url = new File(providerUrl).toURI().toURL();
|
||||||
|
} catch (MalformedURLException ex) {
|
||||||
|
url = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (url == null) {
|
||||||
|
throw new RuntimeException("Bad syntax for " + SmartBuilder.MVND_BUILDER_RULES_PROVIDER_URL, null);
|
||||||
|
}
|
||||||
|
try (BufferedReader r = new BufferedReader(new InputStreamReader(url.openStream()))) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
char[] buf = new char[8192];
|
||||||
|
int l;
|
||||||
|
while ((l = r.read(buf)) >= 0) {
|
||||||
|
sb.append(buf, 0, l);
|
||||||
|
}
|
||||||
|
providerScript = sb.toString();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException("Unable to read provider url " + SmartBuilder.MVND_BUILDER_RULES_PROVIDER_URL,
|
||||||
|
e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (providerScript == null) {
|
||||||
|
providerScript = topLevelProject.getProperties()
|
||||||
|
.getProperty(SmartBuilder.MVND_BUILDER_RULES_PROVIDER_SCRIPT);
|
||||||
|
}
|
||||||
|
if (providerScript != null) {
|
||||||
|
logger.warn(SmartBuilder.MVND_BUILDER_RULES_PROVIDER_SCRIPT
|
||||||
|
+ " property is deprecated and the support for it will be removed in mvnd 0.3. See https://github.com/mvndaemon/mvnd/issues/264");
|
||||||
|
|
||||||
|
Binding binding = new Binding();
|
||||||
|
GroovyShell shell = new GroovyShell(binding);
|
||||||
|
binding.setProperty("session", session);
|
||||||
|
Object result = shell.evaluate(providerScript);
|
||||||
|
if (result instanceof Iterable) {
|
||||||
|
for (Object r : (Iterable) result) {
|
||||||
|
list.add(r.toString());
|
||||||
|
}
|
||||||
|
} else if (result != null) {
|
||||||
|
list.add(result.toString());
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException("The provider script did not return a valid string or string collection", null);
|
||||||
|
}
|
||||||
|
list.add(result.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
String topRule = topLevelProject.getProperties().getProperty(SmartBuilder.MVND_BUILDER_RULES);
|
||||||
|
if (topRule != null) {
|
||||||
|
logger.warn(SmartBuilder.MVND_BUILDER_RULES
|
||||||
|
+ " property is deprecated and the support for it will be removed in mvnd 0.3. See https://github.com/mvndaemon/mvnd/issues/264");
|
||||||
|
list.add(topRule);
|
||||||
|
}
|
||||||
|
|
||||||
|
projects.forEach(p -> {
|
||||||
|
String rule = p.getProperties().getProperty(SmartBuilder.MVND_BUILDER_RULE);
|
||||||
|
if (rule != null) {
|
||||||
|
logger.warn(SmartBuilder.MVND_BUILDER_RULE
|
||||||
|
+ " property is deprecated and the support for it will be removed in mvnd 0.3. See https://github.com/mvndaemon/mvnd/issues/264");
|
||||||
|
rule = rule.trim();
|
||||||
|
if (!rule.isEmpty()) {
|
||||||
|
rule = mvndRuleSanitizerPattern.matcher(rule).replaceAll(",");
|
||||||
|
list.add(rule + " before " + p.getGroupId() + ":" + p.getArtifactId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
String rules = null;
|
||||||
|
if (!list.isEmpty()) {
|
||||||
|
rules = String.join("\n", list);
|
||||||
|
}
|
||||||
|
return rules;
|
||||||
|
}
|
||||||
|
|
||||||
static DependencyGraph<MavenProject> fromMaven(ProjectDependencyGraph graph, String rules) {
|
static DependencyGraph<MavenProject> fromMaven(ProjectDependencyGraph graph, String rules) {
|
||||||
List<MavenProject> projects = graph.getSortedProjects();
|
final List<MavenProject> projects = graph.getSortedProjects();
|
||||||
Map<MavenProject, List<MavenProject>> upstreams = projects.stream()
|
Map<MavenProject, List<MavenProject>> upstreams = projects.stream()
|
||||||
.collect(Collectors.toMap(p -> p, p -> graph.getUpstreamProjects(p, false)));
|
.collect(Collectors.toMap(p -> p, p -> graph.getUpstreamProjects(p, false)));
|
||||||
Map<MavenProject, List<MavenProject>> downstreams = projects.stream()
|
Map<MavenProject, List<MavenProject>> downstreams = projects.stream()
|
||||||
.collect(Collectors.toMap(p -> p, p -> graph.getDownstreamProjects(p, false)));
|
.collect(
|
||||||
|
Collectors.toMap(p -> p, p -> graph.getDownstreamProjects(p, false)));
|
||||||
|
|
||||||
if (rules != null) {
|
if (rules != null) {
|
||||||
for (String rule : rules.split("\\s*;\\s*|\n")) {
|
for (String rule : rules.split("\\s*;\\s*|\n")) {
|
||||||
@@ -80,35 +203,162 @@ interface DependencyGraph<K> {
|
|||||||
deps.get(0).forEach(p -> downstreams.get(p).addAll(deps.get(1)));
|
deps.get(0).forEach(p -> downstreams.get(p).addAll(deps.get(1)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new DependencyGraph<MavenProject>() {
|
return new DependencyGraph<MavenProject>(Collections.unmodifiableList(projects), upstreams, downstreams);
|
||||||
@Override
|
}
|
||||||
public Stream<MavenProject> getDownstreamProjects(MavenProject project) {
|
|
||||||
|
public DependencyGraph(List<K> projects, Map<K, List<K>> upstreams, Map<K, List<K>> downstreams) {
|
||||||
|
this.projects = projects;
|
||||||
|
this.upstreams = upstreams;
|
||||||
|
this.downstreams = downstreams;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Stream<K> getDownstreamProjects(K project) {
|
||||||
return downstreams.get(project).stream();
|
return downstreams.get(project).stream();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public Stream<K> getUpstreamProjects(K project) {
|
||||||
public Stream<MavenProject> getProjects() {
|
|
||||||
return projects.stream();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Stream<MavenProject> getUpstreamProjects(MavenProject project) {
|
|
||||||
return upstreams.get(project).stream();
|
return upstreams.get(project).stream();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public boolean isRoot(K project) {
|
||||||
public boolean isRoot(MavenProject project) {
|
|
||||||
return upstreams.get(project).isEmpty();
|
return upstreams.get(project).isEmpty();
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
public Stream<K> getProjects() {
|
||||||
|
return projects.stream();
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream<K> getProjects();
|
public int computeMaxWidth(int max, long maxTimeMillis) {
|
||||||
|
return new DagWidth<>(this).getMaxWidth(max, maxTimeMillis);
|
||||||
|
}
|
||||||
|
|
||||||
boolean isRoot(K project);
|
public void store(Function<K, String> toString, Path path) {
|
||||||
|
try (Writer w = Files.newBufferedWriter(path)) {
|
||||||
|
getProjects().forEach(k -> {
|
||||||
|
try {
|
||||||
|
w.write(toString.apply(k));
|
||||||
|
w.write(" = ");
|
||||||
|
w.write(getUpstreamProjects(k).map(toString).collect(Collectors.joining(",")));
|
||||||
|
w.write(System.lineSeparator());
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Stream<K> getDownstreamProjects(K project);
|
static class DagWidth<K> {
|
||||||
|
|
||||||
Stream<K> getUpstreamProjects(K project);
|
private final DependencyGraph<K> graph;
|
||||||
|
private final Map<K, Set<K>> allUpstreams = new HashMap<>();
|
||||||
|
|
||||||
|
public DagWidth(DependencyGraph<K> graph) {
|
||||||
|
this.graph = graph;
|
||||||
|
graph.getProjects().forEach(this::allUpstreams);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMaxWidth() {
|
||||||
|
return getMaxWidth(Integer.MAX_VALUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMaxWidth(int maxmax) {
|
||||||
|
return getMaxWidth(maxmax, Long.MAX_VALUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMaxWidth(int maxmax, long maxTimeMillis) {
|
||||||
|
int max = 0;
|
||||||
|
if (maxmax < allUpstreams.size()) {
|
||||||
|
// try inverted upstream bound
|
||||||
|
Map<Set<K>, Set<K>> mapByUpstreams = new HashMap<>();
|
||||||
|
allUpstreams.forEach((k, ups) -> {
|
||||||
|
mapByUpstreams.computeIfAbsent(ups, n -> new HashSet<>()).add(k);
|
||||||
|
});
|
||||||
|
max = mapByUpstreams.values().stream()
|
||||||
|
.mapToInt(Set::size)
|
||||||
|
.max()
|
||||||
|
.orElse(0);
|
||||||
|
if (max >= maxmax) {
|
||||||
|
return maxmax;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
long tmax = System.currentTimeMillis() + maxTimeMillis;
|
||||||
|
int tries = 0;
|
||||||
|
SubsetIterator iterator = new SubsetIterator(getRoots());
|
||||||
|
while (max < maxmax && iterator.hasNext()) {
|
||||||
|
if (++tries % 100 == 0 && System.currentTimeMillis() < tmax) {
|
||||||
|
return maxmax;
|
||||||
|
}
|
||||||
|
List<K> l = iterator.next();
|
||||||
|
max = Math.max(max, l.size());
|
||||||
|
}
|
||||||
|
return Math.min(max, maxmax);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class SubsetIterator implements Iterator<List<K>> {
|
||||||
|
|
||||||
|
final List<List<K>> nexts = new ArrayList<>();
|
||||||
|
final Set<List<K>> visited = new HashSet<>();
|
||||||
|
|
||||||
|
public SubsetIterator(List<K> roots) {
|
||||||
|
nexts.add(roots);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
return !nexts.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<K> next() {
|
||||||
|
List<K> list = nexts.remove(0);
|
||||||
|
list.stream()
|
||||||
|
.map(node -> ensembleWithChildrenOf(list, node))
|
||||||
|
.filter(visited::add)
|
||||||
|
.forEach(nexts::add);
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<K> getRoots() {
|
||||||
|
return graph.getProjects()
|
||||||
|
.filter(graph::isRoot)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a stream of all subset of descendants of the given nodes
|
||||||
|
*/
|
||||||
|
private Stream<List<K>> childEnsembles(List<K> list) {
|
||||||
|
return Stream.concat(
|
||||||
|
Stream.of(list),
|
||||||
|
list.parallelStream()
|
||||||
|
.map(node -> ensembleWithChildrenOf(list, node))
|
||||||
|
.flatMap(this::childEnsembles));
|
||||||
|
}
|
||||||
|
|
||||||
|
List<K> ensembleWithChildrenOf(List<K> list, K node) {
|
||||||
|
return Stream.concat(
|
||||||
|
list.stream().filter(k -> !Objects.equals(k, node)),
|
||||||
|
graph.getDownstreamProjects(node)
|
||||||
|
.filter(k -> allUpstreams(k).stream()
|
||||||
|
.noneMatch(k2 -> !Objects.equals(k2, node) && list.contains(k2))))
|
||||||
|
.distinct().collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<K> allUpstreams(K node) {
|
||||||
|
Set<K> aups = allUpstreams.get(node);
|
||||||
|
if (aups == null) {
|
||||||
|
aups = Stream.concat(
|
||||||
|
graph.getUpstreamProjects(node),
|
||||||
|
graph.getUpstreamProjects(node).map(this::allUpstreams).flatMap(Set::stream))
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
allUpstreams.put(node, aups);
|
||||||
|
}
|
||||||
|
return aups;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -15,14 +15,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.mvndaemon.mvnd.builder;
|
package org.mvndaemon.mvnd.builder;
|
||||||
|
|
||||||
import groovy.lang.Binding;
|
|
||||||
import groovy.lang.GroovyShell;
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.net.MalformedURLException;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.util.AbstractMap;
|
import java.util.AbstractMap;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -105,96 +97,15 @@ public class SmartBuilder implements Builder {
|
|||||||
public synchronized void build(final MavenSession session, final ReactorContext reactorContext,
|
public synchronized void build(final MavenSession session, final ReactorContext reactorContext,
|
||||||
ProjectBuildList projectBuilds, final List<TaskSegment> taskSegments,
|
ProjectBuildList projectBuilds, final List<TaskSegment> taskSegments,
|
||||||
ReactorBuildStatus reactorBuildStatus) throws ExecutionException, InterruptedException {
|
ReactorBuildStatus reactorBuildStatus) throws ExecutionException, InterruptedException {
|
||||||
List<String> list = new ArrayList<>();
|
|
||||||
|
|
||||||
String providerScript = null;
|
DependencyGraph<MavenProject> graph = (DependencyGraph<MavenProject>) session.getRequest().getData()
|
||||||
String providerUrl = session.getTopLevelProject().getProperties()
|
.get(DependencyGraph.class.getName());
|
||||||
.getProperty(MVND_BUILDER_RULES_PROVIDER_URL);
|
|
||||||
if (providerUrl != null) {
|
|
||||||
logger.warn(MVND_BUILDER_RULES_PROVIDER_URL
|
|
||||||
+ " property is deprecated and the support for it will be removed in mvnd 0.3. See https://github.com/mvndaemon/mvnd/issues/264");
|
|
||||||
|
|
||||||
URL url;
|
|
||||||
try {
|
|
||||||
url = new URL(providerUrl);
|
|
||||||
} catch (MalformedURLException e) {
|
|
||||||
try {
|
|
||||||
url = new File(providerUrl).toURI().toURL();
|
|
||||||
} catch (MalformedURLException ex) {
|
|
||||||
url = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (url == null) {
|
|
||||||
throw new ExecutionException("Bad syntax for " + MVND_BUILDER_RULES_PROVIDER_URL, null);
|
|
||||||
}
|
|
||||||
try (BufferedReader r = new BufferedReader(new InputStreamReader(url.openStream()))) {
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
char[] buf = new char[8192];
|
|
||||||
int l;
|
|
||||||
while ((l = r.read(buf)) >= 0) {
|
|
||||||
sb.append(buf, 0, l);
|
|
||||||
}
|
|
||||||
providerScript = sb.toString();
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new ExecutionException("Unable to read provider url " + MVND_BUILDER_RULES_PROVIDER_URL, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (providerScript == null) {
|
|
||||||
providerScript = session.getTopLevelProject().getProperties()
|
|
||||||
.getProperty(MVND_BUILDER_RULES_PROVIDER_SCRIPT);
|
|
||||||
}
|
|
||||||
if (providerScript != null) {
|
|
||||||
logger.warn(MVND_BUILDER_RULES_PROVIDER_SCRIPT
|
|
||||||
+ " property is deprecated and the support for it will be removed in mvnd 0.3. See https://github.com/mvndaemon/mvnd/issues/264");
|
|
||||||
|
|
||||||
Binding binding = new Binding();
|
|
||||||
GroovyShell shell = new GroovyShell(binding);
|
|
||||||
binding.setProperty("session", session);
|
|
||||||
Object result = shell.evaluate(providerScript);
|
|
||||||
if (result instanceof Iterable) {
|
|
||||||
for (Object r : (Iterable) result) {
|
|
||||||
list.add(r.toString());
|
|
||||||
}
|
|
||||||
} else if (result != null) {
|
|
||||||
list.add(result.toString());
|
|
||||||
} else {
|
|
||||||
throw new ExecutionException("The provider script did not return a valid string or string collection", null);
|
|
||||||
}
|
|
||||||
list.add(result.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
String topRule = session.getTopLevelProject().getProperties()
|
|
||||||
.getProperty(MVND_BUILDER_RULES);
|
|
||||||
if (topRule != null) {
|
|
||||||
logger.warn(MVND_BUILDER_RULES
|
|
||||||
+ " property is deprecated and the support for it will be removed in mvnd 0.3. See https://github.com/mvndaemon/mvnd/issues/264");
|
|
||||||
list.add(topRule);
|
|
||||||
}
|
|
||||||
|
|
||||||
session.getAllProjects().forEach(p -> {
|
|
||||||
String rule = p.getProperties().getProperty(MVND_BUILDER_RULE);
|
|
||||||
if (rule != null) {
|
|
||||||
logger.warn(MVND_BUILDER_RULE
|
|
||||||
+ " property is deprecated and the support for it will be removed in mvnd 0.3. See https://github.com/mvndaemon/mvnd/issues/264");
|
|
||||||
rule = rule.trim();
|
|
||||||
if (!rule.isEmpty()) {
|
|
||||||
rule = mvndRuleSanitizerPattern.matcher(rule).replaceAll(",");
|
|
||||||
list.add(rule + " before " + p.getGroupId() + ":" + p.getArtifactId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
String rules = null;
|
|
||||||
if (!list.isEmpty()) {
|
|
||||||
rules = String.join("\n", list);
|
|
||||||
}
|
|
||||||
|
|
||||||
DependencyGraph<MavenProject> graph = DependencyGraph.fromMaven(session.getProjectDependencyGraph(), rules);
|
|
||||||
|
|
||||||
// log overall build info
|
// log overall build info
|
||||||
final int degreeOfConcurrency = session.getRequest().getDegreeOfConcurrency();
|
final int degreeOfConcurrency = session.getRequest().getDegreeOfConcurrency();
|
||||||
logger.info("Task segments : " + taskSegments.stream().map(Object::toString).collect(Collectors.joining(" ")));
|
logger.info("Task segments : " + taskSegments.stream().map(Object::toString).collect(Collectors.joining(" ")));
|
||||||
logger.info("Build maximum degree of concurrency is " + degreeOfConcurrency);
|
logger.info("Build maximum degree of concurrency is " + degreeOfConcurrency);
|
||||||
logger.info("Total number of projects is " + graph.getProjects().count());
|
logger.info("Total number of projects is " + session.getProjects().size());
|
||||||
|
|
||||||
// the actual build execution
|
// the actual build execution
|
||||||
List<Map.Entry<TaskSegment, ReactorBuildStats>> allstats = new ArrayList<>();
|
List<Map.Entry<TaskSegment, ReactorBuildStats>> allstats = new ArrayList<>();
|
||||||
|
@@ -43,6 +43,7 @@ import java.util.stream.Collectors;
|
|||||||
import org.apache.maven.cli.DaemonMavenCli;
|
import org.apache.maven.cli.DaemonMavenCli;
|
||||||
import org.apache.maven.execution.MavenSession;
|
import org.apache.maven.execution.MavenSession;
|
||||||
import org.apache.maven.project.MavenProject;
|
import org.apache.maven.project.MavenProject;
|
||||||
|
import org.mvndaemon.mvnd.builder.DependencyGraph;
|
||||||
import org.mvndaemon.mvnd.builder.SmartBuilder;
|
import org.mvndaemon.mvnd.builder.SmartBuilder;
|
||||||
import org.mvndaemon.mvnd.common.DaemonConnection;
|
import org.mvndaemon.mvnd.common.DaemonConnection;
|
||||||
import org.mvndaemon.mvnd.common.DaemonException;
|
import org.mvndaemon.mvnd.common.DaemonException;
|
||||||
@@ -655,8 +656,12 @@ public class Server implements AutoCloseable, Runnable {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onStartSession(MavenSession session) {
|
protected void onStartSession(MavenSession session) {
|
||||||
queue.add(new BuildStarted(getCurrentProject(session).getName(), session.getProjects().size(),
|
final int degreeOfConcurrency = session.getRequest().getDegreeOfConcurrency();
|
||||||
session.getRequest().getDegreeOfConcurrency()));
|
final DependencyGraph<MavenProject> dependencyGraph = DependencyGraph.fromMaven(session);
|
||||||
|
session.getRequest().getData().put(DependencyGraph.class.getName(), dependencyGraph);
|
||||||
|
|
||||||
|
final int maxThreads = degreeOfConcurrency == 1 ? 1 : dependencyGraph.computeMaxWidth(degreeOfConcurrency, 1000);
|
||||||
|
queue.add(new BuildStarted(getCurrentProject(session).getName(), session.getProjects().size(), maxThreads));
|
||||||
}
|
}
|
||||||
|
|
||||||
private MavenProject getCurrentProject(MavenSession mavenSession) {
|
private MavenProject getCurrentProject(MavenSession mavenSession) {
|
||||||
|
@@ -0,0 +1,122 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 the original author or authors.
|
||||||
|
*
|
||||||
|
* 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.mvndaemon.mvnd.builder;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.mvndaemon.mvnd.builder.DependencyGraph.DagWidth;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
public class DagWidthTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSimpleGraph() {
|
||||||
|
//
|
||||||
|
// A B
|
||||||
|
// / | \ / \
|
||||||
|
// C D E F
|
||||||
|
// \/
|
||||||
|
// G
|
||||||
|
Map<String, List<String>> upstreams = new HashMap<>();
|
||||||
|
upstreams.put("A", Collections.emptyList());
|
||||||
|
upstreams.put("B", Collections.emptyList());
|
||||||
|
upstreams.put("C", Collections.singletonList("A"));
|
||||||
|
upstreams.put("D", Collections.singletonList("A"));
|
||||||
|
upstreams.put("E", Arrays.asList("A", "B"));
|
||||||
|
upstreams.put("F", Collections.singletonList("B"));
|
||||||
|
upstreams.put("G", Arrays.asList("D", "E"));
|
||||||
|
DependencyGraph<String> graph = newGraph(upstreams);
|
||||||
|
|
||||||
|
assertEquals(4, new DagWidth<>(graph).getMaxWidth(12));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSingle() {
|
||||||
|
//
|
||||||
|
// A
|
||||||
|
//
|
||||||
|
Map<String, List<String>> upstreams = new HashMap<>();
|
||||||
|
upstreams.put("A", Collections.emptyList());
|
||||||
|
DependencyGraph<String> graph = newGraph(upstreams);
|
||||||
|
|
||||||
|
assertEquals(1, new DagWidth<>(graph).getMaxWidth(12));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testLinear() {
|
||||||
|
//
|
||||||
|
// A -> B -> C -> D
|
||||||
|
//
|
||||||
|
Map<String, List<String>> upstreams = new HashMap<>();
|
||||||
|
upstreams.put("A", Collections.emptyList());
|
||||||
|
upstreams.put("B", Collections.singletonList("A"));
|
||||||
|
upstreams.put("C", Collections.singletonList("B"));
|
||||||
|
upstreams.put("D", Collections.singletonList("C"));
|
||||||
|
DependencyGraph<String> graph = newGraph(upstreams);
|
||||||
|
|
||||||
|
assertEquals(1, new DagWidth<>(graph).getMaxWidth(12));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHugeGraph() throws IOException {
|
||||||
|
Map<String, List<String>> upstreams = new HashMap<>();
|
||||||
|
try (BufferedReader r = new BufferedReader(
|
||||||
|
new InputStreamReader(getClass().getResourceAsStream("huge-graph.properties")))) {
|
||||||
|
r.lines().forEach(l -> {
|
||||||
|
int idxEq = l.indexOf(" = ");
|
||||||
|
if (!l.startsWith("#") && idxEq > 0) {
|
||||||
|
String k = l.substring(0, idxEq).trim();
|
||||||
|
String[] ups = l.substring(idxEq + 3).trim().split(",");
|
||||||
|
List<String> list = Stream.of(ups).map(String::trim)
|
||||||
|
.filter(s -> !s.isEmpty())
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
upstreams.put(k, list);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
DependencyGraph<String> graph = newGraph(upstreams);
|
||||||
|
|
||||||
|
DagWidth<String> w = new DagWidth<>(graph);
|
||||||
|
List<String> d = w.ensembleWithChildrenOf(
|
||||||
|
graph.getDownstreamProjects("org.apache.camel:camel").collect(Collectors.toList()),
|
||||||
|
"org.apache.camel:camel-parent");
|
||||||
|
|
||||||
|
assertEquals(12, w.getMaxWidth(12));
|
||||||
|
}
|
||||||
|
|
||||||
|
static <K> DependencyGraph<K> newGraph(Map<K, List<K>> upstreams) {
|
||||||
|
List<K> nodes = Stream.concat(upstreams.keySet().stream(), upstreams.values().stream().flatMap(List::stream))
|
||||||
|
.distinct()
|
||||||
|
.sorted()
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
Map<K, List<K>> downstreams = nodes.stream().collect(Collectors.toMap(k -> k, k -> new ArrayList<>()));
|
||||||
|
upstreams.forEach((k, ups) -> {
|
||||||
|
ups.forEach(up -> downstreams.get(up).add(k));
|
||||||
|
});
|
||||||
|
return new DependencyGraph<>(nodes, upstreams, downstreams);
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user