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 52d453caf9 Complete Packaging in v4 api (#1451) 52d453caf9 is described below commit 52d453caf92c939d758eada2d4bff393e3d91380 Author: Guillaume Nodet <gno...@gmail.com> AuthorDate: Mon Mar 25 10:56:37 2024 +0100 Complete Packaging in v4 api (#1451) --- .../main/java/org/apache/maven/api/Packaging.java | 12 +- .../apache/maven/api/spi/PackagingProvider.java} | 38 +----- .../internal/impl/DefaultPackagingRegistry.java | 142 +++++++++++++++++++-- 3 files changed, 146 insertions(+), 46 deletions(-) diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/Packaging.java b/api/maven-api-core/src/main/java/org/apache/maven/api/Packaging.java index b64d1e0705..37d3ed61c8 100644 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/Packaging.java +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/Packaging.java @@ -21,6 +21,7 @@ package org.apache.maven.api; import org.apache.maven.api.annotations.Experimental; import org.apache.maven.api.annotations.Immutable; import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.model.PluginContainer; /** * Interface representing a Maven project packaging. @@ -44,12 +45,19 @@ public interface Packaging extends ExtensibleEnum { */ @Nonnull default Language language() { - return getType().getLanguage(); + return type().getLanguage(); } /** * The type of main artifact produced by this packaging. */ @Nonnull - Type getType(); + Type type(); + + /** + * Returns the binding to use specifically for this packaging. + * This will be merged to the default packaging definition. + */ + @Nonnull + PluginContainer plugins(); } diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/Packaging.java b/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/PackagingProvider.java similarity index 53% copy from api/maven-api-core/src/main/java/org/apache/maven/api/Packaging.java copy to api/maven-api-spi/src/main/java/org/apache/maven/api/spi/PackagingProvider.java index b64d1e0705..b1ef5b5e13 100644 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/Packaging.java +++ b/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/PackagingProvider.java @@ -16,40 +16,12 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.maven.api; +package org.apache.maven.api.spi; +import org.apache.maven.api.Packaging; +import org.apache.maven.api.annotations.Consumer; import org.apache.maven.api.annotations.Experimental; -import org.apache.maven.api.annotations.Immutable; -import org.apache.maven.api.annotations.Nonnull; -/** - * Interface representing a Maven project packaging. - * <p> - * TODO: define how to plug in new packaging definitions using the SPI. - * the packaging are currently defined by Maven 3 {@code Provider<LifecycleMapping>} - * - * @since 4.0.0 - */ @Experimental -@Immutable -public interface Packaging extends ExtensibleEnum { - /** - * The packaging id. - */ - @Nonnull - String id(); - - /** - * The language of this packaging. - */ - @Nonnull - default Language language() { - return getType().getLanguage(); - } - - /** - * The type of main artifact produced by this packaging. - */ - @Nonnull - Type getType(); -} +@Consumer +public interface PackagingProvider extends ExtensibleEnumProvider<Packaging> {} diff --git a/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultPackagingRegistry.java b/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultPackagingRegistry.java index 2a5b8a63b5..4f1b9c4ed0 100644 --- a/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultPackagingRegistry.java +++ b/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultPackagingRegistry.java @@ -22,27 +22,53 @@ import javax.inject.Inject; import javax.inject.Named; import javax.inject.Singleton; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; import org.apache.maven.api.Packaging; import org.apache.maven.api.Type; +import org.apache.maven.api.model.Dependency; +import org.apache.maven.api.model.InputLocation; +import org.apache.maven.api.model.InputSource; +import org.apache.maven.api.model.Plugin; +import org.apache.maven.api.model.PluginContainer; +import org.apache.maven.api.model.PluginExecution; import org.apache.maven.api.services.PackagingRegistry; import org.apache.maven.api.services.TypeRegistry; +import org.apache.maven.api.spi.PackagingProvider; +import org.apache.maven.lifecycle.internal.DefaultLifecyclePluginAnalyzer; import org.apache.maven.lifecycle.mapping.LifecycleMapping; +import org.apache.maven.lifecycle.mapping.LifecycleMojo; +import org.apache.maven.lifecycle.mapping.LifecyclePhase; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * TODO: this is session scoped as SPI can contribute. */ @Named @Singleton -public class DefaultPackagingRegistry implements PackagingRegistry { +public class DefaultPackagingRegistry + extends ExtensibleEnumRegistries.DefaultExtensibleEnumRegistry<Packaging, PackagingProvider> + implements PackagingRegistry { + + private static final Logger LOGGER = LoggerFactory.getLogger(DefaultPackagingRegistry.class); + private final Map<String, LifecycleMapping> lifecycleMappings; private final TypeRegistry typeRegistry; @Inject - public DefaultPackagingRegistry(Map<String, LifecycleMapping> lifecycleMappings, TypeRegistry typeRegistry) { + public DefaultPackagingRegistry( + Map<String, LifecycleMapping> lifecycleMappings, + TypeRegistry typeRegistry, + List<PackagingProvider> providers) { + super(providers); this.lifecycleMappings = lifecycleMappings; this.typeRegistry = typeRegistry; } @@ -57,17 +83,111 @@ public class DefaultPackagingRegistry implements PackagingRegistry { if (type == null) { return Optional.empty(); } + return Optional.of(new DefaultPackaging(id, type, getPlugins(lifecycleMapping))); + } - return Optional.of(new Packaging() { - @Override - public String id() { - return id; - } + private PluginContainer getPlugins(LifecycleMapping lifecycleMapping) { + Map<String, Plugin> plugins = new HashMap<>(); + lifecycleMapping.getLifecycles().forEach((id, lifecycle) -> lifecycle + .getLifecyclePhases() + .forEach((phase, lifecyclePhase) -> parseLifecyclePhaseDefinitions(plugins, phase, lifecyclePhase))); + return PluginContainer.newBuilder().plugins(plugins.values()).build(); + } + + private void parseLifecyclePhaseDefinitions(Map<String, Plugin> plugins, String phase, LifecyclePhase goals) { + InputSource inputSource = + new InputSource(DefaultLifecyclePluginAnalyzer.DEFAULTLIFECYCLEBINDINGS_MODELID, null); + InputLocation location = new InputLocation(-1, -1, inputSource, 0); + + List<LifecycleMojo> mojos = goals.getMojos(); + if (mojos != null) { + for (int i = 0; i < mojos.size(); i++) { + LifecycleMojo mojo = mojos.get(i); + + // Compute goal coordinates + String groupId, artifactId, version, goal; + String[] p = mojo.getGoal().trim().split(":"); + if (p.length == 3) { + // <groupId>:<artifactId>:<goal> + groupId = p[0]; + artifactId = p[1]; + version = null; + goal = p[2]; + } else if (p.length == 4) { + // <groupId>:<artifactId>:<version>:<goal> + groupId = p[0]; + artifactId = p[1]; + version = p[2]; + goal = p[3]; + } else { + // invalid + LOGGER.warn( + "Ignored invalid goal specification '{}' from lifecycle mapping for phase {}", + mojo.getGoal(), + phase); + continue; + } + + String key = groupId + ":" + artifactId; + + // Build plugin + List<PluginExecution> execs = new ArrayList<>(); + List<Dependency> deps = new ArrayList<>(); - @Override - public Type getType() { - return type; + Plugin existing = plugins.get(key); + if (existing != null) { + if (version == null) { + version = existing.getVersion(); + } + execs.addAll(existing.getExecutions()); + deps.addAll(existing.getDependencies()); + } + + PluginExecution execution = PluginExecution.newBuilder() + .id(getExecutionId(existing, goal)) + .priority(i - mojos.size()) + .phase(phase) + .goals(List.of(goal)) + .configuration(mojo.getConfiguration()) + .location("", location) + .location("id", location) + .location("phase", location) + .location("goals", location) + .build(); + execs.add(execution); + + if (mojo.getDependencies() != null) { + mojo.getDependencies().forEach(d -> deps.add(d.getDelegate())); + } + + Plugin plugin = Plugin.newBuilder() + .groupId(groupId) + .artifactId(artifactId) + .version(version) + .location("", location) + .location("groupId", location) + .location("artifactId", location) + .location("version", location) + .executions(execs) + .dependencies(deps) + .build(); + + plugins.put(key, plugin); } - }); + } } + + private static String getExecutionId(Plugin plugin, String goal) { + Set<String> existingIds = plugin != null + ? plugin.getExecutions().stream().map(PluginExecution::getId).collect(Collectors.toSet()) + : Set.of(); + String base = "default-" + goal; + String id = base; + for (int index = 1; existingIds.contains(id); index++) { + id = base + '-' + index; + } + return id; + } + + private record DefaultPackaging(String id, Type type, PluginContainer plugins) implements Packaging {} }