From 09ccba1ccc8a0a2398d6f5a66244b28b2289ead8 Mon Sep 17 00:00:00 2001 From: Vincent Potucek Date: Sun, 1 Jun 2025 08:59:01 +0200 Subject: [PATCH 01/15] spotless --- buildSrc/build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index 7d0027111ba2..785935ff536c 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -1,6 +1,7 @@ plugins { id 'java-gradle-plugin' id 'checkstyle' + id 'com.diffplug.gradle.spotless' version '7.0.4' } repositories { From f0e0dbac432927f44fde87487e3616e1f1ebd161 Mon Sep 17 00:00:00 2001 From: Vincent Potucek Date: Sun, 1 Jun 2025 09:07:25 +0200 Subject: [PATCH 02/15] spotless --- buildSrc/build.gradle | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index 785935ff536c..417bc8e1f437 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -1,9 +1,13 @@ plugins { id 'java-gradle-plugin' - id 'checkstyle' id 'com.diffplug.gradle.spotless' version '7.0.4' } - +spotless { + java { + removeUnusedImports() + googleJavaFormat() + } +} repositories { mavenCentral() gradlePluginPortal() From a712fcdd91db3623b433cc5ff42a7a6f9e87a665 Mon Sep 17 00:00:00 2001 From: Vincent Potucek Date: Sun, 1 Jun 2025 09:07:52 +0200 Subject: [PATCH 03/15] spotless --- buildSrc/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index 417bc8e1f437..234764f62b32 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java-gradle-plugin' - id 'com.diffplug.gradle.spotless' version '7.0.4' + id 'com.diffplug.spotless' version '7.0.4' } spotless { java { From 8adab67bb560d16f92f7bfeb818981d82784a0a3 Mon Sep 17 00:00:00 2001 From: Vincent Potucek Date: Sun, 1 Jun 2025 09:10:10 +0200 Subject: [PATCH 04/15] spotless --- buildSrc/build.gradle | 1 - buildSrc/config/checkstyle/checkstyle.xml | 26 ------ .../build/CheckstyleConventions.java | 79 ------------------- .../build/ConventionsPlugin.java | 5 +- 4 files changed, 2 insertions(+), 109 deletions(-) delete mode 100644 buildSrc/config/checkstyle/checkstyle.xml delete mode 100644 buildSrc/src/main/java/org/springframework/build/CheckstyleConventions.java diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index 234764f62b32..444de5d035e7 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -23,7 +23,6 @@ ext { } dependencies { - checkstyle "io.spring.javaformat:spring-javaformat-checkstyle:${javaFormatVersion}" implementation "org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}" implementation "com.tngtech.archunit:archunit:1.4.0" implementation "org.gradle:test-retry-gradle-plugin:1.6.2" diff --git a/buildSrc/config/checkstyle/checkstyle.xml b/buildSrc/config/checkstyle/checkstyle.xml deleted file mode 100644 index b75fed0d6583..000000000000 --- a/buildSrc/config/checkstyle/checkstyle.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/buildSrc/src/main/java/org/springframework/build/CheckstyleConventions.java b/buildSrc/src/main/java/org/springframework/build/CheckstyleConventions.java deleted file mode 100644 index 9cdcca1af129..000000000000 --- a/buildSrc/src/main/java/org/springframework/build/CheckstyleConventions.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2002-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.build; - -import java.io.File; -import java.nio.file.Path; -import java.util.List; - -import io.spring.javaformat.gradle.SpringJavaFormatPlugin; -import io.spring.nohttp.gradle.NoHttpExtension; -import io.spring.nohttp.gradle.NoHttpPlugin; -import org.gradle.api.Plugin; -import org.gradle.api.Project; -import org.gradle.api.artifacts.DependencySet; -import org.gradle.api.plugins.JavaBasePlugin; -import org.gradle.api.plugins.quality.Checkstyle; -import org.gradle.api.plugins.quality.CheckstyleExtension; -import org.gradle.api.plugins.quality.CheckstylePlugin; - -/** - * {@link Plugin} that applies conventions for checkstyle. - * - * @author Brian Clozel - */ -public class CheckstyleConventions { - - /** - * Applies the Spring Java Format and Checkstyle plugins with the project conventions. - * @param project the current project - */ - public void apply(Project project) { - project.getPlugins().withType(JavaBasePlugin.class, (java) -> { - if (project.getRootProject() == project) { - configureNoHttpPlugin(project); - } - project.getPlugins().apply(CheckstylePlugin.class); - project.getTasks().withType(Checkstyle.class).forEach(checkstyle -> checkstyle.getMaxHeapSize().set("1g")); - CheckstyleExtension checkstyle = project.getExtensions().getByType(CheckstyleExtension.class); - checkstyle.setToolVersion("10.23.1"); - checkstyle.getConfigDirectory().set(project.getRootProject().file("src/checkstyle")); - String version = SpringJavaFormatPlugin.class.getPackage().getImplementationVersion(); - DependencySet checkstyleDependencies = project.getConfigurations().getByName("checkstyle").getDependencies(); - checkstyleDependencies.add( - project.getDependencies().create("io.spring.javaformat:spring-javaformat-checkstyle:" + version)); - }); - } - - private static void configureNoHttpPlugin(Project project) { - project.getPlugins().apply(NoHttpPlugin.class); - NoHttpExtension noHttp = project.getExtensions().getByType(NoHttpExtension.class); - noHttp.setAllowlistFile(project.file("src/nohttp/allowlist.lines")); - noHttp.getSource().exclude("**/test-output/**", "**/.settings/**", "**/.classpath", - "**/.project", "**/.gradle/**", "**/node_modules/**", "**/spring-jcl/**", "buildSrc/build/**"); - List buildFolders = List.of("bin", "build", "out"); - project.allprojects(subproject -> { - Path rootPath = project.getRootDir().toPath(); - Path projectPath = rootPath.relativize(subproject.getProjectDir().toPath()); - for (String buildFolder : buildFolders) { - Path innerBuildDir = projectPath.resolve(buildFolder); - noHttp.getSource().exclude(innerBuildDir + File.separator + "**"); - } - }); - } - -} diff --git a/buildSrc/src/main/java/org/springframework/build/ConventionsPlugin.java b/buildSrc/src/main/java/org/springframework/build/ConventionsPlugin.java index 1cd9e43cf0c4..93d54c4a2ab5 100644 --- a/buildSrc/src/main/java/org/springframework/build/ConventionsPlugin.java +++ b/buildSrc/src/main/java/org/springframework/build/ConventionsPlugin.java @@ -27,8 +27,8 @@ * Plugin to apply conventions to projects that are part of Spring Framework's build. * Conventions are applied in response to various plugins being applied. * - *

When the {@link JavaBasePlugin} is applied, the conventions in {@link CheckstyleConventions}, - * {@link TestConventions} and {@link JavaConventions} are applied. + *

When the {@link JavaBasePlugin} is applied, the conventions in {@link TestConventions} + * and {@link JavaConventions} are applied. * The {@link ArchitecturePlugin} plugin is also applied. * When the {@link KotlinBasePlugin} is applied, the conventions in {@link KotlinConventions} * are applied. @@ -41,7 +41,6 @@ public class ConventionsPlugin implements Plugin { public void apply(Project project) { project.getExtensions().create("springFramework", SpringFrameworkExtension.class); new ArchitecturePlugin().apply(project); - new CheckstyleConventions().apply(project); new JavaConventions().apply(project); new KotlinConventions().apply(project); new TestConventions().apply(project); From 67e17b34fceaf82ac77f87524578719c72302be2 Mon Sep 17 00:00:00 2001 From: Vincent Potucek Date: Sun, 1 Jun 2025 09:16:02 +0200 Subject: [PATCH 05/15] spotless --- .../build/ConventionsPlugin.java | 31 +- .../build/JavaConventions.java | 216 +++++++------ .../build/KotlinConventions.java | 45 +-- .../build/SpringFrameworkExtension.java | 52 ++-- .../build/TestConventions.java | 103 ++++--- .../build/architecture/ArchitectureCheck.java | 150 +++++----- .../architecture/ArchitecturePlugin.java | 76 ++--- .../build/architecture/ArchitectureRules.java | 141 ++++----- .../build/dev/LocalDevelopmentPlugin.java | 37 +-- .../RuntimeHintsAgentArgumentProvider.java | 41 +-- .../hint/RuntimeHintsAgentExtension.java | 5 +- .../build/hint/RuntimeHintsAgentPlugin.java | 140 +++++---- .../multirelease/MultiReleaseExtension.java | 230 ++++++++------ .../multirelease/MultiReleaseJarPlugin.java | 45 +-- .../optional/OptionalDependenciesPlugin.java | 53 ++-- .../build/shadow/ShadowSource.java | 283 +++++++++--------- .../MultiReleaseJarPluginTests.java | 131 ++++---- 17 files changed, 974 insertions(+), 805 deletions(-) diff --git a/buildSrc/src/main/java/org/springframework/build/ConventionsPlugin.java b/buildSrc/src/main/java/org/springframework/build/ConventionsPlugin.java index 93d54c4a2ab5..9a564b36328a 100644 --- a/buildSrc/src/main/java/org/springframework/build/ConventionsPlugin.java +++ b/buildSrc/src/main/java/org/springframework/build/ConventionsPlugin.java @@ -20,30 +20,27 @@ import org.gradle.api.Project; import org.gradle.api.plugins.JavaBasePlugin; import org.jetbrains.kotlin.gradle.plugin.KotlinBasePlugin; - import org.springframework.build.architecture.ArchitecturePlugin; /** - * Plugin to apply conventions to projects that are part of Spring Framework's build. - * Conventions are applied in response to various plugins being applied. + * Plugin to apply conventions to projects that are part of Spring Framework's build. Conventions + * are applied in response to various plugins being applied. * - *

When the {@link JavaBasePlugin} is applied, the conventions in {@link TestConventions} - * and {@link JavaConventions} are applied. - * The {@link ArchitecturePlugin} plugin is also applied. - * When the {@link KotlinBasePlugin} is applied, the conventions in {@link KotlinConventions} - * are applied. + *

When the {@link JavaBasePlugin} is applied, the conventions in {@link TestConventions} and + * {@link JavaConventions} are applied. The {@link ArchitecturePlugin} plugin is also applied. When + * the {@link KotlinBasePlugin} is applied, the conventions in {@link KotlinConventions} are + * applied. * * @author Brian Clozel */ public class ConventionsPlugin implements Plugin { - @Override - public void apply(Project project) { - project.getExtensions().create("springFramework", SpringFrameworkExtension.class); - new ArchitecturePlugin().apply(project); - new JavaConventions().apply(project); - new KotlinConventions().apply(project); - new TestConventions().apply(project); - } - + @Override + public void apply(Project project) { + project.getExtensions().create("springFramework", SpringFrameworkExtension.class); + new ArchitecturePlugin().apply(project); + new JavaConventions().apply(project); + new KotlinConventions().apply(project); + new TestConventions().apply(project); + } } diff --git a/buildSrc/src/main/java/org/springframework/build/JavaConventions.java b/buildSrc/src/main/java/org/springframework/build/JavaConventions.java index 634f1ce5924e..e816820f510a 100644 --- a/buildSrc/src/main/java/org/springframework/build/JavaConventions.java +++ b/buildSrc/src/main/java/org/springframework/build/JavaConventions.java @@ -18,7 +18,6 @@ import java.util.ArrayList; import java.util.List; - import org.gradle.api.Plugin; import org.gradle.api.Project; import org.gradle.api.plugins.JavaBasePlugin; @@ -37,101 +36,138 @@ */ public class JavaConventions { - private static final List COMPILER_ARGS; - - private static final List TEST_COMPILER_ARGS; - - /** - * The Java version we should use as the JVM baseline for building the project. - *

NOTE: If you update this value, you should also update the value used in - * the {@code javadoc} task in {@code framework-api.gradle}. - */ - private static final JavaLanguageVersion DEFAULT_LANGUAGE_VERSION = JavaLanguageVersion.of(24); + private static final List COMPILER_ARGS; - /** - * The Java version we should use as the baseline for the compiled bytecode - * (the "-release" compiler argument). - */ - private static final JavaLanguageVersion DEFAULT_RELEASE_VERSION = JavaLanguageVersion.of(17); + private static final List TEST_COMPILER_ARGS; - static { - List commonCompilerArgs = List.of( - "-Xlint:serial", "-Xlint:cast", "-Xlint:classfile", "-Xlint:dep-ann", - "-Xlint:divzero", "-Xlint:empty", "-Xlint:finally", "-Xlint:overrides", - "-Xlint:path", "-Xlint:processing", "-Xlint:static", "-Xlint:try", "-Xlint:-options", - "-parameters" - ); - COMPILER_ARGS = new ArrayList<>(); - COMPILER_ARGS.addAll(commonCompilerArgs); - COMPILER_ARGS.addAll(List.of( - "-Xlint:varargs", "-Xlint:fallthrough", "-Xlint:rawtypes", "-Xlint:deprecation", - "-Xlint:unchecked", "-Werror" - )); - TEST_COMPILER_ARGS = new ArrayList<>(); - TEST_COMPILER_ARGS.addAll(commonCompilerArgs); - TEST_COMPILER_ARGS.addAll(List.of("-Xlint:-varargs", "-Xlint:-fallthrough", "-Xlint:-rawtypes", - "-Xlint:-deprecation", "-Xlint:-unchecked")); - } + /** + * The Java version we should use as the JVM baseline for building the project. + * + *

NOTE: If you update this value, you should also update the value used in the {@code javadoc} + * task in {@code framework-api.gradle}. + */ + private static final JavaLanguageVersion DEFAULT_LANGUAGE_VERSION = JavaLanguageVersion.of(24); - public void apply(Project project) { - project.getPlugins().withType(JavaBasePlugin.class, javaPlugin -> { - applyToolchainConventions(project); - applyJavaCompileConventions(project); - }); - } + /** + * The Java version we should use as the baseline for the compiled bytecode (the "-release" + * compiler argument). + */ + private static final JavaLanguageVersion DEFAULT_RELEASE_VERSION = JavaLanguageVersion.of(17); - /** - * Configure the Toolchain support for the project. - * @param project the current project - */ - private static void applyToolchainConventions(Project project) { - project.getExtensions().getByType(JavaPluginExtension.class).toolchain(toolchain -> { - toolchain.getVendor().set(JvmVendorSpec.BELLSOFT); - toolchain.getLanguageVersion().set(DEFAULT_LANGUAGE_VERSION); - }); - } + static { + List commonCompilerArgs = + List.of( + "-Xlint:serial", + "-Xlint:cast", + "-Xlint:classfile", + "-Xlint:dep-ann", + "-Xlint:divzero", + "-Xlint:empty", + "-Xlint:finally", + "-Xlint:overrides", + "-Xlint:path", + "-Xlint:processing", + "-Xlint:static", + "-Xlint:try", + "-Xlint:-options", + "-parameters"); + COMPILER_ARGS = new ArrayList<>(); + COMPILER_ARGS.addAll(commonCompilerArgs); + COMPILER_ARGS.addAll( + List.of( + "-Xlint:varargs", + "-Xlint:fallthrough", + "-Xlint:rawtypes", + "-Xlint:deprecation", + "-Xlint:unchecked", + "-Werror")); + TEST_COMPILER_ARGS = new ArrayList<>(); + TEST_COMPILER_ARGS.addAll(commonCompilerArgs); + TEST_COMPILER_ARGS.addAll( + List.of( + "-Xlint:-varargs", + "-Xlint:-fallthrough", + "-Xlint:-rawtypes", + "-Xlint:-deprecation", + "-Xlint:-unchecked")); + } - /** - * Apply the common Java compiler options for main sources, test fixture sources, and - * test sources. - * @param project the current project - */ - private void applyJavaCompileConventions(Project project) { - project.afterEvaluate(p -> { - p.getTasks().withType(JavaCompile.class) - .matching(compileTask -> compileTask.getName().startsWith(JavaPlugin.COMPILE_JAVA_TASK_NAME)) - .forEach(compileTask -> { - compileTask.getOptions().setCompilerArgs(COMPILER_ARGS); - compileTask.getOptions().setEncoding("UTF-8"); - setJavaRelease(compileTask); - }); - p.getTasks().withType(JavaCompile.class) - .matching(compileTask -> compileTask.getName().startsWith(JavaPlugin.COMPILE_TEST_JAVA_TASK_NAME) - || compileTask.getName().equals("compileTestFixturesJava")) - .forEach(compileTask -> { - compileTask.getOptions().setCompilerArgs(TEST_COMPILER_ARGS); - compileTask.getOptions().setEncoding("UTF-8"); - setJavaRelease(compileTask); - }); + public void apply(Project project) { + project + .getPlugins() + .withType( + JavaBasePlugin.class, + javaPlugin -> { + applyToolchainConventions(project); + applyJavaCompileConventions(project); + }); + } - }); - } + /** + * Configure the Toolchain support for the project. + * + * @param project the current project + */ + private static void applyToolchainConventions(Project project) { + project + .getExtensions() + .getByType(JavaPluginExtension.class) + .toolchain( + toolchain -> { + toolchain.getVendor().set(JvmVendorSpec.BELLSOFT); + toolchain.getLanguageVersion().set(DEFAULT_LANGUAGE_VERSION); + }); + } - /** - * We should pick the {@link #DEFAULT_RELEASE_VERSION} for all compiled classes, - * unless the current task is compiling multi-release JAR code with a higher version. - */ - private void setJavaRelease(JavaCompile task) { - int defaultVersion = DEFAULT_RELEASE_VERSION.asInt(); - int releaseVersion = defaultVersion; - int compilerVersion = task.getJavaCompiler().get().getMetadata().getLanguageVersion().asInt(); - for (int version = defaultVersion ; version <= compilerVersion ; version++) { - if (task.getName().contains("Java" + version)) { - releaseVersion = version; - break; - } - } - task.getOptions().getRelease().set(releaseVersion); - } + /** + * Apply the common Java compiler options for main sources, test fixture sources, and test + * sources. + * + * @param project the current project + */ + private void applyJavaCompileConventions(Project project) { + project.afterEvaluate( + p -> { + p.getTasks() + .withType(JavaCompile.class) + .matching( + compileTask -> + compileTask.getName().startsWith(JavaPlugin.COMPILE_JAVA_TASK_NAME)) + .forEach( + compileTask -> { + compileTask.getOptions().setCompilerArgs(COMPILER_ARGS); + compileTask.getOptions().setEncoding("UTF-8"); + setJavaRelease(compileTask); + }); + p.getTasks() + .withType(JavaCompile.class) + .matching( + compileTask -> + compileTask.getName().startsWith(JavaPlugin.COMPILE_TEST_JAVA_TASK_NAME) + || compileTask.getName().equals("compileTestFixturesJava")) + .forEach( + compileTask -> { + compileTask.getOptions().setCompilerArgs(TEST_COMPILER_ARGS); + compileTask.getOptions().setEncoding("UTF-8"); + setJavaRelease(compileTask); + }); + }); + } + /** + * We should pick the {@link #DEFAULT_RELEASE_VERSION} for all compiled classes, unless the + * current task is compiling multi-release JAR code with a higher version. + */ + private void setJavaRelease(JavaCompile task) { + int defaultVersion = DEFAULT_RELEASE_VERSION.asInt(); + int releaseVersion = defaultVersion; + int compilerVersion = task.getJavaCompiler().get().getMetadata().getLanguageVersion().asInt(); + for (int version = defaultVersion; version <= compilerVersion; version++) { + if (task.getName().contains("Java" + version)) { + releaseVersion = version; + break; + } + } + task.getOptions().getRelease().set(releaseVersion); + } } diff --git a/buildSrc/src/main/java/org/springframework/build/KotlinConventions.java b/buildSrc/src/main/java/org/springframework/build/KotlinConventions.java index 388c324ffc6d..5e1bf29f3e44 100644 --- a/buildSrc/src/main/java/org/springframework/build/KotlinConventions.java +++ b/buildSrc/src/main/java/org/springframework/build/KotlinConventions.java @@ -27,25 +27,30 @@ */ public class KotlinConventions { - void apply(Project project) { - project.getPlugins().withId("org.jetbrains.kotlin.jvm", - (plugin) -> project.getTasks().withType(KotlinCompile.class, this::configure)); - } - - private void configure(KotlinCompile compile) { - compile.compilerOptions(options -> { - options.getApiVersion().set(KotlinVersion.KOTLIN_2_1); - options.getLanguageVersion().set(KotlinVersion.KOTLIN_2_1); - options.getJvmTarget().set(JvmTarget.JVM_17); - options.getJavaParameters().set(true); - options.getAllWarningsAsErrors().set(true); - options.getFreeCompilerArgs().addAll( - "-Xsuppress-version-warnings", - "-Xjsr305=strict", // For dependencies using JSR 305 - "-opt-in=kotlin.RequiresOptIn", - "-Xjdk-release=17" // Needed due to https://youtrack.jetbrains.com/issue/KT-49746 - ); - }); - } + void apply(Project project) { + project + .getPlugins() + .withId( + "org.jetbrains.kotlin.jvm", + (plugin) -> project.getTasks().withType(KotlinCompile.class, this::configure)); + } + private void configure(KotlinCompile compile) { + compile.compilerOptions( + options -> { + options.getApiVersion().set(KotlinVersion.KOTLIN_2_1); + options.getLanguageVersion().set(KotlinVersion.KOTLIN_2_1); + options.getJvmTarget().set(JvmTarget.JVM_17); + options.getJavaParameters().set(true); + options.getAllWarningsAsErrors().set(true); + options + .getFreeCompilerArgs() + .addAll( + "-Xsuppress-version-warnings", + "-Xjsr305=strict", // For dependencies using JSR 305 + "-opt-in=kotlin.RequiresOptIn", + "-Xjdk-release=17" // Needed due to https://youtrack.jetbrains.com/issue/KT-49746 + ); + }); + } } diff --git a/buildSrc/src/main/java/org/springframework/build/SpringFrameworkExtension.java b/buildSrc/src/main/java/org/springframework/build/SpringFrameworkExtension.java index c4001388eb05..f0ee9989ceb0 100644 --- a/buildSrc/src/main/java/org/springframework/build/SpringFrameworkExtension.java +++ b/buildSrc/src/main/java/org/springframework/build/SpringFrameworkExtension.java @@ -18,7 +18,6 @@ import java.util.Collections; import java.util.List; - import org.gradle.api.Project; import org.gradle.api.provider.Property; import org.gradle.api.tasks.compile.JavaCompile; @@ -27,27 +26,32 @@ public class SpringFrameworkExtension { - private final Property enableJavaPreviewFeatures; - - public SpringFrameworkExtension(Project project) { - this.enableJavaPreviewFeatures = project.getObjects().property(Boolean.class); - project.getTasks().withType(JavaCompile.class).configureEach(javaCompile -> - javaCompile.getOptions().getCompilerArgumentProviders().add(asArgumentProvider())); - project.getTasks().withType(Test.class).configureEach(test -> - test.getJvmArgumentProviders().add(asArgumentProvider())); - - } - - public Property getEnableJavaPreviewFeatures() { - return this.enableJavaPreviewFeatures; - } - - private CommandLineArgumentProvider asArgumentProvider() { - return () -> { - if (getEnableJavaPreviewFeatures().getOrElse(false)) { - return List.of("--enable-preview"); - } - return Collections.emptyList(); - }; - } + private final Property enableJavaPreviewFeatures; + + public SpringFrameworkExtension(Project project) { + this.enableJavaPreviewFeatures = project.getObjects().property(Boolean.class); + project + .getTasks() + .withType(JavaCompile.class) + .configureEach( + javaCompile -> + javaCompile.getOptions().getCompilerArgumentProviders().add(asArgumentProvider())); + project + .getTasks() + .withType(Test.class) + .configureEach(test -> test.getJvmArgumentProviders().add(asArgumentProvider())); + } + + public Property getEnableJavaPreviewFeatures() { + return this.enableJavaPreviewFeatures; + } + + private CommandLineArgumentProvider asArgumentProvider() { + return () -> { + if (getEnableJavaPreviewFeatures().getOrElse(false)) { + return List.of("--enable-preview"); + } + return Collections.emptyList(); + }; + } } diff --git a/buildSrc/src/main/java/org/springframework/build/TestConventions.java b/buildSrc/src/main/java/org/springframework/build/TestConventions.java index e6e2dd0d9a9b..58e42d556e28 100644 --- a/buildSrc/src/main/java/org/springframework/build/TestConventions.java +++ b/buildSrc/src/main/java/org/springframework/build/TestConventions.java @@ -17,7 +17,6 @@ package org.springframework.build; import java.util.Map; - import org.gradle.api.Project; import org.gradle.api.plugins.JavaBasePlugin; import org.gradle.api.tasks.testing.Test; @@ -27,11 +26,12 @@ import org.gradle.testretry.TestRetryTaskExtension; /** - * Conventions that are applied in the presence of the {@link JavaBasePlugin}. When the - * plugin is applied: + * Conventions that are applied in the presence of the {@link JavaBasePlugin}. When the plugin is + * applied: + * *

    - *
  • The {@link TestRetryPlugin Test Retry} plugin is applied so that flaky tests - * are retried 3 times when running on the CI server. + *
  • The {@link TestRetryPlugin Test Retry} plugin is applied so that flaky tests are retried 3 + * times when running on the CI server. *
* * @author Brian Clozel @@ -40,50 +40,59 @@ */ class TestConventions { - void apply(Project project) { - project.getPlugins().withType(JavaBasePlugin.class, (java) -> configureTestConventions(project)); - } - - private void configureTestConventions(Project project) { - project.getTasks().withType(Test.class, - test -> { - configureTests(project, test); - configureTestRetryPlugin(project, test); - }); - } + void apply(Project project) { + project + .getPlugins() + .withType(JavaBasePlugin.class, (java) -> configureTestConventions(project)); + } - private void configureTests(Project project, Test test) { - TestFrameworkOptions existingOptions = test.getOptions(); - test.useJUnitPlatform(options -> { - if (existingOptions instanceof JUnitPlatformOptions junitPlatformOptions) { - options.copyFrom(junitPlatformOptions); - } - }); - test.include("**/*Tests.class", "**/*Test.class"); - test.setSystemProperties(Map.of( - "java.awt.headless", "true", - "io.netty.leakDetection.level", "paranoid" - )); - if (project.hasProperty("testGroups")) { - test.systemProperty("testGroups", project.getProperties().get("testGroups")); - } - test.jvmArgs( - "--add-opens=java.base/java.lang=ALL-UNNAMED", - "--add-opens=java.base/java.util=ALL-UNNAMED", - "-Xshare:off" - ); - } + private void configureTestConventions(Project project) { + project + .getTasks() + .withType( + Test.class, + test -> { + configureTests(project, test); + configureTestRetryPlugin(project, test); + }); + } - private void configureTestRetryPlugin(Project project, Test test) { - project.getPlugins().withType(TestRetryPlugin.class, testRetryPlugin -> { - TestRetryTaskExtension testRetry = test.getExtensions().getByType(TestRetryTaskExtension.class); - testRetry.getFailOnPassedAfterRetry().set(true); - testRetry.getMaxRetries().set(isCi() ? 3 : 0); - }); - } + private void configureTests(Project project, Test test) { + TestFrameworkOptions existingOptions = test.getOptions(); + test.useJUnitPlatform( + options -> { + if (existingOptions instanceof JUnitPlatformOptions junitPlatformOptions) { + options.copyFrom(junitPlatformOptions); + } + }); + test.include("**/*Tests.class", "**/*Test.class"); + test.setSystemProperties( + Map.of( + "java.awt.headless", "true", + "io.netty.leakDetection.level", "paranoid")); + if (project.hasProperty("testGroups")) { + test.systemProperty("testGroups", project.getProperties().get("testGroups")); + } + test.jvmArgs( + "--add-opens=java.base/java.lang=ALL-UNNAMED", + "--add-opens=java.base/java.util=ALL-UNNAMED", + "-Xshare:off"); + } - private boolean isCi() { - return Boolean.parseBoolean(System.getenv("CI")); - } + private void configureTestRetryPlugin(Project project, Test test) { + project + .getPlugins() + .withType( + TestRetryPlugin.class, + testRetryPlugin -> { + TestRetryTaskExtension testRetry = + test.getExtensions().getByType(TestRetryTaskExtension.class); + testRetry.getFailOnPassedAfterRetry().set(true); + testRetry.getMaxRetries().set(isCi() ? 3 : 0); + }); + } + private boolean isCi() { + return Boolean.parseBoolean(System.getenv("CI")); + } } diff --git a/buildSrc/src/main/java/org/springframework/build/architecture/ArchitectureCheck.java b/buildSrc/src/main/java/org/springframework/build/architecture/ArchitectureCheck.java index ef150a5321d7..0133f37cc5e6 100644 --- a/buildSrc/src/main/java/org/springframework/build/architecture/ArchitectureCheck.java +++ b/buildSrc/src/main/java/org/springframework/build/architecture/ArchitectureCheck.java @@ -16,6 +16,13 @@ package org.springframework.build.architecture; +import static org.springframework.build.architecture.ArchitectureRules.allPackagesShouldBeFreeOfTangles; +import static org.springframework.build.architecture.ArchitectureRules.classesShouldNotImportForbiddenTypes; +import static org.springframework.build.architecture.ArchitectureRules.javaClassesShouldNotImportKotlinAnnotations; +import static org.springframework.build.architecture.ArchitectureRules.noClassesShouldCallStringToLowerCaseWithoutLocale; +import static org.springframework.build.architecture.ArchitectureRules.noClassesShouldCallStringToUpperCaseWithoutLocale; +import static org.springframework.build.architecture.ArchitectureRules.packageInfoShouldBeNullMarked; + import com.tngtech.archunit.core.domain.JavaClasses; import com.tngtech.archunit.core.importer.ClassFileImporter; import com.tngtech.archunit.lang.ArchRule; @@ -44,13 +51,6 @@ import org.gradle.api.tasks.SkipWhenEmpty; import org.gradle.api.tasks.TaskAction; -import static org.springframework.build.architecture.ArchitectureRules.allPackagesShouldBeFreeOfTangles; -import static org.springframework.build.architecture.ArchitectureRules.classesShouldNotImportForbiddenTypes; -import static org.springframework.build.architecture.ArchitectureRules.javaClassesShouldNotImportKotlinAnnotations; -import static org.springframework.build.architecture.ArchitectureRules.noClassesShouldCallStringToLowerCaseWithoutLocale; -import static org.springframework.build.architecture.ArchitectureRules.noClassesShouldCallStringToUpperCaseWithoutLocale; -import static org.springframework.build.architecture.ArchitectureRules.packageInfoShouldBeNullMarked; - /** * {@link Task} that checks for architecture problems. * @@ -59,79 +59,85 @@ */ public abstract class ArchitectureCheck extends DefaultTask { - private FileCollection classes; + private FileCollection classes; - public ArchitectureCheck() { - getOutputDirectory().convention(getProject().getLayout().getBuildDirectory().dir(getName())); - getProhibitObjectsRequireNonNull().convention(true); - getRules().addAll(packageInfoShouldBeNullMarked(), - classesShouldNotImportForbiddenTypes(), - javaClassesShouldNotImportKotlinAnnotations(), - allPackagesShouldBeFreeOfTangles(), - noClassesShouldCallStringToLowerCaseWithoutLocale(), - noClassesShouldCallStringToUpperCaseWithoutLocale()); - getRuleDescriptions().set(getRules().map((rules) -> rules.stream().map(ArchRule::getDescription).toList())); - } + public ArchitectureCheck() { + getOutputDirectory().convention(getProject().getLayout().getBuildDirectory().dir(getName())); + getProhibitObjectsRequireNonNull().convention(true); + getRules() + .addAll( + packageInfoShouldBeNullMarked(), + classesShouldNotImportForbiddenTypes(), + javaClassesShouldNotImportKotlinAnnotations(), + allPackagesShouldBeFreeOfTangles(), + noClassesShouldCallStringToLowerCaseWithoutLocale(), + noClassesShouldCallStringToUpperCaseWithoutLocale()); + getRuleDescriptions() + .set(getRules().map((rules) -> rules.stream().map(ArchRule::getDescription).toList())); + } - @TaskAction - void checkArchitecture() throws IOException { - JavaClasses javaClasses = new ClassFileImporter() - .importPaths(this.classes.getFiles().stream().map(File::toPath).toList()); - List violations = getRules().get() - .stream() - .map((rule) -> rule.evaluate(javaClasses)) - .filter(EvaluationResult::hasViolation) - .toList(); - File outputFile = getOutputDirectory().file("failure-report.txt").get().getAsFile(); - outputFile.getParentFile().mkdirs(); - if (!violations.isEmpty()) { - StringBuilder report = new StringBuilder(); - for (EvaluationResult violation : violations) { - report.append(violation.getFailureReport()); - report.append(String.format("%n")); - } - Files.writeString(outputFile.toPath(), report.toString(), StandardOpenOption.CREATE, - StandardOpenOption.TRUNCATE_EXISTING); - throw new GradleException("Architecture check failed. See '" + outputFile + "' for details."); - } - else { - outputFile.createNewFile(); - } - } + @TaskAction + void checkArchitecture() throws IOException { + JavaClasses javaClasses = + new ClassFileImporter() + .importPaths(this.classes.getFiles().stream().map(File::toPath).toList()); + List violations = + getRules().get().stream() + .map((rule) -> rule.evaluate(javaClasses)) + .filter(EvaluationResult::hasViolation) + .toList(); + File outputFile = getOutputDirectory().file("failure-report.txt").get().getAsFile(); + outputFile.getParentFile().mkdirs(); + if (!violations.isEmpty()) { + StringBuilder report = new StringBuilder(); + for (EvaluationResult violation : violations) { + report.append(violation.getFailureReport()); + report.append(String.format("%n")); + } + Files.writeString( + outputFile.toPath(), + report.toString(), + StandardOpenOption.CREATE, + StandardOpenOption.TRUNCATE_EXISTING); + throw new GradleException("Architecture check failed. See '" + outputFile + "' for details."); + } else { + outputFile.createNewFile(); + } + } - public void setClasses(FileCollection classes) { - this.classes = classes; - } + public void setClasses(FileCollection classes) { + this.classes = classes; + } - @Internal - public FileCollection getClasses() { - return this.classes; - } + @Internal + public FileCollection getClasses() { + return this.classes; + } - @InputFiles - @SkipWhenEmpty - @IgnoreEmptyDirectories - @PathSensitive(PathSensitivity.RELATIVE) - final FileTree getInputClasses() { - return this.classes.getAsFileTree(); - } + @InputFiles + @SkipWhenEmpty + @IgnoreEmptyDirectories + @PathSensitive(PathSensitivity.RELATIVE) + final FileTree getInputClasses() { + return this.classes.getAsFileTree(); + } - @Optional - @InputFiles - @PathSensitive(PathSensitivity.RELATIVE) - public abstract DirectoryProperty getResourcesDirectory(); + @Optional + @InputFiles + @PathSensitive(PathSensitivity.RELATIVE) + public abstract DirectoryProperty getResourcesDirectory(); - @OutputDirectory - public abstract DirectoryProperty getOutputDirectory(); + @OutputDirectory + public abstract DirectoryProperty getOutputDirectory(); - @Internal - public abstract ListProperty getRules(); + @Internal + public abstract ListProperty getRules(); - @Internal - public abstract Property getProhibitObjectsRequireNonNull(); + @Internal + public abstract Property getProhibitObjectsRequireNonNull(); - @Input - // The rules themselves can't be an input as they aren't serializable so we use - // their descriptions instead - abstract ListProperty getRuleDescriptions(); + @Input + // The rules themselves can't be an input as they aren't serializable so we use + // their descriptions instead + abstract ListProperty getRuleDescriptions(); } diff --git a/buildSrc/src/main/java/org/springframework/build/architecture/ArchitecturePlugin.java b/buildSrc/src/main/java/org/springframework/build/architecture/ArchitecturePlugin.java index 22fdcef2b2de..b669118ab498 100644 --- a/buildSrc/src/main/java/org/springframework/build/architecture/ArchitecturePlugin.java +++ b/buildSrc/src/main/java/org/springframework/build/architecture/ArchitecturePlugin.java @@ -34,41 +34,47 @@ */ public class ArchitecturePlugin implements Plugin { - @Override - public void apply(Project project) { - project.getPlugins().withType(JavaPlugin.class, (javaPlugin) -> registerTasks(project)); - } + @Override + public void apply(Project project) { + project.getPlugins().withType(JavaPlugin.class, (javaPlugin) -> registerTasks(project)); + } - private void registerTasks(Project project) { - JavaPluginExtension javaPluginExtension = project.getExtensions().getByType(JavaPluginExtension.class); - List> architectureChecks = new ArrayList<>(); - for (SourceSet sourceSet : javaPluginExtension.getSourceSets()) { - if (sourceSet.getName().contains("test")) { - // skip test source sets. - continue; - } - TaskProvider checkArchitecture = project.getTasks() - .register(taskName(sourceSet), ArchitectureCheck.class, - (task) -> { - task.setClasses(sourceSet.getOutput().getClassesDirs()); - task.getResourcesDirectory().set(sourceSet.getOutput().getResourcesDir()); - task.dependsOn(sourceSet.getProcessResourcesTaskName()); - task.setDescription("Checks the architecture of the classes of the " + sourceSet.getName() - + " source set."); - task.setGroup(LifecycleBasePlugin.VERIFICATION_GROUP); - }); - architectureChecks.add(checkArchitecture); - } - if (!architectureChecks.isEmpty()) { - TaskProvider checkTask = project.getTasks().named(LifecycleBasePlugin.CHECK_TASK_NAME); - checkTask.configure((check) -> check.dependsOn(architectureChecks)); - } - } - - private static String taskName(SourceSet sourceSet) { - return "checkArchitecture" - + sourceSet.getName().substring(0, 1).toUpperCase() - + sourceSet.getName().substring(1); - } + private void registerTasks(Project project) { + JavaPluginExtension javaPluginExtension = + project.getExtensions().getByType(JavaPluginExtension.class); + List> architectureChecks = new ArrayList<>(); + for (SourceSet sourceSet : javaPluginExtension.getSourceSets()) { + if (sourceSet.getName().contains("test")) { + // skip test source sets. + continue; + } + TaskProvider checkArchitecture = + project + .getTasks() + .register( + taskName(sourceSet), + ArchitectureCheck.class, + (task) -> { + task.setClasses(sourceSet.getOutput().getClassesDirs()); + task.getResourcesDirectory().set(sourceSet.getOutput().getResourcesDir()); + task.dependsOn(sourceSet.getProcessResourcesTaskName()); + task.setDescription( + "Checks the architecture of the classes of the " + + sourceSet.getName() + + " source set."); + task.setGroup(LifecycleBasePlugin.VERIFICATION_GROUP); + }); + architectureChecks.add(checkArchitecture); + } + if (!architectureChecks.isEmpty()) { + TaskProvider checkTask = project.getTasks().named(LifecycleBasePlugin.CHECK_TASK_NAME); + checkTask.configure((check) -> check.dependsOn(architectureChecks)); + } + } + private static String taskName(SourceSet sourceSet) { + return "checkArchitecture" + + sourceSet.getName().substring(0, 1).toUpperCase() + + sourceSet.getName().substring(1); + } } diff --git a/buildSrc/src/main/java/org/springframework/build/architecture/ArchitectureRules.java b/buildSrc/src/main/java/org/springframework/build/architecture/ArchitectureRules.java index 56a04071b96a..ba80409b2ad1 100644 --- a/buildSrc/src/main/java/org/springframework/build/architecture/ArchitectureRules.java +++ b/buildSrc/src/main/java/org/springframework/build/architecture/ArchitectureRules.java @@ -27,81 +27,88 @@ abstract class ArchitectureRules { - static ArchRule allPackagesShouldBeFreeOfTangles() { - return SlicesRuleDefinition.slices() - .assignedFrom(new SpringSlices()).should().beFreeOfCycles(); - } + static ArchRule allPackagesShouldBeFreeOfTangles() { + return SlicesRuleDefinition.slices().assignedFrom(new SpringSlices()).should().beFreeOfCycles(); + } - static ArchRule noClassesShouldCallStringToLowerCaseWithoutLocale() { - return ArchRuleDefinition.noClasses() - .should() - .callMethod(String.class, "toLowerCase") - .because("String.toLowerCase(Locale.ROOT) should be used instead"); - } + static ArchRule noClassesShouldCallStringToLowerCaseWithoutLocale() { + return ArchRuleDefinition.noClasses() + .should() + .callMethod(String.class, "toLowerCase") + .because("String.toLowerCase(Locale.ROOT) should be used instead"); + } - static ArchRule noClassesShouldCallStringToUpperCaseWithoutLocale() { - return ArchRuleDefinition.noClasses() - .should() - .callMethod(String.class, "toUpperCase") - .because("String.toUpperCase(Locale.ROOT) should be used instead"); - } + static ArchRule noClassesShouldCallStringToUpperCaseWithoutLocale() { + return ArchRuleDefinition.noClasses() + .should() + .callMethod(String.class, "toUpperCase") + .because("String.toUpperCase(Locale.ROOT) should be used instead"); + } - static ArchRule packageInfoShouldBeNullMarked() { - return ArchRuleDefinition.classes() - .that().haveSimpleName("package-info") - .should().beAnnotatedWith("org.jspecify.annotations.NullMarked") - .allowEmptyShould(true); - } + static ArchRule packageInfoShouldBeNullMarked() { + return ArchRuleDefinition.classes() + .that() + .haveSimpleName("package-info") + .should() + .beAnnotatedWith("org.jspecify.annotations.NullMarked") + .allowEmptyShould(true); + } - static ArchRule classesShouldNotImportForbiddenTypes() { - return ArchRuleDefinition.noClasses() - .should().dependOnClassesThat() - .haveFullyQualifiedName("reactor.core.support.Assert") - .orShould().dependOnClassesThat() - .haveFullyQualifiedName("org.slf4j.LoggerFactory") - .orShould().dependOnClassesThat() - .haveFullyQualifiedName("org.springframework.lang.NonNull") - .orShould().dependOnClassesThat() - .haveFullyQualifiedName("org.springframework.lang.Nullable"); - } + static ArchRule classesShouldNotImportForbiddenTypes() { + return ArchRuleDefinition.noClasses() + .should() + .dependOnClassesThat() + .haveFullyQualifiedName("reactor.core.support.Assert") + .orShould() + .dependOnClassesThat() + .haveFullyQualifiedName("org.slf4j.LoggerFactory") + .orShould() + .dependOnClassesThat() + .haveFullyQualifiedName("org.springframework.lang.NonNull") + .orShould() + .dependOnClassesThat() + .haveFullyQualifiedName("org.springframework.lang.Nullable"); + } - static ArchRule javaClassesShouldNotImportKotlinAnnotations() { - return ArchRuleDefinition.noClasses() - .that(new DescribedPredicate("is not a Kotlin class") { - @Override - public boolean test(JavaClass javaClass) { - return javaClass.getSourceCodeLocation() - .getSourceFileName().endsWith(".java"); - } - } - ) - .should().dependOnClassesThat() - .resideInAnyPackage("org.jetbrains.annotations..") - .allowEmptyShould(true); - } + static ArchRule javaClassesShouldNotImportKotlinAnnotations() { + return ArchRuleDefinition.noClasses() + .that( + new DescribedPredicate("is not a Kotlin class") { + @Override + public boolean test(JavaClass javaClass) { + return javaClass.getSourceCodeLocation().getSourceFileName().endsWith(".java"); + } + }) + .should() + .dependOnClassesThat() + .resideInAnyPackage("org.jetbrains.annotations..") + .allowEmptyShould(true); + } - static class SpringSlices implements SliceAssignment { + static class SpringSlices implements SliceAssignment { - private final List ignoredPackages = List.of("org.springframework.asm", - "org.springframework.cglib", - "org.springframework.javapoet", - "org.springframework.objenesis"); + private final List ignoredPackages = + List.of( + "org.springframework.asm", + "org.springframework.cglib", + "org.springframework.javapoet", + "org.springframework.objenesis"); - @Override - public SliceIdentifier getIdentifierOf(JavaClass javaClass) { + @Override + public SliceIdentifier getIdentifierOf(JavaClass javaClass) { - String packageName = javaClass.getPackageName(); - for (String ignoredPackage : ignoredPackages) { - if (packageName.startsWith(ignoredPackage)) { - return SliceIdentifier.ignore(); - } - } - return SliceIdentifier.of("spring framework"); - } + String packageName = javaClass.getPackageName(); + for (String ignoredPackage : ignoredPackages) { + if (packageName.startsWith(ignoredPackage)) { + return SliceIdentifier.ignore(); + } + } + return SliceIdentifier.of("spring framework"); + } - @Override - public String getDescription() { - return "Spring Framework Slices"; - } - } + @Override + public String getDescription() { + return "Spring Framework Slices"; + } + } } diff --git a/buildSrc/src/main/java/org/springframework/build/dev/LocalDevelopmentPlugin.java b/buildSrc/src/main/java/org/springframework/build/dev/LocalDevelopmentPlugin.java index 8c8a2fd4523c..055a71bd0245 100644 --- a/buildSrc/src/main/java/org/springframework/build/dev/LocalDevelopmentPlugin.java +++ b/buildSrc/src/main/java/org/springframework/build/dev/LocalDevelopmentPlugin.java @@ -27,23 +27,26 @@ */ public class LocalDevelopmentPlugin implements Plugin { - private static final String SKIP_DOCS_PROPERTY = "skipDocs"; + private static final String SKIP_DOCS_PROPERTY = "skipDocs"; - @Override - public void apply(Project target) { - if (target.hasProperty(SKIP_DOCS_PROPERTY)) { - skipDocumentationTasks(target); - target.subprojects(this::skipDocumentationTasks); - } - } + @Override + public void apply(Project target) { + if (target.hasProperty(SKIP_DOCS_PROPERTY)) { + skipDocumentationTasks(target); + target.subprojects(this::skipDocumentationTasks); + } + } - private void skipDocumentationTasks(Project project) { - project.afterEvaluate(p -> { - p.getTasks().matching(task -> { - return JavaBasePlugin.DOCUMENTATION_GROUP.equals(task.getGroup()) - || "distribution".equals(task.getGroup()); - }) - .forEach(task -> task.setEnabled(false)); - }); - } + private void skipDocumentationTasks(Project project) { + project.afterEvaluate( + p -> { + p.getTasks() + .matching( + task -> { + return JavaBasePlugin.DOCUMENTATION_GROUP.equals(task.getGroup()) + || "distribution".equals(task.getGroup()); + }) + .forEach(task -> task.setEnabled(false)); + }); + } } diff --git a/buildSrc/src/main/java/org/springframework/build/hint/RuntimeHintsAgentArgumentProvider.java b/buildSrc/src/main/java/org/springframework/build/hint/RuntimeHintsAgentArgumentProvider.java index 2a7169fd885f..e4ec8da3578a 100644 --- a/buildSrc/src/main/java/org/springframework/build/hint/RuntimeHintsAgentArgumentProvider.java +++ b/buildSrc/src/main/java/org/springframework/build/hint/RuntimeHintsAgentArgumentProvider.java @@ -17,32 +17,33 @@ package org.springframework.build.hint; import java.util.Collections; - import org.gradle.api.file.ConfigurableFileCollection; import org.gradle.api.provider.SetProperty; import org.gradle.api.tasks.Classpath; import org.gradle.api.tasks.Input; import org.gradle.process.CommandLineArgumentProvider; -/** - * Argument provider for registering the runtime hints agent with a Java process. - */ +/** Argument provider for registering the runtime hints agent with a Java process. */ public interface RuntimeHintsAgentArgumentProvider extends CommandLineArgumentProvider { - @Classpath - ConfigurableFileCollection getAgentJar(); - - @Input - SetProperty getIncludedPackages(); - - @Input - SetProperty getExcludedPackages(); - - @Override - default Iterable asArguments() { - StringBuilder packages = new StringBuilder(); - getIncludedPackages().get().forEach(packageName -> packages.append('+').append(packageName).append(',')); - getExcludedPackages().get().forEach(packageName -> packages.append('-').append(packageName).append(',')); - return Collections.singleton("-javaagent:" + getAgentJar().getSingleFile() + "=" + packages); - } + @Classpath + ConfigurableFileCollection getAgentJar(); + + @Input + SetProperty getIncludedPackages(); + + @Input + SetProperty getExcludedPackages(); + + @Override + default Iterable asArguments() { + StringBuilder packages = new StringBuilder(); + getIncludedPackages() + .get() + .forEach(packageName -> packages.append('+').append(packageName).append(',')); + getExcludedPackages() + .get() + .forEach(packageName -> packages.append('-').append(packageName).append(',')); + return Collections.singleton("-javaagent:" + getAgentJar().getSingleFile() + "=" + packages); + } } diff --git a/buildSrc/src/main/java/org/springframework/build/hint/RuntimeHintsAgentExtension.java b/buildSrc/src/main/java/org/springframework/build/hint/RuntimeHintsAgentExtension.java index 6c7789cd02fd..2cdd79bb2f8b 100644 --- a/buildSrc/src/main/java/org/springframework/build/hint/RuntimeHintsAgentExtension.java +++ b/buildSrc/src/main/java/org/springframework/build/hint/RuntimeHintsAgentExtension.java @@ -20,11 +20,12 @@ /** * Entry point to the DSL extension for the {@link RuntimeHintsAgentPlugin} Gradle plugin. + * * @author Brian Clozel */ public interface RuntimeHintsAgentExtension { - SetProperty getIncludedPackages(); + SetProperty getIncludedPackages(); - SetProperty getExcludedPackages(); + SetProperty getExcludedPackages(); } diff --git a/buildSrc/src/main/java/org/springframework/build/hint/RuntimeHintsAgentPlugin.java b/buildSrc/src/main/java/org/springframework/build/hint/RuntimeHintsAgentPlugin.java index fab5ab5134b0..a9fcf9e53162 100644 --- a/buildSrc/src/main/java/org/springframework/build/hint/RuntimeHintsAgentPlugin.java +++ b/buildSrc/src/main/java/org/springframework/build/hint/RuntimeHintsAgentPlugin.java @@ -16,6 +16,7 @@ package org.springframework.build.hint; +import java.util.Collections; import org.gradle.api.JavaVersion; import org.gradle.api.Plugin; import org.gradle.api.Project; @@ -31,8 +32,6 @@ import org.gradle.api.tasks.testing.Test; import org.gradle.testing.base.TestingExtension; -import java.util.Collections; - /** * {@link Plugin} that configures the {@code RuntimeHints} Java agent to test tasks. * @@ -41,61 +40,94 @@ */ public class RuntimeHintsAgentPlugin implements Plugin { - public static final String RUNTIMEHINTS_TEST_TASK = "runtimeHintsTest"; - private static final String EXTENSION_NAME = "runtimeHintsAgent"; - private static final String CONFIGURATION_NAME = "testRuntimeHintsAgentJar"; - + public static final String RUNTIMEHINTS_TEST_TASK = "runtimeHintsTest"; + private static final String EXTENSION_NAME = "runtimeHintsAgent"; + private static final String CONFIGURATION_NAME = "testRuntimeHintsAgentJar"; - @Override - public void apply(Project project) { + @Override + public void apply(Project project) { - project.getPlugins().withType(JavaPlugin.class, javaPlugin -> { - TestingExtension testing = project.getExtensions().getByType(TestingExtension.class); - JvmTestSuite jvmTestSuite = (JvmTestSuite) testing.getSuites().getByName("test"); - RuntimeHintsAgentExtension agentExtension = createRuntimeHintsAgentExtension(project); - TaskProvider agentTest = project.getTasks().register(RUNTIMEHINTS_TEST_TASK, Test.class, test -> { - test.useJUnitPlatform(options -> { - options.includeTags("RuntimeHintsTests"); - }); - test.include("**/*Tests.class", "**/*Test.class"); - test.systemProperty("java.awt.headless", "true"); - test.systemProperty("org.graalvm.nativeimage.imagecode", "runtime"); - test.setTestClassesDirs(jvmTestSuite.getSources().getOutput().getClassesDirs()); - test.setClasspath(jvmTestSuite.getSources().getRuntimeClasspath()); - test.getJvmArgumentProviders().add(createRuntimeHintsAgentArgumentProvider(project, agentExtension)); - }); - project.getTasks().named("check", task -> task.dependsOn(agentTest)); - project.getDependencies().add(CONFIGURATION_NAME, project.project(":spring-core-test")); - }); - } + project + .getPlugins() + .withType( + JavaPlugin.class, + javaPlugin -> { + TestingExtension testing = project.getExtensions().getByType(TestingExtension.class); + JvmTestSuite jvmTestSuite = (JvmTestSuite) testing.getSuites().getByName("test"); + RuntimeHintsAgentExtension agentExtension = createRuntimeHintsAgentExtension(project); + TaskProvider agentTest = + project + .getTasks() + .register( + RUNTIMEHINTS_TEST_TASK, + Test.class, + test -> { + test.useJUnitPlatform( + options -> { + options.includeTags("RuntimeHintsTests"); + }); + test.include("**/*Tests.class", "**/*Test.class"); + test.systemProperty("java.awt.headless", "true"); + test.systemProperty("org.graalvm.nativeimage.imagecode", "runtime"); + test.setTestClassesDirs( + jvmTestSuite.getSources().getOutput().getClassesDirs()); + test.setClasspath(jvmTestSuite.getSources().getRuntimeClasspath()); + test.getJvmArgumentProviders() + .add( + createRuntimeHintsAgentArgumentProvider( + project, agentExtension)); + }); + project.getTasks().named("check", task -> task.dependsOn(agentTest)); + project + .getDependencies() + .add(CONFIGURATION_NAME, project.project(":spring-core-test")); + }); + } - private static RuntimeHintsAgentExtension createRuntimeHintsAgentExtension(Project project) { - RuntimeHintsAgentExtension agentExtension = project.getExtensions().create(EXTENSION_NAME, RuntimeHintsAgentExtension.class); - agentExtension.getIncludedPackages().convention(Collections.singleton("org.springframework")); - agentExtension.getExcludedPackages().convention(Collections.emptySet()); - return agentExtension; - } + private static RuntimeHintsAgentExtension createRuntimeHintsAgentExtension(Project project) { + RuntimeHintsAgentExtension agentExtension = + project.getExtensions().create(EXTENSION_NAME, RuntimeHintsAgentExtension.class); + agentExtension.getIncludedPackages().convention(Collections.singleton("org.springframework")); + agentExtension.getExcludedPackages().convention(Collections.emptySet()); + return agentExtension; + } - private static RuntimeHintsAgentArgumentProvider createRuntimeHintsAgentArgumentProvider( - Project project, RuntimeHintsAgentExtension agentExtension) { - RuntimeHintsAgentArgumentProvider agentArgumentProvider = project.getObjects().newInstance(RuntimeHintsAgentArgumentProvider.class); - agentArgumentProvider.getAgentJar().from(createRuntimeHintsAgentConfiguration(project)); - agentArgumentProvider.getIncludedPackages().set(agentExtension.getIncludedPackages()); - agentArgumentProvider.getExcludedPackages().set(agentExtension.getExcludedPackages()); - return agentArgumentProvider; - } + private static RuntimeHintsAgentArgumentProvider createRuntimeHintsAgentArgumentProvider( + Project project, RuntimeHintsAgentExtension agentExtension) { + RuntimeHintsAgentArgumentProvider agentArgumentProvider = + project.getObjects().newInstance(RuntimeHintsAgentArgumentProvider.class); + agentArgumentProvider.getAgentJar().from(createRuntimeHintsAgentConfiguration(project)); + agentArgumentProvider.getIncludedPackages().set(agentExtension.getIncludedPackages()); + agentArgumentProvider.getExcludedPackages().set(agentExtension.getExcludedPackages()); + return agentArgumentProvider; + } - private static Configuration createRuntimeHintsAgentConfiguration(Project project) { - return project.getConfigurations().create(CONFIGURATION_NAME, configuration -> { - configuration.setCanBeConsumed(false); - configuration.setTransitive(false); // Only the built artifact is required - configuration.attributes(attributes -> { - attributes.attribute(Bundling.BUNDLING_ATTRIBUTE, project.getObjects().named(Bundling.class, Bundling.EXTERNAL)); - attributes.attribute(Category.CATEGORY_ATTRIBUTE, project.getObjects().named(Category.class, Category.LIBRARY)); - attributes.attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, project.getObjects().named(LibraryElements.class, LibraryElements.JAR)); - attributes.attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, Integer.valueOf(JavaVersion.current().getMajorVersion())); - attributes.attribute(Usage.USAGE_ATTRIBUTE, project.getObjects().named(Usage.class, Usage.JAVA_RUNTIME)); - }); - }); - } + private static Configuration createRuntimeHintsAgentConfiguration(Project project) { + return project + .getConfigurations() + .create( + CONFIGURATION_NAME, + configuration -> { + configuration.setCanBeConsumed(false); + configuration.setTransitive(false); // Only the built artifact is required + configuration.attributes( + attributes -> { + attributes.attribute( + Bundling.BUNDLING_ATTRIBUTE, + project.getObjects().named(Bundling.class, Bundling.EXTERNAL)); + attributes.attribute( + Category.CATEGORY_ATTRIBUTE, + project.getObjects().named(Category.class, Category.LIBRARY)); + attributes.attribute( + LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, + project.getObjects().named(LibraryElements.class, LibraryElements.JAR)); + attributes.attribute( + TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, + Integer.valueOf(JavaVersion.current().getMajorVersion())); + attributes.attribute( + Usage.USAGE_ATTRIBUTE, + project.getObjects().named(Usage.class, Usage.JAVA_RUNTIME)); + }); + }); + } } diff --git a/buildSrc/src/main/java/org/springframework/build/multirelease/MultiReleaseExtension.java b/buildSrc/src/main/java/org/springframework/build/multirelease/MultiReleaseExtension.java index 547c3f480de4..ea7aa7e81e5e 100644 --- a/buildSrc/src/main/java/org/springframework/build/multirelease/MultiReleaseExtension.java +++ b/buildSrc/src/main/java/org/springframework/build/multirelease/MultiReleaseExtension.java @@ -17,7 +17,6 @@ package org.springframework.build.multirelease; import javax.inject.Inject; - import org.gradle.api.artifacts.Configuration; import org.gradle.api.artifacts.ConfigurationContainer; import org.gradle.api.artifacts.dsl.DependencyHandler; @@ -40,100 +39,137 @@ * @author Brian Clozel */ public abstract class MultiReleaseExtension { - private final TaskContainer tasks; - private final SourceSetContainer sourceSets; - private final DependencyHandler dependencies; - private final ObjectFactory objects; - private final ConfigurationContainer configurations; - - @Inject - public MultiReleaseExtension(SourceSetContainer sourceSets, - ConfigurationContainer configurations, - TaskContainer tasks, - DependencyHandler dependencies, - ObjectFactory objectFactory) { - this.sourceSets = sourceSets; - this.configurations = configurations; - this.tasks = tasks; - this.dependencies = dependencies; - this.objects = objectFactory; - } - - public void releaseVersions(int... javaVersions) { - releaseVersions("src/main/", "src/test/", javaVersions); - } - - private void releaseVersions(String mainSourceDirectory, String testSourceDirectory, int... javaVersions) { - for (int javaVersion : javaVersions) { - addLanguageVersion(javaVersion, mainSourceDirectory, testSourceDirectory); - } - } - - private void addLanguageVersion(int javaVersion, String mainSourceDirectory, String testSourceDirectory) { - String javaN = "java" + javaVersion; - - SourceSet langSourceSet = sourceSets.create(javaN, srcSet -> srcSet.getJava().srcDir(mainSourceDirectory + javaN)); - SourceSet testSourceSet = sourceSets.create(javaN + "Test", srcSet -> srcSet.getJava().srcDir(testSourceDirectory + javaN)); - SourceSet sharedSourceSet = sourceSets.findByName(SourceSet.MAIN_SOURCE_SET_NAME); - SourceSet sharedTestSourceSet = sourceSets.findByName(SourceSet.TEST_SOURCE_SET_NAME); - - FileCollection mainClasses = objects.fileCollection().from(sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME).getOutput().getClassesDirs()); - dependencies.add(javaN + "Implementation", mainClasses); - - tasks.named(langSourceSet.getCompileJavaTaskName(), JavaCompile.class, task -> - task.getOptions().getRelease().set(javaVersion) - ); - tasks.named(testSourceSet.getCompileJavaTaskName(), JavaCompile.class, task -> - task.getOptions().getRelease().set(javaVersion) - ); - - TaskProvider testTask = createTestTask(javaVersion, testSourceSet, sharedTestSourceSet, langSourceSet, sharedSourceSet); - tasks.named("check", task -> task.dependsOn(testTask)); - - configureMultiReleaseJar(javaVersion, langSourceSet); - } - - private TaskProvider createTestTask(int javaVersion, SourceSet testSourceSet, SourceSet sharedTestSourceSet, SourceSet langSourceSet, SourceSet sharedSourceSet) { - Configuration testImplementation = configurations.getByName(testSourceSet.getImplementationConfigurationName()); - testImplementation.extendsFrom(configurations.getByName(sharedTestSourceSet.getImplementationConfigurationName())); - Configuration testCompileOnly = configurations.getByName(testSourceSet.getCompileOnlyConfigurationName()); - testCompileOnly.extendsFrom(configurations.getByName(sharedTestSourceSet.getCompileOnlyConfigurationName())); - testCompileOnly.getDependencies().add(dependencies.create(langSourceSet.getOutput().getClassesDirs())); - testCompileOnly.getDependencies().add(dependencies.create(sharedSourceSet.getOutput().getClassesDirs())); - - Configuration testRuntimeClasspath = configurations.getByName(testSourceSet.getRuntimeClasspathConfigurationName()); - // so here's the deal. MRjars are JARs! Which means that to execute tests, we need - // the JAR on classpath, not just classes + resources as Gradle usually does - testRuntimeClasspath.getAttributes() - .attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements.class, LibraryElements.JAR)); - - TaskProvider testTask = tasks.register("java" + javaVersion + "Test", Test.class, test -> { - test.setGroup(LifecycleBasePlugin.VERIFICATION_GROUP); - - ConfigurableFileCollection testClassesDirs = objects.fileCollection(); - testClassesDirs.from(testSourceSet.getOutput()); - testClassesDirs.from(sharedTestSourceSet.getOutput()); - test.setTestClassesDirs(testClassesDirs); - ConfigurableFileCollection classpath = objects.fileCollection(); - // must put the MRJar first on classpath - classpath.from(tasks.named("jar")); - // then we put the specific test sourceset tests, so that we can override - // the shared versions - classpath.from(testSourceSet.getOutput()); - - // then we add the shared tests - classpath.from(sharedTestSourceSet.getRuntimeClasspath()); - test.setClasspath(classpath); - }); - return testTask; - } - - private void configureMultiReleaseJar(int version, SourceSet languageSourceSet) { - tasks.named("jar", Jar.class, jar -> { - jar.into("META-INF/versions/" + version, s -> s.from(languageSourceSet.getOutput())); - Attributes attributes = jar.getManifest().getAttributes(); - attributes.put("Multi-Release", "true"); - }); - } - + private final TaskContainer tasks; + private final SourceSetContainer sourceSets; + private final DependencyHandler dependencies; + private final ObjectFactory objects; + private final ConfigurationContainer configurations; + + @Inject + public MultiReleaseExtension( + SourceSetContainer sourceSets, + ConfigurationContainer configurations, + TaskContainer tasks, + DependencyHandler dependencies, + ObjectFactory objectFactory) { + this.sourceSets = sourceSets; + this.configurations = configurations; + this.tasks = tasks; + this.dependencies = dependencies; + this.objects = objectFactory; + } + + public void releaseVersions(int... javaVersions) { + releaseVersions("src/main/", "src/test/", javaVersions); + } + + private void releaseVersions( + String mainSourceDirectory, String testSourceDirectory, int... javaVersions) { + for (int javaVersion : javaVersions) { + addLanguageVersion(javaVersion, mainSourceDirectory, testSourceDirectory); + } + } + + private void addLanguageVersion( + int javaVersion, String mainSourceDirectory, String testSourceDirectory) { + String javaN = "java" + javaVersion; + + SourceSet langSourceSet = + sourceSets.create(javaN, srcSet -> srcSet.getJava().srcDir(mainSourceDirectory + javaN)); + SourceSet testSourceSet = + sourceSets.create( + javaN + "Test", srcSet -> srcSet.getJava().srcDir(testSourceDirectory + javaN)); + SourceSet sharedSourceSet = sourceSets.findByName(SourceSet.MAIN_SOURCE_SET_NAME); + SourceSet sharedTestSourceSet = sourceSets.findByName(SourceSet.TEST_SOURCE_SET_NAME); + + FileCollection mainClasses = + objects + .fileCollection() + .from( + sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME).getOutput().getClassesDirs()); + dependencies.add(javaN + "Implementation", mainClasses); + + tasks.named( + langSourceSet.getCompileJavaTaskName(), + JavaCompile.class, + task -> task.getOptions().getRelease().set(javaVersion)); + tasks.named( + testSourceSet.getCompileJavaTaskName(), + JavaCompile.class, + task -> task.getOptions().getRelease().set(javaVersion)); + + TaskProvider testTask = + createTestTask( + javaVersion, testSourceSet, sharedTestSourceSet, langSourceSet, sharedSourceSet); + tasks.named("check", task -> task.dependsOn(testTask)); + + configureMultiReleaseJar(javaVersion, langSourceSet); + } + + private TaskProvider createTestTask( + int javaVersion, + SourceSet testSourceSet, + SourceSet sharedTestSourceSet, + SourceSet langSourceSet, + SourceSet sharedSourceSet) { + Configuration testImplementation = + configurations.getByName(testSourceSet.getImplementationConfigurationName()); + testImplementation.extendsFrom( + configurations.getByName(sharedTestSourceSet.getImplementationConfigurationName())); + Configuration testCompileOnly = + configurations.getByName(testSourceSet.getCompileOnlyConfigurationName()); + testCompileOnly.extendsFrom( + configurations.getByName(sharedTestSourceSet.getCompileOnlyConfigurationName())); + testCompileOnly + .getDependencies() + .add(dependencies.create(langSourceSet.getOutput().getClassesDirs())); + testCompileOnly + .getDependencies() + .add(dependencies.create(sharedSourceSet.getOutput().getClassesDirs())); + + Configuration testRuntimeClasspath = + configurations.getByName(testSourceSet.getRuntimeClasspathConfigurationName()); + // so here's the deal. MRjars are JARs! Which means that to execute tests, we need + // the JAR on classpath, not just classes + resources as Gradle usually does + testRuntimeClasspath + .getAttributes() + .attribute( + LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, + objects.named(LibraryElements.class, LibraryElements.JAR)); + + TaskProvider testTask = + tasks.register( + "java" + javaVersion + "Test", + Test.class, + test -> { + test.setGroup(LifecycleBasePlugin.VERIFICATION_GROUP); + + ConfigurableFileCollection testClassesDirs = objects.fileCollection(); + testClassesDirs.from(testSourceSet.getOutput()); + testClassesDirs.from(sharedTestSourceSet.getOutput()); + test.setTestClassesDirs(testClassesDirs); + ConfigurableFileCollection classpath = objects.fileCollection(); + // must put the MRJar first on classpath + classpath.from(tasks.named("jar")); + // then we put the specific test sourceset tests, so that we can override + // the shared versions + classpath.from(testSourceSet.getOutput()); + + // then we add the shared tests + classpath.from(sharedTestSourceSet.getRuntimeClasspath()); + test.setClasspath(classpath); + }); + return testTask; + } + + private void configureMultiReleaseJar(int version, SourceSet languageSourceSet) { + tasks.named( + "jar", + Jar.class, + jar -> { + jar.into("META-INF/versions/" + version, s -> s.from(languageSourceSet.getOutput())); + Attributes attributes = jar.getManifest().getAttributes(); + attributes.put("Multi-Release", "true"); + }); + } } diff --git a/buildSrc/src/main/java/org/springframework/build/multirelease/MultiReleaseJarPlugin.java b/buildSrc/src/main/java/org/springframework/build/multirelease/MultiReleaseJarPlugin.java index 1716d016b285..475ceeb40374 100644 --- a/buildSrc/src/main/java/org/springframework/build/multirelease/MultiReleaseJarPlugin.java +++ b/buildSrc/src/main/java/org/springframework/build/multirelease/MultiReleaseJarPlugin.java @@ -17,7 +17,6 @@ package org.springframework.build.multirelease; import javax.inject.Inject; - import org.gradle.api.Plugin; import org.gradle.api.Project; import org.gradle.api.artifacts.ConfigurationContainer; @@ -30,32 +29,34 @@ import org.gradle.jvm.toolchain.JavaToolchainService; /** - * A plugin which adds support for building multi-release jars - * with Gradle. + * A plugin which adds support for building multi-release jars with Gradle. + * * @author Cedric Champeau * @author Brian Clozel * @see original project */ public class MultiReleaseJarPlugin implements Plugin { - @Inject - protected JavaToolchainService getToolchains() { - throw new UnsupportedOperationException(); - } + @Inject + protected JavaToolchainService getToolchains() { + throw new UnsupportedOperationException(); + } - public void apply(Project project) { - project.getPlugins().apply(JavaPlugin.class); - ExtensionContainer extensions = project.getExtensions(); - JavaPluginExtension javaPluginExtension = extensions.getByType(JavaPluginExtension.class); - ConfigurationContainer configurations = project.getConfigurations(); - TaskContainer tasks = project.getTasks(); - DependencyHandler dependencies = project.getDependencies(); - ObjectFactory objects = project.getObjects(); - extensions.create("multiRelease", MultiReleaseExtension.class, - javaPluginExtension.getSourceSets(), - configurations, - tasks, - dependencies, - objects); - } + public void apply(Project project) { + project.getPlugins().apply(JavaPlugin.class); + ExtensionContainer extensions = project.getExtensions(); + JavaPluginExtension javaPluginExtension = extensions.getByType(JavaPluginExtension.class); + ConfigurationContainer configurations = project.getConfigurations(); + TaskContainer tasks = project.getTasks(); + DependencyHandler dependencies = project.getDependencies(); + ObjectFactory objects = project.getObjects(); + extensions.create( + "multiRelease", + MultiReleaseExtension.class, + javaPluginExtension.getSourceSets(), + configurations, + tasks, + dependencies, + objects); + } } diff --git a/buildSrc/src/main/java/org/springframework/build/optional/OptionalDependenciesPlugin.java b/buildSrc/src/main/java/org/springframework/build/optional/OptionalDependenciesPlugin.java index 89475866612d..17d647192503 100644 --- a/buildSrc/src/main/java/org/springframework/build/optional/OptionalDependenciesPlugin.java +++ b/buildSrc/src/main/java/org/springframework/build/optional/OptionalDependenciesPlugin.java @@ -24,33 +24,40 @@ import org.gradle.api.tasks.SourceSetContainer; /** - * A {@code Plugin} that adds support for Maven-style optional dependencies. Creates a new - * {@code optional} configuration. The {@code optional} configuration is part of the - * project's compile and runtime classpaths but does not affect the classpath of - * dependent projects. + * A {@code Plugin} that adds support for Maven-style optional dependencies. Creates a new {@code + * optional} configuration. The {@code optional} configuration is part of the project's compile and + * runtime classpaths but does not affect the classpath of dependent projects. * * @author Andy Wilkinson */ public class OptionalDependenciesPlugin implements Plugin { - /** - * Name of the {@code optional} configuration. - */ - public static final String OPTIONAL_CONFIGURATION_NAME = "optional"; - - @Override - public void apply(Project project) { - Configuration optional = project.getConfigurations().create(OPTIONAL_CONFIGURATION_NAME); - optional.setCanBeConsumed(false); - optional.setCanBeResolved(false); - project.getPlugins().withType(JavaBasePlugin.class, (javaBasePlugin) -> { - SourceSetContainer sourceSets = project.getExtensions().getByType(JavaPluginExtension.class) - .getSourceSets(); - sourceSets.all((sourceSet) -> { - project.getConfigurations().getByName(sourceSet.getCompileClasspathConfigurationName()).extendsFrom(optional); - project.getConfigurations().getByName(sourceSet.getRuntimeClasspathConfigurationName()).extendsFrom(optional); - }); - }); - } + /** Name of the {@code optional} configuration. */ + public static final String OPTIONAL_CONFIGURATION_NAME = "optional"; + @Override + public void apply(Project project) { + Configuration optional = project.getConfigurations().create(OPTIONAL_CONFIGURATION_NAME); + optional.setCanBeConsumed(false); + optional.setCanBeResolved(false); + project + .getPlugins() + .withType( + JavaBasePlugin.class, + (javaBasePlugin) -> { + SourceSetContainer sourceSets = + project.getExtensions().getByType(JavaPluginExtension.class).getSourceSets(); + sourceSets.all( + (sourceSet) -> { + project + .getConfigurations() + .getByName(sourceSet.getCompileClasspathConfigurationName()) + .extendsFrom(optional); + project + .getConfigurations() + .getByName(sourceSet.getRuntimeClasspathConfigurationName()) + .extendsFrom(optional); + }); + }); + } } diff --git a/buildSrc/src/main/java/org/springframework/build/shadow/ShadowSource.java b/buildSrc/src/main/java/org/springframework/build/shadow/ShadowSource.java index ed30b8609caa..7bdb539855d1 100644 --- a/buildSrc/src/main/java/org/springframework/build/shadow/ShadowSource.java +++ b/buildSrc/src/main/java/org/springframework/build/shadow/ShadowSource.java @@ -21,7 +21,6 @@ import java.util.Collections; import java.util.List; import java.util.Set; - import org.gradle.api.DefaultTask; import org.gradle.api.artifacts.Configuration; import org.gradle.api.artifacts.component.ModuleComponentSelector; @@ -51,141 +50,149 @@ */ public class ShadowSource extends DefaultTask { - private final DirectoryProperty outputDirectory = getProject().getObjects().directoryProperty(); - - private List configurations = new ArrayList<>(); - - private final List relocations = new ArrayList<>(); - - - @Classpath - @Optional - public List getConfigurations() { - return this.configurations; - } - - public void setConfigurations(List configurations) { - this.configurations = configurations; - } - - @Nested - public List getRelocations() { - return this.relocations; - } - - public void relocate(String pattern, String destination) { - this.relocations.add(new Relocation(pattern, destination)); - } - - @OutputDirectory - DirectoryProperty getOutputDirectory() { - return this.outputDirectory; - } - - @TaskAction - void syncSourceJarFiles() { - sync(getSourceJarFiles()); - } - - private List getSourceJarFiles() { - List sourceJarFiles = new ArrayList<>(); - for (Configuration configuration : this.configurations) { - ResolutionResult resolutionResult = configuration.getIncoming().getResolutionResult(); - resolutionResult.getRootComponent().get().getDependencies().forEach(dependency -> { - Set artifactsResults = resolveSourceArtifacts(dependency); - for (ComponentArtifactsResult artifactResult : artifactsResults) { - artifactResult.getArtifacts(SourcesArtifact.class).forEach(sourceArtifact -> { - sourceJarFiles.add(((ResolvedArtifactResult) sourceArtifact).getFile()); - }); - } - }); - } - return Collections.unmodifiableList(sourceJarFiles); - } - - private Set resolveSourceArtifacts(DependencyResult dependency) { - ModuleComponentSelector componentSelector = (ModuleComponentSelector) dependency.getRequested(); - ArtifactResolutionQuery query = getProject().getDependencies().createArtifactResolutionQuery() - .forModule(componentSelector.getGroup(), componentSelector.getModule(), componentSelector.getVersion()); - return executeQuery(query).getResolvedComponents(); - } - - @SuppressWarnings("unchecked") - private ArtifactResolutionResult executeQuery(ArtifactResolutionQuery query) { - return query.withArtifacts(JvmLibrary.class, SourcesArtifact.class).execute(); - } - - private void sync(List sourceJarFiles) { - getProject().sync(spec -> { - spec.into(this.outputDirectory); - spec.eachFile(this::relocateFile); - spec.filter(this::transformContent); - spec.exclude("META-INF/**"); - spec.setIncludeEmptyDirs(false); - sourceJarFiles.forEach(sourceJar -> spec.from(zipTree(sourceJar))); - }); - } - - private void relocateFile(FileCopyDetails details) { - String path = details.getPath(); - for (Relocation relocation : this.relocations) { - path = relocation.relocatePath(path); - } - details.setPath(path); - } - - private String transformContent(String content) { - for (Relocation relocation : this.relocations) { - content = relocation.transformContent(content); - } - return content; - } - - private FileTree zipTree(File sourceJar) { - return getProject().zipTree(sourceJar); - } - - - /** - * A single relocation. - */ - static class Relocation { - - private final String pattern; - - private final String pathPattern; - - private final String destination; - - private final String pathDestination; - - - Relocation(String pattern, String destination) { - this.pattern = pattern; - this.pathPattern = pattern.replace('.', '/'); - this.destination = destination; - this.pathDestination = destination.replace('.', '/'); - } - - - @Input - public String getPattern() { - return this.pattern; - } - - @Input - public String getDestination() { - return this.destination; - } - - String relocatePath(String path) { - return path.replace(this.pathPattern, this.pathDestination); - } - - public String transformContent(String content) { - return content.replaceAll("\\b" + this.pattern, this.destination); - } - - } - + private final DirectoryProperty outputDirectory = getProject().getObjects().directoryProperty(); + + private List configurations = new ArrayList<>(); + + private final List relocations = new ArrayList<>(); + + @Classpath + @Optional + public List getConfigurations() { + return this.configurations; + } + + public void setConfigurations(List configurations) { + this.configurations = configurations; + } + + @Nested + public List getRelocations() { + return this.relocations; + } + + public void relocate(String pattern, String destination) { + this.relocations.add(new Relocation(pattern, destination)); + } + + @OutputDirectory + DirectoryProperty getOutputDirectory() { + return this.outputDirectory; + } + + @TaskAction + void syncSourceJarFiles() { + sync(getSourceJarFiles()); + } + + private List getSourceJarFiles() { + List sourceJarFiles = new ArrayList<>(); + for (Configuration configuration : this.configurations) { + ResolutionResult resolutionResult = configuration.getIncoming().getResolutionResult(); + resolutionResult + .getRootComponent() + .get() + .getDependencies() + .forEach( + dependency -> { + Set artifactsResults = resolveSourceArtifacts(dependency); + for (ComponentArtifactsResult artifactResult : artifactsResults) { + artifactResult + .getArtifacts(SourcesArtifact.class) + .forEach( + sourceArtifact -> { + sourceJarFiles.add(((ResolvedArtifactResult) sourceArtifact).getFile()); + }); + } + }); + } + return Collections.unmodifiableList(sourceJarFiles); + } + + private Set resolveSourceArtifacts(DependencyResult dependency) { + ModuleComponentSelector componentSelector = (ModuleComponentSelector) dependency.getRequested(); + ArtifactResolutionQuery query = + getProject() + .getDependencies() + .createArtifactResolutionQuery() + .forModule( + componentSelector.getGroup(), + componentSelector.getModule(), + componentSelector.getVersion()); + return executeQuery(query).getResolvedComponents(); + } + + @SuppressWarnings("unchecked") + private ArtifactResolutionResult executeQuery(ArtifactResolutionQuery query) { + return query.withArtifacts(JvmLibrary.class, SourcesArtifact.class).execute(); + } + + private void sync(List sourceJarFiles) { + getProject() + .sync( + spec -> { + spec.into(this.outputDirectory); + spec.eachFile(this::relocateFile); + spec.filter(this::transformContent); + spec.exclude("META-INF/**"); + spec.setIncludeEmptyDirs(false); + sourceJarFiles.forEach(sourceJar -> spec.from(zipTree(sourceJar))); + }); + } + + private void relocateFile(FileCopyDetails details) { + String path = details.getPath(); + for (Relocation relocation : this.relocations) { + path = relocation.relocatePath(path); + } + details.setPath(path); + } + + private String transformContent(String content) { + for (Relocation relocation : this.relocations) { + content = relocation.transformContent(content); + } + return content; + } + + private FileTree zipTree(File sourceJar) { + return getProject().zipTree(sourceJar); + } + + /** A single relocation. */ + static class Relocation { + + private final String pattern; + + private final String pathPattern; + + private final String destination; + + private final String pathDestination; + + Relocation(String pattern, String destination) { + this.pattern = pattern; + this.pathPattern = pattern.replace('.', '/'); + this.destination = destination; + this.pathDestination = destination.replace('.', '/'); + } + + @Input + public String getPattern() { + return this.pattern; + } + + @Input + public String getDestination() { + return this.destination; + } + + String relocatePath(String path) { + return path.replace(this.pathPattern, this.pathDestination); + } + + public String transformContent(String content) { + return content.replaceAll("\\b" + this.pattern, this.destination); + } + } } diff --git a/buildSrc/src/test/java/org/springframework/build/multirelease/MultiReleaseJarPluginTests.java b/buildSrc/src/test/java/org/springframework/build/multirelease/MultiReleaseJarPluginTests.java index b4d2f618803f..7c37942e3a47 100644 --- a/buildSrc/src/test/java/org/springframework/build/multirelease/MultiReleaseJarPluginTests.java +++ b/buildSrc/src/test/java/org/springframework/build/multirelease/MultiReleaseJarPluginTests.java @@ -16,6 +16,8 @@ package org.springframework.build.multirelease; +import static org.assertj.core.api.Assertions.assertThat; + import java.io.File; import java.io.FileWriter; import java.io.IOException; @@ -30,26 +32,23 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link MultiReleaseJarPlugin} - */ +/** Tests for {@link MultiReleaseJarPlugin} */ public class MultiReleaseJarPluginTests { - private File projectDir; + private File projectDir; - private File buildFile; + private File buildFile; - @BeforeEach - void setup(@TempDir File projectDir) { - this.projectDir = projectDir; - this.buildFile = new File(this.projectDir, "build.gradle"); - } + @BeforeEach + void setup(@TempDir File projectDir) { + this.projectDir = projectDir; + this.buildFile = new File(this.projectDir, "build.gradle"); + } - @Test - void configureSourceSets() throws IOException { - writeBuildFile(""" + @Test + void configureSourceSets() throws IOException { + writeBuildFile( + """ plugins { id 'java' id 'org.springframework.build.multiReleaseJar' @@ -61,13 +60,15 @@ void configureSourceSets() throws IOException { } } """); - BuildResult buildResult = runGradle("printSourceSets"); - assertThat(buildResult.getOutput()).contains("main", "test", "java21", "java21Test", "java24", "java24Test"); - } - - @Test - void configureToolchainReleaseVersion() throws IOException { - writeBuildFile(""" + BuildResult buildResult = runGradle("printSourceSets"); + assertThat(buildResult.getOutput()) + .contains("main", "test", "java21", "java21Test", "java24", "java24Test"); + } + + @Test + void configureToolchainReleaseVersion() throws IOException { + writeBuildFile( + """ plugins { id 'java' id 'org.springframework.build.multiReleaseJar' @@ -86,14 +87,16 @@ void configureToolchainReleaseVersion() throws IOException { } """); - BuildResult buildResult = runGradle("printReleaseVersion"); - assertThat(buildResult.getOutput()).contains("compileJava21Java releaseVersion: 21") - .contains("compileJava21TestJava releaseVersion: 21"); - } + BuildResult buildResult = runGradle("printReleaseVersion"); + assertThat(buildResult.getOutput()) + .contains("compileJava21Java releaseVersion: 21") + .contains("compileJava21TestJava releaseVersion: 21"); + } - @Test - void packageInJar() throws IOException { - writeBuildFile(""" + @Test + void packageInJar() throws IOException { + writeBuildFile( + """ plugins { id 'java' id 'org.springframework.build.multiReleaseJar' @@ -101,37 +104,45 @@ void packageInJar() throws IOException { version = '1.2.3' multiRelease { releaseVersions 17 } """); - writeClass("src/main/java17", "Main.java", """ + writeClass( + "src/main/java17", + "Main.java", + """ public class Main {} """); - BuildResult buildResult = runGradle("assemble"); - File file = new File(this.projectDir, "/build/libs/" + this.projectDir.getName() + "-1.2.3.jar"); - assertThat(file).exists(); - try (JarFile jar = new JarFile(file)) { - Attributes mainAttributes = jar.getManifest().getMainAttributes(); - assertThat(mainAttributes.getValue("Multi-Release")).isEqualTo("true"); - - assertThat(jar.entries().asIterator()).toIterable() - .anyMatch(entry -> entry.getName().equals("META-INF/versions/17/Main.class")); - } - } - - private void writeBuildFile(String buildContent) throws IOException { - try (PrintWriter out = new PrintWriter(new FileWriter(this.buildFile))) { - out.print(buildContent); - } - } - - private void writeClass(String path, String fileName, String fileContent) throws IOException { - Path folder = this.projectDir.toPath().resolve(path); - Files.createDirectories(folder); - Path filePath = folder.resolve(fileName); - Files.createFile(filePath); - Files.writeString(filePath, fileContent); - } - - private BuildResult runGradle(String... args) { - return GradleRunner.create().withProjectDir(this.projectDir).withArguments(args).withPluginClasspath().build(); - } - + BuildResult buildResult = runGradle("assemble"); + File file = + new File(this.projectDir, "/build/libs/" + this.projectDir.getName() + "-1.2.3.jar"); + assertThat(file).exists(); + try (JarFile jar = new JarFile(file)) { + Attributes mainAttributes = jar.getManifest().getMainAttributes(); + assertThat(mainAttributes.getValue("Multi-Release")).isEqualTo("true"); + + assertThat(jar.entries().asIterator()) + .toIterable() + .anyMatch(entry -> entry.getName().equals("META-INF/versions/17/Main.class")); + } + } + + private void writeBuildFile(String buildContent) throws IOException { + try (PrintWriter out = new PrintWriter(new FileWriter(this.buildFile))) { + out.print(buildContent); + } + } + + private void writeClass(String path, String fileName, String fileContent) throws IOException { + Path folder = this.projectDir.toPath().resolve(path); + Files.createDirectories(folder); + Path filePath = folder.resolve(fileName); + Files.createFile(filePath); + Files.writeString(filePath, fileContent); + } + + private BuildResult runGradle(String... args) { + return GradleRunner.create() + .withProjectDir(this.projectDir) + .withArguments(args) + .withPluginClasspath() + .build(); + } } From ec1414057eae1e8df96b4913fc4ef566a14f8e93 Mon Sep 17 00:00:00 2001 From: Vincent Potucek Date: Sun, 1 Jun 2025 09:20:56 +0200 Subject: [PATCH 06/15] spotless --- buildSrc/build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index 444de5d035e7..46b139cecbe9 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -6,6 +6,7 @@ spotless { java { removeUnusedImports() googleJavaFormat() +// palantirJavaFormat() } } repositories { From 807155501e6358ebb7f57cc8c3caa255fd494e29 Mon Sep 17 00:00:00 2001 From: Vincent Potucek Date: Sun, 1 Jun 2025 09:21:42 +0200 Subject: [PATCH 07/15] spotless --- buildSrc/build.gradle | 3 +- .../build/ConventionsPlugin.java | 16 +- .../build/JavaConventions.java | 231 ++++++++-------- .../build/KotlinConventions.java | 43 ++- .../build/SpringFrameworkExtension.java | 46 ++-- .../build/TestConventions.java | 85 +++--- .../build/architecture/ArchitectureCheck.java | 140 +++++----- .../architecture/ArchitecturePlugin.java | 73 +++--- .../build/architecture/ArchitectureRules.java | 148 ++++++----- .../build/dev/LocalDevelopmentPlugin.java | 34 ++- .../RuntimeHintsAgentArgumentProvider.java | 34 +-- .../hint/RuntimeHintsAgentExtension.java | 4 +- .../build/hint/RuntimeHintsAgentPlugin.java | 133 ++++------ .../multirelease/MultiReleaseExtension.java | 246 ++++++++---------- .../multirelease/MultiReleaseJarPlugin.java | 42 +-- .../optional/OptionalDependenciesPlugin.java | 37 ++- .../build/shadow/ShadowSource.java | 234 ++++++++--------- .../MultiReleaseJarPluginTests.java | 116 ++++----- 18 files changed, 776 insertions(+), 889 deletions(-) diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index 46b139cecbe9..edc708e5d3a9 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -5,8 +5,7 @@ plugins { spotless { java { removeUnusedImports() - googleJavaFormat() -// palantirJavaFormat() + palantirJavaFormat() } } repositories { diff --git a/buildSrc/src/main/java/org/springframework/build/ConventionsPlugin.java b/buildSrc/src/main/java/org/springframework/build/ConventionsPlugin.java index 9a564b36328a..19c1e6a819e8 100644 --- a/buildSrc/src/main/java/org/springframework/build/ConventionsPlugin.java +++ b/buildSrc/src/main/java/org/springframework/build/ConventionsPlugin.java @@ -35,12 +35,12 @@ */ public class ConventionsPlugin implements Plugin { - @Override - public void apply(Project project) { - project.getExtensions().create("springFramework", SpringFrameworkExtension.class); - new ArchitecturePlugin().apply(project); - new JavaConventions().apply(project); - new KotlinConventions().apply(project); - new TestConventions().apply(project); - } + @Override + public void apply(Project project) { + project.getExtensions().create("springFramework", SpringFrameworkExtension.class); + new ArchitecturePlugin().apply(project); + new JavaConventions().apply(project); + new KotlinConventions().apply(project); + new TestConventions().apply(project); + } } diff --git a/buildSrc/src/main/java/org/springframework/build/JavaConventions.java b/buildSrc/src/main/java/org/springframework/build/JavaConventions.java index e816820f510a..63f7836905b7 100644 --- a/buildSrc/src/main/java/org/springframework/build/JavaConventions.java +++ b/buildSrc/src/main/java/org/springframework/build/JavaConventions.java @@ -36,138 +36,121 @@ */ public class JavaConventions { - private static final List COMPILER_ARGS; + private static final List COMPILER_ARGS; - private static final List TEST_COMPILER_ARGS; + private static final List TEST_COMPILER_ARGS; - /** - * The Java version we should use as the JVM baseline for building the project. - * - *

NOTE: If you update this value, you should also update the value used in the {@code javadoc} - * task in {@code framework-api.gradle}. - */ - private static final JavaLanguageVersion DEFAULT_LANGUAGE_VERSION = JavaLanguageVersion.of(24); + /** + * The Java version we should use as the JVM baseline for building the project. + * + *

NOTE: If you update this value, you should also update the value used in the {@code javadoc} + * task in {@code framework-api.gradle}. + */ + private static final JavaLanguageVersion DEFAULT_LANGUAGE_VERSION = JavaLanguageVersion.of(24); - /** - * The Java version we should use as the baseline for the compiled bytecode (the "-release" - * compiler argument). - */ - private static final JavaLanguageVersion DEFAULT_RELEASE_VERSION = JavaLanguageVersion.of(17); + /** + * The Java version we should use as the baseline for the compiled bytecode (the "-release" + * compiler argument). + */ + private static final JavaLanguageVersion DEFAULT_RELEASE_VERSION = JavaLanguageVersion.of(17); - static { - List commonCompilerArgs = - List.of( - "-Xlint:serial", - "-Xlint:cast", - "-Xlint:classfile", - "-Xlint:dep-ann", - "-Xlint:divzero", - "-Xlint:empty", - "-Xlint:finally", - "-Xlint:overrides", - "-Xlint:path", - "-Xlint:processing", - "-Xlint:static", - "-Xlint:try", - "-Xlint:-options", - "-parameters"); - COMPILER_ARGS = new ArrayList<>(); - COMPILER_ARGS.addAll(commonCompilerArgs); - COMPILER_ARGS.addAll( - List.of( - "-Xlint:varargs", - "-Xlint:fallthrough", - "-Xlint:rawtypes", - "-Xlint:deprecation", - "-Xlint:unchecked", - "-Werror")); - TEST_COMPILER_ARGS = new ArrayList<>(); - TEST_COMPILER_ARGS.addAll(commonCompilerArgs); - TEST_COMPILER_ARGS.addAll( - List.of( - "-Xlint:-varargs", - "-Xlint:-fallthrough", - "-Xlint:-rawtypes", - "-Xlint:-deprecation", - "-Xlint:-unchecked")); - } + static { + List commonCompilerArgs = List.of( + "-Xlint:serial", + "-Xlint:cast", + "-Xlint:classfile", + "-Xlint:dep-ann", + "-Xlint:divzero", + "-Xlint:empty", + "-Xlint:finally", + "-Xlint:overrides", + "-Xlint:path", + "-Xlint:processing", + "-Xlint:static", + "-Xlint:try", + "-Xlint:-options", + "-parameters"); + COMPILER_ARGS = new ArrayList<>(); + COMPILER_ARGS.addAll(commonCompilerArgs); + COMPILER_ARGS.addAll(List.of( + "-Xlint:varargs", + "-Xlint:fallthrough", + "-Xlint:rawtypes", + "-Xlint:deprecation", + "-Xlint:unchecked", + "-Werror")); + TEST_COMPILER_ARGS = new ArrayList<>(); + TEST_COMPILER_ARGS.addAll(commonCompilerArgs); + TEST_COMPILER_ARGS.addAll(List.of( + "-Xlint:-varargs", + "-Xlint:-fallthrough", + "-Xlint:-rawtypes", + "-Xlint:-deprecation", + "-Xlint:-unchecked")); + } - public void apply(Project project) { - project - .getPlugins() - .withType( - JavaBasePlugin.class, - javaPlugin -> { - applyToolchainConventions(project); - applyJavaCompileConventions(project); - }); - } + public void apply(Project project) { + project.getPlugins().withType(JavaBasePlugin.class, javaPlugin -> { + applyToolchainConventions(project); + applyJavaCompileConventions(project); + }); + } - /** - * Configure the Toolchain support for the project. - * - * @param project the current project - */ - private static void applyToolchainConventions(Project project) { - project - .getExtensions() - .getByType(JavaPluginExtension.class) - .toolchain( - toolchain -> { - toolchain.getVendor().set(JvmVendorSpec.BELLSOFT); - toolchain.getLanguageVersion().set(DEFAULT_LANGUAGE_VERSION); - }); - } + /** + * Configure the Toolchain support for the project. + * + * @param project the current project + */ + private static void applyToolchainConventions(Project project) { + project.getExtensions().getByType(JavaPluginExtension.class).toolchain(toolchain -> { + toolchain.getVendor().set(JvmVendorSpec.BELLSOFT); + toolchain.getLanguageVersion().set(DEFAULT_LANGUAGE_VERSION); + }); + } - /** - * Apply the common Java compiler options for main sources, test fixture sources, and test - * sources. - * - * @param project the current project - */ - private void applyJavaCompileConventions(Project project) { - project.afterEvaluate( - p -> { - p.getTasks() - .withType(JavaCompile.class) - .matching( - compileTask -> - compileTask.getName().startsWith(JavaPlugin.COMPILE_JAVA_TASK_NAME)) - .forEach( - compileTask -> { - compileTask.getOptions().setCompilerArgs(COMPILER_ARGS); - compileTask.getOptions().setEncoding("UTF-8"); - setJavaRelease(compileTask); - }); - p.getTasks() - .withType(JavaCompile.class) - .matching( - compileTask -> - compileTask.getName().startsWith(JavaPlugin.COMPILE_TEST_JAVA_TASK_NAME) - || compileTask.getName().equals("compileTestFixturesJava")) - .forEach( - compileTask -> { - compileTask.getOptions().setCompilerArgs(TEST_COMPILER_ARGS); - compileTask.getOptions().setEncoding("UTF-8"); - setJavaRelease(compileTask); - }); + /** + * Apply the common Java compiler options for main sources, test fixture sources, and test + * sources. + * + * @param project the current project + */ + private void applyJavaCompileConventions(Project project) { + project.afterEvaluate(p -> { + p.getTasks() + .withType(JavaCompile.class) + .matching(compileTask -> compileTask.getName().startsWith(JavaPlugin.COMPILE_JAVA_TASK_NAME)) + .forEach(compileTask -> { + compileTask.getOptions().setCompilerArgs(COMPILER_ARGS); + compileTask.getOptions().setEncoding("UTF-8"); + setJavaRelease(compileTask); + }); + p.getTasks() + .withType(JavaCompile.class) + .matching(compileTask -> compileTask.getName().startsWith(JavaPlugin.COMPILE_TEST_JAVA_TASK_NAME) + || compileTask.getName().equals("compileTestFixturesJava")) + .forEach(compileTask -> { + compileTask.getOptions().setCompilerArgs(TEST_COMPILER_ARGS); + compileTask.getOptions().setEncoding("UTF-8"); + setJavaRelease(compileTask); + }); }); - } + } - /** - * We should pick the {@link #DEFAULT_RELEASE_VERSION} for all compiled classes, unless the - * current task is compiling multi-release JAR code with a higher version. - */ - private void setJavaRelease(JavaCompile task) { - int defaultVersion = DEFAULT_RELEASE_VERSION.asInt(); - int releaseVersion = defaultVersion; - int compilerVersion = task.getJavaCompiler().get().getMetadata().getLanguageVersion().asInt(); - for (int version = defaultVersion; version <= compilerVersion; version++) { - if (task.getName().contains("Java" + version)) { - releaseVersion = version; - break; - } + /** + * We should pick the {@link #DEFAULT_RELEASE_VERSION} for all compiled classes, unless the + * current task is compiling multi-release JAR code with a higher version. + */ + private void setJavaRelease(JavaCompile task) { + int defaultVersion = DEFAULT_RELEASE_VERSION.asInt(); + int releaseVersion = defaultVersion; + int compilerVersion = + task.getJavaCompiler().get().getMetadata().getLanguageVersion().asInt(); + for (int version = defaultVersion; version <= compilerVersion; version++) { + if (task.getName().contains("Java" + version)) { + releaseVersion = version; + break; + } + } + task.getOptions().getRelease().set(releaseVersion); } - task.getOptions().getRelease().set(releaseVersion); - } } diff --git a/buildSrc/src/main/java/org/springframework/build/KotlinConventions.java b/buildSrc/src/main/java/org/springframework/build/KotlinConventions.java index 5e1bf29f3e44..7cd64aaf2bc0 100644 --- a/buildSrc/src/main/java/org/springframework/build/KotlinConventions.java +++ b/buildSrc/src/main/java/org/springframework/build/KotlinConventions.java @@ -27,30 +27,25 @@ */ public class KotlinConventions { - void apply(Project project) { - project - .getPlugins() - .withId( - "org.jetbrains.kotlin.jvm", - (plugin) -> project.getTasks().withType(KotlinCompile.class, this::configure)); - } + void apply(Project project) { + project.getPlugins().withId("org.jetbrains.kotlin.jvm", (plugin) -> project.getTasks() + .withType(KotlinCompile.class, this::configure)); + } - private void configure(KotlinCompile compile) { - compile.compilerOptions( - options -> { - options.getApiVersion().set(KotlinVersion.KOTLIN_2_1); - options.getLanguageVersion().set(KotlinVersion.KOTLIN_2_1); - options.getJvmTarget().set(JvmTarget.JVM_17); - options.getJavaParameters().set(true); - options.getAllWarningsAsErrors().set(true); - options - .getFreeCompilerArgs() - .addAll( - "-Xsuppress-version-warnings", - "-Xjsr305=strict", // For dependencies using JSR 305 - "-opt-in=kotlin.RequiresOptIn", - "-Xjdk-release=17" // Needed due to https://youtrack.jetbrains.com/issue/KT-49746 - ); + private void configure(KotlinCompile compile) { + compile.compilerOptions(options -> { + options.getApiVersion().set(KotlinVersion.KOTLIN_2_1); + options.getLanguageVersion().set(KotlinVersion.KOTLIN_2_1); + options.getJvmTarget().set(JvmTarget.JVM_17); + options.getJavaParameters().set(true); + options.getAllWarningsAsErrors().set(true); + options.getFreeCompilerArgs() + .addAll( + "-Xsuppress-version-warnings", + "-Xjsr305=strict", // For dependencies using JSR 305 + "-opt-in=kotlin.RequiresOptIn", + "-Xjdk-release=17" // Needed due to https://youtrack.jetbrains.com/issue/KT-49746 + ); }); - } + } } diff --git a/buildSrc/src/main/java/org/springframework/build/SpringFrameworkExtension.java b/buildSrc/src/main/java/org/springframework/build/SpringFrameworkExtension.java index f0ee9989ceb0..1ed02965cdc7 100644 --- a/buildSrc/src/main/java/org/springframework/build/SpringFrameworkExtension.java +++ b/buildSrc/src/main/java/org/springframework/build/SpringFrameworkExtension.java @@ -26,32 +26,28 @@ public class SpringFrameworkExtension { - private final Property enableJavaPreviewFeatures; + private final Property enableJavaPreviewFeatures; - public SpringFrameworkExtension(Project project) { - this.enableJavaPreviewFeatures = project.getObjects().property(Boolean.class); - project - .getTasks() - .withType(JavaCompile.class) - .configureEach( - javaCompile -> - javaCompile.getOptions().getCompilerArgumentProviders().add(asArgumentProvider())); - project - .getTasks() - .withType(Test.class) - .configureEach(test -> test.getJvmArgumentProviders().add(asArgumentProvider())); - } + public SpringFrameworkExtension(Project project) { + this.enableJavaPreviewFeatures = project.getObjects().property(Boolean.class); + project.getTasks() + .withType(JavaCompile.class) + .configureEach(javaCompile -> + javaCompile.getOptions().getCompilerArgumentProviders().add(asArgumentProvider())); + project.getTasks().withType(Test.class).configureEach(test -> test.getJvmArgumentProviders() + .add(asArgumentProvider())); + } - public Property getEnableJavaPreviewFeatures() { - return this.enableJavaPreviewFeatures; - } + public Property getEnableJavaPreviewFeatures() { + return this.enableJavaPreviewFeatures; + } - private CommandLineArgumentProvider asArgumentProvider() { - return () -> { - if (getEnableJavaPreviewFeatures().getOrElse(false)) { - return List.of("--enable-preview"); - } - return Collections.emptyList(); - }; - } + private CommandLineArgumentProvider asArgumentProvider() { + return () -> { + if (getEnableJavaPreviewFeatures().getOrElse(false)) { + return List.of("--enable-preview"); + } + return Collections.emptyList(); + }; + } } diff --git a/buildSrc/src/main/java/org/springframework/build/TestConventions.java b/buildSrc/src/main/java/org/springframework/build/TestConventions.java index 58e42d556e28..4cd5e18454fb 100644 --- a/buildSrc/src/main/java/org/springframework/build/TestConventions.java +++ b/buildSrc/src/main/java/org/springframework/build/TestConventions.java @@ -40,59 +40,46 @@ */ class TestConventions { - void apply(Project project) { - project - .getPlugins() - .withType(JavaBasePlugin.class, (java) -> configureTestConventions(project)); - } + void apply(Project project) { + project.getPlugins().withType(JavaBasePlugin.class, (java) -> configureTestConventions(project)); + } - private void configureTestConventions(Project project) { - project - .getTasks() - .withType( - Test.class, - test -> { - configureTests(project, test); - configureTestRetryPlugin(project, test); - }); - } + private void configureTestConventions(Project project) { + project.getTasks().withType(Test.class, test -> { + configureTests(project, test); + configureTestRetryPlugin(project, test); + }); + } - private void configureTests(Project project, Test test) { - TestFrameworkOptions existingOptions = test.getOptions(); - test.useJUnitPlatform( - options -> { - if (existingOptions instanceof JUnitPlatformOptions junitPlatformOptions) { - options.copyFrom(junitPlatformOptions); - } + private void configureTests(Project project, Test test) { + TestFrameworkOptions existingOptions = test.getOptions(); + test.useJUnitPlatform(options -> { + if (existingOptions instanceof JUnitPlatformOptions junitPlatformOptions) { + options.copyFrom(junitPlatformOptions); + } }); - test.include("**/*Tests.class", "**/*Test.class"); - test.setSystemProperties( - Map.of( - "java.awt.headless", "true", - "io.netty.leakDetection.level", "paranoid")); - if (project.hasProperty("testGroups")) { - test.systemProperty("testGroups", project.getProperties().get("testGroups")); + test.include("**/*Tests.class", "**/*Test.class"); + test.setSystemProperties(Map.of( + "java.awt.headless", "true", + "io.netty.leakDetection.level", "paranoid")); + if (project.hasProperty("testGroups")) { + test.systemProperty("testGroups", project.getProperties().get("testGroups")); + } + test.jvmArgs( + "--add-opens=java.base/java.lang=ALL-UNNAMED", + "--add-opens=java.base/java.util=ALL-UNNAMED", + "-Xshare:off"); } - test.jvmArgs( - "--add-opens=java.base/java.lang=ALL-UNNAMED", - "--add-opens=java.base/java.util=ALL-UNNAMED", - "-Xshare:off"); - } - private void configureTestRetryPlugin(Project project, Test test) { - project - .getPlugins() - .withType( - TestRetryPlugin.class, - testRetryPlugin -> { - TestRetryTaskExtension testRetry = - test.getExtensions().getByType(TestRetryTaskExtension.class); - testRetry.getFailOnPassedAfterRetry().set(true); - testRetry.getMaxRetries().set(isCi() ? 3 : 0); - }); - } + private void configureTestRetryPlugin(Project project, Test test) { + project.getPlugins().withType(TestRetryPlugin.class, testRetryPlugin -> { + TestRetryTaskExtension testRetry = test.getExtensions().getByType(TestRetryTaskExtension.class); + testRetry.getFailOnPassedAfterRetry().set(true); + testRetry.getMaxRetries().set(isCi() ? 3 : 0); + }); + } - private boolean isCi() { - return Boolean.parseBoolean(System.getenv("CI")); - } + private boolean isCi() { + return Boolean.parseBoolean(System.getenv("CI")); + } } diff --git a/buildSrc/src/main/java/org/springframework/build/architecture/ArchitectureCheck.java b/buildSrc/src/main/java/org/springframework/build/architecture/ArchitectureCheck.java index 0133f37cc5e6..cb595832866b 100644 --- a/buildSrc/src/main/java/org/springframework/build/architecture/ArchitectureCheck.java +++ b/buildSrc/src/main/java/org/springframework/build/architecture/ArchitectureCheck.java @@ -59,85 +59,85 @@ */ public abstract class ArchitectureCheck extends DefaultTask { - private FileCollection classes; + private FileCollection classes; - public ArchitectureCheck() { - getOutputDirectory().convention(getProject().getLayout().getBuildDirectory().dir(getName())); - getProhibitObjectsRequireNonNull().convention(true); - getRules() - .addAll( - packageInfoShouldBeNullMarked(), - classesShouldNotImportForbiddenTypes(), - javaClassesShouldNotImportKotlinAnnotations(), - allPackagesShouldBeFreeOfTangles(), - noClassesShouldCallStringToLowerCaseWithoutLocale(), - noClassesShouldCallStringToUpperCaseWithoutLocale()); - getRuleDescriptions() - .set(getRules().map((rules) -> rules.stream().map(ArchRule::getDescription).toList())); - } + public ArchitectureCheck() { + getOutputDirectory() + .convention(getProject().getLayout().getBuildDirectory().dir(getName())); + getProhibitObjectsRequireNonNull().convention(true); + getRules() + .addAll( + packageInfoShouldBeNullMarked(), + classesShouldNotImportForbiddenTypes(), + javaClassesShouldNotImportKotlinAnnotations(), + allPackagesShouldBeFreeOfTangles(), + noClassesShouldCallStringToLowerCaseWithoutLocale(), + noClassesShouldCallStringToUpperCaseWithoutLocale()); + getRuleDescriptions().set(getRules().map((rules) -> rules.stream() + .map(ArchRule::getDescription) + .toList())); + } - @TaskAction - void checkArchitecture() throws IOException { - JavaClasses javaClasses = - new ClassFileImporter() - .importPaths(this.classes.getFiles().stream().map(File::toPath).toList()); - List violations = - getRules().get().stream() - .map((rule) -> rule.evaluate(javaClasses)) - .filter(EvaluationResult::hasViolation) - .toList(); - File outputFile = getOutputDirectory().file("failure-report.txt").get().getAsFile(); - outputFile.getParentFile().mkdirs(); - if (!violations.isEmpty()) { - StringBuilder report = new StringBuilder(); - for (EvaluationResult violation : violations) { - report.append(violation.getFailureReport()); - report.append(String.format("%n")); - } - Files.writeString( - outputFile.toPath(), - report.toString(), - StandardOpenOption.CREATE, - StandardOpenOption.TRUNCATE_EXISTING); - throw new GradleException("Architecture check failed. See '" + outputFile + "' for details."); - } else { - outputFile.createNewFile(); + @TaskAction + void checkArchitecture() throws IOException { + JavaClasses javaClasses = new ClassFileImporter() + .importPaths(this.classes.getFiles().stream().map(File::toPath).toList()); + List violations = getRules().get().stream() + .map((rule) -> rule.evaluate(javaClasses)) + .filter(EvaluationResult::hasViolation) + .toList(); + File outputFile = getOutputDirectory().file("failure-report.txt").get().getAsFile(); + outputFile.getParentFile().mkdirs(); + if (!violations.isEmpty()) { + StringBuilder report = new StringBuilder(); + for (EvaluationResult violation : violations) { + report.append(violation.getFailureReport()); + report.append(String.format("%n")); + } + Files.writeString( + outputFile.toPath(), + report.toString(), + StandardOpenOption.CREATE, + StandardOpenOption.TRUNCATE_EXISTING); + throw new GradleException("Architecture check failed. See '" + outputFile + "' for details."); + } else { + outputFile.createNewFile(); + } } - } - public void setClasses(FileCollection classes) { - this.classes = classes; - } + public void setClasses(FileCollection classes) { + this.classes = classes; + } - @Internal - public FileCollection getClasses() { - return this.classes; - } + @Internal + public FileCollection getClasses() { + return this.classes; + } - @InputFiles - @SkipWhenEmpty - @IgnoreEmptyDirectories - @PathSensitive(PathSensitivity.RELATIVE) - final FileTree getInputClasses() { - return this.classes.getAsFileTree(); - } + @InputFiles + @SkipWhenEmpty + @IgnoreEmptyDirectories + @PathSensitive(PathSensitivity.RELATIVE) + final FileTree getInputClasses() { + return this.classes.getAsFileTree(); + } - @Optional - @InputFiles - @PathSensitive(PathSensitivity.RELATIVE) - public abstract DirectoryProperty getResourcesDirectory(); + @Optional + @InputFiles + @PathSensitive(PathSensitivity.RELATIVE) + public abstract DirectoryProperty getResourcesDirectory(); - @OutputDirectory - public abstract DirectoryProperty getOutputDirectory(); + @OutputDirectory + public abstract DirectoryProperty getOutputDirectory(); - @Internal - public abstract ListProperty getRules(); + @Internal + public abstract ListProperty getRules(); - @Internal - public abstract Property getProhibitObjectsRequireNonNull(); + @Internal + public abstract Property getProhibitObjectsRequireNonNull(); - @Input - // The rules themselves can't be an input as they aren't serializable so we use - // their descriptions instead - abstract ListProperty getRuleDescriptions(); + @Input + // The rules themselves can't be an input as they aren't serializable so we use + // their descriptions instead + abstract ListProperty getRuleDescriptions(); } diff --git a/buildSrc/src/main/java/org/springframework/build/architecture/ArchitecturePlugin.java b/buildSrc/src/main/java/org/springframework/build/architecture/ArchitecturePlugin.java index b669118ab498..d33c3502760b 100644 --- a/buildSrc/src/main/java/org/springframework/build/architecture/ArchitecturePlugin.java +++ b/buildSrc/src/main/java/org/springframework/build/architecture/ArchitecturePlugin.java @@ -34,47 +34,40 @@ */ public class ArchitecturePlugin implements Plugin { - @Override - public void apply(Project project) { - project.getPlugins().withType(JavaPlugin.class, (javaPlugin) -> registerTasks(project)); - } - - private void registerTasks(Project project) { - JavaPluginExtension javaPluginExtension = - project.getExtensions().getByType(JavaPluginExtension.class); - List> architectureChecks = new ArrayList<>(); - for (SourceSet sourceSet : javaPluginExtension.getSourceSets()) { - if (sourceSet.getName().contains("test")) { - // skip test source sets. - continue; - } - TaskProvider checkArchitecture = - project - .getTasks() - .register( - taskName(sourceSet), - ArchitectureCheck.class, - (task) -> { - task.setClasses(sourceSet.getOutput().getClassesDirs()); - task.getResourcesDirectory().set(sourceSet.getOutput().getResourcesDir()); - task.dependsOn(sourceSet.getProcessResourcesTaskName()); - task.setDescription( - "Checks the architecture of the classes of the " - + sourceSet.getName() - + " source set."); - task.setGroup(LifecycleBasePlugin.VERIFICATION_GROUP); - }); - architectureChecks.add(checkArchitecture); + @Override + public void apply(Project project) { + project.getPlugins().withType(JavaPlugin.class, (javaPlugin) -> registerTasks(project)); } - if (!architectureChecks.isEmpty()) { - TaskProvider checkTask = project.getTasks().named(LifecycleBasePlugin.CHECK_TASK_NAME); - checkTask.configure((check) -> check.dependsOn(architectureChecks)); + + private void registerTasks(Project project) { + JavaPluginExtension javaPluginExtension = project.getExtensions().getByType(JavaPluginExtension.class); + List> architectureChecks = new ArrayList<>(); + for (SourceSet sourceSet : javaPluginExtension.getSourceSets()) { + if (sourceSet.getName().contains("test")) { + // skip test source sets. + continue; + } + TaskProvider checkArchitecture = project.getTasks() + .register(taskName(sourceSet), ArchitectureCheck.class, (task) -> { + task.setClasses(sourceSet.getOutput().getClassesDirs()); + task.getResourcesDirectory().set(sourceSet.getOutput().getResourcesDir()); + task.dependsOn(sourceSet.getProcessResourcesTaskName()); + task.setDescription("Checks the architecture of the classes of the " + + sourceSet.getName() + + " source set."); + task.setGroup(LifecycleBasePlugin.VERIFICATION_GROUP); + }); + architectureChecks.add(checkArchitecture); + } + if (!architectureChecks.isEmpty()) { + TaskProvider checkTask = project.getTasks().named(LifecycleBasePlugin.CHECK_TASK_NAME); + checkTask.configure((check) -> check.dependsOn(architectureChecks)); + } } - } - private static String taskName(SourceSet sourceSet) { - return "checkArchitecture" - + sourceSet.getName().substring(0, 1).toUpperCase() - + sourceSet.getName().substring(1); - } + private static String taskName(SourceSet sourceSet) { + return "checkArchitecture" + + sourceSet.getName().substring(0, 1).toUpperCase() + + sourceSet.getName().substring(1); + } } diff --git a/buildSrc/src/main/java/org/springframework/build/architecture/ArchitectureRules.java b/buildSrc/src/main/java/org/springframework/build/architecture/ArchitectureRules.java index ba80409b2ad1..d048b72b648e 100644 --- a/buildSrc/src/main/java/org/springframework/build/architecture/ArchitectureRules.java +++ b/buildSrc/src/main/java/org/springframework/build/architecture/ArchitectureRules.java @@ -27,88 +27,92 @@ abstract class ArchitectureRules { - static ArchRule allPackagesShouldBeFreeOfTangles() { - return SlicesRuleDefinition.slices().assignedFrom(new SpringSlices()).should().beFreeOfCycles(); - } + static ArchRule allPackagesShouldBeFreeOfTangles() { + return SlicesRuleDefinition.slices() + .assignedFrom(new SpringSlices()) + .should() + .beFreeOfCycles(); + } - static ArchRule noClassesShouldCallStringToLowerCaseWithoutLocale() { - return ArchRuleDefinition.noClasses() - .should() - .callMethod(String.class, "toLowerCase") - .because("String.toLowerCase(Locale.ROOT) should be used instead"); - } + static ArchRule noClassesShouldCallStringToLowerCaseWithoutLocale() { + return ArchRuleDefinition.noClasses() + .should() + .callMethod(String.class, "toLowerCase") + .because("String.toLowerCase(Locale.ROOT) should be used instead"); + } - static ArchRule noClassesShouldCallStringToUpperCaseWithoutLocale() { - return ArchRuleDefinition.noClasses() - .should() - .callMethod(String.class, "toUpperCase") - .because("String.toUpperCase(Locale.ROOT) should be used instead"); - } + static ArchRule noClassesShouldCallStringToUpperCaseWithoutLocale() { + return ArchRuleDefinition.noClasses() + .should() + .callMethod(String.class, "toUpperCase") + .because("String.toUpperCase(Locale.ROOT) should be used instead"); + } - static ArchRule packageInfoShouldBeNullMarked() { - return ArchRuleDefinition.classes() - .that() - .haveSimpleName("package-info") - .should() - .beAnnotatedWith("org.jspecify.annotations.NullMarked") - .allowEmptyShould(true); - } + static ArchRule packageInfoShouldBeNullMarked() { + return ArchRuleDefinition.classes() + .that() + .haveSimpleName("package-info") + .should() + .beAnnotatedWith("org.jspecify.annotations.NullMarked") + .allowEmptyShould(true); + } - static ArchRule classesShouldNotImportForbiddenTypes() { - return ArchRuleDefinition.noClasses() - .should() - .dependOnClassesThat() - .haveFullyQualifiedName("reactor.core.support.Assert") - .orShould() - .dependOnClassesThat() - .haveFullyQualifiedName("org.slf4j.LoggerFactory") - .orShould() - .dependOnClassesThat() - .haveFullyQualifiedName("org.springframework.lang.NonNull") - .orShould() - .dependOnClassesThat() - .haveFullyQualifiedName("org.springframework.lang.Nullable"); - } + static ArchRule classesShouldNotImportForbiddenTypes() { + return ArchRuleDefinition.noClasses() + .should() + .dependOnClassesThat() + .haveFullyQualifiedName("reactor.core.support.Assert") + .orShould() + .dependOnClassesThat() + .haveFullyQualifiedName("org.slf4j.LoggerFactory") + .orShould() + .dependOnClassesThat() + .haveFullyQualifiedName("org.springframework.lang.NonNull") + .orShould() + .dependOnClassesThat() + .haveFullyQualifiedName("org.springframework.lang.Nullable"); + } - static ArchRule javaClassesShouldNotImportKotlinAnnotations() { - return ArchRuleDefinition.noClasses() - .that( - new DescribedPredicate("is not a Kotlin class") { - @Override - public boolean test(JavaClass javaClass) { - return javaClass.getSourceCodeLocation().getSourceFileName().endsWith(".java"); - } - }) - .should() - .dependOnClassesThat() - .resideInAnyPackage("org.jetbrains.annotations..") - .allowEmptyShould(true); - } + static ArchRule javaClassesShouldNotImportKotlinAnnotations() { + return ArchRuleDefinition.noClasses() + .that(new DescribedPredicate("is not a Kotlin class") { + @Override + public boolean test(JavaClass javaClass) { + return javaClass + .getSourceCodeLocation() + .getSourceFileName() + .endsWith(".java"); + } + }) + .should() + .dependOnClassesThat() + .resideInAnyPackage("org.jetbrains.annotations..") + .allowEmptyShould(true); + } - static class SpringSlices implements SliceAssignment { + static class SpringSlices implements SliceAssignment { - private final List ignoredPackages = - List.of( - "org.springframework.asm", - "org.springframework.cglib", - "org.springframework.javapoet", - "org.springframework.objenesis"); + private final List ignoredPackages = List.of( + "org.springframework.asm", + "org.springframework.cglib", + "org.springframework.javapoet", + "org.springframework.objenesis"); - @Override - public SliceIdentifier getIdentifierOf(JavaClass javaClass) { + @Override + public SliceIdentifier getIdentifierOf(JavaClass javaClass) { - String packageName = javaClass.getPackageName(); - for (String ignoredPackage : ignoredPackages) { - if (packageName.startsWith(ignoredPackage)) { - return SliceIdentifier.ignore(); + String packageName = javaClass.getPackageName(); + for (String ignoredPackage : ignoredPackages) { + if (packageName.startsWith(ignoredPackage)) { + return SliceIdentifier.ignore(); + } + } + return SliceIdentifier.of("spring framework"); } - } - return SliceIdentifier.of("spring framework"); - } - @Override - public String getDescription() { - return "Spring Framework Slices"; + @Override + public String getDescription() { + return "Spring Framework Slices"; + } } - } } diff --git a/buildSrc/src/main/java/org/springframework/build/dev/LocalDevelopmentPlugin.java b/buildSrc/src/main/java/org/springframework/build/dev/LocalDevelopmentPlugin.java index 055a71bd0245..c304d5d64b40 100644 --- a/buildSrc/src/main/java/org/springframework/build/dev/LocalDevelopmentPlugin.java +++ b/buildSrc/src/main/java/org/springframework/build/dev/LocalDevelopmentPlugin.java @@ -27,26 +27,24 @@ */ public class LocalDevelopmentPlugin implements Plugin { - private static final String SKIP_DOCS_PROPERTY = "skipDocs"; + private static final String SKIP_DOCS_PROPERTY = "skipDocs"; - @Override - public void apply(Project target) { - if (target.hasProperty(SKIP_DOCS_PROPERTY)) { - skipDocumentationTasks(target); - target.subprojects(this::skipDocumentationTasks); + @Override + public void apply(Project target) { + if (target.hasProperty(SKIP_DOCS_PROPERTY)) { + skipDocumentationTasks(target); + target.subprojects(this::skipDocumentationTasks); + } } - } - private void skipDocumentationTasks(Project project) { - project.afterEvaluate( - p -> { - p.getTasks() - .matching( - task -> { - return JavaBasePlugin.DOCUMENTATION_GROUP.equals(task.getGroup()) - || "distribution".equals(task.getGroup()); - }) - .forEach(task -> task.setEnabled(false)); + private void skipDocumentationTasks(Project project) { + project.afterEvaluate(p -> { + p.getTasks() + .matching(task -> { + return JavaBasePlugin.DOCUMENTATION_GROUP.equals(task.getGroup()) + || "distribution".equals(task.getGroup()); + }) + .forEach(task -> task.setEnabled(false)); }); - } + } } diff --git a/buildSrc/src/main/java/org/springframework/build/hint/RuntimeHintsAgentArgumentProvider.java b/buildSrc/src/main/java/org/springframework/build/hint/RuntimeHintsAgentArgumentProvider.java index e4ec8da3578a..26c8dd214738 100644 --- a/buildSrc/src/main/java/org/springframework/build/hint/RuntimeHintsAgentArgumentProvider.java +++ b/buildSrc/src/main/java/org/springframework/build/hint/RuntimeHintsAgentArgumentProvider.java @@ -26,24 +26,24 @@ /** Argument provider for registering the runtime hints agent with a Java process. */ public interface RuntimeHintsAgentArgumentProvider extends CommandLineArgumentProvider { - @Classpath - ConfigurableFileCollection getAgentJar(); + @Classpath + ConfigurableFileCollection getAgentJar(); - @Input - SetProperty getIncludedPackages(); + @Input + SetProperty getIncludedPackages(); - @Input - SetProperty getExcludedPackages(); + @Input + SetProperty getExcludedPackages(); - @Override - default Iterable asArguments() { - StringBuilder packages = new StringBuilder(); - getIncludedPackages() - .get() - .forEach(packageName -> packages.append('+').append(packageName).append(',')); - getExcludedPackages() - .get() - .forEach(packageName -> packages.append('-').append(packageName).append(',')); - return Collections.singleton("-javaagent:" + getAgentJar().getSingleFile() + "=" + packages); - } + @Override + default Iterable asArguments() { + StringBuilder packages = new StringBuilder(); + getIncludedPackages() + .get() + .forEach(packageName -> packages.append('+').append(packageName).append(',')); + getExcludedPackages() + .get() + .forEach(packageName -> packages.append('-').append(packageName).append(',')); + return Collections.singleton("-javaagent:" + getAgentJar().getSingleFile() + "=" + packages); + } } diff --git a/buildSrc/src/main/java/org/springframework/build/hint/RuntimeHintsAgentExtension.java b/buildSrc/src/main/java/org/springframework/build/hint/RuntimeHintsAgentExtension.java index 2cdd79bb2f8b..de7392ec6f02 100644 --- a/buildSrc/src/main/java/org/springframework/build/hint/RuntimeHintsAgentExtension.java +++ b/buildSrc/src/main/java/org/springframework/build/hint/RuntimeHintsAgentExtension.java @@ -25,7 +25,7 @@ */ public interface RuntimeHintsAgentExtension { - SetProperty getIncludedPackages(); + SetProperty getIncludedPackages(); - SetProperty getExcludedPackages(); + SetProperty getExcludedPackages(); } diff --git a/buildSrc/src/main/java/org/springframework/build/hint/RuntimeHintsAgentPlugin.java b/buildSrc/src/main/java/org/springframework/build/hint/RuntimeHintsAgentPlugin.java index a9fcf9e53162..ca9e645edf6d 100644 --- a/buildSrc/src/main/java/org/springframework/build/hint/RuntimeHintsAgentPlugin.java +++ b/buildSrc/src/main/java/org/springframework/build/hint/RuntimeHintsAgentPlugin.java @@ -40,94 +40,69 @@ */ public class RuntimeHintsAgentPlugin implements Plugin { - public static final String RUNTIMEHINTS_TEST_TASK = "runtimeHintsTest"; - private static final String EXTENSION_NAME = "runtimeHintsAgent"; - private static final String CONFIGURATION_NAME = "testRuntimeHintsAgentJar"; + public static final String RUNTIMEHINTS_TEST_TASK = "runtimeHintsTest"; + private static final String EXTENSION_NAME = "runtimeHintsAgent"; + private static final String CONFIGURATION_NAME = "testRuntimeHintsAgentJar"; - @Override - public void apply(Project project) { + @Override + public void apply(Project project) { - project - .getPlugins() - .withType( - JavaPlugin.class, - javaPlugin -> { - TestingExtension testing = project.getExtensions().getByType(TestingExtension.class); - JvmTestSuite jvmTestSuite = (JvmTestSuite) testing.getSuites().getByName("test"); - RuntimeHintsAgentExtension agentExtension = createRuntimeHintsAgentExtension(project); - TaskProvider agentTest = - project - .getTasks() - .register( - RUNTIMEHINTS_TEST_TASK, - Test.class, - test -> { - test.useJUnitPlatform( - options -> { - options.includeTags("RuntimeHintsTests"); - }); - test.include("**/*Tests.class", "**/*Test.class"); - test.systemProperty("java.awt.headless", "true"); - test.systemProperty("org.graalvm.nativeimage.imagecode", "runtime"); - test.setTestClassesDirs( - jvmTestSuite.getSources().getOutput().getClassesDirs()); - test.setClasspath(jvmTestSuite.getSources().getRuntimeClasspath()); - test.getJvmArgumentProviders() - .add( - createRuntimeHintsAgentArgumentProvider( - project, agentExtension)); - }); - project.getTasks().named("check", task -> task.dependsOn(agentTest)); - project - .getDependencies() - .add(CONFIGURATION_NAME, project.project(":spring-core-test")); + project.getPlugins().withType(JavaPlugin.class, javaPlugin -> { + TestingExtension testing = project.getExtensions().getByType(TestingExtension.class); + JvmTestSuite jvmTestSuite = (JvmTestSuite) testing.getSuites().getByName("test"); + RuntimeHintsAgentExtension agentExtension = createRuntimeHintsAgentExtension(project); + TaskProvider agentTest = project.getTasks().register(RUNTIMEHINTS_TEST_TASK, Test.class, test -> { + test.useJUnitPlatform(options -> { + options.includeTags("RuntimeHintsTests"); + }); + test.include("**/*Tests.class", "**/*Test.class"); + test.systemProperty("java.awt.headless", "true"); + test.systemProperty("org.graalvm.nativeimage.imagecode", "runtime"); + test.setTestClassesDirs(jvmTestSuite.getSources().getOutput().getClassesDirs()); + test.setClasspath(jvmTestSuite.getSources().getRuntimeClasspath()); + test.getJvmArgumentProviders().add(createRuntimeHintsAgentArgumentProvider(project, agentExtension)); }); - } + project.getTasks().named("check", task -> task.dependsOn(agentTest)); + project.getDependencies().add(CONFIGURATION_NAME, project.project(":spring-core-test")); + }); + } - private static RuntimeHintsAgentExtension createRuntimeHintsAgentExtension(Project project) { - RuntimeHintsAgentExtension agentExtension = - project.getExtensions().create(EXTENSION_NAME, RuntimeHintsAgentExtension.class); - agentExtension.getIncludedPackages().convention(Collections.singleton("org.springframework")); - agentExtension.getExcludedPackages().convention(Collections.emptySet()); - return agentExtension; - } + private static RuntimeHintsAgentExtension createRuntimeHintsAgentExtension(Project project) { + RuntimeHintsAgentExtension agentExtension = + project.getExtensions().create(EXTENSION_NAME, RuntimeHintsAgentExtension.class); + agentExtension.getIncludedPackages().convention(Collections.singleton("org.springframework")); + agentExtension.getExcludedPackages().convention(Collections.emptySet()); + return agentExtension; + } - private static RuntimeHintsAgentArgumentProvider createRuntimeHintsAgentArgumentProvider( - Project project, RuntimeHintsAgentExtension agentExtension) { - RuntimeHintsAgentArgumentProvider agentArgumentProvider = - project.getObjects().newInstance(RuntimeHintsAgentArgumentProvider.class); - agentArgumentProvider.getAgentJar().from(createRuntimeHintsAgentConfiguration(project)); - agentArgumentProvider.getIncludedPackages().set(agentExtension.getIncludedPackages()); - agentArgumentProvider.getExcludedPackages().set(agentExtension.getExcludedPackages()); - return agentArgumentProvider; - } + private static RuntimeHintsAgentArgumentProvider createRuntimeHintsAgentArgumentProvider( + Project project, RuntimeHintsAgentExtension agentExtension) { + RuntimeHintsAgentArgumentProvider agentArgumentProvider = + project.getObjects().newInstance(RuntimeHintsAgentArgumentProvider.class); + agentArgumentProvider.getAgentJar().from(createRuntimeHintsAgentConfiguration(project)); + agentArgumentProvider.getIncludedPackages().set(agentExtension.getIncludedPackages()); + agentArgumentProvider.getExcludedPackages().set(agentExtension.getExcludedPackages()); + return agentArgumentProvider; + } - private static Configuration createRuntimeHintsAgentConfiguration(Project project) { - return project - .getConfigurations() - .create( - CONFIGURATION_NAME, - configuration -> { - configuration.setCanBeConsumed(false); - configuration.setTransitive(false); // Only the built artifact is required - configuration.attributes( - attributes -> { - attributes.attribute( - Bundling.BUNDLING_ATTRIBUTE, - project.getObjects().named(Bundling.class, Bundling.EXTERNAL)); - attributes.attribute( - Category.CATEGORY_ATTRIBUTE, - project.getObjects().named(Category.class, Category.LIBRARY)); - attributes.attribute( + private static Configuration createRuntimeHintsAgentConfiguration(Project project) { + return project.getConfigurations().create(CONFIGURATION_NAME, configuration -> { + configuration.setCanBeConsumed(false); + configuration.setTransitive(false); // Only the built artifact is required + configuration.attributes(attributes -> { + attributes.attribute( + Bundling.BUNDLING_ATTRIBUTE, project.getObjects().named(Bundling.class, Bundling.EXTERNAL)); + attributes.attribute( + Category.CATEGORY_ATTRIBUTE, project.getObjects().named(Category.class, Category.LIBRARY)); + attributes.attribute( LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, project.getObjects().named(LibraryElements.class, LibraryElements.JAR)); - attributes.attribute( + attributes.attribute( TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, Integer.valueOf(JavaVersion.current().getMajorVersion())); - attributes.attribute( - Usage.USAGE_ATTRIBUTE, - project.getObjects().named(Usage.class, Usage.JAVA_RUNTIME)); - }); + attributes.attribute( + Usage.USAGE_ATTRIBUTE, project.getObjects().named(Usage.class, Usage.JAVA_RUNTIME)); }); - } + }); + } } diff --git a/buildSrc/src/main/java/org/springframework/build/multirelease/MultiReleaseExtension.java b/buildSrc/src/main/java/org/springframework/build/multirelease/MultiReleaseExtension.java index ea7aa7e81e5e..1f22239cc48c 100644 --- a/buildSrc/src/main/java/org/springframework/build/multirelease/MultiReleaseExtension.java +++ b/buildSrc/src/main/java/org/springframework/build/multirelease/MultiReleaseExtension.java @@ -39,137 +39,121 @@ * @author Brian Clozel */ public abstract class MultiReleaseExtension { - private final TaskContainer tasks; - private final SourceSetContainer sourceSets; - private final DependencyHandler dependencies; - private final ObjectFactory objects; - private final ConfigurationContainer configurations; - - @Inject - public MultiReleaseExtension( - SourceSetContainer sourceSets, - ConfigurationContainer configurations, - TaskContainer tasks, - DependencyHandler dependencies, - ObjectFactory objectFactory) { - this.sourceSets = sourceSets; - this.configurations = configurations; - this.tasks = tasks; - this.dependencies = dependencies; - this.objects = objectFactory; - } - - public void releaseVersions(int... javaVersions) { - releaseVersions("src/main/", "src/test/", javaVersions); - } - - private void releaseVersions( - String mainSourceDirectory, String testSourceDirectory, int... javaVersions) { - for (int javaVersion : javaVersions) { - addLanguageVersion(javaVersion, mainSourceDirectory, testSourceDirectory); + private final TaskContainer tasks; + private final SourceSetContainer sourceSets; + private final DependencyHandler dependencies; + private final ObjectFactory objects; + private final ConfigurationContainer configurations; + + @Inject + public MultiReleaseExtension( + SourceSetContainer sourceSets, + ConfigurationContainer configurations, + TaskContainer tasks, + DependencyHandler dependencies, + ObjectFactory objectFactory) { + this.sourceSets = sourceSets; + this.configurations = configurations; + this.tasks = tasks; + this.dependencies = dependencies; + this.objects = objectFactory; } - } - - private void addLanguageVersion( - int javaVersion, String mainSourceDirectory, String testSourceDirectory) { - String javaN = "java" + javaVersion; - - SourceSet langSourceSet = - sourceSets.create(javaN, srcSet -> srcSet.getJava().srcDir(mainSourceDirectory + javaN)); - SourceSet testSourceSet = - sourceSets.create( - javaN + "Test", srcSet -> srcSet.getJava().srcDir(testSourceDirectory + javaN)); - SourceSet sharedSourceSet = sourceSets.findByName(SourceSet.MAIN_SOURCE_SET_NAME); - SourceSet sharedTestSourceSet = sourceSets.findByName(SourceSet.TEST_SOURCE_SET_NAME); - - FileCollection mainClasses = - objects - .fileCollection() - .from( - sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME).getOutput().getClassesDirs()); - dependencies.add(javaN + "Implementation", mainClasses); - - tasks.named( - langSourceSet.getCompileJavaTaskName(), - JavaCompile.class, - task -> task.getOptions().getRelease().set(javaVersion)); - tasks.named( - testSourceSet.getCompileJavaTaskName(), - JavaCompile.class, - task -> task.getOptions().getRelease().set(javaVersion)); - - TaskProvider testTask = - createTestTask( - javaVersion, testSourceSet, sharedTestSourceSet, langSourceSet, sharedSourceSet); - tasks.named("check", task -> task.dependsOn(testTask)); - - configureMultiReleaseJar(javaVersion, langSourceSet); - } - - private TaskProvider createTestTask( - int javaVersion, - SourceSet testSourceSet, - SourceSet sharedTestSourceSet, - SourceSet langSourceSet, - SourceSet sharedSourceSet) { - Configuration testImplementation = - configurations.getByName(testSourceSet.getImplementationConfigurationName()); - testImplementation.extendsFrom( - configurations.getByName(sharedTestSourceSet.getImplementationConfigurationName())); - Configuration testCompileOnly = - configurations.getByName(testSourceSet.getCompileOnlyConfigurationName()); - testCompileOnly.extendsFrom( - configurations.getByName(sharedTestSourceSet.getCompileOnlyConfigurationName())); - testCompileOnly - .getDependencies() - .add(dependencies.create(langSourceSet.getOutput().getClassesDirs())); - testCompileOnly - .getDependencies() - .add(dependencies.create(sharedSourceSet.getOutput().getClassesDirs())); - - Configuration testRuntimeClasspath = - configurations.getByName(testSourceSet.getRuntimeClasspathConfigurationName()); - // so here's the deal. MRjars are JARs! Which means that to execute tests, we need - // the JAR on classpath, not just classes + resources as Gradle usually does - testRuntimeClasspath - .getAttributes() - .attribute( - LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, - objects.named(LibraryElements.class, LibraryElements.JAR)); - - TaskProvider testTask = - tasks.register( - "java" + javaVersion + "Test", - Test.class, - test -> { - test.setGroup(LifecycleBasePlugin.VERIFICATION_GROUP); - - ConfigurableFileCollection testClassesDirs = objects.fileCollection(); - testClassesDirs.from(testSourceSet.getOutput()); - testClassesDirs.from(sharedTestSourceSet.getOutput()); - test.setTestClassesDirs(testClassesDirs); - ConfigurableFileCollection classpath = objects.fileCollection(); - // must put the MRJar first on classpath - classpath.from(tasks.named("jar")); - // then we put the specific test sourceset tests, so that we can override - // the shared versions - classpath.from(testSourceSet.getOutput()); - - // then we add the shared tests - classpath.from(sharedTestSourceSet.getRuntimeClasspath()); - test.setClasspath(classpath); - }); - return testTask; - } - - private void configureMultiReleaseJar(int version, SourceSet languageSourceSet) { - tasks.named( - "jar", - Jar.class, - jar -> { - jar.into("META-INF/versions/" + version, s -> s.from(languageSourceSet.getOutput())); - Attributes attributes = jar.getManifest().getAttributes(); - attributes.put("Multi-Release", "true"); + + public void releaseVersions(int... javaVersions) { + releaseVersions("src/main/", "src/test/", javaVersions); + } + + private void releaseVersions(String mainSourceDirectory, String testSourceDirectory, int... javaVersions) { + for (int javaVersion : javaVersions) { + addLanguageVersion(javaVersion, mainSourceDirectory, testSourceDirectory); + } + } + + private void addLanguageVersion(int javaVersion, String mainSourceDirectory, String testSourceDirectory) { + String javaN = "java" + javaVersion; + + SourceSet langSourceSet = + sourceSets.create(javaN, srcSet -> srcSet.getJava().srcDir(mainSourceDirectory + javaN)); + SourceSet testSourceSet = + sourceSets.create(javaN + "Test", srcSet -> srcSet.getJava().srcDir(testSourceDirectory + javaN)); + SourceSet sharedSourceSet = sourceSets.findByName(SourceSet.MAIN_SOURCE_SET_NAME); + SourceSet sharedTestSourceSet = sourceSets.findByName(SourceSet.TEST_SOURCE_SET_NAME); + + FileCollection mainClasses = objects.fileCollection() + .from(sourceSets + .getByName(SourceSet.MAIN_SOURCE_SET_NAME) + .getOutput() + .getClassesDirs()); + dependencies.add(javaN + "Implementation", mainClasses); + + tasks.named(langSourceSet.getCompileJavaTaskName(), JavaCompile.class, task -> task.getOptions() + .getRelease() + .set(javaVersion)); + tasks.named(testSourceSet.getCompileJavaTaskName(), JavaCompile.class, task -> task.getOptions() + .getRelease() + .set(javaVersion)); + + TaskProvider testTask = + createTestTask(javaVersion, testSourceSet, sharedTestSourceSet, langSourceSet, sharedSourceSet); + tasks.named("check", task -> task.dependsOn(testTask)); + + configureMultiReleaseJar(javaVersion, langSourceSet); + } + + private TaskProvider createTestTask( + int javaVersion, + SourceSet testSourceSet, + SourceSet sharedTestSourceSet, + SourceSet langSourceSet, + SourceSet sharedSourceSet) { + Configuration testImplementation = configurations.getByName(testSourceSet.getImplementationConfigurationName()); + testImplementation.extendsFrom( + configurations.getByName(sharedTestSourceSet.getImplementationConfigurationName())); + Configuration testCompileOnly = configurations.getByName(testSourceSet.getCompileOnlyConfigurationName()); + testCompileOnly.extendsFrom(configurations.getByName(sharedTestSourceSet.getCompileOnlyConfigurationName())); + testCompileOnly + .getDependencies() + .add(dependencies.create(langSourceSet.getOutput().getClassesDirs())); + testCompileOnly + .getDependencies() + .add(dependencies.create(sharedSourceSet.getOutput().getClassesDirs())); + + Configuration testRuntimeClasspath = + configurations.getByName(testSourceSet.getRuntimeClasspathConfigurationName()); + // so here's the deal. MRjars are JARs! Which means that to execute tests, we need + // the JAR on classpath, not just classes + resources as Gradle usually does + testRuntimeClasspath + .getAttributes() + .attribute( + LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, + objects.named(LibraryElements.class, LibraryElements.JAR)); + + TaskProvider testTask = tasks.register("java" + javaVersion + "Test", Test.class, test -> { + test.setGroup(LifecycleBasePlugin.VERIFICATION_GROUP); + + ConfigurableFileCollection testClassesDirs = objects.fileCollection(); + testClassesDirs.from(testSourceSet.getOutput()); + testClassesDirs.from(sharedTestSourceSet.getOutput()); + test.setTestClassesDirs(testClassesDirs); + ConfigurableFileCollection classpath = objects.fileCollection(); + // must put the MRJar first on classpath + classpath.from(tasks.named("jar")); + // then we put the specific test sourceset tests, so that we can override + // the shared versions + classpath.from(testSourceSet.getOutput()); + + // then we add the shared tests + classpath.from(sharedTestSourceSet.getRuntimeClasspath()); + test.setClasspath(classpath); }); - } + return testTask; + } + + private void configureMultiReleaseJar(int version, SourceSet languageSourceSet) { + tasks.named("jar", Jar.class, jar -> { + jar.into("META-INF/versions/" + version, s -> s.from(languageSourceSet.getOutput())); + Attributes attributes = jar.getManifest().getAttributes(); + attributes.put("Multi-Release", "true"); + }); + } } diff --git a/buildSrc/src/main/java/org/springframework/build/multirelease/MultiReleaseJarPlugin.java b/buildSrc/src/main/java/org/springframework/build/multirelease/MultiReleaseJarPlugin.java index 475ceeb40374..859814fddfa8 100644 --- a/buildSrc/src/main/java/org/springframework/build/multirelease/MultiReleaseJarPlugin.java +++ b/buildSrc/src/main/java/org/springframework/build/multirelease/MultiReleaseJarPlugin.java @@ -37,26 +37,26 @@ */ public class MultiReleaseJarPlugin implements Plugin { - @Inject - protected JavaToolchainService getToolchains() { - throw new UnsupportedOperationException(); - } + @Inject + protected JavaToolchainService getToolchains() { + throw new UnsupportedOperationException(); + } - public void apply(Project project) { - project.getPlugins().apply(JavaPlugin.class); - ExtensionContainer extensions = project.getExtensions(); - JavaPluginExtension javaPluginExtension = extensions.getByType(JavaPluginExtension.class); - ConfigurationContainer configurations = project.getConfigurations(); - TaskContainer tasks = project.getTasks(); - DependencyHandler dependencies = project.getDependencies(); - ObjectFactory objects = project.getObjects(); - extensions.create( - "multiRelease", - MultiReleaseExtension.class, - javaPluginExtension.getSourceSets(), - configurations, - tasks, - dependencies, - objects); - } + public void apply(Project project) { + project.getPlugins().apply(JavaPlugin.class); + ExtensionContainer extensions = project.getExtensions(); + JavaPluginExtension javaPluginExtension = extensions.getByType(JavaPluginExtension.class); + ConfigurationContainer configurations = project.getConfigurations(); + TaskContainer tasks = project.getTasks(); + DependencyHandler dependencies = project.getDependencies(); + ObjectFactory objects = project.getObjects(); + extensions.create( + "multiRelease", + MultiReleaseExtension.class, + javaPluginExtension.getSourceSets(), + configurations, + tasks, + dependencies, + objects); + } } diff --git a/buildSrc/src/main/java/org/springframework/build/optional/OptionalDependenciesPlugin.java b/buildSrc/src/main/java/org/springframework/build/optional/OptionalDependenciesPlugin.java index 17d647192503..ec013a65b3bf 100644 --- a/buildSrc/src/main/java/org/springframework/build/optional/OptionalDependenciesPlugin.java +++ b/buildSrc/src/main/java/org/springframework/build/optional/OptionalDependenciesPlugin.java @@ -32,32 +32,25 @@ */ public class OptionalDependenciesPlugin implements Plugin { - /** Name of the {@code optional} configuration. */ - public static final String OPTIONAL_CONFIGURATION_NAME = "optional"; + /** Name of the {@code optional} configuration. */ + public static final String OPTIONAL_CONFIGURATION_NAME = "optional"; - @Override - public void apply(Project project) { - Configuration optional = project.getConfigurations().create(OPTIONAL_CONFIGURATION_NAME); - optional.setCanBeConsumed(false); - optional.setCanBeResolved(false); - project - .getPlugins() - .withType( - JavaBasePlugin.class, - (javaBasePlugin) -> { - SourceSetContainer sourceSets = - project.getExtensions().getByType(JavaPluginExtension.class).getSourceSets(); - sourceSets.all( - (sourceSet) -> { - project - .getConfigurations() + @Override + public void apply(Project project) { + Configuration optional = project.getConfigurations().create(OPTIONAL_CONFIGURATION_NAME); + optional.setCanBeConsumed(false); + optional.setCanBeResolved(false); + project.getPlugins().withType(JavaBasePlugin.class, (javaBasePlugin) -> { + SourceSetContainer sourceSets = + project.getExtensions().getByType(JavaPluginExtension.class).getSourceSets(); + sourceSets.all((sourceSet) -> { + project.getConfigurations() .getByName(sourceSet.getCompileClasspathConfigurationName()) .extendsFrom(optional); - project - .getConfigurations() + project.getConfigurations() .getByName(sourceSet.getRuntimeClasspathConfigurationName()) .extendsFrom(optional); - }); }); - } + }); + } } diff --git a/buildSrc/src/main/java/org/springframework/build/shadow/ShadowSource.java b/buildSrc/src/main/java/org/springframework/build/shadow/ShadowSource.java index 7bdb539855d1..e8c1191ccc8f 100644 --- a/buildSrc/src/main/java/org/springframework/build/shadow/ShadowSource.java +++ b/buildSrc/src/main/java/org/springframework/build/shadow/ShadowSource.java @@ -50,149 +50,135 @@ */ public class ShadowSource extends DefaultTask { - private final DirectoryProperty outputDirectory = getProject().getObjects().directoryProperty(); - - private List configurations = new ArrayList<>(); - - private final List relocations = new ArrayList<>(); - - @Classpath - @Optional - public List getConfigurations() { - return this.configurations; - } - - public void setConfigurations(List configurations) { - this.configurations = configurations; - } - - @Nested - public List getRelocations() { - return this.relocations; - } - - public void relocate(String pattern, String destination) { - this.relocations.add(new Relocation(pattern, destination)); - } - - @OutputDirectory - DirectoryProperty getOutputDirectory() { - return this.outputDirectory; - } - - @TaskAction - void syncSourceJarFiles() { - sync(getSourceJarFiles()); - } - - private List getSourceJarFiles() { - List sourceJarFiles = new ArrayList<>(); - for (Configuration configuration : this.configurations) { - ResolutionResult resolutionResult = configuration.getIncoming().getResolutionResult(); - resolutionResult - .getRootComponent() - .get() - .getDependencies() - .forEach( - dependency -> { - Set artifactsResults = resolveSourceArtifacts(dependency); - for (ComponentArtifactsResult artifactResult : artifactsResults) { - artifactResult - .getArtifacts(SourcesArtifact.class) - .forEach( - sourceArtifact -> { - sourceJarFiles.add(((ResolvedArtifactResult) sourceArtifact).getFile()); - }); - } - }); + private final DirectoryProperty outputDirectory = getProject().getObjects().directoryProperty(); + + private List configurations = new ArrayList<>(); + + private final List relocations = new ArrayList<>(); + + @Classpath + @Optional + public List getConfigurations() { + return this.configurations; } - return Collections.unmodifiableList(sourceJarFiles); - } - - private Set resolveSourceArtifacts(DependencyResult dependency) { - ModuleComponentSelector componentSelector = (ModuleComponentSelector) dependency.getRequested(); - ArtifactResolutionQuery query = - getProject() - .getDependencies() - .createArtifactResolutionQuery() - .forModule( - componentSelector.getGroup(), - componentSelector.getModule(), - componentSelector.getVersion()); - return executeQuery(query).getResolvedComponents(); - } - - @SuppressWarnings("unchecked") - private ArtifactResolutionResult executeQuery(ArtifactResolutionQuery query) { - return query.withArtifacts(JvmLibrary.class, SourcesArtifact.class).execute(); - } - - private void sync(List sourceJarFiles) { - getProject() - .sync( - spec -> { - spec.into(this.outputDirectory); - spec.eachFile(this::relocateFile); - spec.filter(this::transformContent); - spec.exclude("META-INF/**"); - spec.setIncludeEmptyDirs(false); - sourceJarFiles.forEach(sourceJar -> spec.from(zipTree(sourceJar))); - }); - } - private void relocateFile(FileCopyDetails details) { - String path = details.getPath(); - for (Relocation relocation : this.relocations) { - path = relocation.relocatePath(path); + public void setConfigurations(List configurations) { + this.configurations = configurations; } - details.setPath(path); - } - private String transformContent(String content) { - for (Relocation relocation : this.relocations) { - content = relocation.transformContent(content); + @Nested + public List getRelocations() { + return this.relocations; } - return content; - } - private FileTree zipTree(File sourceJar) { - return getProject().zipTree(sourceJar); - } + public void relocate(String pattern, String destination) { + this.relocations.add(new Relocation(pattern, destination)); + } - /** A single relocation. */ - static class Relocation { + @OutputDirectory + DirectoryProperty getOutputDirectory() { + return this.outputDirectory; + } - private final String pattern; + @TaskAction + void syncSourceJarFiles() { + sync(getSourceJarFiles()); + } - private final String pathPattern; + private List getSourceJarFiles() { + List sourceJarFiles = new ArrayList<>(); + for (Configuration configuration : this.configurations) { + ResolutionResult resolutionResult = configuration.getIncoming().getResolutionResult(); + resolutionResult.getRootComponent().get().getDependencies().forEach(dependency -> { + Set artifactsResults = resolveSourceArtifacts(dependency); + for (ComponentArtifactsResult artifactResult : artifactsResults) { + artifactResult.getArtifacts(SourcesArtifact.class).forEach(sourceArtifact -> { + sourceJarFiles.add(((ResolvedArtifactResult) sourceArtifact).getFile()); + }); + } + }); + } + return Collections.unmodifiableList(sourceJarFiles); + } - private final String destination; + private Set resolveSourceArtifacts(DependencyResult dependency) { + ModuleComponentSelector componentSelector = (ModuleComponentSelector) dependency.getRequested(); + ArtifactResolutionQuery query = getProject() + .getDependencies() + .createArtifactResolutionQuery() + .forModule(componentSelector.getGroup(), componentSelector.getModule(), componentSelector.getVersion()); + return executeQuery(query).getResolvedComponents(); + } - private final String pathDestination; + @SuppressWarnings("unchecked") + private ArtifactResolutionResult executeQuery(ArtifactResolutionQuery query) { + return query.withArtifacts(JvmLibrary.class, SourcesArtifact.class).execute(); + } - Relocation(String pattern, String destination) { - this.pattern = pattern; - this.pathPattern = pattern.replace('.', '/'); - this.destination = destination; - this.pathDestination = destination.replace('.', '/'); + private void sync(List sourceJarFiles) { + getProject().sync(spec -> { + spec.into(this.outputDirectory); + spec.eachFile(this::relocateFile); + spec.filter(this::transformContent); + spec.exclude("META-INF/**"); + spec.setIncludeEmptyDirs(false); + sourceJarFiles.forEach(sourceJar -> spec.from(zipTree(sourceJar))); + }); } - @Input - public String getPattern() { - return this.pattern; + private void relocateFile(FileCopyDetails details) { + String path = details.getPath(); + for (Relocation relocation : this.relocations) { + path = relocation.relocatePath(path); + } + details.setPath(path); } - @Input - public String getDestination() { - return this.destination; + private String transformContent(String content) { + for (Relocation relocation : this.relocations) { + content = relocation.transformContent(content); + } + return content; } - String relocatePath(String path) { - return path.replace(this.pathPattern, this.pathDestination); + private FileTree zipTree(File sourceJar) { + return getProject().zipTree(sourceJar); } - public String transformContent(String content) { - return content.replaceAll("\\b" + this.pattern, this.destination); + /** A single relocation. */ + static class Relocation { + + private final String pattern; + + private final String pathPattern; + + private final String destination; + + private final String pathDestination; + + Relocation(String pattern, String destination) { + this.pattern = pattern; + this.pathPattern = pattern.replace('.', '/'); + this.destination = destination; + this.pathDestination = destination.replace('.', '/'); + } + + @Input + public String getPattern() { + return this.pattern; + } + + @Input + public String getDestination() { + return this.destination; + } + + String relocatePath(String path) { + return path.replace(this.pathPattern, this.pathDestination); + } + + public String transformContent(String content) { + return content.replaceAll("\\b" + this.pattern, this.destination); + } } - } } diff --git a/buildSrc/src/test/java/org/springframework/build/multirelease/MultiReleaseJarPluginTests.java b/buildSrc/src/test/java/org/springframework/build/multirelease/MultiReleaseJarPluginTests.java index 7c37942e3a47..a2d5693d2849 100644 --- a/buildSrc/src/test/java/org/springframework/build/multirelease/MultiReleaseJarPluginTests.java +++ b/buildSrc/src/test/java/org/springframework/build/multirelease/MultiReleaseJarPluginTests.java @@ -35,20 +35,20 @@ /** Tests for {@link MultiReleaseJarPlugin} */ public class MultiReleaseJarPluginTests { - private File projectDir; + private File projectDir; - private File buildFile; + private File buildFile; - @BeforeEach - void setup(@TempDir File projectDir) { - this.projectDir = projectDir; - this.buildFile = new File(this.projectDir, "build.gradle"); - } + @BeforeEach + void setup(@TempDir File projectDir) { + this.projectDir = projectDir; + this.buildFile = new File(this.projectDir, "build.gradle"); + } - @Test - void configureSourceSets() throws IOException { - writeBuildFile( - """ + @Test + void configureSourceSets() throws IOException { + writeBuildFile( + """ plugins { id 'java' id 'org.springframework.build.multiReleaseJar' @@ -60,15 +60,14 @@ void configureSourceSets() throws IOException { } } """); - BuildResult buildResult = runGradle("printSourceSets"); - assertThat(buildResult.getOutput()) - .contains("main", "test", "java21", "java21Test", "java24", "java24Test"); - } + BuildResult buildResult = runGradle("printSourceSets"); + assertThat(buildResult.getOutput()).contains("main", "test", "java21", "java21Test", "java24", "java24Test"); + } - @Test - void configureToolchainReleaseVersion() throws IOException { - writeBuildFile( - """ + @Test + void configureToolchainReleaseVersion() throws IOException { + writeBuildFile( + """ plugins { id 'java' id 'org.springframework.build.multiReleaseJar' @@ -87,16 +86,16 @@ void configureToolchainReleaseVersion() throws IOException { } """); - BuildResult buildResult = runGradle("printReleaseVersion"); - assertThat(buildResult.getOutput()) - .contains("compileJava21Java releaseVersion: 21") - .contains("compileJava21TestJava releaseVersion: 21"); - } + BuildResult buildResult = runGradle("printReleaseVersion"); + assertThat(buildResult.getOutput()) + .contains("compileJava21Java releaseVersion: 21") + .contains("compileJava21TestJava releaseVersion: 21"); + } - @Test - void packageInJar() throws IOException { - writeBuildFile( - """ + @Test + void packageInJar() throws IOException { + writeBuildFile( + """ plugins { id 'java' id 'org.springframework.build.multiReleaseJar' @@ -104,45 +103,40 @@ void packageInJar() throws IOException { version = '1.2.3' multiRelease { releaseVersions 17 } """); - writeClass( - "src/main/java17", - "Main.java", - """ + writeClass("src/main/java17", "Main.java", """ public class Main {} """); - BuildResult buildResult = runGradle("assemble"); - File file = - new File(this.projectDir, "/build/libs/" + this.projectDir.getName() + "-1.2.3.jar"); - assertThat(file).exists(); - try (JarFile jar = new JarFile(file)) { - Attributes mainAttributes = jar.getManifest().getMainAttributes(); - assertThat(mainAttributes.getValue("Multi-Release")).isEqualTo("true"); + BuildResult buildResult = runGradle("assemble"); + File file = new File(this.projectDir, "/build/libs/" + this.projectDir.getName() + "-1.2.3.jar"); + assertThat(file).exists(); + try (JarFile jar = new JarFile(file)) { + Attributes mainAttributes = jar.getManifest().getMainAttributes(); + assertThat(mainAttributes.getValue("Multi-Release")).isEqualTo("true"); - assertThat(jar.entries().asIterator()) - .toIterable() - .anyMatch(entry -> entry.getName().equals("META-INF/versions/17/Main.class")); + assertThat(jar.entries().asIterator()).toIterable().anyMatch(entry -> entry.getName() + .equals("META-INF/versions/17/Main.class")); + } } - } - private void writeBuildFile(String buildContent) throws IOException { - try (PrintWriter out = new PrintWriter(new FileWriter(this.buildFile))) { - out.print(buildContent); + private void writeBuildFile(String buildContent) throws IOException { + try (PrintWriter out = new PrintWriter(new FileWriter(this.buildFile))) { + out.print(buildContent); + } } - } - private void writeClass(String path, String fileName, String fileContent) throws IOException { - Path folder = this.projectDir.toPath().resolve(path); - Files.createDirectories(folder); - Path filePath = folder.resolve(fileName); - Files.createFile(filePath); - Files.writeString(filePath, fileContent); - } + private void writeClass(String path, String fileName, String fileContent) throws IOException { + Path folder = this.projectDir.toPath().resolve(path); + Files.createDirectories(folder); + Path filePath = folder.resolve(fileName); + Files.createFile(filePath); + Files.writeString(filePath, fileContent); + } - private BuildResult runGradle(String... args) { - return GradleRunner.create() - .withProjectDir(this.projectDir) - .withArguments(args) - .withPluginClasspath() - .build(); - } + private BuildResult runGradle(String... args) { + return GradleRunner.create() + .withProjectDir(this.projectDir) + .withArguments(args) + .withPluginClasspath() + .build(); + } } From fa1f8f5451b8d034871f14031262671384a22bac Mon Sep 17 00:00:00 2001 From: Vincent Potucek Date: Sun, 1 Jun 2025 09:25:59 +0200 Subject: [PATCH 08/15] spotless --- buildSrc/build.gradle | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index edc708e5d3a9..aaa5ca4d5d07 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -2,10 +2,12 @@ plugins { id 'java-gradle-plugin' id 'com.diffplug.spotless' version '7.0.4' } -spotless { - java { - removeUnusedImports() - palantirJavaFormat() +allprojects { + spotless { + java { + removeUnusedImports() + palantirJavaFormat() + } } } repositories { From d20a2055350cb83496c0830809f082014d66cefa Mon Sep 17 00:00:00 2001 From: Vincent Potucek Date: Sun, 1 Jun 2025 09:28:33 +0200 Subject: [PATCH 09/15] spotless --- .../build/CheckstyleConventions.java | 79 +++++++++++++++++++ .../build/ConventionsPlugin.java | 32 ++++---- 2 files changed, 97 insertions(+), 14 deletions(-) create mode 100644 buildSrc/src/main/java/org/springframework/build/CheckstyleConventions.java diff --git a/buildSrc/src/main/java/org/springframework/build/CheckstyleConventions.java b/buildSrc/src/main/java/org/springframework/build/CheckstyleConventions.java new file mode 100644 index 000000000000..9cdcca1af129 --- /dev/null +++ b/buildSrc/src/main/java/org/springframework/build/CheckstyleConventions.java @@ -0,0 +1,79 @@ +/* + * Copyright 2002-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.build; + +import java.io.File; +import java.nio.file.Path; +import java.util.List; + +import io.spring.javaformat.gradle.SpringJavaFormatPlugin; +import io.spring.nohttp.gradle.NoHttpExtension; +import io.spring.nohttp.gradle.NoHttpPlugin; +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.artifacts.DependencySet; +import org.gradle.api.plugins.JavaBasePlugin; +import org.gradle.api.plugins.quality.Checkstyle; +import org.gradle.api.plugins.quality.CheckstyleExtension; +import org.gradle.api.plugins.quality.CheckstylePlugin; + +/** + * {@link Plugin} that applies conventions for checkstyle. + * + * @author Brian Clozel + */ +public class CheckstyleConventions { + + /** + * Applies the Spring Java Format and Checkstyle plugins with the project conventions. + * @param project the current project + */ + public void apply(Project project) { + project.getPlugins().withType(JavaBasePlugin.class, (java) -> { + if (project.getRootProject() == project) { + configureNoHttpPlugin(project); + } + project.getPlugins().apply(CheckstylePlugin.class); + project.getTasks().withType(Checkstyle.class).forEach(checkstyle -> checkstyle.getMaxHeapSize().set("1g")); + CheckstyleExtension checkstyle = project.getExtensions().getByType(CheckstyleExtension.class); + checkstyle.setToolVersion("10.23.1"); + checkstyle.getConfigDirectory().set(project.getRootProject().file("src/checkstyle")); + String version = SpringJavaFormatPlugin.class.getPackage().getImplementationVersion(); + DependencySet checkstyleDependencies = project.getConfigurations().getByName("checkstyle").getDependencies(); + checkstyleDependencies.add( + project.getDependencies().create("io.spring.javaformat:spring-javaformat-checkstyle:" + version)); + }); + } + + private static void configureNoHttpPlugin(Project project) { + project.getPlugins().apply(NoHttpPlugin.class); + NoHttpExtension noHttp = project.getExtensions().getByType(NoHttpExtension.class); + noHttp.setAllowlistFile(project.file("src/nohttp/allowlist.lines")); + noHttp.getSource().exclude("**/test-output/**", "**/.settings/**", "**/.classpath", + "**/.project", "**/.gradle/**", "**/node_modules/**", "**/spring-jcl/**", "buildSrc/build/**"); + List buildFolders = List.of("bin", "build", "out"); + project.allprojects(subproject -> { + Path rootPath = project.getRootDir().toPath(); + Path projectPath = rootPath.relativize(subproject.getProjectDir().toPath()); + for (String buildFolder : buildFolders) { + Path innerBuildDir = projectPath.resolve(buildFolder); + noHttp.getSource().exclude(innerBuildDir + File.separator + "**"); + } + }); + } + +} diff --git a/buildSrc/src/main/java/org/springframework/build/ConventionsPlugin.java b/buildSrc/src/main/java/org/springframework/build/ConventionsPlugin.java index 19c1e6a819e8..1cd9e43cf0c4 100644 --- a/buildSrc/src/main/java/org/springframework/build/ConventionsPlugin.java +++ b/buildSrc/src/main/java/org/springframework/build/ConventionsPlugin.java @@ -20,27 +20,31 @@ import org.gradle.api.Project; import org.gradle.api.plugins.JavaBasePlugin; import org.jetbrains.kotlin.gradle.plugin.KotlinBasePlugin; + import org.springframework.build.architecture.ArchitecturePlugin; /** - * Plugin to apply conventions to projects that are part of Spring Framework's build. Conventions - * are applied in response to various plugins being applied. + * Plugin to apply conventions to projects that are part of Spring Framework's build. + * Conventions are applied in response to various plugins being applied. * - *

When the {@link JavaBasePlugin} is applied, the conventions in {@link TestConventions} and - * {@link JavaConventions} are applied. The {@link ArchitecturePlugin} plugin is also applied. When - * the {@link KotlinBasePlugin} is applied, the conventions in {@link KotlinConventions} are - * applied. + *

When the {@link JavaBasePlugin} is applied, the conventions in {@link CheckstyleConventions}, + * {@link TestConventions} and {@link JavaConventions} are applied. + * The {@link ArchitecturePlugin} plugin is also applied. + * When the {@link KotlinBasePlugin} is applied, the conventions in {@link KotlinConventions} + * are applied. * * @author Brian Clozel */ public class ConventionsPlugin implements Plugin { - @Override - public void apply(Project project) { - project.getExtensions().create("springFramework", SpringFrameworkExtension.class); - new ArchitecturePlugin().apply(project); - new JavaConventions().apply(project); - new KotlinConventions().apply(project); - new TestConventions().apply(project); - } + @Override + public void apply(Project project) { + project.getExtensions().create("springFramework", SpringFrameworkExtension.class); + new ArchitecturePlugin().apply(project); + new CheckstyleConventions().apply(project); + new JavaConventions().apply(project); + new KotlinConventions().apply(project); + new TestConventions().apply(project); + } + } From 0d07df968ea1e649cd6b9a5558e6a80156d338dd Mon Sep 17 00:00:00 2001 From: Vincent Potucek Date: Sun, 1 Jun 2025 10:39:52 +0200 Subject: [PATCH 10/15] spotless --- .../build/CheckstyleConventions.java | 1 + .../build/SpotlessConventions.java | 79 +++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 buildSrc/src/main/java/org/springframework/build/SpotlessConventions.java diff --git a/buildSrc/src/main/java/org/springframework/build/CheckstyleConventions.java b/buildSrc/src/main/java/org/springframework/build/CheckstyleConventions.java index 9cdcca1af129..2940e30f47f3 100644 --- a/buildSrc/src/main/java/org/springframework/build/CheckstyleConventions.java +++ b/buildSrc/src/main/java/org/springframework/build/CheckstyleConventions.java @@ -48,6 +48,7 @@ public void apply(Project project) { configureNoHttpPlugin(project); } project.getPlugins().apply(CheckstylePlugin.class); + project.getPlugins().apply(SpotlessPlugin.class); project.getTasks().withType(Checkstyle.class).forEach(checkstyle -> checkstyle.getMaxHeapSize().set("1g")); CheckstyleExtension checkstyle = project.getExtensions().getByType(CheckstyleExtension.class); checkstyle.setToolVersion("10.23.1"); diff --git a/buildSrc/src/main/java/org/springframework/build/SpotlessConventions.java b/buildSrc/src/main/java/org/springframework/build/SpotlessConventions.java new file mode 100644 index 000000000000..97f5a2dc6754 --- /dev/null +++ b/buildSrc/src/main/java/org/springframework/build/SpotlessConventions.java @@ -0,0 +1,79 @@ +/* + * Copyright 2002-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.build; + +import io.spring.javaformat.gradle.SpringJavaFormatPlugin; +import io.spring.nohttp.gradle.NoHttpExtension; +import io.spring.nohttp.gradle.NoHttpPlugin; +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.artifacts.DependencySet; +import org.gradle.api.plugins.JavaBasePlugin; +import org.gradle.api.plugins.quality.Checkstyle; +import org.gradle.api.plugins.quality.CheckstyleExtension; +import org.gradle.api.plugins.quality.CheckstylePlugin; + +import java.io.File; +import java.nio.file.Path; +import java.util.List; + +/** + * {@link Plugin} that applies conventions for checkstyle. + * + * @author Brian Clozel + */ +public class SpotlessConventions { + + /** + * Applies the Spring Java Format and Checkstyle plugins with the project conventions. + * @param project the current project + */ + public void apply(Project project) { + project.getPlugins().withType(JavaBasePlugin.class, (java) -> { + if (project.getRootProject() == project) { + configureNoHttpPlugin(project); + } + project.getPlugins().apply(SpotlessPlugin.class); + project.getTasks().withType(Checkstyle.class).forEach(checkstyle -> checkstyle.getMaxHeapSize().set("1g")); + CheckstyleExtension checkstyle = project.getExtensions().getByType(CheckstyleExtension.class); + checkstyle.setToolVersion("10.23.1"); + checkstyle.getConfigDirectory().set(project.getRootProject().file("src/checkstyle")); + String version = SpringJavaFormatPlugin.class.getPackage().getImplementationVersion(); + DependencySet checkstyleDependencies = project.getConfigurations().getByName("checkstyle").getDependencies(); + checkstyleDependencies.add( + project.getDependencies().create("io.spring.javaformat:spring-javaformat-checkstyle:" + version)); + }); + } + + private static void configureNoHttpPlugin(Project project) { + project.getPlugins().apply(NoHttpPlugin.class); + NoHttpExtension noHttp = project.getExtensions().getByType(NoHttpExtension.class); + noHttp.setAllowlistFile(project.file("src/nohttp/allowlist.lines")); + noHttp.getSource().exclude("**/test-output/**", "**/.settings/**", "**/.classpath", + "**/.project", "**/.gradle/**", "**/node_modules/**", "**/spring-jcl/**", "buildSrc/build/**"); + List buildFolders = List.of("bin", "build", "out"); + project.allprojects(subproject -> { + Path rootPath = project.getRootDir().toPath(); + Path projectPath = rootPath.relativize(subproject.getProjectDir().toPath()); + for (String buildFolder : buildFolders) { + Path innerBuildDir = projectPath.resolve(buildFolder); + noHttp.getSource().exclude(innerBuildDir + File.separator + "**"); + } + }); + } + +} From 16211a3e6c8bac5817b58a7406851517dae145de Mon Sep 17 00:00:00 2001 From: Vincent Potucek Date: Sun, 1 Jun 2025 10:40:29 +0200 Subject: [PATCH 11/15] spotless --- .../java/org/springframework/build/CheckstyleConventions.java | 1 - .../main/java/org/springframework/build/ConventionsPlugin.java | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/buildSrc/src/main/java/org/springframework/build/CheckstyleConventions.java b/buildSrc/src/main/java/org/springframework/build/CheckstyleConventions.java index 2940e30f47f3..9cdcca1af129 100644 --- a/buildSrc/src/main/java/org/springframework/build/CheckstyleConventions.java +++ b/buildSrc/src/main/java/org/springframework/build/CheckstyleConventions.java @@ -48,7 +48,6 @@ public void apply(Project project) { configureNoHttpPlugin(project); } project.getPlugins().apply(CheckstylePlugin.class); - project.getPlugins().apply(SpotlessPlugin.class); project.getTasks().withType(Checkstyle.class).forEach(checkstyle -> checkstyle.getMaxHeapSize().set("1g")); CheckstyleExtension checkstyle = project.getExtensions().getByType(CheckstyleExtension.class); checkstyle.setToolVersion("10.23.1"); diff --git a/buildSrc/src/main/java/org/springframework/build/ConventionsPlugin.java b/buildSrc/src/main/java/org/springframework/build/ConventionsPlugin.java index 1cd9e43cf0c4..74b714d5d873 100644 --- a/buildSrc/src/main/java/org/springframework/build/ConventionsPlugin.java +++ b/buildSrc/src/main/java/org/springframework/build/ConventionsPlugin.java @@ -41,7 +41,8 @@ public class ConventionsPlugin implements Plugin { public void apply(Project project) { project.getExtensions().create("springFramework", SpringFrameworkExtension.class); new ArchitecturePlugin().apply(project); - new CheckstyleConventions().apply(project); +// new CheckstyleConventions().apply(project); + new SpotlessConventions().apply(project); new JavaConventions().apply(project); new KotlinConventions().apply(project); new TestConventions().apply(project); From b4317c816de3a5c043677d317a1dbe1077094d8d Mon Sep 17 00:00:00 2001 From: Vincent Potucek Date: Sun, 1 Jun 2025 10:40:59 +0200 Subject: [PATCH 12/15] spotless --- .../java/org/springframework/build/SpotlessConventions.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/buildSrc/src/main/java/org/springframework/build/SpotlessConventions.java b/buildSrc/src/main/java/org/springframework/build/SpotlessConventions.java index 97f5a2dc6754..2dbaa48e9a0b 100644 --- a/buildSrc/src/main/java/org/springframework/build/SpotlessConventions.java +++ b/buildSrc/src/main/java/org/springframework/build/SpotlessConventions.java @@ -23,7 +23,7 @@ import org.gradle.api.Project; import org.gradle.api.artifacts.DependencySet; import org.gradle.api.plugins.JavaBasePlugin; -import org.gradle.api.plugins.quality.Checkstyle; +import org.gradle.api.plugins.quality.Spotless; import org.gradle.api.plugins.quality.CheckstyleExtension; import org.gradle.api.plugins.quality.CheckstylePlugin; @@ -39,7 +39,7 @@ public class SpotlessConventions { /** - * Applies the Spring Java Format and Checkstyle plugins with the project conventions. + * Applies the Spring Java Format and Spotless plugins with the project conventions. * @param project the current project */ public void apply(Project project) { @@ -48,7 +48,7 @@ public void apply(Project project) { configureNoHttpPlugin(project); } project.getPlugins().apply(SpotlessPlugin.class); - project.getTasks().withType(Checkstyle.class).forEach(checkstyle -> checkstyle.getMaxHeapSize().set("1g")); + project.getTasks().withType(Spotless.class).forEach(checkstyle -> checkstyle.getMaxHeapSize().set("1g")); CheckstyleExtension checkstyle = project.getExtensions().getByType(CheckstyleExtension.class); checkstyle.setToolVersion("10.23.1"); checkstyle.getConfigDirectory().set(project.getRootProject().file("src/checkstyle")); From 4d766a29615229f43322ea03d909da6f9a8a1dba Mon Sep 17 00:00:00 2001 From: Vincent Potucek Date: Sun, 1 Jun 2025 10:43:16 +0200 Subject: [PATCH 13/15] spotless --- buildSrc/build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index aaa5ca4d5d07..67b6ca3feafa 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -27,6 +27,7 @@ ext { dependencies { implementation "org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}" implementation "com.tngtech.archunit:archunit:1.4.0" + implementation "com.diffplug.spotless:spotless-plugin-gradle:7.0.4" implementation "org.gradle:test-retry-gradle-plugin:1.6.2" implementation "io.spring.javaformat:spring-javaformat-gradle-plugin:${javaFormatVersion}" implementation "io.spring.nohttp:nohttp-gradle:0.0.11" From c63098b9c28d34dfaf3413424dd523c75732a584 Mon Sep 17 00:00:00 2001 From: Vincent Potucek Date: Sun, 1 Jun 2025 10:46:36 +0200 Subject: [PATCH 14/15] spotless --- .../java/org/springframework/build/SpotlessConventions.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/buildSrc/src/main/java/org/springframework/build/SpotlessConventions.java b/buildSrc/src/main/java/org/springframework/build/SpotlessConventions.java index 2dbaa48e9a0b..d1d35a62ae9d 100644 --- a/buildSrc/src/main/java/org/springframework/build/SpotlessConventions.java +++ b/buildSrc/src/main/java/org/springframework/build/SpotlessConventions.java @@ -23,9 +23,7 @@ import org.gradle.api.Project; import org.gradle.api.artifacts.DependencySet; import org.gradle.api.plugins.JavaBasePlugin; -import org.gradle.api.plugins.quality.Spotless; import org.gradle.api.plugins.quality.CheckstyleExtension; -import org.gradle.api.plugins.quality.CheckstylePlugin; import java.io.File; import java.nio.file.Path; @@ -47,7 +45,7 @@ public void apply(Project project) { if (project.getRootProject() == project) { configureNoHttpPlugin(project); } - project.getPlugins().apply(SpotlessPlugin.class); + project.getPlugins().apply(com.diffplug.gradle.spotless.SpotlessPlugin.class); project.getTasks().withType(Spotless.class).forEach(checkstyle -> checkstyle.getMaxHeapSize().set("1g")); CheckstyleExtension checkstyle = project.getExtensions().getByType(CheckstyleExtension.class); checkstyle.setToolVersion("10.23.1"); From 1ee4bb30d745e656e16c95583fe8292534e4ad57 Mon Sep 17 00:00:00 2001 From: Vincent Potucek Date: Sun, 1 Jun 2025 10:50:26 +0200 Subject: [PATCH 15/15] spotless --- .../build/SpotlessConventions.java | 31 ++----------------- 1 file changed, 2 insertions(+), 29 deletions(-) diff --git a/buildSrc/src/main/java/org/springframework/build/SpotlessConventions.java b/buildSrc/src/main/java/org/springframework/build/SpotlessConventions.java index d1d35a62ae9d..95842b8e7488 100644 --- a/buildSrc/src/main/java/org/springframework/build/SpotlessConventions.java +++ b/buildSrc/src/main/java/org/springframework/build/SpotlessConventions.java @@ -41,36 +41,9 @@ public class SpotlessConventions { * @param project the current project */ public void apply(Project project) { - project.getPlugins().withType(JavaBasePlugin.class, (java) -> { - if (project.getRootProject() == project) { - configureNoHttpPlugin(project); - } + project.getPlugins().withType(JavaBasePlugin.class, (_) -> { project.getPlugins().apply(com.diffplug.gradle.spotless.SpotlessPlugin.class); - project.getTasks().withType(Spotless.class).forEach(checkstyle -> checkstyle.getMaxHeapSize().set("1g")); - CheckstyleExtension checkstyle = project.getExtensions().getByType(CheckstyleExtension.class); - checkstyle.setToolVersion("10.23.1"); - checkstyle.getConfigDirectory().set(project.getRootProject().file("src/checkstyle")); - String version = SpringJavaFormatPlugin.class.getPackage().getImplementationVersion(); - DependencySet checkstyleDependencies = project.getConfigurations().getByName("checkstyle").getDependencies(); - checkstyleDependencies.add( - project.getDependencies().create("io.spring.javaformat:spring-javaformat-checkstyle:" + version)); - }); - } - - private static void configureNoHttpPlugin(Project project) { - project.getPlugins().apply(NoHttpPlugin.class); - NoHttpExtension noHttp = project.getExtensions().getByType(NoHttpExtension.class); - noHttp.setAllowlistFile(project.file("src/nohttp/allowlist.lines")); - noHttp.getSource().exclude("**/test-output/**", "**/.settings/**", "**/.classpath", - "**/.project", "**/.gradle/**", "**/node_modules/**", "**/spring-jcl/**", "buildSrc/build/**"); - List buildFolders = List.of("bin", "build", "out"); - project.allprojects(subproject -> { - Path rootPath = project.getRootDir().toPath(); - Path projectPath = rootPath.relativize(subproject.getProjectDir().toPath()); - for (String buildFolder : buildFolders) { - Path innerBuildDir = projectPath.resolve(buildFolder); - noHttp.getSource().exclude(innerBuildDir + File.separator + "**"); - } + project.getTasks().withType(Spotless.class).forEach(spotless -> spotless.getMaxHeapSize().set("1g")); }); }