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/10/15 07:15:57 UTC

(maven) branch master updated: Fix #10375: activate parent file profiles with the parent's properties

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 ce26838575 Fix #10375: activate parent file profiles with the parent's properties
ce26838575 is described below

commit ce26838575407bc4a784dee58ebb5d65bcf639f2
Author: Mathias Nicolajsen Kjærgaard <mn...@mnk.dk>
AuthorDate: Thu Oct 15 09:15:50 2026 +0200

    Fix #10375: activate parent file profiles with the parent's properties
    
    When reading a parent for a child, ParentModelFrame activated the parent's profiles against a context derived from the child's pre-inheritance model. That model has the right basedir for per-module file checks but lacks the parent's properties, so property placeholders (e.g. ${message.file}) did not interpolate and the profile stayed inactive.
    
    - Activate parent profiles in a fresh recording context (new DefaultProfileActivationContext.start(Map)) whose model properties are those of the assembled parent, keeping the child's basedir, packaging and artifactId
    - The recording context also serves as the parent-model cache key, so it is not mutated in place with setModel(parent) to avoid stale cache hits across grandparent records
    - Add a new replayRecordIntoContext overload that replays the activation record without model properties, so parent-derived values don't pollute child lookups
    - Add DefaultModelBuilderTest case and core IT MavenITmng8710
    
    Fixes https://github.com/apache/maven/issues/10375 (MNG-8710)
---
 .../maven/impl/model/DefaultModelBuilder.java      | 17 ++++-
 .../model/DefaultProfileActivationContext.java     | 17 +++++
 .../maven/impl/model/DefaultModelBuilderTest.java  | 25 +++++++
 .../poms/factory/mng-8710/activate.marker          |  0
 .../poms/factory/mng-8710/child/activate.marker    |  0
 .../resources/poms/factory/mng-8710/child/pom.xml  | 10 +++
 .../test/resources/poms/factory/mng-8710/pom.xml   | 27 ++++++++
 ...enITmng8710ParentFileProfileActivationTest.java | 52 +++++++++++++++
 .../src/test/resources/mng-8710/my-lib/pom.xml     | 30 +++++++++
 .../src/test/resources/mng-8710/my-lib/test.txt    |  1 +
 .../src/test/resources/mng-8710/pom.xml            | 78 ++++++++++++++++++++++
 .../src/test/resources/mng-8710/test.txt           |  1 +
 12 files changed, 256 insertions(+), 2 deletions(-)

diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelBuilder.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelBuilder.java
index 5a8f6fd165..c816d33cd4 100644
--- a/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelBuilder.java
+++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelBuilder.java
@@ -2523,7 +2523,11 @@ ParentFrame advance(Set<ModelSource> activeSources, Set<ResolvedParentKey> activ
                             phase = ParentModelPhase.MERGE_MIXIN;
                             return new ParentResolutionFrame(model, mixins.next(), recording, chain, false, false);
                         }
-                        List<Profile> profiles = getActiveProfiles(model.getProfiles(), recording);
+                        // Evaluate with the parent's properties but the child's basedir (as Maven 3 does);
+                        // parent properties are kept out of the cache key since they derive from the source.
+                        DefaultProfileActivationContext activation = recording.start(model.getProperties());
+                        List<Profile> profiles = getActiveProfiles(model.getProfiles(), activation);
+                        replayRecordIntoContext(activation.stop(), recording, false);
                         model = profileInjector
                                 .injectProfiles(model, profiles, request, ModelBuilderSessionState.this)
                                 .withProfiles(List.of())
@@ -3001,6 +3005,13 @@ boolean isBuildRequestWithActivation() {
          */
         private void replayRecordIntoContext(
                 DefaultProfileActivationContext.Record cachedRecord, DefaultProfileActivationContext targetContext) {
+            replayRecordIntoContext(cachedRecord, targetContext, true);
+        }
+
+        private void replayRecordIntoContext(
+                DefaultProfileActivationContext.Record cachedRecord,
+                DefaultProfileActivationContext targetContext,
+                boolean includeModelProperties) {
             if (targetContext.record == null) {
                 return; // Target context is not recording
             }
@@ -3022,7 +3033,9 @@ private void replayRecordIntoContext(
             cachedRecord.usedUserProperties.forEach(targetRecord.usedUserProperties::putIfAbsent);
 
             // Replay model properties
-            cachedRecord.usedModelProperties.forEach(targetRecord.usedModelProperties::putIfAbsent);
+            if (includeModelProperties) {
+                cachedRecord.usedModelProperties.forEach(targetRecord.usedModelProperties::putIfAbsent);
+            }
 
             // Replay model infos
             cachedRecord.usedModelInfos.forEach(targetRecord.usedModelInfos::putIfAbsent);
diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultProfileActivationContext.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultProfileActivationContext.java
index 7a4ccd7850..895d0b45b2 100644
--- a/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultProfileActivationContext.java
+++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultProfileActivationContext.java
@@ -226,6 +226,23 @@ DefaultProfileActivationContext start() {
                 new Record());
     }
 
+    /**
+     * Like {@link #start()}, but overrides the model's properties with the given map.
+     * The model's other attributes (basedir, packaging, artifactId) are preserved
+     * so that file-exists profile conditions resolve against the original project directory.
+     */
+    DefaultProfileActivationContext start(Map<String, String> modelProperties) {
+        return new DefaultProfileActivationContext(
+                rootLocator,
+                interpolator,
+                activeProfileIds,
+                inactiveProfileIds,
+                systemProperties,
+                userProperties,
+                model != null ? model.withProperties(modelProperties) : null,
+                new Record());
+    }
+
     Record stop() {
         Objects.requireNonNull(record, "start() must be called before stop()");
         return new Record(record); // Return immutable copy for thread-safe caching
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 9a5af0e462..ce11369b31 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
@@ -1739,6 +1739,31 @@ void testBomImportWarningFromNonReactorParentIsPreserved() {
         }
     }
 
+    @Test
+    void testMng8710ParentFileProfileWithPropertyActivationAppliesToChild() {
+        Path pom = Paths.get("src/test/resources/poms/factory/mng-8710/pom.xml").toAbsolutePath();
+        ModelBuilderResult result = builder.newSession()
+                .build(ModelBuilderRequest.builder()
+                        .session(session)
+                        .requestType(ModelBuilderRequest.RequestType.BUILD_PROJECT)
+                        .recursive(true)
+                        .source(Sources.buildSource(pom))
+                        .build());
+
+        Model parentModel = result.getEffectiveModel();
+        assertEquals("activated", parentModel.getProperties().get("profile.from.parent"));
+
+        ModelBuilderResult childResult = result.getChildren().stream()
+                .filter(r -> "mng-8710-child".equals(r.getEffectiveModel().getArtifactId()))
+                .findFirst()
+                .orElseThrow(() -> new AssertionError("child result missing: " + result.getChildren()));
+        Model childModel = childResult.getEffectiveModel();
+        assertEquals(
+                "activated",
+                childModel.getProperties().get("profile.from.parent"),
+                "Parent file profile interpolating ${message.file} must inject into child (MNG-8710)");
+    }
+
     @Test
     void testBomImportWarningFromParentProfileActivatedForChild() {
         Path pom = Paths.get("src/test/resources/poms/factory/mng-8450-profile-context/pom.xml")
diff --git a/impl/maven-impl/src/test/resources/poms/factory/mng-8710/activate.marker b/impl/maven-impl/src/test/resources/poms/factory/mng-8710/activate.marker
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/impl/maven-impl/src/test/resources/poms/factory/mng-8710/child/activate.marker b/impl/maven-impl/src/test/resources/poms/factory/mng-8710/child/activate.marker
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/impl/maven-impl/src/test/resources/poms/factory/mng-8710/child/pom.xml b/impl/maven-impl/src/test/resources/poms/factory/mng-8710/child/pom.xml
new file mode 100644
index 0000000000..f3c51cb7a6
--- /dev/null
+++ b/impl/maven-impl/src/test/resources/poms/factory/mng-8710/child/pom.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.maven.test</groupId>
+    <artifactId>mng-8710-parent</artifactId>
+    <version>1.0.0</version>
+  </parent>
+  <artifactId>mng-8710-child</artifactId>
+</project>
diff --git a/impl/maven-impl/src/test/resources/poms/factory/mng-8710/pom.xml b/impl/maven-impl/src/test/resources/poms/factory/mng-8710/pom.xml
new file mode 100644
index 0000000000..c6f6fda833
--- /dev/null
+++ b/impl/maven-impl/src/test/resources/poms/factory/mng-8710/pom.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0">
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>org.apache.maven.test</groupId>
+  <artifactId>mng-8710-parent</artifactId>
+  <version>1.0.0</version>
+  <packaging>pom</packaging>
+  <properties>
+    <message.file>activate.marker</message.file>
+  </properties>
+  <modules>
+    <module>child</module>
+  </modules>
+  <profiles>
+    <profile>
+      <id>file-from-parent</id>
+      <activation>
+        <file>
+          <exists>${message.file}</exists>
+        </file>
+      </activation>
+      <properties>
+        <profile.from.parent>activated</profile.from.parent>
+      </properties>
+    </profile>
+  </profiles>
+</project>
diff --git a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng8710ParentFileProfileActivationTest.java b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng8710ParentFileProfileActivationTest.java
new file mode 100644
index 0000000000..cf5ab199e0
--- /dev/null
+++ b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng8710ParentFileProfileActivationTest.java
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+package org.apache.maven.it;
+
+import java.nio.file.Path;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * This is a test set for <a href="https://issues.apache.org/jira/browse/MNG-8710">MNG-8710</a>.
+ *
+ * A file-based profile defined in a parent POM that interpolates a parent property in
+ * {@code <exists>} must activate for child modules (using each child's basedir) and inject
+ * the profile's build plugins into the child's effective model.
+ */
+class MavenITmng8710ParentFileProfileActivationTest extends AbstractMavenIntegrationTestCase {
+
+    @Test
+    void testParentFileProfileAppliesToChild() throws Exception {
+        Path testDir = extractResources("mng-8710");
+
+        Verifier verifier = newVerifier(testDir);
+        verifier.setAutoclean(false);
+        verifier.deleteDirectory("target");
+        verifier.deleteDirectory("my-lib/target");
+        verifier.addCliArgument("validate");
+        verifier.execute();
+        verifier.verifyErrorFreeLog();
+
+        verifier.verifyFilePresent("target/profile-activated.txt");
+        verifier.verifyFilePresent("my-lib/target/profile-activated.txt");
+
+        verifier.verifyTextInLog("Profile activated in my-app: Test 1");
+        verifier.verifyTextInLog("Profile activated in my-lib: Test 2");
+    }
+}
diff --git a/its/core-it-suite/src/test/resources/mng-8710/my-lib/pom.xml b/its/core-it-suite/src/test/resources/mng-8710/my-lib/pom.xml
new file mode 100644
index 0000000000..3742ae094a
--- /dev/null
+++ b/its/core-it-suite/src/test/resources/mng-8710/my-lib/pom.xml
@@ -0,0 +1,30 @@
+<?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://maven.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.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.maven.its.mng8710</groupId>
+    <artifactId>my-app</artifactId>
+    <version>1.0-SNAPSHOT</version>
+  </parent>
+  <artifactId>my-lib</artifactId>
+</project>
diff --git a/its/core-it-suite/src/test/resources/mng-8710/my-lib/test.txt b/its/core-it-suite/src/test/resources/mng-8710/my-lib/test.txt
new file mode 100644
index 0000000000..110c58e6f3
--- /dev/null
+++ b/its/core-it-suite/src/test/resources/mng-8710/my-lib/test.txt
@@ -0,0 +1 @@
+Test 2
diff --git a/its/core-it-suite/src/test/resources/mng-8710/pom.xml b/its/core-it-suite/src/test/resources/mng-8710/pom.xml
new file mode 100644
index 0000000000..b1f551a16e
--- /dev/null
+++ b/its/core-it-suite/src/test/resources/mng-8710/pom.xml
@@ -0,0 +1,78 @@
+<?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.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <groupId>org.apache.maven.its.mng8710</groupId>
+  <artifactId>my-app</artifactId>
+  <version>1.0-SNAPSHOT</version>
+  <packaging>pom</packaging>
+
+  <name>Maven Integration Test :: MNG-8710 :: Parent</name>
+  <description>Parent file-based profile with property interpolation must apply to children</description>
+
+  <properties>
+    <message.file>test.txt</message.file>
+  </properties>
+
+  <modules>
+    <module>my-lib</module>
+  </modules>
+
+  <profiles>
+    <profile>
+      <id>test</id>
+      <activation>
+        <file>
+          <exists>${message.file}</exists>
+        </file>
+      </activation>
+      <properties>
+        <profile.activated>true</profile.activated>
+      </properties>
+      <build>
+        <plugins>
+          <plugin>
+            <artifactId>maven-antrun-plugin</artifactId>
+            <version>3.1.0</version>
+            <executions>
+              <execution>
+                <id>echo-profile</id>
+                <phase>validate</phase>
+                <configuration>
+                  <target>
+                    <loadfile property="message" srcFile="${message.file}"/>
+                    <echo>Profile activated in ${project.artifactId}: ${message}</echo>
+                    <mkdir dir="${project.build.directory}"/>
+                    <touch file="${project.build.directory}/profile-activated.txt"/>
+                  </target>
+                </configuration>
+                <goals>
+                  <goal>run</goal>
+                </goals>
+              </execution>
+            </executions>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+  </profiles>
+</project>
diff --git a/its/core-it-suite/src/test/resources/mng-8710/test.txt b/its/core-it-suite/src/test/resources/mng-8710/test.txt
new file mode 100644
index 0000000000..ac5ab8fbdb
--- /dev/null
+++ b/its/core-it-suite/src/test/resources/mng-8710/test.txt
@@ -0,0 +1 @@
+Test 1