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 extends ProjectRunnable> 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 @@