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"); }
