Skip to content

Commit 02cdc33

Browse files
committed
Concurrently lifecycle executor
1 parent 956ee16 commit 02cdc33

File tree

20 files changed

+2107
-13
lines changed

20 files changed

+2107
-13
lines changed

.github/workflows/maven_build_itself.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,4 +80,4 @@ jobs:
8080
run: |
8181
set +e
8282
export PATH=${{ env.TEMP_MAVEN_BIN_DIR }}:$PATH
83-
mvn verify site -e -B -V -DdistributionFileName=apache-maven -Preporting
83+
mvn verify site -e -B -V -DdistributionFileName=apache-maven -Preporting -T1

maven-core/pom.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,14 @@ under the License.
8787
<groupId>org.apache.maven</groupId>
8888
<artifactId>maven-api-spi</artifactId>
8989
</dependency>
90+
<dependency>
91+
<groupId>org.apache.maven</groupId>
92+
<artifactId>maven-jline</artifactId>
93+
</dependency>
94+
<dependency>
95+
<groupId>org.apache.maven</groupId>
96+
<artifactId>maven-slf4j-provider</artifactId>
97+
</dependency>
9098
<dependency>
9199
<groupId>org.apache.maven.resolver</groupId>
92100
<artifactId>maven-resolver-api</artifactId>

maven-core/src/main/java/org/apache/maven/execution/BuildFailure.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,19 @@ public class BuildFailure extends BuildSummary {
3939
* @param cause The cause of the build failure, may be {@code null}.
4040
*/
4141
public BuildFailure(MavenProject project, long time, Throwable cause) {
42-
super(project, time);
42+
this(project, time, time, cause);
43+
}
44+
45+
/**
46+
* Creates a new build summary for the specified project.
47+
*
48+
* @param project The project being summarized, must not be {@code null}.
49+
* @param execTime The exec time of the project in milliseconds.
50+
* @param wallTime The wall time of the project in milliseconds.
51+
* @param cause The cause of the build failure, may be {@code null}.
52+
*/
53+
public BuildFailure(MavenProject project, long execTime, long wallTime, Throwable cause) {
54+
super(project, execTime, wallTime);
4355
this.cause = cause;
4456
}
4557

maven-core/src/main/java/org/apache/maven/execution/BuildSuccess.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,17 @@ public class BuildSuccess extends BuildSummary {
3333
* @param time The build time of the project in milliseconds.
3434
*/
3535
public BuildSuccess(MavenProject project, long time) {
36-
super(project, time);
36+
super(project, time, time);
37+
}
38+
39+
/**
40+
* Creates a new build summary for the specified project.
41+
*
42+
* @param project The project being summarized, must not be {@code null}.
43+
* @param wallTime The wall time of the project in milliseconds.
44+
* @param execTime The exec time of the project in milliseconds.
45+
*/
46+
public BuildSuccess(MavenProject project, long wallTime, long execTime) {
47+
super(project, wallTime, execTime);
3748
}
3849
}

maven-core/src/main/java/org/apache/maven/execution/BuildSummary.java

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,12 @@ public abstract class BuildSummary {
3636
/**
3737
* The build time of the project in milliseconds.
3838
*/
39-
private final long time;
39+
private final long wallTime;
40+
41+
/**
42+
* The total amount of time spent for to run mojos in milliseconds.
43+
*/
44+
private final long execTime;
4045

4146
/**
4247
* Creates a new build summary for the specified project.
@@ -45,9 +50,21 @@ public abstract class BuildSummary {
4550
* @param time The build time of the project in milliseconds.
4651
*/
4752
protected BuildSummary(MavenProject project, long time) {
53+
this(project, time, time);
54+
}
55+
56+
/**
57+
* Creates a new build summary for the specified project.
58+
*
59+
* @param project The project being summarized, must not be {@code null}.
60+
* @param execTime The exec time of the project in milliseconds.
61+
* @param wallTime The wall time of the project in milliseconds.
62+
*/
63+
protected BuildSummary(MavenProject project, long execTime, long wallTime) {
4864
this.project = Objects.requireNonNull(project, "project cannot be null");
4965
// TODO Validate for < 0?
50-
this.time = time;
66+
this.execTime = execTime;
67+
this.wallTime = wallTime;
5168
}
5269

5370
/**
@@ -60,11 +77,20 @@ public MavenProject getProject() {
6077
}
6178

6279
/**
63-
* Gets the build time of the project in milliseconds.
80+
* Gets the wall time of the project in milliseconds.
6481
*
65-
* @return The build time of the project in milliseconds.
82+
* @return The wall time of the project in milliseconds.
6683
*/
6784
public long getTime() {
68-
return time;
85+
return execTime;
86+
}
87+
88+
/**
89+
* Gets the exec time of the project in milliseconds.
90+
*
91+
* @return The exec time of the project in milliseconds.
92+
*/
93+
public long getWallTime() {
94+
return wallTime;
6995
}
7096
}

maven-core/src/main/java/org/apache/maven/internal/impl/DefaultLifecycleRegistry.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ private static void addPhase(
156156
phase.phases().forEach(child -> addPhase(graph, ep1, ep2, child));
157157
}
158158

159+
@Named
159160
static class LifecycleWrapperProvider implements LifecycleProvider {
160161
private final Map<String, org.apache.maven.lifecycle.Lifecycle> lifecycles;
161162

maven-core/src/main/java/org/apache/maven/lifecycle/internal/CompoundProjectExecutionListener.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@
2424
import org.apache.maven.execution.ProjectExecutionListener;
2525
import org.apache.maven.lifecycle.LifecycleExecutionException;
2626

27-
class CompoundProjectExecutionListener implements ProjectExecutionListener {
27+
public class CompoundProjectExecutionListener implements ProjectExecutionListener {
2828
private final Collection<ProjectExecutionListener> listeners;
2929

30-
CompoundProjectExecutionListener(Collection<ProjectExecutionListener> listeners) {
30+
public CompoundProjectExecutionListener(Collection<ProjectExecutionListener> listeners) {
3131
this.listeners = listeners; // NB this is live injected collection
3232
}
3333

maven-core/src/main/java/org/apache/maven/lifecycle/internal/MojoExecutor.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,7 @@ private class ProjectLock implements AutoCloseable {
242242
warn(msg);
243243
acquiredAggregatorLock.lock();
244244
}
245+
/*
245246
if (!acquiredProjectLock.tryLock()) {
246247
Thread owner = acquiredProjectLock.getOwner();
247248
MojoDescriptor ownerMojo = owner != null ? mojos.get(owner) : null;
@@ -254,6 +255,7 @@ private class ProjectLock implements AutoCloseable {
254255
warn(msg);
255256
acquiredProjectLock.lock();
256257
}
258+
*/
257259
} else {
258260
acquiredAggregatorLock = null;
259261
acquiredProjectLock = null;
@@ -264,7 +266,7 @@ private class ProjectLock implements AutoCloseable {
264266
public void close() {
265267
// release the lock in the reverse order of the acquisition
266268
if (acquiredProjectLock != null) {
267-
acquiredProjectLock.unlock();
269+
// acquiredProjectLock.unlock();
268270
}
269271
if (acquiredAggregatorLock != null) {
270272
acquiredAggregatorLock.unlock();
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.maven.lifecycle.lfv4;
20+
21+
import java.util.ArrayList;
22+
import java.util.Collections;
23+
import java.util.HashMap;
24+
import java.util.HashSet;
25+
import java.util.LinkedHashMap;
26+
import java.util.List;
27+
import java.util.Map;
28+
import java.util.Optional;
29+
import java.util.Set;
30+
import java.util.function.Function;
31+
import java.util.stream.Collectors;
32+
import java.util.stream.Stream;
33+
34+
import org.apache.maven.plugin.MojoExecution;
35+
import org.apache.maven.project.MavenProject;
36+
37+
public class BuildPlan {
38+
39+
private final Map<MavenProject, Map<String, BuildStep>> plan = new LinkedHashMap<>();
40+
private final Map<MavenProject, List<MavenProject>> projects;
41+
private final Map<String, String> aliases = new HashMap<>();
42+
private volatile Set<String> duplicateIds;
43+
private volatile List<BuildStep> sortedNodes;
44+
45+
BuildPlan() {
46+
this.projects = null;
47+
}
48+
49+
public BuildPlan(Map<MavenProject, List<MavenProject>> projects) {
50+
this.projects = projects;
51+
}
52+
53+
public Map<MavenProject, List<MavenProject>> getAllProjects() {
54+
return projects;
55+
}
56+
57+
public Map<String, String> aliases() {
58+
return aliases;
59+
}
60+
61+
public Stream<MavenProject> projects() {
62+
return plan.keySet().stream();
63+
}
64+
65+
public void addProject(MavenProject project, Map<String, BuildStep> steps) {
66+
plan.put(project, steps);
67+
}
68+
69+
public void addStep(MavenProject project, String name, BuildStep step) {
70+
plan.get(project).put(name, step);
71+
}
72+
73+
public Stream<BuildStep> allSteps() {
74+
return plan.values().stream().flatMap(m -> m.values().stream());
75+
}
76+
77+
public Stream<BuildStep> steps(MavenProject project) {
78+
return Optional.ofNullable(plan.get(project))
79+
.map(m -> m.values().stream())
80+
.orElse(Stream.empty());
81+
}
82+
83+
public Optional<BuildStep> step(MavenProject project, String name) {
84+
return Optional.ofNullable(plan.get(project)).map(m -> m.get(name));
85+
}
86+
87+
public BuildStep requiredStep(MavenProject project, String name) {
88+
return step(project, name).get();
89+
}
90+
91+
// add a follow-up plan to this one
92+
public void then(BuildPlan step) {
93+
step.plan.forEach((k, v) -> plan.merge(k, v, this::merge));
94+
aliases.putAll(step.aliases);
95+
}
96+
97+
private Map<String, BuildStep> merge(Map<String, BuildStep> org, Map<String, BuildStep> add) {
98+
// all new phases should be added after the existing ones
99+
List<BuildStep> lasts =
100+
org.values().stream().filter(b -> b.successors.isEmpty()).collect(Collectors.toList());
101+
List<BuildStep> firsts =
102+
add.values().stream().filter(b -> b.predecessors.isEmpty()).collect(Collectors.toList());
103+
firsts.stream()
104+
.filter(addNode -> !org.containsKey(addNode.name))
105+
.forEach(addNode -> lasts.forEach(orgNode -> addNode.executeAfter(orgNode)));
106+
add.forEach((name, node) -> org.merge(name, node, this::merge));
107+
return org;
108+
}
109+
110+
private BuildStep merge(BuildStep node1, BuildStep node2) {
111+
node1.predecessors.addAll(node2.predecessors);
112+
node1.successors.addAll(node2.successors);
113+
node2.mojos.forEach((k, v) -> node1.mojos.merge(k, v, this::mergeMojos));
114+
return node1;
115+
}
116+
117+
private Map<String, MojoExecution> mergeMojos(Map<String, MojoExecution> l1, Map<String, MojoExecution> l2) {
118+
l2.forEach(l1::putIfAbsent);
119+
return l1;
120+
}
121+
122+
// gather artifactIds which are not unique so that the respective thread names can be extended with the groupId
123+
public Set<String> duplicateIds() {
124+
if (duplicateIds == null) {
125+
synchronized (this) {
126+
if (duplicateIds == null) {
127+
duplicateIds = projects()
128+
.map(MavenProject::getArtifactId)
129+
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
130+
.entrySet()
131+
.stream()
132+
.filter(p -> p.getValue() > 1)
133+
.map(Map.Entry::getKey)
134+
.collect(Collectors.toSet());
135+
}
136+
}
137+
}
138+
return duplicateIds;
139+
}
140+
141+
public List<BuildStep> sortedNodes() {
142+
if (sortedNodes == null) {
143+
synchronized (this) {
144+
if (sortedNodes == null) {
145+
List<BuildStep> sortedNodes = new ArrayList<>();
146+
Set<BuildStep> visited = new HashSet<>();
147+
// Visit each unvisited node
148+
allSteps().forEach(node -> visitNode(node, visited, sortedNodes));
149+
// Reverse the sorted nodes to get the correct order
150+
Collections.reverse(sortedNodes);
151+
this.sortedNodes = sortedNodes;
152+
}
153+
}
154+
}
155+
return sortedNodes;
156+
}
157+
158+
// Helper method to visit a node
159+
private static void visitNode(BuildStep node, Set<BuildStep> visited, List<BuildStep> sortedNodes) {
160+
if (visited.add(node)) {
161+
// For each successor of the current node, visit unvisited successors
162+
node.successors.forEach(successor -> visitNode(successor, visited, sortedNodes));
163+
sortedNodes.add(node);
164+
}
165+
}
166+
}

0 commit comments

Comments
 (0)