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
commit 9be08ccef8d6c59867f49ecb98e772776fc8f9c0 Author: Guillaume Nodet <gno...@gmail.com> AuthorDate: Thu Apr 11 08:17:32 2024 +0200 [MNG-8084] Include lifecycle in the API --- .../api/{MetadataStorage.java => Lifecycle.java} | 50 ++++- .../main/java/org/apache/maven/api/PathScope.java | 1 + .../maven/api/plugin/annotations/Execute.java | 13 +- .../api/plugin/annotations/LifecyclePhase.java | 74 ------- .../apache/maven/api/plugin/annotations/Mojo.java | 2 +- .../maven/api/services/LifecycleRegistry.java | 34 +-- .../apache/maven/api/spi/LifecycleProvider.java} | 16 +- .../internal/impl/DefaultLifecycleRegistry.java | 227 +++++++++++++++++++++ .../internal/impl/ExtensibleEnumRegistries.java | 9 +- .../java/org/apache/maven/internal/impl/Graph.java | 145 +++++++++++++ .../org/apache/maven/internal/impl/Lifecycles.java | 73 +++++++ .../apache/maven/lifecycle/DefaultLifecycles.java | 16 +- .../java/org/apache/maven/lifecycle/Lifecycle.java | 29 ++- .../providers/CleanLifecycleProvider.java | 47 ----- .../providers/DefaultLifecycleProvider.java | 70 ------- .../lifecycle/providers/SiteLifecycleProvider.java | 48 ----- .../maven/lifecycle/DefaultLifecyclesTest.java | 5 +- .../internal/stub/DefaultLifecyclesStub.java | 4 +- 18 files changed, 559 insertions(+), 304 deletions(-) diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/MetadataStorage.java b/api/maven-api-core/src/main/java/org/apache/maven/api/Lifecycle.java similarity index 50% copy from api/maven-api-core/src/main/java/org/apache/maven/api/MetadataStorage.java copy to api/maven-api-core/src/main/java/org/apache/maven/api/Lifecycle.java index a7b8075ada..c461ef0ad0 100644 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/MetadataStorage.java +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/Lifecycle.java @@ -18,16 +18,56 @@ */ package org.apache.maven.api; +import java.util.*; + import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Immutable; +import org.apache.maven.api.model.Plugin; /** - * Storage location for metadata + * Lifecycle definition * * @since 4.0.0 */ @Experimental -public enum MetadataStorage { - GROUP, - ARTIFACT, - VERSION +@Immutable +public interface Lifecycle extends ExtensibleEnum { + + String CLEAN = "clean"; + + String DEFAULT = "default"; + + String SITE = "site"; + + String WRAPPER = "wrapper"; + + /** + * Name or identifier of this lifecycle. + * + * @return the unique identifier for this lifecycle + */ + @Override + String id(); + + /** + * Collection of phases for this lifecycle + */ + Collection<Phase> phases(); + + /** + * Pre-ordered list of phases. + * If not provided, a default order will be computed. + */ + default Optional<List<String>> orderedPhases() { + return Optional.empty(); + } + + /** + * A phase in the lifecycle. + */ + interface Phase { + String name(); + + List<Plugin> plugins(); + } } diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/PathScope.java b/api/maven-api-core/src/main/java/org/apache/maven/api/PathScope.java index 93b1c90545..b32dd3c172 100644 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/PathScope.java +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/PathScope.java @@ -46,6 +46,7 @@ import static org.apache.maven.api.ExtensibleEnums.pathScope; @Immutable public interface PathScope extends ExtensibleEnum { + // TODO: what if I simply want all dependencies ? @Nonnull ProjectScope projectScope(); diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/Execute.java b/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/Execute.java index 903d224f38..f4830b87ae 100644 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/Execute.java +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/Execute.java @@ -41,21 +41,10 @@ import org.apache.maven.api.annotations.Nonnull; public @interface Execute { /** * Lifecycle phase to fork. Note that specifying a phase overrides specifying a goal. - * For custom lifecycle phase ids use {@link #customPhase()} instead. - * Only one of {@link #customPhase()} and {@link #phase()} must be set. * @return the phase */ @Nonnull - LifecyclePhase phase() default LifecyclePhase.NONE; - - /** - * Custom lifecycle phase to fork. Note that specifying a phase overrides specifying a goal. - * This element should only be used for non-standard phases. For standard phases rather use {@link #phase()}. - * Only one of {@link #customPhase()} and {@link #phase()} must be set. - * @return the custom phase id - */ - @Nonnull - String customPhase() default ""; + String phase() default ""; /** * Goal to fork. Note that specifying a phase overrides specifying a goal. The specified <code>goal</code> must be diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/LifecyclePhase.java b/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/LifecyclePhase.java deleted file mode 100644 index b3ddb72f38..0000000000 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/LifecyclePhase.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * 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.api.plugin.annotations; - -import org.apache.maven.api.annotations.Experimental; - -/** - * <a href="/ref/3.0.4/maven-core/lifecycles.html">Lifecycle phases</a>. - * - * @since 4.0.0 - */ -@Experimental -public enum LifecyclePhase { - VALIDATE("validate"), - INITIALIZE("initialize"), - GENERATE_SOURCES("generate-sources"), - PROCESS_SOURCES("process-sources"), - GENERATE_RESOURCES("generate-resources"), - PROCESS_RESOURCES("process-resources"), - COMPILE("compile"), - PROCESS_CLASSES("process-classes"), - GENERATE_TEST_SOURCES("generate-test-sources"), - PROCESS_TEST_SOURCES("process-test-sources"), - GENERATE_TEST_RESOURCES("generate-test-resources"), - PROCESS_TEST_RESOURCES("process-test-resources"), - TEST_COMPILE("test-compile"), - PROCESS_TEST_CLASSES("process-test-classes"), - TEST("test"), - PREPARE_PACKAGE("prepare-package"), - PACKAGE("package"), - PRE_INTEGRATION_TEST("pre-integration-test"), - INTEGRATION_TEST("integration-test"), - POST_INTEGRATION_TEST("post-integration-test"), - VERIFY("verify"), - INSTALL("install"), - DEPLOY("deploy"), - - PRE_CLEAN("pre-clean"), - CLEAN("clean"), - POST_CLEAN("post-clean"), - - PRE_SITE("pre-site"), - SITE("site"), - POST_SITE("post-site"), - SITE_DEPLOY("site-deploy"), - - NONE(""); - - private final String id; - - LifecyclePhase(String id) { - this.id = id; - } - - public String id() { - return this.id; - } -} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/Mojo.java b/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/Mojo.java index 4f01f0f6d2..a5768dfb4a 100644 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/Mojo.java +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/Mojo.java @@ -54,7 +54,7 @@ public @interface Mojo { * @return the default phase */ @Nonnull - LifecyclePhase defaultPhase() default LifecyclePhase.NONE; + String defaultPhase() default ""; /** * does your mojo requires a project to be executed? diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/providers/WrapperLifecycleProvider.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/LifecycleRegistry.java similarity index 50% rename from maven-core/src/main/java/org/apache/maven/lifecycle/providers/WrapperLifecycleProvider.java rename to api/maven-api-core/src/main/java/org/apache/maven/api/services/LifecycleRegistry.java index 29ddaea043..2718582a24 100644 --- a/maven-core/src/main/java/org/apache/maven/lifecycle/providers/WrapperLifecycleProvider.java +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/LifecycleRegistry.java @@ -16,32 +16,18 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.maven.lifecycle.providers; +package org.apache.maven.api.services; -import javax.inject.Inject; -import javax.inject.Named; -import javax.inject.Singleton; +import java.util.List; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; -/** - * {@code wrapper} lifecycle provider. - */ -@Named(WrapperLifecycleProvider.LIFECYCLE_ID) -@Singleton -public final class WrapperLifecycleProvider extends AbstractLifecycleProvider { - static final String LIFECYCLE_ID = "wrapper"; - - // START SNIPPET: wrapper - private static final String[] PHASES = {"wrapper"}; - - private static final String MAVEN_WRAPPER_PLUGIN_VERSION = "3.2.0"; +import org.apache.maven.api.Lifecycle; - private static final String[] BINDINGS = { - "wrapper", "org.apache.maven.plugins:maven-wrapper-plugin:" + MAVEN_WRAPPER_PLUGIN_VERSION + ":wrapper" - }; - // END SNIPPET: wrapper - - @Inject - public WrapperLifecycleProvider() { - super(LIFECYCLE_ID, PHASES, BINDINGS); +public interface LifecycleRegistry extends ExtensibleEnumRegistry<Lifecycle>, Iterable<Lifecycle> { + default Stream<Lifecycle> stream() { + return StreamSupport.stream(spliterator(), false); } + + List<String> computePhases(Lifecycle lifecycle); } diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/MetadataStorage.java b/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/LifecycleProvider.java similarity index 80% rename from api/maven-api-core/src/main/java/org/apache/maven/api/MetadataStorage.java rename to api/maven-api-spi/src/main/java/org/apache/maven/api/spi/LifecycleProvider.java index a7b8075ada..539c628299 100644 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/MetadataStorage.java +++ b/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/LifecycleProvider.java @@ -16,18 +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.Lifecycle; +import org.apache.maven.api.annotations.Consumer; import org.apache.maven.api.annotations.Experimental; -/** - * Storage location for metadata - * - * @since 4.0.0 - */ @Experimental -public enum MetadataStorage { - GROUP, - ARTIFACT, - VERSION -} +@Consumer +public interface LifecycleProvider extends ExtensibleEnumProvider<Lifecycle> {} diff --git a/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultLifecycleRegistry.java b/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultLifecycleRegistry.java new file mode 100644 index 0000000000..776f8d8a27 --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultLifecycleRegistry.java @@ -0,0 +1,227 @@ +/* + * 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.internal.impl; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.apache.maven.api.Lifecycle; +import org.apache.maven.api.services.LifecycleRegistry; +import org.apache.maven.api.spi.LifecycleProvider; + +import static java.util.Arrays.asList; +import static java.util.Collections.*; +import static org.apache.maven.internal.impl.Lifecycles.*; + +/** + * TODO: this is session scoped as SPI can contribute. + */ +@Named +@Singleton +public class DefaultLifecycleRegistry + extends ExtensibleEnumRegistries.DefaultExtensibleEnumRegistry<Lifecycle, LifecycleProvider> + implements LifecycleRegistry { + + public DefaultLifecycleRegistry() { + super(Collections.emptyList()); + } + + @Inject + public DefaultLifecycleRegistry( + List<LifecycleProvider> providers, Map<String, org.apache.maven.lifecycle.Lifecycle> lifecycles) { + super( + concat(providers, new LifecycleWrapperProvider(lifecycles)), + new CleanLifecycle(), + new DefaultLifecycle(), + new SiteLifecycle(), + new WrapperLifecycle()); + // validate lifecycle + for (Lifecycle lifecycle : this) { + Set<String> set = new HashSet<>(); + lifecycle.phases().forEach(phase -> { + if (!set.add(phase.name())) { + throw new IllegalArgumentException( + "Found duplicated phase '" + phase.name() + "' in '" + lifecycle.id() + "' lifecycle"); + } + }); + } + } + + @Override + public Iterator<Lifecycle> iterator() { + return values.values().iterator(); + } + + @Override + public Stream<Lifecycle> stream() { + return values.values().stream(); + } + + static <T> List<T> concat(List<T> l, T t) { + List<T> nl = new ArrayList<>(l.size() + 1); + nl.addAll(l); + nl.add(t); + return nl; + } + + @Override + public List<String> computePhases(Lifecycle lifecycle) { + return lifecycle.phases().stream().map(Lifecycle.Phase::name).toList(); + } + + static class LifecycleWrapperProvider implements LifecycleProvider { + private final Map<String, org.apache.maven.lifecycle.Lifecycle> lifecycles; + + @Inject + LifecycleWrapperProvider(Map<String, org.apache.maven.lifecycle.Lifecycle> lifecycles) { + this.lifecycles = lifecycles; + } + + @Override + public Collection<Lifecycle> provides() { + return lifecycles.values().stream().map(this::wrap).collect(Collectors.toList()); + } + + private Lifecycle wrap(org.apache.maven.lifecycle.Lifecycle lifecycle) { + return new Lifecycle() { + @Override + public String id() { + return lifecycle.getId(); + } + + @Override + public Collection<Phase> phases() { + // TODO: implement + throw new UnsupportedOperationException(); + } + }; + } + } + + static class CleanLifecycle implements Lifecycle { + + private static final String MAVEN_CLEAN_PLUGIN_VERSION = "3.2.0"; + + @Override + public String id() { + return Lifecycle.CLEAN; + } + + @Override + public Collection<Phase> phases() { + return asList( + phase("pre-clean"), + phase( + "clean", + plugin( + "org.apache.maven.plugins:maven-clean-plugin:" + MAVEN_CLEAN_PLUGIN_VERSION + + ":clean", + "clean")), + phase("post-clean")); + } + } + + static class DefaultLifecycle implements Lifecycle { + @Override + public String id() { + return Lifecycle.DEFAULT; + } + + @Override + public Collection<Phase> phases() { + return asList( + phase("validate"), + phase("initialize"), + phase("generate-sources"), + phase("process-sources"), + phase("generate-resources"), + phase("process-resources"), + phase("compile"), + phase("process-classes"), + phase("generate-test-sources"), + phase("process-test-sources"), + phase("generate-test-resources"), + phase("process-test-resources"), + phase("test-compile"), + phase("process-test-classes"), + phase("test"), + phase("prepare-package"), + phase("package"), + phase("pre-integration-test"), + phase("integration-test"), + phase("post-integration-test"), + phase("verify"), + phase("install"), + phase("deploy")); + } + } + + static class SiteLifecycle implements Lifecycle { + + private static final String MAVEN_SITE_PLUGIN_VERSION = "3.12.1"; + + @Override + public String id() { + return Lifecycle.SITE; + } + + @Override + public Collection<Phase> phases() { + return asList( + phase("pre-site"), + phase( + "site", + plugin( + "org.apache.maven.plugins:maven-site-plugin:" + MAVEN_SITE_PLUGIN_VERSION + ":site", + "site")), + phase("post-site"), + phase( + "site-deploy", + plugin( + "org.apache.maven.plugins:maven-site-plugin:" + MAVEN_SITE_PLUGIN_VERSION + + ":deploy", + "site-deploy"))); + } + } + + static class WrapperLifecycle implements Lifecycle { + + private static final String MAVEN_WRAPPER_PLUGIN_VERSION = "3.2.0"; + + @Override + public String id() { + return WRAPPER; + } + + @Override + public Collection<Phase> phases() { + return singleton(phase( + "wrapper", + plugin( + "org.apache.maven.plugins:maven-wrapper-plugin:" + MAVEN_WRAPPER_PLUGIN_VERSION + + ":wrapper", + "wrapper"))); + } + } +} diff --git a/maven-core/src/main/java/org/apache/maven/internal/impl/ExtensibleEnumRegistries.java b/maven-core/src/main/java/org/apache/maven/internal/impl/ExtensibleEnumRegistries.java index d512e74b46..e661a40397 100644 --- a/maven-core/src/main/java/org/apache/maven/internal/impl/ExtensibleEnumRegistries.java +++ b/maven-core/src/main/java/org/apache/maven/internal/impl/ExtensibleEnumRegistries.java @@ -25,6 +25,7 @@ import javax.inject.Singleton; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -33,6 +34,8 @@ import org.apache.maven.api.*; import org.apache.maven.api.services.*; import org.apache.maven.api.spi.*; +import static org.apache.maven.internal.impl.Utils.nonNull; + public class ExtensibleEnumRegistries { @Named @@ -76,17 +79,17 @@ public class ExtensibleEnumRegistries { static class DefaultExtensibleEnumRegistry<T extends ExtensibleEnum, P extends ExtensibleEnumProvider<T>> implements ExtensibleEnumRegistry<T> { - private final Map<String, T> values; + protected final Map<String, T> values; DefaultExtensibleEnumRegistry(List<P> providers, T... builtinValues) { values = Stream.<T>concat( Stream.of(builtinValues), providers.stream().flatMap(p -> p.provides().stream())) - .collect(Collectors.toMap(t -> t.id(), t -> t)); + .collect(Collectors.toUnmodifiableMap(ExtensibleEnum::id, Function.identity())); } @Override public Optional<T> lookup(String id) { - return Optional.ofNullable(values.get(id)); + return Optional.ofNullable(values.get(nonNull(id, "id"))); } } } diff --git a/maven-core/src/main/java/org/apache/maven/internal/impl/Graph.java b/maven-core/src/main/java/org/apache/maven/internal/impl/Graph.java new file mode 100644 index 0000000000..6bbdb51ab8 --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/internal/impl/Graph.java @@ -0,0 +1,145 @@ +/* + * 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.internal.impl; + +import java.util.*; + +import org.apache.maven.project.CycleDetectedException; + +class Graph { + private enum DfsState { + VISITING, + VISITED + } + + final Map<String, Vertex> vertices = new LinkedHashMap<>(); + + public Vertex getVertex(String id) { + return vertices.get(id); + } + + public Collection<Vertex> getVertices() { + return vertices.values(); + } + + Vertex addVertex(String label) { + return vertices.computeIfAbsent(label, Vertex::new); + } + + void addEdge(Vertex from, Vertex to) throws CycleDetectedException { + from.children.add(to); + to.parents.add(from); + List<String> cycle = findCycle(to); + if (cycle != null) { + // remove edge which introduced cycle + removeEdge(from, to); + throw new CycleDetectedException( + "Edge between '" + from.label + "' and '" + to.label + "' introduces to cycle in the graph", cycle); + } + } + + void removeEdge(Vertex from, Vertex to) { + from.children.remove(to); + to.parents.remove(from); + } + + List<String> visitAll() { + return visitAll(vertices.values(), new HashMap<>(), new ArrayList<>()); + } + + List<String> findCycle(Vertex vertex) { + return visitCycle(Collections.singleton(vertex), new HashMap<>(), new LinkedList<>()); + } + + private static List<String> visitAll( + Collection<Vertex> children, Map<Vertex, DfsState> stateMap, List<String> list) { + for (Vertex v : children) { + DfsState state = stateMap.putIfAbsent(v, DfsState.VISITING); + if (state == null) { + visitAll(v.children, stateMap, list); + stateMap.put(v, DfsState.VISITED); + list.add(v.label); + } + } + return list; + } + + private static List<String> visitCycle( + Collection<Vertex> children, Map<Vertex, DfsState> stateMap, LinkedList<String> cycle) { + for (Vertex v : children) { + DfsState state = stateMap.putIfAbsent(v, DfsState.VISITING); + if (state == null) { + cycle.addLast(v.label); + List<String> ret = visitCycle(v.children, stateMap, cycle); + if (ret != null) { + return ret; + } + cycle.removeLast(); + stateMap.put(v, DfsState.VISITED); + } else if (state == DfsState.VISITING) { + // we are already visiting this vertex, this mean we have a cycle + int pos = cycle.lastIndexOf(v.label); + List<String> ret = cycle.subList(pos, cycle.size()); + ret.add(v.label); + return ret; + } + } + return null; + } + + static class Vertex { + final String label; + final List<Vertex> children = new ArrayList<>(); + final List<Vertex> parents = new ArrayList<>(); + + Vertex(String label) { + this.label = label; + } + + String getLabel() { + return label; + } + + List<Vertex> getChildren() { + return children; + } + + List<Vertex> getParents() { + return parents; + } + } + + static class CycleDetectedException extends RuntimeException { + private final List<String> cycle; + + CycleDetectedException(String message, List<String> cycle) { + super(message); + this.cycle = cycle; + } + + public List<String> getCycle() { + return cycle; + } + + @Override + public String getMessage() { + return super.getMessage() + " " + String.join(" --> ", cycle); + } + } +} diff --git a/maven-core/src/main/java/org/apache/maven/internal/impl/Lifecycles.java b/maven-core/src/main/java/org/apache/maven/internal/impl/Lifecycles.java new file mode 100644 index 0000000000..42da23b290 --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/internal/impl/Lifecycles.java @@ -0,0 +1,73 @@ +/* + * 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.internal.impl; + +import java.util.Collections; +import java.util.List; + +import org.apache.maven.api.Lifecycle; +import org.apache.maven.api.model.Plugin; +import org.apache.maven.api.model.PluginExecution; + +public class Lifecycles { + + static Lifecycle.Phase phase(String name) { + return new DefaultPhase(name, Collections.emptyList(), Collections.emptyList()); + } + + static Lifecycle.Phase phase(String name, Plugin plugin) { + return new DefaultPhase(name, Collections.singletonList(plugin), Collections.emptyList()); + } + + static Plugin plugin(String coord, String phase) { + String[] c = coord.split(":"); + return Plugin.newBuilder() + .groupId(c[0]) + .artifactId(c[1]) + .version(c[2]) + .executions(Collections.singletonList(PluginExecution.newBuilder() + .id("default-" + c[3]) + .phase(phase) + .goals(Collections.singletonList(c[3])) + .build())) + .build(); + } + + static class DefaultPhase implements Lifecycle.Phase { + private final String name; + private final List<Plugin> plugins; + private final List<Lifecycle.Phase> phases; + + DefaultPhase(String name, List<Plugin> plugins, List<Lifecycle.Phase> phases) { + this.name = name; + this.plugins = plugins; + this.phases = phases; + } + + @Override + public String name() { + return name; + } + + @Override + public List<Plugin> plugins() { + return plugins; + } + } +} diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/DefaultLifecycles.java b/maven-core/src/main/java/org/apache/maven/lifecycle/DefaultLifecycles.java index ed9313db75..3987985cbe 100644 --- a/maven-core/src/main/java/org/apache/maven/lifecycle/DefaultLifecycles.java +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/DefaultLifecycles.java @@ -30,6 +30,7 @@ import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; +import org.apache.maven.api.services.LifecycleRegistry; import org.apache.maven.api.services.Lookup; import org.apache.maven.api.services.LookupException; import org.slf4j.Logger; @@ -51,24 +52,29 @@ public class DefaultLifecycles { private final Lookup lookup; + private final LifecycleRegistry registry; + private Map<String, Lifecycle> customLifecycles; public DefaultLifecycles() { this.lookup = null; + this.registry = null; } /** - * @deprecated Rely on {@link #DefaultLifecycles(Lookup)} instead + * @deprecated Use {@link #DefaultLifecycles(LifecycleRegistry,Lookup)} instead */ @Deprecated public DefaultLifecycles(Map<String, Lifecycle> lifecycles, org.codehaus.plexus.logging.Logger logger) { this.customLifecycles = lifecycles; this.lookup = null; + this.registry = null; } @Inject - public DefaultLifecycles(Lookup lookup) { + public DefaultLifecycles(LifecycleRegistry registry, Lookup lookup) { this.lookup = lookup; + this.registry = registry; } /** @@ -149,7 +155,11 @@ public class DefaultLifecycles { // Lifecycles cannot be cached as extensions might add custom lifecycles later in the execution. try { - return lookup.lookupMap(Lifecycle.class); + Map<String, Lifecycle> lifecycles = new HashMap<>(lookup.lookupMap(Lifecycle.class)); + for (org.apache.maven.api.Lifecycle lf : registry) { + lifecycles.put(lf.id(), new Lifecycle(registry, lf)); + } + return lifecycles; } catch (LookupException e) { throw new IllegalStateException("Unable to lookup lifecycles from the plexus container", e); } diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/Lifecycle.java b/maven-core/src/main/java/org/apache/maven/lifecycle/Lifecycle.java index b3b63cbaaa..089e412ad5 100644 --- a/maven-core/src/main/java/org/apache/maven/lifecycle/Lifecycle.java +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/Lifecycle.java @@ -18,8 +18,8 @@ */ package org.apache.maven.lifecycle; -import java.util.List; -import java.util.Map; +import java.util.*; +import java.util.stream.Collectors; import org.apache.maven.lifecycle.mapping.LifecyclePhase; @@ -35,6 +35,14 @@ public class Lifecycle { this.defaultPhases = defaultPhases; } + public Lifecycle( + org.apache.maven.api.services.LifecycleRegistry registry, org.apache.maven.api.Lifecycle lifecycle) { + this.lifecycle = lifecycle; + this.id = lifecycle.id(); + this.phases = registry.computePhases(lifecycle); + this.defaultPhases = getDefaultPhases(lifecycle); + } + // <lifecycle> // <id>clean</id> // <phases> @@ -53,12 +61,25 @@ public class Lifecycle { private Map<String, LifecyclePhase> defaultPhases; + private org.apache.maven.api.Lifecycle lifecycle; + public String getId() { - return this.id; + return id; } public List<String> getPhases() { - return this.phases; + return phases; + } + + static Map<String, LifecyclePhase> getDefaultPhases(org.apache.maven.api.Lifecycle lifecycle) { + Map<String, List<String>> goals = new HashMap<>(); + lifecycle.phases().forEach(phase -> phase.plugins() + .forEach(plugin -> plugin.getExecutions().forEach(exec -> exec.getGoals() + .forEach(goal -> goals.computeIfAbsent(phase.name(), n -> new ArrayList<>()) + .add(plugin.getGroupId() + ":" + plugin.getArtifactId() + ":" + plugin.getVersion() + + ":" + goal))))); + return goals.entrySet().stream() + .collect(Collectors.toMap(Map.Entry::getKey, e -> new LifecyclePhase(String.join(",", e.getValue())))); } public Map<String, LifecyclePhase> getDefaultLifecyclePhases() { diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/providers/CleanLifecycleProvider.java b/maven-core/src/main/java/org/apache/maven/lifecycle/providers/CleanLifecycleProvider.java deleted file mode 100644 index cd8873d1b9..0000000000 --- a/maven-core/src/main/java/org/apache/maven/lifecycle/providers/CleanLifecycleProvider.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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.lifecycle.providers; - -import javax.inject.Inject; -import javax.inject.Named; -import javax.inject.Singleton; - -/** - * {@code clean} lifecycle provider. - */ -@Named(CleanLifecycleProvider.LIFECYCLE_ID) -@Singleton -public final class CleanLifecycleProvider extends AbstractLifecycleProvider { - static final String LIFECYCLE_ID = "clean"; - - // START SNIPPET: clean - private static final String[] PHASES = {"pre-clean", "clean", "post-clean"}; - - private static final String MAVEN_CLEAN_PLUGIN_VERSION = "3.2.0"; - - private static final String[] BINDINGS = { - "clean", "org.apache.maven.plugins:maven-clean-plugin:" + MAVEN_CLEAN_PLUGIN_VERSION + ":clean" - }; - // END SNIPPET: clean - - @Inject - public CleanLifecycleProvider() { - super(LIFECYCLE_ID, PHASES, BINDINGS); - } -} diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/providers/DefaultLifecycleProvider.java b/maven-core/src/main/java/org/apache/maven/lifecycle/providers/DefaultLifecycleProvider.java deleted file mode 100644 index 9f7602ab69..0000000000 --- a/maven-core/src/main/java/org/apache/maven/lifecycle/providers/DefaultLifecycleProvider.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * 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.lifecycle.providers; - -import javax.inject.Inject; -import javax.inject.Named; -import javax.inject.Singleton; - -/** - * {@code default} lifecycle provider. - */ -@Named(DefaultLifecycleProvider.LIFECYCLE_ID) -@Singleton -public final class DefaultLifecycleProvider extends AbstractLifecycleProvider { - static final String LIFECYCLE_ID = "default"; - - // START SNIPPET: default - private static final String[] PHASES = { - "validate", - "initialize", - "generate-sources", - "process-sources", - "generate-resources", - "process-resources", - "compile", - "process-classes", - "generate-test-sources", - "process-test-sources", - "generate-test-resources", - "process-test-resources", - "test-compile", - "process-test-classes", - "test", - "prepare-package", - "package", - "pre-integration-test", - "integration-test", - "post-integration-test", - "verify", - "install", - "deploy" - }; - // END SNIPPET: default - - @Inject - public DefaultLifecycleProvider() { - super( - LIFECYCLE_ID, - PHASES, - null // no global plugin bindings for default lifecycle: they are defined per-packaging in separate - // providers - ); - } -} diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/providers/SiteLifecycleProvider.java b/maven-core/src/main/java/org/apache/maven/lifecycle/providers/SiteLifecycleProvider.java deleted file mode 100644 index 8dd6439b8a..0000000000 --- a/maven-core/src/main/java/org/apache/maven/lifecycle/providers/SiteLifecycleProvider.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * 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.lifecycle.providers; - -import javax.inject.Inject; -import javax.inject.Named; -import javax.inject.Singleton; - -/** - * {@code site} lifecycle provider. - */ -@Named(SiteLifecycleProvider.LIFECYCLE_ID) -@Singleton -public final class SiteLifecycleProvider extends AbstractLifecycleProvider { - static final String LIFECYCLE_ID = "site"; - - // START SNIPPET: site - private static final String[] PHASES = {"pre-site", "site", "post-site", "site-deploy"}; - - private static final String MAVEN_SITE_PLUGIN_VERSION = "3.12.1"; - - private static final String[] BINDINGS = { - "site", "org.apache.maven.plugins:maven-site-plugin:" + MAVEN_SITE_PLUGIN_VERSION + ":site", - "site-deploy", "org.apache.maven.plugins:maven-site-plugin:" + MAVEN_SITE_PLUGIN_VERSION + ":deploy" - }; - // END SNIPPET: site - - @Inject - public SiteLifecycleProvider() { - super(LIFECYCLE_ID, PHASES, BINDINGS); - } -} diff --git a/maven-core/src/test/java/org/apache/maven/lifecycle/DefaultLifecyclesTest.java b/maven-core/src/test/java/org/apache/maven/lifecycle/DefaultLifecyclesTest.java index fe42923898..99b8793eae 100644 --- a/maven-core/src/test/java/org/apache/maven/lifecycle/DefaultLifecyclesTest.java +++ b/maven-core/src/test/java/org/apache/maven/lifecycle/DefaultLifecyclesTest.java @@ -27,6 +27,7 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; +import org.apache.maven.internal.impl.DefaultLifecycleRegistry; import org.apache.maven.internal.impl.DefaultLookup; import org.codehaus.plexus.PlexusContainer; import org.codehaus.plexus.component.repository.exception.ComponentLookupException; @@ -94,7 +95,9 @@ class DefaultLifecyclesTest { PlexusContainer mockedPlexusContainer = mock(PlexusContainer.class); when(mockedPlexusContainer.lookupMap(Lifecycle.class)).thenReturn(lifeCycles); - DefaultLifecycles dl = new DefaultLifecycles(new DefaultLookup(mockedPlexusContainer)); + DefaultLifecycles dl = new DefaultLifecycles( + new DefaultLifecycleRegistry(Collections.emptyList(), Collections.emptyMap()), + new DefaultLookup(mockedPlexusContainer)); assertThat(dl.getLifeCycles().get(0).getId(), is("clean")); assertThat(dl.getLifeCycles().get(1).getId(), is("default")); diff --git a/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/DefaultLifecyclesStub.java b/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/DefaultLifecyclesStub.java index 9260b8332c..65c0b65570 100644 --- a/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/DefaultLifecyclesStub.java +++ b/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/DefaultLifecyclesStub.java @@ -24,6 +24,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +import org.apache.maven.internal.impl.DefaultLifecycleRegistry; import org.apache.maven.internal.impl.DefaultLookup; import org.apache.maven.lifecycle.DefaultLifecycles; import org.apache.maven.lifecycle.Lifecycle; @@ -71,6 +72,7 @@ public class DefaultLifecyclesStub { PlexusContainer mockedContainer = mock(PlexusContainer.class); when(mockedContainer.lookupMap(Lifecycle.class)).thenReturn(lifeCycles); - return new DefaultLifecycles(new DefaultLookup(mockedContainer)); + DefaultLifecycleRegistry reg = new DefaultLifecycleRegistry(); + return new DefaultLifecycles(reg, new DefaultLookup(mockedContainer)); } }