From 1ed072b2d777e0fb6114bb5284fafc694a52eaf3 Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Fri, 4 Oct 2024 08:35:42 +0200 Subject: [PATCH] [MNG-3309] Cascading profile activation --- .../model/ProfileActivationContext.java | 10 + .../api/services/model/ProfileSelector.java | 7 +- .../maven/internal/impl/SettingsUtilsV4.java | 2 +- .../impl/model/DefaultModelBuilder.java | 224 ++++-------------- .../DefaultProfileActivationContext.java | 11 + .../impl/model/DefaultProfileSelector.java | 214 ++++++++++++++--- .../impl/model/profile/ConditionParser.java | 6 +- .../profile/PropertyProfileActivator.java | 3 + .../standalone/RepositorySystemSupplier.java | 7 +- .../impl/DefaultConsumerPomBuilder.java | 12 +- .../MavenRepositorySystemSupplier.java | 7 +- 11 files changed, 274 insertions(+), 229 deletions(-) diff --git a/maven-api-impl/src/main/java/org/apache/maven/api/services/model/ProfileActivationContext.java b/maven-api-impl/src/main/java/org/apache/maven/api/services/model/ProfileActivationContext.java index 8c1750342b5b..1cbc26a8986e 100644 --- a/maven-api-impl/src/main/java/org/apache/maven/api/services/model/ProfileActivationContext.java +++ b/maven-api-impl/src/main/java/org/apache/maven/api/services/model/ProfileActivationContext.java @@ -19,9 +19,12 @@ package org.apache.maven.api.services.model; import java.nio.file.Path; +import java.util.Collection; import java.util.List; import java.util.Map; +import org.apache.maven.api.model.Profile; + /** * Describes the environmental context used to determine the activation status of profiles. * @@ -78,4 +81,11 @@ public interface ProfileActivationContext { * @return The project properties, never {@code null}. */ Map getProjectProperties(); + + /** + * Inject properties from newly activated profiles in order to trigger the cascading mechanism. + * + * @param activatedProfiles The collection of profiles that have been activated that may trigger the cascading effect. + */ + void addProfileProperties(Collection activatedProfiles); } diff --git a/maven-api-impl/src/main/java/org/apache/maven/api/services/model/ProfileSelector.java b/maven-api-impl/src/main/java/org/apache/maven/api/services/model/ProfileSelector.java index b85cdd43eb5c..caea8bc98e47 100644 --- a/maven-api-impl/src/main/java/org/apache/maven/api/services/model/ProfileSelector.java +++ b/maven-api-impl/src/main/java/org/apache/maven/api/services/model/ProfileSelector.java @@ -38,8 +38,13 @@ public interface ProfileSelector { * @param context The environmental context used to determine the activation status of a profile, must not be * {@code null}. * @param problems The container used to collect problems that were encountered, must not be {@code null}. + * @param cascade Indicates whether profile activation should cascade, i.e. properties from an activated profile may + * trigger the activation of other profiles. * @return The profiles that have been activated, never {@code null}. */ List getActiveProfiles( - Collection profiles, ProfileActivationContext context, ModelProblemCollector problems); + Collection profiles, + ProfileActivationContext context, + ModelProblemCollector problems, + boolean cascade); } diff --git a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/SettingsUtilsV4.java b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/SettingsUtilsV4.java index 9c2a33482d72..2b3384e870b0 100644 --- a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/SettingsUtilsV4.java +++ b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/SettingsUtilsV4.java @@ -237,7 +237,7 @@ public static org.apache.maven.api.model.Profile convertFromSettingsProfile(Prof } org.apache.maven.api.model.Profile value = profile.build(); - value.setSource("settings.xml"); + value.setSource(org.apache.maven.api.model.Profile.SOURCE_SETTINGS); return value; } diff --git a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelBuilder.java b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelBuilder.java index 52562ae1570b..6935afe419d6 100644 --- a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelBuilder.java +++ b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelBuilder.java @@ -35,7 +35,6 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; -import java.util.Properties; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; @@ -44,7 +43,6 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.Function; import java.util.function.Supplier; -import java.util.function.UnaryOperator; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -59,8 +57,6 @@ import org.apache.maven.api.di.Inject; import org.apache.maven.api.di.Named; import org.apache.maven.api.di.Singleton; -import org.apache.maven.api.model.Activation; -import org.apache.maven.api.model.ActivationFile; import org.apache.maven.api.model.Dependency; import org.apache.maven.api.model.DependencyManagement; import org.apache.maven.api.model.Exclusion; @@ -73,7 +69,6 @@ import org.apache.maven.api.services.BuilderProblem; import org.apache.maven.api.services.BuilderProblem.Severity; import org.apache.maven.api.services.Interpolator; -import org.apache.maven.api.services.InterpolatorException; import org.apache.maven.api.services.MavenException; import org.apache.maven.api.services.ModelBuilder; import org.apache.maven.api.services.ModelBuilderException; @@ -112,7 +107,6 @@ import org.apache.maven.api.spi.ModelParserException; import org.apache.maven.api.spi.ModelTransformer; import org.apache.maven.internal.impl.util.PhasingExecutor; -import org.apache.maven.model.v4.MavenTransformer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -148,7 +142,6 @@ public class DefaultModelBuilder implements ModelBuilder { private final DependencyManagementInjector dependencyManagementInjector; private final DependencyManagementImporter dependencyManagementImporter; private final PluginConfigurationExpander pluginConfigurationExpander; - private final ProfileActivationFilePathInterpolator profileActivationFilePathInterpolator; private final ModelVersionParser versionParser; private final List transformers; private final ModelCacheFactory modelCacheFactory; @@ -172,7 +165,6 @@ public DefaultModelBuilder( DependencyManagementInjector dependencyManagementInjector, DependencyManagementImporter dependencyManagementImporter, PluginConfigurationExpander pluginConfigurationExpander, - ProfileActivationFilePathInterpolator profileActivationFilePathInterpolator, ModelVersionParser versionParser, List transformers, ModelCacheFactory modelCacheFactory, @@ -192,7 +184,6 @@ public DefaultModelBuilder( this.dependencyManagementInjector = dependencyManagementInjector; this.dependencyManagementImporter = dependencyManagementImporter; this.pluginConfigurationExpander = pluginConfigurationExpander; - this.profileActivationFilePathInterpolator = profileActivationFilePathInterpolator; this.versionParser = versionParser; this.transformers = transformers; this.modelCacheFactory = modelCacheFactory; @@ -728,7 +719,8 @@ private void loadFilePom( // keep all loaded file models in memory, those will be needed // during the raw to build transformation putSource(getGroupId(model), model.getArtifactId(), src); - Model activated = activateFileModel(model); + setRootModel(model); + Model activated = activateModel(model, List.of(), false); for (String subproject : getSubprojects(activated)) { if (subproject == null || subproject.isEmpty()) { continue; @@ -790,12 +782,6 @@ private void loadFilePom( } } - static Set concat(Set a, T b) { - Set result = new HashSet<>(a); - result.add(b); - return Set.copyOf(result); - } - void buildEffectiveModel(Collection importIds) throws ModelBuilderException { Model resultModel = readEffectiveModel(); setSource(resultModel); @@ -1049,44 +1035,38 @@ Model resolveAndReadParentExternally(Model childModel) throws ModelBuilderExcept return parentModel; } - Model activateFileModel(Model inputModel) throws ModelBuilderException { - setRootModel(inputModel); - - // profile activation - DefaultProfileActivationContext profileActivationContext = getProfileActivationContext(request, inputModel); - - setSource("(external profiles)"); - List activeExternalProfiles = - profileSelector.getActiveProfiles(request.getProfiles(), profileActivationContext, this); - - result.setActiveExternalProfiles(activeExternalProfiles); - - if (!activeExternalProfiles.isEmpty()) { - Properties profileProps = new Properties(); - for (Profile profile : activeExternalProfiles) { - profileProps.putAll(profile.getProperties()); + Model activateModel(Model model, List parentProfiles, boolean saveInfo) throws ModelBuilderException { + int nbProfiles = request.getProfiles().size() + + parentProfiles.size() + + model.getProfiles().size(); + if (nbProfiles == 0) { + if (saveInfo) { + result.setActivePomProfiles(List.of()); + result.setActiveExternalProfiles(List.of()); } - profileProps.putAll(profileActivationContext.getUserProperties()); - profileActivationContext.setUserProperties(profileProps); + return model; } - profileActivationContext.setProjectProperties(inputModel.getProperties()); - setSource(inputModel); - List activePomProfiles = - profileSelector.getActiveProfiles(inputModel.getProfiles(), profileActivationContext, this); + DefaultProfileActivationContext profileActivationContext = getProfileActivationContext(request, model); - // model normalization - setSource(inputModel); - inputModel = modelNormalizer.mergeDuplicates(inputModel, request, this); + List profiles = new ArrayList<>(nbProfiles); + profiles.addAll(model.getProfiles()); + profiles.addAll(parentProfiles); + profiles.addAll(request.getProfiles()); - Map interpolatedActivations = getProfileActivations(inputModel); - inputModel = injectProfileActivations(inputModel, interpolatedActivations); + boolean cascade = !MODEL_VERSION_4_0_0.equals(model.getModelVersion()); + List activated = + profileSelector.getActiveProfiles(profiles, profileActivationContext, this, cascade); - // profile injection - inputModel = profileInjector.injectProfiles(inputModel, activePomProfiles, request, this); - inputModel = profileInjector.injectProfiles(inputModel, activeExternalProfiles, request, this); + if (saveInfo) { + Map> partitioned = activated.stream() + .collect(Collectors.partitioningBy(p -> Profile.SOURCE_POM.equals(p.getSource()))); + result.setActivePomProfiles(partitioned.get(true)); + result.setActiveExternalProfiles(partitioned.get(false)); + } - return inputModel; + // profile injection + return profileInjector.injectProfiles(model, activated, request, this); } @SuppressWarnings("checkstyle:methodlength") @@ -1096,52 +1076,17 @@ private Model readEffectiveModel() throws ModelBuilderException { throw newModelBuilderException(); } - inputModel = activateFileModel(inputModel); - setRootModel(inputModel); - // profile activation - DefaultProfileActivationContext profileActivationContext = getProfileActivationContext(request, inputModel); - - List activeExternalProfiles = result.getActiveExternalProfiles(); - - if (!activeExternalProfiles.isEmpty()) { - Properties profileProps = new Properties(); - for (Profile profile : activeExternalProfiles) { - profileProps.putAll(profile.getProperties()); - } - profileProps.putAll(profileActivationContext.getUserProperties()); - profileActivationContext.setUserProperties(profileProps); - } - Model parentModel = readParent(inputModel); - List parentInterpolatedProfiles = - interpolateActivations(parentModel.getProfiles(), profileActivationContext, this); - // profile injection - List parentActivePomProfiles = - profileSelector.getActiveProfiles(parentInterpolatedProfiles, profileActivationContext, this); - Model injectedParentModel = profileInjector - .injectProfiles(parentModel, parentActivePomProfiles, request, this) - .withProfiles(List.of()); - - Model model = inheritanceAssembler.assembleModelInheritance(inputModel, injectedParentModel, request, this); + Model model = inheritanceAssembler.assembleModelInheritance(inputModel, parentModel, request, this); // model normalization model = modelNormalizer.mergeDuplicates(model, request, this); // profile activation - profileActivationContext.setProjectProperties(model.getProperties()); - - List interpolatedProfiles = - interpolateActivations(model.getProfiles(), profileActivationContext, this); - - // profile injection - List activePomProfiles = - profileSelector.getActiveProfiles(interpolatedProfiles, profileActivationContext, this); - result.setActivePomProfiles(activePomProfiles); - model = profileInjector.injectProfiles(model, activePomProfiles, request, this); - model = profileInjector.injectProfiles(model, activeExternalProfiles, request, this); + model = activateModel(model, parentModel.getProfiles(), true); // model interpolation Model resultModel = model; @@ -1653,80 +1598,6 @@ private T cache(String groupId, String artifactId, String version, String ta private T cache(Source source, String tag, Supplier supplier) { return cache.computeIfAbsent(source, tag, supplier); } - - private List interpolateActivations( - List profiles, DefaultProfileActivationContext context, ModelProblemCollector problems) { - if (profiles.stream() - .map(org.apache.maven.api.model.Profile::getActivation) - .noneMatch(Objects::nonNull)) { - return profiles; - } - Interpolator interpolator = request.getSession().getService(Interpolator.class); - - class ProfileInterpolator extends MavenTransformer implements UnaryOperator { - ProfileInterpolator() { - super(s -> { - try { - Map map1 = context.getUserProperties(); - Map map2 = context.getSystemProperties(); - return interpolator.interpolate(s, Interpolator.chain(List.of(map1::get, map2::get))); - } catch (InterpolatorException e) { - problems.add(Severity.ERROR, Version.BASE, e.getMessage(), e); - } - return s; - }); - } - - @Override - public Profile apply(Profile p) { - return Profile.newBuilder(p) - .activation(transformActivation(p.getActivation())) - .build(); - } - - @Override - protected Activation.Builder transformActivation_Condition( - Supplier creator, Activation.Builder builder, Activation target) { - // do not interpolate the condition activation - return builder; - } - - @Override - protected ActivationFile.Builder transformActivationFile_Missing( - Supplier creator, - ActivationFile.Builder builder, - ActivationFile target) { - String path = target.getMissing(); - String xformed = transformPath(path, target, "missing"); - return xformed != path ? (builder != null ? builder : creator.get()).missing(xformed) : builder; - } - - @Override - protected ActivationFile.Builder transformActivationFile_Exists( - Supplier creator, - ActivationFile.Builder builder, - ActivationFile target) { - final String path = target.getExists(); - final String xformed = transformPath(path, target, "exists"); - return xformed != path ? (builder != null ? builder : creator.get()).exists(xformed) : builder; - } - - private String transformPath(String path, ActivationFile target, String locationKey) { - try { - return profileActivationFilePathInterpolator.interpolate(path, context); - } catch (InterpolatorException e) { - problems.add( - Severity.ERROR, - Version.BASE, - "Failed to interpolate file location " + path + ": " + e.getMessage(), - target.getLocation(locationKey), - e); - } - return path; - } - } - return profiles.stream().map(new ProfileInterpolator()).toList(); - } } @SuppressWarnings("deprecation") @@ -1765,7 +1636,6 @@ static String getVersion(Model model) { private DefaultProfileActivationContext getProfileActivationContext(ModelBuilderRequest request, Model model) { DefaultProfileActivationContext context = new DefaultProfileActivationContext(); - context.setActiveProfileIds(request.getActiveProfileIds()); context.setInactiveProfileIds(request.getInactiveProfileIds()); context.setSystemProperties(request.getSystemProperties()); @@ -1775,32 +1645,11 @@ private DefaultProfileActivationContext getProfileActivationContext(ModelBuilder userProperties.put(ProfileActivationContext.PROPERTY_NAME_PACKAGING, model.getPackaging()); } context.setUserProperties(userProperties); + context.setProjectProperties(model.getProperties()); context.setProjectDirectory(model.getProjectDirectory()); - return context; } - private Map getProfileActivations(Model model) { - return model.getProfiles().stream() - .filter(p -> p.getActivation() != null) - .collect(Collectors.toMap(Profile::getId, Profile::getActivation)); - } - - private Model injectProfileActivations(Model model, Map activations) { - List profiles = new ArrayList<>(); - boolean modified = false; - for (Profile profile : model.getProfiles()) { - Activation activation = profile.getActivation(); - if (activation != null) { - // restore activation - profile = profile.withActivation(activations.get(profile.getId())); - modified = true; - } - profiles.add(profile); - } - return modified ? model.withProfiles(profiles) : model; - } - private Model interpolateModel(Model model, ModelBuilderRequest request, ModelProblemCollector problems) { Model interpolatedModel = modelInterpolator.interpolateModel(model, model.getProjectDirectory(), request, problems); @@ -1850,8 +1699,7 @@ private Model getSuperModel(String modelVersion) { private static org.apache.maven.api.model.Dependency addExclusions( org.apache.maven.api.model.Dependency candidate, List exclusions) { - return candidate.withExclusions(Stream.concat(candidate.getExclusions().stream(), exclusions.stream()) - .toList()); + return candidate.withExclusions(concat(candidate.getExclusions(), exclusions)); } private boolean match(Exclusion exclusion, Dependency candidate) { @@ -1870,5 +1718,15 @@ private boolean containsCoordinates(String message, String groupId, String artif && (version == null || message.contains(version)); } + private static List concat(Collection l1, Collection l2) { + return Stream.concat(l1.stream(), l2.stream()).toList(); + } + + private static Set concat(Set a, T b) { + Set result = new HashSet<>(a); + result.add(b); + return Set.copyOf(result); + } + record GAKey(String groupId, String artifactId) {} } diff --git a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultProfileActivationContext.java b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultProfileActivationContext.java index 29fda5f4012e..f9e034c0972e 100644 --- a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultProfileActivationContext.java +++ b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultProfileActivationContext.java @@ -19,12 +19,15 @@ package org.apache.maven.internal.impl.model; import java.nio.file.Path; +import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.stream.Collectors; +import org.apache.maven.api.model.Profile; import org.apache.maven.api.services.model.ProfileActivationContext; /** @@ -170,6 +173,14 @@ public DefaultProfileActivationContext setProjectProperties(Map return this; } + public void addProfileProperties(Collection profiles) { + Map props = new HashMap<>(this.projectProperties); + for (var profile : profiles) { + props.putAll(profile.getProperties()); + } + this.projectProperties = unmodifiable(props); + } + private static List unmodifiable(List list) { return list != null ? Collections.unmodifiableList(list) : Collections.emptyList(); } diff --git a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultProfileSelector.java b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultProfileSelector.java index 36dc4a9fcd06..c5dbe498aa4a 100644 --- a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultProfileSelector.java +++ b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultProfileSelector.java @@ -22,77 +22,166 @@ import java.util.Collection; import java.util.HashSet; import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.Supplier; +import java.util.function.UnaryOperator; import org.apache.maven.api.di.Inject; import org.apache.maven.api.di.Named; import org.apache.maven.api.di.Singleton; import org.apache.maven.api.model.Activation; +import org.apache.maven.api.model.ActivationFile; import org.apache.maven.api.model.Profile; import org.apache.maven.api.services.BuilderProblem.Severity; +import org.apache.maven.api.services.Interpolator; +import org.apache.maven.api.services.InterpolatorException; import org.apache.maven.api.services.ModelProblem.Version; import org.apache.maven.api.services.ModelProblemCollector; import org.apache.maven.api.services.model.ProfileActivationContext; import org.apache.maven.api.services.model.ProfileActivator; import org.apache.maven.api.services.model.ProfileSelector; +import org.apache.maven.model.v4.MavenTransformer; /** * Calculates the active profiles among a given collection of profiles. - * */ @Named @Singleton public class DefaultProfileSelector implements ProfileSelector { + private final Interpolator interpolator; + private final ProfileActivationFilePathInterpolator profileActivationFilePathInterpolator; private final List activators; - public DefaultProfileSelector() { - this.activators = new ArrayList<>(); + public DefaultProfileSelector( + Interpolator interpolator, ProfileActivationFilePathInterpolator profileActivationFilePathInterpolator) { + this(interpolator, profileActivationFilePathInterpolator, new ArrayList<>()); } @Inject - public DefaultProfileSelector(List activators) { + public DefaultProfileSelector( + Interpolator interpolator, + ProfileActivationFilePathInterpolator profileActivationFilePathInterpolator, + List activators) { + this.interpolator = interpolator; + this.profileActivationFilePathInterpolator = profileActivationFilePathInterpolator; this.activators = new ArrayList<>(activators); } - public DefaultProfileSelector addProfileActivator(ProfileActivator profileActivator) { - if (profileActivator != null) { - activators.add(profileActivator); + @Override + public List getActiveProfiles( + Collection orgProfiles, + ProfileActivationContext context, + ModelProblemCollector problems, + boolean cascade) { + + if (cascade) { + return getActiveProfilesCascading(orgProfiles, context, problems); + } else { + return getActiveProfilesNonCascading(orgProfiles, context, problems); } - return this; } - @Override - public List getActiveProfiles( + public List getActiveProfilesNonCascading( Collection profiles, ProfileActivationContext context, ModelProblemCollector problems) { + Collection activatedIds = new HashSet<>(context.getActiveProfileIds()); Collection deactivatedIds = new HashSet<>(context.getInactiveProfileIds()); - List activeProfiles = new ArrayList<>(profiles.size()); + List activeSettingsProfiles = new ArrayList<>(); + List activePomProfiles = new ArrayList<>(); List activePomProfilesByDefault = new ArrayList<>(); - boolean activatedPomProfileNotByDefault = false; - - for (Profile profile : profiles) { - if (!deactivatedIds.contains(profile.getId())) { - if (activatedIds.contains(profile.getId()) || isActive(profile, context, problems)) { - activeProfiles.add(profile); - if (Profile.SOURCE_POM.equals(profile.getSource())) { - activatedPomProfileNotByDefault = true; - } - } else if (isActiveByDefault(profile)) { - if (Profile.SOURCE_POM.equals(profile.getSource())) { - activePomProfilesByDefault.add(profile); - } else { - activeProfiles.add(profile); + + ProfileActivationInterpolator activationInterpolator = new ProfileActivationInterpolator(context, problems); + for (String source : List.of(Profile.SOURCE_SETTINGS, Profile.SOURCE_POM)) { + // Iterate over the profiles and check if a given profile is activated + List activatedProfiles = new ArrayList<>(); + for (Profile profile : profiles) { + if (Objects.equals(source, profile.getSource())) { + Profile iprofile = activationInterpolator.apply(profile); + if (!deactivatedIds.contains(iprofile.getId())) { + boolean activated = activatedIds.contains(iprofile.getId()); + boolean active = isActive(iprofile, context, problems); + boolean activeByDefault = isActiveByDefault(iprofile); + if (activated || active || activeByDefault) { + if (Profile.SOURCE_POM.equals(profile.getSource())) { + if (activated || active) { + activePomProfiles.add(profile); + } else { + activePomProfilesByDefault.add(profile); + } + } else { + activeSettingsProfiles.add(profile); + } + activatedProfiles.add(profile); + } } } } + context.addProfileProperties(activatedProfiles); + } + + List allActivated = new ArrayList<>(); + if (activePomProfiles.isEmpty()) { + allActivated.addAll(activePomProfilesByDefault); + } else { + allActivated.addAll(activePomProfiles); } + allActivated.addAll(activeSettingsProfiles); + + return allActivated; + } + + public List getActiveProfilesCascading( + Collection orgProfiles, ProfileActivationContext context, ModelProblemCollector problems) { + + Collection activatedIds = new HashSet<>(context.getActiveProfileIds()); + Collection deactivatedIds = new HashSet<>(context.getInactiveProfileIds()); + + List activeSettingsProfiles = new ArrayList<>(); + List activePomProfiles = new ArrayList<>(); + List activePomProfilesByDefault = new ArrayList<>(); + + List profiles = new ArrayList<>(orgProfiles); + ProfileActivationInterpolator activationInterpolator = new ProfileActivationInterpolator(context, problems); + List activatedProfiles; + do { + // Iterate over the profiles and check if a given profile is activated + activatedProfiles = new ArrayList<>(); + for (Profile profile : List.copyOf(profiles)) { + Profile iprofile = activationInterpolator.apply(profile); + if (!deactivatedIds.contains(iprofile.getId())) { + boolean activated = activatedIds.contains(iprofile.getId()); + boolean active = isActive(iprofile, context, problems); + boolean activeByDefault = isActiveByDefault(iprofile); + if (activated || active || activeByDefault) { + if (Profile.SOURCE_POM.equals(profile.getSource())) { + if (activated || active) { + activePomProfiles.add(profile); + } else { + activePomProfilesByDefault.add(profile); + } + } else { + activeSettingsProfiles.add(profile); + } + profiles.remove(profile); + activatedProfiles.add(profile); + } + } + } + context.addProfileProperties(activatedProfiles); + } while (!activatedProfiles.isEmpty()); - if (!activatedPomProfileNotByDefault) { - activeProfiles.addAll(activePomProfilesByDefault); + List allActivated = new ArrayList<>(); + if (activePomProfiles.isEmpty()) { + allActivated.addAll(activePomProfilesByDefault); + } else { + allActivated.addAll(activePomProfiles); } + allActivated.addAll(activeSettingsProfiles); - return activeProfiles; + return allActivated; } private boolean isActive(Profile profile, ProfileActivationContext context, ModelProblemCollector problems) { @@ -122,4 +211,73 @@ private boolean isActiveByDefault(Profile profile) { Activation activation = profile.getActivation(); return activation != null && activation.isActiveByDefault(); } + + private class ProfileActivationInterpolator extends MavenTransformer implements UnaryOperator { + private final ProfileActivationContext context; + private final ModelProblemCollector problems; + + ProfileActivationInterpolator(ProfileActivationContext context, ModelProblemCollector problems) { + super(s -> { + try { + Map map1 = context.getUserProperties(); + Map map2 = context.getProjectProperties(); + Map map3 = context.getSystemProperties(); + return interpolator.interpolate(s, Interpolator.chain(List.of(map1::get, map2::get, map3::get))); + } catch (InterpolatorException e) { + problems.add(Severity.ERROR, Version.BASE, e.getMessage(), e); + } + return s; + }); + this.context = context; + this.problems = problems; + } + + @Override + public Profile apply(Profile p) { + return Profile.newBuilder(p) + .activation(transformActivation(p.getActivation())) + .build(); + } + + @Override + protected Activation.Builder transformActivation_Condition( + Supplier creator, Activation.Builder builder, Activation target) { + // do not interpolate the condition activation + return builder; + } + + @Override + protected ActivationFile.Builder transformActivationFile_Missing( + Supplier creator, + ActivationFile.Builder builder, + ActivationFile target) { + String path = target.getMissing(); + String xformed = transformPath(path, target, "missing"); + return xformed != path ? (builder != null ? builder : creator.get()).missing(xformed) : builder; + } + + @Override + protected ActivationFile.Builder transformActivationFile_Exists( + Supplier creator, + ActivationFile.Builder builder, + ActivationFile target) { + final String path = target.getExists(); + final String xformed = transformPath(path, target, "exists"); + return xformed != path ? (builder != null ? builder : creator.get()).exists(xformed) : builder; + } + + private String transformPath(String path, ActivationFile target, String locationKey) { + try { + return profileActivationFilePathInterpolator.interpolate(path, context); + } catch (InterpolatorException e) { + problems.add( + Severity.ERROR, + Version.BASE, + "Failed to interpolate file location " + path + ": " + e.getMessage(), + target.getLocation(locationKey), + e); + } + return path; + } + } } diff --git a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/profile/ConditionParser.java b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/profile/ConditionParser.java index 5d216f95f8c0..ba348c832a01 100644 --- a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/profile/ConditionParser.java +++ b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/profile/ConditionParser.java @@ -72,7 +72,7 @@ private List tokenize(String expression) { inPropertyAlias = false; tokens.add("property"); tokens.add("("); - tokens.add("'" + sb.toString() + "'"); + tokens.add("'" + sb + "'"); tokens.add(")"); sb.setLength(0); } else { @@ -89,7 +89,7 @@ private List tokenize(String expression) { } if (c == '$' && i + 1 < expression.length() && expression.charAt(i + 1) == '{') { - if (sb.length() > 0) { + if (!sb.isEmpty()) { tokens.add(sb.toString()); sb.setLength(0); } @@ -100,7 +100,7 @@ private List tokenize(String expression) { } if (c == '"' || c == '\'') { - if (sb.length() > 0) { + if (!sb.isEmpty()) { tokens.add(sb.toString()); sb.setLength(0); } diff --git a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/profile/PropertyProfileActivator.java b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/profile/PropertyProfileActivator.java index a8eb0fc7cf16..884f4a994a67 100644 --- a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/profile/PropertyProfileActivator.java +++ b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/profile/PropertyProfileActivator.java @@ -70,6 +70,9 @@ public boolean isActive(Profile profile, ProfileActivationContext context, Model } String sysValue = context.getUserProperties().get(name); + if (sysValue == null) { + sysValue = context.getProjectProperties().get(name); + } if (sysValue == null) { sysValue = context.getSystemProperties().get(name); } diff --git a/maven-api-impl/src/test/java/org/apache/maven/internal/impl/standalone/RepositorySystemSupplier.java b/maven-api-impl/src/test/java/org/apache/maven/internal/impl/standalone/RepositorySystemSupplier.java index bb0d4c2df5fd..6e70c8b2d88b 100644 --- a/maven-api-impl/src/test/java/org/apache/maven/internal/impl/standalone/RepositorySystemSupplier.java +++ b/maven-api-impl/src/test/java/org/apache/maven/internal/impl/standalone/RepositorySystemSupplier.java @@ -1052,14 +1052,15 @@ protected ModelBuilder createModelBuilder() { new DefaultModelUrlNormalizer(new DefaultUrlNormalizer()), new DefaultSuperPomProvider(modelProcessor), new DefaultInheritanceAssembler(), - new DefaultProfileSelector(), + new DefaultProfileSelector( + new DefaultInterpolator(), + new ProfileActivationFilePathInterpolator( + new DefaultPathTranslator(), new DefaultRootLocator(), new DefaultInterpolator())), new DefaultProfileInjector(), new DefaultPluginManagementInjector(), new DefaultDependencyManagementInjector(), new DefaultDependencyManagementImporter(), new DefaultPluginConfigurationExpander(), - new ProfileActivationFilePathInterpolator( - new DefaultPathTranslator(), new DefaultRootLocator(), new DefaultInterpolator()), new DefaultModelVersionParser(getVersionScheme()), List.of(), new DefaultModelCacheFactory(), diff --git a/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/DefaultConsumerPomBuilder.java b/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/DefaultConsumerPomBuilder.java index d2953b291626..b27f7f8e6fc1 100644 --- a/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/DefaultConsumerPomBuilder.java +++ b/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/DefaultConsumerPomBuilder.java @@ -65,7 +65,6 @@ import org.apache.maven.internal.impl.InternalSession; import org.apache.maven.internal.impl.model.DefaultModelBuilder; import org.apache.maven.internal.impl.model.DefaultProfileSelector; -import org.apache.maven.internal.impl.model.ProfileActivationFilePathInterpolator; import org.apache.maven.model.v4.MavenModelVersion; import org.apache.maven.project.MavenProject; import org.eclipse.aether.RepositorySystemSession; @@ -93,7 +92,6 @@ class DefaultConsumerPomBuilder implements ConsumerPomBuilder { private final PluginManagementInjector pluginManagementInjector; private final SuperPomProvider superPomProvider; private final ModelVersionParser versionParser; - private final ProfileActivationFilePathInterpolator profileActivationFilePathInterpolator; private final List transformers; private final ModelCacheFactory modelCacheFactory; private final ModelResolver modelResolver; @@ -117,7 +115,6 @@ class DefaultConsumerPomBuilder implements ConsumerPomBuilder { PluginManagementInjector pluginManagementInjector, SuperPomProvider superPomProvider, ModelVersionParser versionParser, - ProfileActivationFilePathInterpolator profileActivationFilePathInterpolator, List transformers, ModelCacheFactory modelCacheFactory, ModelResolver modelResolver, @@ -137,7 +134,6 @@ class DefaultConsumerPomBuilder implements ConsumerPomBuilder { this.pluginManagementInjector = pluginManagementInjector; this.superPomProvider = superPomProvider; this.versionParser = versionParser; - this.profileActivationFilePathInterpolator = profileActivationFilePathInterpolator; this.transformers = transformers; this.modelCacheFactory = modelCacheFactory; this.modelResolver = modelResolver; @@ -174,10 +170,13 @@ protected Model buildNonPom(RepositorySystemSession session, MavenProject projec private ModelBuilderResult buildModel(RepositorySystemSession session, MavenProject project, Path src) throws ModelBuilderException { - ProfileSelector customSelector = new DefaultProfileSelector() { + ProfileSelector customSelector = new DefaultProfileSelector(null, null) { @Override public List getActiveProfiles( - Collection profiles, ProfileActivationContext context, ModelProblemCollector problems) { + Collection profiles, + ProfileActivationContext context, + ModelProblemCollector problems, + boolean cascade) { return new ArrayList<>(); } }; @@ -197,7 +196,6 @@ public List getActiveProfiles( dependencyManagementInjector, dependencyManagementImporter, pluginConfigurationExpander, - profileActivationFilePathInterpolator, versionParser, transformers, modelCacheFactory, diff --git a/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/MavenRepositorySystemSupplier.java b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/MavenRepositorySystemSupplier.java index 17893aa6c9ba..ed7bfb9e84f2 100644 --- a/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/MavenRepositorySystemSupplier.java +++ b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/MavenRepositorySystemSupplier.java @@ -1055,14 +1055,15 @@ protected ModelBuilder createModelBuilder() { new DefaultModelUrlNormalizer(new DefaultUrlNormalizer()), new DefaultSuperPomProvider(modelProcessor), new DefaultInheritanceAssembler(), - new DefaultProfileSelector(), + new DefaultProfileSelector( + new DefaultInterpolator(), + new ProfileActivationFilePathInterpolator( + new DefaultPathTranslator(), new DefaultRootLocator(), new DefaultInterpolator())), new DefaultProfileInjector(), new DefaultPluginManagementInjector(), new DefaultDependencyManagementInjector(), new DefaultDependencyManagementImporter(), new DefaultPluginConfigurationExpander(), - new ProfileActivationFilePathInterpolator( - new DefaultPathTranslator(), new DefaultRootLocator(), new DefaultInterpolator()), new DefaultModelVersionParser(getVersionScheme()), List.of(), new DefaultModelCacheFactory(),