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()));
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 .map(id -> wrap(all.get(id)))
192 .collect(Collectors.toList());
193 } catch (ComponentLookupException e) {
194 throw new LookupException(e);
195 }
196 }
197
198 private Lifecycle wrap(org.apache.maven.lifecycle.Lifecycle lifecycle) {
199 return new Lifecycle() {
200 @Override
201 public String id() {
202 return lifecycle.getId();
203 }
204
205 @Override
206 public Collection<Phase> phases() {
207 List<String> names = lifecycle.getPhases();
208 List<Phase> phases = new ArrayList<>();
209 for (int i = 0; i < names.size(); i++) {
210 String name = names.get(i);
211 String prev = i > 0 ? names.get(i - 1) : null;
212 phases.add(new Phase() {
213 @Override
214 public String name() {
215 return name;
216 }
217
218 @Override
219 public List<Phase> phases() {
220 return List.of();
221 }
222
223 @Override
224 public Stream<Phase> allPhases() {
225 return Stream.concat(
226 Stream.of(this), phases().stream().flatMap(Lifecycle.Phase::allPhases));
227 }
228
229 @Override
230 public List<Plugin> plugins() {
231 Map<String, LifecyclePhase> lfPhases = lifecycle.getDefaultLifecyclePhases();
232 LifecyclePhase phase = lfPhases != null ? lfPhases.get(name) : null;
233 if (phase != null) {
234 Map<String, Plugin> plugins = new LinkedHashMap<>();
235 DefaultPackagingRegistry.parseLifecyclePhaseDefinitions(plugins, name, phase);
236 return plugins.values().stream().toList();
237 }
238 return List.of();
239 }
240
241 @Override
242 public Collection<Link> links() {
243 if (prev == null) {
244 return List.of();
245 } else {
246 return List.of(new Link() {
247 @Override
248 public Kind kind() {
249 return Kind.AFTER;
250 }
251
252 @Override
253 public Pointer pointer() {
254 return new Pointer() {
255 @Override
256 public String phase() {
257 return prev;
258 }
259
260 @Override
261 public Type type() {
262 return Type.PROJECT;
263 }
264 };
265 }
266 });
267 }
268 }
269 });
270 }
271 return phases;
272 }
273
274 @Override
275 public Collection<Alias> aliases() {
276 return Collections.emptyList();
277 }
278 };
279 }
280 }
281
282 static class WrappedLifecycle extends org.apache.maven.lifecycle.Lifecycle {
283 WrappedLifecycle(LifecycleRegistry registry, Lifecycle lifecycle) {
284 super(registry, lifecycle);
285 }
286 }
287
288 abstract static class BaseLifecycleProvider implements Provider<org.apache.maven.lifecycle.Lifecycle> {
289 @Inject
290 private PlexusContainer lookup;
291
292 private final String name;
293
294 BaseLifecycleProvider(String name) {
295 this.name = name;
296 }
297
298 @Override
299 public org.apache.maven.lifecycle.Lifecycle get() {
300 try {
301 LifecycleRegistry registry = lookup.lookup(LifecycleRegistry.class);
302 return new WrappedLifecycle(registry, registry.require(name));
303 } catch (ComponentLookupException e) {
304 throw new LookupException(e);
305 }
306 }
307 }
308
309 @Singleton
310 @Named(Lifecycle.CLEAN)
311 @SuppressWarnings("unused")
312 static class CleanLifecycleProvider extends BaseLifecycleProvider {
313 CleanLifecycleProvider() {
314 super(Lifecycle.CLEAN);
315 }
316 }
317
318 @Singleton
319 @Named(Lifecycle.DEFAULT)
320 @SuppressWarnings("unused")
321 static class DefaultLifecycleProvider extends BaseLifecycleProvider {
322 DefaultLifecycleProvider() {
323 super(Lifecycle.DEFAULT);
324 }
325 }
326
327 @Singleton
328 @Named(Lifecycle.SITE)
329 @SuppressWarnings("unused")
330 static class SiteLifecycleProvider extends BaseLifecycleProvider {
331 SiteLifecycleProvider() {
332 super(Lifecycle.SITE);
333 }
334 }
335
336 static class CleanLifecycle implements Lifecycle {
337
338 private static final String MAVEN_CLEAN_PLUGIN_VERSION = "3.2.0";
339
340 @Override
341 public String id() {
342 return Lifecycle.CLEAN;
343 }
344
345 @Override
346 public Collection<Phase> phases() {
347 return List.of(phase(
348 Phase.CLEAN,
349 plugin(
350 MAVEN_PLUGINS + "maven-clean-plugin:" + MAVEN_CLEAN_PLUGIN_VERSION + ":clean",
351 Phase.CLEAN)));
352 }
353
354 @Override
355 public Collection<Alias> aliases() {
356 return List.of(alias("pre-clean", BEFORE + Phase.CLEAN), alias("post-clean", AFTER + Phase.CLEAN));
357 }
358 }
359
360 static class DefaultLifecycle implements Lifecycle {
361 @Override
362 public String id() {
363 return Lifecycle.DEFAULT;
364 }
365
366 @Override
367 public Collection<Phase> phases() {
368 return List.of(phase(
369 "all",
370 phase(INITIALIZE, phase(VALIDATE)),
371 phase(
372 BUILD,
373 after(VALIDATE),
374 phase(SOURCES),
375 phase(RESOURCES),
376 phase(COMPILE, after(SOURCES), dependencies(COMPILE, READY)),
377 phase(READY, after(COMPILE), after(RESOURCES)),
378 phase(PACKAGE, after(READY), dependencies("runtime", PACKAGE))),
379 phase(
380 VERIFY,
381 after(VALIDATE),
382 phase(
383 UNIT_TEST,
384 phase(TEST_SOURCES),
385 phase(TEST_RESOURCES),
386 phase(
387 TEST_COMPILE,
388 after(TEST_SOURCES),
389 after(READY),
390 dependencies("test-only", READY)),
391 phase(
392 TEST,
393 after(TEST_COMPILE),
394 after(TEST_RESOURCES),
395 dependencies("test", READY))),
396 phase(INTEGRATION_TEST)),
397 phase(INSTALL, after(PACKAGE)),
398 phase(DEPLOY, after(PACKAGE))));
399 }
400
401 @Override
402 public Collection<Alias> aliases() {
403 return List.of(
404 alias("generate-sources", SOURCES),
405 alias("process-sources", AFTER + SOURCES),
406 alias("generate-resources", RESOURCES),
407 alias("process-resources", AFTER + RESOURCES),
408 alias("process-classes", AFTER + COMPILE),
409 alias("generate-test-sources", TEST_SOURCES),
410 alias("process-test-sources", AFTER + TEST_SOURCES),
411 alias("generate-test-resources", TEST_RESOURCES),
412 alias("process-test-resources", AFTER + TEST_RESOURCES),
413 alias("process-test-classes", AFTER + TEST_COMPILE),
414 alias("prepare-package", BEFORE + PACKAGE),
415 alias("pre-integration-test", BEFORE + INTEGRATION_TEST),
416 alias("post-integration-test", AFTER + INTEGRATION_TEST));
417 }
418
419 @Override
420 public Optional<List<String>> orderedPhases() {
421 return Optional.of(Arrays.asList(
422 VALIDATE,
423 INITIALIZE,
424
425 SOURCES,
426
427
428 RESOURCES,
429
430 COMPILE,
431
432 READY,
433
434 TEST_SOURCES,
435
436
437 TEST_RESOURCES,
438
439 TEST_COMPILE,
440
441 TEST,
442 UNIT_TEST,
443
444 PACKAGE,
445 BUILD,
446
447 INTEGRATION_TEST,
448
449 VERIFY,
450 INSTALL,
451 DEPLOY,
452 "all"));
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 }