You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by gn...@apache.org on 2026/08/26 21:47:32 UTC

(maven) branch master updated: Support Maven version ranges in profile activation (#12767)

This is an automated email from the ASF dual-hosted git repository.

gnodet pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/maven.git


The following commit(s) were added to refs/heads/master by this push:
     new 79f7ee41cd Support Maven version ranges in profile activation (#12767)
79f7ee41cd is described below

commit 79f7ee41cd24e46f4527fc5db71b333d9b255fa6
Author: Goutam Adwant <86...@users.noreply.github.com>
AuthorDate: Wed Aug 26 14:47:26 2026 -0700

    Support Maven version ranges in profile activation (#12767)
    
    Recognize version ranges for the existing maven.version activation property without changing the POM model. Preserve exact matching for other values and report malformed ranges as warnings.\n\nCover bounded, open-ended, negated, and malformed ranges, including activation through the model builder.
---
 .../model/profile/PropertyProfileActivator.java    |  42 ++++++++-
 .../maven/impl/model/DefaultModelBuilderTest.java  |  15 +++
 .../profile/PropertyProfileActivatorTest.java      | 105 ++++++++++++++++++++-
 .../poms/factory/maven-version-range-profile.xml   |  37 ++++++++
 4 files changed, 196 insertions(+), 3 deletions(-)

diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/model/profile/PropertyProfileActivator.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/model/profile/PropertyProfileActivator.java
index 8b2114ad36..f83f7ab072 100644
--- a/impl/maven-impl/src/main/java/org/apache/maven/impl/model/profile/PropertyProfileActivator.java
+++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/model/profile/PropertyProfileActivator.java
@@ -18,6 +18,8 @@
  */
 package org.apache.maven.impl.model.profile;
 
+import org.apache.maven.api.Constants;
+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;
@@ -26,6 +28,8 @@
 import org.apache.maven.api.services.BuilderProblem;
 import org.apache.maven.api.services.ModelProblem;
 import org.apache.maven.api.services.ModelProblemCollector;
+import org.apache.maven.api.services.VersionParserException;
+import org.apache.maven.api.services.model.ModelVersionParser;
 import org.apache.maven.api.services.model.ProfileActivationContext;
 import org.apache.maven.api.services.model.ProfileActivator;
 
@@ -38,6 +42,13 @@
 @Singleton
 public class PropertyProfileActivator implements ProfileActivator {
 
+    private final ModelVersionParser versionParser;
+
+    @Inject
+    public PropertyProfileActivator(ModelVersionParser versionParser) {
+        this.versionParser = versionParser;
+    }
+
     @Override
     public boolean isActive(Profile profile, ProfileActivationContext context, ModelProblemCollector problems) {
         Activation activation = profile.getActivation();
@@ -85,13 +96,40 @@ public boolean isActive(Profile profile, ProfileActivationContext context, Model
                 propValue = propValue.substring(1);
             }
 
-            // we have a value, so it has to match the system value...
-            return reverseValue != propValue.equals(sysValue);
+            boolean result;
+            if (Constants.MAVEN_VERSION.equals(name) && isVersionRange(propValue)) {
+                if (sysValue == null || sysValue.isEmpty()) {
+                    result = false;
+                } else {
+                    try {
+                        result = versionParser
+                                .parseVersionRange(propValue)
+                                .contains(versionParser.parseVersion(sysValue));
+                    } catch (VersionParserException e) {
+                        problems.add(
+                                BuilderProblem.Severity.WARNING,
+                                ModelProblem.Version.BASE,
+                                "Failed to determine Maven version activation for profile " + profile.getId()
+                                        + " due to invalid version range: '" + propValue + "'",
+                                property.getLocation("value"),
+                                e);
+                        return false;
+                    }
+                }
+            } else {
+                // we have a value, so it has to match the system value...
+                result = propValue.equals(sysValue);
+            }
+            return reverseValue != result;
         } else {
             return reverseName != (sysValue != null && !sysValue.isEmpty());
         }
     }
 
+    private static boolean isVersionRange(String value) {
+        return value.startsWith("[") || value.startsWith("(");
+    }
+
     @Override
     public boolean presentInConfig(Profile profile, ProfileActivationContext context, ModelProblemCollector problems) {
         Activation activation = profile.getActivation();
diff --git a/impl/maven-impl/src/test/java/org/apache/maven/impl/model/DefaultModelBuilderTest.java b/impl/maven-impl/src/test/java/org/apache/maven/impl/model/DefaultModelBuilderTest.java
index 17e8f24e64..905b5035f0 100644
--- a/impl/maven-impl/src/test/java/org/apache/maven/impl/model/DefaultModelBuilderTest.java
+++ b/impl/maven-impl/src/test/java/org/apache/maven/impl/model/DefaultModelBuilderTest.java
@@ -33,6 +33,7 @@
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
 
+import org.apache.maven.api.Constants;
 import org.apache.maven.api.RemoteRepository;
 import org.apache.maven.api.Session;
 import org.apache.maven.api.model.Dependency;
@@ -184,6 +185,20 @@ void testMappedSourcesSuppressesDuplicateSources() {
         assertEquals(source, state.getSource(null, "duplicate-artifact"));
     }
 
+    @Test
+    void testMavenVersionRangeProfileActivation() {
+        ModelBuilderRequest request = ModelBuilderRequest.builder()
+                .session(session)
+                .requestType(ModelBuilderRequest.RequestType.BUILD_PROJECT)
+                .source(Sources.buildSource(getPom("maven-version-range-profile")))
+                .systemProperties(Map.of(Constants.MAVEN_VERSION, "4.1.0"))
+                .build();
+
+        ModelBuilderResult result = builder.newSession().build(request);
+
+        assertEquals("true", result.getEffectiveModel().getProperties().get("maven.range.profile.active"));
+    }
+
     @Test
     public void testMergeRepositories() throws Exception {
         // this is here only to trigger mainSession creation; unrelated
diff --git a/impl/maven-impl/src/test/java/org/apache/maven/impl/model/profile/PropertyProfileActivatorTest.java b/impl/maven-impl/src/test/java/org/apache/maven/impl/model/profile/PropertyProfileActivatorTest.java
index 0f193614b2..9b378fe76d 100644
--- a/impl/maven-impl/src/test/java/org/apache/maven/impl/model/profile/PropertyProfileActivatorTest.java
+++ b/impl/maven-impl/src/test/java/org/apache/maven/impl/model/profile/PropertyProfileActivatorTest.java
@@ -20,12 +20,19 @@
 
 import java.util.Map;
 
+import org.apache.maven.api.Constants;
 import org.apache.maven.api.model.Activation;
 import org.apache.maven.api.model.ActivationProperty;
 import org.apache.maven.api.model.Profile;
+import org.apache.maven.api.services.model.ProfileActivationContext;
+import org.apache.maven.impl.DefaultModelVersionParser;
+import org.eclipse.aether.util.version.GenericVersionScheme;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+
 /**
  * Tests {@link PropertyProfileActivator}.
  *
@@ -35,7 +42,7 @@ class PropertyProfileActivatorTest extends AbstractProfileActivatorTest<Property
     @BeforeEach
     @Override
     void setUp() throws Exception {
-        activator = new PropertyProfileActivator();
+        activator = new PropertyProfileActivator(new DefaultModelVersionParser(new GenericVersionScheme()));
     }
 
     private Profile newProfile(String key, String value) {
@@ -163,4 +170,100 @@ void testWithValueUserPropertyDominantOverSystemProperty() throws Exception {
 
         assertActivation(false, profile, newContext(props2, props1));
     }
+
+    @Test
+    void testMavenVersionRange() {
+        Profile profile = newProfile(Constants.MAVEN_VERSION, "[4.0.0-rc-5,4.0.0)");
+
+        assertActivation(true, profile, newContext(null, newProperties(Constants.MAVEN_VERSION, "4.0.0-rc-5")));
+        assertActivation(
+                true, profile, newContext(null, newProperties(Constants.MAVEN_VERSION, "4.0.0-rc-6-SNAPSHOT")));
+        assertActivation(false, profile, newContext(null, newProperties(Constants.MAVEN_VERSION, "4.0.0")));
+        assertActivation(false, profile, newContext(null, newProperties(Constants.MAVEN_VERSION, "3.9.11")));
+        assertActivation(false, profile, newContext(null, newProperties(Constants.MAVEN_VERSION, "")));
+        assertActivation(false, profile, newContext(null, Map.of()));
+    }
+
+    @Test
+    void testMavenVersionLowerBoundedRange() {
+        Profile profile = newProfile(Constants.MAVEN_VERSION, "[4.0.0,)");
+
+        assertActivation(false, profile, newContext(null, newProperties(Constants.MAVEN_VERSION, "4.0.0-rc-5")));
+        assertActivation(true, profile, newContext(null, newProperties(Constants.MAVEN_VERSION, "4.0.0")));
+        assertActivation(true, profile, newContext(null, newProperties(Constants.MAVEN_VERSION, "4.1.0")));
+    }
+
+    @Test
+    void testMavenVersionUpperBoundedRange() {
+        Profile profile = newProfile(Constants.MAVEN_VERSION, "(,4.0.0)");
+
+        assertActivation(true, profile, newContext(null, newProperties(Constants.MAVEN_VERSION, "3.9.11")));
+        assertActivation(true, profile, newContext(null, newProperties(Constants.MAVEN_VERSION, "4.0.0-rc-5")));
+        assertActivation(false, profile, newContext(null, newProperties(Constants.MAVEN_VERSION, "4.0.0")));
+    }
+
+    @Test
+    void testMavenVersionRangeBoundaries() {
+        Profile inclusive = newProfile(Constants.MAVEN_VERSION, "[4.0.0,5.0.0]");
+        Profile exclusive = newProfile(Constants.MAVEN_VERSION, "(4.0.0,5.0.0)");
+
+        assertActivation(true, inclusive, newContext(null, newProperties(Constants.MAVEN_VERSION, "4.0.0")));
+        assertActivation(true, inclusive, newContext(null, newProperties(Constants.MAVEN_VERSION, "5.0.0")));
+        assertActivation(false, exclusive, newContext(null, newProperties(Constants.MAVEN_VERSION, "4.0.0")));
+        assertActivation(true, exclusive, newContext(null, newProperties(Constants.MAVEN_VERSION, "4.1.0")));
+        assertActivation(false, exclusive, newContext(null, newProperties(Constants.MAVEN_VERSION, "5.0.0")));
+    }
+
+    @Test
+    void testNegatedMavenVersionRange() {
+        Profile profile = newProfile(Constants.MAVEN_VERSION, "![4.0.0,5.0.0)");
+
+        assertActivation(false, profile, newContext(null, newProperties(Constants.MAVEN_VERSION, "4.1.0")));
+        assertActivation(true, profile, newContext(null, newProperties(Constants.MAVEN_VERSION, "5.0.0")));
+        assertActivation(true, profile, newContext(null, Map.of()));
+    }
+
+    @Test
+    void testMavenVersionExactMatchIsPreserved() {
+        Profile profile = newProfile(Constants.MAVEN_VERSION, "4.0.0-rc-5");
+
+        assertActivation(true, profile, newContext(null, newProperties(Constants.MAVEN_VERSION, "4.0.0-rc-5")));
+        assertActivation(false, profile, newContext(null, newProperties(Constants.MAVEN_VERSION, "4.0.0-rc-6")));
+    }
+
+    @Test
+    void testRangeSyntaxRemainsAnExactMatchForOtherProperties() {
+        Profile profile = newProfile("prop", "[1,2)");
+
+        assertActivation(true, profile, newContext(null, newProperties("prop", "[1,2)")));
+        assertActivation(false, profile, newContext(null, newProperties("prop", "1.5")));
+    }
+
+    @Test
+    void testMalformedMavenVersionRangeDoesNotActivate() {
+        Profile profile = newProfile(Constants.MAVEN_VERSION, "[4.0.0,");
+        ProfileActivationContext context = newContext(null, newProperties(Constants.MAVEN_VERSION, "4.1.0"));
+        SimpleProblemCollector problems = new SimpleProblemCollector();
+
+        assertFalse(activator.isActive(profile, context, problems));
+        assertEquals(0, problems.getErrors().size());
+        assertEquals(1, problems.getWarnings().size());
+        assertEquals(
+                "Failed to determine Maven version activation for profile default due to invalid version range: '[4.0.0,'",
+                problems.getWarnings().get(0));
+    }
+
+    @Test
+    void testMalformedNegatedMavenVersionRangeDoesNotActivate() {
+        Profile profile = newProfile(Constants.MAVEN_VERSION, "![4.0.0,");
+        ProfileActivationContext context = newContext(null, newProperties(Constants.MAVEN_VERSION, "4.1.0"));
+        SimpleProblemCollector problems = new SimpleProblemCollector();
+
+        assertFalse(activator.isActive(profile, context, problems));
+        assertEquals(0, problems.getErrors().size());
+        assertEquals(1, problems.getWarnings().size());
+        assertEquals(
+                "Failed to determine Maven version activation for profile default due to invalid version range: '[4.0.0,'",
+                problems.getWarnings().get(0));
+    }
 }
diff --git a/impl/maven-impl/src/test/resources/poms/factory/maven-version-range-profile.xml b/impl/maven-impl/src/test/resources/poms/factory/maven-version-range-profile.xml
new file mode 100644
index 0000000000..f64b05b4ad
--- /dev/null
+++ b/impl/maven-impl/src/test/resources/poms/factory/maven-version-range-profile.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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 xmlns="http://maven.apache.org/POM/4.1.0">
+    <groupId>org.apache.maven.tests</groupId>
+    <artifactId>maven-version-range-profile</artifactId>
+    <version>1.0</version>
+
+    <profiles>
+        <profile>
+            <id>maven-version-range</id>
+            <activation>
+                <property>
+                    <name>maven.version</name>
+                    <value>[4.0.0,)</value>
+                </property>
+            </activation>
+            <properties>
+                <maven.range.profile.active>true</maven.range.profile.active>
+            </properties>
+        </profile>
+    </profiles>
+</project>