View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.internal.impl;
20  
21  import javax.inject.Inject;
22  import javax.inject.Named;
23  import javax.inject.Provider;
24  import javax.inject.Singleton;
25  
26  import java.util.ArrayList;
27  import java.util.Collection;
28  import java.util.Collections;
29  import java.util.HashSet;
30  import java.util.Iterator;
31  import java.util.LinkedHashMap;
32  import java.util.List;
33  import java.util.Map;
34  import java.util.Objects;
35  import java.util.Optional;
36  import java.util.Set;
37  import java.util.stream.Collectors;
38  import java.util.stream.Stream;
39  
40  import org.apache.maven.api.DependencyScope;
41  import org.apache.maven.api.Lifecycle;
42  import org.apache.maven.api.model.InputLocation;
43  import org.apache.maven.api.model.InputSource;
44  import org.apache.maven.api.model.Plugin;
45  import org.apache.maven.api.services.LifecycleRegistry;
46  import org.apache.maven.api.services.LookupException;
47  import org.apache.maven.api.spi.ExtensibleEnumProvider;
48  import org.apache.maven.api.spi.LifecycleProvider;
49  import org.apache.maven.lifecycle.mapping.LifecyclePhase;
50  import org.codehaus.plexus.PlexusContainer;
51  import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
52  
53  import static org.apache.maven.api.Lifecycle.AFTER;
54  import static org.apache.maven.api.Lifecycle.BEFORE;
55  import static org.apache.maven.api.Lifecycle.Phase.ALL;
56  import static org.apache.maven.api.Lifecycle.Phase.BUILD;
57  import static org.apache.maven.api.Lifecycle.Phase.COMPILE;
58  import static org.apache.maven.api.Lifecycle.Phase.DEPLOY;
59  import static org.apache.maven.api.Lifecycle.Phase.EACH;
60  import static org.apache.maven.api.Lifecycle.Phase.INITIALIZE;
61  import static org.apache.maven.api.Lifecycle.Phase.INSTALL;
62  import static org.apache.maven.api.Lifecycle.Phase.INTEGRATION_TEST;
63  import static org.apache.maven.api.Lifecycle.Phase.PACKAGE;
64  import static org.apache.maven.api.Lifecycle.Phase.READY;
65  import static org.apache.maven.api.Lifecycle.Phase.RESOURCES;
66  import static org.apache.maven.api.Lifecycle.Phase.SOURCES;
67  import static org.apache.maven.api.Lifecycle.Phase.TEST;
68  import static org.apache.maven.api.Lifecycle.Phase.TEST_COMPILE;
69  import static org.apache.maven.api.Lifecycle.Phase.TEST_RESOURCES;
70  import static org.apache.maven.api.Lifecycle.Phase.TEST_SOURCES;
71  import static org.apache.maven.api.Lifecycle.Phase.UNIT_TEST;
72  import static org.apache.maven.api.Lifecycle.Phase.VALIDATE;
73  import static org.apache.maven.api.Lifecycle.Phase.VERIFY;
74  import static org.apache.maven.internal.impl.Lifecycles.after;
75  import static org.apache.maven.internal.impl.Lifecycles.alias;
76  import static org.apache.maven.internal.impl.Lifecycles.children;
77  import static org.apache.maven.internal.impl.Lifecycles.dependencies;
78  import static org.apache.maven.internal.impl.Lifecycles.phase;
79  import static org.apache.maven.internal.impl.Lifecycles.plugin;
80  
81  /**
82   * TODO: this is session scoped as SPI can contribute.
83   */
84  @Named
85  @Singleton
86  public class DefaultLifecycleRegistry implements LifecycleRegistry {
87  
88      private static final String MAVEN_PLUGINS = "org.apache.maven.plugins:";
89  
90      public static final String DEFAULT_LIFECYCLE_MODELID = "org.apache.maven:maven-core:"
91              + DefaultLifecycleRegistry.class.getPackage().getImplementationVersion()
92              + ":default-lifecycle-bindings";
93  
94      public static final InputLocation DEFAULT_LIFECYCLE_INPUT_LOCATION =
95              new InputLocation(new InputSource(DEFAULT_LIFECYCLE_MODELID, null));
96  
97      public static final String SCOPE_COMPILE = DependencyScope.COMPILE.id();
98      public static final String SCOPE_RUNTIME = DependencyScope.RUNTIME.id();
99      public static final String SCOPE_TEST_ONLY = DependencyScope.TEST_ONLY.id();
100     public static final String SCOPE_TEST = DependencyScope.TEST.id();
101 
102     private final List<LifecycleProvider> providers;
103 
104     public DefaultLifecycleRegistry() {
105         this(Collections.emptyList());
106     }
107 
108     @Inject
109     public DefaultLifecycleRegistry(List<LifecycleProvider> providers) {
110         List<LifecycleProvider> p = new ArrayList<>(providers);
111         p.add(() -> List.of(new CleanLifecycle(), new DefaultLifecycle(), new SiteLifecycle()));
112         this.providers = p;
113         // validate lifecycle
114         for (Lifecycle lifecycle : this) {
115             Set<String> set = new HashSet<>();
116             lifecycle.allPhases().forEach(phase -> {
117                 if (!set.add(phase.name())) {
118                     throw new IllegalArgumentException(
119                             "Found duplicated phase '" + phase.name() + "' in '" + lifecycle.id() + "' lifecycle");
120                 }
121             });
122         }
123     }
124 
125     @Override
126     public Iterator<Lifecycle> iterator() {
127         return stream().toList().iterator();
128     }
129 
130     @Override
131     public Stream<Lifecycle> stream() {
132         return providers.stream().map(ExtensibleEnumProvider::provides).flatMap(Collection::stream);
133     }
134 
135     @Override
136     public Optional<Lifecycle> lookup(String id) {
137         return stream().filter(lf -> Objects.equals(id, lf.id())).findAny();
138     }
139 
140     @Override
141     public List<String> computePhases(Lifecycle lifecycle) {
142         Graph graph = new Graph();
143         addPhases(graph, null, null, lifecycle.v3phases());
144         List<String> allPhases = graph.visitAll();
145         Collections.reverse(allPhases);
146         List<String> computed =
147                 allPhases.stream().filter(s -> !s.startsWith("$")).collect(Collectors.toList());
148         return computed;
149     }
150 
151     private static void addPhase(
152             Graph graph, Graph.Vertex before, Graph.Vertex after, org.apache.maven.api.Lifecycle.Phase phase) {
153         Graph.Vertex ep0 = graph.addVertex(BEFORE + phase.name());
154         Graph.Vertex ep1 = graph.addVertex("$$" + phase.name());
155         Graph.Vertex ep2 = graph.addVertex(phase.name());
156         Graph.Vertex ep3 = graph.addVertex(AFTER + phase.name());
157         graph.addEdge(ep0, ep1);
158         graph.addEdge(ep1, ep2);
159         graph.addEdge(ep2, ep3);
160         if (before != null) {
161             graph.addEdge(before, ep0);
162         }
163         if (after != null) {
164             graph.addEdge(ep3, after);
165         }
166         phase.links().forEach(link -> {
167             if (link.pointer().type() == Lifecycle.Pointer.Type.PROJECT) {
168                 if (link.kind() == Lifecycle.Link.Kind.AFTER) {
169                     graph.addEdge(graph.addVertex(link.pointer().phase()), ep0);
170                 } else {
171                     graph.addEdge(ep3, graph.addVertex(BEFORE + link.pointer().phase()));
172                 }
173             }
174         });
175         addPhases(graph, ep1, ep2, phase.phases());
176     }
177 
178     private static void addPhases(
179             Graph graph, Graph.Vertex before, Graph.Vertex after, Collection<Lifecycle.Phase> phases) {
180         // We add ordering between internal phases.
181         // This would be wrong at execution time, but we are here computing a list and not a graph,
182         // so in order to obtain the expected order, we add these links between phases.
183         Lifecycle.Phase prev = null;
184         for (Lifecycle.Phase child : phases) {
185             // add phase
186             addPhase(graph, before, after, child);
187             if (prev != null) {
188                 // add link between end of previous phase and beginning of this one
189                 graph.addEdge(graph.addVertex(AFTER + prev.name()), graph.addVertex(BEFORE + child.name()));
190             }
191             prev = child;
192         }
193     }
194 
195     @Named
196     @Singleton
197     public static class LifecycleWrapperProvider implements LifecycleProvider {
198         private final PlexusContainer container;
199 
200         @Inject
201         public LifecycleWrapperProvider(PlexusContainer container) {
202             this.container = container;
203         }
204 
205         @Override
206         public Collection<Lifecycle> provides() {
207             try {
208                 Map<String, org.apache.maven.lifecycle.Lifecycle> all =
209                         container.lookupMap(org.apache.maven.lifecycle.Lifecycle.class);
210                 return all.keySet().stream()
211                         .filter(id -> !Lifecycle.CLEAN.equals(id)
212                                 && !Lifecycle.DEFAULT.equals(id)
213                                 && !Lifecycle.SITE.equals(id))
214                         .map(id -> wrap(all.get(id)))
215                         .collect(Collectors.toList());
216             } catch (ComponentLookupException e) {
217                 throw new LookupException(e);
218             }
219         }
220 
221         private Lifecycle wrap(org.apache.maven.lifecycle.Lifecycle lifecycle) {
222             return new Lifecycle() {
223                 @Override
224                 public String id() {
225                     return lifecycle.getId();
226                 }
227 
228                 @Override
229                 public Collection<Phase> phases() {
230                     List<String> names = lifecycle.getPhases();
231                     List<Phase> phases = new ArrayList<>();
232                     for (int i = 0; i < names.size(); i++) {
233                         String name = names.get(i);
234                         String prev = i > 0 ? names.get(i - 1) : null;
235                         phases.add(new Phase() {
236                             @Override
237                             public String name() {
238                                 return name;
239                             }
240 
241                             @Override
242                             public List<Phase> phases() {
243                                 return List.of();
244                             }
245 
246                             @Override
247                             public Stream<Phase> allPhases() {
248                                 return Stream.concat(
249                                         Stream.of(this), phases().stream().flatMap(Lifecycle.Phase::allPhases));
250                             }
251 
252                             @Override
253                             public List<Plugin> plugins() {
254                                 Map<String, LifecyclePhase> lfPhases = lifecycle.getDefaultLifecyclePhases();
255                                 LifecyclePhase phase = lfPhases != null ? lfPhases.get(name) : null;
256                                 if (phase != null) {
257                                     Map<String, Plugin> plugins = new LinkedHashMap<>();
258                                     DefaultPackagingRegistry.parseLifecyclePhaseDefinitions(plugins, name, phase);
259                                     return plugins.values().stream().toList();
260                                 }
261                                 return List.of();
262                             }
263 
264                             @Override
265                             public Collection<Link> links() {
266                                 if (prev == null) {
267                                     return List.of();
268                                 } else {
269                                     return List.of(new Link() {
270                                         @Override
271                                         public Kind kind() {
272                                             return Kind.AFTER;
273                                         }
274 
275                                         @Override
276                                         public Pointer pointer() {
277                                             return new Pointer() {
278                                                 @Override
279                                                 public String phase() {
280                                                     return prev;
281                                                 }
282 
283                                                 @Override
284                                                 public Type type() {
285                                                     return Type.PROJECT;
286                                                 }
287                                             };
288                                         }
289                                     });
290                                 }
291                             }
292                         });
293                     }
294                     return phases;
295                 }
296 
297                 @Override
298                 public Collection<Alias> aliases() {
299                     return Collections.emptyList();
300                 }
301             };
302         }
303     }
304 
305     static class WrappedLifecycle extends org.apache.maven.lifecycle.Lifecycle {
306         WrappedLifecycle(LifecycleRegistry registry, Lifecycle lifecycle) {
307             super(registry, lifecycle);
308         }
309     }
310 
311     abstract static class BaseLifecycleProvider implements Provider<org.apache.maven.lifecycle.Lifecycle> {
312         @Inject
313         private PlexusContainer lookup;
314 
315         private final String name;
316 
317         BaseLifecycleProvider(String name) {
318             this.name = name;
319         }
320 
321         @Override
322         public org.apache.maven.lifecycle.Lifecycle get() {
323             try {
324                 LifecycleRegistry registry = lookup.lookup(LifecycleRegistry.class);
325                 return new WrappedLifecycle(registry, registry.require(name));
326             } catch (ComponentLookupException e) {
327                 throw new LookupException(e);
328             }
329         }
330     }
331 
332     @Singleton
333     @Named(Lifecycle.CLEAN)
334     @SuppressWarnings("unused")
335     static class CleanLifecycleProvider extends BaseLifecycleProvider {
336         CleanLifecycleProvider() {
337             super(Lifecycle.CLEAN);
338         }
339     }
340 
341     @Singleton
342     @Named(Lifecycle.DEFAULT)
343     @SuppressWarnings("unused")
344     static class DefaultLifecycleProvider extends BaseLifecycleProvider {
345         DefaultLifecycleProvider() {
346             super(Lifecycle.DEFAULT);
347         }
348     }
349 
350     @Singleton
351     @Named(Lifecycle.SITE)
352     @SuppressWarnings("unused")
353     static class SiteLifecycleProvider extends BaseLifecycleProvider {
354         SiteLifecycleProvider() {
355             super(Lifecycle.SITE);
356         }
357     }
358 
359     static class CleanLifecycle implements Lifecycle {
360 
361         private static final String MAVEN_CLEAN_PLUGIN_VERSION = "3.4.0";
362 
363         @Override
364         public String id() {
365             return Lifecycle.CLEAN;
366         }
367 
368         @Override
369         public Collection<Phase> phases() {
370             // START SNIPPET: clean
371             return List.of(phase(
372                     Phase.CLEAN,
373                     plugin(
374                             MAVEN_PLUGINS + "maven-clean-plugin:" + MAVEN_CLEAN_PLUGIN_VERSION + ":clean",
375                             Phase.CLEAN)));
376             // END SNIPPET: clean
377         }
378 
379         @Override
380         public Collection<Alias> aliases() {
381             return List.of(alias("pre-clean", BEFORE + Phase.CLEAN), alias("post-clean", AFTER + Phase.CLEAN));
382         }
383     }
384 
385     static class DefaultLifecycle implements Lifecycle {
386         @Override
387         public String id() {
388             return Lifecycle.DEFAULT;
389         }
390 
391         @Override
392         public Collection<Phase> phases() {
393             // START SNIPPET: default
394             return List.of(phase(
395                     ALL,
396                     children(ALL),
397                     phase(
398                             EACH,
399                             phase(VALIDATE, phase(INITIALIZE)),
400                             phase(
401                                     BUILD,
402                                     after(VALIDATE),
403                                     phase(SOURCES),
404                                     phase(RESOURCES),
405                                     phase(COMPILE, after(SOURCES), dependencies(SCOPE_COMPILE, READY)),
406                                     phase(READY, after(COMPILE), after(RESOURCES)),
407                                     phase(PACKAGE, after(READY), dependencies(SCOPE_RUNTIME, PACKAGE))),
408                             phase(
409                                     VERIFY,
410                                     after(VALIDATE),
411                                     phase(
412                                             UNIT_TEST,
413                                             phase(TEST_SOURCES),
414                                             phase(TEST_RESOURCES),
415                                             phase(
416                                                     TEST_COMPILE,
417                                                     after(TEST_SOURCES),
418                                                     after(READY),
419                                                     dependencies(SCOPE_TEST_ONLY, READY)),
420                                             phase(
421                                                     TEST,
422                                                     after(TEST_COMPILE),
423                                                     after(TEST_RESOURCES),
424                                                     dependencies(SCOPE_TEST, READY))),
425                                     phase(INTEGRATION_TEST)),
426                             phase(INSTALL, after(PACKAGE)),
427                             phase(DEPLOY, after(PACKAGE)))));
428             // END SNIPPET: default
429         }
430 
431         @Override
432         public Collection<Phase> v3phases() {
433             return List.of(phase(
434                     ALL,
435                     phase(INITIALIZE, phase(VALIDATE)),
436                     phase(
437                             BUILD,
438                             phase(SOURCES),
439                             phase(RESOURCES),
440                             phase(COMPILE),
441                             phase(READY),
442                             phase(TEST_SOURCES),
443                             phase(TEST_RESOURCES),
444                             phase(TEST_COMPILE),
445                             phase(TEST),
446                             phase(UNIT_TEST),
447                             phase(PACKAGE)),
448                     phase(VERIFY, phase(INTEGRATION_TEST)),
449                     phase(INSTALL),
450                     phase(DEPLOY)));
451         }
452 
453         @Override
454         public Collection<Alias> aliases() {
455             return List.of(
456                     alias("generate-sources", SOURCES),
457                     alias("process-sources", AFTER + SOURCES),
458                     alias("generate-resources", RESOURCES),
459                     alias("process-resources", AFTER + RESOURCES),
460                     alias("process-classes", AFTER + COMPILE),
461                     alias("generate-test-sources", TEST_SOURCES),
462                     alias("process-test-sources", AFTER + TEST_SOURCES),
463                     alias("generate-test-resources", TEST_RESOURCES),
464                     alias("process-test-resources", AFTER + TEST_RESOURCES),
465                     alias("process-test-classes", AFTER + TEST_COMPILE),
466                     alias("prepare-package", BEFORE + PACKAGE),
467                     alias("pre-integration-test", BEFORE + INTEGRATION_TEST),
468                     alias("post-integration-test", AFTER + INTEGRATION_TEST));
469         }
470     }
471 
472     static class SiteLifecycle implements Lifecycle {
473 
474         private static final String MAVEN_SITE_PLUGIN_VERSION = "3.21.0";
475         private static final String MAVEN_SITE_PLUGIN =
476                 MAVEN_PLUGINS + "maven-site-plugin:" + MAVEN_SITE_PLUGIN_VERSION + ":";
477         private static final String PHASE_SITE = "site";
478         private static final String PHASE_SITE_DEPLOY = "site-deploy";
479 
480         @Override
481         public String id() {
482             return Lifecycle.SITE;
483         }
484 
485         @Override
486         public Collection<Phase> phases() {
487             // START SNIPPET: site
488             return List.of(
489                     phase(PHASE_SITE, plugin(MAVEN_SITE_PLUGIN + "site", PHASE_SITE)),
490                     phase(
491                             PHASE_SITE_DEPLOY,
492                             after(PHASE_SITE),
493                             plugin(MAVEN_SITE_PLUGIN + "deploy", PHASE_SITE_DEPLOY)));
494             // END SNIPPET: site
495         }
496 
497         @Override
498         public Collection<Alias> aliases() {
499             return List.of(alias("pre-site", BEFORE + PHASE_SITE), alias("post-site", AFTER + PHASE_SITE));
500         }
501     }
502 }