Skip to content

Commit d213b58

Browse files
authored
Disable consumer POM flattening by default and add an opt-in feature (#11347)
This PR introduces a new feature flag maven.consumer.pom.flatten that allows users to control whether consumer POMs are flattened by removing dependency management sections. This addresses dependency management inheritance scenarios and provides better control over consumer POM generation. The consumer POM are NOT flattened anymore by default. Fixes #11346
1 parent 8b7e5c1 commit d213b58

File tree

20 files changed

+465
-9
lines changed

20 files changed

+465
-9
lines changed

api/maven-api-core/src/main/java/org/apache/maven/api/Constants.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,18 @@ public final class Constants {
463463
@Config(type = "java.lang.Boolean", defaultValue = "true")
464464
public static final String MAVEN_CONSUMER_POM = "maven.consumer.pom";
465465

466+
/**
467+
* User property for controlling consumer POM flattening behavior.
468+
* When set to <code>true</code> (default), consumer POMs are flattened by removing
469+
* dependency management and keeping only direct dependencies with transitive scopes.
470+
* When set to <code>false</code>, consumer POMs preserve dependency management
471+
* like parent POMs, allowing dependency management to be inherited by consumers.
472+
*
473+
* @since 4.1.0
474+
*/
475+
@Config(type = "java.lang.Boolean", defaultValue = "false")
476+
public static final String MAVEN_CONSUMER_POM_FLATTEN = "maven.consumer.pom.flatten";
477+
466478
/**
467479
* User property for controlling "maven personality". If activated Maven will behave
468480
* like the previous major version, Maven 3.

api/maven-api-core/src/main/java/org/apache/maven/api/feature/Features.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,13 @@ public static boolean consumerPom(@Nullable Map<String, ?> userProperties) {
4747
return doGet(userProperties, Constants.MAVEN_CONSUMER_POM, !mavenMaven3Personality(userProperties));
4848
}
4949

50+
/**
51+
* Check if consumer POM flattening is enabled.
52+
*/
53+
public static boolean consumerPomFlatten(@Nullable Map<String, ?> userProperties) {
54+
return doGet(userProperties, Constants.MAVEN_CONSUMER_POM_FLATTEN, false);
55+
}
56+
5057
/**
5158
* Check if build POM deployment is enabled.
5259
*/

impl/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/DefaultConsumerPomBuilder.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import org.apache.maven.api.Node;
3434
import org.apache.maven.api.PathScope;
3535
import org.apache.maven.api.SessionData;
36+
import org.apache.maven.api.feature.Features;
3637
import org.apache.maven.api.model.Dependency;
3738
import org.apache.maven.api.model.DistributionManagement;
3839
import org.apache.maven.api.model.Model;
@@ -72,6 +73,15 @@ class DefaultConsumerPomBuilder implements PomBuilder {
7273
@Override
7374
public Model build(RepositorySystemSession session, MavenProject project, Path src) throws ModelBuilderException {
7475
Model model = project.getModel().getDelegate();
76+
boolean flattenEnabled = Features.consumerPomFlatten(session.getConfigProperties());
77+
78+
// Check if consumer POM flattening is disabled
79+
if (!flattenEnabled) {
80+
// When flattening is disabled, treat non-POM projects like parent POMs
81+
// Apply only basic transformations without flattening dependency management
82+
return buildPom(session, project, src);
83+
}
84+
// Default behavior: flatten the consumer POM
7585
String packaging = model.getPackaging();
7686
String originalPackaging = project.getOriginalModel().getPackaging();
7787
if (POM_PACKAGING.equals(packaging)) {
@@ -256,6 +266,7 @@ static Model transformNonPom(Model model, MavenProject project) {
256266
warnNotDowngraded(project);
257267
}
258268
model = model.withModelVersion(modelVersion);
269+
259270
return model;
260271
}
261272

its/core-it-suite/src/test/java/org/apache/maven/it/MavenITgh11084ReactorReaderPreferConsumerPomTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,15 @@ void partialReactorShouldResolveUsingConsumerPom() throws Exception {
3535

3636
// First build module a to populate project-local-repo with artifacts including consumer POM
3737
Verifier v1 = newVerifier(testDir.getAbsolutePath());
38-
v1.addCliArguments("clean", "package", "-X");
38+
v1.addCliArguments("clean", "package", "-X", "-Dmaven.consumer.pom.flatten=true");
3939
v1.setLogFileName("log-1.txt");
4040
v1.execute();
4141
v1.verifyErrorFreeLog();
4242

4343
// Now build only module b; ReactorReader should pick consumer POM from project-local-repo
4444
Verifier v2 = newVerifier(testDir.getAbsolutePath());
4545
v2.setLogFileName("log-2.txt");
46-
v2.addCliArguments("clean", "compile", "-f", "b", "-X");
46+
v2.addCliArguments("clean", "compile", "-f", "b", "-X", "-Dmaven.consumer.pom.flatten=true");
4747
v2.execute();
4848
v2.verifyErrorFreeLog();
4949
}

its/core-it-suite/src/test/java/org/apache/maven/it/MavenITgh11162ConsumerPomScopesTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ void testConsumerPomFiltersScopes() throws Exception {
4444

4545
Verifier verifier = newVerifier(basedir.toString());
4646
verifier.addCliArgument("install");
47+
verifier.addCliArgument("-Dmaven.consumer.pom.flatten=true");
4748
verifier.execute();
4849
verifier.verifyErrorFreeLog();
4950

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.maven.it;
20+
21+
import java.io.File;
22+
import java.util.List;
23+
24+
import org.apache.maven.api.Constants;
25+
import org.junit.jupiter.api.Test;
26+
27+
import static org.junit.jupiter.api.Assertions.assertFalse;
28+
import static org.junit.jupiter.api.Assertions.assertTrue;
29+
30+
/**
31+
* This is a test set for dependency management override scenarios when
32+
* consumer POM flattening is disabled (maven.consumer.pom.flatten=false).
33+
*
34+
* Scenario:
35+
* - A 1.0 depends on B 1.0 and manages C to 1.2
36+
* - B 1.0 has no dependencies
37+
* - B 2.0 depends on C 1.1
38+
* - D depends on A 1.0 and manages B to 2.0
39+
*
40+
* Question: Does D depend on C, and which version?
41+
*
42+
* Expected behavior when flattening is disabled: D should get C 1.2 (from A's dependency management),
43+
* not C 1.1 (from B 2.0's dependency), because A's dependency
44+
* management applies to D's transitive dependencies.
45+
*
46+
* @see <a href="https://github.com/apache/maven/issues/11346">gh-11346</a>
47+
*/
48+
public class MavenITgh11346DependencyManagementOverrideTest extends AbstractMavenIntegrationTestCase {
49+
50+
/**
51+
* Verify that when consumer POM flattening is disabled, dependency management
52+
* from intermediate dependencies applies to the consumer's transitive dependencies.
53+
* This test uses -Dmaven.consumer.pom.flatten=false to enable dependency management
54+
* inheritance from transitive dependencies.
55+
*
56+
* @throws Exception in case of failure
57+
*/
58+
@Test
59+
public void testDependencyManagementOverride() throws Exception {
60+
File testDir = extractResources("/gh-11346-dependency-management-override");
61+
62+
Verifier verifier = newVerifier(testDir.getAbsolutePath());
63+
verifier.deleteArtifacts("org.apache.maven.its.mng.depman");
64+
// Test with dependency manager transitivity disabled instead of consumer POM flattening
65+
verifier.addCliArgument("-D" + Constants.MAVEN_CONSUMER_POM_FLATTEN + "=false");
66+
verifier.addCliArgument("verify");
67+
verifier.execute();
68+
verifier.verifyErrorFreeLog();
69+
70+
// Check module D's classpath
71+
List<String> dClasspath = verifier.loadLines("module-d/target/classpath.txt");
72+
73+
// D should have A 1.0
74+
assertTrue(dClasspath.contains("module-a-1.0.jar"), "D should depend on A 1.0: " + dClasspath);
75+
76+
// D should have B 2.0 (managed by D)
77+
assertTrue(dClasspath.contains("module-b-2.0.jar"), "D should depend on B 2.0 (managed by D): " + dClasspath);
78+
assertFalse(dClasspath.contains("module-b-1.0.jar"), "D should not depend on B 1.0: " + dClasspath);
79+
80+
// D should have C 1.2 (from A's dependency management)
81+
// A's dependency management of C to 1.2 should apply to D
82+
assertTrue(
83+
dClasspath.contains("module-c-1.2.jar"),
84+
"D should depend on C 1.2 (A's dependency management should apply): " + dClasspath);
85+
assertFalse(
86+
dClasspath.contains("module-c-1.1.jar"),
87+
"D should not depend on C 1.1 (should be managed to 1.2): " + dClasspath);
88+
}
89+
90+
@Test
91+
public void testDependencyManagementOverrideNoTransitive() throws Exception {
92+
File testDir = extractResources("/gh-11346-dependency-management-override");
93+
94+
Verifier verifier = newVerifier(testDir.getAbsolutePath());
95+
verifier.deleteArtifacts("org.apache.maven.its.mng.depman");
96+
// Test with dependency manager transitivity disabled instead of consumer POM flattening
97+
verifier.addCliArgument("-D" + Constants.MAVEN_CONSUMER_POM_FLATTEN + "=false");
98+
verifier.addCliArgument("-D" + Constants.MAVEN_RESOLVER_DEPENDENCY_MANAGER_TRANSITIVITY + "=false");
99+
verifier.addCliArgument("verify");
100+
verifier.execute();
101+
verifier.verifyErrorFreeLog();
102+
103+
// Check module D's classpath
104+
List<String> dClasspath = verifier.loadLines("module-d/target/classpath.txt");
105+
106+
// D should have A 1.0
107+
assertTrue(dClasspath.contains("module-a-1.0.jar"), "D should depend on A 1.0: " + dClasspath);
108+
109+
// D should have B 2.0 (managed by D)
110+
assertTrue(dClasspath.contains("module-b-2.0.jar"), "D should depend on B 2.0 (managed by D): " + dClasspath);
111+
assertFalse(dClasspath.contains("module-b-1.0.jar"), "D should not depend on B 1.0: " + dClasspath);
112+
113+
// D should have C 1.1 as the resolver is not transitive
114+
assertFalse(
115+
dClasspath.contains("module-c-1.2.jar"),
116+
"D should depend on C 1.2 (A's dependency management should apply): " + dClasspath);
117+
assertTrue(
118+
dClasspath.contains("module-c-1.1.jar"),
119+
"D should not depend on C 1.1 (should be managed to 1.2): " + dClasspath);
120+
}
121+
}

its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng5102MixinsTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public void testWithPath() throws Exception {
4848
verifier.setAutoclean(false);
4949
verifier.deleteDirectory("target");
5050
verifier.deleteArtifacts("org.apache.maven.its.mng5102");
51-
verifier.addCliArgument("install");
51+
verifier.addCliArguments("install", "-Dmaven.consumer.pom.flatten=true");
5252
verifier.execute();
5353
verifier.verifyErrorFreeLog();
5454

its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng6656BuildConsumer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ public void testPublishedPoms() throws Exception {
6464
verifier.setAutoclean(false);
6565
verifier.addCliArgument("-Dchangelist=MNG6656");
6666

67-
verifier.addCliArgument("install");
67+
verifier.addCliArguments("install", "-Dmaven.consumer.pom.flatten=true");
6868
verifier.execute();
6969
verifier.verifyErrorFreeLog();
7070

its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng6957BuildConsumer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ public void testPublishedPoms() throws Exception {
6262

6363
Verifier verifier = newVerifier(testDir.getAbsolutePath());
6464
verifier.setAutoclean(false);
65-
verifier.addCliArgument("-Dchangelist=MNG6957");
65+
verifier.addCliArguments("-Dchangelist=MNG6957", "-Dmaven.consumer.pom.flatten=true");
6666

6767
verifier.addCliArgument("install");
6868
verifier.execute();

its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng8414ConsumerPomWithNewFeaturesTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ void testNotPreserving() throws Exception {
4646
extractResources("/mng-8414-consumer-pom-with-new-features").toPath();
4747

4848
Verifier verifier = newVerifier(basedir.toString(), null);
49-
verifier.addCliArguments("package");
49+
verifier.addCliArguments("package", "-Dmaven.consumer.pom.flatten=true");
5050
verifier.execute();
5151
verifier.verifyErrorFreeLog();
5252

@@ -78,7 +78,7 @@ void testPreserving() throws Exception {
7878

7979
Verifier verifier = newVerifier(basedir.toString(), null);
8080
verifier.setLogFileName("log-preserving.txt");
81-
verifier.addCliArguments("-f", "pom-preserving.xml", "package");
81+
verifier.addCliArguments("-f", "pom-preserving.xml", "package", "-Dmaven.consumer.pom.flatten=true");
8282
verifier.execute();
8383
verifier.verifyErrorFreeLog();
8484

0 commit comments

Comments
 (0)