diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/asciidoc/packaging.adoc b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/asciidoc/packaging.adoc index a6806c8c08..da43bf2448 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/asciidoc/packaging.adoc +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/asciidoc/packaging.adoc @@ -88,7 +88,7 @@ A number of configuration options that are specific to executable jars and wars [[packaging-executable.configuring.main-class]] === Configuring the Main Class -By default, the executable archive's main class will be configured automatically by looking for a class with a `public static void main(String[])` method in directories on the task's classpath. +By default, the executable archive's main class will be configured automatically by looking for a class with a `public static void main(String[])` method in the main source set's output. The main class can also be configured explicitly using the task's `mainClass` property: diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/asciidoc/running.adoc b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/asciidoc/running.adoc index 7853abde10..9c230e6f71 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/asciidoc/running.adoc +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/asciidoc/running.adoc @@ -11,7 +11,7 @@ The `bootRun` task is an instance of {boot-run-javadoc}[`BootRun`] which is a `J As such, all of the {gradle-dsl}/org.gradle.api.tasks.JavaExec.html[usual configuration options] for executing a Java process in Gradle are available to you. The task is automatically configured to use the runtime classpath of the main source set. -By default, the main class will be configured automatically by looking for a class with a `public static void main(String[])` method in directories on the task's classpath. +By default, the main class will be configured automatically by looking for a class with a `public static void main(String[])` method in the main source set's output. The main class can also be configured explicitly using the task's `main` property: diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/JavaPluginAction.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/JavaPluginAction.java index 7cc23c5ca5..bb10345637 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/JavaPluginAction.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/JavaPluginAction.java @@ -35,6 +35,8 @@ import org.gradle.api.file.FileCollection; import org.gradle.api.model.ObjectFactory; import org.gradle.api.plugins.ApplicationPlugin; import org.gradle.api.plugins.BasePlugin; +import org.gradle.api.plugins.ExtensionContainer; +import org.gradle.api.plugins.JavaApplication; import org.gradle.api.plugins.JavaPlugin; import org.gradle.api.plugins.JavaPluginExtension; import org.gradle.api.provider.Provider; @@ -46,6 +48,7 @@ import org.gradle.api.tasks.compile.JavaCompile; import org.gradle.jvm.toolchain.JavaToolchainService; import org.gradle.jvm.toolchain.JavaToolchainSpec; +import org.springframework.boot.gradle.dsl.SpringBootExtension; import org.springframework.boot.gradle.tasks.bundling.BootBuildImage; import org.springframework.boot.gradle.tasks.bundling.BootJar; import org.springframework.boot.gradle.tasks.run.BootRun; @@ -77,10 +80,11 @@ final class JavaPluginAction implements PluginApplicationAction { classifyJarTask(project); configureBuildTask(project); configureDevelopmentOnlyConfiguration(project); - TaskProvider bootJar = configureBootJarTask(project); + TaskProvider resolveMainClassName = configureResolveMainClassNameTask(project); + TaskProvider bootJar = configureBootJarTask(project, resolveMainClassName); configureBootBuildImageTask(project, bootJar); configureArtifactPublication(bootJar); - configureBootRunTask(project); + configureBootRunTask(project, resolveMainClassName); project.afterEvaluate(this::configureUtf8Encoding); configureParametersCompilerArg(project); configureAdditionalMetadataLocations(project); @@ -96,7 +100,39 @@ final class JavaPluginAction implements PluginApplicationAction { .configure((task) -> task.dependsOn(this.singlePublishedArtifact)); } - private TaskProvider configureBootJarTask(Project project) { + private TaskProvider configureResolveMainClassNameTask(Project project) { + return project.getTasks().register(SpringBootPlugin.RESOLVE_MAIN_CLASS_NAME_TASK_NAME, + ResolveMainClassName.class, (resolveMainClassName) -> { + ExtensionContainer extensions = project.getExtensions(); + resolveMainClassName.setDescription("Resolves the name of the application's main class."); + resolveMainClassName.setGroup(BasePlugin.BUILD_GROUP); + Callable classpath = () -> project.getExtensions() + .getByType(SourceSetContainer.class).getByName(SourceSet.MAIN_SOURCE_SET_NAME).getOutput(); + resolveMainClassName.setClasspath(classpath); + resolveMainClassName.getConfiguredMainClassName().convention(project.provider(() -> { + String javaApplicationMainClass = getJavaApplicationMainClass(extensions); + if (javaApplicationMainClass != null) { + return javaApplicationMainClass; + } + SpringBootExtension springBootExtension = project.getExtensions() + .findByType(SpringBootExtension.class); + return springBootExtension.getMainClass().getOrNull(); + })); + resolveMainClassName.getOutputFile() + .set(project.getLayout().getBuildDirectory().file("resolvedMainClassName")); + }); + } + + private static String getJavaApplicationMainClass(ExtensionContainer extensions) { + JavaApplication javaApplication = extensions.findByType(JavaApplication.class); + if (javaApplication == null) { + return null; + } + return javaApplication.getMainClass().getOrNull(); + } + + private TaskProvider configureBootJarTask(Project project, + TaskProvider resolveMainClassName) { SourceSet mainSourceSet = javaPluginExtension(project).getSourceSets() .getByName(SourceSet.MAIN_SOURCE_SET_NAME); Configuration developmentOnly = project.getConfigurations() @@ -105,8 +141,6 @@ final class JavaPluginAction implements PluginApplicationAction { .getByName(SpringBootPlugin.PRODUCTION_RUNTIME_CLASSPATH_CONFIGURATION_NAME); Callable classpath = () -> mainSourceSet.getRuntimeClasspath() .minus((developmentOnly.minus(productionRuntimeClasspath))).filter(new JarTypeFileSpec()); - TaskProvider resolveMainClassName = ResolveMainClassName - .registerForTask(SpringBootPlugin.BOOT_JAR_TASK_NAME, project, classpath); return project.getTasks().register(SpringBootPlugin.BOOT_JAR_TASK_NAME, BootJar.class, (bootJar) -> { bootJar.setDescription( "Assembles an executable jar archive containing the main classes and their dependencies."); @@ -134,11 +168,9 @@ final class JavaPluginAction implements PluginApplicationAction { this.singlePublishedArtifact.addJarCandidate(bootJar); } - private void configureBootRunTask(Project project) { + private void configureBootRunTask(Project project, TaskProvider resolveMainClassName) { Callable classpath = () -> javaPluginExtension(project).getSourceSets() .findByName(SourceSet.MAIN_SOURCE_SET_NAME).getRuntimeClasspath().filter(new JarTypeFileSpec()); - TaskProvider resolveProvider = ResolveMainClassName.registerForTask("bootRun", project, - classpath); project.getTasks().register("bootRun", BootRun.class, (run) -> { run.setDescription("Runs this project as a Spring Boot application."); run.setGroup(ApplicationPlugin.APPLICATION_GROUP); @@ -149,7 +181,7 @@ final class JavaPluginAction implements PluginApplicationAction { } return Collections.emptyList(); }); - run.getMainClass().convention(resolveProvider.flatMap(ResolveMainClassName::readMainClassName)); + run.getMainClass().convention(resolveMainClassName.flatMap(ResolveMainClassName::readMainClassName)); configureToolchainConvention(project, run); }); } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/ResolveMainClassName.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/ResolveMainClassName.java index 064d2ec590..d96337216b 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/ResolveMainClassName.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/ResolveMainClassName.java @@ -23,7 +23,6 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.Objects; -import java.util.concurrent.Callable; import org.gradle.api.DefaultTask; import org.gradle.api.InvalidUserDataException; @@ -33,9 +32,6 @@ import org.gradle.api.Transformer; import org.gradle.api.file.FileCollection; import org.gradle.api.file.RegularFile; import org.gradle.api.file.RegularFileProperty; -import org.gradle.api.plugins.BasePlugin; -import org.gradle.api.plugins.ExtensionContainer; -import org.gradle.api.plugins.JavaApplication; import org.gradle.api.provider.Property; import org.gradle.api.provider.Provider; import org.gradle.api.tasks.Classpath; @@ -43,10 +39,8 @@ import org.gradle.api.tasks.Input; import org.gradle.api.tasks.Optional; import org.gradle.api.tasks.OutputFile; import org.gradle.api.tasks.TaskAction; -import org.gradle.api.tasks.TaskProvider; import org.gradle.work.DisableCachingByDefault; -import org.springframework.boot.gradle.dsl.SpringBootExtension; import org.springframework.boot.loader.tools.MainClassFinder; /** @@ -154,38 +148,6 @@ public class ResolveMainClassName extends DefaultTask { return this.outputFile.map(new ClassNameReader()); } - static TaskProvider registerForTask(String taskName, Project project, - Callable classpath) { - TaskProvider resolveMainClassNameProvider = project.getTasks() - .register(taskName + "MainClassName", ResolveMainClassName.class, (resolveMainClassName) -> { - ExtensionContainer extensions = project.getExtensions(); - resolveMainClassName.setDescription( - "Resolves the name of the application's main class for the " + taskName + " task."); - resolveMainClassName.setGroup(BasePlugin.BUILD_GROUP); - resolveMainClassName.setClasspath(classpath); - resolveMainClassName.getConfiguredMainClassName().convention(project.provider(() -> { - String javaApplicationMainClass = getJavaApplicationMainClass(extensions); - if (javaApplicationMainClass != null) { - return javaApplicationMainClass; - } - SpringBootExtension springBootExtension = project.getExtensions() - .findByType(SpringBootExtension.class); - return springBootExtension.getMainClass().getOrNull(); - })); - resolveMainClassName.getOutputFile() - .set(project.getLayout().getBuildDirectory().file(taskName + "MainClassName")); - }); - return resolveMainClassNameProvider; - } - - private static String getJavaApplicationMainClass(ExtensionContainer extensions) { - JavaApplication javaApplication = extensions.findByType(JavaApplication.class); - if (javaApplication == null) { - return null; - } - return javaApplication.getMainClass().getOrNull(); - } - private static final class ClassNameReader implements Transformer { @Override diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootPlugin.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootPlugin.java index 4511633d17..7f0365a0eb 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootPlugin.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootPlugin.java @@ -81,6 +81,12 @@ public class SpringBootPlugin implements Plugin { */ public static final String PRODUCTION_RUNTIME_CLASSPATH_CONFIGURATION_NAME = "productionRuntimeClasspath"; + /** + * The name of the {@link ResolveMainClassName} task. + * @since 3.0.0 + */ + public static final String RESOLVE_MAIN_CLASS_NAME_TASK_NAME = "resolveMainClassName"; + /** * The coordinates {@code (group:name:version)} of the * {@code spring-boot-dependencies} bom. diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/WarPluginAction.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/WarPluginAction.java index d81ecde2b5..853fb8ca1e 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/WarPluginAction.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/WarPluginAction.java @@ -76,8 +76,8 @@ class WarPluginAction implements PluginApplicationAction { .getByName(SourceSet.MAIN_SOURCE_SET_NAME).getRuntimeClasspath() .minus(providedRuntimeConfiguration(project)).minus((developmentOnly.minus(productionRuntimeClasspath))) .filter(new JarTypeFileSpec()); - TaskProvider resolveMainClassName = ResolveMainClassName - .registerForTask(SpringBootPlugin.BOOT_WAR_TASK_NAME, project, classpath); + TaskProvider resolveMainClassName = project.getTasks() + .named(SpringBootPlugin.RESOLVE_MAIN_CLASS_NAME_TASK_NAME, ResolveMainClassName.class); TaskProvider bootWarProvider = project.getTasks().register(SpringBootPlugin.BOOT_WAR_TASK_NAME, BootWar.class, (bootWar) -> { bootWar.setGroup(BasePlugin.BUILD_GROUP);