From 519250cacfba9c09fefbbd682dd86f4590d63323 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Tue, 1 Nov 2022 16:17:21 +0000 Subject: [PATCH] Polish Gradle tasks for AOT processing Closes gh-32946 --- .../gradle/plugin/SpringBootAotPlugin.java | 19 ++++-- .../boot/gradle/tasks/aot/AbstractAot.java | 47 +++++++-------- .../boot/gradle/tasks/aot/ProcessAot.java | 10 +++- .../boot/gradle/tasks/aot/ProcessTestAot.java | 59 ++++++++++--------- .../SpringBootAotPluginIntegrationTests.java | 6 -- ...otHasLibraryResourcesOnItsClasspath.gradle | 18 +++++- ...veRuntimeDependenciesOnItsClasspath.gradle | 14 ++++- ...spring-boot-dependencies-TEST-SNAPSHOT.pom | 5 ++ 8 files changed, 108 insertions(+), 70 deletions(-) diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootAotPlugin.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootAotPlugin.java index 467045fa05..f6461912fb 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootAotPlugin.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootAotPlugin.java @@ -19,6 +19,9 @@ package org.springframework.boot.gradle.plugin; import org.gradle.api.Plugin; import org.gradle.api.Project; import org.gradle.api.artifacts.Configuration; +import org.gradle.api.artifacts.Dependency; +import org.gradle.api.artifacts.DependencySet; +import org.gradle.api.artifacts.dsl.DependencyHandler; import org.gradle.api.attributes.Attribute; import org.gradle.api.attributes.AttributeContainer; import org.gradle.api.attributes.LibraryElements; @@ -119,7 +122,7 @@ public class SpringBootAotPlugin implements Plugin { TaskProvider processAot = project.getTasks().register(PROCESS_AOT_TASK_NAME, ProcessAot.class, (task) -> { configureAotTask(project, aotSourceSet, task, mainSourceSet, resourcesOutput); - task.getApplicationClass() + task.getApplicationMainClass() .set(resolveMainClassName.flatMap(ResolveMainClassName::readMainClassName)); task.setClasspath(aotClasspath); }); @@ -140,7 +143,6 @@ public class SpringBootAotPlugin implements Plugin { .set(project.getLayout().getBuildDirectory().dir("generated/" + sourceSet.getName() + "Classes")); task.getGroupId().set(project.provider(() -> String.valueOf(project.getGroup()))); task.getArtifactId().set(project.provider(() -> project.getName())); - task.setClasspathRoots(inputSourceSet.getOutput().getClassesDirs()); } @SuppressWarnings("unchecked") @@ -171,6 +173,7 @@ public class SpringBootAotPlugin implements Plugin { private void registerProcessTestAotTask(Project project, SourceSet mainSourceSet, SourceSet aotTestSourceSet, SourceSet testSourceSet) { Configuration aotClasspath = createAotProcessingClasspath(project, PROCESS_TEST_AOT_TASK_NAME, testSourceSet); + addJUnitPlatformLauncherDependency(project, aotClasspath); Configuration compileClasspath = project.getConfigurations() .getByName(aotTestSourceSet.getCompileClasspathConfigurationName()); compileClasspath.extendsFrom(aotClasspath); @@ -180,8 +183,7 @@ public class SpringBootAotPlugin implements Plugin { ProcessTestAot.class, (task) -> { configureAotTask(project, aotTestSourceSet, task, testSourceSet, resourcesOutput); task.setClasspath(aotClasspath); - task.setTestRuntimeClasspath( - project.getConfigurations().getByName(testSourceSet.getImplementationConfigurationName())); + task.setClasspathRoots(testSourceSet.getOutput()); }); aotTestSourceSet.getJava().srcDir(processTestAot.map(ProcessTestAot::getSourcesOutput)); aotTestSourceSet.getResources().srcDir(resourcesOutput); @@ -194,4 +196,13 @@ public class SpringBootAotPlugin implements Plugin { configureDependsOn(project, aotTestSourceSet, processTestAot); } + private void addJUnitPlatformLauncherDependency(Project project, Configuration configuration) { + DependencyHandler dependencyHandler = project.getDependencies(); + Dependency springBootDependencies = dependencyHandler + .create(dependencyHandler.platform(SpringBootPlugin.BOM_COORDINATES)); + DependencySet dependencies = configuration.getDependencies(); + dependencies.add(springBootDependencies); + dependencies.add(dependencyHandler.create("org.junit.platform:junit-platform-launcher")); + } + } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/aot/AbstractAot.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/aot/AbstractAot.java index eb1a20d0b1..e8355be870 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/aot/AbstractAot.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/aot/AbstractAot.java @@ -20,17 +20,10 @@ import java.util.ArrayList; import java.util.List; import org.gradle.api.file.DirectoryProperty; -import org.gradle.api.file.FileCollection; -import org.gradle.api.file.FileTree; import org.gradle.api.provider.Property; -import org.gradle.api.tasks.IgnoreEmptyDirectories; import org.gradle.api.tasks.Input; -import org.gradle.api.tasks.InputFiles; import org.gradle.api.tasks.JavaExec; import org.gradle.api.tasks.OutputDirectory; -import org.gradle.api.tasks.PathSensitive; -import org.gradle.api.tasks.PathSensitivity; -import org.gradle.api.tasks.SkipWhenEmpty; import org.gradle.work.DisableCachingByDefault; /** @@ -53,8 +46,6 @@ public abstract class AbstractAot extends JavaExec { private final Property artifactId; - private FileCollection classpathRoots; - protected AbstractAot() { this.sourcesDir = getProject().getObjects().directoryProperty(); this.resourcesDir = getProject().getObjects().directoryProperty(); @@ -63,49 +54,51 @@ public abstract class AbstractAot extends JavaExec { this.artifactId = getProject().getObjects().property(String.class); } + /** + * The group ID of the application that is to be processed ahead-of-time. + * @return the group ID property + */ @Input public final Property getGroupId() { return this.groupId; } + /** + * The artifact ID of the application that is to be processed ahead-of-time. + * @return the artifact ID property + */ @Input public final Property getArtifactId() { return this.artifactId; } + /** + * The directory to which AOT-generated sources should be written. + * @return the sources directory property + */ @OutputDirectory public final DirectoryProperty getSourcesOutput() { return this.sourcesDir; } + /** + * The directory to which AOT-generated resources should be written. + * @return the resources directory property + */ @OutputDirectory public final DirectoryProperty getResourcesOutput() { return this.resourcesDir; } + /** + * The directory to which AOT-generated classes should be written. + * @return the classes directory property + */ @OutputDirectory public final DirectoryProperty getClassesOutput() { return this.classesDir; } - @InputFiles - @PathSensitive(PathSensitivity.RELATIVE) - public final FileCollection getClasspathRoots() { - return this.classpathRoots; - } - - @InputFiles - @SkipWhenEmpty - @IgnoreEmptyDirectories - @PathSensitive(PathSensitivity.RELATIVE) - final FileTree getInputClasses() { - return this.classpathRoots.getAsFileTree(); - } - - public void setClasspathRoots(FileCollection classpathRoots) { - this.classpathRoots = classpathRoots; - } - List processorArgs() { List args = new ArrayList<>(); args.add(getSourcesOutput().getAsFile().get().getAbsolutePath()); diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/aot/ProcessAot.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/aot/ProcessAot.java index 4da1d27d45..f680a3e2a5 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/aot/ProcessAot.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/aot/ProcessAot.java @@ -26,7 +26,7 @@ import org.gradle.api.tasks.JavaExec; import org.gradle.api.tasks.TaskAction; /** - * Custom {@link JavaExec} task for processing main code ahead-of-time. + * Custom {@link JavaExec} task for ahead-of-time processing of a Spring Boot application. * * @author Andy Wilkinson * @since 3.0.0 @@ -38,14 +38,18 @@ public abstract class ProcessAot extends AbstractAot { getMainClass().set("org.springframework.boot.SpringApplicationAotProcessor"); } + /** + * Returns the main class of the application that is to be processed ahead-of-time. + * @return the application main class property + */ @Input - public abstract Property getApplicationClass(); + public abstract Property getApplicationMainClass(); @Override @TaskAction public void exec() { List args = new ArrayList<>(); - args.add(getApplicationClass().get()); + args.add(getApplicationMainClass().get()); args.addAll(processorArgs()); setArgs(args); super.exec(); diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/aot/ProcessTestAot.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/aot/ProcessTestAot.java index 3531d18e34..11de712577 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/aot/ProcessTestAot.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/aot/ProcessTestAot.java @@ -21,20 +21,20 @@ import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; -import org.gradle.api.artifacts.Configuration; -import org.gradle.api.artifacts.Dependency; -import org.gradle.api.artifacts.DependencySet; -import org.gradle.api.artifacts.dsl.DependencyHandler; import org.gradle.api.file.FileCollection; +import org.gradle.api.file.FileTree; import org.gradle.api.tasks.CacheableTask; -import org.gradle.api.tasks.Classpath; +import org.gradle.api.tasks.IgnoreEmptyDirectories; +import org.gradle.api.tasks.InputFiles; import org.gradle.api.tasks.JavaExec; +import org.gradle.api.tasks.PathSensitive; +import org.gradle.api.tasks.PathSensitivity; +import org.gradle.api.tasks.SkipWhenEmpty; import org.gradle.api.tasks.TaskAction; -import org.springframework.boot.gradle.util.VersionExtractor; - /** - * Custom {@link JavaExec} task for processing test code ahead-of-time. + * Custom {@link JavaExec} task for ahead-of-time processing of a Spring Boot + * application's tests. * * @author Andy Wilkinson * @since 3.0.0 @@ -42,28 +42,36 @@ import org.springframework.boot.gradle.util.VersionExtractor; @CacheableTask public class ProcessTestAot extends AbstractAot { - private final Configuration junitPlatformLauncher; + private FileCollection classpathRoots; public ProcessTestAot() { getMainClass().set("org.springframework.boot.test.context.SpringBootTestAotProcessor"); - this.junitPlatformLauncher = createJUnitPlatformLauncher(); } - private Configuration createJUnitPlatformLauncher() { - Configuration configuration = getProject().getConfigurations().create(getName() + "JUnitPlatformLauncher"); - DependencyHandler dependencyHandler = getProject().getDependencies(); - Dependency springBootDependencies = dependencyHandler - .create(dependencyHandler.platform("org.springframework.boot:spring-boot-dependencies:" - + VersionExtractor.forClass(ProcessTestAot.class))); - DependencySet dependencies = configuration.getDependencies(); - dependencies.add(springBootDependencies); - dependencies.add(dependencyHandler.create("org.junit.platform:junit-platform-launcher")); - return configuration; + /** + * Returns the classpath roots that should be scanned for test classes to process. + * @return the classpath roots + */ + @InputFiles + @PathSensitive(PathSensitivity.RELATIVE) + public final FileCollection getClasspathRoots() { + return this.classpathRoots; + } + + /** + * Sets the classpath roots that should be scanned for test classes to process. + * @param classpathRoots the classpath roots + */ + public void setClasspathRoots(FileCollection classpathRoots) { + this.classpathRoots = classpathRoots; } - @Classpath - FileCollection getJUnitPlatformLauncher() { - return this.junitPlatformLauncher; + @InputFiles + @SkipWhenEmpty + @IgnoreEmptyDirectories + @PathSensitive(PathSensitivity.RELATIVE) + final FileTree getInputClasses() { + return this.classpathRoots.getAsFileTree(); } @Override @@ -74,12 +82,7 @@ public class ProcessTestAot extends AbstractAot { .collect(Collectors.joining(File.pathSeparator))); args.addAll(processorArgs()); setArgs(args); - classpath(this.junitPlatformLauncher); super.exec(); } - public void setTestRuntimeClasspath(Configuration configuration) { - this.junitPlatformLauncher.extendsFrom(configuration); - } - } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/SpringBootAotPluginIntegrationTests.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/SpringBootAotPluginIntegrationTests.java index 963d54a5e1..9603e77b24 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/SpringBootAotPluginIntegrationTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/SpringBootAotPluginIntegrationTests.java @@ -95,12 +95,6 @@ class SpringBootAotPluginIntegrationTests { assertThat(output).contains("org.jboss.logging" + File.separatorChar + "jboss-logging"); } - @TestTemplate - void processAotIsSkippedWhenProjectHasNoMainSource() { - assertThat(this.gradleBuild.build("processAot").task(":processAot").getOutcome()) - .isEqualTo(TaskOutcome.NO_SOURCE); - } - @TestTemplate void processAotRunsWhenProjectHasMainSource() throws IOException { writeMainClass("org.springframework.boot", "SpringApplicationAotProcessor"); diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/SpringBootAotPluginIntegrationTests-processTestAotHasLibraryResourcesOnItsClasspath.gradle b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/SpringBootAotPluginIntegrationTests-processTestAotHasLibraryResourcesOnItsClasspath.gradle index f3465edfe7..759bfa5167 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/SpringBootAotPluginIntegrationTests-processTestAotHasLibraryResourcesOnItsClasspath.gradle +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/SpringBootAotPluginIntegrationTests-processTestAotHasLibraryResourcesOnItsClasspath.gradle @@ -4,12 +4,28 @@ plugins { id 'java' } +repositories { + mavenCentral() + maven { url 'file:repository' } +} + +configurations.all { + resolutionStrategy { + eachDependency { + if (it.requested.group == 'org.springframework.boot') { + it.useVersion project.bootVersion + } + } + } +} + dependencies { implementation project(":library") } task('processTestAotClasspath') { + dependsOn configurations.processTestAotClasspath doFirst { - tasks.findByName('processTestAot').classpath.files.each { println it } + configurations.processTestAotClasspath.files.each { println it } } } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/SpringBootAotPluginIntegrationTests-processTestAotHasTransitiveRuntimeDependenciesOnItsClasspath.gradle b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/SpringBootAotPluginIntegrationTests-processTestAotHasTransitiveRuntimeDependenciesOnItsClasspath.gradle index 5d51a1a2e0..2b3a7e93fb 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/SpringBootAotPluginIntegrationTests-processTestAotHasTransitiveRuntimeDependenciesOnItsClasspath.gradle +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/SpringBootAotPluginIntegrationTests-processTestAotHasTransitiveRuntimeDependenciesOnItsClasspath.gradle @@ -6,6 +6,17 @@ plugins { repositories { mavenCentral() + maven { url 'file:repository' } +} + +configurations.all { + resolutionStrategy { + eachDependency { + if (it.requested.group == 'org.springframework.boot') { + it.useVersion project.bootVersion + } + } + } } dependencies { @@ -13,7 +24,8 @@ dependencies { } task('processTestAotClasspath') { + dependsOn configurations.processTestAotClasspath doFirst { - tasks.findByName('processTestAot').classpath.files.each { println it } + configurations.processTestAotClasspath.files.each { println it } } } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/repository/org/springframework/boot/spring-boot-dependencies/TEST-SNAPSHOT/spring-boot-dependencies-TEST-SNAPSHOT.pom b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/repository/org/springframework/boot/spring-boot-dependencies/TEST-SNAPSHOT/spring-boot-dependencies-TEST-SNAPSHOT.pom index e3b617e510..d7bcda93bc 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/repository/org/springframework/boot/spring-boot-dependencies/TEST-SNAPSHOT/spring-boot-dependencies-TEST-SNAPSHOT.pom +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/repository/org/springframework/boot/spring-boot-dependencies/TEST-SNAPSHOT/spring-boot-dependencies-TEST-SNAPSHOT.pom @@ -22,6 +22,11 @@ spring-boot-starter TEST-SNAPSHOT + + org.junit.platform + junit-platform-launcher + 1.9.1 +