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

commit a58551d9ab572b534aea4671d831faf7ea13abe6
Author: Guillaume Nodet <gno...@gmail.com>
AuthorDate: Thu Nov 28 10:45:14 2024 +0100

    [MNG-8391] Wrong effective model when conflicting values come from parents 
and profiles
---
 .../services/model/ProfileActivationContext.java   | 105 +++++--
 .../internal/impl/model/DefaultModelBuilder.java   | 123 ++++----
 .../model/DefaultProfileActivationContext.java     | 331 ++++++++++++++++++++-
 .../impl/model/DefaultProfileSelector.java         |   8 +-
 .../ProfileActivationFilePathInterpolator.java     |  86 ------
 .../impl/model/profile/ConditionFunctions.java     |  82 +----
 .../model/profile/ConditionProfileActivator.java   |  52 +---
 .../impl/model/profile/FileProfileActivator.java   |  32 +-
 .../model/profile/JdkVersionProfileActivator.java  |   2 +-
 .../profile/OperatingSystemProfileActivator.java   |  18 +-
 .../model/profile/PackagingProfileActivator.java   |   2 +-
 .../model/profile/PropertyProfileActivator.java    |   6 +-
 .../impl/model/DefaultModelBuilderTest.java        |  66 ++++
 .../profile/AbstractProfileActivatorTest.java      |   5 +-
 .../impl/model/profile/ConditionParserTest.java    |  14 +-
 .../profile/ConditionProfileActivatorTest.java     |  11 +-
 .../model/profile/FileProfileActivatorTest.java    |   8 +-
 .../impl/standalone/RepositorySystemSupplier.java  |   7 +-
 .../factory/props-and-profiles-grand-parent.xml    |  34 +++
 .../poms/factory/props-and-profiles-parent.xml     |  28 ++
 .../resources/poms/factory/props-and-profiles.xml  |  24 ++
 21 files changed, 686 insertions(+), 358 deletions(-)

diff --git 
a/impl/maven-impl/src/main/java/org/apache/maven/api/services/model/ProfileActivationContext.java
 
b/impl/maven-impl/src/main/java/org/apache/maven/api/services/model/ProfileActivationContext.java
index 1cc7f3c46f..b7951ea3e1 100644
--- 
a/impl/maven-impl/src/main/java/org/apache/maven/api/services/model/ProfileActivationContext.java
+++ 
b/impl/maven-impl/src/main/java/org/apache/maven/api/services/model/ProfileActivationContext.java
@@ -18,11 +18,11 @@
  */
 package org.apache.maven.api.services.model;
 
-import java.util.List;
-import java.util.Map;
-
 import org.apache.maven.api.annotations.Nonnull;
+import org.apache.maven.api.annotations.Nullable;
 import org.apache.maven.api.model.Model;
+import org.apache.maven.api.services.InterpolatorException;
+import org.apache.maven.api.services.ModelBuilderException;
 
 /**
  * Describes the environmental context used to determine the activation status 
of profiles.
@@ -34,45 +34,100 @@ import org.apache.maven.api.model.Model;
 public interface ProfileActivationContext {
 
     /**
-     * Gets the identifiers of those profiles that should be activated by 
explicit demand.
+     * Checks if the specified profile has been explicitly activated.
      *
-     * @return The identifiers of those profiles to activate, never {@code 
null}.
+     * @param profileId the profile id
+     * @return whether the profile has been activated
      */
-    @Nonnull
-    List<String> getActiveProfileIds();
+    boolean isProfileActive(@Nonnull String profileId);
 
     /**
-     * Gets the identifiers of those profiles that should be deactivated by 
explicit demand.
+     * Checks if the specified profile has been explicitly deactivated.
      *
-     * @return The identifiers of those profiles to deactivate, never {@code 
null}.
+     * @param profileId the profile id
+     * @return whether the profile has been deactivated
      */
-    @Nonnull
-    List<String> getInactiveProfileIds();
+    boolean isProfileInactive(@Nonnull String profileId);
 
     /**
-     * Gets the system properties to use for interpolation and profile 
activation. The system properties are collected
+     * Gets the system property to use for interpolation and profile 
activation. The system properties are collected
      * from the runtime environment like {@link System#getProperties()} and 
environment variables.
      *
-     * @return The execution properties, never {@code null}.
+     * @param key the name of the system property
+     * @return the system property for the specified key, or {@code null}
+     */
+    @Nullable
+    String getSystemProperty(@Nonnull String key);
+
+    /**
+     * Gets the user property to use for interpolation and profile activation. 
The user properties have been
+     * configured directly by the user on his discretion, e.g. via the {@code 
-Dkey=value} parameter on the command line.
+     *
+     * @param key the name of the user property
+     * @return The user property for the specified key, or {@code null}.
+     */
+    @Nullable
+    String getUserProperty(@Nonnull String key);
+
+    /**
+     * Gets the model property to use for interpolation and profile activation.
+     *
+     * @param key the name of the model property
+     * @return The model property for the specified key, or {@code null};
+     */
+    @Nullable
+    String getModelProperty(@Nonnull String key);
+
+    /**
+     * Gets the artifactId from the current model.
+     *
+     * @return The artifactId of the current model, or {@code null} if not set.
+     */
+    @Nullable
+    String getModelArtifactId();
+
+    /**
+     * Gets the packaging type from the current model.
+     *
+     * @return The packaging type of the current model, or {@code null} if not 
set.
+     */
+    @Nullable
+    String getModelPackaging();
+
+    /**
+     * Gets the root directory of the current model.
+     *
+     * @return The root directory path of the current model, or {@code null} 
if not set.
+     */
+    @Nullable
+    String getModelRootDirectory();
+
+    /**
+     * Gets the base directory of the current model.
+     *
+     * @return The base directory path of the current model, or {@code null} 
if not set.
      */
-    @Nonnull
-    Map<String, String> getSystemProperties();
+    @Nullable
+    String getModelBaseDirectory();
 
     /**
-     * Gets the user properties to use for interpolation and profile 
activation. The user properties have been
-     * configured directly by the user on his discretion, e.g. via the {@code 
-Dkey=value} parameter on the command
-     * line.
+     * Interpolates the given path string using the current context's 
properties.
      *
-     * @return The user properties, never {@code null}.
+     * @param path The path string to interpolate
+     * @return The interpolated path string
+     * @throws InterpolatorException if an error occurs during interpolation
      */
-    @Nonnull
-    Map<String, String> getUserProperties();
+    @Nullable
+    String interpolatePath(@Nullable String path);
 
     /**
-     * Gets the model which is being activated.
+     * Checks if a file or directory matching the given glob pattern exists at 
the specified path.
      *
-     * @return The project model, never {@code null}.
+     * @param path the base path to check
+     * @param glob whether the path can be a glob expression
+     * @return {@code true} if a matching file exists, {@code false} otherwise
+     * @throws ModelBuilderException if an error occurs while checking the path
+     * @throws InterpolatorException if an error occurs during interpolation
      */
-    @Nonnull
-    Model getModel();
+    boolean exists(@Nullable String path, boolean glob);
 }
diff --git 
a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelBuilder.java
 
b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelBuilder.java
index 908f2e0e1e..0490de64fb 100644
--- 
a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelBuilder.java
+++ 
b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelBuilder.java
@@ -99,6 +99,7 @@ import 
org.apache.maven.api.services.model.ModelResolverException;
 import org.apache.maven.api.services.model.ModelUrlNormalizer;
 import org.apache.maven.api.services.model.ModelValidator;
 import org.apache.maven.api.services.model.ModelVersionParser;
+import org.apache.maven.api.services.model.PathTranslator;
 import org.apache.maven.api.services.model.PluginConfigurationExpander;
 import org.apache.maven.api.services.model.PluginManagementInjector;
 import org.apache.maven.api.services.model.ProfileInjector;
@@ -145,12 +146,13 @@ public class DefaultModelBuilder implements ModelBuilder {
     private final DependencyManagementInjector dependencyManagementInjector;
     private final DependencyManagementImporter dependencyManagementImporter;
     private final PluginConfigurationExpander pluginConfigurationExpander;
-    private final ProfileActivationFilePathInterpolator 
profileActivationFilePathInterpolator;
     private final ModelVersionParser versionParser;
     private final List<ModelTransformer> transformers;
     private final ModelCacheFactory modelCacheFactory;
     private final ModelResolver modelResolver;
     private final Interpolator interpolator;
+    private final PathTranslator pathTranslator;
+    private final RootLocator rootLocator;
 
     @SuppressWarnings("checkstyle:ParameterNumber")
     @Inject
@@ -169,12 +171,13 @@ public class DefaultModelBuilder implements ModelBuilder {
             DependencyManagementInjector dependencyManagementInjector,
             DependencyManagementImporter dependencyManagementImporter,
             PluginConfigurationExpander pluginConfigurationExpander,
-            ProfileActivationFilePathInterpolator 
profileActivationFilePathInterpolator,
             ModelVersionParser versionParser,
             List<ModelTransformer> transformers,
             ModelCacheFactory modelCacheFactory,
             ModelResolver modelResolver,
-            Interpolator interpolator) {
+            Interpolator interpolator,
+            PathTranslator pathTranslator,
+            RootLocator rootLocator) {
         this.modelProcessor = modelProcessor;
         this.modelValidator = modelValidator;
         this.modelNormalizer = modelNormalizer;
@@ -189,12 +192,13 @@ public class DefaultModelBuilder implements ModelBuilder {
         this.dependencyManagementInjector = dependencyManagementInjector;
         this.dependencyManagementImporter = dependencyManagementImporter;
         this.pluginConfigurationExpander = pluginConfigurationExpander;
-        this.profileActivationFilePathInterpolator = 
profileActivationFilePathInterpolator;
         this.versionParser = versionParser;
         this.transformers = transformers;
         this.modelCacheFactory = modelCacheFactory;
         this.modelResolver = modelResolver;
         this.interpolator = interpolator;
+        this.pathTranslator = pathTranslator;
+        this.rootLocator = rootLocator;
     }
 
     public ModelBuilderSession newSession() {
@@ -862,12 +866,12 @@ public class DefaultModelBuilder implements ModelBuilder {
             }
         }
 
-        Model readParent(Model childModel) throws ModelBuilderException {
+        Model readParent(Model childModel, DefaultProfileActivationContext 
profileActivationContext) {
             Model parentModel;
 
             Parent parent = childModel.getParent();
             if (parent != null) {
-                parentModel = resolveParent(childModel);
+                parentModel = resolveParent(childModel, 
profileActivationContext);
 
                 if (!"pom".equals(parentModel.getPackaging())) {
                     add(
@@ -892,18 +896,20 @@ public class DefaultModelBuilder implements ModelBuilder {
             return parentModel;
         }
 
-        private Model resolveParent(Model childModel) throws 
ModelBuilderException {
+        private Model resolveParent(Model childModel, 
DefaultProfileActivationContext profileActivationContext)
+                throws ModelBuilderException {
             Model parentModel = null;
             if (isBuildRequest()) {
-                parentModel = readParentLocally(childModel);
+                parentModel = readParentLocally(childModel, 
profileActivationContext);
             }
             if (parentModel == null) {
-                parentModel = resolveAndReadParentExternally(childModel);
+                parentModel = resolveAndReadParentExternally(childModel, 
profileActivationContext);
             }
             return parentModel;
         }
 
-        private Model readParentLocally(Model childModel) throws 
ModelBuilderException {
+        private Model readParentLocally(Model childModel, 
DefaultProfileActivationContext profileActivationContext)
+                throws ModelBuilderException {
             ModelSource candidateSource;
 
             Parent parent = childModel.getParent();
@@ -938,7 +944,9 @@ public class DefaultModelBuilder implements ModelBuilder {
                 return null;
             }
 
-            Model candidateModel = derive(candidateSource).readAsParentModel();
+            ModelBuilderSessionState derived = derive(candidateSource);
+            Model candidateModel = 
derived.readAsParentModel(profileActivationContext);
+            addActivePomProfiles(derived.result.getActivePomProfiles());
 
             String groupId = getGroupId(candidateModel);
             String artifactId = candidateModel.getArtifactId();
@@ -1020,7 +1028,8 @@ public class DefaultModelBuilder implements ModelBuilder {
             add(Severity.FATAL, Version.BASE, buffer.toString(), 
parent.getLocation(""));
         }
 
-        Model resolveAndReadParentExternally(Model childModel) throws 
ModelBuilderException {
+        Model resolveAndReadParentExternally(Model childModel, 
DefaultProfileActivationContext profileActivationContext)
+                throws ModelBuilderException {
             ModelBuilderRequest request = this.request;
             setSource(childModel);
 
@@ -1078,7 +1087,7 @@ public class DefaultModelBuilder implements ModelBuilder {
                     .source(modelSource)
                     .build();
 
-            Model parentModel = derive(lenientRequest).readAsParentModel();
+            Model parentModel = 
derive(lenientRequest).readAsParentModel(profileActivationContext);
 
             if (!parent.getVersion().equals(version)) {
                 String rawChildModelVersion = childModel.getVersion();
@@ -1119,7 +1128,7 @@ public class DefaultModelBuilder implements ModelBuilder {
                 for (Profile profile : activeExternalProfiles) {
                     profileProps.putAll(profile.getProperties());
                 }
-                
profileProps.putAll(profileActivationContext.getUserProperties());
+                profileProps.putAll(request.getUserProperties());
                 profileActivationContext.setUserProperties(profileProps);
             }
 
@@ -1163,11 +1172,12 @@ public class DefaultModelBuilder implements 
ModelBuilder {
                 for (Profile profile : activeExternalProfiles) {
                     profileProps.putAll(profile.getProperties());
                 }
-                
profileProps.putAll(profileActivationContext.getUserProperties());
+                profileProps.putAll(request.getUserProperties());
                 profileActivationContext.setUserProperties(profileProps);
             }
 
-            Model parentModel = readParent(activatedFileModel);
+            Model parentModel = readParent(activatedFileModel, 
profileActivationContext);
+
             // Now that we have read the parent, we can set the relative
             // path correctly if it was not set in the input model
             if (inputModel.getParent() != null && 
inputModel.getParent().getRelativePath() == null) {
@@ -1186,16 +1196,7 @@ public class DefaultModelBuilder implements ModelBuilder 
{
                 inputModel = 
inputModel.withParent(inputModel.getParent().withRelativePath(relPath));
             }
 
-            List<Profile> parentInterpolatedProfiles =
-                    interpolateActivations(parentModel.getProfiles(), 
profileActivationContext, this);
-            // profile injection
-            List<Profile> parentActivePomProfiles =
-                    getActiveProfiles(parentInterpolatedProfiles, 
profileActivationContext);
-            Model injectedParentModel = profileInjector
-                    .injectProfiles(parentModel, parentActivePomProfiles, 
request, this)
-                    .withProfiles(List.of());
-
-            Model model = 
inheritanceAssembler.assembleModelInheritance(inputModel, injectedParentModel, 
request, this);
+            Model model = 
inheritanceAssembler.assembleModelInheritance(inputModel, parentModel, request, 
this);
 
             // model normalization
             model = modelNormalizer.mergeDuplicates(model, request, this);
@@ -1211,10 +1212,7 @@ public class DefaultModelBuilder implements ModelBuilder 
{
             model = profileInjector.injectProfiles(model, activePomProfiles, 
request, this);
             model = profileInjector.injectProfiles(model, 
activeExternalProfiles, request, this);
 
-            List<Profile> allProfiles = new 
ArrayList<>(parentActivePomProfiles.size() + activePomProfiles.size());
-            allProfiles.addAll(parentActivePomProfiles);
-            allProfiles.addAll(activePomProfiles);
-            result.setActivePomProfiles(allProfiles);
+            addActivePomProfiles(activePomProfiles);
 
             // model interpolation
             Model resultModel = model;
@@ -1239,6 +1237,15 @@ public class DefaultModelBuilder implements ModelBuilder 
{
             return resultModel;
         }
 
+        private void addActivePomProfiles(List<Profile> activePomProfiles) {
+            if (activePomProfiles != null) {
+                if (result.getActivePomProfiles() == null) {
+                    result.setActivePomProfiles(new ArrayList<>());
+                }
+                result.getActivePomProfiles().addAll(activePomProfiles);
+            }
+        }
+
         private List<Profile> getActiveProfiles(
                 Collection<Profile> interpolatedProfiles, 
DefaultProfileActivationContext profileActivationContext) {
             if (isBuildRequestWithActivation()) {
@@ -1491,13 +1498,25 @@ public class DefaultModelBuilder implements 
ModelBuilder {
         /**
          * Reads the request source's parent.
          */
-        Model readAsParentModel() throws ModelBuilderException {
-            return cache(request.getSource(), PARENT, 
this::doReadAsParentModel);
+        Model readAsParentModel(DefaultProfileActivationContext 
profileActivationContext) throws ModelBuilderException {
+            Map<DefaultProfileActivationContext.Record, Model> 
parentsPerContext =
+                    cache(request.getSource(), PARENT, ConcurrentHashMap::new);
+            for (Map.Entry<DefaultProfileActivationContext.Record, Model> e : 
parentsPerContext.entrySet()) {
+                if (e.getKey().matches(profileActivationContext)) {
+                    return e.getValue();
+                }
+            }
+            DefaultProfileActivationContext.Record prev = 
profileActivationContext.start();
+            Model model = doReadAsParentModel(profileActivationContext);
+            DefaultProfileActivationContext.Record record = 
profileActivationContext.stop(prev);
+            parentsPerContext.put(record, model);
+            return model;
         }
 
-        private Model doReadAsParentModel() throws ModelBuilderException {
+        private Model doReadAsParentModel(DefaultProfileActivationContext 
profileActivationContext)
+                throws ModelBuilderException {
             Model raw = readRawModel();
-            Model parentData = readParent(raw);
+            Model parentData = readParent(raw, profileActivationContext);
             Model parent = new DefaultInheritanceAssembler(new 
DefaultInheritanceAssembler.InheritanceModelMerger() {
                         @Override
                         protected void mergeModel_Modules(
@@ -1514,23 +1533,21 @@ public class DefaultModelBuilder implements 
ModelBuilder {
                                 Model source,
                                 boolean sourceDominant,
                                 Map<Object, Object> context) {}
-
-                        @SuppressWarnings("deprecation")
-                        @Override
-                        protected void mergeModel_Profiles(
-                                Model.Builder builder,
-                                Model target,
-                                Model source,
-                                boolean sourceDominant,
-                                Map<Object, Object> context) {
-                            
builder.profiles(Stream.concat(source.getProfiles().stream(), 
target.getProfiles().stream())
-                                    .map(p -> 
p.withModules(null).withSubprojects(null))
-                                    .toList());
-                        }
                     })
                     .assembleModelInheritance(raw, parentData, request, this);
 
-            return parent.withParent(null);
+            // activate profiles
+            List<Profile> parentInterpolatedProfiles =
+                    interpolateActivations(parent.getProfiles(), 
profileActivationContext, this);
+            // profile injection
+            List<Profile> parentActivePomProfiles =
+                    getActiveProfiles(parentInterpolatedProfiles, 
profileActivationContext);
+            Model injectedParentModel = profileInjector
+                    .injectProfiles(parent, parentActivePomProfiles, request, 
this)
+                    .withProfiles(List.of());
+            addActivePomProfiles(parentActivePomProfiles);
+
+            return injectedParentModel.withParent(null);
         }
 
         private Model importDependencyManagement(Model model, 
Collection<String> importIds) {
@@ -1763,9 +1780,8 @@ public class DefaultModelBuilder implements ModelBuilder {
                 ProfileInterpolator() {
                     super(s -> {
                         try {
-                            Map<String, String> map1 = 
context.getUserProperties();
-                            Map<String, String> map2 = 
context.getSystemProperties();
-                            return interpolator.interpolate(s, 
Interpolator.chain(map1::get, map2::get));
+                            return interpolator.interpolate(
+                                    s, 
Interpolator.chain(context::getUserProperty, context::getSystemProperty));
                         } catch (InterpolatorException e) {
                             problems.add(Severity.ERROR, Version.BASE, 
e.getMessage(), e);
                         }
@@ -1809,7 +1825,7 @@ public class DefaultModelBuilder implements ModelBuilder {
 
                 private String transformPath(String path, ActivationFile 
target, String locationKey) {
                     try {
-                        return 
profileActivationFilePathInterpolator.interpolate(path, context);
+                        return context.interpolatePath(path);
                     } catch (InterpolatorException e) {
                         problems.add(
                                 Severity.ERROR,
@@ -1860,7 +1876,8 @@ public class DefaultModelBuilder implements ModelBuilder {
     }
 
     private DefaultProfileActivationContext 
getProfileActivationContext(ModelBuilderRequest request, Model model) {
-        DefaultProfileActivationContext context = new 
DefaultProfileActivationContext();
+        DefaultProfileActivationContext context =
+                new DefaultProfileActivationContext(pathTranslator, 
rootLocator, interpolator);
 
         context.setActiveProfileIds(request.getActiveProfileIds());
         context.setInactiveProfileIds(request.getInactiveProfileIds());
diff --git 
a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultProfileActivationContext.java
 
b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultProfileActivationContext.java
index ef04023b21..6735661484 100644
--- 
a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultProfileActivationContext.java
+++ 
b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultProfileActivationContext.java
@@ -18,32 +18,168 @@
  */
 package org.apache.maven.internal.impl.model;
 
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.PathMatcher;
+import java.nio.file.Paths;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 import org.apache.maven.api.model.Model;
+import org.apache.maven.api.services.Interpolator;
+import org.apache.maven.api.services.InterpolatorException;
+import org.apache.maven.api.services.ModelBuilderException;
+import org.apache.maven.api.services.ProjectBuilderException;
+import org.apache.maven.api.services.model.PathTranslator;
 import org.apache.maven.api.services.model.ProfileActivationContext;
+import org.apache.maven.api.services.model.RootLocator;
 
 /**
  * Describes the environmental context used to determine the activation status 
of profiles.
- *
  */
 public class DefaultProfileActivationContext implements 
ProfileActivationContext {
 
-    private List<String> activeProfileIds = Collections.emptyList();
+    record ExistRequest(String path, boolean enableGlob) {}
 
-    private List<String> inactiveProfileIds = Collections.emptyList();
+    enum ModelInfo {
+        ArtifactId,
+        Packaging,
+        BaseDirectory,
+        RootDirectory
+    }
 
-    private Map<String, String> systemProperties = Collections.emptyMap();
+    /**
+     * This class keeps track of information that are used during profile 
activation.
+     * This allows to cache the activated parent and check if the result of the
+     * activation will be the same by verifying that the used keys are the 
same.
+     */
+    static class Record {
+        private final Map<String, Boolean> usedActiveProfiles = new 
HashMap<>();
+        private final Map<String, Boolean> usedInactiveProfiles = new 
HashMap<>();
+        private final Map<String, String> usedSystemProperties = new 
HashMap<>();
+        private final Map<String, String> usedUserProperties = new HashMap<>();
+        private final Map<String, String> usedModelProperties = new 
HashMap<>();
+        private final Map<ModelInfo, String> usedModelInfos = new HashMap<>();
+        private final Map<ExistRequest, Boolean> usedExists = new HashMap<>();
 
-    private Map<String, String> userProperties = Collections.emptyMap();
+        @Override
+        public boolean equals(Object o) {
+            if (o instanceof Record record) {
+                return Objects.equals(usedActiveProfiles, 
record.usedActiveProfiles)
+                        && Objects.equals(usedInactiveProfiles, 
record.usedInactiveProfiles)
+                        && Objects.equals(usedSystemProperties, 
record.usedSystemProperties)
+                        && Objects.equals(usedUserProperties, 
record.usedUserProperties)
+                        && Objects.equals(usedModelProperties, 
record.usedModelProperties)
+                        && Objects.equals(usedModelInfos, 
record.usedModelInfos)
+                        && Objects.equals(usedExists, record.usedExists);
+            }
+            return false;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(
+                    usedActiveProfiles,
+                    usedInactiveProfiles,
+                    usedSystemProperties,
+                    usedUserProperties,
+                    usedModelProperties,
+                    usedModelInfos,
+                    usedExists);
+        }
+
+        boolean matches(DefaultProfileActivationContext context) {
+            return matchesProfiles(usedActiveProfiles, 
context.activeProfileIds)
+                    && matchesProfiles(usedInactiveProfiles, 
context.inactiveProfileIds)
+                    && matchesProperties(usedSystemProperties, 
context.systemProperties)
+                    && matchesProperties(usedUserProperties, 
context.userProperties)
+                    && matchesProperties(usedModelProperties, 
context.model.getProperties())
+                    && matchesModelInfos(usedModelInfos, context)
+                    && matchesExists(usedExists, context);
+        }
+
+        private boolean matchesProfiles(Map<String, Boolean> expected, 
List<String> actual) {
+            return expected.entrySet().stream()
+                    .allMatch(e -> Objects.equals(e.getValue(), 
actual.contains(e.getKey())));
+        }
+
+        private boolean matchesProperties(Map<String, String> expected, 
Map<String, String> actual) {
+            return expected.entrySet().stream().allMatch(e -> 
Objects.equals(e.getValue(), actual.get(e.getKey())));
+        }
+
+        private boolean matchesModelInfos(Map<ModelInfo, String> infos, 
DefaultProfileActivationContext context) {
+            return infos.entrySet().stream()
+                    .allMatch(e -> Objects.equals(e.getValue(), 
getModelValue(e.getKey(), context)));
+        }
+
+        private String getModelValue(ModelInfo key, 
DefaultProfileActivationContext context) {
+            return switch (key) {
+                case ArtifactId -> context.model.getArtifactId();
+                case Packaging -> context.model.getPackaging();
+                case BaseDirectory -> context.doGetModelBaseDirectory();
+                case RootDirectory -> context.doGetModelRootDirectory();
+            };
+        }
 
+        private boolean matchesExists(Map<ExistRequest, Boolean> exists, 
DefaultProfileActivationContext context) {
+            return exists.entrySet().stream()
+                    .allMatch(e -> Objects.equals(
+                            e.getValue(),
+                            context.doExists(e.getKey().path(), 
e.getKey().enableGlob())));
+        }
+    }
+
+    private final PathTranslator pathTranslator;
+    private final RootLocator rootLocator;
+    private final Interpolator interpolator;
+
+    private List<String> activeProfileIds = Collections.emptyList();
+    private List<String> inactiveProfileIds = Collections.emptyList();
+    private Map<String, String> systemProperties = Collections.emptyMap();
+    private Map<String, String> userProperties = Collections.emptyMap();
     private Model model;
 
+    private final ThreadLocal<Record> records = new ThreadLocal<>();
+
+    public DefaultProfileActivationContext(
+            PathTranslator pathTranslator, RootLocator rootLocator, 
Interpolator interpolator) {
+        this.pathTranslator = pathTranslator;
+        this.rootLocator = rootLocator;
+        this.interpolator = interpolator;
+    }
+
+    Record start() {
+        Record record = records.get();
+        records.set(new Record());
+        return record;
+    }
+
+    Record stop(Record previous) {
+        Record record = records.get();
+        records.set(previous);
+        // only keep keys for which the value is `true`
+        record.usedActiveProfiles.values().removeIf(value -> !value);
+        record.usedInactiveProfiles.values().removeIf(value -> !value);
+        return record;
+    }
+
     @Override
-    public List<String> getActiveProfileIds() {
-        return activeProfileIds;
+    public boolean isProfileActive(String profileId) {
+        Record record = records.get();
+        if (record != null) {
+            return record.usedActiveProfiles.computeIfAbsent(profileId, 
activeProfileIds::contains);
+        } else {
+            return activeProfileIds.contains(profileId);
+        }
     }
 
     /**
@@ -58,8 +194,13 @@ public class DefaultProfileActivationContext implements 
ProfileActivationContext
     }
 
     @Override
-    public List<String> getInactiveProfileIds() {
-        return inactiveProfileIds;
+    public boolean isProfileInactive(String profileId) {
+        Record record = records.get();
+        if (record != null) {
+            return record.usedInactiveProfiles.computeIfAbsent(profileId, 
inactiveProfileIds::contains);
+        } else {
+            return inactiveProfileIds.contains(profileId);
+        }
     }
 
     /**
@@ -74,8 +215,13 @@ public class DefaultProfileActivationContext implements 
ProfileActivationContext
     }
 
     @Override
-    public Map<String, String> getSystemProperties() {
-        return systemProperties;
+    public String getSystemProperty(String key) {
+        Record record = records.get();
+        if (record != null) {
+            return record.usedSystemProperties.computeIfAbsent(key, 
systemProperties::get);
+        } else {
+            return systemProperties.get(key);
+        }
     }
 
     /**
@@ -91,8 +237,13 @@ public class DefaultProfileActivationContext implements 
ProfileActivationContext
     }
 
     @Override
-    public Map<String, String> getUserProperties() {
-        return userProperties;
+    public String getUserProperty(String key) {
+        Record record = records.get();
+        if (record != null) {
+            return record.usedUserProperties.computeIfAbsent(key, 
userProperties::get);
+        } else {
+            return userProperties.get(key);
+        }
     }
 
     /**
@@ -109,16 +260,164 @@ public class DefaultProfileActivationContext implements 
ProfileActivationContext
     }
 
     @Override
-    public Model getModel() {
-        return model;
+    public String getModelArtifactId() {
+        Record record = records.get();
+        if (record != null) {
+            return record.usedModelInfos.computeIfAbsent(ModelInfo.ArtifactId, 
k -> model.getArtifactId());
+        } else {
+            return model.getArtifactId();
+        }
+    }
+
+    @Override
+    public String getModelPackaging() {
+        Record record = records.get();
+        if (record != null) {
+            return record.usedModelInfos.computeIfAbsent(ModelInfo.Packaging, 
k -> model.getPackaging());
+        } else {
+            return model.getPackaging();
+        }
+    }
+
+    @Override
+    public String getModelProperty(String key) {
+        Record record = records.get();
+        if (record != null) {
+            return record.usedModelProperties.computeIfAbsent(
+                    key, k -> model.getProperties().get(k));
+        } else {
+            return model.getProperties().get(key);
+        }
+    }
+
+    @Override
+    public String getModelBaseDirectory() {
+        Record record = records.get();
+        if (record != null) {
+            return 
record.usedModelInfos.computeIfAbsent(ModelInfo.BaseDirectory, k -> 
doGetModelBaseDirectory());
+        } else {
+            return doGetModelBaseDirectory();
+        }
+    }
+
+    private String doGetModelBaseDirectory() {
+        Path basedir = model.getProjectDirectory();
+        return basedir != null ? basedir.toAbsolutePath().toString() : null;
+    }
+
+    @Override
+    public String getModelRootDirectory() {
+        Record record = records.get();
+        if (record != null) {
+            return 
record.usedModelInfos.computeIfAbsent(ModelInfo.RootDirectory, k -> 
doGetModelRootDirectory());
+        } else {
+            return doGetModelRootDirectory();
+        }
+    }
+
+    private String doGetModelRootDirectory() {
+        Path basedir = model != null ? model.getProjectDirectory() : null;
+        Path rootdir = rootLocator != null ? rootLocator.findRoot(basedir) : 
null;
+        return rootdir != null ? rootdir.toAbsolutePath().toString() : null;
     }
 
     public DefaultProfileActivationContext setModel(Model model) {
         this.model = model;
-
         return this;
     }
 
+    @Override
+    public String interpolatePath(String path) throws InterpolatorException {
+        if (path == null) {
+            return null;
+        }
+        String absolutePath = interpolator.interpolate(path, s -> {
+            if ("basedir".equals(s) || "project.basedir".equals(s)) {
+                return getModelBaseDirectory();
+            }
+            if ("project.rootDirectory".equals(s)) {
+                return getModelRootDirectory();
+            }
+            String r = getModelProperty(s);
+            if (r == null) {
+                r = getUserProperty(s);
+            }
+            if (r == null) {
+                r = getSystemProperty(s);
+            }
+            return r;
+        });
+        return pathTranslator.alignToBaseDirectory(absolutePath, 
model.getProjectDirectory());
+    }
+
+    @Override
+    public boolean exists(String path, boolean enableGlob) throws 
ModelBuilderException {
+        Record record = records.get();
+        if (record != null) {
+            return record.usedExists.computeIfAbsent(
+                    new ExistRequest(path, enableGlob), r -> doExists(r.path, 
r.enableGlob));
+        } else {
+            return doExists(path, enableGlob);
+        }
+    }
+
+    private boolean doExists(String path, boolean enableGlob) throws 
ModelBuilderException {
+        String pattern = interpolatePath(path);
+        String fixed, glob;
+        if (enableGlob) {
+            int asteriskIndex = pattern.indexOf('*');
+            int questionMarkIndex = pattern.indexOf('?');
+            int firstWildcardIndex = questionMarkIndex < 0
+                    ? asteriskIndex
+                    : asteriskIndex < 0 ? questionMarkIndex : 
Math.min(asteriskIndex, questionMarkIndex);
+            if (firstWildcardIndex < 0) {
+                fixed = pattern;
+                glob = "";
+            } else {
+                int lastSep = pattern.substring(0, 
firstWildcardIndex).lastIndexOf(File.separatorChar);
+                if (lastSep < 0) {
+                    fixed = "";
+                    glob = pattern;
+                } else {
+                    fixed = pattern.substring(0, lastSep);
+                    glob = pattern.substring(lastSep + 1);
+                }
+            }
+        } else {
+            fixed = pattern;
+            glob = "";
+        }
+        Path fixedPath = Paths.get(fixed);
+        return doExists(fixedPath, glob);
+    }
+
+    private static Boolean doExists(Path fixedPath, String glob) {
+        if (fixedPath == null || !Files.exists(fixedPath)) {
+            return false;
+        }
+        if (glob != null && !glob.isEmpty()) {
+            try {
+                PathMatcher matcher = 
fixedPath.getFileSystem().getPathMatcher("glob:" + glob);
+                AtomicBoolean found = new AtomicBoolean(false);
+                Files.walkFileTree(fixedPath, new SimpleFileVisitor<>() {
+                    @Override
+                    public FileVisitResult visitFile(Path file, 
BasicFileAttributes attrs) {
+                        if (found.get() || 
matcher.matches(fixedPath.relativize(file))) {
+                            found.set(true);
+                            return FileVisitResult.TERMINATE;
+                        }
+                        return FileVisitResult.CONTINUE;
+                    }
+                });
+                return found.get();
+            } catch (IOException e) {
+                throw new ProjectBuilderException(
+                        "Unable to verify file existence for '" + glob + "' 
inside '" + fixedPath + "'", e);
+            }
+        }
+        return true;
+    }
+
     private static List<String> unmodifiable(List<String> list) {
         return list != null ? Collections.unmodifiableList(list) : 
Collections.emptyList();
     }
diff --git 
a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultProfileSelector.java
 
b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultProfileSelector.java
index 36dc4a9fcd..3415de844e 100644
--- 
a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultProfileSelector.java
+++ 
b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultProfileSelector.java
@@ -20,7 +20,6 @@ package org.apache.maven.internal.impl.model;
 
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.HashSet;
 import java.util.List;
 
 import org.apache.maven.api.di.Inject;
@@ -64,16 +63,13 @@ public class DefaultProfileSelector implements 
ProfileSelector {
     @Override
     public List<Profile> getActiveProfiles(
             Collection<Profile> profiles, ProfileActivationContext context, 
ModelProblemCollector problems) {
-        Collection<String> activatedIds = new 
HashSet<>(context.getActiveProfileIds());
-        Collection<String> deactivatedIds = new 
HashSet<>(context.getInactiveProfileIds());
-
         List<Profile> activeProfiles = new ArrayList<>(profiles.size());
         List<Profile> activePomProfilesByDefault = new ArrayList<>();
         boolean activatedPomProfileNotByDefault = false;
 
         for (Profile profile : profiles) {
-            if (!deactivatedIds.contains(profile.getId())) {
-                if (activatedIds.contains(profile.getId()) || 
isActive(profile, context, problems)) {
+            if (!context.isProfileInactive(profile.getId())) {
+                if (context.isProfileActive(profile.getId()) || 
isActive(profile, context, problems)) {
                     activeProfiles.add(profile);
                     if (Profile.SOURCE_POM.equals(profile.getSource())) {
                         activatedPomProfileNotByDefault = true;
diff --git 
a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/model/ProfileActivationFilePathInterpolator.java
 
b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/model/ProfileActivationFilePathInterpolator.java
deleted file mode 100644
index d512b727ec..0000000000
--- 
a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/model/ProfileActivationFilePathInterpolator.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * 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.internal.impl.model;
-
-import java.nio.file.Path;
-
-import org.apache.maven.api.di.Inject;
-import org.apache.maven.api.di.Named;
-import org.apache.maven.api.di.Singleton;
-import org.apache.maven.api.model.ActivationFile;
-import org.apache.maven.api.services.Interpolator;
-import org.apache.maven.api.services.InterpolatorException;
-import org.apache.maven.api.services.model.PathTranslator;
-import org.apache.maven.api.services.model.ProfileActivationContext;
-import org.apache.maven.api.services.model.RootLocator;
-
-/**
- * Finds an absolute path for {@link ActivationFile#getExists()} or {@link 
ActivationFile#getMissing()}
- */
-@Named
-@Singleton
-public class ProfileActivationFilePathInterpolator {
-
-    private final PathTranslator pathTranslator;
-
-    private final RootLocator rootLocator;
-
-    private final Interpolator interpolator;
-
-    @Inject
-    public ProfileActivationFilePathInterpolator(
-            PathTranslator pathTranslator, RootLocator rootLocator, 
Interpolator interpolator) {
-        this.pathTranslator = pathTranslator;
-        this.rootLocator = rootLocator;
-        this.interpolator = interpolator;
-    }
-
-    /**
-     * Interpolates given {@code path}.
-     *
-     * @return absolute path or {@code null} if the input was {@code null}
-     */
-    public String interpolate(String path, ProfileActivationContext context) 
throws InterpolatorException {
-        if (path == null) {
-            return null;
-        }
-
-        Path basedir = context.getModel().getProjectDirectory();
-
-        String absolutePath = interpolator.interpolate(path, s -> {
-            if ("basedir".equals(s) || "project.basedir".equals(s)) {
-                return basedir != null ? basedir.toFile().getAbsolutePath() : 
null;
-            }
-            if ("project.rootDirectory".equals(s)) {
-                Path root = rootLocator.findMandatoryRoot(basedir);
-                return root.toFile().getAbsolutePath();
-            }
-            String r = context.getModel().getProperties().get(s);
-            if (r == null) {
-                r = context.getUserProperties().get(s);
-            }
-            if (r == null) {
-                r = context.getSystemProperties().get(s);
-            }
-            return r;
-        });
-
-        return pathTranslator.alignToBaseDirectory(absolutePath, basedir);
-    }
-}
diff --git 
a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/model/profile/ConditionFunctions.java
 
b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/model/profile/ConditionFunctions.java
index 82b4c4744c..c154f4b5ed 100644
--- 
a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/model/profile/ConditionFunctions.java
+++ 
b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/model/profile/ConditionFunctions.java
@@ -18,21 +18,12 @@
  */
 package org.apache.maven.internal.impl.model.profile;
 
-import java.io.File;
-import java.io.IOException;
-import java.nio.file.FileVisitResult;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.PathMatcher;
-import java.nio.file.Paths;
-import java.nio.file.SimpleFileVisitor;
-import java.nio.file.attribute.BasicFileAttributes;
 import java.util.List;
-import java.util.concurrent.atomic.AtomicBoolean;
 
+import org.apache.maven.api.services.InterpolatorException;
+import org.apache.maven.api.services.ModelBuilderException;
 import org.apache.maven.api.services.VersionParser;
 import org.apache.maven.api.services.model.ProfileActivationContext;
-import 
org.apache.maven.internal.impl.model.ProfileActivationFilePathInterpolator;
 
 import static 
org.apache.maven.internal.impl.model.profile.ConditionParser.toInt;
 
@@ -45,22 +36,16 @@ import static 
org.apache.maven.internal.impl.model.profile.ConditionParser.toInt
 public class ConditionFunctions {
     private final ProfileActivationContext context;
     private final VersionParser versionParser;
-    private final ProfileActivationFilePathInterpolator interpolator;
 
     /**
      * Constructs a new ConditionFunctions instance.
      *
      * @param context The profile activation context
      * @param versionParser The version parser for comparing versions
-     * @param interpolator The interpolator for resolving file paths
      */
-    public ConditionFunctions(
-            ProfileActivationContext context,
-            VersionParser versionParser,
-            ProfileActivationFilePathInterpolator interpolator) {
+    public ConditionFunctions(ProfileActivationContext context, VersionParser 
versionParser) {
         this.context = context;
         this.versionParser = versionParser;
-        this.interpolator = interpolator;
     }
 
     /**
@@ -207,75 +192,34 @@ public class ConditionFunctions {
      * Checks if a file or directory exists at the given path.
      *
      * @param args A list containing a single string argument representing the 
path
-     * @return true if the file or directory exists, false otherwise
+     * @return {@code true} if the file or directory exists, {@code false} 
otherwise
      * @throws IllegalArgumentException if the number of arguments is not 
exactly one
-     * @throws IOException if a problem occurs while walking the file system
+     * @throws ModelBuilderException if a problem occurs while walking the 
file system
+     * @throws InterpolatorException if an error occurs during interpolation
      */
-    public Object exists(List<Object> args) throws IOException {
+    public Object exists(List<Object> args) {
         if (args.size() != 1) {
             throw new IllegalArgumentException("exists function requires 
exactly one argument");
         }
         String path = ConditionParser.toString(args.get(0));
-        return fileExists(path);
+        return context.exists(path, true);
     }
 
     /**
      * Checks if a file or directory is missing at the given path.
      *
      * @param args A list containing a single string argument representing the 
path
-     * @return true if the file or directory does not exist, false otherwise
+     * @return {@code true} if the file or directory does not exist, {@code 
false} otherwise
      * @throws IllegalArgumentException if the number of arguments is not 
exactly one
-     * @throws IOException if a problem occurs while walking the file system
+     * @throws ModelBuilderException if a problem occurs while walking the 
file system
+     * @throws InterpolatorException if an error occurs during interpolation
      */
-    public Object missing(List<Object> args) throws IOException {
+    public Object missing(List<Object> args) {
         if (args.size() != 1) {
             throw new IllegalArgumentException("missing function requires 
exactly one argument");
         }
         String path = ConditionParser.toString(args.get(0));
-        return !fileExists(path);
-    }
-
-    private boolean fileExists(String path) throws IOException {
-        String pattern = interpolator.interpolate(path, context);
-        int asteriskIndex = pattern.indexOf('*');
-        int questionMarkIndex = pattern.indexOf('?');
-        int firstWildcardIndex = questionMarkIndex < 0
-                ? asteriskIndex
-                : asteriskIndex < 0 ? questionMarkIndex : 
Math.min(asteriskIndex, questionMarkIndex);
-        String fixed, glob;
-        if (firstWildcardIndex < 0) {
-            fixed = pattern;
-            glob = "";
-        } else {
-            int lastSep = pattern.substring(0, 
firstWildcardIndex).lastIndexOf(File.separatorChar);
-            if (lastSep < 0) {
-                fixed = "";
-                glob = pattern;
-            } else {
-                fixed = pattern.substring(0, lastSep);
-                glob = pattern.substring(lastSep + 1);
-            }
-        }
-        Path fixedPath = Paths.get(fixed);
-        if (!Files.exists(fixedPath)) {
-            return false;
-        }
-        if (!glob.isEmpty()) {
-            PathMatcher matcher = 
fixedPath.getFileSystem().getPathMatcher("glob:" + glob);
-            AtomicBoolean found = new AtomicBoolean(false);
-            Files.walkFileTree(fixedPath, new SimpleFileVisitor<>() {
-                @Override
-                public FileVisitResult visitFile(Path file, 
BasicFileAttributes attrs) {
-                    if (found.get() || 
matcher.matches(fixedPath.relativize(file))) {
-                        found.set(true);
-                        return FileVisitResult.TERMINATE;
-                    }
-                    return FileVisitResult.CONTINUE;
-                }
-            });
-            return found.get();
-        }
-        return true;
+        return !context.exists(path, true);
     }
 
     /**
diff --git 
a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/model/profile/ConditionProfileActivator.java
 
b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/model/profile/ConditionProfileActivator.java
index 3c0d4b040f..7070905a2e 100644
--- 
a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/model/profile/ConditionProfileActivator.java
+++ 
b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/model/profile/ConditionProfileActivator.java
@@ -18,7 +18,6 @@
  */
 package org.apache.maven.internal.impl.model.profile;
 
-import java.nio.file.Path;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.function.Function;
@@ -34,9 +33,7 @@ import org.apache.maven.api.services.ModelProblemCollector;
 import org.apache.maven.api.services.VersionParser;
 import org.apache.maven.api.services.model.ProfileActivationContext;
 import org.apache.maven.api.services.model.ProfileActivator;
-import org.apache.maven.api.services.model.RootLocator;
 import org.apache.maven.internal.impl.model.DefaultInterpolator;
-import 
org.apache.maven.internal.impl.model.ProfileActivationFilePathInterpolator;
 
 import static 
org.apache.maven.internal.impl.model.profile.ConditionParser.toBoolean;
 
@@ -49,22 +46,15 @@ import static 
org.apache.maven.internal.impl.model.profile.ConditionParser.toBoo
 public class ConditionProfileActivator implements ProfileActivator {
 
     private final VersionParser versionParser;
-    private final ProfileActivationFilePathInterpolator interpolator;
-    private final RootLocator rootLocator;
 
     /**
      * Constructs a new ConditionProfileActivator with the necessary 
dependencies.
      *
      * @param versionParser The parser for handling version comparisons
-     * @param interpolator The interpolator for resolving file paths
-     * @param rootLocator The locator for finding the project root directory
      */
     @Inject
-    public ConditionProfileActivator(
-            VersionParser versionParser, ProfileActivationFilePathInterpolator 
interpolator, RootLocator rootLocator) {
+    public ConditionProfileActivator(VersionParser versionParser) {
         this.versionParser = versionParser;
-        this.interpolator = interpolator;
-        this.rootLocator = rootLocator;
     }
 
     /**
@@ -82,9 +72,8 @@ public class ConditionProfileActivator implements 
ProfileActivator {
         }
         String condition = profile.getActivation().getCondition();
         try {
-            Map<String, ConditionParser.ExpressionFunction> functions =
-                    registerFunctions(context, versionParser, interpolator);
-            Function<String, String> propertyResolver = s -> property(context, 
rootLocator, s);
+            Map<String, ConditionParser.ExpressionFunction> functions = 
registerFunctions(context, versionParser);
+            Function<String, String> propertyResolver = s -> property(context, 
s);
             return toBoolean(new ConditionParser(functions, 
propertyResolver).parse(condition));
         } catch (Exception e) {
             problems.add(
@@ -115,16 +104,13 @@ public class ConditionProfileActivator implements 
ProfileActivator {
      *
      * @param context The profile activation context
      * @param versionParser The parser for handling version comparisons
-     * @param interpolator The interpolator for resolving file paths
      * @return A map of function names to their implementations
      */
     public static Map<String, ConditionParser.ExpressionFunction> 
registerFunctions(
-            ProfileActivationContext context,
-            VersionParser versionParser,
-            ProfileActivationFilePathInterpolator interpolator) {
+            ProfileActivationContext context, VersionParser versionParser) {
         Map<String, ConditionParser.ExpressionFunction> functions = new 
HashMap<>();
 
-        ConditionFunctions conditionFunctions = new 
ConditionFunctions(context, versionParser, interpolator);
+        ConditionFunctions conditionFunctions = new 
ConditionFunctions(context, versionParser);
 
         for (java.lang.reflect.Method method : 
ConditionFunctions.class.getDeclaredMethods()) {
             String methodName = method.getName();
@@ -170,43 +156,37 @@ public class ConditionProfileActivator implements 
ProfileActivator {
      * @return The value of the property, or null if not found
      * @throws IllegalArgumentException if the number of arguments is not 
exactly one
      */
-    static String property(ProfileActivationContext context, RootLocator 
rootLocator, String name) {
-        String value = doGetProperty(context, rootLocator, name);
-        return new DefaultInterpolator().interpolate(value, s -> 
doGetProperty(context, rootLocator, s));
+    static String property(ProfileActivationContext context, String name) {
+        String value = doGetProperty(context, name);
+        return new DefaultInterpolator().interpolate(value, s -> 
doGetProperty(context, s));
     }
 
-    static String doGetProperty(ProfileActivationContext context, RootLocator 
rootLocator, String name) {
+    static String doGetProperty(ProfileActivationContext context, String name) 
{
         // Handle special project-related properties
         if ("project.basedir".equals(name)) {
-            Path basedir = context.getModel().getProjectDirectory();
-            return basedir != null ? basedir.toFile().getAbsolutePath() : null;
+            return context.getModelBaseDirectory();
         }
         if ("project.rootDirectory".equals(name)) {
-            Path basedir = context.getModel().getProjectDirectory();
-            if (basedir != null) {
-                Path root = rootLocator.findMandatoryRoot(basedir);
-                return root.toFile().getAbsolutePath();
-            }
-            return null;
+            return context.getModelRootDirectory();
         }
         if ("project.artifactId".equals(name)) {
-            return context.getModel().getArtifactId();
+            return context.getModelArtifactId();
         }
         if ("project.packaging".equals(name)) {
-            return context.getModel().getPackaging();
+            return context.getModelPackaging();
         }
 
         // Check user properties
-        String v = context.getUserProperties().get(name);
+        String v = context.getUserProperty(name);
         if (v == null) {
             // Check project properties
             // TODO: this may leads to instability between file model 
activation and effective model activation
             //       as the effective model properties may be different from 
the file model
-            v = context.getModel().getProperties().get(name);
+            v = context.getModelProperty(name);
         }
         if (v == null) {
             // Check system properties
-            v = context.getSystemProperties().get(name);
+            v = context.getSystemProperty(name);
         }
         return v;
     }
diff --git 
a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/model/profile/FileProfileActivator.java
 
b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/model/profile/FileProfileActivator.java
index b0824a168a..77abd86307 100644
--- 
a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/model/profile/FileProfileActivator.java
+++ 
b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/model/profile/FileProfileActivator.java
@@ -18,21 +18,17 @@
  */
 package org.apache.maven.internal.impl.model.profile;
 
-import java.io.File;
-
-import org.apache.maven.api.di.Inject;
 import org.apache.maven.api.di.Named;
 import org.apache.maven.api.di.Singleton;
 import org.apache.maven.api.model.Activation;
 import org.apache.maven.api.model.ActivationFile;
 import org.apache.maven.api.model.Profile;
 import org.apache.maven.api.services.BuilderProblem;
-import org.apache.maven.api.services.InterpolatorException;
+import org.apache.maven.api.services.MavenException;
 import org.apache.maven.api.services.ModelProblem;
 import org.apache.maven.api.services.ModelProblemCollector;
 import org.apache.maven.api.services.model.ProfileActivationContext;
 import org.apache.maven.api.services.model.ProfileActivator;
-import 
org.apache.maven.internal.impl.model.ProfileActivationFilePathInterpolator;
 
 /**
  * Determines profile activation based on the existence/absence of some file.
@@ -46,13 +42,6 @@ import 
org.apache.maven.internal.impl.model.ProfileActivationFilePathInterpolato
 @Singleton
 public class FileProfileActivator implements ProfileActivator {
 
-    private final ProfileActivationFilePathInterpolator 
profileActivationFilePathInterpolator;
-
-    @Inject
-    public FileProfileActivator(ProfileActivationFilePathInterpolator 
profileActivationFilePathInterpolator) {
-        this.profileActivationFilePathInterpolator = 
profileActivationFilePathInterpolator;
-    }
-
     @Override
     public boolean isActive(Profile profile, ProfileActivationContext context, 
ModelProblemCollector problems) {
         Activation activation = profile.getActivation();
@@ -92,31 +81,20 @@ public class FileProfileActivator implements 
ProfileActivator {
             return false;
         }
 
+        boolean fileExists;
         try {
-            path = profileActivationFilePathInterpolator.interpolate(path, 
context);
-        } catch (InterpolatorException e) {
+            fileExists = context.exists(path, false);
+        } catch (MavenException e) {
             problems.add(
                     BuilderProblem.Severity.ERROR,
                     ModelProblem.Version.BASE,
-                    "Failed to interpolate file location " + path + " for 
profile " + profile.getId() + ": "
+                    "Failed to check file existence " + path + " for profile " 
+ profile.getId() + ": "
                             + e.getMessage(),
                     file.getLocation(missing ? "missing" : "exists"),
                     e);
             return false;
         }
 
-        if (path == null) {
-            return false;
-        }
-
-        File f = new File(path);
-
-        if (!f.isAbsolute()) {
-            return false;
-        }
-
-        boolean fileExists = f.exists();
-
         return missing != fileExists;
     }
 
diff --git 
a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/model/profile/JdkVersionProfileActivator.java
 
b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/model/profile/JdkVersionProfileActivator.java
index 2a417a3bc6..b3f95fa750 100644
--- 
a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/model/profile/JdkVersionProfileActivator.java
+++ 
b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/model/profile/JdkVersionProfileActivator.java
@@ -60,7 +60,7 @@ public class JdkVersionProfileActivator implements 
ProfileActivator {
             return false;
         }
 
-        String version = context.getSystemProperties().get("java.version");
+        String version = context.getSystemProperty("java.version");
 
         if (version == null || version.isEmpty()) {
             problems.add(
diff --git 
a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/model/profile/OperatingSystemProfileActivator.java
 
b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/model/profile/OperatingSystemProfileActivator.java
index 1d03e26ae2..3d6af38356 100644
--- 
a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/model/profile/OperatingSystemProfileActivator.java
+++ 
b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/model/profile/OperatingSystemProfileActivator.java
@@ -56,15 +56,10 @@ public class OperatingSystemProfileActivator implements 
ProfileActivator {
 
         boolean active = ensureAtLeastOneNonNull(os);
 
-        String actualOsName = context.getSystemProperties()
-                .getOrDefault("os.name", Os.OS_NAME)
-                .toLowerCase(Locale.ENGLISH);
-        String actualOsArch = context.getSystemProperties()
-                .getOrDefault("os.arch", Os.OS_ARCH)
-                .toLowerCase(Locale.ENGLISH);
-        String actualOsVersion = context.getSystemProperties()
-                .getOrDefault("os.version", Os.OS_VERSION)
-                .toLowerCase(Locale.ENGLISH);
+        String actualOsName = getSystemProperty(context, "os.name", 
Os.OS_NAME).toLowerCase(Locale.ENGLISH);
+        String actualOsArch = getSystemProperty(context, "os.arch", 
Os.OS_ARCH).toLowerCase(Locale.ENGLISH);
+        String actualOsVersion =
+                getSystemProperty(context, "os.version", 
Os.OS_VERSION).toLowerCase(Locale.ENGLISH);
 
         if (active && os.getFamily() != null) {
             active = determineFamilyMatch(os.getFamily(), actualOsName);
@@ -82,6 +77,11 @@ public class OperatingSystemProfileActivator implements 
ProfileActivator {
         return active;
     }
 
+    private String getSystemProperty(ProfileActivationContext context, String 
key, String defValue) {
+        String val = context.getSystemProperty(key);
+        return val != null ? val : defValue;
+    }
+
     @Override
     public boolean presentInConfig(Profile profile, ProfileActivationContext 
context, ModelProblemCollector problems) {
         Activation activation = profile.getActivation();
diff --git 
a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/model/profile/PackagingProfileActivator.java
 
b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/model/profile/PackagingProfileActivator.java
index 65a508355d..f291f9240e 100644
--- 
a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/model/profile/PackagingProfileActivator.java
+++ 
b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/model/profile/PackagingProfileActivator.java
@@ -47,7 +47,7 @@ public class PackagingProfileActivator implements 
ProfileActivator {
     }
 
     private static boolean isPackaging(ProfileActivationContext context, 
String p) {
-        String packaging = context.getModel().getPackaging();
+        String packaging = context.getModelPackaging();
         return Objects.equals(p, packaging);
     }
 
diff --git 
a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/model/profile/PropertyProfileActivator.java
 
b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/model/profile/PropertyProfileActivator.java
index d36ecda8f9..6ac5e0d3b6 100644
--- 
a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/model/profile/PropertyProfileActivator.java
+++ 
b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/model/profile/PropertyProfileActivator.java
@@ -69,12 +69,12 @@ public class PropertyProfileActivator implements 
ProfileActivator {
             return false;
         }
 
-        String sysValue = context.getUserProperties().get(name);
+        String sysValue = context.getUserProperty(name);
         if (sysValue == null && "packaging".equals(name)) {
-            sysValue = context.getModel().getPackaging();
+            sysValue = context.getModelPackaging();
         }
         if (sysValue == null) {
-            sysValue = context.getSystemProperties().get(name);
+            sysValue = context.getSystemProperty(name);
         }
 
         String propValue = property.getValue();
diff --git 
a/impl/maven-impl/src/test/java/org/apache/maven/internal/impl/model/DefaultModelBuilderTest.java
 
b/impl/maven-impl/src/test/java/org/apache/maven/internal/impl/model/DefaultModelBuilderTest.java
new file mode 100644
index 0000000000..cf06e09248
--- /dev/null
+++ 
b/impl/maven-impl/src/test/java/org/apache/maven/internal/impl/model/DefaultModelBuilderTest.java
@@ -0,0 +1,66 @@
+/*
+ * 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.internal.impl.model;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import org.apache.maven.api.Session;
+import org.apache.maven.api.services.ModelBuilder;
+import org.apache.maven.api.services.ModelBuilderRequest;
+import org.apache.maven.api.services.ModelBuilderResult;
+import org.apache.maven.api.services.ModelSource;
+import org.apache.maven.internal.impl.standalone.ApiRunner;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+/**
+ *
+ */
+class DefaultModelBuilderTest {
+
+    Session session;
+    ModelBuilder builder;
+
+    @BeforeEach
+    void setup() {
+        session = ApiRunner.createSession();
+        builder = session.getService(ModelBuilder.class);
+        assertNotNull(builder);
+    }
+
+    @Test
+    public void testPropertiesAndProfiles() {
+        ModelBuilderRequest request = ModelBuilderRequest.builder()
+                .session(session)
+                .requestType(ModelBuilderRequest.RequestType.BUILD_PROJECT)
+                .source(ModelSource.fromPath(getPom("props-and-profiles")))
+                .build();
+        ModelBuilderResult result = builder.newSession().build(request);
+        assertNotNull(result);
+        assertEquals("21", 
result.getEffectiveModel().getProperties().get("maven.compiler.release"));
+    }
+
+    private Path getPom(String name) {
+        return Paths.get("src/test/resources/poms/factory/" + name + 
".xml").toAbsolutePath();
+    }
+}
diff --git 
a/impl/maven-impl/src/test/java/org/apache/maven/internal/impl/model/profile/AbstractProfileActivatorTest.java
 
b/impl/maven-impl/src/test/java/org/apache/maven/internal/impl/model/profile/AbstractProfileActivatorTest.java
index 6e40ef8f10..a55022c183 100644
--- 
a/impl/maven-impl/src/test/java/org/apache/maven/internal/impl/model/profile/AbstractProfileActivatorTest.java
+++ 
b/impl/maven-impl/src/test/java/org/apache/maven/internal/impl/model/profile/AbstractProfileActivatorTest.java
@@ -25,6 +25,8 @@ import org.apache.maven.api.model.Model;
 import org.apache.maven.api.model.Profile;
 import org.apache.maven.api.services.model.ProfileActivationContext;
 import org.apache.maven.api.services.model.ProfileActivator;
+import org.apache.maven.internal.impl.model.DefaultInterpolator;
+import org.apache.maven.internal.impl.model.DefaultPathTranslator;
 import org.apache.maven.internal.impl.model.DefaultProfileActivationContext;
 import org.apache.maven.internal.impl.model.rootlocator.DefaultRootLocator;
 import org.junit.jupiter.api.AfterEach;
@@ -55,7 +57,8 @@ public abstract class AbstractProfileActivatorTest<T extends 
ProfileActivator> {
     }
 
     protected DefaultProfileActivationContext newContext() {
-        return new DefaultProfileActivationContext();
+        return new DefaultProfileActivationContext(
+                new DefaultPathTranslator(), new FakeRootLocator(), new 
DefaultInterpolator());
     }
 
     protected ProfileActivationContext newContext(
diff --git 
a/impl/maven-impl/src/test/java/org/apache/maven/internal/impl/model/profile/ConditionParserTest.java
 
b/impl/maven-impl/src/test/java/org/apache/maven/internal/impl/model/profile/ConditionParserTest.java
index fe6081d28a..c2887d96f7 100644
--- 
a/impl/maven-impl/src/test/java/org/apache/maven/internal/impl/model/profile/ConditionParserTest.java
+++ 
b/impl/maven-impl/src/test/java/org/apache/maven/internal/impl/model/profile/ConditionParserTest.java
@@ -28,7 +28,6 @@ import org.apache.maven.internal.impl.DefaultVersionParser;
 import org.apache.maven.internal.impl.model.DefaultInterpolator;
 import org.apache.maven.internal.impl.model.DefaultPathTranslator;
 import org.apache.maven.internal.impl.model.DefaultProfileActivationContext;
-import 
org.apache.maven.internal.impl.model.ProfileActivationFilePathInterpolator;
 import 
org.apache.maven.internal.impl.model.profile.ConditionParser.ExpressionFunction;
 import org.apache.maven.internal.impl.model.rootlocator.DefaultRootLocator;
 import org.eclipse.aether.util.version.GenericVersionScheme;
@@ -51,19 +50,18 @@ class ConditionParserTest {
         ProfileActivationContext context = createMockContext();
         DefaultVersionParser versionParser =
                 new DefaultVersionParser(new DefaultModelVersionParser(new 
GenericVersionScheme()));
-        ProfileActivationFilePathInterpolator interpolator = new 
ProfileActivationFilePathInterpolator(
-                new DefaultPathTranslator(),
-                new AbstractProfileActivatorTest.FakeRootLocator(),
-                new DefaultInterpolator());
         DefaultRootLocator rootLocator = new DefaultRootLocator();
 
-        functions = ConditionProfileActivator.registerFunctions(context, 
versionParser, interpolator);
-        propertyResolver = s -> ConditionProfileActivator.property(context, 
rootLocator, s);
+        functions = ConditionProfileActivator.registerFunctions(context, 
versionParser);
+        propertyResolver = s -> ConditionProfileActivator.property(context, s);
         parser = new ConditionParser(functions, propertyResolver);
     }
 
     private ProfileActivationContext createMockContext() {
-        DefaultProfileActivationContext context = new 
DefaultProfileActivationContext();
+        DefaultProfileActivationContext context = new 
DefaultProfileActivationContext(
+                new DefaultPathTranslator(),
+                new AbstractProfileActivatorTest.FakeRootLocator(),
+                new DefaultInterpolator());
         context.setSystemProperties(Map.of(
                 "os.name", "windows",
                 "os.arch", "amd64",
diff --git 
a/impl/maven-impl/src/test/java/org/apache/maven/internal/impl/model/profile/ConditionProfileActivatorTest.java
 
b/impl/maven-impl/src/test/java/org/apache/maven/internal/impl/model/profile/ConditionProfileActivatorTest.java
index 71cd0228de..6dd31e293f 100644
--- 
a/impl/maven-impl/src/test/java/org/apache/maven/internal/impl/model/profile/ConditionProfileActivatorTest.java
+++ 
b/impl/maven-impl/src/test/java/org/apache/maven/internal/impl/model/profile/ConditionProfileActivatorTest.java
@@ -33,8 +33,6 @@ import org.apache.maven.internal.impl.DefaultVersionParser;
 import org.apache.maven.internal.impl.model.DefaultInterpolator;
 import org.apache.maven.internal.impl.model.DefaultPathTranslator;
 import org.apache.maven.internal.impl.model.DefaultProfileActivationContext;
-import 
org.apache.maven.internal.impl.model.ProfileActivationFilePathInterpolator;
-import org.apache.maven.internal.impl.model.rootlocator.DefaultRootLocator;
 import org.eclipse.aether.util.version.GenericVersionScheme;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Disabled;
@@ -55,10 +53,7 @@ public class ConditionProfileActivatorTest extends 
AbstractProfileActivatorTest<
     @Override
     void setUp() throws Exception {
         activator = new ConditionProfileActivator(
-                new DefaultVersionParser(new DefaultModelVersionParser(new 
GenericVersionScheme())),
-                new ProfileActivationFilePathInterpolator(
-                        new DefaultPathTranslator(), new FakeRootLocator(), 
new DefaultInterpolator()),
-                new DefaultRootLocator());
+                new DefaultVersionParser(new DefaultModelVersionParser(new 
GenericVersionScheme())));
 
         Path file = tempDir.resolve("file.txt");
         Files.createFile(file);
@@ -484,7 +479,9 @@ public class ConditionProfileActivatorTest extends 
AbstractProfileActivatorTest<
     }
 
     protected ProfileActivationContext newFileContext(Path path) {
-        DefaultProfileActivationContext context = new 
DefaultProfileActivationContext();
+        DefaultProfileActivationContext context = new 
DefaultProfileActivationContext(
+                new DefaultPathTranslator(), new FakeRootLocator(), new 
DefaultInterpolator());
+
         
context.setModel(Model.newBuilder().pomFile(path.resolve("pom.xml")).build());
         return context;
     }
diff --git 
a/impl/maven-impl/src/test/java/org/apache/maven/internal/impl/model/profile/FileProfileActivatorTest.java
 
b/impl/maven-impl/src/test/java/org/apache/maven/internal/impl/model/profile/FileProfileActivatorTest.java
index bde150d74e..955bb436f4 100644
--- 
a/impl/maven-impl/src/test/java/org/apache/maven/internal/impl/model/profile/FileProfileActivatorTest.java
+++ 
b/impl/maven-impl/src/test/java/org/apache/maven/internal/impl/model/profile/FileProfileActivatorTest.java
@@ -27,10 +27,7 @@ import org.apache.maven.api.model.ActivationFile;
 import org.apache.maven.api.model.Model;
 import org.apache.maven.api.model.Profile;
 import org.apache.maven.api.services.model.RootLocator;
-import org.apache.maven.internal.impl.model.DefaultInterpolator;
-import org.apache.maven.internal.impl.model.DefaultPathTranslator;
 import org.apache.maven.internal.impl.model.DefaultProfileActivationContext;
-import 
org.apache.maven.internal.impl.model.ProfileActivationFilePathInterpolator;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.io.TempDir;
@@ -47,13 +44,12 @@ class FileProfileActivatorTest extends 
AbstractProfileActivatorTest<FileProfileA
     @TempDir
     Path tempDir;
 
-    private final DefaultProfileActivationContext context = new 
DefaultProfileActivationContext();
+    private final DefaultProfileActivationContext context = newContext();
 
     @BeforeEach
     @Override
     void setUp() throws Exception {
-        activator = new FileProfileActivator(new 
ProfileActivationFilePathInterpolator(
-                new DefaultPathTranslator(), new FakeRootLocator(), new 
DefaultInterpolator()));
+        activator = new FileProfileActivator();
 
         
context.setModel(Model.newBuilder().pomFile(tempDir.resolve("pom.xml")).build());
 
diff --git 
a/impl/maven-impl/src/test/java/org/apache/maven/internal/impl/standalone/RepositorySystemSupplier.java
 
b/impl/maven-impl/src/test/java/org/apache/maven/internal/impl/standalone/RepositorySystemSupplier.java
index 597cf9a76d..11c3083bc4 100644
--- 
a/impl/maven-impl/src/test/java/org/apache/maven/internal/impl/standalone/RepositorySystemSupplier.java
+++ 
b/impl/maven-impl/src/test/java/org/apache/maven/internal/impl/standalone/RepositorySystemSupplier.java
@@ -47,7 +47,6 @@ import 
org.apache.maven.internal.impl.model.DefaultPathTranslator;
 import org.apache.maven.internal.impl.model.DefaultPluginManagementInjector;
 import org.apache.maven.internal.impl.model.DefaultProfileInjector;
 import org.apache.maven.internal.impl.model.DefaultProfileSelector;
-import 
org.apache.maven.internal.impl.model.ProfileActivationFilePathInterpolator;
 import org.apache.maven.internal.impl.model.rootlocator.DefaultRootLocator;
 import org.apache.maven.internal.impl.resolver.DefaultArtifactDescriptorReader;
 import org.apache.maven.internal.impl.resolver.DefaultModelResolver;
@@ -1058,13 +1057,13 @@ public class RepositorySystemSupplier implements 
Supplier<RepositorySystem> {
                 new DefaultDependencyManagementInjector(),
                 new DefaultDependencyManagementImporter(),
                 new DefaultPluginConfigurationExpander(),
-                new ProfileActivationFilePathInterpolator(
-                        new DefaultPathTranslator(), new DefaultRootLocator(), 
new DefaultInterpolator()),
                 new DefaultModelVersionParser(getVersionScheme()),
                 List.of(),
                 new DefaultModelCacheFactory(),
                 new DefaultModelResolver(),
-                new DefaultInterpolator());
+                new DefaultInterpolator(),
+                new DefaultPathTranslator(),
+                new DefaultRootLocator());
     }
 
     private RepositorySystem repositorySystem;
diff --git 
a/impl/maven-impl/src/test/resources/poms/factory/props-and-profiles-grand-parent.xml
 
b/impl/maven-impl/src/test/resources/poms/factory/props-and-profiles-grand-parent.xml
new file mode 100644
index 0000000000..2623069658
--- /dev/null
+++ 
b/impl/maven-impl/src/test/resources/poms/factory/props-and-profiles-grand-parent.xml
@@ -0,0 +1,34 @@
+<?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.1.0";>
+    <groupId>org.apache.maven.tests</groupId>
+    <artifactId>props-and-profiles-grand-parent</artifactId>
+    <version>1.0-SNAPSHOT</version>
+    <packaging>pom</packaging>
+    <profiles>
+        <profile>
+            <id>jdk9+</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <properties>
+                <maven.compiler.release>17</maven.compiler.release>
+            </properties>
+        </profile>
+    </profiles>
+</project>
diff --git 
a/impl/maven-impl/src/test/resources/poms/factory/props-and-profiles-parent.xml 
b/impl/maven-impl/src/test/resources/poms/factory/props-and-profiles-parent.xml
new file mode 100644
index 0000000000..00ffc6d0c9
--- /dev/null
+++ 
b/impl/maven-impl/src/test/resources/poms/factory/props-and-profiles-parent.xml
@@ -0,0 +1,28 @@
+<?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.1.0";>
+    <parent>
+        <relativePath>props-and-profiles-grand-parent.xml</relativePath>
+    </parent>
+    <artifactId>props-and-profiles</artifactId>
+    <version>1.0-SNAPSHOT</version>
+    <packaging>pom</packaging>
+    <properties>
+        <maven.compiler.release>21</maven.compiler.release>
+    </properties>
+</project>
diff --git 
a/impl/maven-impl/src/test/resources/poms/factory/props-and-profiles.xml 
b/impl/maven-impl/src/test/resources/poms/factory/props-and-profiles.xml
new file mode 100644
index 0000000000..02fb63cfd0
--- /dev/null
+++ b/impl/maven-impl/src/test/resources/poms/factory/props-and-profiles.xml
@@ -0,0 +1,24 @@
+<?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.1.0";>
+    <parent>
+        <relativePath>props-and-profiles-parent.xml</relativePath>
+    </parent>
+    <artifactId>props-and-profiles</artifactId>
+    <version>1.0-SNAPSHOT</version>
+</project>

Reply via email to