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 60a49fcc2e96..f577a608f918 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 @@ -1131,7 +1131,19 @@ private Model readParentLocally( } try { - ModelBuilderSessionState derived = derive(candidateSource); + // When processing BUILD_CONSUMER requests, parent models must be read + // with CONSUMER_PARENT type to enable profile activation. Without this, + // property-activated profiles in parent POMs are skipped (since + // BUILD_CONSUMER disables profile activation), leaving properties + // like BOM import versions unresolved. + ModelBuilderRequest parentRequest = + request.getRequestType() == ModelBuilderRequest.RequestType.BUILD_CONSUMER + ? ModelBuilderRequest.builder(request) + .requestType(ModelBuilderRequest.RequestType.CONSUMER_PARENT) + .source(candidateSource) + .build() + : ModelBuilderRequest.build(request, candidateSource); + ModelBuilderSessionState derived = derive(parentRequest); Model candidateModel = derived.readAsParentModel(profileActivationContext, parentChain); // Add profiles from parent, preserving model ID tracking for (Map.Entry> entry : 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 5f6146fc2c26..11d1becee03e 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 @@ -38,6 +38,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; @@ -254,6 +255,67 @@ public void testMissingDependencyGroupIdInference() throws Exception { } } + /** + * Verifies that BUILD_CONSUMER resolves properties defined in parent POM profiles + * when the parent is found via reactor model resolution (mappedSources). + * + * This reproduces the bug where readParentLocally() finds a parent via + * resolveReactorModel() during BUILD_CONSUMER processing, but the derived + * session preserves BUILD_CONSUMER type instead of using CONSUMER_PARENT. + * Since isBuildRequestWithActivation() returns false for BUILD_CONSUMER, + * property-activated profiles in parent POMs are skipped, leaving + * properties (like BOM import versions) unresolved. + */ + @Test + public void testBuildConsumerResolvesParentProfileProperties() { + Path parentPom = getPom("consumer-profile-property-parent"); + Path childPom = getPom("consumer-profile-property-child"); + + ModelBuilder.ModelBuilderSession mbs = builder.newSession(); + + // Build parent with BUILD_PROJECT to populate the reactor (mappedSources). + // This causes the parent to be discoverable via resolveReactorModel(). + mbs.build(ModelBuilderRequest.builder() + .session(session) + .requestType(ModelBuilderRequest.RequestType.BUILD_PROJECT) + .source(Sources.buildSource(parentPom)) + .build()); + + // Build child with BUILD_CONSUMER on the same session. + // The parent will be found via resolveReactorModel() in readParentLocally(). + // Without the fix, the derived session preserves BUILD_CONSUMER type, + // which disables profile activation, leaving ${managed.version} unresolved. + ModelBuilderResult consumerResult = assertDoesNotThrow( + () -> mbs.build(ModelBuilderRequest.builder() + .session(session) + .requestType(ModelBuilderRequest.RequestType.BUILD_CONSUMER) + .source(Sources.buildSource(childPom)) + .build()), + "BUILD_CONSUMER should not fail when parent defines properties in profiles"); + + assertNotNull(consumerResult); + Model effectiveModel = consumerResult.getEffectiveModel(); + assertNotNull(effectiveModel); + + // The property from the parent's profile should be resolved + assertEquals( + "1.2.3", + effectiveModel.getProperties().get("managed.version"), + "Property from parent's profile should be resolved in BUILD_CONSUMER effective model"); + + // The managed dependency version should be interpolated + assertNotNull(effectiveModel.getDependencyManagement()); + Dependency managedDep = effectiveModel.getDependencyManagement().getDependencies().stream() + .filter(d -> "managed-lib".equals(d.getArtifactId())) + .findFirst() + .orElse(null); + assertNotNull(managedDep, "Managed dependency from parent should be inherited"); + assertEquals( + "1.2.3", + managedDep.getVersion(), + "Managed dependency version should be interpolated, not ${managed.version}"); + } + private Path getPom(String name) { return Paths.get("src/test/resources/poms/factory/" + name + ".xml").toAbsolutePath(); } diff --git a/impl/maven-impl/src/test/resources/poms/factory/consumer-profile-property-child.xml b/impl/maven-impl/src/test/resources/poms/factory/consumer-profile-property-child.xml new file mode 100644 index 000000000000..a85499d5fe16 --- /dev/null +++ b/impl/maven-impl/src/test/resources/poms/factory/consumer-profile-property-child.xml @@ -0,0 +1,27 @@ + + + + + org.apache.maven.tests + consumer-profile-property-parent + consumer-profile-property-parent.xml + + consumer-profile-property-child + 1.0-SNAPSHOT + jar + diff --git a/impl/maven-impl/src/test/resources/poms/factory/consumer-profile-property-parent.xml b/impl/maven-impl/src/test/resources/poms/factory/consumer-profile-property-parent.xml new file mode 100644 index 000000000000..b7919ef2546e --- /dev/null +++ b/impl/maven-impl/src/test/resources/poms/factory/consumer-profile-property-parent.xml @@ -0,0 +1,52 @@ + + + + org.apache.maven.tests + consumer-profile-property-parent + 1.0-SNAPSHOT + pom + + + + + default-versions + + + !skipDefaultVersions + + + + 1.2.3 + + + + + + + + org.example + managed-lib + ${managed.version} + + + +