diff --git a/org.eclipse.m2e.core/META-INF/MANIFEST.MF b/org.eclipse.m2e.core/META-INF/MANIFEST.MF index 61cad785f..187ebf94b 100644 --- a/org.eclipse.m2e.core/META-INF/MANIFEST.MF +++ b/org.eclipse.m2e.core/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %Bundle-Name Bundle-SymbolicName: org.eclipse.m2e.core;singleton:=true -Bundle-Version: 2.0.4.qualifier +Bundle-Version: 2.0.5.qualifier Bundle-Activator: org.eclipse.m2e.core.internal.MavenPluginActivator Bundle-Vendor: %Bundle-Vendor Bundle-Localization: plugin diff --git a/org.eclipse.m2e.core/pom.xml b/org.eclipse.m2e.core/pom.xml index 018368056..559f4b3e2 100644 --- a/org.eclipse.m2e.core/pom.xml +++ b/org.eclipse.m2e.core/pom.xml @@ -19,7 +19,7 @@ org.eclipse.m2e.core - 2.0.4-SNAPSHOT + 2.0.5-SNAPSHOT eclipse-plugin Maven Integration for Eclipse Core Plug-in diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/embedder/IMaven.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/embedder/IMaven.java index fd7c117c7..b632ba2e0 100644 --- a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/embedder/IMaven.java +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/embedder/IMaven.java @@ -158,6 +158,17 @@ MojoExecution setupMojoExecution(MavenProject project, MojoExecution execution, throws CoreException; /** + * Resolves a configuration parameter from the given {@code mojoExecution}. It coerces from String to the given type + * and considers expressions and default values. + * + * @param + * @param project the Maven project + * @param mojoExecution the mojo execution from which to retrieve the configuration value + * @param parameter the name of the parameter (may be nested with separating {@code .}) + * @param asType the type to coerce to + * @param monitor the progress monitor + * @return the parameter value or {@code null} if the parameter with the given name was not found + * @throws CoreException * @since 1.4 */ T getMojoParameterValue(MavenProject project, MojoExecution mojoExecution, String parameter, diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/embedder/MavenImpl.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/embedder/MavenImpl.java index a6cc1fc65..a064c1e86 100644 --- a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/embedder/MavenImpl.java +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/embedder/MavenImpl.java @@ -709,8 +709,24 @@ private String formatAsDirectory(String directory) { return directory.replace(GROUP_SEPARATOR, PATH_SEPARATOR); } - private T getMojoParameterValue(MavenSession session, MojoExecution mojoExecution, String parameter, + private T getMojoParameterValue(MavenSession session, MojoExecution mojoExecution, List parameterPath, Class asType) throws CoreException { + Xpp3Dom dom = mojoExecution.getConfiguration(); + if(dom == null) { + return null; + } + PlexusConfiguration configuration = new XmlPlexusConfiguration(dom); + for(String parameter : parameterPath) { + configuration = configuration.getChild(parameter); + if(configuration == null) { + return null; + } + } + return getMojoParameterValue(session, mojoExecution, configuration, asType, String.join("/", parameterPath)); + } + + private T getMojoParameterValue(MavenSession session, MojoExecution mojoExecution, + PlexusConfiguration configuration, Class asType, String parameterLabel) throws CoreException { try { MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor(); @@ -719,20 +735,13 @@ private T getMojoParameterValue(MavenSession session, MojoExecution mojoExec ExpressionEvaluator expressionEvaluator = new PluginParameterExpressionEvaluator(session, mojoExecution); ConfigurationConverter typeConverter = converterLookup.lookupConverterForType(asType); - Xpp3Dom dom = mojoExecution.getConfiguration(); - if(dom == null) { - return null; - } - PlexusConfiguration configuration = new XmlPlexusConfiguration(dom).getChild(parameter); - if(configuration == null) { - return null; - } + Object value = typeConverter.fromConfiguration(converterLookup, configuration, asType, mojoDescriptor.getImplementationClass(), pluginRealm, expressionEvaluator, null); return asType.cast(value); } catch(Exception e) { - throw new CoreException(Status - .error(NLS.bind(Messages.MavenImpl_error_param_for_execution, parameter, mojoExecution.getExecutionId()), e)); + throw new CoreException(Status.error( + NLS.bind(Messages.MavenImpl_error_param_for_execution, parameterLabel, mojoExecution.getExecutionId()), e)); } } @@ -740,7 +749,30 @@ private T getMojoParameterValue(MavenSession session, MojoExecution mojoExec public T getMojoParameterValue(MavenProject project, MojoExecution mojoExecution, String parameter, Class asType, IProgressMonitor monitor) throws CoreException { return getExecutionContext().execute(project, - (context, pm) -> getMojoParameterValue(context.getSession(), mojoExecution, parameter, asType), monitor); + (context, pm) -> getMojoParameterValue(context.getSession(), mojoExecution, List.of(parameter), asType), + monitor); + } + + /** + * Resolves a nested configuration parameter from the given {@code mojoExecution}. It coerces from String to the given + * type and considers expressions and default values. Deliberately no public API yet as probably refactored in the + * near future. + * + * @param + * @param project the Maven project + * @param mojoExecution the mojo execution from which to retrieve the configuration value + * @param parameterPath the path of the parameter to look up, the first item is the name of the element directly below + * {@code } and the last one is the element containing the actual value + * @param asType the type to coerce to + * @param monitor the progress monitor + * @return the parameter value or {@code null} if the parameter with the given name was not found + * @throws CoreException + * @see IMaven#getMojoParameterValue(MavenProject, MojoExecution, String, Class, IProgressMonitor) + */ + public T getMojoParameterValue(MavenProject project, MojoExecution mojoExecution, List parameterPath, + Class asType, IProgressMonitor monitor) throws CoreException { + return getExecutionContext().execute(project, + (context, pm) -> getMojoParameterValue(context.getSession(), mojoExecution, parameterPath, asType), monitor); } private T getMojoParameterValue(String parameter, Class type, MavenSession session, Plugin plugin, diff --git a/org.eclipse.m2e.jdt.tests/projects/enforcerSettingsWithVersion/pom.xml b/org.eclipse.m2e.jdt.tests/projects/enforcerSettingsWithVersion/pom.xml new file mode 100644 index 000000000..fcf1350b3 --- /dev/null +++ b/org.eclipse.m2e.jdt.tests/projects/enforcerSettingsWithVersion/pom.xml @@ -0,0 +1,47 @@ + + + 4.0.0 + foo.bar + demo + 0.0.1-SNAPSHOT + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.10.1 + + 1.8 + 1.8 + + + + + + + org.apache.maven.plugins + maven-enforcer-plugin + 3.1.0 + + + enforce-java-version + + enforce + + validate + + + + 13.0.3 + + + + + + + + + diff --git a/org.eclipse.m2e.jdt.tests/projects/enforcerSettingsWithVersionRange/pom.xml b/org.eclipse.m2e.jdt.tests/projects/enforcerSettingsWithVersionRange/pom.xml new file mode 100644 index 000000000..888ab0d30 --- /dev/null +++ b/org.eclipse.m2e.jdt.tests/projects/enforcerSettingsWithVersionRange/pom.xml @@ -0,0 +1,47 @@ + + + 4.0.0 + foo.bar + demo + 0.0.1-SNAPSHOT + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.10.1 + + 1.8 + 1.8 + + + + + + + org.apache.maven.plugins + maven-enforcer-plugin + 3.1.0 + + + enforce-java-version + + enforce + + validate + + + + [11.0.10,16) + + + + + + + + + diff --git a/org.eclipse.m2e.jdt.tests/src/org/eclipse/m2e/jdt/tests/JavaConfigurationFromEnforcer.java b/org.eclipse.m2e.jdt.tests/src/org/eclipse/m2e/jdt/tests/JavaConfigurationFromEnforcer.java new file mode 100644 index 000000000..acab42fbf --- /dev/null +++ b/org.eclipse.m2e.jdt.tests/src/org/eclipse/m2e/jdt/tests/JavaConfigurationFromEnforcer.java @@ -0,0 +1,59 @@ +/******************************************************************************* + * Copyright (c) 2022, 2022 Hannes Wellmann and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Hannes Wellmann - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.jdt.tests; + +import static org.junit.Assert.assertEquals; + +import java.util.Arrays; +import java.util.List; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.IPath; +import org.eclipse.jdt.core.IClasspathEntry; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.JavaCore; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.m2e.tests.common.AbstractMavenProjectTestCase; +import org.junit.Test; + +public class JavaConfigurationFromEnforcer extends AbstractMavenProjectTestCase { + private static final String JRE_CONTAINER_PREFIX = "org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/"; + + @Test + public void testEnforcerVersion() throws Exception { + IProject project = importProject("projects/enforcerSettingsWithVersion/pom.xml"); + waitForJobsToComplete(); + IJavaProject jproject = JavaCore.create(project); + assertEquals("1.8", jproject.getOption(JavaCore.COMPILER_SOURCE, false)); + assertEquals("1.8", jproject.getOption(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, false)); + assertEquals(List.of("JavaSE-13"), getJREContainerVMType(jproject)); + } + + @Test + public void testEnforcerVersionRange() throws Exception { + IProject project = importProject("projects/enforcerSettingsWithVersionRange/pom.xml"); + waitForJobsToComplete(); + IJavaProject jproject = JavaCore.create(project); + assertEquals("1.8", jproject.getOption(JavaCore.COMPILER_SOURCE, false)); + assertEquals("1.8", jproject.getOption(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, false)); + assertEquals(List.of("JavaSE-11"), getJREContainerVMType(jproject)); + } + + private static List getJREContainerVMType(IJavaProject jproject) throws JavaModelException { + return Arrays.stream(jproject.getRawClasspath()) + .filter(cp -> cp.getEntryKind() == IClasspathEntry.CPE_CONTAINER).map(IClasspathEntry::getPath) + .map(IPath::toString).filter(p -> p.startsWith(JRE_CONTAINER_PREFIX)) + .map(p -> p.substring(JRE_CONTAINER_PREFIX.length())).toList(); + } +} diff --git a/org.eclipse.m2e.jdt/META-INF/MANIFEST.MF b/org.eclipse.m2e.jdt/META-INF/MANIFEST.MF index a35c32ed4..b7c512a02 100644 --- a/org.eclipse.m2e.jdt/META-INF/MANIFEST.MF +++ b/org.eclipse.m2e.jdt/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %Bundle-Name Bundle-SymbolicName: org.eclipse.m2e.jdt;singleton:=true -Bundle-Version: 2.0.2.qualifier +Bundle-Version: 2.0.3.qualifier Bundle-Localization: plugin Export-Package: org.eclipse.m2e.jdt, org.eclipse.m2e.jdt.internal;x-friends:="org.eclipse.m2e.jdt.ui", diff --git a/org.eclipse.m2e.jdt/src/org/eclipse/m2e/jdt/internal/AbstractJavaProjectConfigurator.java b/org.eclipse.m2e.jdt/src/org/eclipse/m2e/jdt/internal/AbstractJavaProjectConfigurator.java index acebe4f8c..b589e6964 100644 --- a/org.eclipse.m2e.jdt/src/org/eclipse/m2e/jdt/internal/AbstractJavaProjectConfigurator.java +++ b/org.eclipse.m2e.jdt/src/org/eclipse/m2e/jdt/internal/AbstractJavaProjectConfigurator.java @@ -44,11 +44,10 @@ import org.eclipse.jdt.launching.environments.IExecutionEnvironment; import org.eclipse.jdt.launching.environments.IExecutionEnvironmentsManager; -import org.codehaus.plexus.util.xml.Xpp3Dom; - import org.apache.maven.artifact.versioning.ArtifactVersion; import org.apache.maven.artifact.versioning.DefaultArtifactVersion; import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; +import org.apache.maven.artifact.versioning.Restriction; import org.apache.maven.artifact.versioning.VersionRange; import org.apache.maven.model.Resource; import org.apache.maven.plugin.MojoExecution; @@ -56,6 +55,7 @@ import org.eclipse.m2e.core.MavenPlugin; import org.eclipse.m2e.core.internal.M2EUtils; +import org.eclipse.m2e.core.internal.embedder.MavenImpl; import org.eclipse.m2e.core.project.IMavenProjectFacade; import org.eclipse.m2e.core.project.IProjectConfigurationManager; import org.eclipse.m2e.core.project.configurator.AbstractProjectConfigurator; @@ -254,11 +254,12 @@ private IExecutionEnvironment getExecutionEnvironment(String environmentId) { return null; } IExecutionEnvironmentsManager manager = JavaRuntime.getExecutionEnvironmentsManager(); - for(IExecutionEnvironment environment : manager.getExecutionEnvironments()) { - if(environment.getId().equals(environmentId)) { - return environment; - } + IExecutionEnvironment environment = manager.getEnvironment(environmentId); + if(environment != null && environment.getCompatibleVMs().length > 0) { + return environment; } + log.error("Failed to find a compatible VM for environment id '{}', falling back to workspace default", + environmentId); return null; } @@ -845,45 +846,41 @@ private String getMinimumJavaBuildEnvironmentId(ProjectConfigurationRequest requ try { List mojoExecutions = getEnforcerMojoExecutions(request, monitor); for(MojoExecution mojoExecution : mojoExecutions) { - String version = getMinimumJavaBuildEnvironmentId(mojoExecution); + String version = getMinimumJavaBuildEnvironmentId(request.mavenProject(), mojoExecution, monitor); if(version != null) { return version; } } } catch(CoreException | InvalidVersionSpecificationException ex) { - log.error("Failed to determine minimum build version, assuming default", ex); + log.error("Failed to determine minimum build Java version, assuming default", ex); } return null; } - private String getMinimumJavaBuildEnvironmentId(MojoExecution mojoExecution) - throws InvalidVersionSpecificationException { + private String getMinimumJavaBuildEnvironmentId(MavenProject mavenProject, MojoExecution mojoExecution, + IProgressMonitor monitor) throws InvalidVersionSpecificationException, CoreException { // https://maven.apache.org/enforcer/enforcer-rules/requireJavaVersion.html - Xpp3Dom dom = mojoExecution.getConfiguration(); - Xpp3Dom rules = dom.getChild("rules"); - if(rules == null) { - return null; - } - Xpp3Dom requireJavaVersion = rules.getChild("requireJavaVersion"); - if(requireJavaVersion == null) { - return null; - } - Xpp3Dom version = requireJavaVersion.getChild("version"); - if(version == null) { - return null; - } - return getMinimumJavaBuildEnvironmentId(version.getValue()); + String version = ((MavenImpl) maven).getMojoParameterValue(mavenProject, mojoExecution, + List.of("rules", "requireJavaVersion", "version"), String.class, monitor); + return getMinimumJavaBuildEnvironmentId(version); } private String getMinimumJavaBuildEnvironmentId(String versionSpec) throws InvalidVersionSpecificationException { VersionRange vr = VersionRange.createFromVersionSpec(versionSpec); ArtifactVersion recommendedVersion = vr.getRecommendedVersion(); + List versionRestrictions = List.of(); + if(recommendedVersion == null) { + versionRestrictions = getVersionRangeRestrictionsIgnoringMicroAndQualifier(vr); + } else { + // only consider major and minor version here, micro and qualifier not relevant inside IDE (probably) + recommendedVersion = getMajorMinorOnlyVersion(recommendedVersion); + } // find lowest matching environment id for(Entry entry : ENVIRONMENTS.entrySet()) { ArtifactVersion environmentVersion = new DefaultArtifactVersion(entry.getKey()); - boolean foundMatchingVersion = false; + boolean foundMatchingVersion; if(recommendedVersion == null) { - foundMatchingVersion = vr.containsVersion(environmentVersion); + foundMatchingVersion = versionRestrictions.stream().anyMatch(r -> r.containsVersion(environmentVersion)); } else { // only singular versions ever have a recommendedVersion int compareTo = recommendedVersion.compareTo(environmentVersion); @@ -896,6 +893,20 @@ private String getMinimumJavaBuildEnvironmentId(String versionSpec) throws Inval return null; } + private static List getVersionRangeRestrictionsIgnoringMicroAndQualifier(VersionRange versionRange) { + return versionRange.getRestrictions().stream().map(restriction -> { + ArtifactVersion lowerBound = restriction.getLowerBound(); + ArtifactVersion upperBound = restriction.getUpperBound(); + return new Restriction(// + lowerBound != null ? getMajorMinorOnlyVersion(lowerBound) : null, restriction.isLowerBoundInclusive(), + upperBound != null ? getMajorMinorOnlyVersion(upperBound) : null, restriction.isUpperBoundInclusive()); + }).toList(); + } + + private static ArtifactVersion getMajorMinorOnlyVersion(ArtifactVersion lower) { + return new DefaultArtifactVersion(lower.getMajorVersion() + "." + lower.getMinorVersion()); + } + private double asDouble(String level) { if(level == null || level.isEmpty()) { return -1;