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

gnodet pushed a commit to branch gh-11772-consumer-pom-validation
in repository https://gitbox.apache.org/repos/asf/maven.git

commit d39081265ba50a0dde1c2a9bfa5d374db2a97823
Author: Guillaume Nodet <[email protected]>
AuthorDate: Tue Mar 10 16:33:44 2026 +0100

    [GH-11772] Add managed dep removal flag and consumer POM validation
    
    - Add `maven.consumer.pom.removeUnusedManagedDependencies` property
      (default: true) to control whether unused managed dependencies are
      removed during consumer POM flattening. When using BOMs like Spring
      Boot or Quarkus, removal keeps POMs lean. Setting to false preserves
      all managed deps for cases where downstream consumers need them.
    
    - Replace the mixin-specific error with a general validation: when
      consumer POM flattening is disabled and the consumer POM cannot be
      downgraded to model version 4.0.0, reject the build with actionable
      guidance (enable flattening, preserve model version, or remove
      incompatible features).
    
    - Add the same validation for POM-packaged projects when flattening is
      enabled, since parent POMs keep their parent reference regardless.
    
    Fixes #11772
    
    Co-Authored-By: Claude Opus 4.6 <[email protected]>
---
 .../main/java/org/apache/maven/api/Constants.java  | 15 +++++
 .../org/apache/maven/api/feature/Features.java     |  7 +++
 .../impl/DefaultConsumerPomBuilder.java            | 66 ++++++++++++++--------
 .../it/MavenITgh11456MixinsConsumerPomTest.java    |  2 +-
 4 files changed, 65 insertions(+), 25 deletions(-)

diff --git 
a/api/maven-api-core/src/main/java/org/apache/maven/api/Constants.java 
b/api/maven-api-core/src/main/java/org/apache/maven/api/Constants.java
index 5ddc59983a..92117e518a 100644
--- a/api/maven-api-core/src/main/java/org/apache/maven/api/Constants.java
+++ b/api/maven-api-core/src/main/java/org/apache/maven/api/Constants.java
@@ -475,6 +475,21 @@ public final class Constants {
     @Config(type = "java.lang.Boolean", defaultValue = "false")
     public static final String MAVEN_CONSUMER_POM_FLATTEN = 
"maven.consumer.pom.flatten";
 
+    /**
+     * User property for controlling removal of unused managed dependencies 
during consumer POM flattening.
+     * When set to {@code true} (default), managed dependencies that do not 
appear in the resolved
+     * dependency tree are removed from the consumer POM to keep it lean. This 
is important when using
+     * BOMs like Spring Boot or Quarkus that contain hundreds of managed 
dependency entries.
+     * When set to {@code false}, all managed dependencies are preserved in 
the consumer POM,
+     * which may be needed in rare cases where downstream consumers override 
transitive dependency
+     * versions and rely on the original managed dependencies for alignment.
+     *
+     * @since 4.1.0
+     */
+    @Config(type = "java.lang.Boolean", defaultValue = "true")
+    public static final String 
MAVEN_CONSUMER_POM_REMOVE_UNUSED_MANAGED_DEPENDENCIES =
+            "maven.consumer.pom.removeUnusedManagedDependencies";
+
     /**
      * User property for controlling "maven personality". If activated Maven 
will behave
      * like the previous major version, Maven 3.
diff --git 
a/api/maven-api-core/src/main/java/org/apache/maven/api/feature/Features.java 
b/api/maven-api-core/src/main/java/org/apache/maven/api/feature/Features.java
index 52feae0cd8..12f044f607 100644
--- 
a/api/maven-api-core/src/main/java/org/apache/maven/api/feature/Features.java
+++ 
b/api/maven-api-core/src/main/java/org/apache/maven/api/feature/Features.java
@@ -54,6 +54,13 @@ public static boolean consumerPomFlatten(@Nullable 
Map<String, ?> userProperties
         return doGet(userProperties, Constants.MAVEN_CONSUMER_POM_FLATTEN, 
false);
     }
 
+    /**
+     * Check if unused managed dependency removal is enabled during consumer 
POM flattening.
+     */
+    public static boolean consumerPomRemoveUnusedManagedDependencies(@Nullable 
Map<String, ?> userProperties) {
+        return doGet(userProperties, 
Constants.MAVEN_CONSUMER_POM_REMOVE_UNUSED_MANAGED_DEPENDENCIES, true);
+    }
+
     /**
      * Check if build POM deployment is enabled.
      */
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 d631e8fd7e..f9daacec0f 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
@@ -122,20 +122,6 @@ public Model build(RepositorySystemSession session, 
MavenProject project, ModelS
         // Check if this is a BOM (original packaging is "bom")
         boolean isBom = BOM_PACKAGING.equals(originalPackaging);
 
-        // Check if mixins are present without flattening enabled
-        if (!model.getMixins().isEmpty() && !flattenEnabled && 
!model.isPreserveModelVersion()) {
-            throw new MavenException("The consumer POM for "
-                    + project.getId()
-                    + " cannot be created because the POM contains mixins. "
-                    + "Mixins are not supported in the default consumer POM 
format. "
-                    + "You have the following options to resolve this:" + 
System.lineSeparator()
-                    + "  1. Preserve the model version by setting 
'preserve.model.version=true' to generate a consumer POM with 
<modelVersion>4.2.0</modelVersion>, which supports mixins"
-                    + System.lineSeparator()
-                    + "  2. Enable flattening by setting the property 
'maven.consumer.pom.flatten=true' to remove mixins during transformation"
-                    + System.lineSeparator()
-                    + "  3. Remove the mixins from your POM");
-        }
-
         // Check if consumer POM flattening is disabled
         if (!flattenEnabled) {
             // When flattening is disabled, treat non-POM projects like parent 
POMs
@@ -144,7 +130,28 @@ public Model build(RepositorySystemSession session, 
MavenProject project, ModelS
             if (isBom) {
                 return buildBomWithoutFlatten(session, project, src);
             } else {
-                return buildPom(session, project, src);
+                Model result = buildPom(session, project, src);
+                // Validate: if flattening is disabled and the consumer POM 
cannot be downgraded
+                // to 4.0.0, the consumer POM will contain features that Maven 
3 / Gradle cannot
+                // understand. Since enabling flattening would inline the 
parent and produce a
+                // self-contained 4.0.0 POM, reject the build with actionable 
guidance.
+                if (!model.isPreserveModelVersion()
+                        && 
!ModelBuilder.MODEL_VERSION_4_0_0.equals(result.getModelVersion())) {
+                    throw new MavenException("The consumer POM for " + 
project.getId()
+                            + " cannot be downgraded to model version 4.0.0 
because it contains"
+                            + " features that require a newer model version."
+                            + " Since consumer POM flattening is disabled, the 
parent reference is"
+                            + " preserved, which requires consumers to resolve 
the parent POM." + System.lineSeparator()
+                            + "You have the following options to resolve 
this:" + System.lineSeparator()
+                            + "  1. Enable flattening by setting the property 
'maven.consumer.pom.flatten=true'"
+                            + " to inline parent content and produce a 
self-contained 4.0.0 consumer POM"
+                            + System.lineSeparator()
+                            + "  2. Preserve the model version by setting 
'preserve.model.version=true'"
+                            + " on the <project> element (Maven 4 consumers 
only)"
+                            + System.lineSeparator()
+                            + "  3. Remove the features that require a newer 
model version");
+                }
+                return result;
             }
         }
         // Default behavior: flatten the consumer POM
@@ -152,7 +159,22 @@ public Model build(RepositorySystemSession session, 
MavenProject project, ModelS
             if (isBom) {
                 return buildBom(session, project, src);
             } else {
-                return buildPom(session, project, src);
+                Model result = buildPom(session, project, src);
+                // Validate: POM-packaged projects (parents) keep the parent 
reference
+                // regardless of flatten setting. If the consumer POM cannot 
be downgraded
+                // to 4.0.0, reject the build.
+                if (!model.isPreserveModelVersion()
+                        && 
!ModelBuilder.MODEL_VERSION_4_0_0.equals(result.getModelVersion())) {
+                    throw new MavenException("The consumer POM for " + 
project.getId()
+                            + " cannot be downgraded to model version 4.0.0 
because it contains"
+                            + " features that require a newer model version." 
+ System.lineSeparator()
+                            + "You have the following options to resolve 
this:" + System.lineSeparator()
+                            + "  1. Preserve the model version by setting 
'preserve.model.version=true'"
+                            + " on the <project> element (Maven 4 consumers 
only)"
+                            + System.lineSeparator()
+                            + "  2. Remove the features that require a newer 
model version");
+                }
+                return result;
             }
         } else {
             return buildNonPom(session, project, src);
@@ -192,6 +214,8 @@ private Model buildEffectiveModel(RepositorySystemSession 
session, ModelSource s
         InternalSession iSession = InternalSession.from(session);
         ModelBuilderResult result = buildModel(session, src);
         Model model = result.getEffectiveModel();
+        boolean removeUnusedManagedDeps =
+                
Features.consumerPomRemoveUnusedManagedDependencies(session.getConfigProperties());
 
         if (model.getDependencyManagement() != null
                 && 
!model.getDependencyManagement().getDependencies().isEmpty()) {
@@ -210,20 +234,14 @@ private Model buildEffectiveModel(RepositorySystemSession 
session, ModelSource s
                             this::merge,
                             LinkedHashMap::new));
             Map<String, Dependency> managedDependencies = 
model.getDependencyManagement().getDependencies().stream()
-                    .filter(dependency ->
-                            nodes.containsKey(getDependencyKey(dependency)) && 
!"import".equals(dependency.getScope()))
+                    .filter(dependency -> 
!"import".equals(dependency.getScope())
+                            && (!removeUnusedManagedDeps || 
nodes.containsKey(getDependencyKey(dependency))))
                     .collect(Collectors.toMap(
                             DefaultConsumerPomBuilder::getDependencyKey,
                             Function.identity(),
                             this::merge,
                             LinkedHashMap::new));
 
-            // for each managed dep in the model:
-            // * if there is no corresponding node in the tree, discard the 
managed dep
-            // * if there's a direct dependency, apply the managed dependency 
to it and discard the managed dep
-            // * else keep the managed dep
-            managedDependencies.keySet().retainAll(nodes.keySet());
-
             directDependencies.replaceAll((key, dependency) -> {
                 var managedDependency = managedDependencies.get(key);
                 if (managedDependency != null) {
diff --git 
a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITgh11456MixinsConsumerPomTest.java
 
b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITgh11456MixinsConsumerPomTest.java
index 1d70146ced..d116e1de44 100644
--- 
a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITgh11456MixinsConsumerPomTest.java
+++ 
b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITgh11456MixinsConsumerPomTest.java
@@ -52,7 +52,7 @@ void testMixinsWithoutFlattening() throws Exception {
             // Expected to fail due to mixins without flattening
         }
 
-        verifier.verifyTextInLog("cannot be created because the POM contains 
mixins");
+        verifier.verifyTextInLog("cannot be downgraded to model version 
4.0.0");
         verifier.verifyTextInLog("maven.consumer.pom.flatten=true");
     }
 

Reply via email to