blob: 81bb5596b37922dc6b6e894be125c8c83cee276a [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.maven.lifecycle.internal.builder;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.execution.BuildFailure;
import org.apache.maven.execution.ExecutionEvent;
import org.apache.maven.execution.MavenExecutionRequest;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.internal.MultilineMessageHelper;
import org.apache.maven.lifecycle.LifecycleExecutionException;
import org.apache.maven.lifecycle.LifecycleNotFoundException;
import org.apache.maven.lifecycle.LifecyclePhaseNotFoundException;
import org.apache.maven.lifecycle.MavenExecutionPlan;
import org.apache.maven.lifecycle.internal.DefaultLifecyclePluginAnalyzer;
import org.apache.maven.lifecycle.internal.ExecutionEventCatapult;
import org.apache.maven.lifecycle.internal.LifecycleDebugLogger;
import org.apache.maven.lifecycle.internal.LifecycleExecutionPlanCalculator;
import org.apache.maven.lifecycle.internal.ReactorContext;
import org.apache.maven.lifecycle.internal.TaskSegment;
import org.apache.maven.model.Plugin;
import org.apache.maven.plugin.InvalidPluginDescriptorException;
import org.apache.maven.plugin.MojoExecution;
import org.apache.maven.plugin.MojoNotFoundException;
import org.apache.maven.plugin.PluginDescriptorParsingException;
import org.apache.maven.plugin.PluginNotFoundException;
import org.apache.maven.plugin.PluginResolutionException;
import org.apache.maven.plugin.descriptor.MojoDescriptor;
import org.apache.maven.plugin.prefix.NoPluginFoundForPrefixException;
import org.apache.maven.plugin.version.PluginVersionResolutionException;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.classworlds.realm.ClassRealm;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Common code that is shared by the LifecycleModuleBuilder and the LifeCycleWeaveBuilder
*
* @since 3.0
* Builds one or more lifecycles for a full module
* NOTE: This class is not part of any public api and can be changed or deleted without prior notice.
*/
@Named
@Singleton
public class BuilderCommon {
private final Logger logger;
private final LifecycleDebugLogger lifecycleDebugLogger;
private final LifecycleExecutionPlanCalculator lifeCycleExecutionPlanCalculator;
private final ExecutionEventCatapult eventCatapult;
@Inject
public BuilderCommon(
LifecycleDebugLogger lifecycleDebugLogger,
LifecycleExecutionPlanCalculator lifeCycleExecutionPlanCalculator,
ExecutionEventCatapult eventCatapult) {
this.logger = LoggerFactory.getLogger(getClass());
this.lifecycleDebugLogger = lifecycleDebugLogger;
this.lifeCycleExecutionPlanCalculator = lifeCycleExecutionPlanCalculator;
this.eventCatapult = eventCatapult;
}
/**
* Ctor needed for UT.
*/
BuilderCommon(
LifecycleDebugLogger lifecycleDebugLogger,
LifecycleExecutionPlanCalculator lifeCycleExecutionPlanCalculator,
ExecutionEventCatapult eventCatapult,
Logger logger) {
this.lifecycleDebugLogger = lifecycleDebugLogger;
this.lifeCycleExecutionPlanCalculator = lifeCycleExecutionPlanCalculator;
this.eventCatapult = eventCatapult;
this.logger = logger;
}
public MavenExecutionPlan resolveBuildPlan(
MavenSession session, MavenProject project, TaskSegment taskSegment, Set<Artifact> projectArtifacts)
throws PluginNotFoundException, PluginResolutionException, LifecyclePhaseNotFoundException,
PluginDescriptorParsingException, MojoNotFoundException, InvalidPluginDescriptorException,
NoPluginFoundForPrefixException, LifecycleNotFoundException, PluginVersionResolutionException,
LifecycleExecutionException {
MavenExecutionPlan executionPlan =
lifeCycleExecutionPlanCalculator.calculateExecutionPlan(session, project, taskSegment.getTasks());
lifecycleDebugLogger.debugProjectPlan(project, executionPlan);
if (session.getRequest().getDegreeOfConcurrency() > 1
&& session.getProjects().size() > 1) {
final Set<Plugin> unsafePlugins = executionPlan.getNonThreadSafePlugins();
if (!unsafePlugins.isEmpty()) {
for (String s : MultilineMessageHelper.format(
"Your build is requesting parallel execution, but this project contains the following "
+ "plugin(s) that have goals not marked as thread-safe to support parallel execution.",
"While this /may/ work fine, please look for plugin updates and/or "
+ "request plugins be made thread-safe.",
"If reporting an issue, report it against the plugin in question, not against Apache Maven.")) {
logger.warn(s);
}
if (logger.isDebugEnabled()) {
final Set<MojoDescriptor> unsafeGoals = executionPlan.getNonThreadSafeMojos();
logger.warn("The following goals are not marked as thread-safe in " + project.getName() + ":");
for (MojoDescriptor unsafeGoal : unsafeGoals) {
logger.warn(" " + unsafeGoal.getId());
}
} else {
logger.warn("The following plugins are not marked as thread-safe in " + project.getName() + ":");
for (Plugin unsafePlugin : unsafePlugins) {
logger.warn(" " + unsafePlugin.getId());
}
logger.warn("");
logger.warn("Enable verbose output (-X) to see precisely which goals are not marked as"
+ " thread-safe.");
}
logger.warn(MultilineMessageHelper.separatorLine());
}
}
final String defaulModelId = DefaultLifecyclePluginAnalyzer.DEFAULTLIFECYCLEBINDINGS_MODELID;
List<String> unversionedPlugins = executionPlan.getMojoExecutions().stream()
.map(MojoExecution::getPlugin)
.filter(p -> p.getLocation("version") != null
&& p.getLocation("version").getSource() != null
&& defaulModelId.equals(
p.getLocation("version").getSource().getModelId()))
.distinct()
.map(Plugin::getArtifactId) // managed by us, groupId is always o.a.m.plugins
.collect(Collectors.toList());
if (!unversionedPlugins.isEmpty()) {
logger.warn("Version not locked for default bindings plugins " + unversionedPlugins
+ ", you should define versions in pluginManagement section of your " + "pom.xml or parent");
}
return executionPlan;
}
public void handleBuildError(
final ReactorContext buildContext,
final MavenSession rootSession,
final MavenSession currentSession,
final MavenProject mavenProject,
Throwable t,
final long buildStartTime) {
// record the error and mark the project as failed
long buildEndTime = System.currentTimeMillis();
buildContext.getResult().addException(t);
buildContext.getResult().addBuildSummary(new BuildFailure(mavenProject, buildEndTime - buildStartTime, t));
// notify listeners about "soft" project build failures only
if (t instanceof Exception && !(t instanceof RuntimeException)) {
eventCatapult.fire(ExecutionEvent.Type.ProjectFailed, currentSession, null, (Exception) t);
}
// reactor failure modes
if (t instanceof RuntimeException || !(t instanceof Exception)) {
// fail fast on RuntimeExceptions, Errors and "other" Throwables
// assume these are system errors and further build is meaningless
buildContext.getReactorBuildStatus().halt();
} else if (MavenExecutionRequest.REACTOR_FAIL_NEVER.equals(rootSession.getReactorFailureBehavior())) {
// continue the build
} else if (MavenExecutionRequest.REACTOR_FAIL_AT_END.equals(rootSession.getReactorFailureBehavior())) {
// continue the build but ban all projects that depend on the failed one
buildContext.getReactorBuildStatus().blackList(mavenProject);
} else if (MavenExecutionRequest.REACTOR_FAIL_FAST.equals(rootSession.getReactorFailureBehavior())) {
buildContext.getReactorBuildStatus().halt();
} else {
logger.error("invalid reactor failure behavior " + rootSession.getReactorFailureBehavior());
buildContext.getReactorBuildStatus().halt();
}
}
public static void attachToThread(MavenProject currentProject) {
ClassRealm projectRealm = currentProject.getClassRealm();
if (projectRealm != null) {
Thread.currentThread().setContextClassLoader(projectRealm);
}
}
// TODO I'm really wondering where this method belongs; smells like it should be on MavenProject, but for some
// reason it isn't ? This localization is kind-of a code smell.
public static String getKey(MavenProject project) {
return project.getGroupId() + ':' + project.getArtifactId() + ':' + project.getVersion();
}
}