1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
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
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
406 SOURCES,
407
408
409 RESOURCES,
410
411 COMPILE,
412
413 READY,
414
415 TEST_SOURCES,
416
417
418 TEST_RESOURCES,
419
420 TEST_COMPILE,
421
422 TEST,
423 UNIT_TEST,
424
425 PACKAGE,
426 BUILD,
427
428 INTEGRATION_TEST,
429
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 }