From 8defd169651db30fe0ac3ad3f318e471f0241d02 Mon Sep 17 00:00:00 2001 From: Maarten Mulders Date: Wed, 9 Dec 2020 14:43:15 +0100 Subject: [PATCH] [MNG-7051] Optionally skip non-existing profiles and break on missing required profiles. --- .../java/org/apache/maven/DefaultMaven.java | 142 ++++++++++--- .../maven/MissingProfilesException.java | 33 +++ .../DefaultMavenExecutionRequest.java | 36 ++-- .../execution/MavenExecutionRequest.java | 40 ++++ .../maven/execution/ProfileActivation.java | 201 ++++++++++++++++++ .../java/org/apache/maven/cli/MavenCli.java | 72 +++---- .../org/apache/maven/cli/MavenCliTest.java | 37 ++-- .../maven/model/building/ModelProblem.java | 3 +- .../validation/DefaultModelValidator.java | 2 + .../validation/DefaultModelValidatorTest.java | 10 + .../poms/validation/invalid-profile-id.xml | 32 +++ 11 files changed, 496 insertions(+), 112 deletions(-) create mode 100644 maven-core/src/main/java/org/apache/maven/MissingProfilesException.java create mode 100644 maven-core/src/main/java/org/apache/maven/execution/ProfileActivation.java create mode 100644 maven-model-builder/src/test/resources/poms/validation/invalid-profile-id.xml diff --git a/maven-core/src/main/java/org/apache/maven/DefaultMaven.java b/maven-core/src/main/java/org/apache/maven/DefaultMaven.java index 2e1a684e64..7b758f5457 100644 --- a/maven-core/src/main/java/org/apache/maven/DefaultMaven.java +++ b/maven-core/src/main/java/org/apache/maven/DefaultMaven.java @@ -19,22 +19,6 @@ package org.apache.maven; * 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.execution.BuildResumptionAnalyzer; 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.MavenExecutionResult; import org.apache.maven.execution.MavenSession; +import org.apache.maven.execution.ProfileActivation; import org.apache.maven.execution.ProjectDependencyGraph; import org.apache.maven.graph.GraphBuilder; import org.apache.maven.internal.aether.DefaultRepositorySystemSessionFactory; import org.apache.maven.lifecycle.LifecycleExecutionException; import org.apache.maven.lifecycle.internal.ExecutionEventCatapult; import org.apache.maven.lifecycle.internal.LifecycleStarter; +import org.apache.maven.model.Model; 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.Result; +import org.apache.maven.model.superpom.SuperPomProvider; import org.apache.maven.plugin.LegacySupport; import org.apache.maven.project.MavenProject; 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.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 */ @@ -109,6 +117,9 @@ public class DefaultMaven @Inject private BuildResumptionDataRepository buildResumptionDataRepository; + @Inject + private SuperPomProvider superPomProvider; + @Override public MavenExecutionResult execute( MavenExecutionRequest request ) { @@ -316,11 +327,17 @@ public class DefaultMaven 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 ); - validateActivatedProfiles( session.getProjects(), request.getActiveProfiles() ); + validateOptionalProfiles( session, request.getProfileActivation() ); if ( session.getResult().hasExceptions() ) { @@ -493,22 +510,93 @@ public class DefaultMaven } } - private void validateActivatedProfiles( List projects, List 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 getAllProfiles( MavenSession session ) { - Collection notActivatedProfileIds = new LinkedHashSet<>( activeProfileIds ); - - for ( MavenProject project : projects ) + final Model superPomModel = superPomProvider.getSuperModel( "4.0.0" ); + final Set projectsIncludingParents = new HashSet<>(); + for ( MavenProject project : session.getProjects() ) { - for ( List 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 projectProfiles = projectsIncludingParents.stream() + .map( MavenProject::getModel ) + .map( Model::getProfiles ) + .flatMap( Collection::stream ) + .map( Profile::getId ); + final Stream settingsProfiles = session.getSettings().getProfiles().stream() + .map( org.apache.maven.settings.Profile::getId ); + final Stream 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 allAvailableProfiles = getAllProfiles( session ); + + final Set 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 notFoundRequiredProfiles = requiredProfiles.stream() + .filter( rap -> !allAvailableProfiles.contains( rap ) ) + .collect( toSet() ); + + if ( !notFoundRequiredProfiles.isEmpty() ) { - logger.warn( "The requested profile \"" + notActivatedProfileId - + "\" could not be activated because it does not exist." ); + final String message = String.format( + "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 allAvailableProfiles = getAllProfiles( session ); + + final Set optionalProfiles = new HashSet<>( ); + optionalProfiles.addAll( profileActivation.getOptionalActiveProfileIds() ); + optionalProfiles.addAll( profileActivation.getOptionalInactiveProfileIds() ); + + final Set 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 ); } } diff --git a/maven-core/src/main/java/org/apache/maven/MissingProfilesException.java b/maven-core/src/main/java/org/apache/maven/MissingProfilesException.java new file mode 100644 index 0000000000..6e9ee767d5 --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/MissingProfilesException.java @@ -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 ); + } +} diff --git a/maven-core/src/main/java/org/apache/maven/execution/DefaultMavenExecutionRequest.java b/maven-core/src/main/java/org/apache/maven/execution/DefaultMavenExecutionRequest.java index f344359fa3..554928b2a7 100644 --- a/maven-core/src/main/java/org/apache/maven/execution/DefaultMavenExecutionRequest.java +++ b/maven-core/src/main/java/org/apache/maven/execution/DefaultMavenExecutionRequest.java @@ -76,6 +76,8 @@ public class DefaultMavenExecutionRequest private List profiles; + private final ProfileActivation profileActivation = new ProfileActivation(); + private List pluginGroups; private boolean isProjectPresent = true; @@ -130,10 +132,6 @@ public class DefaultMavenExecutionRequest private boolean showErrors = false; - private List activeProfiles; - - private List inactiveProfiles; - private TransferListener transferListener; private int loggingLevel = LOGGING_LEVEL_INFO; @@ -343,11 +341,7 @@ public class DefaultMavenExecutionRequest { if ( activeProfiles != null ) { - this.activeProfiles = new ArrayList<>( activeProfiles ); - } - else - { - this.activeProfiles = null; + this.profileActivation.overwriteActiveProfiles( activeProfiles ); } return this; @@ -358,16 +352,18 @@ public class DefaultMavenExecutionRequest { if ( inactiveProfiles != null ) { - this.inactiveProfiles = new ArrayList<>( inactiveProfiles ); - } - else - { - this.inactiveProfiles = null; + this.profileActivation.overwriteInactiveProfiles( inactiveProfiles ); } return this; } + @Override + public ProfileActivation getProfileActivation() + { + return this.profileActivation; + } + @Override public MavenExecutionRequest setRemoteRepositories( List remoteRepositories ) { @@ -406,21 +402,13 @@ public class DefaultMavenExecutionRequest @Override public List getActiveProfiles() { - if ( activeProfiles == null ) - { - activeProfiles = new ArrayList<>(); - } - return activeProfiles; + return this.profileActivation.getActiveProfiles(); } @Override public List getInactiveProfiles() { - if ( inactiveProfiles == null ) - { - inactiveProfiles = new ArrayList<>(); - } - return inactiveProfiles; + return this.profileActivation.getInactiveProfiles(); } @Override diff --git a/maven-core/src/main/java/org/apache/maven/execution/MavenExecutionRequest.java b/maven-core/src/main/java/org/apache/maven/execution/MavenExecutionRequest.java index 1ccd7ecb23..f31e33fb24 100644 --- a/maven-core/src/main/java/org/apache/maven/execution/MavenExecutionRequest.java +++ b/maven-core/src/main/java/org/apache/maven/execution/MavenExecutionRequest.java @@ -277,22 +277,62 @@ public interface MavenExecutionRequest MavenExecutionRequest setProfiles( List profiles ); + /** + * @deprecated Use {@link #getProfileActivation()}. + */ + @Deprecated MavenExecutionRequest addActiveProfile( String profile ); + /** + * @deprecated Use {@link #getProfileActivation()}. + */ + @Deprecated MavenExecutionRequest addActiveProfiles( List profiles ); + /** + * @deprecated Use {@link #getProfileActivation()}. + */ + @Deprecated MavenExecutionRequest setActiveProfiles( List profiles ); + /** + * @return The list of profiles that the user wants to activate. + * @deprecated Use {@link #getProfileActivation()}. + */ + @Deprecated List getActiveProfiles(); + /** + * @deprecated Use {@link #getProfileActivation()}. + */ + @Deprecated MavenExecutionRequest addInactiveProfile( String profile ); + /** + * @deprecated Use {@link #getProfileActivation()}. + */ + @Deprecated MavenExecutionRequest addInactiveProfiles( List profiles ); + /** + * @deprecated Use {@link #getProfileActivation()}. + */ + @Deprecated MavenExecutionRequest setInactiveProfiles( List profiles ); + /** + * @return The list of profiles that the user wants to de-activate. + * @deprecated Use {@link #getProfileActivation()}. + */ + @Deprecated List 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 List getProxies(); diff --git a/maven-core/src/main/java/org/apache/maven/execution/ProfileActivation.java b/maven-core/src/main/java/org/apache/maven/execution/ProfileActivation.java new file mode 100644 index 0000000000..21530bd75c --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/execution/ProfileActivation.java @@ -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 activations = new HashMap<>(); + + /** + * Mimics the pre-Maven 4 "active profiles" list. + * @deprecated Use {@link #getRequiredActiveProfileIds()} and {@link #getOptionalActiveProfileIds()} instead. + */ + @Deprecated + public List 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 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 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 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 getProfileIds( final Predicate 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 getRequiredActiveProfileIds() + { + return getProfileIds( pa -> !pa.optional && pa.active ); + } + + /** + * @return Optional active profile identifiers, never {@code null}. + */ + public Set getOptionalActiveProfileIds() + { + return getProfileIds( pa -> pa.optional && pa.active ); + } + + /** + * @return Required inactive profile identifiers, never {@code null}. + */ + public Set getRequiredInactiveProfileIds() + { + return getProfileIds( pa -> !pa.optional && !pa.active ); + } + + /** + * @return Optional inactive profile identifiers, never {@code null}. + */ + public Set getOptionalInactiveProfileIds() + { + return getProfileIds( pa -> pa.optional && !pa.active ); + } +} diff --git a/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java b/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java index 7afe07385c..792a831500 100644 --- a/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java +++ b/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java @@ -54,6 +54,7 @@ import org.apache.maven.execution.MavenExecutionRequest; import org.apache.maven.execution.MavenExecutionRequestPopulationException; import org.apache.maven.execution.MavenExecutionRequestPopulator; import org.apache.maven.execution.MavenExecutionResult; +import org.apache.maven.execution.ProfileActivation; import org.apache.maven.execution.scope.internal.MojoExecutionScopeModule; import org.apache.maven.extension.internal.CoreExports; import org.apache.maven.extension.internal.CoreExtensionEntry; @@ -1379,9 +1380,7 @@ public class MavenCli request.setSelectedProjects( projectActivation.activeProjects ); request.setExcludedProjects( projectActivation.inactiveProjects ); - final ProfileActivation profileActivation = determineProfileActivation( commandLine ); - request.addActiveProfiles( profileActivation.activeProfiles ); - request.addInactiveProfiles( profileActivation.inactiveProfiles ); + performProfileActivation( commandLine, request.getProfileActivation() ); final String localRepositoryPath = determineLocalRepositoryPath( request ); if ( localRepositoryPath != null ) @@ -1507,41 +1506,41 @@ public class MavenCli } // 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 ) ) { - String[] profileOptionValues = commandLine.getOptionValues( CLIManager.ACTIVATE_PROFILES ); - if ( profileOptionValues != null ) + final String[] optionValues = commandLine.getOptionValues( CLIManager.ACTIVATE_PROFILES ); + + 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, "," ); - - while ( profileTokens.hasMoreTokens() ) + String profileId = token.trim(); + boolean active = true; + if ( profileId.charAt( 0 ) == '-' || profileId.charAt( 0 ) == '!' ) { - String profileAction = profileTokens.nextToken().trim(); - - if ( profileAction.startsWith( "-" ) || profileAction.startsWith( "!" ) ) - { - result.deactivate( profileAction.substring( 1 ) ); - } - else if ( profileAction.startsWith( "+" ) ) - { - result.activate( profileAction.substring( 1 ) ); - } - else - { - result.activate( profileAction ); - } + active = false; + profileId = profileId.substring( 1 ); } + 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() @@ -1796,23 +1795,6 @@ public class MavenCli return container.lookup( ModelProcessor.class ); } - // Visible for testing - static class ProfileActivation - { - final List activeProfiles = new ArrayList<>(); - final List inactiveProfiles = new ArrayList<>(); - - public void deactivate( final String profile ) - { - inactiveProfiles.add( profile ); - } - - public void activate( final String profile ) - { - activeProfiles.add( profile ); - } - } - // Visible for testing static class ProjectActivation { diff --git a/maven-embedder/src/test/java/org/apache/maven/cli/MavenCliTest.java b/maven-embedder/src/test/java/org/apache/maven/cli/MavenCliTest.java index c7a41a5e2a..de7485cd4c 100644 --- a/maven-embedder/src/test/java/org/apache/maven/cli/MavenCliTest.java +++ b/maven-embedder/src/test/java/org/apache/maven/cli/MavenCliTest.java @@ -20,7 +20,7 @@ package org.apache.maven.cli; */ 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.hamcrest.CoreMatchers.is; 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.mockito.ArgumentMatchers.any; 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.mock; 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.Options; import org.apache.commons.cli.ParseException; +import org.apache.commons.cli.Parser; import org.apache.maven.Maven; import org.apache.maven.eventspy.internal.EventSpyDispatcher; import org.apache.maven.execution.MavenExecutionRequest; +import org.apache.maven.execution.ProfileActivation; import org.apache.maven.project.MavenProject; import org.apache.maven.shared.utils.logging.MessageUtils; import org.apache.maven.toolchain.building.ToolchainsBuildingRequest; @@ -88,25 +91,29 @@ public class MavenCliTest } @Test - public void testDetermineProfileActivation() throws ParseException + public void testPerformProfileActivation() throws ParseException { - MavenCli.ProfileActivation result; - Options options = new Options(); + final Parser parser = new GnuParser(); + + final Options options = new Options(); options.addOption( Option.builder( Character.toString( CLIManager.ACTIVATE_PROFILES ) ).hasArg().build() ); - result = determineProfileActivation( new GnuParser().parse( options, new String[]{ "-P", "test1,+test2" } ) ); - assertThat( result.activeProfiles.size(), is( 2 ) ); - assertThat( result.activeProfiles, contains( "test1", "test2" ) ); + ProfileActivation activation; - result = determineProfileActivation( new GnuParser().parse( options, new String[]{ "-P", "!test1,-test2" } ) ); - assertThat( result.inactiveProfiles.size(), is( 2 ) ); - assertThat( result.inactiveProfiles, contains( "test1", "test2" ) ); + activation = new ProfileActivation(); + performProfileActivation( parser.parse( options, new String[]{ "-P", "test1,+test2,?test3,+?test4" } ), activation ); + assertThat( activation.getRequiredActiveProfileIds(), containsInAnyOrder( "test1", "test2" ) ); + assertThat( activation.getOptionalActiveProfileIds(), containsInAnyOrder( "test3", "test4" ) ); - result = determineProfileActivation( new GnuParser().parse( options, new String[]{ "-P", "-test1,+test2" } ) ); - assertThat( result.activeProfiles.size(), is( 1 ) ); - assertThat( result.activeProfiles, contains( "test2" ) ); - assertThat( result.inactiveProfiles.size(), is( 1 ) ); - assertThat( result.inactiveProfiles, contains( "test1" ) ); + activation = new ProfileActivation(); + performProfileActivation( parser.parse( options, new String[]{ "-P", "!test1,-test2,-?test3,!?test4" } ), activation ); + assertThat( activation.getRequiredInactiveProfileIds(), containsInAnyOrder( "test1", "test2" ) ); + assertThat( activation.getOptionalInactiveProfileIds(), containsInAnyOrder( "test3", "test4" ) ); + + activation = new ProfileActivation(); + performProfileActivation( parser.parse( options, new String[]{ "-P", "-test1,+test2" } ), activation ); + assertThat( activation.getRequiredActiveProfileIds(), containsInAnyOrder( "test2" ) ); + assertThat( activation.getRequiredInactiveProfileIds(), containsInAnyOrder( "test1" ) ); } @Test diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelProblem.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelProblem.java index 30b67249ea..a08b5efe23 100644 --- a/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelProblem.java +++ b/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelProblem.java @@ -51,7 +51,8 @@ public interface ModelProblem V20, V30, V31, - V37 + V37, + V40 } /** diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/validation/DefaultModelValidator.java b/maven-model-builder/src/main/java/org/apache/maven/model/validation/DefaultModelValidator.java index a4c3cda3ce..ba812b5f7a 100644 --- a/maven-model-builder/src/main/java/org/apache/maven/model/validation/DefaultModelValidator.java +++ b/maven-model-builder/src/main/java/org/apache/maven/model/validation/DefaultModelValidator.java @@ -190,6 +190,8 @@ public class DefaultModelValidator { String prefix = "profiles.profile[" + profile.getId() + "]."; + validateId( prefix, "id", problems, Severity.ERROR, Version.V40, profile.getId(), null, m ); + if ( !profileIds.add( profile.getId() ) ) { addViolation( problems, errOn30, Version.V20, "profiles.profile.id", null, diff --git a/maven-model-builder/src/test/java/org/apache/maven/model/validation/DefaultModelValidatorTest.java b/maven-model-builder/src/test/java/org/apache/maven/model/validation/DefaultModelValidatorTest.java index 5f17a8aded..d73dcb1928 100644 --- a/maven-model-builder/src/test/java/org/apache/maven/model/validation/DefaultModelValidatorTest.java +++ b/maven-model-builder/src/test/java/org/apache/maven/model/validation/DefaultModelValidatorTest.java @@ -403,6 +403,16 @@ public class DefaultModelValidatorTest } @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() throws Exception { diff --git a/maven-model-builder/src/test/resources/poms/validation/invalid-profile-id.xml b/maven-model-builder/src/test/resources/poms/validation/invalid-profile-id.xml new file mode 100644 index 0000000000..6e9602693e --- /dev/null +++ b/maven-model-builder/src/test/resources/poms/validation/invalid-profile-id.xml @@ -0,0 +1,32 @@ + + + + 4.0.0 + aid + gid + 0.1 + pom + + + + ?invalid-id + + +