diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index 7d0027111ba2..67b6ca3feafa 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -1,8 +1,15 @@ plugins { id 'java-gradle-plugin' - id 'checkstyle' + id 'com.diffplug.spotless' version '7.0.4' +} +allprojects { + spotless { + java { + removeUnusedImports() + palantirJavaFormat() + } + } } - repositories { mavenCentral() gradlePluginPortal() @@ -18,9 +25,9 @@ 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 "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" 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/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); diff --git a/buildSrc/src/main/java/org/springframework/build/JavaConventions.java b/buildSrc/src/main/java/org/springframework/build/JavaConventions.java index 634f1ce5924e..63f7836905b7 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,121 @@ */ 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..7cd64aaf2bc0 100644 --- a/buildSrc/src/main/java/org/springframework/build/KotlinConventions.java +++ b/buildSrc/src/main/java/org/springframework/build/KotlinConventions.java @@ -27,25 +27,25 @@ */ 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/SpotlessConventions.java b/buildSrc/src/main/java/org/springframework/build/SpotlessConventions.java new file mode 100644 index 000000000000..95842b8e7488 --- /dev/null +++ b/buildSrc/src/main/java/org/springframework/build/SpotlessConventions.java @@ -0,0 +1,50 @@ +/* + * 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.CheckstyleExtension; + +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 Spotless plugins with the project conventions. + * @param project the current project + */ + public void apply(Project project) { + project.getPlugins().withType(JavaBasePlugin.class, (_) -> { + project.getPlugins().apply(com.diffplug.gradle.spotless.SpotlessPlugin.class); + project.getTasks().withType(Spotless.class).forEach(spotless -> spotless.getMaxHeapSize().set("1g")); + }); + } + +} diff --git a/buildSrc/src/main/java/org/springframework/build/SpringFrameworkExtension.java b/buildSrc/src/main/java/org/springframework/build/SpringFrameworkExtension.java index c4001388eb05..1ed02965cdc7 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,28 @@ 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..4cd5e18454fb 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,46 @@ */ 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..cb595832866b 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..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,41 +34,40 @@ */ 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..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,81 +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(); - } - } - 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..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,23 +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 2a7169fd885f..26c8dd214738 100644 --- a/buildSrc/src/main/java/org/springframework/build/hint/RuntimeHintsAgentArgumentProvider.java +++ b/buildSrc/src/main/java/org/springframework/build/hint/RuntimeHintsAgentArgumentProvider.java @@ -17,20 +17,17 @@ 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(); + @Classpath + ConfigurableFileCollection getAgentJar(); @Input SetProperty getIncludedPackages(); @@ -41,8 +38,12 @@ public interface RuntimeHintsAgentArgumentProvider extends CommandLineArgumentPr @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(',')); + 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..de7392ec6f02 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..ca9e645edf6d 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,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(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..1f22239cc48c 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,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 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..859814fddfa8 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..ec013a65b3bf 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,33 @@ 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..e8c1191ccc8f 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,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()); - }); - } - }); - } - 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..a2d5693d2849 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,14 @@ 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 +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' @@ -101,37 +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"); - - 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(); + } }