diff --git a/src/main/java/org/apache/maven/plugins/help/DescribeMojo.java b/src/main/java/org/apache/maven/plugins/help/DescribeMojo.java index 7cf0ec8d..95ed9d21 100644 --- a/src/main/java/org/apache/maven/plugins/help/DescribeMojo.java +++ b/src/main/java/org/apache/maven/plugins/help/DescribeMojo.java @@ -27,7 +27,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.StringTokenizer; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -37,6 +36,8 @@ import org.apache.maven.lifecycle.Lifecycle; import org.apache.maven.lifecycle.internal.MojoDescriptorCreator; import org.apache.maven.lifecycle.mapping.LifecycleMapping; +import org.apache.maven.lifecycle.mapping.LifecycleMojo; +import org.apache.maven.lifecycle.mapping.LifecyclePhase; import org.apache.maven.model.Plugin; import org.apache.maven.model.building.ModelBuildingRequest; import org.apache.maven.plugin.MavenPluginManager; @@ -633,14 +634,16 @@ private boolean describeCommand(StringBuilder descriptionBuffer) throws MojoExec throw new MojoExecutionException("The given phase '" + cmd + "' is an unknown phase."); } - Map defaultLifecyclePhases = lifecycleMappings - .get(project.getPackaging()) - .getLifecycles() - .get("default") - .getPhases(); List phases = lifecycle.getPhases(); - if (lifecycle.getDefaultPhases() == null) { + if (lifecycle.getDefaultLifecyclePhases() == null + || lifecycle.getDefaultLifecyclePhases().isEmpty()) { + Map defaultLifecyclePhases = lifecycleMappings + .get(project.getPackaging()) + .getLifecycles() + .get("default") + .getLifecyclePhases(); + descriptionBuffer.append("'").append(cmd); descriptionBuffer .append("' is a phase corresponding to this plugin:") @@ -662,17 +665,13 @@ private boolean describeCommand(StringBuilder descriptionBuffer) throws MojoExec descriptionBuffer.append(LS); for (String key : phases) { descriptionBuffer.append("* ").append(key).append(": "); - String value = defaultLifecyclePhases.get(key); - if (value != null && !value.isEmpty()) { - for (StringTokenizer tok = new StringTokenizer(value, ","); tok.hasMoreTokens(); ) { - descriptionBuffer.append(tok.nextToken().trim()); - - if (!tok.hasMoreTokens()) { - descriptionBuffer.append(LS); - } else { - descriptionBuffer.append(", "); - } - } + LifecyclePhase phase = defaultLifecyclePhases.get(key); + if (phase != null && !phase.getMojos().isEmpty()) { + descriptionBuffer + .append(phase.getMojos().stream() + .map(LifecycleMojo::getGoal) + .collect(Collectors.joining(", "))) + .append(LS); } else { descriptionBuffer.append(NOT_DEFINED).append(LS); } @@ -685,9 +684,9 @@ private boolean describeCommand(StringBuilder descriptionBuffer) throws MojoExec for (String key : phases) { descriptionBuffer.append("* ").append(key).append(": "); - if (lifecycle.getDefaultPhases().get(key) != null) { + if (lifecycle.getDefaultLifecyclePhases().get(key) != null) { descriptionBuffer - .append(lifecycle.getDefaultPhases().get(key)) + .append(lifecycle.getDefaultLifecyclePhases().get(key)) .append(LS); } else { descriptionBuffer.append(NOT_DEFINED).append(LS); diff --git a/src/test/java/org/apache/maven/plugins/help/DescribeMojoTest.java b/src/test/java/org/apache/maven/plugins/help/DescribeMojoTest.java index 135ad5b9..23c3f2dd 100644 --- a/src/test/java/org/apache/maven/plugins/help/DescribeMojoTest.java +++ b/src/test/java/org/apache/maven/plugins/help/DescribeMojoTest.java @@ -21,10 +21,18 @@ import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; import org.apache.maven.execution.MavenSession; +import org.apache.maven.lifecycle.DefaultLifecycles; +import org.apache.maven.lifecycle.Lifecycle; import org.apache.maven.lifecycle.internal.MojoDescriptorCreator; +import org.apache.maven.lifecycle.mapping.LifecycleMapping; +import org.apache.maven.lifecycle.mapping.LifecyclePhase; import org.apache.maven.model.Plugin; import org.apache.maven.plugin.MavenPluginManager; import org.apache.maven.plugin.descriptor.MojoDescriptor; @@ -353,6 +361,97 @@ public void testLookupPluginDescriptorAMissingG() { } } + /** + * Regression test for Maven 3.10, where the default lifecycle has an empty map of built-in bindings. The mojo must + * use the packaging-specific lifecycle mapping in that case. + */ + @Test + public void testDescribeCommandPackagingSpecificPhaseShowsBindings() throws Exception { + Lifecycle lifecycle = mock(Lifecycle.class); + when(lifecycle.getId()).thenReturn("default"); + when(lifecycle.getPhases()).thenReturn(Arrays.asList("validate", "compile", "test", "package")); + when(lifecycle.getDefaultLifecyclePhases()).thenReturn(Collections.emptyMap()); + + Map phaseMap = new HashMap<>(); + for (String phase : Arrays.asList("validate", "compile", "test", "package")) { + phaseMap.put(phase, lifecycle); + } + DefaultLifecycles defaultLifecycles = mock(DefaultLifecycles.class); + when(defaultLifecycles.getPhaseToLifecycleMap()).thenReturn(phaseMap); + + Map lifecyclePhases = new LinkedHashMap<>(); + lifecyclePhases.put( + "compile", new LifecyclePhase("org.apache.maven.plugins:maven-compiler-plugin:3.11.0:compile")); + + org.apache.maven.lifecycle.mapping.Lifecycle mappingLifecycle = + mock(org.apache.maven.lifecycle.mapping.Lifecycle.class); + when(mappingLifecycle.getLifecyclePhases()).thenReturn(lifecyclePhases); + LifecycleMapping lifecycleMapping = mock(LifecycleMapping.class); + when(lifecycleMapping.getLifecycles()).thenReturn(Collections.singletonMap("default", mappingLifecycle)); + + MavenProject project = mock(MavenProject.class); + when(project.getPackaging()).thenReturn("jar"); + + DescribeMojo mojo = new DescribeMojo(); + setFieldWithReflection(mojo, "cmd", "compile"); + setFieldWithReflection(mojo, "defaultLifecycles", defaultLifecycles); + setFieldWithReflection(mojo, "lifecycleMappings", Collections.singletonMap("jar", lifecycleMapping)); + setParentFieldWithReflection(mojo, "project", project); + + StringBuilder buffer = new StringBuilder(); + Method describeCommand = DescribeMojo.class.getDeclaredMethod("describeCommand", StringBuilder.class); + describeCommand.setAccessible(true); + boolean result = (boolean) describeCommand.invoke(mojo, buffer); + + String output = buffer.toString(); + assertFalse(result); + assertTrue("Should show the compiler plugin: " + output, output.contains("maven-compiler-plugin")); + assertFalse("compile should not be 'Not defined': " + output, output.contains("* compile: Not defined")); + assertTrue("validate has no binding: " + output, output.contains("* validate: Not defined")); + assertTrue(output, output.contains("It is a part of the lifecycle for the POM packaging 'jar'")); + } + + @Test + public void testDescribeCommandBuiltinLifecyclePhaseShowsBindings() throws Exception { + Map lifecyclePhases = new LinkedHashMap<>(); + lifecyclePhases.put("pre-clean", null); + lifecyclePhases.put("clean", new LifecyclePhase("org.apache.maven.plugins:maven-clean-plugin:3.2.0:clean")); + lifecyclePhases.put("post-clean", null); + + Lifecycle lifecycle = mock(Lifecycle.class); + when(lifecycle.getId()).thenReturn("clean"); + when(lifecycle.getPhases()).thenReturn(Arrays.asList("pre-clean", "clean", "post-clean")); + when(lifecycle.getDefaultLifecyclePhases()).thenReturn(lifecyclePhases); + + Map phaseMap = new HashMap<>(); + for (String phase : Arrays.asList("pre-clean", "clean", "post-clean")) { + phaseMap.put(phase, lifecycle); + } + DefaultLifecycles defaultLifecycles = mock(DefaultLifecycles.class); + when(defaultLifecycles.getPhaseToLifecycleMap()).thenReturn(phaseMap); + + MavenProject project = mock(MavenProject.class); + when(project.getPackaging()).thenReturn("jar"); + + DescribeMojo mojo = new DescribeMojo(); + setFieldWithReflection(mojo, "cmd", "clean"); + setFieldWithReflection(mojo, "defaultLifecycles", defaultLifecycles); + setFieldWithReflection(mojo, "lifecycleMappings", Collections.emptyMap()); + setParentFieldWithReflection(mojo, "project", project); + + StringBuilder buffer = new StringBuilder(); + Method describeCommand = DescribeMojo.class.getDeclaredMethod("describeCommand", StringBuilder.class); + describeCommand.setAccessible(true); + boolean result = (boolean) describeCommand.invoke(mojo, buffer); + + String output = buffer.toString(); + assertFalse(result); + assertTrue(output, output.contains("'clean' is a phase within the 'clean' lifecycle")); + assertTrue(output, output.contains("maven-clean-plugin")); + assertTrue(output, output.contains("* pre-clean: Not defined")); + assertTrue(output, output.contains("* post-clean: Not defined")); + } + private static void setParentFieldWithReflection( final DescribeMojo mojo, final String fieldName, final Object value) throws NoSuchFieldException, IllegalAccessException {