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/07/30 11:49:59 UTC

(maven) 01/01: Fix BOM consumer POM leaving property references unresolved

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

gnodet pushed a commit to branch backport/12625-to-maven-4.0.x
in repository https://gitbox.apache.org/repos/asf/maven.git

commit 4032fc8225c879c86245a232787802efcece5d74
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Thu Jul 30 13:41:36 2026 +0200

    Fix BOM consumer POM leaving property references unresolved
    
    Route all BOMs through buildBom() which uses getEffectiveModel() (fully
    interpolated), instead of buildBomWithoutFlatten() which used
    getRawModel(). The latter was incompatible with transformBom() which
    strips parent and properties — leaving dangling ${...} references in
    the consumer POM. Delete the now-dead buildBomWithoutFlatten() method.
    
    Reported by Karl Heinz Marbaise during the 4.0.0-rc-6 vote.
    
    Closes #12625
---
 .../impl/DefaultConsumerPomBuilder.java            |  16 +-
 ...avenITBomConsumerPomPropertyResolutionTest.java | 184 +++++++++++++++++++++
 .../bom/pom.xml                                    |  61 +++++++
 .../mod-1/pom.xml                                  |  35 ++++
 .../bom-consumer-pom-property-resolution/pom.xml   |  41 +++++
 5 files changed, 326 insertions(+), 11 deletions(-)

diff --git a/impl/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/DefaultConsumerPomBuilder.java b/impl/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/DefaultConsumerPomBuilder.java
index 2de90988a1..c9c13f89e2 100644
--- a/impl/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/DefaultConsumerPomBuilder.java
+++ b/impl/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/DefaultConsumerPomBuilder.java
@@ -89,9 +89,12 @@ public Model build(RepositorySystemSession session, MavenProject project, ModelS
         if (!flattenEnabled) {
             // When flattening is disabled, treat non-POM projects like parent POMs
             // Apply only basic transformations without flattening dependency management
-            // However, BOMs still need special handling to transform packaging from "bom" to "pom"
+            // BOMs always need the effective (interpolated) model because transformBom()
+            // strips parent and properties — any ${...} references would become dangling.
+            // The flatten flag has no semantic effect on BOMs (transformBom always produces
+            // a self-contained POM), so we use the same buildBom() path regardless.
             if (isBom) {
-                return buildBomWithoutFlatten(session, project, src);
+                return buildBom(session, project, src);
             } else {
                 Model result = buildPom(session, project, src);
                 // Validate POM-packaged projects (parent POMs): if the consumer POM cannot be
@@ -136,15 +139,6 @@ protected Model buildPom(RepositorySystemSession session, MavenProject project,
         return transformPom(model, project);
     }
 
-    protected Model buildBomWithoutFlatten(RepositorySystemSession session, MavenProject project, ModelSource src)
-            throws ModelBuilderException {
-        ModelBuilderResult result = buildModel(session, project, src);
-        Model model = result.getRawModel();
-        // For BOMs without flattening, we just need to transform the packaging from "bom" to "pom"
-        // but keep everything else from the raw model (including unresolved versions)
-        return transformBom(model, project);
-    }
-
     protected Model buildBom(RepositorySystemSession session, MavenProject project, ModelSource src)
             throws ModelBuilderException {
         ModelBuilderResult result = buildModel(session, project, src);
diff --git a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITBomConsumerPomPropertyResolutionTest.java b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITBomConsumerPomPropertyResolutionTest.java
new file mode 100644
index 0000000000..eaca1545f5
--- /dev/null
+++ b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITBomConsumerPomPropertyResolutionTest.java
@@ -0,0 +1,184 @@
+/*
+ * 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.Files;
+import java.nio.file.Path;
+import java.util.List;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Verifies that the BOM consumer POM resolves property-referenced dependency
+ * versions, especially when the property is defined in a parent POM.
+ * <p>
+ * Reproducer for the issue reported by Karl Heinz Marbaise during the
+ * Maven 4.0.0-rc-6 vote: when a BOM's {@code <dependencyManagement>} uses a
+ * property like {@code ${junit.version}} for an external dependency version,
+ * and that property is defined in the parent POM, the consumer POM leaves the
+ * property reference unresolved. This happens because
+ * {@code DefaultConsumerPomBuilder.buildBomWithoutFlatten()} uses
+ * {@code getRawModel()} (no interpolation) and then {@code transformBom()}
+ * strips the parent and properties — leaving dangling {@code ${...}} references.
+ *
+ * @see <a href="https://lists.apache.org/thread/2s2myrg0zzk0q5z9kjhm8pwsntxmtnxh">Vote thread</a>
+ * @since 4.0.0
+ */
+class MavenITBomConsumerPomPropertyResolutionTest extends AbstractMavenIntegrationTestCase {
+
+    /**
+     * Verify that the BOM consumer POM (default, no flatten) resolves
+     * property-referenced versions in dependencyManagement.
+     * <p>
+     * The parent POM defines {@code junit.version=4.13.2}. The BOM child
+     * references it as {@code ${junit.version}}. The consumer POM must
+     * contain the resolved literal {@code 4.13.2}, not the raw property
+     * expression, because the consumer POM has no parent or properties
+     * section to resolve it from.
+     */
+    @Test
+    void testBomConsumerPomResolvesParentProperties() throws Exception {
+        Path basedir = extractResources("/bom-consumer-pom-property-resolution");
+
+        Verifier verifier = newVerifier(basedir);
+        verifier.deleteArtifacts("org.apache.maven.its.bom-property");
+        verifier.addCliArguments("install");
+        verifier.execute();
+        verifier.verifyErrorFreeLog();
+
+        // Read the consumer POM that was installed to the local repo
+        Path consumerPomPath =
+                verifier.getArtifactPath("org.apache.maven.its.bom-property", "bom", "1.0.0-SNAPSHOT", "pom");
+
+        assertTrue(Files.exists(consumerPomPath), "Consumer POM not found at " + consumerPomPath);
+
+        List<String> lines = Files.readAllLines(consumerPomPath);
+        String content = String.join("\n", lines);
+
+        // 1. Packaging must be "pom" (not "bom")
+        assertTrue(
+                content.contains("<packaging>pom</packaging>"),
+                "Consumer POM packaging should be 'pom', not 'bom'");
+
+        // 2. The consumer POM strips parent and properties, so inherited
+        //    fields must be inlined: groupId and version must be present
+        assertTrue(
+                content.contains("<groupId>org.apache.maven.its.bom-property</groupId>"),
+                "Consumer POM must have groupId inlined (parent is stripped).\nActual:\n" + content);
+        assertTrue(
+                content.contains("<version>1.0.0-SNAPSHOT</version>"),
+                "Consumer POM must have version inlined (parent is stripped).\nActual:\n" + content);
+
+        // 3. The key assertion: no unresolved ${...} property references
+        //    anywhere in the consumer POM. The parent and properties sections
+        //    are removed, so any remaining ${...} would be unresolvable.
+        assertFalse(
+                content.contains("${"),
+                "Consumer POM must not contain unresolved property references.\nActual:\n" + content);
+
+        // 4. Specifically: junit version must be the resolved literal "4.13.2"
+        assertTrue(
+                content.contains("<version>4.13.2</version>"),
+                "Consumer POM must contain resolved junit version '4.13.2'.\nActual:\n" + content);
+    }
+
+    /**
+     * Verify that the BOM consumer POM with flatten=true also resolves
+     * property-referenced versions (this path uses getEffectiveModel()
+     * and should already work).
+     */
+    @Test
+    void testBomConsumerPomWithFlattenResolvesParentProperties() throws Exception {
+        Path basedir = extractResources("/bom-consumer-pom-property-resolution");
+
+        Verifier verifier = newVerifier(basedir);
+        verifier.deleteArtifacts("org.apache.maven.its.bom-property");
+        verifier.addCliArguments("install", "-Dmaven.consumer.pom.flatten=true");
+        verifier.execute();
+        verifier.verifyErrorFreeLog();
+
+        Path consumerPomPath =
+                verifier.getArtifactPath("org.apache.maven.its.bom-property", "bom", "1.0.0-SNAPSHOT", "pom");
+
+        assertTrue(Files.exists(consumerPomPath), "Consumer POM not found at " + consumerPomPath);
+
+        List<String> lines = Files.readAllLines(consumerPomPath);
+        String content = String.join("\n", lines);
+
+        // Packaging must be "pom"
+        assertTrue(content.contains("<packaging>pom</packaging>"), "Consumer POM packaging should be 'pom'");
+
+        // No unresolved property references
+        assertFalse(
+                content.contains("${"),
+                "Consumer POM must not contain unresolved property references.\nActual:\n" + content);
+
+        // junit version must be the resolved literal
+        assertTrue(
+                content.contains("<version>4.13.2</version>"),
+                "Consumer POM must contain resolved junit version '4.13.2'.\nActual:\n" + content);
+    }
+
+    /**
+     * Same test as {@link #testBomConsumerPomResolvesParentProperties} but verifies
+     * the consumer POM that gets written to the project-local repo (not user-home),
+     * using the "-consumer" classifier POM.
+     */
+    @Test
+    void testConsumerPomInProjectLocalRepo() throws Exception {
+        Path basedir = extractResources("/bom-consumer-pom-property-resolution");
+
+        Verifier verifier = newVerifier(basedir);
+        verifier.deleteArtifacts("org.apache.maven.its.bom-property");
+        verifier.addCliArguments("install");
+        verifier.execute();
+        verifier.verifyErrorFreeLog();
+
+        // Check the consumer POM in the project-local-repo
+        Path consumerPom = basedir.resolve(Path.of(
+                "target",
+                "project-local-repo",
+                "org.apache.maven.its.bom-property",
+                "bom",
+                "1.0.0-SNAPSHOT",
+                "bom-1.0.0-SNAPSHOT-consumer.pom"));
+
+        assertTrue(Files.exists(consumerPom), "Consumer POM not found at " + consumerPom);
+
+        String content = Files.readString(consumerPom);
+
+        // Must not contain raw property references
+        assertFalse(
+                content.contains("${"),
+                "Consumer POM must not contain unresolved property references.\nActual:\n" + content);
+
+        // Must contain the resolved version
+        assertTrue(
+                content.contains("<version>4.13.2</version>"),
+                "Consumer POM must contain resolved junit version '4.13.2'.\nActual:\n" + content);
+
+        // Must have packaging=pom
+        assertEquals(-1, content.indexOf("<packaging>bom</packaging>"), "Consumer POM must not have bom packaging");
+        assertTrue(content.contains("<packaging>pom</packaging>"), "Consumer POM must have pom packaging");
+    }
+}
diff --git a/its/core-it-suite/src/test/resources/bom-consumer-pom-property-resolution/bom/pom.xml b/its/core-it-suite/src/test/resources/bom-consumer-pom-property-resolution/bom/pom.xml
new file mode 100644
index 0000000000..e1fba27622
--- /dev/null
+++ b/its/core-it-suite/src/test/resources/bom-consumer-pom-property-resolution/bom/pom.xml
@@ -0,0 +1,61 @@
+<?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>
+
+  <parent>
+    <groupId>org.apache.maven.its.bom-property</groupId>
+    <artifactId>parent</artifactId>
+    <version>1.0.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>bom</artifactId>
+  <packaging>bom</packaging>
+
+  <name>BOM with parent-defined property versions</name>
+
+  <!--
+    Reproduces the issue reported by Karl Heinz Marbaise during the
+    Maven 4.0.0-rc-6 vote: property-referenced versions in
+    dependencyManagement are NOT resolved in the consumer POM,
+    even though the consumer POM strips the parent and properties
+    that would be needed to resolve them.
+
+    junit.version is defined in the parent POM, not here.
+  -->
+
+  <dependencyManagement>
+    <dependencies>
+      <!-- Internal module: version omitted, inferred from reactor -->
+      <dependency>
+        <groupId>org.apache.maven.its.bom-property</groupId>
+        <artifactId>mod-1</artifactId>
+      </dependency>
+      <!-- External dependency: version from parent property -->
+      <dependency>
+        <groupId>junit</groupId>
+        <artifactId>junit</artifactId>
+        <version>${junit.version}</version>
+      </dependency>
+    </dependencies>
+  </dependencyManagement>
+</project>
diff --git a/its/core-it-suite/src/test/resources/bom-consumer-pom-property-resolution/mod-1/pom.xml b/its/core-it-suite/src/test/resources/bom-consumer-pom-property-resolution/mod-1/pom.xml
new file mode 100644
index 0000000000..a2de3e622a
--- /dev/null
+++ b/its/core-it-suite/src/test/resources/bom-consumer-pom-property-resolution/mod-1/pom.xml
@@ -0,0 +1,35 @@
+<?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>
+
+  <parent>
+    <groupId>org.apache.maven.its.bom-property</groupId>
+    <artifactId>parent</artifactId>
+    <version>1.0.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>mod-1</artifactId>
+  <packaging>jar</packaging>
+
+  <name>Module 1</name>
+</project>
diff --git a/its/core-it-suite/src/test/resources/bom-consumer-pom-property-resolution/pom.xml b/its/core-it-suite/src/test/resources/bom-consumer-pom-property-resolution/pom.xml
new file mode 100644
index 0000000000..b2f2df3202
--- /dev/null
+++ b/its/core-it-suite/src/test/resources/bom-consumer-pom-property-resolution/pom.xml
@@ -0,0 +1,41 @@
+<?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.bom-property</groupId>
+  <artifactId>parent</artifactId>
+  <version>1.0.0-SNAPSHOT</version>
+  <packaging>pom</packaging>
+
+  <name>BOM Consumer POM Property Resolution Test</name>
+
+  <!-- Property defined in parent, used by BOM child -->
+  <properties>
+    <junit.version>4.13.2</junit.version>
+  </properties>
+
+  <modules>
+    <module>mod-1</module>
+    <module>bom</module>
+  </modules>
+</project>