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