mirror of https://github.com/apache/maven.git
[MNG-8081] interpolate available properties during default profile selection (Maven 4.x) (#1446)
Co-authored-by: Guillaume Nodet <gnodet@gmail.com>
This commit is contained in:
parent
6814ba386d
commit
399f8b4ffc
|
@ -35,7 +35,9 @@ import java.util.concurrent.Callable;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
import java.util.function.UnaryOperator;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import org.apache.maven.api.VersionRange;
|
import org.apache.maven.api.VersionRange;
|
||||||
import org.apache.maven.api.annotations.Nullable;
|
import org.apache.maven.api.annotations.Nullable;
|
||||||
|
@ -50,6 +52,7 @@ import org.apache.maven.api.model.Dependency;
|
||||||
import org.apache.maven.api.model.DependencyManagement;
|
import org.apache.maven.api.model.DependencyManagement;
|
||||||
import org.apache.maven.api.model.Exclusion;
|
import org.apache.maven.api.model.Exclusion;
|
||||||
import org.apache.maven.api.model.InputLocation;
|
import org.apache.maven.api.model.InputLocation;
|
||||||
|
import org.apache.maven.api.model.InputLocationTracker;
|
||||||
import org.apache.maven.api.model.InputSource;
|
import org.apache.maven.api.model.InputSource;
|
||||||
import org.apache.maven.api.model.Model;
|
import org.apache.maven.api.model.Model;
|
||||||
import org.apache.maven.api.model.Parent;
|
import org.apache.maven.api.model.Parent;
|
||||||
|
@ -80,8 +83,11 @@ import org.apache.maven.api.services.xml.XmlReaderRequest;
|
||||||
import org.apache.maven.internal.impl.InternalSession;
|
import org.apache.maven.internal.impl.InternalSession;
|
||||||
import org.apache.maven.internal.impl.resolver.DefaultModelCache;
|
import org.apache.maven.internal.impl.resolver.DefaultModelCache;
|
||||||
import org.apache.maven.internal.impl.resolver.DefaultModelResolver;
|
import org.apache.maven.internal.impl.resolver.DefaultModelResolver;
|
||||||
|
import org.apache.maven.model.v4.MavenTransformer;
|
||||||
import org.codehaus.plexus.interpolation.InterpolationException;
|
import org.codehaus.plexus.interpolation.InterpolationException;
|
||||||
|
import org.codehaus.plexus.interpolation.Interpolator;
|
||||||
import org.codehaus.plexus.interpolation.MapBasedValueSource;
|
import org.codehaus.plexus.interpolation.MapBasedValueSource;
|
||||||
|
import org.codehaus.plexus.interpolation.RegexBasedInterpolator;
|
||||||
import org.codehaus.plexus.interpolation.StringSearchInterpolator;
|
import org.codehaus.plexus.interpolation.StringSearchInterpolator;
|
||||||
import org.eclipse.aether.impl.RemoteRepositoryManager;
|
import org.eclipse.aether.impl.RemoteRepositoryManager;
|
||||||
|
|
||||||
|
@ -377,54 +383,75 @@ public class DefaultModelBuilder implements ModelBuilder {
|
||||||
|
|
||||||
private List<Profile> interpolateActivations(
|
private List<Profile> interpolateActivations(
|
||||||
List<Profile> profiles, DefaultProfileActivationContext context, DefaultModelProblemCollector problems) {
|
List<Profile> profiles, DefaultProfileActivationContext context, DefaultModelProblemCollector problems) {
|
||||||
List<Profile> newProfiles = null;
|
if (profiles.stream()
|
||||||
for (int index = 0; index < profiles.size(); index++) {
|
.map(org.apache.maven.api.model.Profile::getActivation)
|
||||||
Profile profile = profiles.get(index);
|
.noneMatch(Objects::nonNull)) {
|
||||||
Activation activation = profile.getActivation();
|
return profiles;
|
||||||
if (activation != null) {
|
}
|
||||||
ActivationFile file = activation.getFile();
|
final Interpolator xform = new RegexBasedInterpolator();
|
||||||
if (file != null) {
|
xform.setCacheAnswers(true);
|
||||||
String oldExists = file.getExists();
|
Stream.of(context.getUserProperties(), context.getSystemProperties())
|
||||||
if (isNotEmpty(oldExists)) {
|
.map(MapBasedValueSource::new)
|
||||||
|
.forEach(xform::addValueSource);
|
||||||
|
|
||||||
|
class ProfileInterpolator extends MavenTransformer implements UnaryOperator<Profile> {
|
||||||
|
ProfileInterpolator() {
|
||||||
|
super(s -> {
|
||||||
|
if (isNotEmpty(s)) {
|
||||||
try {
|
try {
|
||||||
String newExists = interpolate(oldExists, context);
|
return xform.interpolate(s);
|
||||||
if (!Objects.equals(oldExists, newExists)) {
|
|
||||||
if (newProfiles == null) {
|
|
||||||
newProfiles = new ArrayList<>(profiles);
|
|
||||||
}
|
|
||||||
newProfiles.set(
|
|
||||||
index, profile.withActivation(activation.withFile(file.withExists(newExists))));
|
|
||||||
}
|
|
||||||
} catch (InterpolationException e) {
|
} catch (InterpolationException e) {
|
||||||
addInterpolationProblem(problems, file, oldExists, e, "exists");
|
problems.add(Severity.ERROR, ModelProblem.Version.BASE, e.getMessage(), e);
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
String oldMissing = file.getMissing();
|
return s;
|
||||||
if (isNotEmpty(oldMissing)) {
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Profile apply(Profile p) {
|
||||||
|
return Profile.newBuilder(p)
|
||||||
|
.activation(transformActivation(p.getActivation()))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ActivationFile.Builder transformActivationFile_Missing(
|
||||||
|
Supplier<? extends ActivationFile.Builder> creator,
|
||||||
|
ActivationFile.Builder builder,
|
||||||
|
ActivationFile target) {
|
||||||
|
String path = target.getMissing();
|
||||||
|
String xformed = transformPath(path, target, "missing");
|
||||||
|
return xformed != path ? (builder != null ? builder : creator.get()).missing(xformed) : builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ActivationFile.Builder transformActivationFile_Exists(
|
||||||
|
Supplier<? extends ActivationFile.Builder> creator,
|
||||||
|
ActivationFile.Builder builder,
|
||||||
|
ActivationFile target) {
|
||||||
|
final String path = target.getExists();
|
||||||
|
final String xformed = transformPath(path, target, "exists");
|
||||||
|
return xformed != path ? (builder != null ? builder : creator.get()).exists(xformed) : builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String transformPath(String path, ActivationFile target, String locationKey) {
|
||||||
|
if (isNotEmpty(path)) {
|
||||||
try {
|
try {
|
||||||
String newMissing = interpolate(oldMissing, context);
|
return profileActivationFilePathInterpolator.interpolate(path, context);
|
||||||
if (!Objects.equals(oldMissing, newMissing)) {
|
|
||||||
if (newProfiles == null) {
|
|
||||||
newProfiles = new ArrayList<>(profiles);
|
|
||||||
}
|
|
||||||
newProfiles.set(
|
|
||||||
index,
|
|
||||||
profile.withActivation(activation.withFile(file.withMissing(newMissing))));
|
|
||||||
}
|
|
||||||
} catch (InterpolationException e) {
|
} catch (InterpolationException e) {
|
||||||
addInterpolationProblem(problems, file, oldMissing, e, "missing");
|
addInterpolationProblem(problems, target, path, e, locationKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return path;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
return profiles.stream().map(new ProfileInterpolator()).toList();
|
||||||
}
|
|
||||||
return newProfiles != null ? newProfiles : profiles;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void addInterpolationProblem(
|
private static void addInterpolationProblem(
|
||||||
DefaultModelProblemCollector problems,
|
DefaultModelProblemCollector problems,
|
||||||
ActivationFile file,
|
InputLocationTracker target,
|
||||||
String path,
|
String path,
|
||||||
InterpolationException e,
|
InterpolationException e,
|
||||||
String locationKey) {
|
String locationKey) {
|
||||||
|
@ -432,14 +459,10 @@ public class DefaultModelBuilder implements ModelBuilder {
|
||||||
Severity.ERROR,
|
Severity.ERROR,
|
||||||
ModelProblem.Version.BASE,
|
ModelProblem.Version.BASE,
|
||||||
"Failed to interpolate file location " + path + ": " + e.getMessage(),
|
"Failed to interpolate file location " + path + ": " + e.getMessage(),
|
||||||
file.getLocation(locationKey),
|
target.getLocation(locationKey),
|
||||||
e);
|
e);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String interpolate(String path, ProfileActivationContext context) throws InterpolationException {
|
|
||||||
return isNotEmpty(path) ? profileActivationFilePathInterpolator.interpolate(path, context) : path;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isNotEmpty(String string) {
|
private static boolean isNotEmpty(String string) {
|
||||||
return string != null && !string.isEmpty();
|
return string != null && !string.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,20 +21,31 @@ package org.apache.maven.internal.impl.model;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.Deque;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import java.util.function.UnaryOperator;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.StreamSupport;
|
||||||
|
|
||||||
import org.apache.maven.api.di.Inject;
|
import org.apache.maven.api.di.Inject;
|
||||||
import org.apache.maven.api.di.Named;
|
import org.apache.maven.api.di.Named;
|
||||||
import org.apache.maven.api.di.Singleton;
|
import org.apache.maven.api.di.Singleton;
|
||||||
import org.apache.maven.api.model.Activation;
|
import org.apache.maven.api.model.Activation;
|
||||||
import org.apache.maven.api.model.ActivationFile;
|
import org.apache.maven.api.model.ActivationFile;
|
||||||
|
import org.apache.maven.api.model.ActivationOS;
|
||||||
|
import org.apache.maven.api.model.ActivationProperty;
|
||||||
import org.apache.maven.api.model.Build;
|
import org.apache.maven.api.model.Build;
|
||||||
import org.apache.maven.api.model.BuildBase;
|
import org.apache.maven.api.model.BuildBase;
|
||||||
import org.apache.maven.api.model.Dependency;
|
import org.apache.maven.api.model.Dependency;
|
||||||
|
@ -59,6 +70,7 @@ import org.apache.maven.api.services.ModelProblem.Version;
|
||||||
import org.apache.maven.api.services.ModelProblemCollector;
|
import org.apache.maven.api.services.ModelProblemCollector;
|
||||||
import org.apache.maven.api.services.model.*;
|
import org.apache.maven.api.services.model.*;
|
||||||
import org.apache.maven.model.v4.MavenModelVersion;
|
import org.apache.maven.model.v4.MavenModelVersion;
|
||||||
|
import org.apache.maven.model.v4.MavenTransformer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
|
@ -80,6 +92,196 @@ public class DefaultModelValidator implements ModelValidator {
|
||||||
|
|
||||||
private static final String EMPTY = "";
|
private static final String EMPTY = "";
|
||||||
|
|
||||||
|
private record ActivationFrame(String location, Optional<? extends InputLocationTracker> parent) {}
|
||||||
|
|
||||||
|
private static class ActivationWalker extends MavenTransformer {
|
||||||
|
|
||||||
|
private final Deque<ActivationFrame> stk;
|
||||||
|
|
||||||
|
ActivationWalker(Deque<ActivationFrame> stk, UnaryOperator<String> transformer) {
|
||||||
|
super(transformer);
|
||||||
|
this.stk = stk;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ActivationFrame nextFrame(String property) {
|
||||||
|
return new ActivationFrame(property, Optional.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
private <P> ActivationFrame nextFrame(String property, Function<P, InputLocationTracker> child) {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
final Optional<P> parent = (Optional<P>) stk.peek().parent;
|
||||||
|
return new ActivationFrame(property, parent.map(child));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Activation transformActivation(Activation target) {
|
||||||
|
stk.push(new ActivationFrame("activation", Optional.of(target)));
|
||||||
|
try {
|
||||||
|
return super.transformActivation(target);
|
||||||
|
} finally {
|
||||||
|
stk.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Activation.Builder transformActivation_ActiveByDefault(
|
||||||
|
Supplier<? extends Activation.Builder> creator, Activation.Builder builder, Activation target) {
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Activation.Builder transformActivation_File(
|
||||||
|
Supplier<? extends Activation.Builder> creator, Activation.Builder builder, Activation target) {
|
||||||
|
stk.push(nextFrame("file", Activation::getFile));
|
||||||
|
Optional.ofNullable(target.getFile());
|
||||||
|
try {
|
||||||
|
return super.transformActivation_File(creator, builder, target);
|
||||||
|
} finally {
|
||||||
|
stk.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ActivationFile.Builder transformActivationFile_Exists(
|
||||||
|
Supplier<? extends ActivationFile.Builder> creator,
|
||||||
|
ActivationFile.Builder builder,
|
||||||
|
ActivationFile target) {
|
||||||
|
stk.push(nextFrame("exists"));
|
||||||
|
try {
|
||||||
|
return super.transformActivationFile_Exists(creator, builder, target);
|
||||||
|
} finally {
|
||||||
|
stk.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ActivationFile.Builder transformActivationFile_Missing(
|
||||||
|
Supplier<? extends ActivationFile.Builder> creator,
|
||||||
|
ActivationFile.Builder builder,
|
||||||
|
ActivationFile target) {
|
||||||
|
stk.push(nextFrame("missing"));
|
||||||
|
try {
|
||||||
|
return super.transformActivationFile_Missing(creator, builder, target);
|
||||||
|
} finally {
|
||||||
|
stk.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Activation.Builder transformActivation_Jdk(
|
||||||
|
Supplier<? extends Activation.Builder> creator, Activation.Builder builder, Activation target) {
|
||||||
|
stk.push(nextFrame("jdk"));
|
||||||
|
try {
|
||||||
|
return super.transformActivation_Jdk(creator, builder, target);
|
||||||
|
} finally {
|
||||||
|
stk.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Activation.Builder transformActivation_Os(
|
||||||
|
Supplier<? extends Activation.Builder> creator, Activation.Builder builder, Activation target) {
|
||||||
|
stk.push(nextFrame("os", Activation::getOs));
|
||||||
|
try {
|
||||||
|
return super.transformActivation_Os(creator, builder, target);
|
||||||
|
} finally {
|
||||||
|
stk.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ActivationOS.Builder transformActivationOS_Arch(
|
||||||
|
Supplier<? extends ActivationOS.Builder> creator, ActivationOS.Builder builder, ActivationOS target) {
|
||||||
|
stk.push(nextFrame("arch"));
|
||||||
|
try {
|
||||||
|
return super.transformActivationOS_Arch(creator, builder, target);
|
||||||
|
} finally {
|
||||||
|
stk.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ActivationOS.Builder transformActivationOS_Family(
|
||||||
|
Supplier<? extends ActivationOS.Builder> creator, ActivationOS.Builder builder, ActivationOS target) {
|
||||||
|
stk.push(nextFrame("family"));
|
||||||
|
try {
|
||||||
|
return super.transformActivationOS_Family(creator, builder, target);
|
||||||
|
} finally {
|
||||||
|
stk.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ActivationOS.Builder transformActivationOS_Name(
|
||||||
|
Supplier<? extends ActivationOS.Builder> creator, ActivationOS.Builder builder, ActivationOS target) {
|
||||||
|
stk.push(nextFrame("name"));
|
||||||
|
try {
|
||||||
|
return super.transformActivationOS_Name(creator, builder, target);
|
||||||
|
} finally {
|
||||||
|
stk.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ActivationOS.Builder transformActivationOS_Version(
|
||||||
|
Supplier<? extends ActivationOS.Builder> creator, ActivationOS.Builder builder, ActivationOS target) {
|
||||||
|
stk.push(nextFrame("version"));
|
||||||
|
try {
|
||||||
|
return super.transformActivationOS_Version(creator, builder, target);
|
||||||
|
} finally {
|
||||||
|
stk.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Activation.Builder transformActivation_Packaging(
|
||||||
|
Supplier<? extends Activation.Builder> creator, Activation.Builder builder, Activation target) {
|
||||||
|
stk.push(nextFrame("packaging"));
|
||||||
|
try {
|
||||||
|
return super.transformActivation_Packaging(creator, builder, target);
|
||||||
|
} finally {
|
||||||
|
stk.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Activation.Builder transformActivation_Property(
|
||||||
|
Supplier<? extends Activation.Builder> creator, Activation.Builder builder, Activation target) {
|
||||||
|
stk.push(nextFrame("property", Activation::getProperty));
|
||||||
|
try {
|
||||||
|
return super.transformActivation_Property(creator, builder, target);
|
||||||
|
} finally {
|
||||||
|
stk.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ActivationProperty.Builder transformActivationProperty_Name(
|
||||||
|
Supplier<? extends ActivationProperty.Builder> creator,
|
||||||
|
ActivationProperty.Builder builder,
|
||||||
|
ActivationProperty target) {
|
||||||
|
stk.push(nextFrame("name"));
|
||||||
|
try {
|
||||||
|
return super.transformActivationProperty_Name(creator, builder, target);
|
||||||
|
} finally {
|
||||||
|
stk.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ActivationProperty.Builder transformActivationProperty_Value(
|
||||||
|
Supplier<? extends ActivationProperty.Builder> creator,
|
||||||
|
ActivationProperty.Builder builder,
|
||||||
|
ActivationProperty target) {
|
||||||
|
stk.push(nextFrame("value"));
|
||||||
|
try {
|
||||||
|
return super.transformActivationProperty_Value(creator, builder, target);
|
||||||
|
} finally {
|
||||||
|
stk.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private final Set<String> validCoordinateIds = new HashSet<>();
|
private final Set<String> validCoordinateIds = new HashSet<>();
|
||||||
|
|
||||||
private final Set<String> validProfileIds = new HashSet<>();
|
private final Set<String> validProfileIds = new HashSet<>();
|
||||||
|
@ -282,42 +484,53 @@ public class DefaultModelValidator implements ModelValidator {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validate30RawProfileActivation(ModelProblemCollector problems, Activation activation, String prefix) {
|
private void validate30RawProfileActivation(ModelProblemCollector problems, Activation activation, String prefix) {
|
||||||
if (activation == null || activation.getFile() == null) {
|
if (activation == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ActivationFile file = activation.getFile();
|
final Deque<ActivationFrame> stk = new LinkedList<>();
|
||||||
|
|
||||||
String path;
|
final Supplier<String> pathSupplier = () -> {
|
||||||
String location;
|
final boolean parallel = false;
|
||||||
|
return StreamSupport.stream(((Iterable<ActivationFrame>) stk::descendingIterator).spliterator(), parallel)
|
||||||
if (file.getExists() != null && !file.getExists().isEmpty()) {
|
.map(ActivationFrame::location)
|
||||||
path = file.getExists();
|
.collect(Collectors.joining("."));
|
||||||
location = "exists";
|
};
|
||||||
} else if (file.getMissing() != null && !file.getMissing().isEmpty()) {
|
final Supplier<InputLocation> locationSupplier = () -> {
|
||||||
path = file.getMissing();
|
if (stk.size() < 2) {
|
||||||
location = "missing";
|
return null;
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
Iterator<ActivationFrame> f = stk.iterator();
|
||||||
|
|
||||||
if (hasProjectExpression(path)) {
|
String location = f.next().location;
|
||||||
Matcher matcher = EXPRESSION_PROJECT_NAME_PATTERN.matcher(path);
|
ActivationFrame parent = f.next();
|
||||||
|
|
||||||
|
return parent.parent.map(p -> p.getLocation(location)).orElse(null);
|
||||||
|
};
|
||||||
|
final UnaryOperator<String> transformer = s -> {
|
||||||
|
if (hasProjectExpression(s)) {
|
||||||
|
String path = pathSupplier.get();
|
||||||
|
Matcher matcher = EXPRESSION_PROJECT_NAME_PATTERN.matcher(s);
|
||||||
while (matcher.find()) {
|
while (matcher.find()) {
|
||||||
String propertyName = matcher.group(0);
|
String propertyName = matcher.group(0);
|
||||||
if (!"${project.basedir}".equals(propertyName)) {
|
|
||||||
|
if (path.startsWith("activation.file.") && "${project.basedir}".equals(propertyName)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
addViolation(
|
addViolation(
|
||||||
problems,
|
problems,
|
||||||
Severity.WARNING,
|
Severity.WARNING,
|
||||||
Version.V30,
|
Version.V30,
|
||||||
prefix + "activation.file." + location,
|
prefix + path,
|
||||||
null,
|
null,
|
||||||
"Failed to interpolate file location " + path + ": " + propertyName
|
"Failed to interpolate profile activation property " + s + ": " + propertyName
|
||||||
+ " expressions are not supported during profile activation.",
|
+ " expressions are not supported during profile activation.",
|
||||||
file.getLocation(location));
|
locationSupplier.get());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return s;
|
||||||
|
};
|
||||||
|
new ActivationWalker(stk, transformer).transformActivation(activation);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validate20RawPlugins(
|
private void validate20RawPlugins(
|
||||||
|
|
|
@ -38,10 +38,13 @@ import java.util.Optional;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
import java.util.function.UnaryOperator;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import org.apache.maven.api.VersionRange;
|
import org.apache.maven.api.VersionRange;
|
||||||
import org.apache.maven.api.feature.Features;
|
import org.apache.maven.api.feature.Features;
|
||||||
|
import org.apache.maven.api.model.ActivationFile;
|
||||||
import org.apache.maven.api.model.Exclusion;
|
import org.apache.maven.api.model.Exclusion;
|
||||||
import org.apache.maven.api.model.InputSource;
|
import org.apache.maven.api.model.InputSource;
|
||||||
import org.apache.maven.api.services.VersionParserException;
|
import org.apache.maven.api.services.VersionParserException;
|
||||||
|
@ -57,6 +60,7 @@ import org.apache.maven.model.Plugin;
|
||||||
import org.apache.maven.model.PluginManagement;
|
import org.apache.maven.model.PluginManagement;
|
||||||
import org.apache.maven.model.Profile;
|
import org.apache.maven.model.Profile;
|
||||||
import org.apache.maven.model.building.ModelProblem.Severity;
|
import org.apache.maven.model.building.ModelProblem.Severity;
|
||||||
|
import org.apache.maven.model.building.ModelProblem.Version;
|
||||||
import org.apache.maven.model.composition.DependencyManagementImporter;
|
import org.apache.maven.model.composition.DependencyManagementImporter;
|
||||||
import org.apache.maven.model.inheritance.InheritanceAssembler;
|
import org.apache.maven.model.inheritance.InheritanceAssembler;
|
||||||
import org.apache.maven.model.interpolation.ModelInterpolator;
|
import org.apache.maven.model.interpolation.ModelInterpolator;
|
||||||
|
@ -82,11 +86,14 @@ import org.apache.maven.model.resolution.ModelResolver;
|
||||||
import org.apache.maven.model.resolution.UnresolvableModelException;
|
import org.apache.maven.model.resolution.UnresolvableModelException;
|
||||||
import org.apache.maven.model.resolution.WorkspaceModelResolver;
|
import org.apache.maven.model.resolution.WorkspaceModelResolver;
|
||||||
import org.apache.maven.model.superpom.SuperPomProvider;
|
import org.apache.maven.model.superpom.SuperPomProvider;
|
||||||
|
import org.apache.maven.model.v4.MavenTransformer;
|
||||||
import org.apache.maven.model.validation.DefaultModelValidator;
|
import org.apache.maven.model.validation.DefaultModelValidator;
|
||||||
import org.apache.maven.model.validation.ModelValidator;
|
import org.apache.maven.model.validation.ModelValidator;
|
||||||
import org.apache.maven.model.version.ModelVersionParser;
|
import org.apache.maven.model.version.ModelVersionParser;
|
||||||
import org.codehaus.plexus.interpolation.InterpolationException;
|
import org.codehaus.plexus.interpolation.InterpolationException;
|
||||||
|
import org.codehaus.plexus.interpolation.Interpolator;
|
||||||
import org.codehaus.plexus.interpolation.MapBasedValueSource;
|
import org.codehaus.plexus.interpolation.MapBasedValueSource;
|
||||||
|
import org.codehaus.plexus.interpolation.RegexBasedInterpolator;
|
||||||
import org.codehaus.plexus.interpolation.StringSearchInterpolator;
|
import org.codehaus.plexus.interpolation.StringSearchInterpolator;
|
||||||
import org.eclipse.sisu.Nullable;
|
import org.eclipse.sisu.Nullable;
|
||||||
|
|
||||||
|
@ -897,69 +904,89 @@ public class DefaultModelBuilder implements ModelBuilder {
|
||||||
List<org.apache.maven.api.model.Profile> profiles,
|
List<org.apache.maven.api.model.Profile> profiles,
|
||||||
DefaultProfileActivationContext context,
|
DefaultProfileActivationContext context,
|
||||||
DefaultModelProblemCollector problems) {
|
DefaultModelProblemCollector problems) {
|
||||||
List<org.apache.maven.api.model.Profile> newProfiles = null;
|
if (profiles.stream()
|
||||||
for (int index = 0; index < profiles.size(); index++) {
|
.map(org.apache.maven.api.model.Profile::getActivation)
|
||||||
org.apache.maven.api.model.Profile profile = profiles.get(index);
|
.noneMatch(Objects::nonNull)) {
|
||||||
org.apache.maven.api.model.Activation activation = profile.getActivation();
|
return profiles;
|
||||||
if (activation != null) {
|
}
|
||||||
org.apache.maven.api.model.ActivationFile file = activation.getFile();
|
final Interpolator xform = new RegexBasedInterpolator();
|
||||||
if (file != null) {
|
xform.setCacheAnswers(true);
|
||||||
String oldExists = file.getExists();
|
Stream.of(context.getUserProperties(), context.getSystemProperties())
|
||||||
if (isNotEmpty(oldExists)) {
|
.map(MapBasedValueSource::new)
|
||||||
|
.forEach(xform::addValueSource);
|
||||||
|
|
||||||
|
class ProfileInterpolator extends MavenTransformer
|
||||||
|
implements UnaryOperator<org.apache.maven.api.model.Profile> {
|
||||||
|
ProfileInterpolator() {
|
||||||
|
super(s -> {
|
||||||
|
if (isNotEmpty(s)) {
|
||||||
try {
|
try {
|
||||||
String newExists = interpolate(oldExists, context);
|
return xform.interpolate(s);
|
||||||
if (!Objects.equals(oldExists, newExists)) {
|
|
||||||
if (newProfiles == null) {
|
|
||||||
newProfiles = new ArrayList<>(profiles);
|
|
||||||
}
|
|
||||||
newProfiles.set(
|
|
||||||
index, profile.withActivation(activation.withFile(file.withExists(newExists))));
|
|
||||||
}
|
|
||||||
} catch (InterpolationException e) {
|
} catch (InterpolationException e) {
|
||||||
addInterpolationProblem(problems, file, oldExists, e, "exists");
|
problems.add(new ModelProblemCollectorRequest(Severity.ERROR, Version.BASE)
|
||||||
|
.setMessage(e.getMessage())
|
||||||
|
.setException(e));
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
String oldMissing = file.getMissing();
|
return s;
|
||||||
if (isNotEmpty(oldMissing)) {
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public org.apache.maven.api.model.Profile apply(org.apache.maven.api.model.Profile p) {
|
||||||
|
return org.apache.maven.api.model.Profile.newBuilder(p)
|
||||||
|
.activation(transformActivation(p.getActivation()))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ActivationFile.Builder transformActivationFile_Missing(
|
||||||
|
Supplier<? extends ActivationFile.Builder> creator,
|
||||||
|
ActivationFile.Builder builder,
|
||||||
|
ActivationFile target) {
|
||||||
|
final String path = target.getMissing();
|
||||||
|
final String xformed = transformPath(path, target, "missing");
|
||||||
|
return xformed != path ? (builder != null ? builder : creator.get()).missing(xformed) : builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ActivationFile.Builder transformActivationFile_Exists(
|
||||||
|
Supplier<? extends ActivationFile.Builder> creator,
|
||||||
|
ActivationFile.Builder builder,
|
||||||
|
ActivationFile target) {
|
||||||
|
final String path = target.getExists();
|
||||||
|
final String xformed = transformPath(path, target, "exists");
|
||||||
|
return xformed != path ? (builder != null ? builder : creator.get()).exists(xformed) : builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String transformPath(String path, ActivationFile target, String locationKey) {
|
||||||
|
if (isNotEmpty(path)) {
|
||||||
try {
|
try {
|
||||||
String newMissing = interpolate(oldMissing, context);
|
return profileActivationFilePathInterpolator.interpolate(path, context);
|
||||||
if (!Objects.equals(oldMissing, newMissing)) {
|
|
||||||
if (newProfiles == null) {
|
|
||||||
newProfiles = new ArrayList<>(profiles);
|
|
||||||
}
|
|
||||||
newProfiles.set(
|
|
||||||
index,
|
|
||||||
profile.withActivation(activation.withFile(file.withMissing(newMissing))));
|
|
||||||
}
|
|
||||||
} catch (InterpolationException e) {
|
} catch (InterpolationException e) {
|
||||||
addInterpolationProblem(problems, file, oldMissing, e, "missing");
|
addInterpolationProblem(problems, target, path, e, locationKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return path;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
return profiles.stream().map(new ProfileInterpolator()).toList();
|
||||||
}
|
|
||||||
return newProfiles != null ? newProfiles : profiles;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void addInterpolationProblem(
|
private static void addInterpolationProblem(
|
||||||
DefaultModelProblemCollector problems,
|
DefaultModelProblemCollector problems,
|
||||||
org.apache.maven.api.model.ActivationFile file,
|
org.apache.maven.api.model.InputLocationTracker target,
|
||||||
String path,
|
String path,
|
||||||
InterpolationException e,
|
InterpolationException e,
|
||||||
String locationKey) {
|
String locationKey) {
|
||||||
problems.add(new ModelProblemCollectorRequest(Severity.ERROR, ModelProblem.Version.BASE)
|
problems.add(new ModelProblemCollectorRequest(Severity.ERROR, ModelProblem.Version.BASE)
|
||||||
.setMessage("Failed to interpolate file location " + path + ": " + e.getMessage())
|
.setMessage("Failed to interpolate file location " + path + ": " + e.getMessage())
|
||||||
.setLocation(Optional.ofNullable(file.getLocation(locationKey))
|
.setLocation(Optional.ofNullable(target.getLocation(locationKey))
|
||||||
.map(InputLocation::new)
|
.map(InputLocation::new)
|
||||||
.orElse(null))
|
.orElse(null))
|
||||||
.setException(e));
|
.setException(e));
|
||||||
}
|
}
|
||||||
|
|
||||||
private String interpolate(String path, ProfileActivationContext context) throws InterpolationException {
|
|
||||||
return isNotEmpty(path) ? profileActivationFilePathInterpolator.interpolate(path, context) : path;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isNotEmpty(String string) {
|
private static boolean isNotEmpty(String string) {
|
||||||
return string != null && !string.isEmpty();
|
return string != null && !string.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,17 +25,28 @@ import javax.inject.Singleton;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.Deque;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import java.util.function.UnaryOperator;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.StreamSupport;
|
||||||
|
|
||||||
import org.apache.maven.api.model.Activation;
|
import org.apache.maven.api.model.Activation;
|
||||||
import org.apache.maven.api.model.ActivationFile;
|
import org.apache.maven.api.model.ActivationFile;
|
||||||
|
import org.apache.maven.api.model.ActivationOS;
|
||||||
|
import org.apache.maven.api.model.ActivationProperty;
|
||||||
import org.apache.maven.api.model.Build;
|
import org.apache.maven.api.model.Build;
|
||||||
import org.apache.maven.api.model.BuildBase;
|
import org.apache.maven.api.model.BuildBase;
|
||||||
import org.apache.maven.api.model.Dependency;
|
import org.apache.maven.api.model.Dependency;
|
||||||
|
@ -61,6 +72,7 @@ import org.apache.maven.model.building.ModelProblemCollector;
|
||||||
import org.apache.maven.model.building.ModelProblemCollectorRequest;
|
import org.apache.maven.model.building.ModelProblemCollectorRequest;
|
||||||
import org.apache.maven.model.interpolation.ModelVersionProcessor;
|
import org.apache.maven.model.interpolation.ModelVersionProcessor;
|
||||||
import org.apache.maven.model.v4.MavenModelVersion;
|
import org.apache.maven.model.v4.MavenModelVersion;
|
||||||
|
import org.apache.maven.model.v4.MavenTransformer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
|
@ -82,6 +94,196 @@ public class DefaultModelValidator implements ModelValidator {
|
||||||
|
|
||||||
private static final String EMPTY = "";
|
private static final String EMPTY = "";
|
||||||
|
|
||||||
|
private record ActivationFrame(String location, Optional<? extends InputLocationTracker> parent) {}
|
||||||
|
|
||||||
|
private static class ActivationWalker extends MavenTransformer {
|
||||||
|
|
||||||
|
private final Deque<ActivationFrame> stk;
|
||||||
|
|
||||||
|
ActivationWalker(Deque<ActivationFrame> stk, UnaryOperator<String> transformer) {
|
||||||
|
super(transformer);
|
||||||
|
this.stk = stk;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ActivationFrame nextFrame(String property) {
|
||||||
|
return new ActivationFrame(property, Optional.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
private <P> ActivationFrame nextFrame(String property, Function<P, InputLocationTracker> child) {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
final Optional<P> parent = (Optional<P>) stk.peek().parent;
|
||||||
|
return new ActivationFrame(property, parent.map(child));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Activation transformActivation(Activation target) {
|
||||||
|
stk.push(new ActivationFrame("activation", Optional.of(target)));
|
||||||
|
try {
|
||||||
|
return super.transformActivation(target);
|
||||||
|
} finally {
|
||||||
|
stk.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Activation.Builder transformActivation_ActiveByDefault(
|
||||||
|
Supplier<? extends Activation.Builder> creator, Activation.Builder builder, Activation target) {
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Activation.Builder transformActivation_File(
|
||||||
|
Supplier<? extends Activation.Builder> creator, Activation.Builder builder, Activation target) {
|
||||||
|
stk.push(nextFrame("file", Activation::getFile));
|
||||||
|
Optional.ofNullable(target.getFile());
|
||||||
|
try {
|
||||||
|
return super.transformActivation_File(creator, builder, target);
|
||||||
|
} finally {
|
||||||
|
stk.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ActivationFile.Builder transformActivationFile_Exists(
|
||||||
|
Supplier<? extends ActivationFile.Builder> creator,
|
||||||
|
ActivationFile.Builder builder,
|
||||||
|
ActivationFile target) {
|
||||||
|
stk.push(nextFrame("exists"));
|
||||||
|
try {
|
||||||
|
return super.transformActivationFile_Exists(creator, builder, target);
|
||||||
|
} finally {
|
||||||
|
stk.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ActivationFile.Builder transformActivationFile_Missing(
|
||||||
|
Supplier<? extends ActivationFile.Builder> creator,
|
||||||
|
ActivationFile.Builder builder,
|
||||||
|
ActivationFile target) {
|
||||||
|
stk.push(nextFrame("missing"));
|
||||||
|
try {
|
||||||
|
return super.transformActivationFile_Missing(creator, builder, target);
|
||||||
|
} finally {
|
||||||
|
stk.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Activation.Builder transformActivation_Jdk(
|
||||||
|
Supplier<? extends Activation.Builder> creator, Activation.Builder builder, Activation target) {
|
||||||
|
stk.push(nextFrame("jdk"));
|
||||||
|
try {
|
||||||
|
return super.transformActivation_Jdk(creator, builder, target);
|
||||||
|
} finally {
|
||||||
|
stk.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Activation.Builder transformActivation_Os(
|
||||||
|
Supplier<? extends Activation.Builder> creator, Activation.Builder builder, Activation target) {
|
||||||
|
stk.push(nextFrame("os", Activation::getOs));
|
||||||
|
try {
|
||||||
|
return super.transformActivation_Os(creator, builder, target);
|
||||||
|
} finally {
|
||||||
|
stk.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ActivationOS.Builder transformActivationOS_Arch(
|
||||||
|
Supplier<? extends ActivationOS.Builder> creator, ActivationOS.Builder builder, ActivationOS target) {
|
||||||
|
stk.push(nextFrame("arch"));
|
||||||
|
try {
|
||||||
|
return super.transformActivationOS_Arch(creator, builder, target);
|
||||||
|
} finally {
|
||||||
|
stk.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ActivationOS.Builder transformActivationOS_Family(
|
||||||
|
Supplier<? extends ActivationOS.Builder> creator, ActivationOS.Builder builder, ActivationOS target) {
|
||||||
|
stk.push(nextFrame("family"));
|
||||||
|
try {
|
||||||
|
return super.transformActivationOS_Family(creator, builder, target);
|
||||||
|
} finally {
|
||||||
|
stk.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ActivationOS.Builder transformActivationOS_Name(
|
||||||
|
Supplier<? extends ActivationOS.Builder> creator, ActivationOS.Builder builder, ActivationOS target) {
|
||||||
|
stk.push(nextFrame("name"));
|
||||||
|
try {
|
||||||
|
return super.transformActivationOS_Name(creator, builder, target);
|
||||||
|
} finally {
|
||||||
|
stk.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ActivationOS.Builder transformActivationOS_Version(
|
||||||
|
Supplier<? extends ActivationOS.Builder> creator, ActivationOS.Builder builder, ActivationOS target) {
|
||||||
|
stk.push(nextFrame("version"));
|
||||||
|
try {
|
||||||
|
return super.transformActivationOS_Version(creator, builder, target);
|
||||||
|
} finally {
|
||||||
|
stk.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Activation.Builder transformActivation_Packaging(
|
||||||
|
Supplier<? extends Activation.Builder> creator, Activation.Builder builder, Activation target) {
|
||||||
|
stk.push(nextFrame("packaging"));
|
||||||
|
try {
|
||||||
|
return super.transformActivation_Packaging(creator, builder, target);
|
||||||
|
} finally {
|
||||||
|
stk.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Activation.Builder transformActivation_Property(
|
||||||
|
Supplier<? extends Activation.Builder> creator, Activation.Builder builder, Activation target) {
|
||||||
|
stk.push(nextFrame("property", Activation::getProperty));
|
||||||
|
try {
|
||||||
|
return super.transformActivation_Property(creator, builder, target);
|
||||||
|
} finally {
|
||||||
|
stk.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ActivationProperty.Builder transformActivationProperty_Name(
|
||||||
|
Supplier<? extends ActivationProperty.Builder> creator,
|
||||||
|
ActivationProperty.Builder builder,
|
||||||
|
ActivationProperty target) {
|
||||||
|
stk.push(nextFrame("name"));
|
||||||
|
try {
|
||||||
|
return super.transformActivationProperty_Name(creator, builder, target);
|
||||||
|
} finally {
|
||||||
|
stk.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ActivationProperty.Builder transformActivationProperty_Value(
|
||||||
|
Supplier<? extends ActivationProperty.Builder> creator,
|
||||||
|
ActivationProperty.Builder builder,
|
||||||
|
ActivationProperty target) {
|
||||||
|
stk.push(nextFrame("value"));
|
||||||
|
try {
|
||||||
|
return super.transformActivationProperty_Value(creator, builder, target);
|
||||||
|
} finally {
|
||||||
|
stk.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private final Set<String> validCoordinateIds = new HashSet<>();
|
private final Set<String> validCoordinateIds = new HashSet<>();
|
||||||
|
|
||||||
private final Set<String> validProfileIds = new HashSet<>();
|
private final Set<String> validProfileIds = new HashSet<>();
|
||||||
|
@ -288,42 +490,52 @@ public class DefaultModelValidator implements ModelValidator {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validate30RawProfileActivation(ModelProblemCollector problems, Activation activation, String prefix) {
|
private void validate30RawProfileActivation(ModelProblemCollector problems, Activation activation, String prefix) {
|
||||||
if (activation == null || activation.getFile() == null) {
|
if (activation == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
final Deque<ActivationFrame> stk = new LinkedList<>();
|
||||||
|
|
||||||
ActivationFile file = activation.getFile();
|
final Supplier<String> pathSupplier = () -> {
|
||||||
|
final boolean parallel = false;
|
||||||
String path;
|
return StreamSupport.stream(((Iterable<ActivationFrame>) stk::descendingIterator).spliterator(), parallel)
|
||||||
String location;
|
.map(ActivationFrame::location)
|
||||||
|
.collect(Collectors.joining("."));
|
||||||
if (file.getExists() != null && !file.getExists().isEmpty()) {
|
};
|
||||||
path = file.getExists();
|
final Supplier<InputLocation> locationSupplier = () -> {
|
||||||
location = "exists";
|
if (stk.size() < 2) {
|
||||||
} else if (file.getMissing() != null && !file.getMissing().isEmpty()) {
|
return null;
|
||||||
path = file.getMissing();
|
|
||||||
location = "missing";
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
Iterator<ActivationFrame> f = stk.iterator();
|
||||||
|
|
||||||
if (hasProjectExpression(path)) {
|
String location = f.next().location;
|
||||||
Matcher matcher = EXPRESSION_PROJECT_NAME_PATTERN.matcher(path);
|
ActivationFrame parent = f.next();
|
||||||
|
|
||||||
|
return parent.parent.map(p -> p.getLocation(location)).orElse(null);
|
||||||
|
};
|
||||||
|
final UnaryOperator<String> transformer = s -> {
|
||||||
|
if (hasProjectExpression(s)) {
|
||||||
|
String path = pathSupplier.get();
|
||||||
|
Matcher matcher = EXPRESSION_PROJECT_NAME_PATTERN.matcher(s);
|
||||||
while (matcher.find()) {
|
while (matcher.find()) {
|
||||||
String propertyName = matcher.group(0);
|
String propertyName = matcher.group(0);
|
||||||
if (!"${project.basedir}".equals(propertyName)) {
|
|
||||||
|
if (path.startsWith("activation.file.") && "${project.basedir}".equals(propertyName)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
addViolation(
|
addViolation(
|
||||||
problems,
|
problems,
|
||||||
Severity.WARNING,
|
Severity.WARNING,
|
||||||
Version.V30,
|
Version.V30,
|
||||||
prefix + "activation.file." + location,
|
prefix + path,
|
||||||
null,
|
null,
|
||||||
"Failed to interpolate file location " + path + ": " + propertyName
|
"Failed to interpolate profile activation property " + s + ": " + propertyName
|
||||||
+ " expressions are not supported during profile activation.",
|
+ " expressions are not supported during profile activation.",
|
||||||
file.getLocation(location));
|
locationSupplier.get());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return s;
|
||||||
|
};
|
||||||
|
new ActivationWalker(stk, transformer).transformActivation(activation);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validate20RawPlugins(
|
private void validate20RawPlugins(
|
||||||
|
|
|
@ -20,6 +20,8 @@ package org.apache.maven.model.validation;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.function.UnaryOperator;
|
||||||
|
|
||||||
import org.apache.maven.model.Model;
|
import org.apache.maven.model.Model;
|
||||||
import org.apache.maven.model.building.DefaultModelBuildingRequest;
|
import org.apache.maven.model.building.DefaultModelBuildingRequest;
|
||||||
|
@ -50,32 +52,40 @@ class DefaultModelValidatorTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
private SimpleProblemCollector validate(String pom) throws Exception {
|
private SimpleProblemCollector validate(String pom) throws Exception {
|
||||||
return validateEffective(pom, ModelBuildingRequest.VALIDATION_LEVEL_STRICT);
|
return validateEffective(pom, UnaryOperator.identity());
|
||||||
}
|
}
|
||||||
|
|
||||||
private SimpleProblemCollector validateRaw(String pom) throws Exception {
|
private SimpleProblemCollector validateRaw(String pom) throws Exception {
|
||||||
return validateRaw(pom, ModelBuildingRequest.VALIDATION_LEVEL_STRICT);
|
return validateRaw(pom, UnaryOperator.identity());
|
||||||
}
|
}
|
||||||
|
|
||||||
private SimpleProblemCollector validateEffective(String pom, int level) throws Exception {
|
private SimpleProblemCollector validateEffective(String pom, int level) throws Exception {
|
||||||
ModelBuildingRequest request = new DefaultModelBuildingRequest().setValidationLevel(level);
|
return validateEffective(pom, mbr -> mbr.setValidationLevel(level));
|
||||||
|
}
|
||||||
|
|
||||||
|
private SimpleProblemCollector validateEffective(String pom, UnaryOperator<ModelBuildingRequest> requestConfigurer)
|
||||||
|
throws Exception {
|
||||||
Model model = read(pom);
|
Model model = read(pom);
|
||||||
|
|
||||||
SimpleProblemCollector problems = new SimpleProblemCollector(model);
|
SimpleProblemCollector problems = new SimpleProblemCollector(model);
|
||||||
|
|
||||||
validator.validateEffectiveModel(model, request, problems);
|
validator.validateEffectiveModel(model, requestConfigurer.apply(new DefaultModelBuildingRequest()), problems);
|
||||||
|
|
||||||
return problems;
|
return problems;
|
||||||
}
|
}
|
||||||
|
|
||||||
private SimpleProblemCollector validateRaw(String pom, int level) throws Exception {
|
private SimpleProblemCollector validateRaw(String pom, int level) throws Exception {
|
||||||
ModelBuildingRequest request = new DefaultModelBuildingRequest().setValidationLevel(level);
|
return validateRaw(pom, mbr -> mbr.setValidationLevel(level));
|
||||||
|
}
|
||||||
|
|
||||||
|
private SimpleProblemCollector validateRaw(String pom, UnaryOperator<ModelBuildingRequest> requestConfigurer)
|
||||||
|
throws Exception {
|
||||||
Model model = read(pom);
|
Model model = read(pom);
|
||||||
|
|
||||||
SimpleProblemCollector problems = new SimpleProblemCollector(model);
|
SimpleProblemCollector problems = new SimpleProblemCollector(model);
|
||||||
|
|
||||||
|
ModelBuildingRequest request = requestConfigurer.apply(new DefaultModelBuildingRequest());
|
||||||
|
|
||||||
validator.validateFileModel(model, request, problems);
|
validator.validateFileModel(model, request, problems);
|
||||||
|
|
||||||
validator.validateRawModel(model, request, problems);
|
validator.validateRawModel(model, request, problems);
|
||||||
|
@ -818,24 +828,52 @@ class DefaultModelValidatorTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void profileActivationWithAllowedExpression() throws Exception {
|
void profileActivationWithAllowedExpression() throws Exception {
|
||||||
SimpleProblemCollector result = validateRaw("raw-model/profile-activation-file-with-allowed-expressions.xml");
|
SimpleProblemCollector result = validateRaw(
|
||||||
|
"raw-model/profile-activation-file-with-allowed-expressions.xml",
|
||||||
|
mbr -> mbr.setUserProperties(new Properties() {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
{
|
||||||
|
setProperty("foo", "foo");
|
||||||
|
setProperty("bar", "foo");
|
||||||
|
}
|
||||||
|
}));
|
||||||
assertViolations(result, 0, 0, 0);
|
assertViolations(result, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void profileActivationWithProjectExpression() throws Exception {
|
void profileActivationFileWithProjectExpression() throws Exception {
|
||||||
SimpleProblemCollector result = validateRaw("raw-model/profile-activation-file-with-project-expressions.xml");
|
SimpleProblemCollector result = validateRaw("raw-model/profile-activation-file-with-project-expressions.xml");
|
||||||
assertViolations(result, 0, 0, 2);
|
assertViolations(result, 0, 0, 2);
|
||||||
|
|
||||||
assertEquals(
|
assertEquals(
|
||||||
"'profiles.profile[exists-project-version].activation.file.exists' "
|
"'profiles.profile[exists-project-version].activation.file.exists' "
|
||||||
+ "Failed to interpolate file location ${project.version}/test.txt: "
|
+ "Failed to interpolate profile activation property ${project.version}/test.txt: "
|
||||||
+ "${project.version} expressions are not supported during profile activation.",
|
+ "${project.version} expressions are not supported during profile activation.",
|
||||||
result.getWarnings().get(0));
|
result.getWarnings().get(0));
|
||||||
|
|
||||||
assertEquals(
|
assertEquals(
|
||||||
"'profiles.profile[missing-project-version].activation.file.missing' "
|
"'profiles.profile[missing-project-version].activation.file.missing' "
|
||||||
+ "Failed to interpolate file location ${project.version}/test.txt: "
|
+ "Failed to interpolate profile activation property ${project.version}/test.txt: "
|
||||||
|
+ "${project.version} expressions are not supported during profile activation.",
|
||||||
|
result.getWarnings().get(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void profileActivationPropertyWithProjectExpression() throws Exception {
|
||||||
|
SimpleProblemCollector result =
|
||||||
|
validateRaw("raw-model/profile-activation-property-with-project-expressions.xml");
|
||||||
|
assertViolations(result, 0, 0, 2);
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
"'profiles.profile[property-name-project-version].activation.property.name' "
|
||||||
|
+ "Failed to interpolate profile activation property ${project.version}: "
|
||||||
|
+ "${project.version} expressions are not supported during profile activation.",
|
||||||
|
result.getWarnings().get(0));
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
"'profiles.profile[property-value-project-version].activation.property.value' "
|
||||||
|
+ "Failed to interpolate profile activation property ${project.version}: "
|
||||||
+ "${project.version} expressions are not supported during profile activation.",
|
+ "${project.version} expressions are not supported during profile activation.",
|
||||||
result.getWarnings().get(1));
|
result.getWarnings().get(1));
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,5 +60,23 @@ under the License.
|
||||||
</activation>
|
</activation>
|
||||||
</profile>
|
</profile>
|
||||||
|
|
||||||
|
<profile>
|
||||||
|
<id>dynamic-property-available</id>
|
||||||
|
<activation>
|
||||||
|
<property>
|
||||||
|
<name>${activationProperty}</name>
|
||||||
|
</property>
|
||||||
|
</activation>
|
||||||
|
</profile>
|
||||||
|
|
||||||
|
<profile>
|
||||||
|
<id>matches-another-property</id>
|
||||||
|
<activation>
|
||||||
|
<property>
|
||||||
|
<name>foo</name>
|
||||||
|
<value>${bar}</value>
|
||||||
|
</property>
|
||||||
|
</activation>
|
||||||
|
</profile>
|
||||||
</profiles>
|
</profiles>
|
||||||
</project>
|
</project>
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
<!--
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
or more contributor license agreements. See the NOTICE file
|
||||||
|
distributed with this work for additional information
|
||||||
|
regarding copyright ownership. The ASF licenses this file
|
||||||
|
to you under the Apache License, Version 2.0 (the
|
||||||
|
"License"); you may not use this file except in compliance
|
||||||
|
with the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing,
|
||||||
|
software distributed under the License is distributed on an
|
||||||
|
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations
|
||||||
|
under the License.
|
||||||
|
-->
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<artifactId>aid</artifactId>
|
||||||
|
<groupId>gid</groupId>
|
||||||
|
<version>0.1</version>
|
||||||
|
<packaging>pom</packaging>
|
||||||
|
|
||||||
|
<profiles>
|
||||||
|
|
||||||
|
<profile>
|
||||||
|
<id>property-name-project-version</id>
|
||||||
|
<activation>
|
||||||
|
<property>
|
||||||
|
<name>${project.version}</name>
|
||||||
|
</property>
|
||||||
|
</activation>
|
||||||
|
</profile>
|
||||||
|
<profile>
|
||||||
|
<id>property-value-project-version</id>
|
||||||
|
<activation>
|
||||||
|
<property>
|
||||||
|
<name>project.version</name>
|
||||||
|
<value>${project.version}</value>
|
||||||
|
</property>
|
||||||
|
</activation>
|
||||||
|
</profile>
|
||||||
|
|
||||||
|
</profiles>
|
||||||
|
</project>
|
Loading…
Reference in New Issue