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));
         });
     }
-     */
 }

Reply via email to