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();
+ }
}