[MNG-7051] Optionally skip non-existing profiles and break on missing required profiles.

This commit is contained in:
Maarten Mulders 2020-12-09 14:43:15 +01:00 committed by Martin Kanters
parent 8df10f93be
commit 8defd16965
11 changed files with 496 additions and 112 deletions

View File

@ -19,22 +19,6 @@ package org.apache.maven;
* under the License. * under the License.
*/ */
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import org.apache.maven.artifact.ArtifactUtils; import org.apache.maven.artifact.ArtifactUtils;
import org.apache.maven.execution.BuildResumptionAnalyzer; import org.apache.maven.execution.BuildResumptionAnalyzer;
import org.apache.maven.execution.BuildResumptionDataRepository; import org.apache.maven.execution.BuildResumptionDataRepository;
@ -44,15 +28,19 @@ import org.apache.maven.execution.ExecutionEvent;
import org.apache.maven.execution.MavenExecutionRequest; import org.apache.maven.execution.MavenExecutionRequest;
import org.apache.maven.execution.MavenExecutionResult; import org.apache.maven.execution.MavenExecutionResult;
import org.apache.maven.execution.MavenSession; import org.apache.maven.execution.MavenSession;
import org.apache.maven.execution.ProfileActivation;
import org.apache.maven.execution.ProjectDependencyGraph; import org.apache.maven.execution.ProjectDependencyGraph;
import org.apache.maven.graph.GraphBuilder; import org.apache.maven.graph.GraphBuilder;
import org.apache.maven.internal.aether.DefaultRepositorySystemSessionFactory; import org.apache.maven.internal.aether.DefaultRepositorySystemSessionFactory;
import org.apache.maven.lifecycle.LifecycleExecutionException; import org.apache.maven.lifecycle.LifecycleExecutionException;
import org.apache.maven.lifecycle.internal.ExecutionEventCatapult; import org.apache.maven.lifecycle.internal.ExecutionEventCatapult;
import org.apache.maven.lifecycle.internal.LifecycleStarter; import org.apache.maven.lifecycle.internal.LifecycleStarter;
import org.apache.maven.model.Model;
import org.apache.maven.model.Prerequisites; import org.apache.maven.model.Prerequisites;
import org.apache.maven.model.Profile;
import org.apache.maven.model.building.ModelProblem; import org.apache.maven.model.building.ModelProblem;
import org.apache.maven.model.building.Result; import org.apache.maven.model.building.Result;
import org.apache.maven.model.superpom.SuperPomProvider;
import org.apache.maven.plugin.LegacySupport; import org.apache.maven.plugin.LegacySupport;
import org.apache.maven.project.MavenProject; import org.apache.maven.project.MavenProject;
import org.apache.maven.project.ProjectBuilder; import org.apache.maven.project.ProjectBuilder;
@ -66,6 +54,26 @@ import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.repository.WorkspaceReader; import org.eclipse.aether.repository.WorkspaceReader;
import org.eclipse.aether.util.repository.ChainedWorkspaceReader; import org.eclipse.aether.util.repository.ChainedWorkspaceReader;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Stream;
import static java.util.stream.Collectors.toSet;
/** /**
* @author Jason van Zyl * @author Jason van Zyl
*/ */
@ -109,6 +117,9 @@ public class DefaultMaven
@Inject @Inject
private BuildResumptionDataRepository buildResumptionDataRepository; private BuildResumptionDataRepository buildResumptionDataRepository;
@Inject
private SuperPomProvider superPomProvider;
@Override @Override
public MavenExecutionResult execute( MavenExecutionRequest request ) public MavenExecutionResult execute( MavenExecutionRequest request )
{ {
@ -316,11 +327,17 @@ public class DefaultMaven
validatePrerequisitesForNonMavenPluginProjects( session.getProjects() ); validatePrerequisitesForNonMavenPluginProjects( session.getProjects() );
validateActivatedProfiles( session.getProjects(), request.getActiveProfiles() ); validateRequiredProfiles( session, request.getProfileActivation() );
if ( session.getResult().hasExceptions() )
{
return result;
}
validateOptionalProfiles( session, request.getProfileActivation() );
lifecycleStarter.execute( session ); lifecycleStarter.execute( session );
validateActivatedProfiles( session.getProjects(), request.getActiveProfiles() ); validateOptionalProfiles( session, request.getProfileActivation() );
if ( session.getResult().hasExceptions() ) if ( session.getResult().hasExceptions() )
{ {
@ -493,22 +510,93 @@ public class DefaultMaven
} }
} }
private void validateActivatedProfiles( List<MavenProject> projects, List<String> activeProfileIds ) /**
* Get all profiles that are detected in the projects, any parent of the projects, or the settings.
* @param session The Maven session
* @return A {@link Set} of profile identifiers, never {@code null}.
*/
private Set<String> getAllProfiles( MavenSession session )
{ {
Collection<String> notActivatedProfileIds = new LinkedHashSet<>( activeProfileIds ); final Model superPomModel = superPomProvider.getSuperModel( "4.0.0" );
final Set<MavenProject> projectsIncludingParents = new HashSet<>();
for ( MavenProject project : projects ) for ( MavenProject project : session.getProjects() )
{ {
for ( List<String> profileIds : project.getInjectedProfileIds().values() ) boolean isAdded = projectsIncludingParents.add( project );
MavenProject parent = project.getParent();
while ( isAdded && parent != null )
{ {
notActivatedProfileIds.removeAll( profileIds ); isAdded = projectsIncludingParents.add( parent );
parent = parent.getParent();
} }
} }
for ( String notActivatedProfileId : notActivatedProfileIds ) final Stream<String> projectProfiles = projectsIncludingParents.stream()
.map( MavenProject::getModel )
.map( Model::getProfiles )
.flatMap( Collection::stream )
.map( Profile::getId );
final Stream<String> settingsProfiles = session.getSettings().getProfiles().stream()
.map( org.apache.maven.settings.Profile::getId );
final Stream<String> superPomProfiles = superPomModel.getProfiles().stream()
.map( Profile::getId );
return Stream.of( projectProfiles, settingsProfiles, superPomProfiles )
.flatMap( Function.identity() )
.collect( toSet() );
}
/**
* Check whether the required profiles were found in any of the projects we're building or the settings.
* @param session the Maven session.
* @param profileActivation the requested optional and required profiles.
*/
private void validateRequiredProfiles( MavenSession session, ProfileActivation profileActivation )
{
final Set<String> allAvailableProfiles = getAllProfiles( session );
final Set<String> requiredProfiles = new HashSet<>( );
requiredProfiles.addAll( profileActivation.getRequiredActiveProfileIds() );
requiredProfiles.addAll( profileActivation.getRequiredInactiveProfileIds() );
// Check whether the required profiles were found in any of the projects we're building.
final Set<String> notFoundRequiredProfiles = requiredProfiles.stream()
.filter( rap -> !allAvailableProfiles.contains( rap ) )
.collect( toSet() );
if ( !notFoundRequiredProfiles.isEmpty() )
{ {
logger.warn( "The requested profile \"" + notActivatedProfileId final String message = String.format(
+ "\" could not be activated because it does not exist." ); "The requested profiles [%s] could not be activated or deactivated because they do not exist.",
String.join( ", ", notFoundRequiredProfiles )
);
addExceptionToResult( session.getResult(), new MissingProfilesException( message ) );
}
}
/**
* Check whether any of the requested optional profiles were not activated or deactivated.
* @param session the Maven session.
* @param profileActivation the requested optional and required profiles.
*/
private void validateOptionalProfiles( MavenSession session, ProfileActivation profileActivation )
{
final Set<String> allAvailableProfiles = getAllProfiles( session );
final Set<String> optionalProfiles = new HashSet<>( );
optionalProfiles.addAll( profileActivation.getOptionalActiveProfileIds() );
optionalProfiles.addAll( profileActivation.getOptionalInactiveProfileIds() );
final Set<String> notFoundOptionalProfiles = optionalProfiles.stream()
.filter( rap -> !allAvailableProfiles.contains( rap ) )
.collect( toSet() );
if ( !notFoundOptionalProfiles.isEmpty() )
{
final String message = String.format(
"The requested optional profiles [%s] could not be activated or deactivated because they "
+ "do not exist.", String.join( ", ", notFoundOptionalProfiles )
);
logger.warn( message );
} }
} }

View File

@ -0,0 +1,33 @@
package org.apache.maven;
/*
* 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.
*/
/**
* Signals that the user referenced one or more Maven profiles that could not be located in either the project or the
* settings.
*/
public class MissingProfilesException
extends Exception
{
public MissingProfilesException( String message )
{
super( message );
}
}

View File

@ -76,6 +76,8 @@ public class DefaultMavenExecutionRequest
private List<Profile> profiles; private List<Profile> profiles;
private final ProfileActivation profileActivation = new ProfileActivation();
private List<String> pluginGroups; private List<String> pluginGroups;
private boolean isProjectPresent = true; private boolean isProjectPresent = true;
@ -130,10 +132,6 @@ public class DefaultMavenExecutionRequest
private boolean showErrors = false; private boolean showErrors = false;
private List<String> activeProfiles;
private List<String> inactiveProfiles;
private TransferListener transferListener; private TransferListener transferListener;
private int loggingLevel = LOGGING_LEVEL_INFO; private int loggingLevel = LOGGING_LEVEL_INFO;
@ -343,11 +341,7 @@ public class DefaultMavenExecutionRequest
{ {
if ( activeProfiles != null ) if ( activeProfiles != null )
{ {
this.activeProfiles = new ArrayList<>( activeProfiles ); this.profileActivation.overwriteActiveProfiles( activeProfiles );
}
else
{
this.activeProfiles = null;
} }
return this; return this;
@ -358,16 +352,18 @@ public class DefaultMavenExecutionRequest
{ {
if ( inactiveProfiles != null ) if ( inactiveProfiles != null )
{ {
this.inactiveProfiles = new ArrayList<>( inactiveProfiles ); this.profileActivation.overwriteInactiveProfiles( inactiveProfiles );
}
else
{
this.inactiveProfiles = null;
} }
return this; return this;
} }
@Override
public ProfileActivation getProfileActivation()
{
return this.profileActivation;
}
@Override @Override
public MavenExecutionRequest setRemoteRepositories( List<ArtifactRepository> remoteRepositories ) public MavenExecutionRequest setRemoteRepositories( List<ArtifactRepository> remoteRepositories )
{ {
@ -406,21 +402,13 @@ public class DefaultMavenExecutionRequest
@Override @Override
public List<String> getActiveProfiles() public List<String> getActiveProfiles()
{ {
if ( activeProfiles == null ) return this.profileActivation.getActiveProfiles();
{
activeProfiles = new ArrayList<>();
}
return activeProfiles;
} }
@Override @Override
public List<String> getInactiveProfiles() public List<String> getInactiveProfiles()
{ {
if ( inactiveProfiles == null ) return this.profileActivation.getInactiveProfiles();
{
inactiveProfiles = new ArrayList<>();
}
return inactiveProfiles;
} }
@Override @Override

View File

@ -277,22 +277,62 @@ public interface MavenExecutionRequest
MavenExecutionRequest setProfiles( List<Profile> profiles ); MavenExecutionRequest setProfiles( List<Profile> profiles );
/**
* @deprecated Use {@link #getProfileActivation()}.
*/
@Deprecated
MavenExecutionRequest addActiveProfile( String profile ); MavenExecutionRequest addActiveProfile( String profile );
/**
* @deprecated Use {@link #getProfileActivation()}.
*/
@Deprecated
MavenExecutionRequest addActiveProfiles( List<String> profiles ); MavenExecutionRequest addActiveProfiles( List<String> profiles );
/**
* @deprecated Use {@link #getProfileActivation()}.
*/
@Deprecated
MavenExecutionRequest setActiveProfiles( List<String> profiles ); MavenExecutionRequest setActiveProfiles( List<String> profiles );
/**
* @return The list of profiles that the user wants to activate.
* @deprecated Use {@link #getProfileActivation()}.
*/
@Deprecated
List<String> getActiveProfiles(); List<String> getActiveProfiles();
/**
* @deprecated Use {@link #getProfileActivation()}.
*/
@Deprecated
MavenExecutionRequest addInactiveProfile( String profile ); MavenExecutionRequest addInactiveProfile( String profile );
/**
* @deprecated Use {@link #getProfileActivation()}.
*/
@Deprecated
MavenExecutionRequest addInactiveProfiles( List<String> profiles ); MavenExecutionRequest addInactiveProfiles( List<String> profiles );
/**
* @deprecated Use {@link #getProfileActivation()}.
*/
@Deprecated
MavenExecutionRequest setInactiveProfiles( List<String> profiles ); MavenExecutionRequest setInactiveProfiles( List<String> profiles );
/**
* @return The list of profiles that the user wants to de-activate.
* @deprecated Use {@link #getProfileActivation()}.
*/
@Deprecated
List<String> getInactiveProfiles(); List<String> getInactiveProfiles();
/**
* Return the requested activation(s) of profile(s) in this execution.
* @return requested (de-)activation(s) of profile(s) in this execution. Never {@code null}.
*/
ProfileActivation getProfileActivation();
// Proxies // Proxies
List<Proxy> getProxies(); List<Proxy> getProxies();

View File

@ -0,0 +1,201 @@
package org.apache.maven.execution;
/*
* 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.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
import static java.util.stream.Collectors.toSet;
/**
* Container for storing the request from the user to activate or de-activate certain profiles and optionally fail the
* build if those profiles do not exist.
*/
public class ProfileActivation
{
private enum ActivationSettings
{
ACTIVATION_OPTIONAL( true, true ),
ACTIVATION_REQUIRED( true, false ),
DEACTIVATION_OPTIONAL( false, true ),
DEACTIVATION_REQUIRED( false, false );
/** Should the profile be active? */
final boolean active;
/** Should the build continue if the profile is not present? */
final boolean optional;
ActivationSettings( final boolean active, final boolean optional )
{
this.active = active;
this.optional = optional;
}
static ActivationSettings of( final boolean active, final boolean optional )
{
if ( optional )
{
return active ? ACTIVATION_OPTIONAL : DEACTIVATION_OPTIONAL;
}
else
{
return active ? ACTIVATION_REQUIRED : DEACTIVATION_REQUIRED;
}
}
}
private final Map<String, ActivationSettings> activations = new HashMap<>();
/**
* Mimics the pre-Maven 4 "active profiles" list.
* @deprecated Use {@link #getRequiredActiveProfileIds()} and {@link #getOptionalActiveProfileIds()} instead.
*/
@Deprecated
public List<String> getActiveProfiles()
{
return new ArrayList<>( getProfileIds( pa -> pa.active ) );
}
/**
* Mimics the pre-Maven 4 "inactive profiles" list.
* @deprecated Use {@link #getRequiredInactiveProfileIds()} and {@link #getOptionalInactiveProfileIds()} instead.
*/
@Deprecated
public List<String> getInactiveProfiles()
{
return new ArrayList<>( getProfileIds( pa -> !pa.active ) );
}
/**
* Overwrites the active profiles based on a pre-Maven 4 "active profiles" list.
* @param activeProfileIds A {@link List} of profile IDs that must be activated.
* @deprecated Use {@link #activateOptionalProfile(String)} or {@link #activateRequiredProfile(String)} instead.
*/
@Deprecated
public void overwriteActiveProfiles( List<String> activeProfileIds )
{
getActiveProfiles().forEach( this.activations::remove );
activeProfileIds.forEach( this::activateOptionalProfile );
}
/**
* Overwrites the inactive profiles based on a pre-Maven 4 "inactive profiles" list.
* @param inactiveProfileIds A {@link List} of profile IDs that must be deactivated.
* @deprecated Use {@link #deactivateOptionalProfile(String)} or {@link #deactivateRequiredProfile(String)} instead.
*/
@Deprecated
public void overwriteInactiveProfiles( List<String> inactiveProfileIds )
{
getInactiveProfiles().forEach( this.activations::remove );
inactiveProfileIds.forEach( this::deactivateOptionalProfile );
}
/**
* Mark a profile as required and activated.
* @param id The identifier of the profile.
*/
public void activateRequiredProfile( String id )
{
this.activations.put( id, ActivationSettings.ACTIVATION_REQUIRED );
}
/**
* Mark a profile as optional and activated.
* @param id The identifier of the profile.
*/
public void activateOptionalProfile( String id )
{
this.activations.put( id, ActivationSettings.ACTIVATION_OPTIONAL );
}
/**
* Mark a profile as required and deactivated.
* @param id The identifier of the profile.
*/
public void deactivateRequiredProfile( String id )
{
this.activations.put( id, ActivationSettings.DEACTIVATION_REQUIRED );
}
/**
* Mark a profile as optional and deactivated.
* @param id The identifier of the profile.
*/
public void deactivateOptionalProfile( String id )
{
this.activations.put( id, ActivationSettings.DEACTIVATION_OPTIONAL );
}
/**
* Adds a profile activation to the request.
* @param id The identifier of the profile.
* @param active Should the profile be activated?
* @param optional Can the build continue if the profile does not exist?
*/
public void addProfileActivation( String id, boolean active, boolean optional )
{
final ActivationSettings settings = ActivationSettings.of( active, optional );
this.activations.put( id, settings );
}
private Set<String> getProfileIds( final Predicate<ActivationSettings> predicate )
{
return this.activations.entrySet().stream()
.filter( e -> predicate.test( e.getValue() ) )
.map( e -> e.getKey() )
.collect( toSet() );
}
/**
* @return Required active profile identifiers, never {@code null}.
*/
public Set<String> getRequiredActiveProfileIds()
{
return getProfileIds( pa -> !pa.optional && pa.active );
}
/**
* @return Optional active profile identifiers, never {@code null}.
*/
public Set<String> getOptionalActiveProfileIds()
{
return getProfileIds( pa -> pa.optional && pa.active );
}
/**
* @return Required inactive profile identifiers, never {@code null}.
*/
public Set<String> getRequiredInactiveProfileIds()
{
return getProfileIds( pa -> !pa.optional && !pa.active );
}
/**
* @return Optional inactive profile identifiers, never {@code null}.
*/
public Set<String> getOptionalInactiveProfileIds()
{
return getProfileIds( pa -> pa.optional && !pa.active );
}
}

View File

@ -54,6 +54,7 @@ import org.apache.maven.execution.MavenExecutionRequest;
import org.apache.maven.execution.MavenExecutionRequestPopulationException; import org.apache.maven.execution.MavenExecutionRequestPopulationException;
import org.apache.maven.execution.MavenExecutionRequestPopulator; import org.apache.maven.execution.MavenExecutionRequestPopulator;
import org.apache.maven.execution.MavenExecutionResult; import org.apache.maven.execution.MavenExecutionResult;
import org.apache.maven.execution.ProfileActivation;
import org.apache.maven.execution.scope.internal.MojoExecutionScopeModule; import org.apache.maven.execution.scope.internal.MojoExecutionScopeModule;
import org.apache.maven.extension.internal.CoreExports; import org.apache.maven.extension.internal.CoreExports;
import org.apache.maven.extension.internal.CoreExtensionEntry; import org.apache.maven.extension.internal.CoreExtensionEntry;
@ -1379,9 +1380,7 @@ public class MavenCli
request.setSelectedProjects( projectActivation.activeProjects ); request.setSelectedProjects( projectActivation.activeProjects );
request.setExcludedProjects( projectActivation.inactiveProjects ); request.setExcludedProjects( projectActivation.inactiveProjects );
final ProfileActivation profileActivation = determineProfileActivation( commandLine ); performProfileActivation( commandLine, request.getProfileActivation() );
request.addActiveProfiles( profileActivation.activeProfiles );
request.addInactiveProfiles( profileActivation.inactiveProfiles );
final String localRepositoryPath = determineLocalRepositoryPath( request ); final String localRepositoryPath = determineLocalRepositoryPath( request );
if ( localRepositoryPath != null ) if ( localRepositoryPath != null )
@ -1507,41 +1506,41 @@ public class MavenCli
} }
// Visible for testing // Visible for testing
static ProfileActivation determineProfileActivation( final CommandLine commandLine ) static void performProfileActivation( final CommandLine commandLine,
final ProfileActivation profileActivation )
{ {
final ProfileActivation result = new ProfileActivation();
if ( commandLine.hasOption( CLIManager.ACTIVATE_PROFILES ) ) if ( commandLine.hasOption( CLIManager.ACTIVATE_PROFILES ) )
{ {
String[] profileOptionValues = commandLine.getOptionValues( CLIManager.ACTIVATE_PROFILES ); final String[] optionValues = commandLine.getOptionValues( CLIManager.ACTIVATE_PROFILES );
if ( profileOptionValues != null )
if ( optionValues == null || optionValues.length == 0 )
{ {
for ( String profileOptionValue : profileOptionValues ) return;
}
for ( final String optionValue : optionValues )
{
for ( String token : optionValue.split( "," ) )
{ {
StringTokenizer profileTokens = new StringTokenizer( profileOptionValue, "," ); String profileId = token.trim();
boolean active = true;
while ( profileTokens.hasMoreTokens() ) if ( profileId.charAt( 0 ) == '-' || profileId.charAt( 0 ) == '!' )
{ {
String profileAction = profileTokens.nextToken().trim(); active = false;
profileId = profileId.substring( 1 );
if ( profileAction.startsWith( "-" ) || profileAction.startsWith( "!" ) )
{
result.deactivate( profileAction.substring( 1 ) );
}
else if ( profileAction.startsWith( "+" ) )
{
result.activate( profileAction.substring( 1 ) );
}
else
{
result.activate( profileAction );
}
} }
else if ( token.charAt( 0 ) == '+' )
{
profileId = profileId.substring( 1 );
}
boolean optional = profileId.charAt( 0 ) == '?';
profileId = profileId.substring( optional ? 1 : 0 );
profileActivation.addProfileActivation( profileId, active, optional );
} }
} }
} }
return result;
} }
private ExecutionListener determineExecutionListener() private ExecutionListener determineExecutionListener()
@ -1796,23 +1795,6 @@ public class MavenCli
return container.lookup( ModelProcessor.class ); return container.lookup( ModelProcessor.class );
} }
// Visible for testing
static class ProfileActivation
{
final List<String> activeProfiles = new ArrayList<>();
final List<String> inactiveProfiles = new ArrayList<>();
public void deactivate( final String profile )
{
inactiveProfiles.add( profile );
}
public void activate( final String profile )
{
activeProfiles.add( profile );
}
}
// Visible for testing // Visible for testing
static class ProjectActivation static class ProjectActivation
{ {

View File

@ -20,7 +20,7 @@ package org.apache.maven.cli;
*/ */
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static org.apache.maven.cli.MavenCli.determineProfileActivation; import static org.apache.maven.cli.MavenCli.performProfileActivation;
import static org.apache.maven.cli.MavenCli.determineProjectActivation; import static org.apache.maven.cli.MavenCli.determineProjectActivation;
import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.CoreMatchers.nullValue;
@ -33,6 +33,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assumptions.assumeTrue; import static org.junit.jupiter.api.Assumptions.assumeTrue;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.hamcrest.collection.IsIterableContainingInOrder.contains; import static org.hamcrest.collection.IsIterableContainingInOrder.contains;
import static org.hamcrest.collection.IsIterableContainingInAnyOrder.containsInAnyOrder;
import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times; import static org.mockito.Mockito.times;
@ -46,9 +47,11 @@ import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.Option; import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options; import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException; import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.Parser;
import org.apache.maven.Maven; import org.apache.maven.Maven;
import org.apache.maven.eventspy.internal.EventSpyDispatcher; import org.apache.maven.eventspy.internal.EventSpyDispatcher;
import org.apache.maven.execution.MavenExecutionRequest; import org.apache.maven.execution.MavenExecutionRequest;
import org.apache.maven.execution.ProfileActivation;
import org.apache.maven.project.MavenProject; import org.apache.maven.project.MavenProject;
import org.apache.maven.shared.utils.logging.MessageUtils; import org.apache.maven.shared.utils.logging.MessageUtils;
import org.apache.maven.toolchain.building.ToolchainsBuildingRequest; import org.apache.maven.toolchain.building.ToolchainsBuildingRequest;
@ -88,25 +91,29 @@ public class MavenCliTest
} }
@Test @Test
public void testDetermineProfileActivation() throws ParseException public void testPerformProfileActivation() throws ParseException
{ {
MavenCli.ProfileActivation result; final Parser parser = new GnuParser();
Options options = new Options();
final Options options = new Options();
options.addOption( Option.builder( Character.toString( CLIManager.ACTIVATE_PROFILES ) ).hasArg().build() ); options.addOption( Option.builder( Character.toString( CLIManager.ACTIVATE_PROFILES ) ).hasArg().build() );
result = determineProfileActivation( new GnuParser().parse( options, new String[]{ "-P", "test1,+test2" } ) ); ProfileActivation activation;
assertThat( result.activeProfiles.size(), is( 2 ) );
assertThat( result.activeProfiles, contains( "test1", "test2" ) );
result = determineProfileActivation( new GnuParser().parse( options, new String[]{ "-P", "!test1,-test2" } ) ); activation = new ProfileActivation();
assertThat( result.inactiveProfiles.size(), is( 2 ) ); performProfileActivation( parser.parse( options, new String[]{ "-P", "test1,+test2,?test3,+?test4" } ), activation );
assertThat( result.inactiveProfiles, contains( "test1", "test2" ) ); assertThat( activation.getRequiredActiveProfileIds(), containsInAnyOrder( "test1", "test2" ) );
assertThat( activation.getOptionalActiveProfileIds(), containsInAnyOrder( "test3", "test4" ) );
result = determineProfileActivation( new GnuParser().parse( options, new String[]{ "-P", "-test1,+test2" } ) ); activation = new ProfileActivation();
assertThat( result.activeProfiles.size(), is( 1 ) ); performProfileActivation( parser.parse( options, new String[]{ "-P", "!test1,-test2,-?test3,!?test4" } ), activation );
assertThat( result.activeProfiles, contains( "test2" ) ); assertThat( activation.getRequiredInactiveProfileIds(), containsInAnyOrder( "test1", "test2" ) );
assertThat( result.inactiveProfiles.size(), is( 1 ) ); assertThat( activation.getOptionalInactiveProfileIds(), containsInAnyOrder( "test3", "test4" ) );
assertThat( result.inactiveProfiles, contains( "test1" ) );
activation = new ProfileActivation();
performProfileActivation( parser.parse( options, new String[]{ "-P", "-test1,+test2" } ), activation );
assertThat( activation.getRequiredActiveProfileIds(), containsInAnyOrder( "test2" ) );
assertThat( activation.getRequiredInactiveProfileIds(), containsInAnyOrder( "test1" ) );
} }
@Test @Test

View File

@ -51,7 +51,8 @@ public interface ModelProblem
V20, V20,
V30, V30,
V31, V31,
V37 V37,
V40
} }
/** /**

View File

@ -190,6 +190,8 @@ public class DefaultModelValidator
{ {
String prefix = "profiles.profile[" + profile.getId() + "]."; String prefix = "profiles.profile[" + profile.getId() + "].";
validateId( prefix, "id", problems, Severity.ERROR, Version.V40, profile.getId(), null, m );
if ( !profileIds.add( profile.getId() ) ) if ( !profileIds.add( profile.getId() ) )
{ {
addViolation( problems, errOn30, Version.V20, "profiles.profile.id", null, addViolation( problems, errOn30, Version.V20, "profiles.profile.id", null,

View File

@ -403,6 +403,16 @@ public class DefaultModelValidatorTest
} }
@Test @Test
public void testInvalidProfileId()
throws Exception
{
SimpleProblemCollector result = validateRaw( "invalid-profile-id.xml" );
assertViolations( result, 0, 1, 0 );
assertTrue( result.getErrors().get( 0 ).contains( "?invalid-id" ) );
}
public void testDuplicateProfileId() public void testDuplicateProfileId()
throws Exception throws Exception
{ {

View File

@ -0,0 +1,32 @@
<!--
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.
-->
<project>
<modelVersion>4.0.0</modelVersion>
<artifactId>aid</artifactId>
<groupId>gid</groupId>
<version>0.1</version>
<packaging>pom</packaging>
<profiles>
<profile>
<id>?invalid-id</id>
</profile>
</profiles>
</project>