Skip to content

Commit c1c114d

Browse files
mbensongnodet
andauthored
[MNG-8081] Interpolate available properties during default profile selection (Maven 3.9.x) (#1447)
Co-authored-by: Guillaume Nodet <[email protected]>
1 parent b4cbda8 commit c1c114d

File tree

7 files changed

+388
-69
lines changed

7 files changed

+388
-69
lines changed

maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilder.java

+75-32
Original file line numberDiff line numberDiff line change
@@ -26,23 +26,28 @@
2626
import java.io.IOException;
2727
import java.util.ArrayList;
2828
import java.util.Collection;
29+
import java.util.Collections;
2930
import java.util.HashMap;
3031
import java.util.Iterator;
3132
import java.util.LinkedHashSet;
3233
import java.util.List;
3334
import java.util.Map;
3435
import java.util.Objects;
36+
import java.util.Optional;
3537
import java.util.Properties;
38+
import java.util.Set;
39+
import java.util.function.Consumer;
40+
import java.util.stream.Collectors;
3641

3742
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
3843
import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
3944
import org.apache.maven.artifact.versioning.VersionRange;
4045
import org.apache.maven.model.Activation;
41-
import org.apache.maven.model.ActivationFile;
4246
import org.apache.maven.model.Build;
4347
import org.apache.maven.model.Dependency;
4448
import org.apache.maven.model.DependencyManagement;
4549
import org.apache.maven.model.InputLocation;
50+
import org.apache.maven.model.InputLocationTracker;
4651
import org.apache.maven.model.InputSource;
4752
import org.apache.maven.model.Model;
4853
import org.apache.maven.model.Parent;
@@ -78,6 +83,7 @@
7883
import org.apache.maven.model.validation.ModelValidator;
7984
import org.codehaus.plexus.interpolation.InterpolationException;
8085
import org.codehaus.plexus.interpolation.MapBasedValueSource;
86+
import org.codehaus.plexus.interpolation.RegexBasedInterpolator;
8187
import org.codehaus.plexus.interpolation.StringSearchInterpolator;
8288
import org.codehaus.plexus.util.StringUtils;
8389
import org.eclipse.sisu.Nullable;
@@ -299,14 +305,19 @@ protected ModelBuildingResult build(ModelBuildingRequest request, Collection<Str
299305

300306
profileActivationContext.setProjectProperties(tmpModel.getProperties());
301307

302-
List<Profile> activePomProfiles =
303-
profileSelector.getActiveProfiles(rawModel.getProfiles(), profileActivationContext, problems);
304-
currentData.setActiveProfiles(activePomProfiles);
305-
306308
Map<String, Activation> interpolatedActivations =
307309
getInterpolatedActivations(rawModel, profileActivationContext, problems);
308310
injectProfileActivations(tmpModel, interpolatedActivations);
309311

312+
List<Profile> activePomProfiles =
313+
profileSelector.getActiveProfiles(tmpModel.getProfiles(), profileActivationContext, problems);
314+
315+
Set<String> activeProfileIds =
316+
activePomProfiles.stream().map(Profile::getId).collect(Collectors.toSet());
317+
currentData.setActiveProfiles(rawModel.getProfiles().stream()
318+
.filter(p -> activeProfileIds.contains(p.getId()))
319+
.collect(Collectors.toList()));
320+
310321
// profile injection
311322
for (Profile activeProfile : activePomProfiles) {
312323
profileInjector.injectProfile(tmpModel, activeProfile, request, problems);
@@ -413,40 +424,72 @@ protected ModelBuildingResult build(ModelBuildingRequest request, Collection<Str
413424
return result;
414425
}
415426

427+
@FunctionalInterface
428+
private interface InterpolateString {
429+
String apply(String s) throws InterpolationException;
430+
}
431+
416432
private Map<String, Activation> getInterpolatedActivations(
417433
Model rawModel, DefaultProfileActivationContext context, DefaultModelProblemCollector problems) {
418434
Map<String, Activation> interpolatedActivations = getProfileActivations(rawModel, true);
419-
for (Activation activation : interpolatedActivations.values()) {
420-
if (activation.getFile() != null) {
421-
replaceWithInterpolatedValue(activation.getFile(), context, problems);
422-
}
435+
436+
if (interpolatedActivations.isEmpty()) {
437+
return Collections.emptyMap();
423438
}
424-
return interpolatedActivations;
425-
}
439+
RegexBasedInterpolator interpolator = new RegexBasedInterpolator();
426440

427-
private void replaceWithInterpolatedValue(
428-
ActivationFile activationFile, ProfileActivationContext context, DefaultModelProblemCollector problems) {
429-
try {
430-
if (StringUtils.isNotEmpty(activationFile.getExists())) {
431-
String path = activationFile.getExists();
432-
String absolutePath = profileActivationFilePathInterpolator.interpolate(path, context);
433-
activationFile.setExists(absolutePath);
434-
} else if (StringUtils.isNotEmpty(activationFile.getMissing())) {
435-
String path = activationFile.getMissing();
436-
String absolutePath = profileActivationFilePathInterpolator.interpolate(path, context);
437-
activationFile.setMissing(absolutePath);
441+
interpolator.addValueSource(new MapBasedValueSource(context.getProjectProperties()));
442+
interpolator.addValueSource(new MapBasedValueSource(context.getUserProperties()));
443+
interpolator.addValueSource(new MapBasedValueSource(context.getSystemProperties()));
444+
445+
class Interpolation {
446+
final InputLocationTracker target;
447+
448+
final InterpolateString impl;
449+
450+
Interpolation(InputLocationTracker target, InterpolateString impl) {
451+
this.target = target;
452+
this.impl = impl;
453+
}
454+
455+
void performFor(String value, String locationKey, Consumer<String> mutator) {
456+
if (StringUtils.isEmpty(value)) {
457+
return;
458+
}
459+
try {
460+
mutator.accept(impl.apply(value));
461+
} catch (InterpolationException e) {
462+
problems.add(new ModelProblemCollectorRequest(Severity.ERROR, Version.BASE)
463+
.setMessage("Failed to interpolate value " + value + ": " + e.getMessage())
464+
.setLocation(target.getLocation(locationKey))
465+
.setException(e));
466+
}
438467
}
439-
} catch (InterpolationException e) {
440-
String path = StringUtils.isNotEmpty(activationFile.getExists())
441-
? activationFile.getExists()
442-
: activationFile.getMissing();
443-
444-
problems.add(new ModelProblemCollectorRequest(Severity.ERROR, Version.BASE)
445-
.setMessage("Failed to interpolate file location " + path + ": " + e.getMessage())
446-
.setLocation(activationFile.getLocation(
447-
StringUtils.isNotEmpty(activationFile.getExists()) ? "exists" : "missing"))
448-
.setException(e));
449468
}
469+
for (Activation activation : interpolatedActivations.values()) {
470+
Optional<Activation> a = Optional.of(activation);
471+
a.map(Activation::getFile).ifPresent(fa -> {
472+
Interpolation nt =
473+
new Interpolation(fa, s -> profileActivationFilePathInterpolator.interpolate(s, context));
474+
nt.performFor(fa.getExists(), "exists", fa::setExists);
475+
nt.performFor(fa.getMissing(), "missing", fa::setMissing);
476+
});
477+
a.map(Activation::getOs).ifPresent(oa -> {
478+
Interpolation nt = new Interpolation(oa, interpolator::interpolate);
479+
nt.performFor(oa.getArch(), "arch", oa::setArch);
480+
nt.performFor(oa.getFamily(), "family", oa::setFamily);
481+
nt.performFor(oa.getName(), "name", oa::setName);
482+
nt.performFor(oa.getVersion(), "version", oa::setVersion);
483+
});
484+
a.map(Activation::getProperty).ifPresent(pa -> {
485+
Interpolation nt = new Interpolation(pa, interpolator::interpolate);
486+
nt.performFor(pa.getName(), "name", pa::setName);
487+
nt.performFor(pa.getValue(), "value", pa::setValue);
488+
});
489+
a.map(Activation::getJdk).ifPresent(ja -> new Interpolation(activation, interpolator::interpolate)
490+
.performFor(ja, "jdk", activation::setJdk));
491+
}
492+
return interpolatedActivations;
450493
}
451494

452495
@Override

maven-model-builder/src/main/java/org/apache/maven/model/validation/DefaultModelValidator.java

+87-25
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,24 @@
2424

2525
import java.io.File;
2626
import java.util.Arrays;
27+
import java.util.Deque;
2728
import java.util.HashMap;
2829
import java.util.HashSet;
30+
import java.util.Iterator;
31+
import java.util.LinkedList;
2932
import java.util.List;
3033
import java.util.Map;
3134
import java.util.Objects;
35+
import java.util.Optional;
3236
import java.util.Set;
37+
import java.util.function.Consumer;
38+
import java.util.function.Supplier;
3339
import java.util.regex.Matcher;
3440
import java.util.regex.Pattern;
41+
import java.util.stream.Collectors;
42+
import java.util.stream.StreamSupport;
3543

3644
import org.apache.maven.model.Activation;
37-
import org.apache.maven.model.ActivationFile;
3845
import org.apache.maven.model.Build;
3946
import org.apache.maven.model.BuildBase;
4047
import org.apache.maven.model.Dependency;
@@ -236,42 +243,97 @@ public void validateRawModel(Model m, ModelBuildingRequest request, ModelProblem
236243
}
237244

238245
private void validate30RawProfileActivation(ModelProblemCollector problems, Activation activation, String prefix) {
239-
if (activation == null || activation.getFile() == null) {
246+
if (activation == null) {
240247
return;
241248
}
249+
class ActivationFrame {
250+
String location;
251+
Optional<? extends InputLocationTracker> parent;
242252

243-
ActivationFile file = activation.getFile();
244-
245-
String path;
246-
String location;
247-
248-
if (file.getExists() != null && !file.getExists().isEmpty()) {
249-
path = file.getExists();
250-
location = "exists";
251-
} else if (file.getMissing() != null && !file.getMissing().isEmpty()) {
252-
path = file.getMissing();
253-
location = "missing";
254-
} else {
255-
return;
253+
ActivationFrame(String location, Optional<? extends InputLocationTracker> parent) {
254+
this.location = location;
255+
this.parent = parent;
256+
}
256257
}
257-
258-
if (hasProjectExpression(path)) {
259-
Matcher matcher = EXPRESSION_PROJECT_NAME_PATTERN.matcher(path);
260-
while (matcher.find()) {
261-
String propertyName = matcher.group(0);
262-
if (!"${project.basedir}".equals(propertyName)) {
258+
final Deque<ActivationFrame> stk = new LinkedList<>();
259+
260+
final Supplier<String> pathSupplier = () -> {
261+
final boolean parallel = false;
262+
return StreamSupport.stream(((Iterable<ActivationFrame>) stk::descendingIterator).spliterator(), parallel)
263+
.map(f -> f.location)
264+
.collect(Collectors.joining("."));
265+
};
266+
final Supplier<InputLocation> locationSupplier = () -> {
267+
if (stk.size() < 2) {
268+
return null;
269+
}
270+
Iterator<ActivationFrame> f = stk.iterator();
271+
272+
String location = f.next().location;
273+
ActivationFrame parent = f.next();
274+
275+
return parent.parent.map(p -> p.getLocation(location)).orElse(null);
276+
};
277+
final Consumer<String> validator = s -> {
278+
if (hasProjectExpression(s)) {
279+
String path = pathSupplier.get();
280+
Matcher matcher = EXPRESSION_PROJECT_NAME_PATTERN.matcher(s);
281+
while (matcher.find()) {
282+
String propertyName = matcher.group(0);
283+
284+
if (path.startsWith("activation.file.") && "${project.basedir}".equals(propertyName)) {
285+
continue;
286+
}
263287
addViolation(
264288
problems,
265289
Severity.WARNING,
266290
Version.V30,
267-
prefix + "activation.file." + location,
291+
prefix + path,
268292
null,
269-
"Failed to interpolate file location " + path + ": " + propertyName
293+
"Failed to interpolate profile activation property " + s + ": " + propertyName
270294
+ " expressions are not supported during profile activation.",
271-
file.getLocation(location));
295+
locationSupplier.get());
272296
}
273297
}
274-
}
298+
};
299+
Optional<Activation> root = Optional.of(activation);
300+
stk.push(new ActivationFrame("activation", root));
301+
root.map(Activation::getFile).ifPresent(fa -> {
302+
stk.push(new ActivationFrame("file", Optional.of(fa)));
303+
stk.push(new ActivationFrame("exists", Optional.empty()));
304+
validator.accept(fa.getExists());
305+
stk.peek().location = "missing";
306+
validator.accept(fa.getMissing());
307+
stk.pop();
308+
stk.pop();
309+
});
310+
root.map(Activation::getOs).ifPresent(oa -> {
311+
stk.push(new ActivationFrame("os", Optional.of(oa)));
312+
stk.push(new ActivationFrame("arch", Optional.empty()));
313+
validator.accept(oa.getArch());
314+
stk.peek().location = "family";
315+
validator.accept(oa.getFamily());
316+
stk.peek().location = "name";
317+
validator.accept(oa.getName());
318+
stk.peek().location = "version";
319+
validator.accept(oa.getVersion());
320+
stk.pop();
321+
stk.pop();
322+
});
323+
root.map(Activation::getProperty).ifPresent(pa -> {
324+
stk.push(new ActivationFrame("property", Optional.of(pa)));
325+
stk.push(new ActivationFrame("name", Optional.empty()));
326+
validator.accept(pa.getName());
327+
stk.peek().location = "value";
328+
validator.accept(pa.getValue());
329+
stk.pop();
330+
stk.pop();
331+
});
332+
root.map(Activation::getJdk).ifPresent(jdk -> {
333+
stk.push(new ActivationFrame("jdk", Optional.empty()));
334+
validator.accept(jdk);
335+
stk.pop();
336+
});
275337
}
276338

277339
private void validate20RawPlugins(

0 commit comments

Comments
 (0)