Skip to content

Commit cd7db1c

Browse files
gnodetGiovds
andauthored
[MNG-8588] Add activated profiles to Project interface (#2132)
Co-authored-by: Giovanni van der Schelde <gvdschelde@gmail.com>
1 parent 12ff710 commit cd7db1c

File tree

6 files changed

+333
-40
lines changed

6 files changed

+333
-40
lines changed

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

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.apache.maven.api.annotations.Nonnull;
2727
import org.apache.maven.api.model.Build;
2828
import org.apache.maven.api.model.Model;
29+
import org.apache.maven.api.model.Profile;
2930

3031
/**
3132
* Interface representing a Maven project which can be created using the
@@ -237,4 +238,69 @@ default String getId() {
237238
*/
238239
@Nonnull
239240
Optional<Project> getParent();
241+
242+
/**
243+
* Returns all profiles defined in this project.
244+
* <p>
245+
* This method returns only the profiles defined directly in the current project's POM
246+
* and does not include profiles from parent projects.
247+
*
248+
* @return a non-null, possibly empty list of profiles defined in this project
249+
* @see Profile
250+
* @see #getEffectiveProfiles()
251+
*/
252+
@Nonnull
253+
List<Profile> getDeclaredProfiles();
254+
255+
/**
256+
* Returns all profiles defined in this project and all of its parent projects.
257+
* <p>
258+
* This method traverses the parent hierarchy and includes profiles defined in parent POMs.
259+
* The returned list contains profiles from the current project and all of its ancestors in
260+
* the project inheritance chain.
261+
*
262+
* @return a non-null, possibly empty list of all profiles from this project and its parents
263+
* @see Profile
264+
* @see #getDeclaredProfiles()
265+
*/
266+
@Nonnull
267+
List<Profile> getEffectiveProfiles();
268+
269+
/**
270+
* Returns all active profiles for the current project build.
271+
* <p>
272+
* Active profiles are those that have been explicitly activated through one of the following means:
273+
* <ul>
274+
* <li>Command line activation using the -P flag</li>
275+
* <li>Maven settings activation in settings.xml via &lt;activeProfiles&gt;</li>
276+
* <li>Automatic activation via &lt;activation&gt; conditions</li>
277+
* <li>The default active profile (marked with &lt;activeByDefault&gt;true&lt;/activeByDefault&gt;)</li>
278+
* </ul>
279+
* <p>
280+
* The active profiles control various aspects of the build configuration including but not
281+
* limited to dependencies, plugins, properties, and build resources.
282+
*
283+
* @return a non-null, possibly empty list of active profiles for this project
284+
* @see Profile
285+
* @see #getEffectiveActiveProfiles()
286+
*/
287+
@Nonnull
288+
List<Profile> getDeclaredActiveProfiles();
289+
290+
/**
291+
* Returns all active profiles for this project and all of its parent projects.
292+
* <p>
293+
* This method traverses the parent hierarchy and collects all active profiles from
294+
* the current project and its ancestors. Active profiles are those that meet the
295+
* activation criteria through explicit activation or automatic conditions.
296+
* <p>
297+
* The combined set of active profiles from the entire project hierarchy affects
298+
* the effective build configuration.
299+
*
300+
* @return a non-null, possibly empty list of all active profiles from this project and its parents
301+
* @see Profile
302+
* @see #getDeclaredActiveProfiles()
303+
*/
304+
@Nonnull
305+
List<Profile> getEffectiveActiveProfiles();
240306
}

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

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@
2323
import java.util.Collection;
2424
import java.util.Collections;
2525
import java.util.List;
26+
import java.util.Objects;
2627
import java.util.Optional;
28+
import java.util.stream.Stream;
2729

2830
import org.apache.maven.RepositoryUtils;
2931
import org.apache.maven.api.DependencyCoordinates;
@@ -38,6 +40,7 @@
3840
import org.apache.maven.api.annotations.Nullable;
3941
import org.apache.maven.api.model.DependencyManagement;
4042
import org.apache.maven.api.model.Model;
43+
import org.apache.maven.api.model.Profile;
4144
import org.apache.maven.impl.MappedCollection;
4245
import org.apache.maven.impl.MappedList;
4346
import org.apache.maven.project.MavenProject;
@@ -165,6 +168,37 @@ public Optional<Project> getParent() {
165168
return Optional.ofNullable(session.getProject(parent));
166169
}
167170

171+
@Override
172+
@Nonnull
173+
public List<Profile> getDeclaredProfiles() {
174+
return getModel().getProfiles();
175+
}
176+
177+
@Override
178+
@Nonnull
179+
public List<Profile> getEffectiveProfiles() {
180+
return Stream.iterate(this.project, Objects::nonNull, MavenProject::getParent)
181+
.flatMap(project -> project.getModel().getDelegate().getProfiles().stream())
182+
.toList();
183+
}
184+
185+
@Override
186+
@Nonnull
187+
public List<Profile> getDeclaredActiveProfiles() {
188+
return project.getActiveProfiles().stream()
189+
.map(org.apache.maven.model.Profile::getDelegate)
190+
.toList();
191+
}
192+
193+
@Override
194+
@Nonnull
195+
public List<Profile> getEffectiveActiveProfiles() {
196+
return Stream.iterate(this.project, Objects::nonNull, MavenProject::getParent)
197+
.flatMap(project -> project.getActiveProfiles().stream())
198+
.map(org.apache.maven.model.Profile::getDelegate)
199+
.toList();
200+
}
201+
168202
@Nonnull
169203
private DependencyCoordinates toDependency(org.apache.maven.api.model.Dependency dependency) {
170204
return new DependencyCoordinates() {

impl/maven-core/src/test/java/org/apache/maven/project/DefaultMavenProjectBuilderTest.java

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,20 +25,26 @@
2525
import java.nio.file.StandardCopyOption;
2626
import java.util.List;
2727

28+
import org.apache.maven.api.model.InputLocation;
29+
import org.apache.maven.api.model.InputSource;
2830
import org.apache.maven.artifact.Artifact;
2931
import org.apache.maven.artifact.repository.ArtifactRepository;
3032
import org.apache.maven.impl.InternalSession;
33+
import org.apache.maven.internal.impl.DefaultProject;
3134
import org.apache.maven.internal.impl.InternalMavenSession;
35+
import org.apache.maven.model.Profile;
3236
import org.junit.jupiter.api.BeforeEach;
3337
import org.junit.jupiter.api.Disabled;
3438
import org.junit.jupiter.api.Test;
3539
import org.junit.jupiter.api.io.TempDir;
40+
import org.mockito.Mockito;
3641

3742
import static org.apache.maven.project.ProjectBuildingResultWithProblemMessageMatcher.projectBuildingResultWithProblemMessage;
3843
import static org.codehaus.plexus.testing.PlexusExtension.getTestFile;
3944
import static org.hamcrest.MatcherAssert.assertThat;
4045
import static org.hamcrest.Matchers.contains;
4146
import static org.hamcrest.Matchers.containsString;
47+
import static org.hamcrest.Matchers.greaterThan;
4248
import static org.hamcrest.Matchers.is;
4349
import static org.junit.jupiter.api.Assertions.assertEquals;
4450
import static org.junit.jupiter.api.Assertions.assertFalse;
@@ -345,6 +351,130 @@ void rereadPom_mng7063() throws Exception {
345351
assertThat(project.getName(), is("PROJECT NAME"));
346352
}
347353

354+
@Test
355+
void testActivatedProfileBySource() throws Exception {
356+
File testPom = getTestFile("src/test/resources/projects/pom-with-profiles/pom.xml");
357+
358+
ProjectBuildingRequest request = newBuildingRequest();
359+
request.setLocalRepository(getLocalRepository());
360+
request.setActiveProfileIds(List.of("profile1"));
361+
362+
MavenProject project = projectBuilder.build(testPom, request).getProject();
363+
364+
assertTrue(project.getInjectedProfileIds().keySet().containsAll(List.of("external", project.getId())));
365+
assertTrue(project.getInjectedProfileIds().get("external").isEmpty());
366+
assertTrue(project.getInjectedProfileIds().get(project.getId()).stream().anyMatch("profile1"::equals));
367+
assertTrue(project.getInjectedProfileIds().get(project.getId()).stream().noneMatch("profile2"::equals));
368+
assertTrue(
369+
project.getInjectedProfileIds().get(project.getId()).stream().noneMatch("active-by-default"::equals));
370+
}
371+
372+
@Test
373+
void testActivatedDefaultProfileBySource() throws Exception {
374+
File testPom = getTestFile("src/test/resources/projects/pom-with-profiles/pom.xml");
375+
376+
ProjectBuildingRequest request = newBuildingRequest();
377+
request.setLocalRepository(getLocalRepository());
378+
379+
MavenProject project = projectBuilder.build(testPom, request).getProject();
380+
381+
assertTrue(project.getInjectedProfileIds().keySet().containsAll(List.of("external", project.getId())));
382+
assertTrue(project.getInjectedProfileIds().get("external").isEmpty());
383+
assertTrue(project.getInjectedProfileIds().get(project.getId()).stream().noneMatch("profile1"::equals));
384+
assertTrue(project.getInjectedProfileIds().get(project.getId()).stream().noneMatch("profile2"::equals));
385+
assertTrue(project.getInjectedProfileIds().get(project.getId()).stream().anyMatch("active-by-default"::equals));
386+
387+
InternalMavenSession session = Mockito.mock(InternalMavenSession.class);
388+
List<org.apache.maven.api.model.Profile> activeProfiles =
389+
new DefaultProject(session, project).getDeclaredActiveProfiles();
390+
assertEquals(1, activeProfiles.size());
391+
org.apache.maven.api.model.Profile profile = activeProfiles.get(0);
392+
assertEquals("active-by-default", profile.getId());
393+
InputLocation location = profile.getLocation("");
394+
assertNotNull(location);
395+
assertThat(location.getLineNumber(), greaterThan(0));
396+
assertThat(location.getColumnNumber(), greaterThan(0));
397+
assertNotNull(location.getSource());
398+
assertThat(location.getSource().getLocation(), containsString("pom-with-profiles/pom.xml"));
399+
}
400+
401+
@Test
402+
void testActivatedExternalProfileBySource() throws Exception {
403+
File testPom = getTestFile("src/test/resources/projects/pom-with-profiles/pom.xml");
404+
405+
ProjectBuildingRequest request = newBuildingRequest();
406+
request.setLocalRepository(getLocalRepository());
407+
408+
final Profile externalProfile = new Profile();
409+
externalProfile.setLocation(
410+
"",
411+
new org.apache.maven.model.InputLocation(
412+
1, 1, new org.apache.maven.model.InputSource(new InputSource(null, "settings.xml", null))));
413+
externalProfile.setId("external-profile");
414+
request.addProfile(externalProfile);
415+
request.setActiveProfileIds(List.of(externalProfile.getId()));
416+
417+
MavenProject project = projectBuilder.build(testPom, request).getProject();
418+
419+
assertTrue(project.getInjectedProfileIds().keySet().containsAll(List.of("external", project.getId())));
420+
assertTrue(project.getInjectedProfileIds().get("external").stream().anyMatch("external-profile"::equals));
421+
assertTrue(project.getInjectedProfileIds().get(project.getId()).stream().noneMatch("profile1"::equals));
422+
assertTrue(project.getInjectedProfileIds().get(project.getId()).stream().noneMatch("profile2"::equals));
423+
assertTrue(project.getInjectedProfileIds().get(project.getId()).stream().anyMatch("active-by-default"::equals));
424+
425+
InternalMavenSession session = Mockito.mock(InternalMavenSession.class);
426+
List<org.apache.maven.api.model.Profile> activeProfiles =
427+
new DefaultProject(session, project).getDeclaredActiveProfiles();
428+
assertEquals(2, activeProfiles.size());
429+
org.apache.maven.api.model.Profile profile = activeProfiles.get(0);
430+
assertEquals("active-by-default", profile.getId());
431+
InputLocation location = profile.getLocation("");
432+
assertNotNull(location);
433+
assertThat(location.getLineNumber(), greaterThan(0));
434+
assertThat(location.getColumnNumber(), greaterThan(0));
435+
assertNotNull(location.getSource());
436+
assertThat(location.getSource().getLocation(), containsString("pom-with-profiles/pom.xml"));
437+
profile = activeProfiles.get(1);
438+
assertEquals("external-profile", profile.getId());
439+
location = profile.getLocation("");
440+
assertNotNull(location);
441+
assertThat(location.getLineNumber(), greaterThan(0));
442+
assertThat(location.getColumnNumber(), greaterThan(0));
443+
assertNotNull(location.getSource());
444+
assertThat(location.getSource().getLocation(), containsString("settings.xml"));
445+
}
446+
447+
@Test
448+
void testActivatedProfileIsResolved() throws Exception {
449+
File testPom = getTestFile("src/test/resources/projects/pom-with-profiles/pom.xml");
450+
451+
ProjectBuildingRequest request = newBuildingRequest();
452+
request.setLocalRepository(getLocalRepository());
453+
request.setActiveProfileIds(List.of("profile1"));
454+
455+
MavenProject project = projectBuilder.build(testPom, request).getProject();
456+
457+
assertEquals(1, project.getActiveProfiles().size());
458+
assertTrue(project.getActiveProfiles().stream().anyMatch(p -> "profile1".equals(p.getId())));
459+
assertTrue(project.getActiveProfiles().stream().noneMatch(p -> "profile2".equals(p.getId())));
460+
assertTrue(project.getActiveProfiles().stream().noneMatch(p -> "active-by-default".equals(p.getId())));
461+
}
462+
463+
@Test
464+
void testActivatedProfileByDefaultIsResolved() throws Exception {
465+
File testPom = getTestFile("src/test/resources/projects/pom-with-profiles/pom.xml");
466+
467+
ProjectBuildingRequest request = newBuildingRequest();
468+
request.setLocalRepository(getLocalRepository());
469+
470+
MavenProject project = projectBuilder.build(testPom, request).getProject();
471+
472+
assertEquals(1, project.getActiveProfiles().size());
473+
assertTrue(project.getActiveProfiles().stream().noneMatch(p -> "profile1".equals(p.getId())));
474+
assertTrue(project.getActiveProfiles().stream().noneMatch(p -> "profile2".equals(p.getId())));
475+
assertTrue(project.getActiveProfiles().stream().anyMatch(p -> "active-by-default".equals(p.getId())));
476+
}
477+
348478
/**
349479
* Tests whether external version range parent references are build correctly.
350480
*
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?xml version="1.0" encoding="ISO-8859-1"?>
2+
3+
<!--
4+
Licensed to the Apache Software Foundation (ASF) under one
5+
or more contributor license agreements. See the NOTICE file
6+
distributed with this work for additional information
7+
regarding copyright ownership. The ASF licenses this file
8+
to you under the Apache License, Version 2.0 (the
9+
"License"); you may not use this file except in compliance
10+
with the License. You may obtain a copy of the License at
11+
12+
http://www.apache.org/licenses/LICENSE-2.0
13+
14+
Unless required by applicable law or agreed to in writing,
15+
software distributed under the License is distributed on an
16+
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17+
KIND, either express or implied. See the License for the
18+
specific language governing permissions and limitations
19+
under the License.
20+
-->
21+
22+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
23+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
24+
<modelVersion>4.0.0</modelVersion>
25+
26+
<groupId>maven</groupId>
27+
<artifactId>maven-core</artifactId>
28+
<version>2.0-SNAPSHOT</version>
29+
30+
<name>Maven</name>
31+
32+
<profiles>
33+
<profile>
34+
<id>active-by-default</id>
35+
<activation>
36+
<activeByDefault>true</activeByDefault>
37+
</activation>
38+
</profile>
39+
<profile>
40+
<id>profile1</id>
41+
</profile>
42+
<profile>
43+
<id>profile2</id>
44+
</profile>
45+
</profiles>
46+
</project>

0 commit comments

Comments
 (0)