[MNG-8052] New lifecycle for Maven 4 (#1448)

This commit is contained in:
Guillaume Nodet 2024-08-29 06:48:54 +01:00 committed by GitHub
parent 9bc5cc88b4
commit acec540547
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
24 changed files with 1001 additions and 110 deletions

View File

@ -21,9 +21,11 @@ package org.apache.maven.api;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
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.Plugin;
/**
@ -41,14 +43,20 @@ import org.apache.maven.api.model.Plugin;
@Immutable
public interface Lifecycle extends ExtensibleEnum {
// =========================
// Maven defined lifecycles
// =========================
String CLEAN = "clean";
String DEFAULT = "default";
String SITE = "site";
String WRAPPER = "wrapper";
// ======================
// Phase qualifiers
// ======================
String BEFORE = "before:";
String AFTER = "after:";
/**
* Name or identifier of this lifecycle.
*
@ -62,6 +70,18 @@ public interface Lifecycle extends ExtensibleEnum {
*/
Collection<Phase> phases();
/**
* Stream of phases containing all child phases recursively.
*/
default Stream<Phase> allPhases() {
return phases().stream().flatMap(Phase::allPhases);
}
/**
* Collection of aliases.
*/
Collection<Alias> aliases();
/**
* Pre-ordered list of phases.
* If not provided, a default order will be computed.
@ -72,10 +92,108 @@ public interface Lifecycle extends ExtensibleEnum {
/**
* A phase in the lifecycle.
*
* A phase is identified by its name. It also contains a list of plugins bound to that phase,
* a list of {@link Link links}, and a list of sub-phases. This forms a tree of phases.
*/
interface Phase {
// ======================
// Maven defined phases
// ======================
String BUILD = "build";
String INITIALIZE = "initialize";
String VALIDATE = "validate";
String SOURCES = "sources";
String RESOURCES = "resources";
String COMPILE = "compile";
String READY = "ready";
String PACKAGE = "package";
String VERIFY = "verify";
String UNIT_TEST = "unit-test";
String TEST_SOURCES = "test-sources";
String TEST_RESOURCES = "test-resources";
String TEST_COMPILE = "test-compile";
String TEST = "test";
String INTEGRATION_TEST = "integration-test";
String INSTALL = "install";
String DEPLOY = "deploy";
String CLEAN = "clean";
@Nonnull
String name();
@Nonnull
List<Plugin> plugins();
@Nonnull
Collection<Link> links();
/**
* {@return the list of sub-phases}
*/
@Nonnull
List<Phase> phases();
@Nonnull
Stream<Phase> allPhases();
}
/**
* A phase alias, mostly used to support the Maven 3 phases which are mapped
* to dynamic phases in Maven 4.
*/
interface Alias {
String v3Phase();
String v4Phase();
}
/**
* A link from a phase to another phase, consisting of a type which can be
* {@link Kind#BEFORE} or {@link Kind#AFTER}, and a {@link Pointer} to
* another phase.
*/
interface Link {
enum Kind {
BEFORE,
AFTER
}
Kind kind();
Pointer pointer();
}
interface Pointer {
enum Type {
PROJECT,
DEPENDENCIES,
CHILDREN
}
String phase();
Type type();
}
interface PhasePointer extends Pointer {
default Type type() {
return Type.PROJECT;
}
}
interface DependenciesPointer extends Pointer {
String scope(); // default: all
default Type type() {
return Type.DEPENDENCIES;
}
}
interface ChildrenPointer extends Pointer {
default Type type() {
return Type.CHILDREN;
}
}
}

View File

@ -0,0 +1,66 @@
/*
* 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 java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.apache.maven.api.annotations.Experimental;
/**
* Specifies that the mojo should be run after the specific phase.
*
* @since 4.0.0
*/
@Experimental
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Inherited
public @interface After {
/**
* Type of pointer.
* @see org.apache.maven.api.Lifecycle.Pointer.Type
*/
enum Type {
PROJECT,
DEPENDENCIES,
CHILDREN
}
/**
* The phase name.
*/
String phase();
/**
* The type of this pointer.
*/
Type type();
/**
* The scope for dependencies, only if {@code type() == Type.Dependencies}.
*/
String scope() default "";
}

View File

@ -18,6 +18,7 @@
*/
package org.apache.maven.api.services;
import java.util.List;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
@ -28,4 +29,6 @@ public interface LifecycleRegistry extends ExtensibleEnumRegistry<Lifecycle>, It
default Stream<Lifecycle> stream() {
return StreamSupport.stream(spliterator(), false);
}
List<String> computePhases(Lifecycle lifecycle);
}

View File

@ -94,7 +94,7 @@ under the License.
<phase>generate-sources</phase>
<configuration>
<velocityBasedir>${project.basedir}/../../src/mdo</velocityBasedir>
<version>1.0.0</version>
<version>2.0.0</version>
<models>
<model>src/main/mdo/lifecycle.mdo</model>
</models>

View File

@ -30,12 +30,12 @@ under the License.
<classes>
<class rootElement="true" xml.tagName="lifecycles" xsd.compositor="sequence">
<name>LifecycleConfiguration</name>
<version>1.0.0</version>
<version>1.0.0+</version>
<description>Root element of the {@code lifecycle.xml} file.</description>
<fields>
<field>
<name>lifecycles</name>
<version>1.0.0</version>
<version>1.0.0+</version>
<association xml.itemsStyle="flat">
<type>Lifecycle</type>
<multiplicity>*</multiplicity>
@ -45,19 +45,19 @@ under the License.
</class>
<class>
<name>Lifecycle</name>
<version>1.0.0</version>
<version>1.0.0+</version>
<description>A custom lifecycle mapping definition.</description>
<fields>
<field>
<name>id</name>
<required>true</required>
<version>1.0.0</version>
<version>1.0.0+</version>
<type>String</type>
<description>The ID of this lifecycle, for identification in the mojo descriptor.</description>
</field>
<field>
<name>phases</name>
<version>1.0.0</version>
<version>1.0.0+</version>
<description>The phase mappings for this lifecycle.</description>
<association>
<type>Phase</type>
@ -68,19 +68,35 @@ under the License.
</class>
<class>
<name>Phase</name>
<version>1.0.0</version>
<version>1.0.0+</version>
<description>A phase mapping definition.</description>
<fields>
<field>
<name>id</name>
<required>true</required>
<version>1.0.0</version>
<version>1.0.0+</version>
<type>String</type>
<description>The ID of this phase, e.g., &lt;code&gt;generate-sources&lt;/code&gt;.</description>
<description>The ID of this phase, e.g., {@code generate-sources}.</description>
</field>
<field xml.attribute="true">
<name>executionPoint</name>
<required>false</required>
<version>2.0.0+</version>
<type>String</type>
<defaultValue><![CDATA[]]></defaultValue>
<description><![CDATA[If specified, identifies this phase as a dynamic phase to decorate the specified phase id, e.g. {@code after} or {@code before}.]]></description>
</field>
<field xml.attribute="true">
<name>priority</name>
<required>false</required>
<version>2.0.0+</version>
<type>int</type>
<defaultValue>0</defaultValue>
<description>If specified, identifies a within phase prioritization of executions.</description>
</field>
<field>
<name>executions</name>
<version>1.0.0</version>
<version>1.0.0+</version>
<description>The goals to execute within the phase.</description>
<association>
<type>Execution</type>
@ -89,26 +105,51 @@ under the License.
</field>
<field>
<name>configuration</name>
<version>1.0.0</version>
<version>1.0.0+</version>
<type>DOM</type>
<description>Configuration to pass to all goals run in this phase.</description>
</field>
</fields>
<codeSegments>
<codeSegment>
<version>2.0.0+</version>
<code><![CDATA[
/**
* Get the effective ID of this phase, e.g.,
* {@code generate-sources} or {@code after:integration-test[1000]}.
*
* @return String
*/
public String getEffectiveId() {
if (executionPoint == null) {
if (priority == 0) {
return id;
}
return id + '[' + priority + ']';
}
if (priority == 0) {
return executionPoint + ':' + id;
}
return executionPoint + ':' + id + '[' + priority + ']';
}
]]></code>
</codeSegment>
</codeSegments>
</class>
<class>
<name>Execution</name>
<version>1.0.0</version>
<version>1.0.0+</version>
<description>A set of goals to execute.</description>
<fields>
<field>
<name>configuration</name>
<version>1.0.0</version>
<version>1.0.0+</version>
<type>DOM</type>
<description>Configuration to pass to the goals.</description>
</field>
<field>
<name>goals</name>
<version>1.0.0</version>
<version>1.0.0+</version>
<description>The goals to execute.</description>
<association>
<type>String</type>

View File

@ -364,6 +364,11 @@ public class ApiRunner {
public Optional<Lifecycle> lookup(String id) {
return Optional.empty();
}
@Override
public List<String> computePhases(Lifecycle lifecycle) {
return List.of();
}
};
}

View File

@ -62,6 +62,11 @@ public class EmptyLifecycleBindingsInjector extends DefaultLifecycleBindingsInje
public Optional<Lifecycle> lookup(String id) {
return Optional.empty();
}
@Override
public List<String> computePhases(Lifecycle lifecycle) {
return List.of();
}
};
private static final PackagingRegistry emptyPackagingRegistry = new PackagingRegistry() {
@ -140,6 +145,11 @@ public class EmptyLifecycleBindingsInjector extends DefaultLifecycleBindingsInje
protected LifecycleRegistry getDelegate() {
return lifecycleRegistry;
}
@Override
public List<String> computePhases(Lifecycle lifecycle) {
return List.of();
}
}
static class WrapperPackagingRegistry implements PackagingRegistry {

View File

@ -24,6 +24,7 @@ import javax.inject.Provider;
import javax.inject.Singleton;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
@ -47,8 +48,26 @@ import org.apache.maven.lifecycle.mapping.LifecyclePhase;
import org.codehaus.plexus.PlexusContainer;
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
import static java.util.Arrays.asList;
import static java.util.Collections.singleton;
import static org.apache.maven.api.Lifecycle.Phase.BUILD;
import static org.apache.maven.api.Lifecycle.Phase.COMPILE;
import static org.apache.maven.api.Lifecycle.Phase.DEPLOY;
import static org.apache.maven.api.Lifecycle.Phase.INITIALIZE;
import static org.apache.maven.api.Lifecycle.Phase.INSTALL;
import static org.apache.maven.api.Lifecycle.Phase.INTEGRATION_TEST;
import static org.apache.maven.api.Lifecycle.Phase.PACKAGE;
import static org.apache.maven.api.Lifecycle.Phase.READY;
import static org.apache.maven.api.Lifecycle.Phase.RESOURCES;
import static org.apache.maven.api.Lifecycle.Phase.SOURCES;
import static org.apache.maven.api.Lifecycle.Phase.TEST;
import static org.apache.maven.api.Lifecycle.Phase.TEST_COMPILE;
import static org.apache.maven.api.Lifecycle.Phase.TEST_RESOURCES;
import static org.apache.maven.api.Lifecycle.Phase.TEST_SOURCES;
import static org.apache.maven.api.Lifecycle.Phase.UNIT_TEST;
import static org.apache.maven.api.Lifecycle.Phase.VALIDATE;
import static org.apache.maven.api.Lifecycle.Phase.VERIFY;
import static org.apache.maven.internal.impl.Lifecycles.after;
import static org.apache.maven.internal.impl.Lifecycles.alias;
import static org.apache.maven.internal.impl.Lifecycles.dependencies;
import static org.apache.maven.internal.impl.Lifecycles.phase;
import static org.apache.maven.internal.impl.Lifecycles.plugin;
@ -59,6 +78,8 @@ import static org.apache.maven.internal.impl.Lifecycles.plugin;
@Singleton
public class DefaultLifecycleRegistry implements LifecycleRegistry {
private static final String MAVEN_PLUGINS = "org.apache.maven.plugins:";
private final List<LifecycleProvider> providers;
public DefaultLifecycleRegistry() {
@ -73,7 +94,7 @@ public class DefaultLifecycleRegistry implements LifecycleRegistry {
// validate lifecycle
for (Lifecycle lifecycle : this) {
Set<String> set = new HashSet<>();
lifecycle.phases().forEach(phase -> {
lifecycle.allPhases().forEach(phase -> {
if (!set.add(phase.name())) {
throw new IllegalArgumentException(
"Found duplicated phase '" + phase.name() + "' in '" + lifecycle.id() + "' lifecycle");
@ -97,6 +118,57 @@ public class DefaultLifecycleRegistry implements LifecycleRegistry {
return stream().filter(lf -> Objects.equals(id, lf.id())).findAny();
}
public List<String> computePhases(Lifecycle lifecycle) {
Graph graph = new Graph();
lifecycle.phases().forEach(phase -> addPhase(graph, null, null, phase));
List<String> allPhases = graph.visitAll();
Collections.reverse(allPhases);
List<String> computed =
allPhases.stream().filter(s -> !s.startsWith("$")).collect(Collectors.toList());
List<String> given = lifecycle.orderedPhases().orElse(null);
if (given != null) {
if (given.size() != computed.size()) {
Set<String> s1 =
given.stream().filter(s -> !computed.contains(s)).collect(Collectors.toSet());
Set<String> s2 =
computed.stream().filter(s -> !given.contains(s)).collect(Collectors.toSet());
throw new IllegalStateException(
"List of phases differ in size: expected " + computed.size() + ", but received " + given.size()
+ (s1.isEmpty() ? "" : ", missing " + s1)
+ (s2.isEmpty() ? "" : ", unexpected " + s2));
}
return given;
}
return computed;
}
private static void addPhase(
Graph graph, Graph.Vertex before, Graph.Vertex after, org.apache.maven.api.Lifecycle.Phase phase) {
Graph.Vertex ep0 = graph.addVertex("$" + phase.name());
Graph.Vertex ep1 = graph.addVertex("$$" + phase.name());
Graph.Vertex ep2 = graph.addVertex(phase.name());
Graph.Vertex ep3 = graph.addVertex("$$$" + phase.name());
graph.addEdge(ep0, ep1);
graph.addEdge(ep1, ep2);
graph.addEdge(ep2, ep3);
if (before != null) {
graph.addEdge(before, ep0);
}
if (after != null) {
graph.addEdge(ep3, after);
}
phase.links().forEach(link -> {
if (link.pointer().type() == Lifecycle.Pointer.Type.PROJECT) {
if (link.kind() == Lifecycle.Link.Kind.AFTER) {
graph.addEdge(graph.addVertex(link.pointer().phase()), ep0);
} else {
graph.addEdge(ep3, graph.addVertex("$" + link.pointer().phase()));
}
}
});
phase.phases().forEach(child -> addPhase(graph, ep1, ep2, child));
}
@Named
@Singleton
public static class LifecycleWrapperProvider implements LifecycleProvider {
@ -140,6 +212,17 @@ public class DefaultLifecycleRegistry implements LifecycleRegistry {
return name;
}
@Override
public List<Phase> phases() {
return List.of();
}
@Override
public Stream<Phase> allPhases() {
return Stream.concat(
Stream.of(this), phases().stream().flatMap(Lifecycle.Phase::allPhases));
}
@Override
public List<Plugin> plugins() {
Map<String, LifecyclePhase> lfPhases = lifecycle.getDefaultLifecyclePhases();
@ -151,16 +234,26 @@ public class DefaultLifecycleRegistry implements LifecycleRegistry {
}
return List.of();
}
@Override
public Collection<Link> links() {
return List.of();
}
})
.toList();
}
@Override
public Collection<Alias> aliases() {
return Collections.emptyList();
}
};
}
}
static class WrappedLifecycle extends org.apache.maven.lifecycle.Lifecycle {
WrappedLifecycle(Lifecycle lifecycle) {
super(lifecycle);
WrappedLifecycle(LifecycleRegistry registry, Lifecycle lifecycle) {
super(registry, lifecycle);
}
}
@ -178,7 +271,7 @@ public class DefaultLifecycleRegistry implements LifecycleRegistry {
public org.apache.maven.lifecycle.Lifecycle get() {
try {
LifecycleRegistry registry = lookup.lookup(LifecycleRegistry.class);
return new WrappedLifecycle(registry.require(name));
return new WrappedLifecycle(registry, registry.require(name));
} catch (ComponentLookupException e) {
throw new LookupException(e);
}
@ -187,6 +280,7 @@ public class DefaultLifecycleRegistry implements LifecycleRegistry {
@Singleton
@Named(Lifecycle.CLEAN)
@SuppressWarnings("unused")
static class CleanLifecycleProvider extends BaseLifecycleProvider {
CleanLifecycleProvider() {
super(Lifecycle.CLEAN);
@ -195,6 +289,7 @@ public class DefaultLifecycleRegistry implements LifecycleRegistry {
@Singleton
@Named(Lifecycle.DEFAULT)
@SuppressWarnings("unused")
static class DefaultLifecycleProvider extends BaseLifecycleProvider {
DefaultLifecycleProvider() {
super(Lifecycle.DEFAULT);
@ -203,6 +298,7 @@ public class DefaultLifecycleRegistry implements LifecycleRegistry {
@Singleton
@Named(Lifecycle.SITE)
@SuppressWarnings("unused")
static class SiteLifecycleProvider extends BaseLifecycleProvider {
SiteLifecycleProvider() {
super(Lifecycle.SITE);
@ -211,6 +307,7 @@ public class DefaultLifecycleRegistry implements LifecycleRegistry {
@Singleton
@Named(Lifecycle.WRAPPER)
@SuppressWarnings("unused")
static class WrapperLifecycleProvider extends BaseLifecycleProvider {
WrapperLifecycleProvider() {
super(Lifecycle.WRAPPER);
@ -228,15 +325,16 @@ public class DefaultLifecycleRegistry implements LifecycleRegistry {
@Override
public Collection<Phase> phases() {
return asList(
phase("pre-clean"),
phase(
"clean",
return List.of(phase(
Phase.CLEAN,
plugin(
"org.apache.maven.plugins:maven-clean-plugin:" + MAVEN_CLEAN_PLUGIN_VERSION
+ ":clean",
"clean")),
phase("post-clean"));
MAVEN_PLUGINS + "maven-clean-plugin:" + MAVEN_CLEAN_PLUGIN_VERSION + ":clean",
Phase.CLEAN)));
}
@Override
public Collection<Alias> aliases() {
return List.of(alias("pre-clean", BEFORE + Phase.CLEAN), alias("post-clean", AFTER + Phase.CLEAN));
}
}
@ -248,36 +346,101 @@ public class DefaultLifecycleRegistry implements LifecycleRegistry {
@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"));
return List.of(phase(
"all",
phase(INITIALIZE, phase(VALIDATE)),
phase(
BUILD,
after(VALIDATE),
phase(SOURCES),
phase(RESOURCES),
phase(COMPILE, after(SOURCES), dependencies(COMPILE, READY)),
phase(READY, after(COMPILE), after(RESOURCES)),
phase(PACKAGE, after(READY), dependencies("runtime", PACKAGE))),
phase(
VERIFY,
after(VALIDATE),
phase(
UNIT_TEST,
phase(TEST_SOURCES),
phase(TEST_RESOURCES),
phase(
TEST_COMPILE,
after(TEST_SOURCES),
after(READY),
dependencies("test-only", READY)),
phase(
TEST,
after(TEST_COMPILE),
after(TEST_RESOURCES),
dependencies("test", READY))),
phase(INTEGRATION_TEST)),
phase(INSTALL, after(PACKAGE)),
phase(DEPLOY, after(PACKAGE))));
}
@Override
public Collection<Alias> aliases() {
return List.of(
alias("generate-sources", SOURCES),
alias("process-sources", AFTER + SOURCES),
alias("generate-resources", RESOURCES),
alias("process-resources", AFTER + RESOURCES),
alias("process-classes", AFTER + COMPILE),
alias("generate-test-sources", TEST_SOURCES),
alias("process-test-sources", AFTER + TEST_SOURCES),
alias("generate-test-resources", TEST_RESOURCES),
alias("process-test-resources", AFTER + TEST_RESOURCES),
alias("process-test-classes", AFTER + TEST_COMPILE),
alias("prepare-package", BEFORE + PACKAGE),
alias("pre-integration-test", BEFORE + INTEGRATION_TEST),
alias("post-integration-test", AFTER + INTEGRATION_TEST));
}
@Override
public Optional<List<String>> orderedPhases() {
return Optional.of(Arrays.asList(
VALIDATE,
INITIALIZE,
// "generate-sources",
SOURCES,
// "process-sources",
// "generate-resources",
RESOURCES,
// "process-resources",
COMPILE,
// "process-classes",
READY,
// "generate-test-sources",
TEST_SOURCES,
// "process-test-sources",
// "generate-test-resources",
TEST_RESOURCES,
// "process-test-resources",
TEST_COMPILE,
// "process-test-classes",
TEST,
UNIT_TEST,
// "prepare-package",
PACKAGE,
BUILD,
// "pre-integration-test",
INTEGRATION_TEST,
// "post-integration-test",
VERIFY,
INSTALL,
DEPLOY,
"all"));
}
}
static class SiteLifecycle implements Lifecycle {
private static final String MAVEN_SITE_PLUGIN_VERSION = "3.12.1";
private static final String MAVEN_SITE_PLUGIN =
MAVEN_PLUGINS + "maven-site-plugin:" + MAVEN_SITE_PLUGIN_VERSION + ":";
private static final String PHASE_SITE = "site";
private static final String PHASE_SITE_DEPLOY = "site-deploy";
@Override
public String id() {
@ -286,26 +449,24 @@ public class DefaultLifecycleRegistry implements LifecycleRegistry {
@Override
public Collection<Phase> phases() {
return asList(
phase("pre-site"),
return List.of(
phase(PHASE_SITE, plugin(MAVEN_SITE_PLUGIN + "site", PHASE_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")));
PHASE_SITE_DEPLOY,
after(PHASE_SITE),
plugin(MAVEN_SITE_PLUGIN + "deploy", PHASE_SITE_DEPLOY)));
}
@Override
public Collection<Alias> aliases() {
return List.of(alias("pre-site", BEFORE + PHASE_SITE), alias("post-site", AFTER + PHASE_SITE));
}
}
static class WrapperLifecycle implements Lifecycle {
private static final String MAVEN_WRAPPER_PLUGIN_VERSION = "3.2.0";
private static final String PHASE_WRAPPER = "wrapper";
@Override
public String id() {
@ -314,12 +475,16 @@ public class DefaultLifecycleRegistry implements LifecycleRegistry {
@Override
public Collection<Phase> phases() {
return singleton(phase(
"wrapper",
return List.of(phase(
PHASE_WRAPPER,
plugin(
"org.apache.maven.plugins:maven-wrapper-plugin:" + MAVEN_WRAPPER_PLUGIN_VERSION
+ ":wrapper",
"wrapper")));
MAVEN_PLUGINS + "maven-wrapper-plugin:" + MAVEN_WRAPPER_PLUGIN_VERSION + ":wrapper",
PHASE_WRAPPER)));
}
@Override
public Collection<Alias> aliases() {
return List.of();
}
}
}

View File

@ -18,21 +18,52 @@
*/
package org.apache.maven.internal.impl;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.stream.Stream;
import org.apache.maven.api.Lifecycle;
import org.apache.maven.api.model.Plugin;
import org.apache.maven.api.model.PluginExecution;
import static java.util.Arrays.asList;
public class Lifecycles {
static Lifecycle.Phase phase(String name) {
return new DefaultPhase(name, Collections.emptyList(), Collections.emptyList());
return new DefaultPhase(name, Collections.emptyList(), Collections.emptyList(), Collections.emptyList());
}
static Lifecycle.Phase phase(String name, Lifecycle.Phase... phases) {
return new DefaultPhase(name, Collections.emptyList(), Collections.emptyList(), asList(phases));
}
static Lifecycle.Phase phase(String name, Lifecycle.Link link, Lifecycle.Phase... phases) {
return new DefaultPhase(name, Collections.emptyList(), Collections.singletonList(link), asList(phases));
}
static Lifecycle.Phase phase(String name, Plugin plugin) {
return new DefaultPhase(name, Collections.singletonList(plugin), Collections.emptyList());
return new DefaultPhase(
name, Collections.singletonList(plugin), Collections.emptyList(), Collections.emptyList());
}
static Lifecycle.Phase phase(String name, Lifecycle.Link link, Plugin plugin) {
return new DefaultPhase(
name, Collections.singletonList(plugin), Collections.singletonList(link), Collections.emptyList());
}
static Lifecycle.Phase phase(String name, Lifecycle.Link link1, Lifecycle.Link link2, Lifecycle.Phase... phases) {
return new DefaultPhase(name, Collections.emptyList(), asList(link1, link2), asList(phases));
}
static Lifecycle.Phase phase(
String name, Lifecycle.Link link1, Lifecycle.Link link2, Lifecycle.Link link3, Lifecycle.Phase... phases) {
return new DefaultPhase(name, Collections.emptyList(), asList(link1, link2, link3), asList(phases));
}
static Lifecycle.Phase phase(String name, Collection<Lifecycle.Link> links, Lifecycle.Phase... phases) {
return new DefaultPhase(name, Collections.emptyList(), links, asList(phases));
}
static Plugin plugin(String coords, String phase) {
@ -49,14 +80,66 @@ public class Lifecycles {
.build();
}
/** Indicates the phase is after the phases given in arguments */
static Lifecycle.Link after(String b) {
return new Lifecycle.Link() {
@Override
public Kind kind() {
return Kind.AFTER;
}
@Override
public Lifecycle.Pointer pointer() {
return new Lifecycle.PhasePointer() {
@Override
public String phase() {
return b;
}
};
}
};
}
/** Indicates the phase is after the phases for the dependencies in the given scope */
static Lifecycle.Link dependencies(String scope, String phase) {
return new Lifecycle.Link() {
@Override
public Kind kind() {
return Kind.AFTER;
}
@Override
public Lifecycle.Pointer pointer() {
return new Lifecycle.DependenciesPointer() {
@Override
public String phase() {
return phase;
}
@Override
public String scope() {
return scope;
}
};
}
};
}
static Lifecycle.Alias alias(String v3Phase, String v4Phase) {
return new DefaultAlias(v3Phase, v4Phase);
}
static class DefaultPhase implements Lifecycle.Phase {
private final String name;
private final List<Plugin> plugins;
private final Collection<Lifecycle.Link> links;
private final List<Lifecycle.Phase> phases;
DefaultPhase(String name, List<Plugin> plugins, List<Lifecycle.Phase> phases) {
DefaultPhase(
String name, List<Plugin> plugins, Collection<Lifecycle.Link> links, List<Lifecycle.Phase> phases) {
this.name = name;
this.plugins = plugins;
this.links = links;
this.phases = phases;
}
@ -69,5 +152,40 @@ public class Lifecycles {
public List<Plugin> plugins() {
return plugins;
}
@Override
public Collection<Lifecycle.Link> links() {
return links;
}
@Override
public List<Lifecycle.Phase> phases() {
return phases;
}
@Override
public Stream<Lifecycle.Phase> allPhases() {
return Stream.concat(Stream.of(this), phases().stream().flatMap(Lifecycle.Phase::allPhases));
}
}
static class DefaultAlias implements Lifecycle.Alias {
private final String v3Phase;
private final String v4Phase;
DefaultAlias(String v3Phase, String v4Phase) {
this.v3Phase = v3Phase;
this.v4Phase = v4Phase;
}
@Override
public String v3Phase() {
return v3Phase;
}
@Override
public String v4Phase() {
return v4Phase;
}
}
}

View File

@ -56,6 +56,8 @@ public class DefaultLifecycles {
private Map<String, Lifecycle> customLifecycles;
private boolean lifecyclesPrinted;
public DefaultLifecycles() {
this.lookup = null;
this.registry = null;
@ -94,20 +96,23 @@ public class DefaultLifecycles {
* @return A map of lifecycles, indexed on id
*/
public Map<String, Lifecycle> getPhaseToLifecycleMap() {
if (logger.isDebugEnabled() && !lifecyclesPrinted) {
for (Lifecycle lifecycle : getLifeCycles()) {
logger.debug("Lifecycle {}", lifecycle);
}
lifecyclesPrinted = true;
}
// If people are going to make their own lifecycles then we need to tell people how to namespace them correctly
// so that they don't interfere with internally defined lifecycles.
Map<String, Lifecycle> phaseToLifecycleMap = new HashMap<>();
for (Lifecycle lifecycle : getLifeCycles()) {
logger.debug("Lifecycle {}", lifecycle);
for (String phase : lifecycle.getPhases()) {
// The first definition wins.
if (!phaseToLifecycleMap.containsKey(phase)) {
phaseToLifecycleMap.put(phase, lifecycle);
} else if (logger.isWarnEnabled()) {
Lifecycle original = phaseToLifecycleMap.get(phase);
Lifecycle original = phaseToLifecycleMap.put(phase, lifecycle);
if (original != null && logger.isWarnEnabled()) {
logger.warn(
"Duplicated lifecycle phase {}. Defined in {} but also in {}",
phase,
@ -115,6 +120,19 @@ public class DefaultLifecycles {
lifecycle.getId());
}
}
if (lifecycle.getDelegate() != null) {
for (org.apache.maven.api.Lifecycle.Alias alias :
lifecycle.getDelegate().aliases()) {
Lifecycle original = phaseToLifecycleMap.put(alias.v3Phase(), lifecycle);
if (original != null && logger.isWarnEnabled()) {
logger.warn(
"Duplicated lifecycle phase {}. Defined in {} but also in {}",
alias.v3Phase(),
original.getId(),
lifecycle.getId());
}
}
}
}
return phaseToLifecycleMap;
@ -156,7 +174,7 @@ public class DefaultLifecycles {
// Lifecycles cannot be cached as extensions might add custom lifecycles later in the execution.
try {
return registry != null
? registry.stream().collect(Collectors.toMap(lf -> lf.id(), lf -> new Lifecycle(lf)))
? registry.stream().collect(Collectors.toMap(lf -> lf.id(), lf -> new Lifecycle(registry, lf)))
: Map.of();
} catch (LookupException e) {
throw new IllegalStateException("Unable to lookup lifecycles from the plexus container", e);

View File

@ -23,9 +23,13 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.maven.lifecycle.mapping.LifecyclePhase;
import static org.apache.maven.api.Lifecycle.AFTER;
import static org.apache.maven.api.Lifecycle.BEFORE;
/**
* Lifecycle definition, with eventual plugin bindings (when they are not packaging-specific).
*/
@ -38,11 +42,12 @@ public class Lifecycle {
this.defaultPhases = defaultPhases;
}
public Lifecycle(org.apache.maven.api.Lifecycle lifecycle) {
public Lifecycle(
org.apache.maven.api.services.LifecycleRegistry registry, org.apache.maven.api.Lifecycle lifecycle) {
this.lifecycle = lifecycle;
this.id = lifecycle.id();
this.phases = lifecycle.phases().stream()
.map(org.apache.maven.api.Lifecycle.Phase::name)
this.phases = registry.computePhases(lifecycle).stream()
.flatMap(p -> Stream.of(BEFORE + p, p, AFTER + p))
.toList();
this.defaultPhases = getDefaultPhases(lifecycle);
}
@ -71,13 +76,17 @@ public class Lifecycle {
return id;
}
public org.apache.maven.api.Lifecycle getDelegate() {
return lifecycle;
}
public List<String> getPhases() {
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()
lifecycle.allPhases().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()
@ -97,6 +106,10 @@ public class Lifecycle {
@Override
public String toString() {
return id + " -> " + phases;
return id + " -> "
+ lifecycle
.allPhases()
.map(org.apache.maven.api.Lifecycle.Phase::name)
.collect(Collectors.joining(", ", "[", "]"));
}
}

View File

@ -475,7 +475,14 @@ public class DefaultLifecycleExecutionPlanCalculator implements LifecycleExecuti
}
for (Phase phase : lifecycleOverlay.getPhases()) {
List<MojoExecution> forkedExecutions = lifecycleMappings.get(phase.getId());
String phaseId = defaultLifecycles.getLifeCycles().stream()
.flatMap(l -> l.getDelegate().aliases().stream())
.filter(a -> phase.getId().equals(a.v3Phase()))
.findFirst()
.map(a -> a.v4Phase())
.orElse(phase.getId());
List<MojoExecution> forkedExecutions = lifecycleMappings.get(phaseId);
if (forkedExecutions != null) {
for (Execution execution : phase.getExecutions()) {

View File

@ -23,10 +23,12 @@ import javax.inject.Named;
import javax.inject.Singleton;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.stream.Collectors;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.lifecycle.Lifecycle;
@ -70,10 +72,19 @@ public class DefaultLifecycleMappingDelegate implements LifecycleMappingDelegate
* is interested in, i.e. all phases up to and including the specified phase.
*/
Map<String, Map<Integer, List<MojoExecution>>> mappings = new LinkedHashMap<>();
Map<String, Map<PhaseId, List<MojoExecution>>> mappings =
new TreeMap<>(new PhaseComparator(lifecycle.getPhases()));
Map<String, String> aliases = lifecycle.getDelegate().aliases().stream()
.collect(Collectors.toMap(a -> a.v3Phase(), a -> a.v4Phase()));
if (aliases.containsKey(lifecyclePhase)) {
lifecyclePhase = PhaseId.of(aliases.get(lifecyclePhase)).phase();
}
for (String phase : lifecycle.getPhases()) {
Map<Integer, List<MojoExecution>> phaseBindings = new TreeMap<>();
Map<PhaseId, List<MojoExecution>> phaseBindings =
new TreeMap<>(Comparator.comparing(PhaseId::toString, new PhaseComparator(lifecycle.getPhases())));
mappings.put(phase, phaseBindings);
@ -93,13 +104,21 @@ public class DefaultLifecycleMappingDelegate implements LifecycleMappingDelegate
for (PluginExecution execution : plugin.getExecutions()) {
// if the phase is specified then I don't have to go fetch the plugin yet and pull it down
// to examine the phase it is associated to.
if (execution.getPhase() != null) {
Map<Integer, List<MojoExecution>> phaseBindings = mappings.get(execution.getPhase());
String phase = execution.getPhase();
if (aliases.containsKey(phase)) {
phase = aliases.get(phase);
}
if (phase != null) {
Map<PhaseId, List<MojoExecution>> phaseBindings = getPhaseBindings(mappings, phase);
if (phaseBindings != null) {
for (String goal : execution.getGoals()) {
MojoExecution mojoExecution = new MojoExecution(plugin, goal, execution.getId());
mojoExecution.setLifecyclePhase(execution.getPhase());
addMojoExecution(phaseBindings, mojoExecution, execution.getPriority());
mojoExecution.setLifecyclePhase(phase);
PhaseId phaseId = PhaseId.of(phase);
if (phaseId.priority() == 0) {
phaseId = PhaseId.of(phase + "[" + execution.getPriority() + "]");
}
addMojoExecution(phaseBindings, mojoExecution, phaseId);
}
}
}
@ -109,11 +128,16 @@ public class DefaultLifecycleMappingDelegate implements LifecycleMappingDelegate
MojoDescriptor mojoDescriptor = pluginManager.getMojoDescriptor(
plugin, goal, project.getRemotePluginRepositories(), session.getRepositorySession());
Map<Integer, List<MojoExecution>> phaseBindings = mappings.get(mojoDescriptor.getPhase());
phase = mojoDescriptor.getPhase();
if (aliases.containsKey(phase)) {
phase = aliases.get(phase);
}
Map<PhaseId, List<MojoExecution>> phaseBindings = getPhaseBindings(mappings, phase);
if (phaseBindings != null) {
MojoExecution mojoExecution = new MojoExecution(mojoDescriptor, execution.getId());
mojoExecution.setLifecyclePhase(mojoDescriptor.getPhase());
addMojoExecution(phaseBindings, mojoExecution, execution.getPriority());
mojoExecution.setLifecyclePhase(phase);
PhaseId phaseId = PhaseId.of(phase + "[" + execution.getPriority() + "]");
addMojoExecution(phaseBindings, mojoExecution, phaseId);
}
}
}
@ -122,7 +146,7 @@ public class DefaultLifecycleMappingDelegate implements LifecycleMappingDelegate
Map<String, List<MojoExecution>> lifecycleMappings = new LinkedHashMap<>();
for (Map.Entry<String, Map<Integer, List<MojoExecution>>> entry : mappings.entrySet()) {
for (Map.Entry<String, Map<PhaseId, List<MojoExecution>>> entry : mappings.entrySet()) {
List<MojoExecution> mojoExecutions = new ArrayList<>();
for (List<MojoExecution> executions : entry.getValue().values()) {
@ -135,9 +159,18 @@ public class DefaultLifecycleMappingDelegate implements LifecycleMappingDelegate
return lifecycleMappings;
}
private Map<PhaseId, List<MojoExecution>> getPhaseBindings(
Map<String, Map<PhaseId, List<MojoExecution>>> mappings, String phase) {
if (phase != null) {
PhaseId id = PhaseId.of(phase);
return mappings.get(id.phase());
}
return null;
}
private void addMojoExecution(
Map<Integer, List<MojoExecution>> phaseBindings, MojoExecution mojoExecution, int priority) {
List<MojoExecution> mojoExecutions = phaseBindings.computeIfAbsent(priority, k -> new ArrayList<>());
Map<PhaseId, List<MojoExecution>> phaseBindings, MojoExecution mojoExecution, PhaseId phaseId) {
List<MojoExecution> mojoExecutions = phaseBindings.computeIfAbsent(phaseId, k -> new ArrayList<>());
mojoExecutions.add(mojoExecution);
}

View File

@ -0,0 +1,74 @@
/*
* 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.internal;
import java.util.Comparator;
import java.util.List;
/**
* Compares phases within the context of a specific lifecycle with secondary sorting based on the {@link PhaseId}.
*/
public class PhaseComparator implements Comparator<String> {
/**
* The lifecycle phase ordering.
*/
private final List<String> lifecyclePhases;
/**
* Constructor.
*
* @param lifecyclePhases the lifecycle phase ordering.
*/
public PhaseComparator(List<String> lifecyclePhases) {
this.lifecyclePhases = lifecyclePhases;
}
@Override
public int compare(String o1, String o2) {
PhaseId p1 = PhaseId.of(o1);
PhaseId p2 = PhaseId.of(o2);
int i1 = lifecyclePhases.indexOf(p1.phase());
int i2 = lifecyclePhases.indexOf(p2.phase());
if (i1 == -1 && i2 == -1) {
// unknown phases, leave in existing order
return 0;
}
if (i1 == -1) {
// second one is known, so it comes first
return 1;
}
if (i2 == -1) {
// first one is known, so it comes first
return -1;
}
int rv = Integer.compare(i1, i2);
if (rv != 0) {
return rv;
}
// same phase, now compare execution points
i1 = p1.executionPoint().ordinal();
i2 = p2.executionPoint().ordinal();
rv = Integer.compare(i1, i2);
if (rv != 0) {
return rv;
}
// same execution point, now compare priorities
return Integer.compare(p1.priority(), p2.priority());
}
}

View File

@ -0,0 +1,50 @@
/*
* 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.internal;
/**
* Represents where a dynamic phase should be executed within a static phase.
*/
public enum PhaseExecutionPoint {
/**
* Execution must occur before any executions of the phase proper. Failure of any {@code #BEFORE} dynamic phase
* execution will prevent the {@link #AT} phase but will not prevent any {@link #AFTER} dynamic phases.
*/
BEFORE("before:"),
/**
* Execution is the execution of the phase proper. Failure of any {@code #AT} dynamic phase execution will fail
* the phase. Any {@link #AFTER} phases will still be executed.
*/
AT(""),
/**
* Guaranteed execution dynamic phases on completion of the static phase. All {@code #AFTER} dynamic phases will
* be executed provided at least one {@link #BEFORE} or {@link #AT} dynamic phase has started execution.
*/
AFTER("after:");
private final String prefix;
PhaseExecutionPoint(String prefix) {
this.prefix = prefix;
}
public String prefix() {
return prefix;
}
}

View File

@ -0,0 +1,142 @@
/*
* 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.internal;
import java.util.Map;
import java.util.Objects;
import java.util.WeakHashMap;
/**
* Represents a parsed phase identifier.
*/
public class PhaseId {
/**
* Interned {@link PhaseId} instances.
*/
private static final Map<String, PhaseId> INSTANCES = new WeakHashMap<>();
/**
* The execution point of this {@link PhaseId}.
*/
private final PhaseExecutionPoint executionPoint;
/**
* The static phase that this dynamic phase belongs to.
*/
private final String phase;
/**
* The priority of this dynamic phase within the static phase.
*/
private final int priority;
/**
* Parses the phase identifier.
*
* @param phase the phase identifier.
* @return the {@link PhaseId}.
*/
public static synchronized PhaseId of(String phase) {
return INSTANCES.computeIfAbsent(phase, PhaseId::new);
}
/**
* Constructor.
*
* @param phase the phase identifier string.
*/
private PhaseId(String phase) {
int phaseStart;
if (phase.startsWith(PhaseExecutionPoint.BEFORE.prefix())) {
executionPoint = PhaseExecutionPoint.BEFORE;
phaseStart = PhaseExecutionPoint.BEFORE.prefix().length();
} else if (phase.startsWith(PhaseExecutionPoint.AFTER.prefix())) {
executionPoint = PhaseExecutionPoint.AFTER;
phaseStart = PhaseExecutionPoint.AFTER.prefix().length();
} else {
executionPoint = PhaseExecutionPoint.AT;
phaseStart = 0;
}
int phaseEnd = phase.indexOf('[');
if (phaseEnd == -1) {
priority = 0;
this.phase = phase.substring(phaseStart);
} else {
int priorityEnd = phase.lastIndexOf(']');
boolean hasPriority;
int priority;
if (priorityEnd < phaseEnd + 1) {
priority = 0;
hasPriority = false;
} else {
try {
priority = Integer.parseInt(phase.substring(phaseEnd + 1, priorityEnd));
hasPriority = true;
} catch (NumberFormatException e) {
// priority must be an integer
priority = 0;
hasPriority = false;
}
}
if (hasPriority) {
this.phase = phase.substring(phaseStart, phaseEnd);
this.priority = priority;
} else {
this.phase = phase.substring(phaseStart);
this.priority = 0;
}
}
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o == null || getClass() != o.getClass()) {
return false;
} else {
PhaseId phaseId = (PhaseId) o;
return Objects.equals(executionPoint(), phaseId.executionPoint())
&& Objects.equals(phase(), phaseId.phase())
&& Objects.equals(priority(), phaseId.priority());
}
}
@Override
public int hashCode() {
return Objects.hash(executionPoint(), phase(), priority());
}
@Override
public String toString() {
return executionPoint().prefix() + phase() + (priority() != 0 ? "[" + priority() + ']' : "");
}
public PhaseExecutionPoint executionPoint() {
return executionPoint;
}
public String phase() {
return phase;
}
public int priority() {
return priority;
}
}

View File

@ -38,11 +38,12 @@ public class PhaseRecorder {
String lifecyclePhase = mojoExecution.getLifecyclePhase();
if (lifecyclePhase != null) {
PhaseId phaseId = PhaseId.of(lifecyclePhase);
if (lastLifecyclePhase == null) {
lastLifecyclePhase = lifecyclePhase;
} else if (!lifecyclePhase.equals(lastLifecyclePhase)) {
lastLifecyclePhase = phaseId.phase();
} else if (!phaseId.phase().equals(lastLifecyclePhase)) {
project.addLifecyclePhase(lastLifecyclePhase);
lastLifecyclePhase = lifecyclePhase;
lastLifecyclePhase = phaseId.phase();
}
}
@ -56,6 +57,6 @@ public class PhaseRecorder {
if (lifecyclePhase == null) {
return lastLifecyclePhase != null;
}
return !lifecyclePhase.equals(lastLifecyclePhase);
return !PhaseId.of(lifecyclePhase).phase().equals(lastLifecyclePhase);
}
}

View File

@ -59,7 +59,7 @@ class DefaultLifecyclesTest {
void testDefaultLifecycle() {
final Lifecycle lifecycle = getLifeCycleById("default");
assertThat(lifecycle.getId(), is("default"));
assertThat(lifecycle.getPhases(), hasSize(23));
assertThat(lifecycle.getPhases(), hasSize(54));
}
@Test
@ -73,14 +73,14 @@ class DefaultLifecyclesTest {
void testSiteLifecycle() {
final Lifecycle lifecycle = getLifeCycleById("site");
assertThat(lifecycle.getId(), is("site"));
assertThat(lifecycle.getPhases(), hasSize(4));
assertThat(lifecycle.getPhases(), hasSize(6));
}
@Test
void testWrapperLifecycle() {
final Lifecycle lifecycle = getLifeCycleById("wrapper");
assertThat(lifecycle.getId(), is("wrapper"));
assertThat(lifecycle.getPhases(), hasSize(1));
assertThat(lifecycle.getPhases(), hasSize(3));
}
@Test

View File

@ -152,6 +152,7 @@ class LifecycleExecutorTest extends AbstractCoreMavenComponentTestCase {
}
// We need to take in multiple lifecycles
@Test
public void testCalculationOfBuildPlanTasksOfTheCleanLifecycleAndTheInstallLifecycle() throws Exception {
File pom = getProject("project-with-additional-lifecycle-elements");
MavenSession session = createMavenSession(pom);
@ -195,6 +196,7 @@ class LifecycleExecutorTest extends AbstractCoreMavenComponentTestCase {
}
// We need to take in multiple lifecycles
@Test
public void testCalculationOfBuildPlanWithMultipleExecutionsOfModello() throws Exception {
File pom = getProject("project-with-multiple-executions");
MavenSession session = createMavenSession(pom);

View File

@ -61,6 +61,11 @@ public class EmptyLifecycleBindingsInjector extends DefaultLifecycleBindingsInje
public Optional<Lifecycle> lookup(String id) {
return Optional.empty();
}
@Override
public List<String> computePhases(Lifecycle lifecycle) {
return List.of();
}
};
private static final PackagingRegistry emptyPackagingRegistry = new PackagingRegistry() {
@ -139,6 +144,11 @@ public class EmptyLifecycleBindingsInjector extends DefaultLifecycleBindingsInje
protected LifecycleRegistry getDelegate() {
return lifecycleRegistry;
}
@Override
public List<String> computePhases(Lifecycle lifecycle) {
return List.of();
}
}
static class WrapperPackagingRegistry implements PackagingRegistry {

View File

@ -117,7 +117,7 @@ under the License.
<models>
<model>../api/maven-api-plugin/src/main/mdo/lifecycle.mdo</model>
</models>
<version>1.0.0</version>
<version>2.0.0</version>
</configuration>
</execution>
<execution>

View File

@ -551,6 +551,9 @@ public class ${className} {
#foreach ( $field in $allFields )
#if ( $Helper.xmlFieldMetadata( $field ).attribute )
#set ( $fieldTagName = $Helper.xmlFieldMetadata( $field ).tagName )
#if ( ! $fieldTagName )
#set ( $fieldTagName = $field.name )
#end
#set ( $fieldCapName = $Helper.capitalise( $field.name ) )
} else if ("$fieldTagName".equals(name)) {
#if ( $locationTracking )
@ -562,6 +565,8 @@ public class ${className} {
${classLcapName}.${field.name}(interpolatedTrimmed(value, "$fieldTagName"));
#elseif ( $field.type == "boolean" || $field.type == "Boolean" )
${classLcapName}.${field.name}(getBooleanValue(interpolatedTrimmed(value, "$fieldTagName"), "$fieldTagName", parser, ${field.defaultValue}));
#elseif ( $field.type == "int" || $field.type == "Integer" )
${classLcapName}.${field.name}(getIntegerValue(interpolatedTrimmed(value, "$fieldTagName"), "$fieldTagName", parser, strict, ${field.defaultValue}));
#else
// TODO: type=${field.type} to=${field.to} multiplicity=${field.multiplicity}
#end

View File

@ -227,6 +227,9 @@ public class ${className} {
#foreach ( $field in $allFields )
#if ( $Helper.xmlFieldMetadata( $field ).attribute && ! $Helper.xmlFieldMetadata( $field ).format )
#set ( $fieldTagName = $Helper.xmlFieldMetadata( $field ).tagName )
#if ( ! $fieldTagName )
#set ( $fieldTagName = $field.name )
#end
#set ( $fieldCapName = $Helper.capitalise( $field.name ) )
#if ( $field.type == "String" )
writeAttr("$fieldTagName", ${classLcapName}.get${fieldCapName}(), serializer);
@ -237,6 +240,8 @@ public class ${className} {
#else
writeAttr("$fieldTagName", ${classLcapName}.is${fieldCapName}() ? "true" : null, serializer);
#end
#elseif ( $field.type == "int" || $field.type == "Integer" )
writeAttr("$fieldTagName", Integer.toString(${classLcapName}.get${fieldCapName}()), serializer);
#else
// TODO: type=${field.type} to=${field.to} multiplicity=${field.multiplicity}
#end

View File

@ -152,6 +152,9 @@ public class ${className} {
#foreach ( $field in $allFields )
#if ( $Helper.xmlFieldMetadata( $field ).attribute )
#set ( $fieldTagName = $Helper.xmlFieldMetadata( $field ).tagName )
#if ( ! $fieldTagName )
#set ( $fieldTagName = $field.name )
#end
#set ( $fieldCapName = $Helper.capitalise( $field.name ) )
#if ( $field.type == "String" )
writeAttr("$fieldTagName", ${classLcapName}.get${fieldCapName}(), serializer);
@ -162,6 +165,8 @@ public class ${className} {
#else
writeAttr("$fieldTagName", ${classLcapName}.is${fieldCapName}() ? "true" : null, serializer);
#end
#elseif ( $field.type == "int" || $field.type == "Integer" )
writeAttr("$fieldTagName", Integer.toString(${classLcapName}.get${fieldCapName}()), serializer);
#else
// TODO: type=${field.type} to=${field.to} multiplicity=${field.multiplicity}
#end