afterLinks = mojoDescriptor.getMojoDescriptorV4().getAfterLinks();
+ if (afterLinks == null || afterLinks.isEmpty()) {
+ return;
+ }
+ for (AfterLink afterLink : afterLinks) {
+ String targetPhase = afterLink.getPhase();
+ String type = afterLink.getType();
+ if ("PROJECT".equals(type)) {
+ // Same-project ordering: this phase starts after target phase completes
+ plan.step(project, AFTER + targetPhase)
+ .ifPresent(targetAfter -> plan.requiredStep(project, BEFORE + resolvedPhase)
+ .executeAfter(targetAfter));
+ } else if ("DEPENDENCIES".equals(type)) {
+ // Cross-project ordering: this phase starts after each dependency's target phase completes
+ String scope = afterLink.getScope();
+ for (MavenProject dep :
+ filterByScope(project, plan.getAllProjects().get(project), scope)) {
+ plan.step(dep, AFTER + targetPhase)
+ .ifPresent(depAfter -> plan.requiredStep(project, BEFORE + resolvedPhase)
+ .executeAfter(depAfter));
+ }
+ } else if ("CHILDREN".equals(type)) {
+ // Parent-child ordering: bidirectional coordination with child modules
+ BuildStep before = plan.requiredStep(project, BEFORE + resolvedPhase);
+ BuildStep after = plan.requiredStep(project, AFTER + resolvedPhase);
+ if (project.getCollectedProjects() != null) {
+ project.getCollectedProjects().forEach(child -> {
+ plan.step(child, BEFORE + targetPhase).ifPresent(before::executeBefore);
+ plan.step(child, AFTER + targetPhase).ifPresent(after::executeAfter);
+ });
+ }
+ }
+ }
+ }
+
+ /**
+ * Filters upstream projects by dependency scope. If the scope is null or empty,
+ * all upstream projects are returned. Otherwise, only projects that the given
+ * project depends on with a matching scope are included.
+ *
+ * Matching is exact on the dependency's declared scope string (e.g. "compile",
+ * "provided", "test"). Maven's default dependency scope is "compile" (when no
+ * scope is declared), so a null scope in the model is treated as "compile" for
+ * matching purposes. Note that this does not perform path-scope
+ * resolution — for example, filtering by "compile" will not include
+ * "provided"-scoped dependencies even though they contribute to
+ * {@code PathScope.MAIN_COMPILE}. This keeps the filter simple and predictable;
+ * broader scope-aware filtering can be added in a follow-up if needed.
+ *
+ * @param project the project whose dependencies to check
+ * @param upstreamProjects the list of upstream reactor projects
+ * @param scope the dependency scope to filter by, or null/empty for all
+ * @return the filtered list of upstream projects
+ */
+ static List filterByScope(
+ MavenProject project, List upstreamProjects, String scope) {
+ if (scope == null || scope.isEmpty()) {
+ return upstreamProjects;
+ }
+ return upstreamProjects.stream()
+ .filter(dep -> project.getDependencies().stream()
+ .anyMatch(d -> dep.getGroupId().equals(d.getGroupId())
+ && dep.getArtifactId().equals(d.getArtifactId())
+ && scope.equals(d.getScope() != null ? d.getScope() : "compile")))
+ .collect(Collectors.toList());
+ }
+
protected BuildPlan computeForkPlan(BuildStep step, MojoExecution execution, BuildPlan buildPlan) {
MojoDescriptor mojoDescriptor = execution.getMojoDescriptor();
PluginDescriptor pluginDescriptor = mojoDescriptor.getPluginDescriptor();
@@ -951,8 +1031,8 @@ public BuildPlan calculateLifecycleMappings(
if (pointer instanceof Lifecycle.DependenciesPointer) {
// For dependencies: ensure current project's phase starts after dependency's phase completes
// Example: project's compile starts after dependency's package completes
- // TODO: String scope = ((Lifecycle.DependenciesPointer) pointer).scope();
- projects.get(project)
+ String scope = ((Lifecycle.DependenciesPointer) pointer).scope();
+ filterByScope(project, projects.get(project), scope)
.forEach(p -> plan.step(p, AFTER + n2).ifPresent(before::executeAfter));
} else if (pointer instanceof Lifecycle.ChildrenPointer) {
// For children: ensure bidirectional phase coordination
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 1881f2dcbda1..38c7912f9fa2 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,10 +26,14 @@
import java.util.stream.Stream;
import org.apache.maven.internal.impl.DefaultLifecycleRegistry;
+import org.apache.maven.model.Dependency;
import org.apache.maven.plugin.MojoExecution;
import org.apache.maven.project.MavenProject;
import org.junit.jupiter.api.Test;
+import static org.apache.maven.api.Lifecycle.AFTER;
+import static org.apache.maven.api.Lifecycle.BEFORE;
+import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -129,6 +133,181 @@ private BuildPlan calculateLifecycleMappings(Map
+ * This is a real constraint: in the V4 lifecycle, "compile" and "resources" are
+ * parallel siblings (compile depends on sources, not resources), so the @After
+ * link creates a genuine ordering edge that doesn't exist naturally.
+ */
+ @Test
+ void testAfterLinkProjectOrdering() {
+ MavenProject project = new MavenProject();
+ project.setCollectedProjects(List.of());
+ Map> projects = Collections.singletonMap(project, Collections.emptyList());
+
+ BuildPlan plan = calculateLifecycleMappings(projects, "package");
+
+ // Simulate @After(phase="resources", type=PROJECT) on a mojo bound to "compile"
+ // This means: compile's BEFORE step must wait for resources' AFTER step
+ // This is a real constraint since compile and resources are parallel in the lifecycle
+ BuildStep compileBefore = plan.requiredStep(project, BEFORE + "compile");
+ BuildStep resourcesAfter = plan.requiredStep(project, AFTER + "resources");
+ compileBefore.executeAfter(resourcesAfter);
+
+ // Verify: compile is now a successor of resources (via the after link)
+ assertIsSuccessor(resourcesAfter, compileBefore);
+ }
+
+ /**
+ * Tests that DEPENDENCIES-type @After link ordering constraints work correctly.
+ * Simulates what {@code applyAfterLinks} does for {@code @After(phase="ready", type=DEPENDENCIES)}.
+ */
+ @Test
+ void testAfterLinkDependenciesOrdering() {
+ MavenProject p1 = new MavenProject();
+ p1.setArtifactId("p1");
+ p1.setCollectedProjects(List.of());
+ MavenProject p2 = new MavenProject();
+ p2.setArtifactId("p2");
+ p2.setCollectedProjects(List.of());
+ Map> projects = new HashMap<>();
+ projects.put(p1, Collections.emptyList());
+ projects.put(p2, Collections.singletonList(p1));
+
+ BuildPlan plan = calculateLifecycleMappings(projects, "package");
+
+ // Simulate @After(phase="ready", type=DEPENDENCIES, scope="compile") on p2's compile phase
+ // This means: p2's compile BEFORE must wait for p1's ready AFTER
+ BuildStep p2CompileBefore = plan.requiredStep(p2, BEFORE + "compile");
+ BuildStep p1ReadyAfter = plan.requiredStep(p1, AFTER + "ready");
+
+ // Apply the DEPENDENCIES link (same logic as applyAfterLinks)
+ for (MavenProject dep : projects.get(p2)) {
+ plan.step(dep, AFTER + "ready").ifPresent(p2CompileBefore::executeAfter);
+ }
+
+ // Verify: p2's compile is now a successor of p1's ready
+ assertIsSuccessor(p1ReadyAfter, p2CompileBefore);
+ }
+
+ /**
+ * Tests that CHILDREN-type @After link ordering constraints work correctly.
+ * Simulates what {@code applyAfterLinks} does for {@code @After(phase="package", type=CHILDREN)}.
+ */
+ @Test
+ void testAfterLinkChildrenOrdering() {
+ MavenProject child = new MavenProject();
+ child.setArtifactId("child");
+ child.setCollectedProjects(List.of());
+ MavenProject parent = new MavenProject();
+ parent.setArtifactId("parent");
+ parent.setCollectedProjects(List.of(child));
+ Map> projects = Map.of(parent, List.of(), child, List.of());
+
+ BuildPlan plan = calculateLifecycleMappings(projects, "install");
+
+ // Simulate @After(phase="package", type=CHILDREN) on parent's install phase
+ // This means: parent waits for children's package before its install completes
+ BuildStep parentInstallBefore = plan.requiredStep(parent, BEFORE + "install");
+ BuildStep parentInstallAfter = plan.requiredStep(parent, AFTER + "install");
+
+ // Apply the CHILDREN link (same logic as applyAfterLinks)
+ parent.getCollectedProjects().forEach(c -> {
+ plan.step(c, BEFORE + "package").ifPresent(parentInstallBefore::executeBefore);
+ plan.step(c, AFTER + "package").ifPresent(parentInstallAfter::executeAfter);
+ });
+
+ // Verify: parent's install after waits for child's package after
+ BuildStep childPackageAfter = plan.requiredStep(child, AFTER + "package");
+ assertIsSuccessor(childPackageAfter, parentInstallAfter);
+ }
+
+ /**
+ * Tests that {@code filterByScope} returns all upstream projects when scope is null or empty.
+ */
+ @Test
+ void testFilterByScopeNullReturnsAll() {
+ MavenProject p1 = createProjectWithId("g", "p1");
+ MavenProject p2 = createProjectWithId("g", "p2");
+ List upstream = List.of(p1, p2);
+
+ MavenProject consumer = new MavenProject();
+ assertEquals(upstream, BuildPlanExecutor.BuildContext.filterByScope(consumer, upstream, null));
+ assertEquals(upstream, BuildPlanExecutor.BuildContext.filterByScope(consumer, upstream, ""));
+ }
+
+ /**
+ * Tests that {@code filterByScope} filters upstream projects by exact scope match,
+ * treating null-scoped dependencies as "compile" (Maven default).
+ */
+ @Test
+ void testFilterByScopeMatchesExact() {
+ MavenProject compileDep = createProjectWithId("g", "compile-dep");
+ MavenProject providedDep = createProjectWithId("g", "provided-dep");
+ MavenProject testDep = createProjectWithId("g", "test-dep");
+ MavenProject nullScopeDep = createProjectWithId("g", "null-scope-dep");
+ List upstream = List.of(compileDep, providedDep, testDep, nullScopeDep);
+
+ MavenProject consumer = new MavenProject();
+ consumer.getDependencies().add(createDependency("g", "compile-dep", "compile"));
+ consumer.getDependencies().add(createDependency("g", "provided-dep", "provided"));
+ consumer.getDependencies().add(createDependency("g", "test-dep", "test"));
+ consumer.getDependencies().add(createDependency("g", "null-scope-dep", null));
+
+ // "compile" matches explicit compile + null-scoped (Maven default is compile)
+ List compileFiltered =
+ BuildPlanExecutor.BuildContext.filterByScope(consumer, upstream, "compile");
+ assertEquals(2, compileFiltered.size());
+ assertTrue(compileFiltered.contains(compileDep));
+ assertTrue(compileFiltered.contains(nullScopeDep));
+
+ // "provided" matches only provided-scoped
+ List providedFiltered =
+ BuildPlanExecutor.BuildContext.filterByScope(consumer, upstream, "provided");
+ assertEquals(1, providedFiltered.size());
+ assertTrue(providedFiltered.contains(providedDep));
+
+ // "test" matches only test-scoped
+ List testFiltered = BuildPlanExecutor.BuildContext.filterByScope(consumer, upstream, "test");
+ assertEquals(1, testFiltered.size());
+ assertTrue(testFiltered.contains(testDep));
+ }
+
+ /**
+ * Tests that {@code filterByScope} excludes upstream projects not declared as dependencies.
+ */
+ @Test
+ void testFilterByScopeExcludesNonDependencies() {
+ MavenProject dep = createProjectWithId("g", "dep");
+ MavenProject nonDep = createProjectWithId("g", "non-dep");
+ List upstream = List.of(dep, nonDep);
+
+ MavenProject consumer = new MavenProject();
+ consumer.getDependencies().add(createDependency("g", "dep", "compile"));
+
+ List filtered = BuildPlanExecutor.BuildContext.filterByScope(consumer, upstream, "compile");
+ assertEquals(1, filtered.size());
+ assertTrue(filtered.contains(dep));
+ }
+
+ private static MavenProject createProjectWithId(String groupId, String artifactId) {
+ MavenProject project = new MavenProject();
+ project.setGroupId(groupId);
+ project.setArtifactId(artifactId);
+ project.setCollectedProjects(List.of());
+ return project;
+ }
+
+ private static Dependency createDependency(String groupId, String artifactId, String scope) {
+ Dependency dep = new Dependency();
+ dep.setGroupId(groupId);
+ dep.setArtifactId(artifactId);
+ dep.setScope(scope);
+ return dep;
+ }
+
/*
@Test
void testPlugins() {
diff --git a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng12534AfterAnnotationTest.java b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng12534AfterAnnotationTest.java
new file mode 100644
index 000000000000..1a4b5800cf4c
--- /dev/null
+++ b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng12534AfterAnnotationTest.java
@@ -0,0 +1,70 @@
+/*
+ * 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.apache.maven.it;
+
+import java.io.File;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * This is a test for
+ * MNG-12534.
+ *
+ * Verifies that {@code afterLinks} in a V2 plugin descriptor
+ * ({@code http://maven.apache.org/PLUGIN/2.0.0}) are correctly loaded
+ * and processed by the build plan executor without errors.
+ *
+ * The test plugin carries a handcrafted V2 plugin.xml with a
+ * {@code } of type PROJECT. The build plan executor
+ * must parse the descriptor, create the ordering edges, and
+ * execute the mojo successfully.
+ */
+class MavenITmng12534AfterAnnotationTest extends AbstractMavenIntegrationTestCase {
+
+ MavenITmng12534AfterAnnotationTest() {
+ super("[4.0.0-rc-6,)");
+ }
+
+ /**
+ * Verify that a plugin with {@code afterLinks} in its V2 descriptor
+ * is correctly loaded and the mojo executes under the concurrent builder.
+ */
+ @Test
+ void testAfterLinksLoadedAndMojoExecutes() throws Exception {
+ File testDir = extractResources("/mng-12534-after-annotation");
+
+ // Step 1: install the test plugin with a handcrafted V2 plugin descriptor
+ Verifier pluginVerifier = newVerifier(new File(testDir, "plugin").getAbsolutePath());
+ pluginVerifier.addCliArgument("install");
+ pluginVerifier.execute();
+ pluginVerifier.verifyErrorFreeLog();
+
+ // Step 2: build the consumer project using the concurrent builder
+ Verifier consumerVerifier = newVerifier(new File(testDir, "consumer").getAbsolutePath());
+ consumerVerifier.addCliArgument("-b");
+ consumerVerifier.addCliArgument("concurrent");
+ consumerVerifier.addCliArgument("compile");
+ consumerVerifier.execute();
+ consumerVerifier.verifyErrorFreeLog();
+
+ // Verify the mojo actually executed
+ consumerVerifier.verifyTextInLog("[MNG-12534] touch goal executed - afterLinks wired correctly");
+ consumerVerifier.verifyFilePresent("target/touch.txt");
+ }
+}
diff --git a/its/core-it-suite/src/test/resources/mng-12534-after-annotation/consumer/pom.xml b/its/core-it-suite/src/test/resources/mng-12534-after-annotation/consumer/pom.xml
new file mode 100644
index 000000000000..23e8c5616163
--- /dev/null
+++ b/its/core-it-suite/src/test/resources/mng-12534-after-annotation/consumer/pom.xml
@@ -0,0 +1,29 @@
+
+
+ 4.0.0
+
+ org.apache.maven.its.mng12534
+ consumer
+ 1.0-SNAPSHOT
+
+ MNG-12534 Consumer
+ Consumes the test plugin with afterLinks
+
+
+
+
+ org.apache.maven.its.mng12534
+ mng12534-plugin
+ 1.0-SNAPSHOT
+
+
+ touch
+
+ touch
+
+
+
+
+
+
+
diff --git a/its/core-it-suite/src/test/resources/mng-12534-after-annotation/plugin/pom.xml b/its/core-it-suite/src/test/resources/mng-12534-after-annotation/plugin/pom.xml
new file mode 100644
index 000000000000..a1b3cc173238
--- /dev/null
+++ b/its/core-it-suite/src/test/resources/mng-12534-after-annotation/plugin/pom.xml
@@ -0,0 +1,48 @@
+
+
+ 4.0.0
+
+ org.apache.maven.its.mng12534
+ mng12534-plugin
+ 1.0-SNAPSHOT
+ maven-plugin
+ MNG-12534 Test Plugin
+ V4 test plugin with afterLinks in V2 descriptor to verify @After annotation wiring
+
+
+ 17
+ 4.0.0-SNAPSHOT
+
+
+
+
+ org.apache.maven
+ maven-api-core
+ ${mavenVersion}
+ provided
+
+
+ org.apache.maven
+ maven-api-di
+ ${mavenVersion}
+ provided
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-plugin-plugin
+ 4.0.0-beta-1
+
+
+ default-descriptor
+ none
+
+
+
+
+
+
diff --git a/its/core-it-suite/src/test/resources/mng-12534-after-annotation/plugin/src/main/java/org/apache/maven/its/mng12534/TouchMojo.java b/its/core-it-suite/src/test/resources/mng-12534-after-annotation/plugin/src/main/java/org/apache/maven/its/mng12534/TouchMojo.java
new file mode 100644
index 000000000000..01ac60c4ef16
--- /dev/null
+++ b/its/core-it-suite/src/test/resources/mng-12534-after-annotation/plugin/src/main/java/org/apache/maven/its/mng12534/TouchMojo.java
@@ -0,0 +1,63 @@
+/*
+ * 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.apache.maven.its.mng12534;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import org.apache.maven.api.Project;
+import org.apache.maven.api.di.Inject;
+import org.apache.maven.api.plugin.Log;
+import org.apache.maven.api.plugin.annotations.After;
+import org.apache.maven.api.plugin.annotations.Mojo;
+
+/**
+ * V4 Mojo that creates a marker file to prove the goal was executed.
+ * The {@code @After} annotation declares that this mojo must run after
+ * the "resources" phase. This is a real ordering constraint because
+ * in the V4 lifecycle, "compile" and "resources" are parallel siblings
+ * (compile depends on sources, not resources) — so without this
+ * {@code @After} link, compile could start before resources completes.
+ *
+ * The handcrafted V2 plugin descriptor mirrors this as an
+ * {@code } element — once maven-plugin-tools learns to
+ * scan {@code @After}, the descriptor will be generated automatically.
+ */
+@Mojo(name = "touch", defaultPhase = "compile")
+@After(phase = "resources")
+public class TouchMojo implements org.apache.maven.api.plugin.Mojo {
+
+ @Inject
+ private Log log;
+
+ @Inject
+ private Project project;
+
+ @Override
+ public void execute() throws Exception {
+ log.info("[MNG-12534] touch goal executed - afterLinks wired correctly");
+ Path targetDir = project.getBasedir().resolve("target");
+ Files.createDirectories(targetDir);
+ Path touchFile = targetDir.resolve("touch.txt");
+ if (!Files.exists(touchFile)) {
+ Files.createFile(touchFile);
+ }
+ }
+}
diff --git a/its/core-it-suite/src/test/resources/mng-12534-after-annotation/plugin/src/main/java/org/apache/maven/its/mng12534/TouchMojoFactory.java b/its/core-it-suite/src/test/resources/mng-12534-after-annotation/plugin/src/main/java/org/apache/maven/its/mng12534/TouchMojoFactory.java
new file mode 100644
index 000000000000..b4d1f3d58350
--- /dev/null
+++ b/its/core-it-suite/src/test/resources/mng-12534-after-annotation/plugin/src/main/java/org/apache/maven/its/mng12534/TouchMojoFactory.java
@@ -0,0 +1,32 @@
+/*
+ * 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.apache.maven.its.mng12534;
+
+import org.apache.maven.api.di.Named;
+
+/**
+ * DI factory for the TouchMojo. Normally maven-plugin-plugin generates this class
+ * at build time, but since we use a handcrafted V2 plugin descriptor (to include
+ * afterLinks), we must provide the factory manually.
+ *
+ * The @Named value must match MojoDescriptor.getRoleHint() at runtime:
+ * "groupId:artifactId:version:goal"
+ */
+@Named("org.apache.maven.its.mng12534:mng12534-plugin:1.0-SNAPSHOT:touch")
+public class TouchMojoFactory extends TouchMojo {}
diff --git a/its/core-it-suite/src/test/resources/mng-12534-after-annotation/plugin/src/main/resources/META-INF/maven/org.apache.maven.api.di.Inject b/its/core-it-suite/src/test/resources/mng-12534-after-annotation/plugin/src/main/resources/META-INF/maven/org.apache.maven.api.di.Inject
new file mode 100644
index 000000000000..450d5b29cc31
--- /dev/null
+++ b/its/core-it-suite/src/test/resources/mng-12534-after-annotation/plugin/src/main/resources/META-INF/maven/org.apache.maven.api.di.Inject
@@ -0,0 +1 @@
+org.apache.maven.its.mng12534.TouchMojoFactory
diff --git a/its/core-it-suite/src/test/resources/mng-12534-after-annotation/plugin/src/main/resources/META-INF/maven/plugin.xml b/its/core-it-suite/src/test/resources/mng-12534-after-annotation/plugin/src/main/resources/META-INF/maven/plugin.xml
new file mode 100644
index 000000000000..1b4009ec3016
--- /dev/null
+++ b/its/core-it-suite/src/test/resources/mng-12534-after-annotation/plugin/src/main/resources/META-INF/maven/plugin.xml
@@ -0,0 +1,23 @@
+
+
+ MNG-12534 Test Plugin
+ org.apache.maven.its.mng12534
+ mng12534-plugin
+ 1.0-SNAPSHOT
+ mng12534
+
+
+ touch
+ compile
+ org.apache.maven.its.mng12534.TouchMojo
+ java
+ Creates a marker file; has afterLinks to test @After wiring
+
+
+ resources
+ PROJECT
+
+
+
+
+