Repository: GradleUp/shadow
Pull request #1198 (2025-01-26 07:25:55)

Tidy up `ShadowApplicationPlugin`


Patch:
diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt
index edc75fe9..a8941bf8 100644
--- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt
+++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt
@@ -33,61 +33,60 @@ public abstract class ShadowApplicationPlugin : Plugin<Project> {
 
   protected open fun configureJarMainClass() {
     val classNameProvider = javaApplication.mainClass
-    shadowJar.configure { jar ->
-      jar.inputs.property("mainClassName", classNameProvider)
-      jar.doFirst {
+    shadowJar.configure { task ->
+      task.inputs.property("mainClassName", classNameProvider)
+      task.doFirst {
         // Inject the Main-Class attribute if it is not already present.
-        if (!jar.manifest.attributes.contains("Main-Class")) {
-          jar.manifest.attributes["Main-Class"] = classNameProvider.get()
+        if (!task.manifest.attributes.contains("Main-Class")) {
+          task.manifest.attributes["Main-Class"] = classNameProvider.get()
         }
       }
     }
   }
 
   protected open fun addRunTask() {
-    project.tasks.register(SHADOW_RUN_TASK_NAME, JavaExec::class.java) {
-      val install = project.tasks.named(SHADOW_INSTALL_TASK_NAME, Sync::class.java)
-      val jarFile = install.zip(shadowJar) { i, s ->
+    project.tasks.register(SHADOW_RUN_TASK_NAME, JavaExec::class.java) { task ->
+      task.description = "Runs this project as a JVM application using the shadow jar"
+      task.group = ApplicationPlugin.APPLICATION_GROUP
+      task.dependsOn(installShadowDist)
+
+      val jarFile = installShadowDist.zip(shadowJar) { i, s ->
         i.destinationDir.resolve("lib/${s.archiveFile.get().asFile.name}")
       }
-
-      it.dependsOn(install)
-      it.classpath(jarFile)
-      it.mainClass.set(javaApplication.mainClass)
-      it.description = "Runs this project as a JVM application using the shadow jar"
-      it.group = ApplicationPlugin.APPLICATION_GROUP
-      it.conventionMapping.map("jvmArgs") { javaApplication.applicationDefaultJvmArgs }
+      task.classpath(jarFile)
+      task.mainClass.set(javaApplication.mainClass)
+      task.conventionMapping.map("jvmArgs", javaApplication::getApplicationDefaultJvmArgs)
       val toolchain = project.extensions.getByType(JavaPluginExtension::class.java).toolchain
-      val defaultLauncher = project.extensions.getByType(JavaToolchainService::class.java)
-        .launcherFor(toolchain)
-      it.javaLauncher.set(defaultLauncher)
+      val defaultLauncher = project.extensions.getByType(JavaToolchainService::class.java).launcherFor(toolchain)
+      task.javaLauncher.set(defaultLauncher)
     }
   }
 
   protected open fun addCreateScriptsTask() {
-    project.tasks.register(SHADOW_SCRIPTS_TASK_NAME, CreateStartScripts::class.java) {
+    project.tasks.register(SHADOW_SCRIPTS_TASK_NAME, CreateStartScripts::class.java) { task ->
+      task.description = "Creates OS specific scripts to run the project as a JVM application using the shadow jar"
+      task.group = ApplicationPlugin.APPLICATION_GROUP
+
       val unixStartScript =
         requireResourceAsText("com/github/jengelman/gradle/plugins/shadow/internal/unixStartScript.txt")
       val windowsStartScript =
         requireResourceAsText("com/github/jengelman/gradle/plugins/shadow/internal/windowsStartScript.txt")
-
-      (it.unixStartScriptGenerator as TemplateBasedScriptGenerator).template =
+      (task.unixStartScriptGenerator as TemplateBasedScriptGenerator).template =
         project.resources.text.fromString(unixStartScript)
-      (it.windowsStartScriptGenerator as TemplateBasedScriptGenerator).template =
+      (task.windowsStartScriptGenerator as TemplateBasedScriptGenerator).template =
         project.resources.text.fromString(windowsStartScript)
-      it.description = "Creates OS specific scripts to run the project as a JVM application using the shadow jar"
-      it.group = ApplicationPlugin.APPLICATION_GROUP
-      it.classpath = project.files(shadowJar)
-      it.mainClass.set(javaApplication.mainClass)
-      it.conventionMapping.map("applicationName") { javaApplication.applicationName }
-      it.conventionMapping.map("outputDir") { project.layout.buildDirectory.dir("scriptsShadow").get().asFile }
-      it.conventionMapping.map("defaultJvmOpts") { javaApplication.applicationDefaultJvmArgs }
-      it.inputs.files(project.files(shadowJar))
+
+      task.classpath = project.files(shadowJar)
+      task.inputs.files(project.files(shadowJar))
+      task.mainClass.set(javaApplication.mainClass)
+      task.conventionMapping.map("applicationName", javaApplication::getApplicationName)
+      task.conventionMapping.map("outputDir") { project.layout.buildDirectory.dir("scriptsShadow").get().asFile }
+      task.conventionMapping.map("defaultJvmOpts", javaApplication::getApplicationDefaultJvmArgs)
     }
   }
 
   protected open fun configureInstallTask() {
-    project.tasks.named(SHADOW_INSTALL_TASK_NAME, Sync::class.java).configure { task ->
+    installShadowDist.configure { task ->
       val applicationName = project.providers.provider { javaApplication.applicationName }
 
       task.doFirst {
@@ -135,6 +134,9 @@ public abstract class ShadowApplicationPlugin : Plugin<Project> {
   protected val shadowJar: TaskProvider<ShadowJar>
     get() = project.tasks.named(ShadowJavaPlugin.SHADOW_JAR_TASK_NAME, ShadowJar::class.java)
 
+  private val installShadowDist: TaskProvider<Sync>
+    get() = project.tasks.named(SHADOW_INSTALL_TASK_NAME, Sync::class.java)
+
   public companion object {
     public const val SHADOW_RUN_TASK_NAME: String = "runShadow"
     public const val SHADOW_SCRIPTS_TASK_NAME: String = "startShadowScripts"


Test patch:
diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt
index e3892950..c0b7f858 100644
--- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt
+++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt
@@ -110,7 +110,7 @@ class ApplicationPluginTest : BasePluginTest() {
       dependenciesBlock = "shadow 'junit:junit:3.8.2'",
     )
 
-    run("shadowDistZip")
+    run(shadowDistZipTask)
 
     val zipPath = path("build/distributions/myapp-shadow-1.0.zip")
     val extractedPath = path("build/distributions/extracted/")
@@ -191,19 +191,29 @@ class ApplicationPluginTest : BasePluginTest() {
         }
       """.trimIndent(),
     )
-
-    val result = run(runShadowTask)
-
-    assertThat(result.output).all {
-      // Prefer main class from `application.main` over the one in manifest attributes.
-      contains("Hello, World! (foo) from Main")
-      doesNotContain("Hello, World! (foo) from Main2")
+    val assertions = { output: String, arg: String ->
+      assertThat(output).all {
+        // Prefer main class from `application.main` over the one in manifest attributes.
+        contains("Hello, World! ($arg) from Main")
+        doesNotContain("Hello, World! ($arg) from Main2")
+      }
     }
+
+    assertions(run(runShadowTask).output, "foo")
     commonAssertions(
       jarPath("build/install/myapp-shadow/lib/myapp-1.0-all.jar"),
       entriesContained = arrayOf("a.properties", "a2.properties", "shadow/Main.class", "shadow/Main2.class"),
       mainClassAttr = "shadow.Main2",
     )
+
+    projectScriptPath.appendText(
+      """
+        run {
+          args 'bar'
+        }
+      """.trimIndent(),
+    )
+    assertions(run(":run").output, "bar")
   }
 
   @Test
@@ -219,7 +229,7 @@ class ApplicationPluginTest : BasePluginTest() {
       """.trimIndent(),
     )
 
-    run("shadowDistZip")
+    run(shadowDistZipTask)
 
     val zipPath = path("build/distributions/myapp-shadow-1.0.zip")
     ZipFile(zipPath.toFile()).use { zip ->
@@ -235,6 +245,27 @@ class ApplicationPluginTest : BasePluginTest() {
     }
   }
 
+  @Test
+  fun canIncludeSrcDistByDefault() {
+    path("src/dist/echo.sh").writeText("echo 'Hello, World!'")
+    prepare()
+
+    run(shadowDistZipTask)
+
+    val zipPath = path("build/distributions/myapp-shadow-1.0.zip")
+    ZipFile(zipPath.toFile()).use { zip ->
+      val entries = zip.entries().toList().filter { !it.isDirectory }.map { it.name }
+      assertThat(entries).containsOnly(
+        "myapp-shadow-1.0/bin/myapp",
+        "myapp-shadow-1.0/bin/myapp.bat",
+        "myapp-shadow-1.0/lib/myapp-1.0-all.jar",
+        "myapp-shadow-1.0/echo.sh",
+      )
+      assertThat(zip.getContent("myapp-shadow-1.0/echo.sh"))
+        .isEqualTo("echo 'Hello, World!'")
+    }
+  }
+
   private fun prepare(
     mainClassWithImports: Boolean = false,
     projectBlock: String = "",
@@ -259,7 +290,7 @@ class ApplicationPluginTest : BasePluginTest() {
           args 'foo'
           $runShadowBlock
         }
-      """.trimIndent(),
+      """.trimIndent() + System.lineSeparator(),
     )
     settingsScriptPath.writeText(
       getDefaultSettingsBuildScript(
diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt
index a611a8cd..62fa1d6b 100644
--- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt
+++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt
@@ -82,6 +82,7 @@ abstract class BasePluginTest {
   val shadowJarTask = ":$SHADOW_JAR_TASK_NAME"
   val serverShadowJarTask = ":server:$SHADOW_JAR_TASK_NAME"
   val runShadowTask = ":$SHADOW_RUN_TASK_NAME"
+  val shadowDistZipTask = ":shadowDistZip"
 
   val projectScriptPath: Path get() = path("build.gradle")
   val settingsScriptPath: Path get() = path("settings.gradle")