1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.lifecycle.internal.builder;
20
21 import javax.inject.Inject;
22 import javax.inject.Named;
23 import javax.inject.Singleton;
24
25 import java.time.Duration;
26 import java.time.Instant;
27 import java.util.List;
28 import java.util.Set;
29
30 import org.apache.maven.api.MonotonicClock;
31 import org.apache.maven.artifact.Artifact;
32 import org.apache.maven.execution.BuildFailure;
33 import org.apache.maven.execution.ExecutionEvent;
34 import org.apache.maven.execution.MavenExecutionRequest;
35 import org.apache.maven.execution.MavenSession;
36 import org.apache.maven.internal.MultilineMessageHelper;
37 import org.apache.maven.internal.impl.DefaultLifecycleRegistry;
38 import org.apache.maven.lifecycle.LifecycleExecutionException;
39 import org.apache.maven.lifecycle.LifecycleNotFoundException;
40 import org.apache.maven.lifecycle.LifecyclePhaseNotFoundException;
41 import org.apache.maven.lifecycle.MavenExecutionPlan;
42 import org.apache.maven.lifecycle.internal.ExecutionEventCatapult;
43 import org.apache.maven.lifecycle.internal.LifecycleDebugLogger;
44 import org.apache.maven.lifecycle.internal.LifecycleExecutionPlanCalculator;
45 import org.apache.maven.lifecycle.internal.ReactorContext;
46 import org.apache.maven.lifecycle.internal.TaskSegment;
47 import org.apache.maven.model.Plugin;
48 import org.apache.maven.plugin.InvalidPluginDescriptorException;
49 import org.apache.maven.plugin.MojoExecution;
50 import org.apache.maven.plugin.MojoNotFoundException;
51 import org.apache.maven.plugin.PluginDescriptorParsingException;
52 import org.apache.maven.plugin.PluginNotFoundException;
53 import org.apache.maven.plugin.PluginResolutionException;
54 import org.apache.maven.plugin.descriptor.MojoDescriptor;
55 import org.apache.maven.plugin.prefix.NoPluginFoundForPrefixException;
56 import org.apache.maven.plugin.version.PluginVersionResolutionException;
57 import org.apache.maven.project.MavenProject;
58 import org.codehaus.plexus.classworlds.realm.ClassRealm;
59 import org.slf4j.Logger;
60 import org.slf4j.LoggerFactory;
61
62
63
64
65
66
67
68
69 @Named
70 @Singleton
71 public class BuilderCommon {
72 private final Logger logger;
73 private final LifecycleDebugLogger lifecycleDebugLogger;
74 private final LifecycleExecutionPlanCalculator lifeCycleExecutionPlanCalculator;
75 private final ExecutionEventCatapult eventCatapult;
76
77 @Inject
78 public BuilderCommon(
79 LifecycleDebugLogger lifecycleDebugLogger,
80 LifecycleExecutionPlanCalculator lifeCycleExecutionPlanCalculator,
81 ExecutionEventCatapult eventCatapult) {
82 this.logger = LoggerFactory.getLogger(getClass());
83 this.lifecycleDebugLogger = lifecycleDebugLogger;
84 this.lifeCycleExecutionPlanCalculator = lifeCycleExecutionPlanCalculator;
85 this.eventCatapult = eventCatapult;
86 }
87
88
89
90
91 BuilderCommon(
92 LifecycleDebugLogger lifecycleDebugLogger,
93 LifecycleExecutionPlanCalculator lifeCycleExecutionPlanCalculator,
94 ExecutionEventCatapult eventCatapult,
95 Logger logger) {
96 this.lifecycleDebugLogger = lifecycleDebugLogger;
97 this.lifeCycleExecutionPlanCalculator = lifeCycleExecutionPlanCalculator;
98 this.eventCatapult = eventCatapult;
99 this.logger = logger;
100 }
101
102 public MavenExecutionPlan resolveBuildPlan(
103 MavenSession session, MavenProject project, TaskSegment taskSegment, Set<Artifact> projectArtifacts)
104 throws PluginNotFoundException, PluginResolutionException, LifecyclePhaseNotFoundException,
105 PluginDescriptorParsingException, MojoNotFoundException, InvalidPluginDescriptorException,
106 NoPluginFoundForPrefixException, LifecycleNotFoundException, PluginVersionResolutionException,
107 LifecycleExecutionException {
108 MavenExecutionPlan executionPlan =
109 lifeCycleExecutionPlanCalculator.calculateExecutionPlan(session, project, taskSegment.getTasks());
110
111 lifecycleDebugLogger.debugProjectPlan(project, executionPlan);
112
113 if (session.getRequest().getDegreeOfConcurrency() > 1
114 && session.getProjects().size() > 1) {
115 final Set<Plugin> unsafePlugins = executionPlan.getNonThreadSafePlugins();
116 if (!unsafePlugins.isEmpty()) {
117 for (String s : MultilineMessageHelper.format(
118 "Your build is requesting parallel execution, but this project contains the following "
119 + "plugin(s) that have goals not marked as thread-safe to support parallel execution.",
120 "While this /may/ work fine, please look for plugin updates and/or "
121 + "request plugins be made thread-safe.",
122 "If reporting an issue, report it against the plugin in question, not against Apache Maven.")) {
123 logger.warn(s);
124 }
125 if (logger.isDebugEnabled()) {
126 final Set<MojoDescriptor> unsafeGoals = executionPlan.getNonThreadSafeMojos();
127 logger.warn("The following goals are not marked as thread-safe in " + project.getName() + ":");
128 for (MojoDescriptor unsafeGoal : unsafeGoals) {
129 logger.warn(" " + unsafeGoal.getId());
130 }
131 } else {
132 logger.warn("The following plugins are not marked as thread-safe in " + project.getName() + ":");
133 for (Plugin unsafePlugin : unsafePlugins) {
134 logger.warn(" " + unsafePlugin.getId());
135 }
136 logger.warn("");
137 logger.warn("Enable verbose output (-X) to see precisely which goals are not marked as"
138 + " thread-safe.");
139 }
140 logger.warn(MultilineMessageHelper.separatorLine());
141 }
142 }
143
144 final String defaulModelId = DefaultLifecycleRegistry.DEFAULT_LIFECYCLE_MODELID;
145
146 List<String> unversionedPlugins = executionPlan.getMojoExecutions().stream()
147 .map(MojoExecution::getPlugin)
148 .filter(p -> p.getLocation("version") != null
149 && p.getLocation("version").getSource() != null
150 && defaulModelId.equals(
151 p.getLocation("version").getSource().getModelId()))
152 .distinct()
153 .map(Plugin::getArtifactId)
154 .toList();
155
156 if (!unversionedPlugins.isEmpty()) {
157 logger.warn("Version not locked for default bindings plugins " + unversionedPlugins
158 + ", you should define versions in pluginManagement section of your " + "pom.xml or parent");
159 }
160
161 return executionPlan;
162 }
163
164 public void handleBuildError(
165 final ReactorContext buildContext,
166 final MavenSession rootSession,
167 final MavenSession currentSession,
168 final MavenProject mavenProject,
169 Throwable t,
170 final Instant buildStartTime) {
171
172 Instant buildEndTime = MonotonicClock.now();
173 buildContext.getResult().addException(t);
174 buildContext
175 .getResult()
176 .addBuildSummary(new BuildFailure(mavenProject, Duration.between(buildStartTime, buildEndTime), t));
177
178
179 if (t instanceof Exception exception && !(t instanceof RuntimeException)) {
180 eventCatapult.fire(ExecutionEvent.Type.ProjectFailed, currentSession, null, exception);
181 }
182
183
184 if (t instanceof RuntimeException || !(t instanceof Exception)) {
185
186
187 buildContext.getReactorBuildStatus().halt();
188 } else if (MavenExecutionRequest.REACTOR_FAIL_NEVER.equals(rootSession.getReactorFailureBehavior())) {
189
190 } else if (MavenExecutionRequest.REACTOR_FAIL_AT_END.equals(rootSession.getReactorFailureBehavior())) {
191
192 buildContext.getReactorBuildStatus().blackList(mavenProject);
193 } else if (MavenExecutionRequest.REACTOR_FAIL_FAST.equals(rootSession.getReactorFailureBehavior())) {
194 buildContext.getReactorBuildStatus().halt();
195 } else {
196 logger.error("invalid reactor failure behavior " + rootSession.getReactorFailureBehavior());
197 buildContext.getReactorBuildStatus().halt();
198 }
199 }
200
201 public static void attachToThread(MavenProject currentProject) {
202 ClassRealm projectRealm = currentProject.getClassRealm();
203 if (projectRealm != null) {
204 Thread.currentThread().setContextClassLoader(projectRealm);
205 }
206 }
207
208
209
210
211 public static String getKey(MavenProject project) {
212 return project.getGroupId() + ':' + project.getArtifactId() + ':' + project.getVersion();
213 }
214 }