This is an automated email from the ASF dual-hosted git repository.
gnodet pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/maven.git
The following commit(s) were added to refs/heads/master by this push:
new 405e2e10fd Fix profile source tracking in multi-module projects (fixes
#11409) (#11440)
405e2e10fd is described below
commit 405e2e10fd663cf866eca1a6360588bbe440d95e
Author: Guillaume Nodet <[email protected]>
AuthorDate: Tue Nov 18 18:20:31 2025 +0100
Fix profile source tracking in multi-module projects (fixes #11409) (#11440)
The root cause was that ModelBuilderResult.getActivePomProfiles()
returned all active profiles as a flat list without tracking which
model each profile came from.
This commit:
- Adds getActivePomProfiles(String modelId) and
getActivePomProfilesByModel() methods to ModelBuilderResult API
to track profiles per model like Maven 3 did
- Updates DefaultModelBuilder to track model IDs when adding profiles,
using ModelProblemUtils.toId() to get groupId:artifactId:version
format (without packaging) to match Maven 3 behavior
- Updates DefaultProjectBuilder to use the new per-model profile
tracking API to correctly set injected profile IDs
- Adds integration test MavenITgh11409ProfileSourceTest to verify
the fix and prevent regression
Profile sources now correctly show groupId:artifactId:version format,
matching Maven 3 behavior.
Fixes #11409
---
.../maven/api/services/ModelBuilderResult.java | 22 +++++++++
.../maven/project/DefaultProjectBuilder.java | 17 ++++++-
.../project/DefaultMavenProjectBuilderTest.java | 34 ++++++-------
.../maven/project/DefaultProjectBuilderTest.java | 10 ++++
.../maven/impl/model/DefaultModelBuilder.java | 41 +++++++++++-----
.../impl/model/DefaultModelBuilderResult.java | 30 ++++++++++--
.../maven/it/MavenITgh11409ProfileSourceTest.java | 56 ++++++++++++++++++++++
...enITmng8477MultithreadedFileActivationTest.java | 2 +-
.../src/test/resources/gh-11409/pom.xml | 49 +++++++++++++++++++
.../src/test/resources/gh-11409/subproject/pom.xml | 48 +++++++++++++++++++
10 files changed, 271 insertions(+), 38 deletions(-)
diff --git
a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelBuilderResult.java
b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelBuilderResult.java
index 4b15818cf0..854f8dcc01 100644
---
a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelBuilderResult.java
+++
b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelBuilderResult.java
@@ -19,6 +19,7 @@
package org.apache.maven.api.services;
import java.util.List;
+import java.util.Map;
import org.apache.maven.api.annotations.Experimental;
import org.apache.maven.api.annotations.Nonnull;
@@ -81,6 +82,27 @@ public interface ModelBuilderResult extends
Result<ModelBuilderRequest> {
@Nonnull
List<Profile> getActivePomProfiles();
+ /**
+ * Gets the profiles that were active during model building for a specific
model in the hierarchy.
+ * This allows tracking which profiles came from which model (parent vs
child).
+ *
+ * @param modelId The identifier of the model (groupId:artifactId:version)
or empty string for the super POM.
+ * @return The active profiles for the specified model or an empty list if
the model has no active profiles.
+ * @since 4.0.0
+ */
+ @Nonnull
+ List<Profile> getActivePomProfiles(String modelId);
+
+ /**
+ * Gets a map of all active POM profiles organized by model ID.
+ * The map keys are model IDs (groupId:artifactId:version) and values are
lists of active profiles for each model.
+ *
+ * @return A map of model IDs to their active profiles, never {@code null}.
+ * @since 4.0.0
+ */
+ @Nonnull
+ Map<String, List<Profile>> getActivePomProfilesByModel();
+
/**
* Gets the external profiles that were active during model building.
External profiles are those that were
* contributed by {@link ModelBuilderRequest#getProfiles()}.
diff --git
a/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java
b/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java
index 5b2576eb9d..a1a331ab69 100644
---
a/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java
+++
b/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java
@@ -714,8 +714,21 @@ private void initProject(MavenProject project,
ModelBuilderResult result) {
.toList());
project.setInjectedProfileIds("external",
getProfileIds(result.getActiveExternalProfiles()));
- project.setInjectedProfileIds(
- result.getEffectiveModel().getId(),
getProfileIds(result.getActivePomProfiles()));
+
+ // Track profile sources correctly by using the per-model profile
tracking
+ Map<String, List<org.apache.maven.api.model.Profile>>
profilesByModel =
+ result.getActivePomProfilesByModel();
+
+ if (profilesByModel.isEmpty()) {
+ // Fallback to old behavior if map is empty
+ // This happens when no profiles are active or there's an
issue with profile tracking
+ project.setInjectedProfileIds(
+ result.getEffectiveModel().getId(),
getProfileIds(result.getActivePomProfiles()));
+ } else {
+ for (Map.Entry<String,
List<org.apache.maven.api.model.Profile>> entry : profilesByModel.entrySet()) {
+ project.setInjectedProfileIds(entry.getKey(),
getProfileIds(entry.getValue()));
+ }
+ }
//
// All the parts that were taken out of MavenProject for Maven
4.0.0
diff --git
a/impl/maven-core/src/test/java/org/apache/maven/project/DefaultMavenProjectBuilderTest.java
b/impl/maven-core/src/test/java/org/apache/maven/project/DefaultMavenProjectBuilderTest.java
index 3f6261f27f..0dee2323a6 100644
---
a/impl/maven-core/src/test/java/org/apache/maven/project/DefaultMavenProjectBuilderTest.java
+++
b/impl/maven-core/src/test/java/org/apache/maven/project/DefaultMavenProjectBuilderTest.java
@@ -358,12 +358,12 @@ void testActivatedProfileBySource() throws Exception {
MavenProject project = projectBuilder.build(testPom,
request).getProject();
-
assertTrue(project.getInjectedProfileIds().keySet().containsAll(List.of("external",
project.getId())));
+ String id = project.getGroupId() + ":" + project.getArtifactId() + ":"
+ project.getVersion();
+
assertTrue(project.getInjectedProfileIds().keySet().containsAll(List.of("external",
id)));
assertTrue(project.getInjectedProfileIds().get("external").isEmpty());
-
assertTrue(project.getInjectedProfileIds().get(project.getId()).stream().anyMatch("profile1"::equals));
-
assertTrue(project.getInjectedProfileIds().get(project.getId()).stream().noneMatch("profile2"::equals));
- assertTrue(
-
project.getInjectedProfileIds().get(project.getId()).stream().noneMatch("active-by-default"::equals));
+
assertTrue(project.getInjectedProfileIds().get(id).stream().anyMatch("profile1"::equals));
+
assertTrue(project.getInjectedProfileIds().get(id).stream().noneMatch("profile2"::equals));
+
assertTrue(project.getInjectedProfileIds().get(id).stream().noneMatch("active-by-default"::equals));
}
/**
@@ -387,14 +387,12 @@ void testActivatedDefaultProfileBySource(String fsName,
Configuration fsConfig,
MavenProject project = projectBuilder.build(source,
request).getProject();
-
assertTrue(project.getInjectedProfileIds().keySet().containsAll(List.of("external",
project.getId())));
+ String id = project.getGroupId() + ":" + project.getArtifactId() +
":" + project.getVersion();
+
assertTrue(project.getInjectedProfileIds().keySet().containsAll(List.of("external",
id)));
assertTrue(project.getInjectedProfileIds().get("external").isEmpty());
-
assertTrue(project.getInjectedProfileIds().get(project.getId()).stream()
- .noneMatch("profile1"::equals));
-
assertTrue(project.getInjectedProfileIds().get(project.getId()).stream()
- .noneMatch("profile2"::equals));
-
assertTrue(project.getInjectedProfileIds().get(project.getId()).stream()
- .anyMatch("active-by-default"::equals));
+
assertTrue(project.getInjectedProfileIds().get(id).stream().noneMatch("profile1"::equals));
+
assertTrue(project.getInjectedProfileIds().get(id).stream().noneMatch("profile2"::equals));
+
assertTrue(project.getInjectedProfileIds().get(id).stream().anyMatch("active-by-default"::equals));
InternalMavenSession session =
Mockito.mock(InternalMavenSession.class);
List<org.apache.maven.api.model.Profile> activeProfiles =
@@ -470,14 +468,12 @@ void testActivatedExternalProfileBySource(String fsName,
Configuration fsConfig,
MavenProject project = projectBuilder.build(source,
request).getProject();
-
assertTrue(project.getInjectedProfileIds().keySet().containsAll(List.of("external",
project.getId())));
+ String id = project.getGroupId() + ":" + project.getArtifactId() +
":" + project.getVersion();
+
assertTrue(project.getInjectedProfileIds().keySet().containsAll(List.of("external",
id)));
assertTrue(project.getInjectedProfileIds().get("external").stream().anyMatch("external-profile"::equals));
-
assertTrue(project.getInjectedProfileIds().get(project.getId()).stream()
- .noneMatch("profile1"::equals));
-
assertTrue(project.getInjectedProfileIds().get(project.getId()).stream()
- .noneMatch("profile2"::equals));
-
assertTrue(project.getInjectedProfileIds().get(project.getId()).stream()
- .anyMatch("active-by-default"::equals));
+
assertTrue(project.getInjectedProfileIds().get(id).stream().noneMatch("profile1"::equals));
+
assertTrue(project.getInjectedProfileIds().get(id).stream().noneMatch("profile2"::equals));
+
assertTrue(project.getInjectedProfileIds().get(id).stream().anyMatch("active-by-default"::equals));
InternalMavenSession session =
Mockito.mock(InternalMavenSession.class);
List<org.apache.maven.api.model.Profile> activeProfiles =
diff --git
a/impl/maven-core/src/test/java/org/apache/maven/project/DefaultProjectBuilderTest.java
b/impl/maven-core/src/test/java/org/apache/maven/project/DefaultProjectBuilderTest.java
index 7532aebbc1..80adc6911c 100644
---
a/impl/maven-core/src/test/java/org/apache/maven/project/DefaultProjectBuilderTest.java
+++
b/impl/maven-core/src/test/java/org/apache/maven/project/DefaultProjectBuilderTest.java
@@ -196,6 +196,16 @@ public List<Profile> getActivePomProfiles() {
return List.of();
}
+ @Override
+ public List<Profile> getActivePomProfiles(String modelId) {
+ return List.of();
+ }
+
+ @Override
+ public java.util.Map<String, List<Profile>>
getActivePomProfilesByModel() {
+ return java.util.Map.of();
+ }
+
@Override
public List<Profile> getActiveExternalProfiles() {
return List.of();
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 038732810f..60a49fcc2e 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
@@ -1133,7 +1133,11 @@ private Model readParentLocally(
try {
ModelBuilderSessionState derived = derive(candidateSource);
Model candidateModel =
derived.readAsParentModel(profileActivationContext, parentChain);
- addActivePomProfiles(derived.result.getActivePomProfiles());
+ // Add profiles from parent, preserving model ID tracking
+ for (Map.Entry<String, List<Profile>> entry :
+
derived.result.getActivePomProfilesByModel().entrySet()) {
+ addActivePomProfiles(entry.getKey(), entry.getValue());
+ }
String groupId = getGroupId(candidateModel);
String artifactId = candidateModel.getArtifactId();
@@ -1292,7 +1296,13 @@ Model resolveAndReadParentExternally(
.source(modelSource)
.build();
- Model parentModel =
derive(lenientRequest).readAsParentModel(profileActivationContext, parentChain);
+ ModelBuilderSessionState derived = derive(lenientRequest);
+ Model parentModel =
derived.readAsParentModel(profileActivationContext, parentChain);
+ // Add profiles from parent, preserving model ID tracking
+ for (Map.Entry<String, List<Profile>> entry :
+ derived.result.getActivePomProfilesByModel().entrySet()) {
+ addActivePomProfiles(entry.getKey(), entry.getValue());
+ }
if (!parent.getVersion().equals(version)) {
String rawChildModelVersion = childModel.getVersion();
@@ -1416,12 +1426,19 @@ private Model readEffectiveModel() throws
ModelBuilderException {
// profile activation
profileActivationContext.setModel(model);
- // profile injection
+ // Activate profiles from the input model (before inheritance) to
get only local profiles
+ // Parent profiles are already added when the parent model is read
+ List<Profile> localActivePomProfiles =
+ getActiveProfiles(inputModel.getProfiles(),
profileActivationContext);
+
+ // profile injection - inject all profiles (local + inherited)
into the model
List<Profile> activePomProfiles =
getActiveProfiles(model.getProfiles(), profileActivationContext);
model = profileInjector.injectProfiles(model, activePomProfiles,
request, this);
model = profileInjector.injectProfiles(model,
activeExternalProfiles, request, this);
- addActivePomProfiles(activePomProfiles);
+ // Track only the local profiles for this model
+ // Use ModelProblemUtils.toId() to get groupId:artifactId:version
format (without packaging)
+ addActivePomProfiles(ModelProblemUtils.toId(model),
localActivePomProfiles);
// model interpolation
Model resultModel = model;
@@ -1449,12 +1466,10 @@ private Model readEffectiveModel() throws
ModelBuilderException {
return resultModel;
}
- private void addActivePomProfiles(List<Profile> activePomProfiles) {
- if (activePomProfiles != null) {
- if (result.getActivePomProfiles() == null) {
- result.setActivePomProfiles(new ArrayList<>());
- }
- result.getActivePomProfiles().addAll(activePomProfiles);
+ private void addActivePomProfiles(String modelId, List<Profile>
activePomProfiles) {
+ if (activePomProfiles != null && !activePomProfiles.isEmpty()) {
+ // Track profiles by model ID
+ result.setActivePomProfiles(modelId, activePomProfiles);
}
}
@@ -1832,7 +1847,7 @@ Model readAsParentModel(DefaultProfileActivationContext
profileActivationContext
replayRecordIntoContext(e.getKey(),
profileActivationContext);
}
// Add the activated profiles from cache to the result
- addActivePomProfiles(cached.activatedProfiles());
+ addActivePomProfiles(cached.model().getId(),
cached.activatedProfiles());
return cached.model();
}
}
@@ -1848,7 +1863,9 @@ Model readAsParentModel(DefaultProfileActivationContext
profileActivationContext
replayRecordIntoContext(record, profileActivationContext);
parentsPerContext.put(record, modelWithProfiles);
- addActivePomProfiles(modelWithProfiles.activatedProfiles());
+ // Use ModelProblemUtils.toId() to get groupId:artifactId:version
format (without packaging)
+ addActivePomProfiles(
+ ModelProblemUtils.toId(modelWithProfiles.model()),
modelWithProfiles.activatedProfiles());
return modelWithProfiles.model();
}
diff --git
a/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelBuilderResult.java
b/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelBuilderResult.java
index d3b115e598..6180510b4f 100644
---
a/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelBuilderResult.java
+++
b/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelBuilderResult.java
@@ -19,7 +19,10 @@
package org.apache.maven.impl.model;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedHashMap;
import java.util.List;
+import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -41,7 +44,7 @@ class DefaultModelBuilderResult implements ModelBuilderResult
{
private Model rawModel;
private Model parentModel;
private Model effectiveModel;
- private List<Profile> activePomProfiles;
+ private Map<String, List<Profile>> activePomProfilesByModel = new
LinkedHashMap<>();
private List<Profile> activeExternalProfiles;
private final ProblemCollector<ModelProblem> problemCollector;
private final List<DefaultModelBuilderResult> children = new ArrayList<>();
@@ -103,11 +106,30 @@ public void setEffectiveModel(Model model) {
@Override
public List<Profile> getActivePomProfiles() {
- return activePomProfiles;
+ // Return all profiles from all models combined
+ if (activePomProfilesByModel.isEmpty()) {
+ return Collections.emptyList();
+ }
+ return
activePomProfilesByModel.values().stream().flatMap(List::stream).collect(Collectors.toList());
}
- public void setActivePomProfiles(List<Profile> activeProfiles) {
- this.activePomProfiles = activeProfiles;
+ @Override
+ public List<Profile> getActivePomProfiles(String modelId) {
+ List<Profile> profiles = activePomProfilesByModel.get(modelId);
+ return profiles != null ? profiles : Collections.emptyList();
+ }
+
+ @Override
+ public Map<String, List<Profile>> getActivePomProfilesByModel() {
+ return Collections.unmodifiableMap(activePomProfilesByModel);
+ }
+
+ public void setActivePomProfiles(String modelId, List<Profile>
activeProfiles) {
+ if (activeProfiles != null) {
+ this.activePomProfilesByModel.put(modelId, new
ArrayList<>(activeProfiles));
+ } else {
+ this.activePomProfilesByModel.remove(modelId);
+ }
}
@Override
diff --git
a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITgh11409ProfileSourceTest.java
b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITgh11409ProfileSourceTest.java
new file mode 100644
index 0000000000..c7aec3f6e8
--- /dev/null
+++
b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITgh11409ProfileSourceTest.java
@@ -0,0 +1,56 @@
+/*
+ * 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.io.File;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * This is a test for <a
href="https://github.com/apache/maven/issues/11409">GH-11409</a>.
+ * Verifies that profiles activated in parent POMs are correctly reported with
the parent POM
+ * as the source, not the child project.
+ *
+ * @since 4.0.0
+ */
+class MavenITgh11409ProfileSourceTest extends AbstractMavenIntegrationTestCase
{
+
+ /**
+ * Verify that help:active-profiles reports correct source for profiles
activated in parent POM.
+ *
+ * @throws Exception in case of failure
+ */
+ @Test
+ void testProfileSourceInMultiModuleProject() throws Exception {
+ File testDir = extractResources("/gh-11409");
+
+ Verifier verifier = newVerifier(new File(testDir,
"subproject").getAbsolutePath());
+ verifier.addCliArgument("help:active-profiles");
+ verifier.execute();
+ verifier.verifyErrorFreeLog();
+
+ // Verify that the parent profile is reported with the parent as the
source
+ // Note: Profile sources use groupId:artifactId:version format
(without packaging)
+ verifier.verifyTextInLog("parent-profile (source:
test.gh11409:parent:1.0-SNAPSHOT)");
+
+ // Verify that the child profile is reported with the child as the
source
+ verifier.verifyTextInLog("child-profile (source:
test.gh11409:subproject:1.0-SNAPSHOT)");
+ }
+}
+
diff --git
a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng8477MultithreadedFileActivationTest.java
b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng8477MultithreadedFileActivationTest.java
index 96d7e5344a..ef42aa3663 100644
---
a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng8477MultithreadedFileActivationTest.java
+++
b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng8477MultithreadedFileActivationTest.java
@@ -41,6 +41,6 @@ void testIt() throws Exception {
verifier.execute();
verifier.verifyErrorFreeLog();
- verifier.verifyTextInLog("- xxx (source: test:m2:jar:1)");
+ verifier.verifyTextInLog("- xxx (source: test:project:1)");
}
}
diff --git a/its/core-it-suite/src/test/resources/gh-11409/pom.xml
b/its/core-it-suite/src/test/resources/gh-11409/pom.xml
new file mode 100644
index 0000000000..427a9926e9
--- /dev/null
+++ b/its/core-it-suite/src/test/resources/gh-11409/pom.xml
@@ -0,0 +1,49 @@
+<?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>test.gh11409</groupId>
+ <artifactId>parent</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ <packaging>pom</packaging>
+
+ <name>GH-11409 Parent</name>
+ <description>Test for profile source tracking in multi-module
projects</description>
+
+ <modules>
+ <module>subproject</module>
+ </modules>
+
+ <profiles>
+ <profile>
+ <id>parent-profile</id>
+ <activation>
+ <activeByDefault>true</activeByDefault>
+ </activation>
+ <properties>
+ <parent.profile.active>true</parent.profile.active>
+ </properties>
+ </profile>
+ </profiles>
+</project>
+
diff --git a/its/core-it-suite/src/test/resources/gh-11409/subproject/pom.xml
b/its/core-it-suite/src/test/resources/gh-11409/subproject/pom.xml
new file mode 100644
index 0000000000..97880768d6
--- /dev/null
+++ b/its/core-it-suite/src/test/resources/gh-11409/subproject/pom.xml
@@ -0,0 +1,48 @@
+<?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>test.gh11409</groupId>
+ <artifactId>parent</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>subproject</artifactId>
+
+ <name>GH-11409 Subproject</name>
+ <description>Child project for testing profile source tracking</description>
+
+ <profiles>
+ <profile>
+ <id>child-profile</id>
+ <activation>
+ <activeByDefault>true</activeByDefault>
+ </activation>
+ <properties>
+ <child.profile.active>true</child.profile.active>
+ </properties>
+ </profile>
+ </profiles>
+</project>
+