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