This is an automated email from the ASF dual-hosted git repository.
gnodet pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/maven.git
The following commit(s) were added to refs/heads/master by this push:
new da75950645 [MNG-8604] Fix concurrent builder crash when reactor module
is used as plugin (#11820)
da75950645 is described below
commit da75950645de8ea9ea0142557a469620fb1e2c0f
Author: Guillaume Nodet <[email protected]>
AuthorDate: Tue Jun 16 22:21:59 2026 +0200
[MNG-8604] Fix concurrent builder crash when reactor module is used as
plugin (#11820)
When using `--builder concurrent`, if a project references a plugin
that is also a module in the reactor (e.g., CXF's codegen-plugin),
the build crashes with "Step $plan$ not found". This happens because
reactor plugin handling in `calculateLifecycleMappings()` calls
`plan.requiredStep(project, PLAN)` before the PLAN steps are created
(they are created later in `buildInitialPlan()`).
Move the reactor plugin ordering logic to `buildInitialPlan()`, after
the PLAN and READY steps have been created. Also simplify the plugin
resolution in `calculateLifecycleMappings()` to only resolve
non-reactor plugins.
---
.../internal/concurrent/BuildPlanExecutor.java | 27 ++++++++++-------
.../internal/concurrent/BuildPlanCreatorTest.java | 34 +++++++++++-----------
2 files changed, 34 insertions(+), 27 deletions(-)
diff --git
a/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/concurrent/BuildPlanExecutor.java
b/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/concurrent/BuildPlanExecutor.java
index 39afdf255c..460d15bb32 100644
---
a/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/concurrent/BuildPlanExecutor.java
+++
b/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/concurrent/BuildPlanExecutor.java
@@ -268,6 +268,21 @@ public BuildPlan buildInitialPlan(List<TaskSegment>
taskSegments) {
Stream.of(pplan, setup, teardown).forEach(step ->
plan.addStep(project, step.name, step));
}
+ // Handle reactor plugins: if a project uses a plugin that is also
a reactor module,
+ // the project's PLAN step must wait for the plugin project's
READY step.
+ // This must be done after PLAN/READY steps are created above.
+ Map<String, MavenProject> reactorGavs =
+
plan.getAllProjects().keySet().stream().collect(Collectors.toMap(BuildPlanExecutor::gav,
p -> p));
+ for (MavenProject project : plan.getAllProjects().keySet()) {
+ for (Plugin plugin : project.getBuild().getPlugins()) {
+ MavenProject pluginProject = reactorGavs.get(gav(plugin));
+ if (pluginProject != null && pluginProject != project) {
+ plan.step(project, PLAN)
+ .ifPresent(pp -> plan.step(pluginProject,
READY).ifPresent(pp::executeAfter));
+ }
+ }
+ }
+
return plan;
}
@@ -968,24 +983,16 @@ public BuildPlan calculateLifecycleMappings(
});
});
- // Keep projects in reactors by GAV
+ // Eagerly resolve all non-reactor plugins in parallel
Map<String, MavenProject> reactorGavs =
projects.keySet().stream().collect(Collectors.toMap(BuildPlanExecutor::gav, p
-> p));
-
- // Go through all plugins
List<Runnable> toResolve = new ArrayList<>();
projects.keySet()
.forEach(project ->
project.getBuild().getPlugins().forEach(plugin -> {
- MavenProject pluginProject =
reactorGavs.get(gav(plugin));
- if (pluginProject != null) {
- // In order to plan the project, we need all its
plugins...
- plan.requiredStep(project,
PLAN).executeAfter(plan.requiredStep(pluginProject, READY));
- } else {
+ if (reactorGavs.get(gav(plugin)) == null) {
toResolve.add(() -> resolvePlugin(session,
project.getRemotePluginRepositories(), plugin));
}
}));
-
- // Eagerly resolve all plugins in parallel
toResolve.parallelStream().forEach(Runnable::run);
// Keep track of phase aliases
diff --git
a/impl/maven-core/src/test/java/org/apache/maven/lifecycle/internal/concurrent/BuildPlanCreatorTest.java
b/impl/maven-core/src/test/java/org/apache/maven/lifecycle/internal/concurrent/BuildPlanCreatorTest.java
index 1881f2dcbd..e32c7b3a36 100644
---
a/impl/maven-core/src/test/java/org/apache/maven/lifecycle/internal/concurrent/BuildPlanCreatorTest.java
+++
b/impl/maven-core/src/test/java/org/apache/maven/lifecycle/internal/concurrent/BuildPlanCreatorTest.java
@@ -26,6 +26,7 @@
import java.util.stream.Stream;
import org.apache.maven.internal.impl.DefaultLifecycleRegistry;
+import org.apache.maven.model.Plugin;
import org.apache.maven.plugin.MojoExecution;
import org.apache.maven.project.MavenProject;
import org.junit.jupiter.api.Test;
@@ -129,33 +130,35 @@ private BuildPlan
calculateLifecycleMappings(Map<MavenProject, List<MavenProject
return context.calculateLifecycleMappings(projects, phase);
}
- /*
@Test
- void testPlugins() {
- DefaultLifecycleRegistry lifecycles =
- new DefaultLifecycleRegistry(Collections.emptyList(),
Collections.emptyMap());
- BuildPlanCreator builder = new BuildPlanCreator(null, null, null,
null, null, lifecycles);
+ void testReactorPlugin() {
MavenProject p1 = new MavenProject();
p1.setGroupId("g");
p1.setArtifactId("p1");
- p1.getBuild().getPlugins().add(new
Plugin(org.apache.maven.api.model.Plugin.newBuilder()
- .groupId("g").artifactId("p2")
- .
- .build()))
+ p1.setVersion("1.0");
+ p1.setCollectedProjects(List.of());
+ Plugin plugin = new Plugin();
+ plugin.setGroupId("g");
+ plugin.setArtifactId("p2");
+ plugin.setVersion("1.0");
+ p1.getBuild().addPlugin(plugin);
+
MavenProject p2 = new MavenProject();
p2.setGroupId("g");
p2.setArtifactId("p2");
+ p2.setVersion("1.0");
+ p2.setCollectedProjects(List.of());
Map<MavenProject, List<MavenProject>> projects = new HashMap<>();
projects.put(p1, Collections.emptyList());
projects.put(p2, Collections.singletonList(p1));
- Lifecycle lifecycle = lifecycles.require("default");
- BuildPlan plan = builder.calculateLifecycleMappings(null, projects,
lifecycle, "verify");
- plan.then(builder.calculateLifecycleMappings(null, projects,
lifecycle, "install"));
+
+ BuildPlan plan = calculateLifecycleMappings(projects, "verify");
+ plan.then(calculateLifecycleMappings(projects, "install"));
Stream.of(p1, p2).forEach(project -> {
- plan.requiredStep(project, "post:resources").addMojo(new
MojoExecution(null), 0);
- plan.requiredStep(project, "post:test-resources").addMojo(new
MojoExecution(null), 0);
+ plan.requiredStep(project, "after:resources").addMojo(new
MojoExecution(null), 0);
+ plan.requiredStep(project, "after:test-resources").addMojo(new
MojoExecution(null), 0);
plan.requiredStep(project, "compile").addMojo(new
MojoExecution(null), 0);
plan.requiredStep(project, "test-compile").addMojo(new
MojoExecution(null), 0);
plan.requiredStep(project, "test").addMojo(new
MojoExecution(null), 0);
@@ -163,8 +166,6 @@ void testPlugins() {
plan.requiredStep(project, "install").addMojo(new
MojoExecution(null), 0);
});
- plan.condense();
-
new BuildPlanLogger() {
@Override
protected void mojo(Consumer<String> writer, MojoExecution
mojoExecution) {}
@@ -175,5 +176,4 @@ protected void mojo(Consumer<String> writer, MojoExecution
mojoExecution) {}
pred -> assertTrue(plan.step(pred.project,
pred.name).isPresent(), "Phase not present: " + pred));
});
}
- */
}