blob: e4ea6ef1336e09859ee542cd31dc2b21b577ae15 [file] [log] [blame]
package org.apache.maven.lifecycle.internal.builder;
/*
* 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.
*/
import java.util.List;
import java.util.Optional;
import java.util.Properties;
import java.util.Set;
import java.util.stream.Collectors;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
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.feature.Features;
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
* @author Kristian Rosenvold
* 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 );
// With Maven 4's build/consumer the POM will always rewrite during distribution.
// The maven-gpg-plugin uses the original POM, causing an invalid signature.
// Fail as long as there's no solution available yet
Properties userProperties = session.getUserProperties();
if ( Features.buildConsumer( userProperties ).isActive() )
{
Optional<MojoExecution> gpgMojo = executionPlan.getMojoExecutions().stream()
.filter( m -> "maven-gpg-plugin".equals( m.getArtifactId() )
&& "org.apache.maven.plugins".equals( m.getGroupId() ) )
.findAny();
if ( gpgMojo.isPresent() )
{
throw new LifecycleExecutionException( "The maven-gpg-plugin is not supported by Maven 4."
+ " Verify if there is a compatible signing solution,"
+ " add -D" + Features.buildConsumer( userProperties ).propertyName() + "=false"
+ " or use Maven 3." );
}
}
if ( session.getRequest().getDegreeOfConcurrency() > 1 )
{
final Set<Plugin> unsafePlugins = executionPlan.getNonThreadSafePlugins();
if ( !unsafePlugins.isEmpty() )
{
logger.warn( "*****************************************************************" );
logger.warn( "* Your build is requesting parallel execution, but project *" );
logger.warn( "* contains the following plugin(s) that have goals not marked *" );
logger.warn( "* as @threadSafe to support parallel building. *" );
logger.warn( "* While this /may/ work fine, please look for plugin updates *" );
logger.warn( "* and/or request plugins be made thread-safe. *" );
logger.warn( "* If reporting an issue, report it against the plugin in *" );
logger.warn( "* question, not against maven-core *" );
logger.warn( "*****************************************************************" );
if ( logger.isDebugEnabled() )
{
final Set<MojoDescriptor> unsafeGoals = executionPlan.getNonThreadSafeMojos();
logger.warn( "The following goals are not marked @threadSafe in " + project.getName() + ":" );
for ( MojoDescriptor unsafeGoal : unsafeGoals )
{
logger.warn( unsafeGoal.getId() );
}
}
else
{
logger.warn( "The following plugins are not marked @threadSafe in " + project.getName() + ":" );
for ( Plugin unsafePlugin : unsafePlugins )
{
logger.warn( unsafePlugin.getId() );
}
logger.warn( "Enable verbose output (-X) to see more precisely which goals are not marked"
+ " @threadSafe." );
}
logger.warn( "*****************************************************************" );
}
}
final String defaulModelId = DefaultLifecyclePluginAnalyzer.DEFAULTLIFECYCLEBINDINGS_MODELID;
List<String> unversionedPlugins = executionPlan.getMojoExecutions().stream()
.map( MojoExecution::getPlugin )
.filter( p -> p.getLocation( "version" ) != null ) // versionless cli goal (?)
.filter( p -> p.getLocation( "version" ).getSource() != null ) // versionless in pom (?)
.filter( p -> 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();
}
}