diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/asciidoc/packaging.adoc b/spring-boot-tools/spring-boot-gradle-plugin/src/main/asciidoc/packaging.adoc index b197459622..76d5319843 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/asciidoc/packaging.adoc +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/asciidoc/packaging.adoc @@ -11,6 +11,8 @@ an application's dependencies and can then be run with `java -jar`. Executable jars can be built using the `bootJar` task. The task is automatically created when the `java` plugin is applied and is an instance of {boot-jar-javadoc}[`BootJar`]. +The `assemble` task is automatically configured to depend upon the `bootJar` task so +running `assemble` (or `build`) will also run the `bootJar` task. @@ -19,6 +21,8 @@ when the `java` plugin is applied and is an instance of {boot-jar-javadoc}[`Boot Executable wars can be built using the `bootWar` task. The task is automatically created when the `war` plugin is applied and is an instance of {boot-war-javadoc}[`BootWar`]. +The `assemble` task is automatically configured to depend upon the `bootWar` task so +running `assemble` (or `build`) will also run the `bootWar` task. @@ -43,6 +47,27 @@ web-based integration tests will fail. +[[packaging-executable-and-normal]] +=== Packaging executable and normal archives + +By default, when the `bootJar` or `bootWar` tasks are configured, the `jar` or `war` +tasks are disabled. A project can be configured to build both an executable archive +and a normal archive at the same time by enabling the `jar` or `war` task: + +[source,groovy,indent=0,subs="verbatim"] +---- +include::../gradle/packaging/boot-jar-and-jar.gradle[tags=enable-jar] +---- + +To avoid the executable archive and the normal archive from being written to the same +location, one or the other should be configured to use a different location. One way to +do so is by configuring a classifier: + +[source,groovy,indent=0,subs="verbatim"] +---- +include::../gradle/packaging/boot-jar-and-jar.gradle[tags=classifier] +---- + [[packaging-executable-configuring]] === Configuring executable archive packaging @@ -164,4 +189,4 @@ manifest to set the `Main-Class` attribute: [source,groovy,indent=0,subs="verbatim"] ---- include::../gradle/packaging/boot-war-properties-launcher.gradle[tags=properties-launcher] ----- \ No newline at end of file +---- diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/asciidoc/reacting.adoc b/spring-boot-tools/spring-boot-gradle-plugin/src/main/asciidoc/reacting.adoc index 4c5fa9a8c1..34fcbe19dd 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/asciidoc/reacting.adoc +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/asciidoc/reacting.adoc @@ -14,13 +14,15 @@ plugin: executable, fat jar for the project. The jar will contain everything on the runtime classpath of the main source set; classes are packaged in `BOOT-INF/classes` and jars are packaged in `BOOT-INF/lib` -2. Creates a {software-component}[software component] named `bootJava` that contains the +2. Configures the `assemble` task to depend on the `bootJar` task. +3. Disables the `jar` task. +4. Creates a {software-component}[software component] named `bootJava` that contains the archive produced by the `bootJar` task. -3. Creates a {boot-run-javadoc}[`BootRun`] task named `bootRun` that can be used to run +5. Creates a {boot-run-javadoc}[`BootRun`] task named `bootRun` that can be used to run your application. -4. Creates a configuration named `bootArchives` that contains the artifact produced by +6. Creates a configuration named `bootArchives` that contains the artifact produced by the `bootJar` task. -5. Configures any `JavaCompile` tasks with no configured encoding to use `UTF-8`. +7. Configures any `JavaCompile` tasks with no configured encoding to use `UTF-8`. @@ -32,9 +34,11 @@ When Gradle's {war-plugin}[`war` plugin] is applied to a project, the Spring Boo 1. Creates a {boot-war-javadoc}[`BootWar`] task named `bootWar` that will create an executable, fat war for the project. In addition to the standard packaging, everything in the `providedRuntime` configuration will be packaged in `WEB-INF/lib-provided`. -2. Creates a {software-component}[software component] named `bootWeb` that contains the +2. Configures the `assemble` task to depend on the `bootWar` task. +3. Disables the `war` task. +4. Creates a {software-component}[software component] named `bootWeb` that contains the archive produced by the `bootWar` task. -3. Configures the `bootArchives` configuration to contain the artifact produced by the +5. Configures the `bootArchives` configuration to contain the artifact produced by the `bootWar` task. diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/packaging/boot-jar-and-jar.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/packaging/boot-jar-and-jar.gradle new file mode 100644 index 0000000000..c96b34d52f --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/packaging/boot-jar-and-jar.gradle @@ -0,0 +1,24 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +apply plugin: 'org.springframework.boot' +apply plugin: 'java' + +// tag::enable-jar[] +jar { + enabled = true +} +// end::enable-jar[] + +// tag::classifier[] +bootJar { + classifier = 'boot' +} +// end::classifier[] + +bootJar { + mainClass = 'com.example.Application' +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/JavaPluginAction.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/JavaPluginAction.java index 16e1a3e98e..26c8e881de 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/JavaPluginAction.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/JavaPluginAction.java @@ -24,6 +24,7 @@ import org.gradle.api.Plugin; import org.gradle.api.Project; import org.gradle.api.file.FileCollection; import org.gradle.api.internal.artifacts.publish.ArchivePublishArtifact; +import org.gradle.api.plugins.BasePlugin; import org.gradle.api.plugins.JavaPlugin; import org.gradle.api.plugins.JavaPluginConvention; import org.gradle.api.tasks.SourceSet; @@ -52,12 +53,23 @@ final class JavaPluginAction implements PluginApplicationAction { @Override public void execute(Project project) { + disableJarTask(project); + configureBuildTask(project); BootJar bootJar = configureBootJarTask(project); configureArtifactPublication(project, bootJar); configureBootRunTask(project); configureUtf8Encoding(project); } + private void disableJarTask(Project project) { + project.getTasks().getByName(JavaPlugin.JAR_TASK_NAME).setEnabled(false); + } + + private void configureBuildTask(Project project) { + project.getTasks().getByName(BasePlugin.ASSEMBLE_TASK_NAME) + .dependsOn(this.singlePublishedArtifact); + } + private BootJar configureBootJarTask(Project project) { BootJar bootJar = project.getTasks().create(SpringBootPlugin.BOOT_JAR_TASK_NAME, BootJar.class); diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SinglePublishedArtifact.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SinglePublishedArtifact.java index 5b081358e9..e7d27e1321 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SinglePublishedArtifact.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SinglePublishedArtifact.java @@ -16,8 +16,10 @@ package org.springframework.boot.gradle.plugin; +import org.gradle.api.Buildable; import org.gradle.api.artifacts.PublishArtifact; import org.gradle.api.artifacts.PublishArtifactSet; +import org.gradle.api.tasks.TaskDependency; /** * A wrapper for a {@PublishArtifactSet} that ensures that only a single artifact is @@ -25,7 +27,7 @@ import org.gradle.api.artifacts.PublishArtifactSet; * * @author Andy Wilkinson */ -final class SinglePublishedArtifact { +final class SinglePublishedArtifact implements Buildable { private final PublishArtifactSet artifacts; @@ -43,4 +45,9 @@ final class SinglePublishedArtifact { } } + @Override + public TaskDependency getBuildDependencies() { + return this.currentArtifact.getBuildDependencies(); + } + } diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/WarPluginAction.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/WarPluginAction.java index f5dec40f06..290345011c 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/WarPluginAction.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/WarPluginAction.java @@ -45,6 +45,7 @@ class WarPluginAction implements PluginApplicationAction { @Override public void execute(Project project) { + project.getTasks().getByName(WarPlugin.WAR_TASK_NAME).setEnabled(false); BootWar bootWar = project.getTasks().create(SpringBootPlugin.BOOT_WAR_TASK_NAME, BootWar.class); bootWar.providedClasspath(providedRuntimeConfiguration(project)); diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/PackagingDocumentationTests.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/PackagingDocumentationTests.java index 03afebb19c..683390b188 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/PackagingDocumentationTests.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/PackagingDocumentationTests.java @@ -173,4 +173,17 @@ public class PackagingDocumentationTests { } } + @Test + public void bootJarAndJar() throws IOException { + this.gradleBuild.script("src/main/gradle/packaging/boot-jar-and-jar.gradle") + .build("assemble"); + File jar = new File(this.gradleBuild.getProjectDir(), + "build/libs/" + this.gradleBuild.getProjectDir().getName() + ".jar"); + assertThat(jar).isFile(); + File bootJar = new File(this.gradleBuild.getProjectDir(), + "build/libs/" + this.gradleBuild.getProjectDir().getName() + "-boot.jar"); + assertThat(bootJar).isFile(); + + } + } diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/JavaPluginActionIntegrationTests.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/JavaPluginActionIntegrationTests.java index 5ae7a016e0..d4ab90408a 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/JavaPluginActionIntegrationTests.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/JavaPluginActionIntegrationTests.java @@ -16,6 +16,10 @@ package org.springframework.boot.gradle.plugin; +import java.io.File; + +import org.gradle.testkit.runner.BuildResult; +import org.gradle.testkit.runner.TaskOutcome; import org.junit.Rule; import org.junit.Test; @@ -79,4 +83,23 @@ public class JavaPluginActionIntegrationTests { .contains("compileTestJava = UTF-8"); } + @Test + public void assembleRunsBootJarAndJarIsSkipped() { + BuildResult result = this.gradleBuild.build("assemble"); + assertThat(result.task(":bootJar").getOutcome()).isEqualTo(TaskOutcome.SUCCESS); + assertThat(result.task(":jar").getOutcome()).isEqualTo(TaskOutcome.SKIPPED); + } + + @Test + public void jarAndBootJarCanBothBeBuilt() { + BuildResult result = this.gradleBuild.build("assemble"); + assertThat(result.task(":bootJar").getOutcome()).isEqualTo(TaskOutcome.SUCCESS); + assertThat(result.task(":jar").getOutcome()).isEqualTo(TaskOutcome.SUCCESS); + File buildLibs = new File(this.gradleBuild.getProjectDir(), "build/libs"); + assertThat(buildLibs.listFiles()).containsExactlyInAnyOrder( + new File(buildLibs, this.gradleBuild.getProjectDir().getName() + ".jar"), + new File(buildLibs, + this.gradleBuild.getProjectDir().getName() + "-boot.jar")); + } + } diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/WarPluginActionIntegrationTests.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/WarPluginActionIntegrationTests.java index c9555ed1b1..bbde1264cd 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/WarPluginActionIntegrationTests.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/WarPluginActionIntegrationTests.java @@ -16,6 +16,10 @@ package org.springframework.boot.gradle.plugin; +import java.io.File; + +import org.gradle.testkit.runner.BuildResult; +import org.gradle.testkit.runner.TaskOutcome; import org.junit.Rule; import org.junit.Test; @@ -59,4 +63,23 @@ public class WarPluginActionIntegrationTests { .getOutput()).contains("bootWeb exists = true"); } + @Test + public void assembleRunsBootWarAndWarIsSkipped() { + BuildResult result = this.gradleBuild.build("assemble"); + assertThat(result.task(":bootWar").getOutcome()).isEqualTo(TaskOutcome.SUCCESS); + assertThat(result.task(":war").getOutcome()).isEqualTo(TaskOutcome.SKIPPED); + } + + @Test + public void warAndBootWarCanBothBeBuilt() { + BuildResult result = this.gradleBuild.build("assemble"); + assertThat(result.task(":bootWar").getOutcome()).isEqualTo(TaskOutcome.SUCCESS); + assertThat(result.task(":war").getOutcome()).isEqualTo(TaskOutcome.SUCCESS); + File buildLibs = new File(this.gradleBuild.getProjectDir(), "build/libs"); + assertThat(buildLibs.listFiles()).containsExactlyInAnyOrder( + new File(buildLibs, this.gradleBuild.getProjectDir().getName() + ".war"), + new File(buildLibs, + this.gradleBuild.getProjectDir().getName() + "-boot.war")); + } + } diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/JavaPluginActionIntegrationTests-assembleRunsBootJarAndJarIsSkipped.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/JavaPluginActionIntegrationTests-assembleRunsBootJarAndJarIsSkipped.gradle new file mode 100644 index 0000000000..e0bd0696bf --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/JavaPluginActionIntegrationTests-assembleRunsBootJarAndJarIsSkipped.gradle @@ -0,0 +1,12 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +apply plugin: 'org.springframework.boot' +apply plugin: 'java' + +bootJar { + mainClass = 'com.example.Application' +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/JavaPluginActionIntegrationTests-jarAndBootJarCanBothBeBuilt.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/JavaPluginActionIntegrationTests-jarAndBootJarCanBothBeBuilt.gradle new file mode 100644 index 0000000000..0455a8bb42 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/JavaPluginActionIntegrationTests-jarAndBootJarCanBothBeBuilt.gradle @@ -0,0 +1,17 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +apply plugin: 'org.springframework.boot' +apply plugin: 'java' + +bootJar { + mainClass = 'com.example.Application' + classifier = 'boot' +} + +jar { + enabled = true +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/WarPluginActionIntegrationTests-assembleRunsBootWarAndWarIsSkipped.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/WarPluginActionIntegrationTests-assembleRunsBootWarAndWarIsSkipped.gradle new file mode 100644 index 0000000000..a697574ea2 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/WarPluginActionIntegrationTests-assembleRunsBootWarAndWarIsSkipped.gradle @@ -0,0 +1,12 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +apply plugin: 'org.springframework.boot' +apply plugin: 'war' + +bootWar { + mainClass = 'com.example.Application' +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/WarPluginActionIntegrationTests-warAndBootWarCanBothBeBuilt.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/WarPluginActionIntegrationTests-warAndBootWarCanBothBeBuilt.gradle new file mode 100644 index 0000000000..bf47606bc8 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/WarPluginActionIntegrationTests-warAndBootWarCanBothBeBuilt.gradle @@ -0,0 +1,17 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +apply plugin: 'org.springframework.boot' +apply plugin: 'war' + +bootWar { + mainClass = 'com.example.Application' + classifier = 'boot' +} + +war { + enabled = true +}