From 65da451ecd8df85f3aefec440919bcf678110b18 Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Fri, 10 Mar 2023 08:48:08 +0100 Subject: [PATCH] Switch back to takari smart builder (#805) --- daemon/pom.xml | 6 + .../mvnd/builder/DependencyGraph.java | 335 ----------------- .../mvnd/builder/ProjectComparator.java | 133 ------- .../mvnd/builder/ProjectExecutorService.java | 130 ------- .../mvnd/builder/ReactorBuildQueue.java | 122 ------ .../mvnd/builder/ReactorBuildStats.java | 199 ---------- .../mvndaemon/mvnd/builder/SmartBuilder.java | 167 --------- .../mvnd/builder/SmartBuilderImpl.java | 219 ----------- .../mvnd/daemon/ClientDispatcher.java | 2 +- .../org/mvndaemon/mvnd/daemon/Server.java | 2 +- .../builder/AbstractSmartBuilderTest.java | 40 -- .../mvndaemon/mvnd/builder/DagWidthTest.java | 346 ------------------ .../mvnd/builder/ProjectComparatorTest.java | 77 ---- .../builder/ProjectExecutorServiceTest.java | 136 ------- .../mvnd/builder/ReactorBuildQueueTest.java | 74 ---- .../builder/TestProjectDependencyGraph.java | 78 ---- dist-m39/src/main/provisio/maven-distro.xml | 3 + dist-m40/src/main/provisio/maven-distro.xml | 3 + .../src/test/resources/logback/logback.xml | 2 +- pom.xml | 7 + 20 files changed, 22 insertions(+), 2059 deletions(-) delete mode 100644 daemon/src/main/java/org/mvndaemon/mvnd/builder/DependencyGraph.java delete mode 100644 daemon/src/main/java/org/mvndaemon/mvnd/builder/ProjectComparator.java delete mode 100644 daemon/src/main/java/org/mvndaemon/mvnd/builder/ProjectExecutorService.java delete mode 100644 daemon/src/main/java/org/mvndaemon/mvnd/builder/ReactorBuildQueue.java delete mode 100644 daemon/src/main/java/org/mvndaemon/mvnd/builder/ReactorBuildStats.java delete mode 100644 daemon/src/main/java/org/mvndaemon/mvnd/builder/SmartBuilder.java delete mode 100644 daemon/src/main/java/org/mvndaemon/mvnd/builder/SmartBuilderImpl.java delete mode 100644 daemon/src/test/java/org/mvndaemon/mvnd/builder/AbstractSmartBuilderTest.java delete mode 100644 daemon/src/test/java/org/mvndaemon/mvnd/builder/DagWidthTest.java delete mode 100644 daemon/src/test/java/org/mvndaemon/mvnd/builder/ProjectComparatorTest.java delete mode 100644 daemon/src/test/java/org/mvndaemon/mvnd/builder/ProjectExecutorServiceTest.java delete mode 100644 daemon/src/test/java/org/mvndaemon/mvnd/builder/ReactorBuildQueueTest.java delete mode 100644 daemon/src/test/java/org/mvndaemon/mvnd/builder/TestProjectDependencyGraph.java diff --git a/daemon/pom.xml b/daemon/pom.xml index d4d235220..3e1ffbd89 100644 --- a/daemon/pom.xml +++ b/daemon/pom.xml @@ -54,6 +54,12 @@ plexus-interactivity-api + + io.takari.maven + takari-smart-builder + 0.6.2 + + ch.qos.logback diff --git a/daemon/src/main/java/org/mvndaemon/mvnd/builder/DependencyGraph.java b/daemon/src/main/java/org/mvndaemon/mvnd/builder/DependencyGraph.java deleted file mode 100644 index 49a9d5fba..000000000 --- a/daemon/src/main/java/org/mvndaemon/mvnd/builder/DependencyGraph.java +++ /dev/null @@ -1,335 +0,0 @@ -/* - * 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.mvndaemon.mvnd.builder; - -import java.io.IOException; -import java.io.Writer; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.function.Function; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.apache.maven.execution.MavenSession; -import org.apache.maven.execution.ProjectDependencyGraph; -import org.apache.maven.project.MavenProject; - -/** - * 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 - */ -public class DependencyGraph { - - private final List projects; - private final Map> upstreams; - private final Map> transitiveUpstreams; - private final Map> downstreams; - - @SuppressWarnings("unchecked") - public static DependencyGraph fromMaven(MavenSession session) { - Map data = session.getRequest().getData(); - DependencyGraph graph = (DependencyGraph) data.get(DependencyGraph.class.getName()); - if (graph == null) { - graph = fromMaven(session.getProjectDependencyGraph()); - data.put(DependencyGraph.class.getName(), graph); - } - return graph; - } - - static DependencyGraph fromMaven(ProjectDependencyGraph graph) { - final List projects = graph.getSortedProjects(); - Map> upstreams = - projects.stream().collect(Collectors.toMap(p -> p, p -> graph.getUpstreamProjects(p, false))); - Map> downstreams = - projects.stream().collect(Collectors.toMap(p -> p, p -> graph.getDownstreamProjects(p, false))); - return new DependencyGraph<>(Collections.unmodifiableList(projects), upstreams, downstreams); - } - - public DependencyGraph(List projects, Map> upstreams, Map> downstreams) { - this.projects = projects; - this.upstreams = upstreams; - this.downstreams = downstreams; - - this.transitiveUpstreams = new HashMap<>(); - projects.forEach(this::transitiveUpstreams); // topological ordering of projects matters - } - - DependencyGraph( - List projects, - Map> upstreams, - Map> downstreams, - Map> transitiveUpstreams) { - this.projects = projects; - this.upstreams = upstreams; - this.downstreams = downstreams; - this.transitiveUpstreams = transitiveUpstreams; - } - - public Stream getDownstreamProjects(K project) { - return downstreams.get(project).stream(); - } - - public Stream getUpstreamProjects(K project) { - return upstreams.get(project).stream(); - } - - public boolean isRoot(K project) { - return upstreams.get(project).isEmpty(); - } - - public Stream getProjects() { - return projects.stream(); - } - - public int computeMaxWidth(int max, long maxTimeMillis) { - return new DagWidth<>(this).getMaxWidth(max, maxTimeMillis); - } - - public void store(Function toString, Path path) { - try (Writer w = Files.newBufferedWriter(path)) { - store(toString, w); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - public void store(Function toString, Appendable w) { - getProjects().forEach(k -> { - try { - w.append(toString.apply(k)); - w.append(" = "); - w.append(getUpstreamProjects(k).map(toString).collect(Collectors.joining(","))); - w.append(System.lineSeparator()); - } catch (IOException e) { - throw new RuntimeException(e); - } - }); - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - store(k -> k.toString(), sb); - return sb.toString(); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((downstreams == null) ? 0 : downstreams.hashCode()); - result = prime * result + ((projects == null) ? 0 : projects.hashCode()); - result = prime * result + ((upstreams == null) ? 0 : upstreams.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (getClass() != obj.getClass()) return false; - @SuppressWarnings("unchecked") - DependencyGraph other = (DependencyGraph) obj; - if (downstreams == null) { - if (other.downstreams != null) return false; - } else if (!downstreams.equals(other.downstreams)) return false; - if (projects == null) { - if (other.projects != null) return false; - } else if (!projects.equals(other.projects)) return false; - if (upstreams == null) { - if (other.upstreams != null) return false; - } else if (!upstreams.equals(other.upstreams)) return false; - return true; - } - - /** - * Creates a new {@link DependencyGraph} which is a - * transitive reduction of this {@link DependencyGraph}. The reduction operation keeps the set of graph nodes - * unchanged and it reduces the set of edges in the following way: An edge {@code C -> A} is removed if an edge - * {@code C -> B} exists such that {@code A != B} and the set of nodes reachable from {@code B} contains {@code A}; - * otherwise the edge {@code C -> A} is kept in the reduced graph. - *

- * Examples: - * - *

-     * Original     Reduced
-     *
-     *    A           A
-     *   /|          /
-     *  B |         B
-     *   \|          \
-     *    C           C
-     *
-     *
-     *    A           A
-     *   /|\         /
-     *  B | |       B
-     *   \| |        \
-     *    C |         C
-     *     \|          \
-     *      D           D
-     *
-     * 
- * - * - * @return a transitive reduction of this {@link DependencyGraph} - */ - DependencyGraph reduce() { - final Map> newUpstreams = new HashMap<>(); - final Map> newDownstreams = new HashMap<>(); - for (K node : projects) { - final List oldNodeUpstreams = upstreams.get(node); - final List newNodeUpstreams; - newDownstreams.computeIfAbsent(node, k -> new ArrayList<>()); - if (oldNodeUpstreams.size() == 0) { - newNodeUpstreams = new ArrayList<>(oldNodeUpstreams); - } else if (oldNodeUpstreams.size() == 1) { - newNodeUpstreams = new ArrayList<>(oldNodeUpstreams); - newDownstreams - .computeIfAbsent(newNodeUpstreams.get(0), k -> new ArrayList<>()) - .add(node); - } else { - newNodeUpstreams = new ArrayList<>(oldNodeUpstreams.size()); - for (K leftNode : oldNodeUpstreams) { - if (oldNodeUpstreams.stream() - .filter(rightNode -> leftNode != rightNode) - .noneMatch(rightNode -> - transitiveUpstreams.get(rightNode).contains(leftNode))) { - - newNodeUpstreams.add(leftNode); - newDownstreams - .computeIfAbsent(leftNode, k -> new ArrayList<>()) - .add(node); - } - } - } - newUpstreams.put(node, newNodeUpstreams); - } - return new DependencyGraph(projects, newUpstreams, newDownstreams, transitiveUpstreams); - } - - /** - * Compute the set of nodes reachable from the given {@code node} through the {@code is upstream of} relation. The - * {@code node} itself is not a part of the returned set. - * - * @param node the node for which the transitive upstream should be computed - * @return the set of transitive upstreams - */ - Set transitiveUpstreams(K node) { - Set result = transitiveUpstreams.get(node); - if (result == null) { - final List firstOrderUpstreams = this.upstreams.get(node); - result = new HashSet<>(firstOrderUpstreams); - firstOrderUpstreams.stream().map(this::transitiveUpstreams).forEach(result::addAll); - transitiveUpstreams.put(node, result); - } - return result; - } - - static class DagWidth { - - private final DependencyGraph graph; - - public DagWidth(DependencyGraph graph) { - this.graph = graph.reduce(); - } - - 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 < graph.transitiveUpstreams.size()) { - // try inverted upstream bound - Map, Set> mapByUpstreams = new HashMap<>(); - graph.transitiveUpstreams.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 l = iterator.next(); - max = Math.max(max, l.size()); - } - return Math.min(max, maxmax); - } - - private class SubsetIterator implements Iterator> { - - final List> nexts = new ArrayList<>(); - final Set> visited = new HashSet<>(); - - public SubsetIterator(List roots) { - nexts.add(roots); - } - - @Override - public boolean hasNext() { - return !nexts.isEmpty(); - } - - @Override - public List next() { - List list = nexts.remove(0); - list.stream() - .map(node -> ensembleWithChildrenOf(list, node)) - .filter(visited::add) - .forEach(nexts::add); - return list; - } - } - - private List getRoots() { - return graph.getProjects().filter(graph::isRoot).collect(Collectors.toList()); - } - - List ensembleWithChildrenOf(List list, K node) { - final List result = Stream.concat( - list.stream().filter(k -> !Objects.equals(k, node)), - graph.getDownstreamProjects(node).filter(k -> graph.transitiveUpstreams.get(k).stream() - .noneMatch(k2 -> !Objects.equals(k2, node) && list.contains(k2)))) - .distinct() - .collect(Collectors.toList()); - return result; - } - } -} diff --git a/daemon/src/main/java/org/mvndaemon/mvnd/builder/ProjectComparator.java b/daemon/src/main/java/org/mvndaemon/mvnd/builder/ProjectComparator.java deleted file mode 100644 index 8b3c915b9..000000000 --- a/daemon/src/main/java/org/mvndaemon/mvnd/builder/ProjectComparator.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * 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.mvndaemon.mvnd.builder; - -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.atomic.AtomicLong; -import java.util.function.Function; -import java.util.function.ToLongFunction; - -import org.apache.maven.project.MavenProject; - -/** - * Project comparator (factory) that uses project build time to establish build order. - *

- * Internally, each project is assigned a weight, which is calculated as sum of project build time - * and maximum weight of any of the project's downstream dependencies. The project weights are - * calculated by recursively traversing project dependency graph starting from build root projects, - * i.e. projects that do not have any upstream dependencies. - *

- * Project build times are estimated based on values persisted during a previous build. Average - * build time is used for projects that do not have persisted build time. - *

- * If there are no persisted build times, all projects build times are assumed the same (arbitrary) - * value of 1. This means that the project with the longest downstream dependency trail will be - * built first. - *

- * Currently, historical build times are stored in - * ${session.request/baseDirectory}/.mvn/timing.properties file. The timings file is - * written only if ${session.request/baseDirectory}/.mvn directory is already present. - * - * File origin: - * https://github.com/takari/takari-smart-builder/blob/takari-smart-builder-0.6.1/src/main/java/io/takari/maven/builder/smart/ProjectComparator.java - */ -class ProjectComparator { - - public static Comparator create(DependencyGraph graph) { - return create0(graph, Collections.emptyMap(), ProjectComparator::id); - } - - static Comparator create0( - DependencyGraph dependencyGraph, - Map historicalServiceTimes, - Function toKey) { - final long defaultServiceTime = average(historicalServiceTimes.values()); - - final Map serviceTimes = new HashMap<>(); - - final Set rootProjects = new HashSet<>(); - dependencyGraph.getProjects().forEach(project -> { - long serviceTime = getServiceTime(historicalServiceTimes, project, defaultServiceTime, toKey); - serviceTimes.put(project, serviceTime); - if (dependencyGraph.isRoot(project)) { - rootProjects.add(project); - } - }); - - final Map projectWeights = calculateWeights(dependencyGraph, serviceTimes, rootProjects); - - return Comparator.comparingLong((ToLongFunction) projectWeights::get) - .thenComparing(toKey, String::compareTo) - .reversed(); - } - - private static long average(Collection values) { - return (long) - (values.stream().mapToLong(AtomicLong::longValue).average().orElse(1.0d)); - } - - private static long getServiceTime( - Map serviceTimes, K project, long defaultServiceTime, Function toKey) { - AtomicLong serviceTime = serviceTimes.get(toKey.apply(project)); - return serviceTime != null ? serviceTime.longValue() : defaultServiceTime; - } - - private static Map calculateWeights( - DependencyGraph dependencyGraph, Map serviceTimes, Collection rootProjects) { - Map weights = new HashMap<>(); - for (K rootProject : rootProjects) { - calculateWeights(dependencyGraph, serviceTimes, rootProject, weights); - } - return weights; - } - - /** - * Returns the maximum sum of build time along a path from the project to an exit project. An - * "exit project" is a project without downstream dependencies. - */ - private static long calculateWeights( - DependencyGraph dependencyGraph, Map serviceTimes, K project, Map weights) { - long weight = serviceTimes.get(project) - + dependencyGraph - .getDownstreamProjects(project) - .mapToLong(successor -> { - long successorWeight; - if (weights.containsKey(successor)) { - successorWeight = weights.get(successor); - } else { - successorWeight = calculateWeights(dependencyGraph, serviceTimes, successor, weights); - } - return successorWeight; - }) - .max() - .orElse(0); - weights.put(project, weight); - return weight; - } - - static String id(MavenProject project) { - return project.getGroupId() + ':' + project.getArtifactId() + ':' + project.getVersion(); - } -} diff --git a/daemon/src/main/java/org/mvndaemon/mvnd/builder/ProjectExecutorService.java b/daemon/src/main/java/org/mvndaemon/mvnd/builder/ProjectExecutorService.java deleted file mode 100644 index d7dc84310..000000000 --- a/daemon/src/main/java/org/mvndaemon/mvnd/builder/ProjectExecutorService.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * 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.mvndaemon.mvnd.builder; - -import java.util.Collection; -import java.util.Comparator; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Future; -import java.util.concurrent.FutureTask; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.PriorityBlockingQueue; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; - -import org.apache.maven.lifecycle.internal.BuildThreadFactory; -import org.apache.maven.project.MavenProject; - -/** - * {@link ThreadPoolExecutor} wrapper. - *

- * Uses {@link PriorityBlockingQueue} and provided {@link Comparator} to order queue - * {@link ProjectRunnable} tasks. - * - * File origin: - * https://github.com/takari/takari-smart-builder/blob/takari-smart-builder-0.6.1/src/main/java/io/takari/maven/builder/smart/ProjectExecutorService.java - */ -class ProjectExecutorService { - - private final ExecutorService executor; - private final BlockingQueue> completion = new LinkedBlockingQueue<>(); - private final Comparator taskComparator; - - public ProjectExecutorService(final int degreeOfConcurrency, final Comparator projectComparator) { - - this.taskComparator = Comparator.comparing(r -> ((ProjectRunnable) r).getProject(), projectComparator); - - final BlockingQueue executorWorkQueue = - new PriorityBlockingQueue<>(degreeOfConcurrency, taskComparator); - - executor = - new ThreadPoolExecutor( - degreeOfConcurrency, // corePoolSize - degreeOfConcurrency, // maximumPoolSize - 0L, - TimeUnit.MILLISECONDS, // keepAliveTime, unit - executorWorkQueue, // workQueue - new BuildThreadFactory() // threadFactory - ) { - - @Override - protected void beforeExecute(Thread t, Runnable r) { - ProjectExecutorService.this.beforeExecute(t, r); - } - }; - } - - public void submitAll(final Collection tasks) { - // when there are available worker threads, tasks are immediately executed, i.e. bypassed the - // ordered queued. need to sort tasks, such that submission order matches desired execution - // order - tasks.stream().sorted(taskComparator).map(ProjectFutureTask::new).forEach(executor::execute); - } - - /** - * Returns {@link MavenProject} corresponding to the next completed task, waiting if none are yet - * present. - */ - public MavenProject take() throws InterruptedException, ExecutionException { - return completion.take().get(); - } - - public void shutdown() { - executor.shutdown(); - } - - public void cancel() { - executor.shutdownNow(); - } - - // hook to allow pausing executor during unit tests - protected void beforeExecute(Thread t, Runnable r) {} - - // for testing purposes only - public void awaitShutdown() throws InterruptedException { - executor.shutdown(); - while (!executor.awaitTermination(5, TimeUnit.SECONDS)) - ; - } - - static interface ProjectRunnable extends Runnable { - public MavenProject getProject(); - } - - private class ProjectFutureTask extends FutureTask implements ProjectRunnable { - private ProjectRunnable task; - - public ProjectFutureTask(ProjectRunnable task) { - super(task, task.getProject()); - this.task = task; - } - - @Override - protected void done() { - completion.add(this); - } - - @Override - public MavenProject getProject() { - return task.getProject(); - } - } -} diff --git a/daemon/src/main/java/org/mvndaemon/mvnd/builder/ReactorBuildQueue.java b/daemon/src/main/java/org/mvndaemon/mvnd/builder/ReactorBuildQueue.java deleted file mode 100644 index a387c2fae..000000000 --- a/daemon/src/main/java/org/mvndaemon/mvnd/builder/ReactorBuildQueue.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * 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.mvndaemon.mvnd.builder; - -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; -import java.util.stream.Stream; - -import org.apache.maven.project.MavenProject; - -/** - * Reactor build queue manages reactor modules that are waiting for their upstream dependencies - * build to finish. - * - * File origin: - * https://github.com/takari/takari-smart-builder/blob/takari-smart-builder-0.6.1/src/main/java/io/takari/maven/builder/smart/ReactorBuildQueue.java - */ -class ReactorBuildQueue { - - private final DependencyGraph graph; - - private final Set rootProjects; - - private final Set projects; - - /** - * Projects waiting for other projects to finish - */ - private final Set blockedProjects; - - private final Set finishedProjects; - - public ReactorBuildQueue(Collection projects, DependencyGraph graph) { - this.graph = graph; - this.projects = new HashSet<>(); - this.rootProjects = new HashSet<>(); - this.blockedProjects = new HashSet<>(); - this.finishedProjects = new HashSet<>(); - projects.forEach(project -> { - this.projects.add(project); - if (this.graph.isRoot(project)) { - this.rootProjects.add(project); - } else { - this.blockedProjects.add(project); - } - }); - } - - /** - * Marks specified project as finished building. Returns, possible empty, set of project's - * downstream dependencies that become ready to build. - */ - public Set onProjectFinish(MavenProject project) { - finishedProjects.add(project); - Set downstreamProjects = new HashSet<>(); - getDownstreamProjects(project) - .filter(successor -> blockedProjects.contains(successor) && isProjectReady(successor)) - .forEach(successor -> { - blockedProjects.remove(successor); - downstreamProjects.add(successor); - }); - return downstreamProjects; - } - - public Stream getDownstreamProjects(MavenProject project) { - return graph.getDownstreamProjects(project); - } - - private boolean isProjectReady(MavenProject project) { - return graph.getUpstreamProjects(project).allMatch(finishedProjects::contains); - } - - /** - * Returns {@code true} when no more projects are left to schedule. - */ - public boolean isEmpty() { - return blockedProjects.isEmpty(); - } - - /** - * Returns reactor build root projects, that is, projects that do not have upstream dependencies. - */ - public Set getRootProjects() { - return rootProjects; - } - - public int getBlockedCount() { - return blockedProjects.size(); - } - - public int getFinishedCount() { - return finishedProjects.size(); - } - - public int getReadyCount() { - return projects.size() - blockedProjects.size() - finishedProjects.size(); - } - - public Set getReadyProjects() { - Set projects = new HashSet<>(this.projects); - projects.removeAll(blockedProjects); - projects.removeAll(finishedProjects); - return projects; - } -} diff --git a/daemon/src/main/java/org/mvndaemon/mvnd/builder/ReactorBuildStats.java b/daemon/src/main/java/org/mvndaemon/mvnd/builder/ReactorBuildStats.java deleted file mode 100644 index 1a00bb45d..000000000 --- a/daemon/src/main/java/org/mvndaemon/mvnd/builder/ReactorBuildStats.java +++ /dev/null @@ -1,199 +0,0 @@ -/* - * 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.mvndaemon.mvnd.builder; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Comparator; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicLong; -import java.util.function.Function; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import com.google.common.collect.ImmutableMap; -import org.apache.maven.project.MavenProject; - -/** - * File origin: - * https://github.com/takari/takari-smart-builder/blob/takari-smart-builder-0.6.1/src/main/java/io/takari/maven/builder/smart/ReactorBuildStats.java - */ -class ReactorBuildStats { - - /** - * Time, in nanoseconds, a worker thread was executing the project build lifecycle. In addition to - * Maven plugin goals execution includes any "overhead" time Maven spends resolving project - * dependencies, calculating build time and perform any post-execution cleanup and maintenance. - */ - private final Map serviceTimes; - /** - * Time, in nanoseconds, when the project was a bottleneck of entire build, i.e. when not all - * available CPU cores were utilized, presumably because the project build time and dependency - * structure prevented higher degree of parallelism. - */ - private final Map bottleneckTimes; - - private long startTime; - private long stopTime; - - private ReactorBuildStats(Map serviceTimes, Map bottleneckTimes) { - this.serviceTimes = ImmutableMap.copyOf(serviceTimes); - this.bottleneckTimes = ImmutableMap.copyOf(bottleneckTimes); - } - - private static String projectGAV(MavenProject project) { - return project.getGroupId() + ":" + project.getArtifactId() + ":" + project.getVersion(); - } - - public static ReactorBuildStats create(Collection projects) { - ImmutableMap.Builder serviceTimes = ImmutableMap.builder(); - ImmutableMap.Builder bottleneckTimes = ImmutableMap.builder(); - projects.stream().map(ReactorBuildStats::projectGAV).forEach(key -> { - serviceTimes.put(key, new AtomicLong()); - bottleneckTimes.put(key, new AtomicLong()); - }); - return new ReactorBuildStats(serviceTimes.build(), bottleneckTimes.build()); - } - - public void recordStart() { - this.startTime = System.nanoTime(); - } - - public void recordStop() { - this.stopTime = System.nanoTime(); - } - - public void recordServiceTime(MavenProject project, long durationNanos) { - AtomicLong serviceTime = serviceTimes.get(projectGAV(project)); - if (serviceTime == null) { - throw new IllegalStateException( - "Unknown project " + projectGAV(project) + ", found " + serviceTimes.keySet()); - } - serviceTime.addAndGet(durationNanos); - } - - public void recordBottlenecks(Set projects, int degreeOfConcurrency, long durationNanos) { - // only projects that result in single-threaded builds - if (projects.size() == 1) { - projects.forEach(p -> bottleneckTimes.get(projectGAV(p)).addAndGet(durationNanos)); - } - } - - // - // Reporting - // - - public long totalServiceTime(TimeUnit unit) { - long nanos = - serviceTimes.values().stream().mapToLong(AtomicLong::longValue).sum(); - return unit.convert(nanos, TimeUnit.NANOSECONDS); - } - - public long walltimeTime(TimeUnit unit) { - return unit.convert(stopTime - startTime, TimeUnit.NANOSECONDS); - } - - public String renderCriticalPath(DependencyGraph graph) { - return renderCriticalPath(graph, ReactorBuildStats::projectGAV); - } - - public String renderCriticalPath(DependencyGraph graph, Function toKey) { - StringBuilder result = new StringBuilder(); - - // render critical path - - long criticalPathServiceTime = 0; - result.append("Build critical path service times (and bottleneck** times):"); - for (K project : calculateCriticalPath(graph, toKey)) { - result.append('\n'); - String key = toKey.apply(project); - criticalPathServiceTime += serviceTimes.get(key).get(); - appendProjectTimes(result, key); - } - result.append( - String.format("\nBuild critical path total service time %s", formatDuration(criticalPathServiceTime))); - - // render bottleneck projects - - List bottleneckProjects = getBottleneckProjects(); - if (!bottleneckProjects.isEmpty()) { - long bottleneckTotalTime = 0; - result.append("\nBuild bottleneck projects service times (and bottleneck** times):"); - for (String bottleneck : bottleneckProjects) { - result.append('\n'); - bottleneckTotalTime += bottleneckTimes.get(bottleneck).get(); - appendProjectTimes(result, bottleneck); - } - result.append(String.format("\nBuild bottlenecks total time %s", formatDuration(bottleneckTotalTime))); - } - - result.append("\n** Bottlenecks are projects that limit build concurrency"); - result.append("\n removing bottlenecks improves overall build time"); - return result.toString(); - } - - private void appendProjectTimes(StringBuilder result, String project) { - final long serviceTime = serviceTimes.get(project).get(); - final long bottleneckTime = bottleneckTimes.get(project).get(); - result.append(String.format(" %-60s %s", project, formatDuration(serviceTime))); - if (bottleneckTime > 0) { - result.append(String.format(" (%s)", formatDuration(bottleneckTime))); - } - } - - private List getBottleneckProjects() { - Comparator comparator = (a, b) -> { - long ta = bottleneckTimes.get(a).longValue(); - long tb = bottleneckTimes.get(b).longValue(); - if (tb > ta) { - return 1; - } else if (tb < ta) { - return -1; - } - return 0; - }; - return bottleneckTimes.keySet().stream() // - .sorted(comparator) // - .filter(project -> bottleneckTimes.get(project).get() > 0) // - .collect(Collectors.toList()); - } - - private String formatDuration(long nanos) { - long secs = TimeUnit.NANOSECONDS.toSeconds(nanos); - return String.format("%5d s", secs); - } - - private List calculateCriticalPath(DependencyGraph graph, Function toKey) { - Comparator comparator = ProjectComparator.create0(graph, serviceTimes, toKey); - Stream rootProjects = graph.getProjects().filter(graph::isRoot); - List criticalPath = new ArrayList<>(); - K project = getCriticalProject(rootProjects, comparator); - do { - criticalPath.add(project); - } while ((project = getCriticalProject(graph.getDownstreamProjects(project), comparator)) != null); - return criticalPath; - } - - private K getCriticalProject(Stream projects, Comparator comparator) { - return projects.min(comparator).orElse(null); - } -} diff --git a/daemon/src/main/java/org/mvndaemon/mvnd/builder/SmartBuilder.java b/daemon/src/main/java/org/mvndaemon/mvnd/builder/SmartBuilder.java deleted file mode 100644 index 6cbd712c3..000000000 --- a/daemon/src/main/java/org/mvndaemon/mvnd/builder/SmartBuilder.java +++ /dev/null @@ -1,167 +0,0 @@ -/* - * 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.mvndaemon.mvnd.builder; - -import javax.inject.Inject; -import javax.inject.Named; -import javax.inject.Singleton; - -import java.util.AbstractMap; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; - -import org.apache.maven.execution.MavenSession; -import org.apache.maven.lifecycle.internal.LifecycleModuleBuilder; -import org.apache.maven.lifecycle.internal.ProjectBuildList; -import org.apache.maven.lifecycle.internal.ReactorBuildStatus; -import org.apache.maven.lifecycle.internal.ReactorContext; -import org.apache.maven.lifecycle.internal.TaskSegment; -import org.apache.maven.lifecycle.internal.builder.Builder; -import org.apache.maven.project.MavenProject; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Trivial Maven {@link Builder} implementation. All interesting stuff happens in - * {@link SmartBuilderImpl} . - * - * File origin: - * https://github.com/takari/takari-smart-builder/blob/takari-smart-builder-0.6.1/src/main/java/io/takari/maven/builder/smart/SmartBuilder.java - */ -@Singleton -@Named("smart") -public class SmartBuilder implements Builder { - - public static final String PROP_PROFILING = "smartbuilder.profiling"; - - private final Logger logger = LoggerFactory.getLogger(getClass()); - - private final LifecycleModuleBuilder moduleBuilder; - - private volatile SmartBuilderImpl builder; - private volatile boolean canceled; - - private static SmartBuilder INSTANCE; - - public static SmartBuilder cancel() { - SmartBuilder builder = INSTANCE; - if (builder != null) { - builder.doCancel(); - } - return builder; - } - - @Inject - public SmartBuilder(LifecycleModuleBuilder moduleBuilder) { - this.moduleBuilder = moduleBuilder; - INSTANCE = this; - } - - void doCancel() { - canceled = true; - SmartBuilderImpl b = builder; - if (b != null) { - b.cancel(); - } - } - - public void doneCancel() { - canceled = false; - } - - @Override - public synchronized void build( - final MavenSession session, - final ReactorContext reactorContext, - ProjectBuildList projectBuilds, - final List taskSegments, - ReactorBuildStatus reactorBuildStatus) - throws ExecutionException, InterruptedException { - - session.getRepositorySession().getData().set(ReactorBuildStatus.class, reactorBuildStatus); - - DependencyGraph graph = DependencyGraph.fromMaven(session); - - // log overall build info - final int degreeOfConcurrency = session.getRequest().getDegreeOfConcurrency(); - logger.info( - "Task segments : " + taskSegments.stream().map(Object::toString).collect(Collectors.joining(" "))); - logger.info("Build maximum degree of concurrency is " + degreeOfConcurrency); - logger.info("Total number of projects is " + session.getProjects().size()); - - // the actual build execution - List> allstats = new ArrayList<>(); - for (TaskSegment taskSegment : taskSegments) { - Set projects = - projectBuilds.getByTaskSegment(taskSegment).getProjects(); - if (canceled) { - return; - } - builder = new SmartBuilderImpl(moduleBuilder, session, reactorContext, taskSegment, projects, graph); - try { - ReactorBuildStats stats = builder.build(); - allstats.add(new AbstractMap.SimpleEntry<>(taskSegment, stats)); - } finally { - builder = null; - } - } - - if (session.getResult().hasExceptions()) { - // don't report stats of failed builds - return; - } - - // log stats of each task segment - for (Map.Entry entry : allstats) { - TaskSegment taskSegment = entry.getKey(); - ReactorBuildStats stats = entry.getValue(); - Set projects = - projectBuilds.getByTaskSegment(taskSegment).getProjects(); - - logger.debug("Task segment {}, number of projects {}", taskSegment, projects.size()); - - final long walltimeReactor = stats.walltimeTime(TimeUnit.NANOSECONDS); - final long walltimeService = stats.totalServiceTime(TimeUnit.NANOSECONDS); - final String effectiveConcurrency = String.format("%2.2f", ((double) walltimeService) / walltimeReactor); - logger.info( - "Segment walltime {} s, segment projects service time {} s, effective/maximum degree of concurrency {}/{}", - TimeUnit.NANOSECONDS.toSeconds(walltimeReactor), - TimeUnit.NANOSECONDS.toSeconds(walltimeService), - effectiveConcurrency, - degreeOfConcurrency); - - if (projects.size() > 1 && isProfiling(session)) { - logger.info(stats.renderCriticalPath(graph)); - } - } - } - - private boolean isProfiling(MavenSession session) { - String value = session.getUserProperties().getProperty(PROP_PROFILING); - if (value == null) { - value = session.getSystemProperties().getProperty(PROP_PROFILING); - } - return Boolean.parseBoolean(value); - } -} diff --git a/daemon/src/main/java/org/mvndaemon/mvnd/builder/SmartBuilderImpl.java b/daemon/src/main/java/org/mvndaemon/mvnd/builder/SmartBuilderImpl.java deleted file mode 100644 index 81cdc6bde..000000000 --- a/daemon/src/main/java/org/mvndaemon/mvnd/builder/SmartBuilderImpl.java +++ /dev/null @@ -1,219 +0,0 @@ -/* - * 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.mvndaemon.mvnd.builder; - -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; -import java.util.Set; -import java.util.concurrent.ExecutionException; -import java.util.stream.Collectors; - -import org.apache.maven.execution.BuildFailure; -import org.apache.maven.execution.BuildSuccess; -import org.apache.maven.execution.BuildSummary; -import org.apache.maven.execution.MavenSession; -import org.apache.maven.lifecycle.internal.LifecycleModuleBuilder; -import org.apache.maven.lifecycle.internal.ReactorContext; -import org.apache.maven.lifecycle.internal.TaskSegment; -import org.apache.maven.lifecycle.internal.builder.Builder; -import org.apache.maven.project.MavenProject; -import org.mvndaemon.mvnd.builder.ProjectExecutorService.ProjectRunnable; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Maven {@link Builder} implementation that schedules execution of the reactor modules on the build - * critical path first. Build critical path is estimated based on module build times collected - * during a previous build, or based on module's downstream dependency trail length, if no prior - * build time information is available. - * - * @author Brian Toal - */ -class SmartBuilderImpl { - - private final Logger logger = LoggerFactory.getLogger(SmartBuilder.class); - - // global components - private final LifecycleModuleBuilder lifecycleModuleBuilder; - - // session-level components - private final MavenSession rootSession; - private final ReactorContext reactorContext; - private final TaskSegment taskSegment; - - // - private final ReactorBuildQueue reactorBuildQueue; - private final ProjectExecutorService executor; - private final int degreeOfConcurrency; - - // - private final ReactorBuildStats stats; - - SmartBuilderImpl( - LifecycleModuleBuilder lifecycleModuleBuilder, - MavenSession session, - ReactorContext reactorContext, - TaskSegment taskSegment, - Set projects, - DependencyGraph graph) { - this.lifecycleModuleBuilder = lifecycleModuleBuilder; - this.rootSession = session; - this.reactorContext = reactorContext; - this.taskSegment = taskSegment; - - this.degreeOfConcurrency = session.getRequest().getDegreeOfConcurrency(); - - final Comparator projectComparator = ProjectComparator.create(graph); - - this.reactorBuildQueue = new ReactorBuildQueue(projects, graph); - this.executor = new ProjectExecutorService(degreeOfConcurrency, projectComparator); - - this.stats = ReactorBuildStats.create(projects); - } - - private static String projectGA(MavenProject project) { - return project.getGroupId() + ":" + project.getArtifactId(); - } - - public ReactorBuildStats build() throws ExecutionException, InterruptedException { - stats.recordStart(); - - Set rootProjects = reactorBuildQueue.getRootProjects(); - - // this is the main build loop - submitAll(rootProjects); - long timstampSubmit = System.nanoTime(); - int submittedCount = rootProjects.size(); - while (submittedCount > 0) { - Set bottlenecks = null; - if (submittedCount < degreeOfConcurrency) { - bottlenecks = reactorBuildQueue.getReadyProjects(); - } - - try { - MavenProject completedProject = executor.take(); - if (bottlenecks != null) { - stats.recordBottlenecks(bottlenecks, degreeOfConcurrency, System.nanoTime() - timstampSubmit); - } - logCompleted(completedProject); - Set readyProjects = reactorBuildQueue.onProjectFinish(completedProject); - submitAll(readyProjects); - timstampSubmit = System.nanoTime(); - submittedCount += (readyProjects.size() - 1); - - logBuildQueueStatus(); - } catch (ExecutionException e) { - // we get here when unhandled exception or error occurred on the worker thread - // this can be low-level system problem, like OOME, or runtime exception in maven code - // there is no meaningful recovery, so we shutdown and rethrow the exception - shutdown(); - throw e; - } - } - shutdown(); - - stats.recordStop(); - return stats; - } - - private void logBuildQueueStatus() { - int blockedCount = reactorBuildQueue.getBlockedCount(); - int finishedCount = reactorBuildQueue.getFinishedCount(); - int readyCount = reactorBuildQueue.getReadyCount(); - String runningProjects = ""; - if (readyCount < degreeOfConcurrency && blockedCount > 0) { - runningProjects = reactorBuildQueue.getReadyProjects().stream() - .map(SmartBuilderImpl::projectGA) - .collect(Collectors.joining(" ", "[", "]")); - } - logger.debug( - "Builder state: blocked={} finished={} ready-or-running={} {}", - blockedCount, - finishedCount, - readyCount, - runningProjects); - } - - private void logCompleted(MavenProject project) { - BuildSummary buildSummary = rootSession.getResult().getBuildSummary(project); - String message = "SKIPPED"; - if (buildSummary instanceof BuildSuccess) { - message = "SUCCESS"; - } else if (buildSummary instanceof BuildFailure) { - message = "FAILURE"; - } else if (buildSummary != null) { - logger.warn("Unexpected project build summary class {}", buildSummary.getClass()); - message = "UNKNOWN"; - } - logger.debug("{} build of project {}:{}", message, project.getGroupId(), project.getArtifactId()); - } - - private void shutdown() { - executor.shutdown(); - } - - public void cancel() { - executor.cancel(); - } - - private void submitAll(Set readyProjects) { - List tasks = new ArrayList<>(); - for (MavenProject project : readyProjects) { - tasks.add(new ProjectBuildTask(project)); - logger.debug("Ready {}:{}", project.getGroupId(), project.getArtifactId()); - } - executor.submitAll(tasks); - } - - /* package */ void buildProject(MavenProject project) { - logger.debug("STARTED build of project {}:{}", project.getGroupId(), project.getArtifactId()); - - try { - MavenSession copiedSession = rootSession.clone(); - lifecycleModuleBuilder.buildProject(copiedSession, rootSession, reactorContext, project, taskSegment); - } catch (RuntimeException ex) { - // preserve the xml stack trace, and the java cause chain - rootSession.getResult().addException(new RuntimeException(project.getName() + ": " + ex.getMessage(), ex)); - } - } - - class ProjectBuildTask implements ProjectRunnable { - private final MavenProject project; - - ProjectBuildTask(MavenProject project) { - this.project = project; - } - - @Override - public void run() { - final long start = System.nanoTime(); - try { - buildProject(project); - } finally { - stats.recordServiceTime(project, System.nanoTime() - start); - } - } - - @Override - public MavenProject getProject() { - return project; - } - } -} diff --git a/daemon/src/main/java/org/mvndaemon/mvnd/daemon/ClientDispatcher.java b/daemon/src/main/java/org/mvndaemon/mvnd/daemon/ClientDispatcher.java index 92bbf6314..f9bb805c1 100644 --- a/daemon/src/main/java/org/mvndaemon/mvnd/daemon/ClientDispatcher.java +++ b/daemon/src/main/java/org/mvndaemon/mvnd/daemon/ClientDispatcher.java @@ -26,6 +26,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.regex.Pattern; +import io.takari.maven.builder.smart.DependencyGraph; import org.apache.maven.execution.ExecutionEvent; import org.apache.maven.execution.MavenSession; import org.apache.maven.plugin.MojoExecution; @@ -33,7 +34,6 @@ import org.eclipse.aether.transfer.TransferEvent; import org.eclipse.aether.transfer.TransferEvent.EventType; import org.eclipse.aether.transfer.TransferEvent.RequestType; -import org.mvndaemon.mvnd.builder.DependencyGraph; import org.mvndaemon.mvnd.common.Message; import org.mvndaemon.mvnd.common.Message.BuildException; import org.mvndaemon.mvnd.common.Message.BuildStarted; diff --git a/daemon/src/main/java/org/mvndaemon/mvnd/daemon/Server.java b/daemon/src/main/java/org/mvndaemon/mvnd/daemon/Server.java index 0bbf27ae4..aeb10a831 100644 --- a/daemon/src/main/java/org/mvndaemon/mvnd/daemon/Server.java +++ b/daemon/src/main/java/org/mvndaemon/mvnd/daemon/Server.java @@ -50,8 +50,8 @@ import java.util.function.Predicate; import java.util.stream.Collectors; +import io.takari.maven.builder.smart.SmartBuilder; import org.apache.maven.cli.DaemonCli; -import org.mvndaemon.mvnd.builder.SmartBuilder; import org.mvndaemon.mvnd.common.DaemonConnection; import org.mvndaemon.mvnd.common.DaemonException; import org.mvndaemon.mvnd.common.DaemonExpirationStatus; diff --git a/daemon/src/test/java/org/mvndaemon/mvnd/builder/AbstractSmartBuilderTest.java b/daemon/src/test/java/org/mvndaemon/mvnd/builder/AbstractSmartBuilderTest.java deleted file mode 100644 index c6e32947a..000000000 --- a/daemon/src/test/java/org/mvndaemon/mvnd/builder/AbstractSmartBuilderTest.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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.mvndaemon.mvnd.builder; - -import java.util.Arrays; -import java.util.Collection; -import java.util.HashSet; - -import org.apache.maven.project.MavenProject; -import org.junit.jupiter.api.Assertions; - -abstract class AbstractSmartBuilderTest { - protected void assertProjects(Collection actual, MavenProject... expected) { - Assertions.assertEquals(new HashSet(Arrays.asList(expected)), new HashSet<>(actual)); - } - - protected MavenProject newProject(String artifactId) { - MavenProject project = new MavenProject(); - project.setGroupId("test"); - project.setArtifactId(artifactId); - project.setVersion("1"); - return project; - } -} diff --git a/daemon/src/test/java/org/mvndaemon/mvnd/builder/DagWidthTest.java b/daemon/src/test/java/org/mvndaemon/mvnd/builder/DagWidthTest.java deleted file mode 100644 index 2dd2fa1b2..000000000 --- a/daemon/src/test/java/org/mvndaemon/mvnd/builder/DagWidthTest.java +++ /dev/null @@ -1,346 +0,0 @@ -/* - * 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.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() { - DependencyGraph graph = newSimpleGraph(); - assertEquals(4, new DagWidth<>(graph).getMaxWidth(12)); - } - - /** - *

-     *   A   B
-     *  /|\ / \
-     * C D E   F
-     *  \|
-     *   G
-     * 
- */ - private DependencyGraph newSimpleGraph() { - return newGraph( - "A", Collections.emptyList(), - "B", Collections.emptyList(), - "C", Collections.singletonList("A"), - "D", Collections.singletonList("A"), - "E", Arrays.asList("A", "B"), - "F", Collections.singletonList("B"), - "G", Arrays.asList("D", "E")); - } - - @Test - void tripleLinearGraph() { - DependencyGraph graph = newTripleLinearGraph(); - assertEquals(1, new DagWidth<>(graph).getMaxWidth()); - } - - /** - *
-     *   A
-     *  /|
-     * B |
-     *  \|
-     *   C
-     * 
- */ - private DependencyGraph newTripleLinearGraph() { - return newGraph( - "A", Collections.emptyList(), - "B", Collections.singletonList("A"), - "C", Arrays.asList("A", "B")); - } - - @Test - void quadrupleLinearGraph() { - DependencyGraph graph = newQuadrupleLinearGraph(); - assertEquals(1, new DagWidth<>(graph).getMaxWidth()); - } - - /** - *
-     *   A
-     *  /|\
-     * B | |
-     *  \| |
-     *   C |
-     *    \|
-     *     D
-     * 
- */ - private DependencyGraph newQuadrupleLinearGraph() { - return newGraph( - "A", Collections.emptyList(), - "B", Collections.singletonList("A"), - "C", Arrays.asList("B", "A"), - "D", Arrays.asList("C", "A")); - } - - @Test - void quadrupleLinearGraph2() { - DependencyGraph graph = newQuadrupleLinearGraph2(); - assertEquals(1, new DagWidth<>(graph).getMaxWidth()); - } - - /** - *
-     *   A
-     *  /|\
-     * B | |
-     * |\| |
-     * | C |
-     *  \|/
-     *   D
-     * 
- */ - private DependencyGraph newQuadrupleLinearGraph2() { - return newGraph( - "A", Collections.emptyList(), - "B", Collections.singletonList("A"), - "C", Arrays.asList("B", "A"), - "D", Arrays.asList("B", "C", "A")); - } - - @Test - void multilevelSum() { - DependencyGraph graph = newMultilevelSumGraph(); - assertEquals(5, new DagWidth<>(graph).getMaxWidth()); - } - - /** - *
-     *     A
-     *    /|\
-     *   B C D
-     *  /|\ \|
-     * E F G H
-     * 
- */ - private DependencyGraph newMultilevelSumGraph() { - return newGraph( - "A", Collections.emptyList(), - "B", Collections.singletonList("A"), - "C", Collections.singletonList("A"), - "D", Collections.singletonList("A"), - "E", Collections.singletonList("B"), - "F", Collections.singletonList("B"), - "G", Collections.singletonList("B"), - "H", Arrays.asList("C", "D")); - } - - @Test - void wideGraph() { - DependencyGraph graph = newWideGraph(); - assertEquals(3, new DagWidth<>(graph).getMaxWidth()); - } - - /** - *
-     *     A
-     *    /|\
-     *   B C D
-     *       |
-     *       E
-     * 
- */ - private DependencyGraph newWideGraph() { - return newGraph( - "A", Collections.emptyList(), - "B", Collections.singletonList("A"), - "C", Collections.singletonList("A"), - "D", Collections.singletonList("A"), - "E", Collections.singletonList("D")); - } - - @Test - void testSingle() { - DependencyGraph graph = newSingleGraph(); - - assertEquals(1, new DagWidth<>(graph).getMaxWidth(12)); - } - - /** - *
-     * A
-     * 
- */ - private DependencyGraph newSingleGraph() { - return newGraph("A", Collections.emptyList()); - } - - @Test - void testLinear() { - DependencyGraph graph = newLinearGraph(); - assertEquals(1, new DagWidth<>(graph).getMaxWidth(12)); - } - - /** - *
-     * A
-     *         |
-     *         B
-     *         |
-     *         C
-     *         |
-     *         D
-     * 
- */ - private DependencyGraph newLinearGraph() { - return newGraph( - "A", Collections.emptyList(), - "B", Collections.singletonList("A"), - "C", Collections.singletonList("B"), - "D", Collections.singletonList("C")); - } - - @Test - public void testHugeGraph() { - DependencyGraph graph = newHugeGraph(); - - DagWidth w = new DagWidth<>(graph); - List d = w.ensembleWithChildrenOf( - graph.getDownstreamProjects("org.apache.camel:camel").collect(Collectors.toList()), - "org.apache.camel:camel-parent"); - - assertEquals(12, w.getMaxWidth(12)); - } - - private DependencyGraph newHugeGraph() { - Map> 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 list = Stream.of(ups) - .map(String::trim) - .filter(s -> !s.isEmpty()) - .collect(Collectors.toList()); - upstreams.put(k, list); - } - }); - } catch (IOException e) { - throw new RuntimeException(e); - } - return newGraph(upstreams); - } - - @Test - public void reduce() { - assertSameReduced(newSimpleGraph()); - - assertReduced( - newTripleLinearGraph(), - "A", - Collections.emptyList(), - "B", - Collections.singletonList("A"), - "C", - Arrays.asList("B")); - - assertReduced( - newQuadrupleLinearGraph(), - "A", - Collections.emptyList(), - "B", - Collections.singletonList("A"), - "C", - Arrays.asList("B"), - "D", - Arrays.asList("C")); - - assertReduced( - newQuadrupleLinearGraph2(), - "A", - Collections.emptyList(), - "B", - Collections.singletonList("A"), - "C", - Arrays.asList("B"), - "D", - Arrays.asList("C")); - - assertSameReduced(newMultilevelSumGraph()); - - assertSameReduced(newWideGraph()); - - assertSameReduced(newSingleGraph()); - - assertSameReduced(newLinearGraph()); - } - - @Test - public void testToString() { - DependencyGraph graph = newSingleGraph(); - assertEquals("A = " + System.lineSeparator(), graph.toString()); - } - - @SuppressWarnings("unchecked") - static DependencyGraph newGraph(Object... upstreams) { - final Map> upstreamsMap = new HashMap<>(); - for (int i = 0; i < upstreams.length; i++) { - upstreamsMap.put((String) upstreams[i++], (List) upstreams[i]); - } - return newGraph(upstreamsMap); - } - - static DependencyGraph newGraph(Map> upstreams) { - List nodes = Stream.concat( - upstreams.keySet().stream(), upstreams.values().stream().flatMap(List::stream)) - .distinct() - .sorted() - .collect(Collectors.toList()); - Map> 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); - } - - static void assertReduced(DependencyGraph graph, Object... expectedUpstreams) { - final DependencyGraph reduced = graph.reduce(); - final DependencyGraph expectedGraph = newGraph(expectedUpstreams); - assertEquals(expectedGraph, reduced); - } - - static void assertSameReduced(DependencyGraph graph) { - final DependencyGraph reduced = graph.reduce(); - assertEquals(graph, reduced); - } -} diff --git a/daemon/src/test/java/org/mvndaemon/mvnd/builder/ProjectComparatorTest.java b/daemon/src/test/java/org/mvndaemon/mvnd/builder/ProjectComparatorTest.java deleted file mode 100644 index 7657dcdc2..000000000 --- a/daemon/src/test/java/org/mvndaemon/mvnd/builder/ProjectComparatorTest.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * 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.mvndaemon.mvnd.builder; - -import java.util.Comparator; -import java.util.HashMap; -import java.util.PriorityQueue; -import java.util.Queue; -import java.util.concurrent.atomic.AtomicLong; - -import org.apache.maven.project.MavenProject; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -import static org.mvndaemon.mvnd.builder.ProjectComparator.id; - -public class ProjectComparatorTest extends AbstractSmartBuilderTest { - - @Test - public void testPriorityQueueOrder() { - MavenProject a = newProject("a"), b = newProject("b"), c = newProject("c"); - TestProjectDependencyGraph graph = new TestProjectDependencyGraph(a, b, c); - graph.addDependency(b, a); - DependencyGraph dp = DependencyGraph.fromMaven(graph); - - Comparator cmp = ProjectComparator.create0(dp, new HashMap<>(), ProjectComparator::id); - - Queue queue = new PriorityQueue<>(3, cmp); - queue.add(a); - queue.add(b); - queue.add(c); - - Assertions.assertEquals(a, queue.poll()); - Assertions.assertEquals(c, queue.poll()); - Assertions.assertEquals(b, queue.poll()); - } - - @Test - public void testPriorityQueueOrder_historicalServiceTimes() { - MavenProject a = newProject("a"), b = newProject("b"), c = newProject("c"); - TestProjectDependencyGraph graph = new TestProjectDependencyGraph(a, b, c); - graph.addDependency(b, a); - DependencyGraph dp = DependencyGraph.fromMaven(graph); - - HashMap serviceTimes = new HashMap<>(); - serviceTimes.put(id(a), new AtomicLong(1L)); - serviceTimes.put(id(b), new AtomicLong(1L)); - serviceTimes.put(id(c), new AtomicLong(3L)); - - Comparator cmp = ProjectComparator.create0(dp, serviceTimes, ProjectComparator::id); - - Queue queue = new PriorityQueue<>(3, cmp); - queue.add(a); - queue.add(b); - queue.add(c); - - Assertions.assertEquals(c, queue.poll()); - Assertions.assertEquals(a, queue.poll()); - Assertions.assertEquals(b, queue.poll()); - } -} diff --git a/daemon/src/test/java/org/mvndaemon/mvnd/builder/ProjectExecutorServiceTest.java b/daemon/src/test/java/org/mvndaemon/mvnd/builder/ProjectExecutorServiceTest.java deleted file mode 100644 index f1ad7da59..000000000 --- a/daemon/src/test/java/org/mvndaemon/mvnd/builder/ProjectExecutorServiceTest.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * 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.mvndaemon.mvnd.builder; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.concurrent.atomic.AtomicLong; - -import com.google.common.util.concurrent.Monitor; -import org.apache.maven.project.MavenProject; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.mvndaemon.mvnd.builder.ProjectExecutorService.ProjectRunnable; - -import static org.mvndaemon.mvnd.builder.ProjectComparator.id; - -public class ProjectExecutorServiceTest extends AbstractSmartBuilderTest { - - @Test - public void testBuildOrder() throws Exception { - final MavenProject a = newProject("a"); - final MavenProject b = newProject("b"); - final MavenProject c = newProject("c"); - TestProjectDependencyGraph graph = new TestProjectDependencyGraph(a, b, c); - graph.addDependency(b, a); - DependencyGraph dp = DependencyGraph.fromMaven(graph); - - HashMap serviceTimes = new HashMap<>(); - serviceTimes.put(id(a), new AtomicLong(1L)); - serviceTimes.put(id(b), new AtomicLong(1L)); - serviceTimes.put(id(c), new AtomicLong(3L)); - - Comparator cmp = ProjectComparator.create0(dp, serviceTimes, ProjectComparator::id); - - PausibleProjectExecutorService executor = new PausibleProjectExecutorService(1, cmp); - - final List executed = new ArrayList<>(); - - class TestProjectRunnable implements ProjectRunnable { - private final MavenProject project; - - TestProjectRunnable(MavenProject project) { - this.project = project; - } - - @Override - public void run() { - executed.add(project); - } - - @Override - public MavenProject getProject() { - return project; - } - } - - // the executor has single work thread and is paused - // first task execution is blocked because the executor is paused - // the subsequent tasks are queued and thus queue order can be asserted - - // this one gets stuck on the worker thread - executor.submitAll(Collections.singleton(new TestProjectRunnable(a))); - - // these are queued and ordered - executor.submitAll( - Arrays.asList(new TestProjectRunnable(a), new TestProjectRunnable(b), new TestProjectRunnable(c))); - - executor.resume(); - executor.awaitShutdown(); - - Assertions.assertEquals(Arrays.asList(a, c, a, b), executed); - } - - // copy&paste from ThreadPoolExecutor javadoc (use of Guava is a nice touch there) - private static class PausibleProjectExecutorService extends org.mvndaemon.mvnd.builder.ProjectExecutorService { - - private final Monitor monitor = new Monitor(); - private boolean isPaused = true; - private final Monitor.Guard paused = new Monitor.Guard(monitor) { - @Override - public boolean isSatisfied() { - return isPaused; - } - }; - - private final Monitor.Guard notPaused = new Monitor.Guard(monitor) { - @Override - public boolean isSatisfied() { - return !isPaused; - } - }; - - public PausibleProjectExecutorService(int degreeOfConcurrency, Comparator projectComparator) { - super(degreeOfConcurrency, projectComparator); - } - - @Override - protected void beforeExecute(Thread t, Runnable r) { - monitor.enterWhenUninterruptibly(notPaused); - try { - monitor.waitForUninterruptibly(notPaused); - } finally { - monitor.leave(); - } - } - - public void resume() { - monitor.enterIf(paused); - try { - isPaused = false; - } finally { - monitor.leave(); - } - } - } -} diff --git a/daemon/src/test/java/org/mvndaemon/mvnd/builder/ReactorBuildQueueTest.java b/daemon/src/test/java/org/mvndaemon/mvnd/builder/ReactorBuildQueueTest.java deleted file mode 100644 index 0de13b046..000000000 --- a/daemon/src/test/java/org/mvndaemon/mvnd/builder/ReactorBuildQueueTest.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * 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.mvndaemon.mvnd.builder; - -import org.apache.maven.project.MavenProject; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -public class ReactorBuildQueueTest extends AbstractSmartBuilderTest { - - @Test - public void testBasic() { - MavenProject a = newProject("a"), b = newProject("b"), c = newProject("c"); - TestProjectDependencyGraph graph = new TestProjectDependencyGraph(a, b, c); - graph.addDependency(b, a); - DependencyGraph dp = DependencyGraph.fromMaven(graph); - - ReactorBuildQueue schl = new ReactorBuildQueue(graph.getSortedProjects(), dp); - - assertProjects(schl.getRootProjects(), a, c); - Assertions.assertFalse(schl.isEmpty()); - - assertProjects(schl.onProjectFinish(a), b); - Assertions.assertTrue(schl.isEmpty()); - } - - @Test - public void testNoDependencies() { - MavenProject a = newProject("a"), b = newProject("b"), c = newProject("c"); - TestProjectDependencyGraph graph = new TestProjectDependencyGraph(a, b, c); - DependencyGraph dp = DependencyGraph.fromMaven(graph); - - ReactorBuildQueue schl = new ReactorBuildQueue(graph.getSortedProjects(), dp); - - assertProjects(schl.getRootProjects(), a, b, c); - Assertions.assertTrue(schl.isEmpty()); - } - - @Test - public void testMultipleUpstreamDependencies() { - MavenProject a = newProject("a"), b = newProject("b"), c = newProject("c"); - TestProjectDependencyGraph graph = new TestProjectDependencyGraph(a, b, c); - graph.addDependency(b, a); - graph.addDependency(b, c); - DependencyGraph dp = DependencyGraph.fromMaven(graph); - - ReactorBuildQueue schl = new ReactorBuildQueue(graph.getSortedProjects(), dp); - - assertProjects(schl.getRootProjects(), a, c); - Assertions.assertFalse(schl.isEmpty()); - - assertProjects(schl.onProjectFinish(a), new MavenProject[0]); - Assertions.assertFalse(schl.isEmpty()); - - assertProjects(schl.onProjectFinish(c), b); - Assertions.assertTrue(schl.isEmpty()); - } -} diff --git a/daemon/src/test/java/org/mvndaemon/mvnd/builder/TestProjectDependencyGraph.java b/daemon/src/test/java/org/mvndaemon/mvnd/builder/TestProjectDependencyGraph.java deleted file mode 100644 index 47d6a104f..000000000 --- a/daemon/src/test/java/org/mvndaemon/mvnd/builder/TestProjectDependencyGraph.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * 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.mvndaemon.mvnd.builder; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import com.google.common.collect.ArrayListMultimap; -import com.google.common.collect.ListMultimap; -import org.apache.maven.execution.ProjectDependencyGraph; -import org.apache.maven.project.MavenProject; -import org.junit.jupiter.api.Assertions; - -public class TestProjectDependencyGraph implements ProjectDependencyGraph { - - private final List projects = new ArrayList(); - - private final ListMultimap downstream = ArrayListMultimap.create(); - - private final ListMultimap upstream = ArrayListMultimap.create(); - - public TestProjectDependencyGraph(MavenProject... projects) { - if (projects != null) { - this.projects.addAll(Arrays.asList(projects)); - } - } - - @Override - public List getAllProjects() { - return projects; - } - - @Override - public List getSortedProjects() { - return projects; - } - - @Override - public List getDownstreamProjects(MavenProject project, boolean transitive) { - Assertions.assertFalse(transitive, "not implemented"); - return downstream.get(project); - } - - @Override - public List getUpstreamProjects(MavenProject project, boolean transitive) { - Assertions.assertFalse(transitive, "not implemented"); - return upstream.get(project); - } - - public void addProject(MavenProject project) { - projects.add(project); - } - - public void addDependency(MavenProject from, MavenProject to) { - // 'from' depends on 'to' - // 'from' is a downstream dependency of 'to' - // 'to' is upstream dependency of 'from' - this.upstream.put(from, to); - this.downstream.put(to, from); - } -} diff --git a/dist-m39/src/main/provisio/maven-distro.xml b/dist-m39/src/main/provisio/maven-distro.xml index db3c73ff9..b08acbf1c 100644 --- a/dist-m39/src/main/provisio/maven-distro.xml +++ b/dist-m39/src/main/provisio/maven-distro.xml @@ -34,6 +34,9 @@ + + + diff --git a/dist-m40/src/main/provisio/maven-distro.xml b/dist-m40/src/main/provisio/maven-distro.xml index 05b775a3f..3a83d8bc0 100644 --- a/dist-m40/src/main/provisio/maven-distro.xml +++ b/dist-m40/src/main/provisio/maven-distro.xml @@ -34,6 +34,9 @@ + + + diff --git a/integration-tests/src/test/resources/logback/logback.xml b/integration-tests/src/test/resources/logback/logback.xml index 1878ed1dd..573dc0785 100644 --- a/integration-tests/src/test/resources/logback/logback.xml +++ b/integration-tests/src/test/resources/logback/logback.xml @@ -36,7 +36,7 @@ - + diff --git a/pom.xml b/pom.xml index 577083206..f93506250 100644 --- a/pom.xml +++ b/pom.xml @@ -115,6 +115,7 @@ 3.3.0 1.4.20 1.0 + 0.6.2 @@ -341,6 +342,12 @@
+ + + io.takari.maven + takari-smart-builder + ${takari-smart-builder.version} +