Repository: GradleUp/shadow (kotlin)

Issue:
Create separate shadowJars for code and dependencies
Using the awesome shadow plugin version 4.0.2 and gradle 4.10, I wanted to create two separate shadowJars, one for my source code and another for my dependencies (since dependencies are large and rarely changes I don’t want to repackage them every time I change my source code). What I have in mind is to have a gradle plugin that add two separate tasks and which takes the same configurations supplied by user for shadowJar and overrides the configurations/sources used to create the shadowJar.

Below is what I have got so far, still trying to figure out a clean way to pass the shadow configs only once and whether there are other gotchas I need to worry about (ex: having two mergeServiceFiles will break etc)
```
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar

task dependencyShadowJar(type: ShadowJar) {
    mergeServiceFiles()
    zip64 = true
    relocate 'com.google.common', 'com.shadow.google.common'
    classifier = 'dependencies'
    configurations = [project.configurations.runtime]
}

task userCodeShadowJar(type: ShadowJar) {
    mergeServiceFiles()
    zip64 = true
    relocate 'com.google.common', 'com.shadow.google.common'
    classifier = 'mycode'
    from sourceSets.main.output
}

task splitShadowJar {
    doLast {
        println "Building separate src and dependency shadowJars"
    }
}

splitShadowJar.dependsOn dependencyShadowJar
dependencyShadowJar.dependsOn userCodeShadowJar
```
- Ideally I would like to have a shadowJar settings specified once and the tasks copies the same settings, does that require creating a custom Plugin task in groovy ? 
- Can I copy the settings from existing shadowJar that user specifies and just overrides the `from` or `configurations` part alone for my purpose

Anybody has attempted something similar ?

Pull request:
Closes #443.

Patch:
diff --git a/docs/custom-tasks/README.md b/docs/custom-tasks/README.md
index 762d868da..a9e56b138 100644
--- a/docs/custom-tasks/README.md
+++ b/docs/custom-tasks/README.md
@@ -11,7 +11,7 @@ the output.
     val testShadowJar by tasks.registering(com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar::class) {
       description = "Create a combined JAR of project and test dependencies"
 
-      archiveClassifier = "tests"
+      archiveClassifier = "test"
       from(sourceSets.test.map { it.output })
       configurations = project.configurations.testRuntimeClasspath.map { listOf(it) }
 
@@ -34,7 +34,7 @@ the output.
     def testShadowJar = tasks.register('testShadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) {
       description = 'Create a combined JAR of project and test dependencies'
 
-      archiveClassifier = 'tests'
+      archiveClassifier = 'test'
       from sourceSets.named('test').map { it.output }
       configurations = project.configurations.named('testRuntimeClasspath').map { [it] }
 
@@ -53,7 +53,36 @@ the output.
 
 The code snippet above will generate a shadowed JAR containing both the `main` and `test` sources as well as all
 `testRuntimeOnly` and `testImplementation` dependencies. The file is output to
-`build/libs/<project>-<version>-tests.jar`.
+`build/libs/<project>-<version>-test.jar`.
+
+## Creating a Dependencies-Only Shadow JAR
+
+It is also possible to create a shadow JAR that contains *only* the dependencies and none of the project's own
+source code. This is accomplished by creating a custom [`ShadowJar`][ShadowJar] task and configuring the
+[`configurations`][ShadowJar.configurations] property, but **not** adding any project sources with `from(...)`.
+
+=== "Kotlin"
+
+    ```kotlin
+    tasks.registering(com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar::class) {
+      description = "Create a shadow JAR of all dependencies"
+      archiveClassifier = "dep"
+      configurations = project.configurations.runtimeClasspath.map { listOf(it) }
+    }
+    ```
+
+=== "Groovy"
+
+    ```groovy
+    tasks.register('dependencyShadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) {
+      description = 'Create a shadow JAR of all dependencies'
+      archiveClassifier = 'dep'
+      configurations = project.configurations.named('runtimeClasspath').map { [it] }
+    }
+    ```
+
+The above configuration will create a shadow JAR file that contains only the classes from the `runtimeClasspath`
+configuration. The standard `jar` task will still produce a JAR with only the project's sources.
 
 
 [Jar]: https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html


Test patch:
diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt
index 7af448500..9cb826eba 100644
--- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt
+++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt
@@ -552,7 +552,7 @@ class JavaPluginTest : BasePluginTest() {
         }
         def $testShadowJarTask = tasks.register('$testShadowJarTask', ${ShadowJar::class.java.name}) {
           description = 'Create a combined JAR of project and test dependencies'
-          archiveClassifier = 'tests'
+          archiveClassifier = 'test'
           from sourceSets.named('test').map { it.output }
           configurations = project.configurations.named('testRuntimeClasspath').map { [it] }
           manifest {
@@ -564,7 +564,7 @@ class JavaPluginTest : BasePluginTest() {
 
     run(testShadowJarTask)
 
-    assertThat(jarPath("build/libs/my-1.0-tests.jar")).useAll {
+    assertThat(jarPath("build/libs/my-1.0-test.jar")).useAll {
       containsOnly(
         "my/",
         mainClassEntry,
@@ -574,7 +574,7 @@ class JavaPluginTest : BasePluginTest() {
       getMainAttr(mainClassAttributeKey).isNotNull()
     }
 
-    val pathString = path("build/libs/my-1.0-tests.jar").toString()
+    val pathString = path("build/libs/my-1.0-test.jar").toString()
     val runningOutput = runProcess("java", "-jar", pathString, "foo")
     assertThat(runningOutput).contains(
       "Hello, World! (foo) from Main",
@@ -582,6 +582,46 @@ class JavaPluginTest : BasePluginTest() {
     )
   }
 
+  @Issue(
+    "https://github.com/GradleUp/shadow/issues/443",
+  )
+  @Test
+  fun registerCustomShadowJarTaskThatContainsDependenciesOnly() {
+    val mainClassEntry = writeClass()
+    val dependencyShadowJar = "dependencyShadowJar"
+
+    projectScriptPath.appendText(
+      """
+        dependencies {
+          implementation 'junit:junit:3.8.2'
+        }
+        def $dependencyShadowJar = tasks.register('$dependencyShadowJar', ${ShadowJar::class.java.name}) {
+          description = 'Create a shadow JAR of all dependencies'
+          archiveClassifier = 'dep'
+          configurations = project.configurations.named('runtimeClasspath').map { [it] }
+        }
+      """.trimIndent(),
+    )
+
+    run("jar", dependencyShadowJar)
+
+    assertThat(jarPath("build/libs/my-1.0.jar")).useAll {
+      containsOnly(
+        "my/",
+        mainClassEntry,
+        *manifestEntries,
+      )
+      transform { it.mainAttrSize }.isEqualTo(1)
+    }
+    assertThat(jarPath("build/libs/my-1.0-dep.jar")).useAll {
+      containsOnly(
+        *junitEntries,
+        *manifestEntries,
+      )
+      transform { it.mainAttrSize }.isEqualTo(1)
+    }
+  }
+
   @Issue(
     "https://github.com/GradleUp/shadow/issues/915",
   )