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