Repository: GradleUp/shadow
Pull request #1197 (2025-01-26 06:10:52)

Replace `JavaJarExec` with `JavaExec`
---

- [x] [CHANGELOG](https://github.com/GradleUp/shadow/blob/main/src/docs/changes/README.md)'s "Unreleased" section has been updated, if applicable.


Goooler: @ljacomet Can you take a review for this? I noticed you did some adoption in #576.

Patch:
diff --git a/api/shadow.api b/api/shadow.api
index a8705785..ec3e5ccc 100644
--- a/api/shadow.api
+++ b/api/shadow.api
@@ -225,12 +225,6 @@ public abstract interface class com/github/jengelman/gradle/plugins/shadow/tasks
 	public abstract fun inheritFrom ([Ljava/lang/Object;Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest;
 }
 
-public abstract class com/github/jengelman/gradle/plugins/shadow/tasks/JavaJarExec : org/gradle/api/tasks/JavaExec {
-	public fun <init> ()V
-	public fun exec ()V
-	public abstract fun getJarFile ()Lorg/gradle/api/file/RegularFileProperty;
-}
-
 public abstract class com/github/jengelman/gradle/plugins/shadow/tasks/KnowsTask : org/gradle/api/DefaultTask {
 	public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/tasks/KnowsTask$Companion;
 	public static final field DESC Ljava/lang/String;
diff --git a/src/docs/application-plugin/README.md b/src/docs/application-plugin/README.md
index 442f5da0..113bfe11 100644
--- a/src/docs/application-plugin/README.md
+++ b/src/docs/application-plugin/README.md
@@ -40,7 +40,7 @@ application {
   mainClass = 'myapp.Main'
 }
 
-tasks.named('runShadow', com.github.jengelman.gradle.plugins.shadow.tasks.JavaJarExec) {
+tasks.named('runShadow', JavaExec) {
   args 'foo'
 }
 ```
diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md
index 8855acfc..14e37857 100644
--- a/src/docs/changes/README.md
+++ b/src/docs/changes/README.md
@@ -19,6 +19,10 @@
 - Support overriding `mainClass` provided by `JavaApplication`. ([#1182](https://github.com/GradleUp/shadow/pull/1182))
 - Fix `ShadowJar` not being successful after `includes` or `excludes` are changed. ([#1200](https://github.com/GradleUp/shadow/pull/1200))
 
+**Removed**
+
+- **BREAKING CHANGE:** Remove `JavaJarExec`, now use `JavaExec` directly for `runShadow` task. ([#1197](https://github.com/GradleUp/shadow/pull/1197))
+
 
 ## [v9.0.0-beta6] (2025-01-23)
 
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 998abe74..edc75fe9 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
@@ -1,7 +1,6 @@
 package com.github.jengelman.gradle.plugins.shadow
 
 import com.github.jengelman.gradle.plugins.shadow.internal.requireResourceAsText
-import com.github.jengelman.gradle.plugins.shadow.tasks.JavaJarExec
 import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
 import org.gradle.api.GradleException
 import org.gradle.api.Plugin
@@ -10,6 +9,7 @@ import org.gradle.api.distribution.DistributionContainer
 import org.gradle.api.plugins.ApplicationPlugin
 import org.gradle.api.plugins.JavaApplication
 import org.gradle.api.plugins.JavaPluginExtension
+import org.gradle.api.tasks.JavaExec
 import org.gradle.api.tasks.Sync
 import org.gradle.api.tasks.TaskProvider
 import org.gradle.api.tasks.application.CreateStartScripts
@@ -45,18 +45,18 @@ public abstract class ShadowApplicationPlugin : Plugin<Project> {
   }
 
   protected open fun addRunTask() {
-    project.tasks.register(SHADOW_RUN_TASK_NAME, JavaJarExec::class.java) {
+    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 ->
+        i.destinationDir.resolve("lib/${s.archiveFile.get().asFile.name}")
+      }
+
       it.dependsOn(install)
-      it.mainClass.set("-jar")
+      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 }
-      it.jarFile.fileProvider(
-        install.zip(shadowJar) { i, s ->
-          project.file("${i.destinationDir.path}/lib/${s.archiveFile.get().asFile.name}")
-        },
-      )
       val toolchain = project.extensions.getByType(JavaPluginExtension::class.java).toolchain
       val defaultLauncher = project.extensions.getByType(JavaToolchainService::class.java)
         .launcherFor(toolchain)
diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/JavaJarExec.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/JavaJarExec.kt
deleted file mode 100644
index 7e74dc7c..00000000
--- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/JavaJarExec.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-package com.github.jengelman.gradle.plugins.shadow.tasks
-
-import org.gradle.api.file.RegularFileProperty
-import org.gradle.api.tasks.InputFile
-import org.gradle.api.tasks.JavaExec
-import org.gradle.api.tasks.TaskAction
-
-public abstract class JavaJarExec : JavaExec() {
-  @get:InputFile
-  public abstract val jarFile: RegularFileProperty
-
-  @TaskAction
-  override fun exec() {
-    val allArgs = buildList {
-      add(jarFile.get().asFile.path)
-      // Must cast args to List<String> here to avoid type mismatch.
-      addAll(args as List<String>)
-    }
-    setArgs(allArgs)
-    super.exec()
-  }
-}


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 27a1f636..e3892950 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
@@ -1,8 +1,10 @@
 package com.github.jengelman.gradle.plugins.shadow
 
+import assertk.all
 import assertk.assertThat
 import assertk.assertions.contains
 import assertk.assertions.containsOnly
+import assertk.assertions.doesNotContain
 import assertk.assertions.isEqualTo
 import com.github.jengelman.gradle.plugins.shadow.ShadowApplicationPlugin.Companion.SHADOW_INSTALL_TASK_NAME
 import com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin.Companion.DISTRIBUTION_NAME
@@ -192,11 +194,14 @@ class ApplicationPluginTest : BasePluginTest() {
 
     val result = run(runShadowTask)
 
-    assertThat(result.output).contains(
-      "Hello, World! (foo) from Main2",
-    )
+    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")
+    }
     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",
     )
   }