From 5f5217d5a00d1c3fc3a6db5a54ceb976e6337f6c Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Fri, 23 Jan 2026 12:09:34 -0800 Subject: [PATCH] Align Eclipse import with Gradle build Update `JavaConventions` and `EclipseConventions` to align the Eclipse import with the regular Gradle Build. Imported projects now use Java 25 for the JRE but have compatibility set to Java 17 and a project configuration that uses the `--release` flag. This update also removes the calls to `setSourceCompatibility` and `setTargetCompatibility` on the `JavaPluginExtension` in favor of just using `compile.options.release`. We now also enforce that Java 25 or above is being used to build the project. Since buildship doesn't offer a way to set the `--release` JDT project setting, a custom task is used. Projects may need to be reimported or cleaned to actually have the setting apply correctly. Closes gh-48929 --- .../boot/build/EclipseConventions.java | 39 +++++++++-- .../build/EclipseSynchronizeJdtSettings.java | 66 +++++++++++++++++++ .../boot/build/JavaConventions.java | 43 ++++++++---- 3 files changed, 129 insertions(+), 19 deletions(-) create mode 100644 buildSrc/src/main/java/org/springframework/boot/build/EclipseSynchronizeJdtSettings.java diff --git a/buildSrc/src/main/java/org/springframework/boot/build/EclipseConventions.java b/buildSrc/src/main/java/org/springframework/boot/build/EclipseConventions.java index f692b3e70f0..3116cfc7b95 100644 --- a/buildSrc/src/main/java/org/springframework/boot/build/EclipseConventions.java +++ b/buildSrc/src/main/java/org/springframework/boot/build/EclipseConventions.java @@ -16,13 +16,17 @@ package org.springframework.boot.build; +import org.gradle.api.DomainObjectCollection; +import org.gradle.api.JavaVersion; import org.gradle.api.Project; import org.gradle.api.plugins.JavaBasePlugin; +import org.gradle.api.tasks.TaskProvider; import org.gradle.plugins.ide.api.XmlFileContentMerger; import org.gradle.plugins.ide.eclipse.EclipsePlugin; import org.gradle.plugins.ide.eclipse.model.Classpath; import org.gradle.plugins.ide.eclipse.model.ClasspathEntry; import org.gradle.plugins.ide.eclipse.model.EclipseClasspath; +import org.gradle.plugins.ide.eclipse.model.EclipseJdt; import org.gradle.plugins.ide.eclipse.model.EclipseModel; import org.gradle.plugins.ide.eclipse.model.Library; @@ -35,12 +39,35 @@ import org.gradle.plugins.ide.eclipse.model.Library; class EclipseConventions { void apply(Project project) { - project.getPlugins() - .withType(EclipsePlugin.class, - (eclipse) -> project.getPlugins().withType(JavaBasePlugin.class, (javaBase) -> { - EclipseModel eclipseModel = project.getExtensions().getByType(EclipseModel.class); - eclipseModel.classpath(this::configureClasspath); - })); + project.getPlugins().withType(EclipsePlugin.class, (eclipse) -> configure(project, eclipse)); + } + + private DomainObjectCollection configure(Project project, EclipsePlugin eclipsePlugin) { + TaskProvider eclipseSynchronizeJdtSettings = registerEclipseSynchronizeJdtSettingsTask( + project); + return project.getPlugins().withType(JavaBasePlugin.class, (javaBase) -> { + EclipseModel model = project.getExtensions().getByType(EclipseModel.class); + model.synchronizationTasks(eclipseSynchronizeJdtSettings); + model.jdt(this::configureJdt); + model.classpath(this::configureClasspath); + }); + } + + private TaskProvider registerEclipseSynchronizeJdtSettingsTask(Project project) { + TaskProvider taskProvider = project.getTasks() + .register("eclipseSynchronizateJdt", EclipseSynchronizeJdtSettings.class); + taskProvider.configure((task) -> { + task.setDescription("Synchronizate the Eclipse JDT settings file from Buildship."); + task.setOutputFile(project.file(".settings/org.eclipse.jdt.core.prefs")); + task.setInputFile(project.file(".settings/org.eclipse.jdt.core.prefs")); + }); + return taskProvider; + } + + private void configureJdt(EclipseJdt jdt) { + jdt.setSourceCompatibility(JavaVersion.toVersion(JavaConventions.RUNTIME_JAVA_VERSION)); + jdt.setTargetCompatibility(JavaVersion.toVersion(JavaConventions.RUNTIME_JAVA_VERSION)); + jdt.setJavaRuntimeName("JavaSE-" + JavaConventions.BUILD_JAVA_VERSION); } private void configureClasspath(EclipseClasspath classpath) { diff --git a/buildSrc/src/main/java/org/springframework/boot/build/EclipseSynchronizeJdtSettings.java b/buildSrc/src/main/java/org/springframework/boot/build/EclipseSynchronizeJdtSettings.java new file mode 100644 index 00000000000..e4a477b1139 --- /dev/null +++ b/buildSrc/src/main/java/org/springframework/boot/build/EclipseSynchronizeJdtSettings.java @@ -0,0 +1,66 @@ +/* + * Copyright 2025 the original author or authors. + * + * Licensed 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 + * + * https://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.springframework.boot.build; + +import java.util.Properties; + +import org.gradle.api.Task; +import org.gradle.api.internal.PropertiesTransformer; +import org.gradle.plugins.ide.api.PropertiesGeneratorTask; +import org.gradle.plugins.ide.internal.generator.PropertiesPersistableConfigurationObject; + +import org.springframework.boot.build.EclipseSynchronizeJdtSettings.Configuration; + +/** + * {@link Task} to do something. + * + * @author Phillip Webb + */ +public abstract class EclipseSynchronizeJdtSettings extends PropertiesGeneratorTask { + + @Override + protected Configuration create() { + return new Configuration(getTransformer()); + } + + @Override + protected void configure(Configuration configuration) { + } + + static class Configuration extends PropertiesPersistableConfigurationObject { + + Configuration(PropertiesTransformer transformer) { + super(transformer); + } + + @Override + protected String getDefaultResourceName() { + return null; + } + + @Override + protected void load(Properties properties) { + } + + @Override + protected void store(Properties properties) { + properties.put("org.eclipse.jdt.core.compiler.release", "true"); + } + + } + +} diff --git a/buildSrc/src/main/java/org/springframework/boot/build/JavaConventions.java b/buildSrc/src/main/java/org/springframework/boot/build/JavaConventions.java index 2b1f053a67d..7a75f86ef64 100644 --- a/buildSrc/src/main/java/org/springframework/boot/build/JavaConventions.java +++ b/buildSrc/src/main/java/org/springframework/boot/build/JavaConventions.java @@ -16,8 +16,10 @@ package org.springframework.boot.build; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -32,6 +34,7 @@ import io.spring.gradle.nullability.NullabilityPluginExtension; import io.spring.javaformat.gradle.SpringJavaFormatPlugin; import io.spring.javaformat.gradle.tasks.CheckFormat; import io.spring.javaformat.gradle.tasks.Format; +import org.gradle.api.GradleException; import org.gradle.api.JavaVersion; import org.gradle.api.Project; import org.gradle.api.Task; @@ -53,6 +56,9 @@ import org.gradle.api.tasks.compile.JavaCompile; import org.gradle.api.tasks.javadoc.Javadoc; import org.gradle.api.tasks.testing.Test; import org.gradle.external.javadoc.CoreJavadocOptions; +import org.gradle.jvm.toolchain.JavaCompiler; +import org.gradle.jvm.toolchain.JavaInstallationMetadata; +import org.gradle.jvm.toolchain.JavaLanguageVersion; import org.springframework.boot.build.architecture.ArchitecturePlugin; import org.springframework.boot.build.classpath.CheckClasspathForProhibitedDependencies; @@ -127,7 +133,9 @@ import org.springframework.util.StringUtils; */ class JavaConventions { - private static final String SOURCE_AND_TARGET_COMPATIBILITY = "17"; + public static final int BUILD_JAVA_VERSION = 25; + + public static final int RUNTIME_JAVA_VERSION = 17; void apply(Project project) { project.getPlugins().withType(JavaBasePlugin.class, (java) -> { @@ -165,7 +173,8 @@ class JavaConventions { jar.manifest((manifest) -> { Map attributes = new TreeMap<>(); attributes.put("Automatic-Module-Name", project.getName().replace("-", ".")); - attributes.put("Build-Jdk-Spec", SOURCE_AND_TARGET_COMPATIBILITY); + // Build-Jdk-Spec is used by buildpacks to pick the JRE to install + attributes.put("Build-Jdk-Spec", RUNTIME_JAVA_VERSION); attributes.put("Built-By", "Spring"); attributes.put("Implementation-Title", determineImplementationTitle(project, sourceJarTaskNames, javadocJarTaskNames, jar)); @@ -243,23 +252,31 @@ class JavaConventions { } private void configureJavaConventions(Project project) { - if (!project.hasProperty("toolchainVersion")) { - JavaPluginExtension javaPluginExtension = project.getExtensions().getByType(JavaPluginExtension.class); - javaPluginExtension.setSourceCompatibility(JavaVersion.toVersion(SOURCE_AND_TARGET_COMPATIBILITY)); - javaPluginExtension.setTargetCompatibility(JavaVersion.toVersion(SOURCE_AND_TARGET_COMPATIBILITY)); - } project.getTasks().withType(JavaCompile.class, (compile) -> { + compile.doFirst((task) -> assertCompatible(compile)); compile.getOptions().setEncoding("UTF-8"); - compile.getOptions().getRelease().set(17); - List args = compile.getOptions().getCompilerArgs(); - if (!args.contains("-parameters")) { - args.add("-parameters"); - } - args.addAll(Arrays.asList("-Werror", "-Xlint:unchecked", "-Xlint:deprecation", "-Xlint:rawtypes", + compile.getOptions().getRelease().set(RUNTIME_JAVA_VERSION); + Set args = new LinkedHashSet<>(compile.getOptions().getCompilerArgs()); + args.addAll(List.of("-parameters", "-Werror", "-Xlint:unchecked", "-Xlint:deprecation", "-Xlint:rawtypes", "-Xlint:varargs")); + compile.getOptions().setCompilerArgs(new ArrayList<>(args)); }); } + private void assertCompatible(JavaCompile compile) { + JavaVersion requiredVersion = JavaVersion.toVersion(BUILD_JAVA_VERSION); + JavaVersion actualVersion = compile.getJavaCompiler() + .map(JavaCompiler::getMetadata) + .map(JavaInstallationMetadata::getLanguageVersion) + .map(JavaLanguageVersion::asInt) + .map(JavaVersion::toVersion) + .orElse(JavaVersion.current()) + .get(); + if (!actualVersion.isCompatibleWith(requiredVersion)) { + throw new GradleException("This project should be built with Java %s or above".formatted(requiredVersion)); + } + } + private void configureSpringJavaFormat(Project project) { project.getPlugins().apply(SpringJavaFormatPlugin.class); project.getTasks().withType(Format.class, (Format) -> Format.setEncoding("UTF-8"));