shadow Documentation

Gradle plugin to create fat/uber JARs, apply file transforms, and relocate packages for applications and libraries. Gradle version of Maven's Shade plugin.

4,196 stars Kotlin Kotlin 1 file ยท ~19,262 tokens #build#bundling#fat-jar#fatjar#gradle#gradle-plugin#groovy#hacktoberfest
RAW Doc

1. Project Overview & Quickstart (GradleUp/shadow)

File: README.md

Shadow

A Gradle plugin for creating fat/uber JARs, transforming files, relocating packages, and optimizing applications
with R8/ProGuard. The Gradle counterpart to Maven Shade Plugin.




[!NOTE]
Previously this plugin was developed by @johnrengelman and published under the
ID com.github.johnrengelman.shadow
before maintenance was transferred to the GradleUp organization to ensure future
development, see #908.

If you are still using the old plugin ID in your build script, we recommend to switch to the new plugin ID
com.gradleup.shadow

and update to the latest version to receive all the latest bug fixes and improvements.

Documentation

Compatibility Matrix

Shadow Version Min Gradle Version Min Java Version Plugin ID
8.0.0+ 8.0 8 com.github.johnrengelman.shadow
8.3.0+ 8.3 8 com.gradleup.shadow
9.0.0+ 8.11 11 com.gradleup.shadow
9.2.0+ 8.11 17 com.gradleup.shadow
9.3.0+ 9.0 17 com.gradleup.shadow
9.5.0+ 9.2 17 com.gradleup.shadow
9.7.0+ 9.4 17 com.gradleup.shadow

File: docs/publishing/README.md

Publishing Shadow JARs

Publishing with Maven-Publish Plugin

The Shadow plugin will automatically configure the necessary tasks in the presence of Gradle's
maven-publish plugin.
The plugin provides the shadow component to configure the publication with the necessary
artifact and dependencies in the POM file.

=== "Kotlin"

text
```kotlin
plugins {
  java
  `maven-publish`
  id("com.gradleup.shadow")
}

publishing {
  publications {
    create<MavenPublication>("shadow") {
      from(components["shadow"])
    }
  }
  repositories {
    maven("https://repo.myorg.com")
  }
}
```

=== "Groovy"

text
```groovy
plugins {
  id 'java'
  id 'maven-publish'
  id 'com.gradleup.shadow'
}

publishing {
  publications {
    shadow(MavenPublication) {
      from components.shadow
    }
  }
  repositories {
    maven { url = 'https://repo.myorg.com' }
  }
}
```

Shadow Variant in Default Java Component

The Shadow plugin adds an optional variant to the java component when publishing. This variant contains the shadowed
JAR. This allows consumers of the published library to choose between the standard JAR and the shadowed JAR.

This feature is enabled by default. It can be disabled by setting the addShadowVariantIntoJavaComponent property in
the shadow extension to false. If you want to publish the standard JAR only, disable this feature like:

=== "Kotlin"

text
```kotlin
plugins {
  java
  `maven-publish`
  id("com.gradleup.shadow")
}

shadow {
  addShadowVariantIntoJavaComponent = false
}

publishing {
  publications {
    create<MavenPublication>("shadow") {
      from(components["shadow"])
    }
  }
  repositories {
    maven("https://repo.myorg.com")
  }
}
```

=== "Groovy"

text
```groovy
plugins {
  id 'java'
  id 'maven-publish'
  id 'com.gradleup.shadow'
}

shadow {
  addShadowVariantIntoJavaComponent = false
}

publishing {
  publications {
    shadow(MavenPublication) {
      from components.shadow
    }
  }
  repositories {
    maven { url = 'https://repo.myorg.com' }
  }
}
```

The target JVM version attribute (org.gradle.jvm.version) of the shadowed variant is added by default, it is useful
for consumers to select the correct variant based on their target JVM version. But it may cause issues in some cases,
you can disable this by setting the addTargetJvmVersionAttribute property in the shadow extension to false:

=== "Kotlin"

text
```kotlin
shadow {
  addTargetJvmVersionAttribute = false
}
```

=== "Groovy"

text
```groovy
shadow {
  addTargetJvmVersionAttribute = false
}
```

The BUNDLING attribute (org.gradle.dependency.bundling) of the shadowed variant is set to shadowed by default,
it is useful for consumers to distinguish between normal and shadowed dependencies. You can override this attribute by
setting the bundlingAttribute property in the shadow extension:

=== "Kotlin"

text
```kotlin
shadow {
  // Per description of the attribute, you should set it to either `Bundling.SHADOWED` or `Bundling.EMBEDDED`.
  bundlingAttribute = Bundling.EMBEDDED
}
```

=== "Groovy"

text
```groovy
shadow {
  // Per description of the attribute, you should set it to either `Bundling.SHADOWED` or `Bundling.EMBEDDED`.
  bundlingAttribute = Bundling.EMBEDDED
}
```

Shadow Configuration and Publishing

The Shadow plugin provides a custom configuration (configurations.shadow) to specify
runtime dependencies that are not merged into the final JAR file.
When configuring publishing with the Shadow plugin, the dependencies in the shadow
configuration, are translated to become RUNTIME scoped dependencies of the
published artifact.

No other dependencies are automatically configured for inclusion in the POM file.
For example, excluded dependencies are not automatically added to the POM file or
if the configuration for merging are modified by specifying
shadowJar.configurations = [configurations.myConfiguration], there is no automatic
configuration of the POM file.

This automatic configuration occurs only when using the above methods for
configuring publishing. If this behavior is not desirable, then publishing must
be manually configured.

=== "Kotlin"

text
```kotlin
plugins {
  java
  `maven-publish`
  id("com.gradleup.shadow")
}

dependencies {
  // This will be bundled in the shadowed JAR and not declared in the POM.
  implementation("com.squareup.retrofit2:retrofit:<version>")
  // This will be excluded from the shadowed JAR but declared as a runtime dependency in `META-INF/MANIFEST.MF`
  // file's `Class-Path` entry, and also in the POM file.
  shadow("com.squareup.retrofit2:converter-java8:<version>")
  // This will be excluded from the shadowed JAR and not declared in the POM or `META-INF/MANIFEST.MF`.
  compileOnly("com.squareup.retrofit2:converter-scalars:<version>")
}

publishing {
  publications {
    create<MavenPublication>("shadow") {
      from(components["shadow"])

      // Optionally, you can add extra dependencies to the POM file like the following:
      pom.withXml {
        val dependenciesNode = asNode().get("dependencies") ?: asNode().appendNode("dependencies")
        val node = (dependenciesNode as groovy.util.Node).appendNode("dependency")
        node.appendNode("groupId", "com.squareup.retrofit2")
        node.appendNode("artifactId", "converter-gson")
        node.appendNode("version", "<version>")
        node.appendNode("scope", "runtime")
      }
    }
  }
  repositories {
    maven("https://repo.myorg.com")
  }
}
```

=== "Groovy"

text
```groovy
plugins {
  id 'java'
  id 'maven-publish'
  id 'com.gradleup.shadow'
}

dependencies {
  // This will be bundled in the shadowed JAR and not declared in the POM.
  implementation "com.squareup.retrofit2:retrofit:<version>"
  // This will be excluded from the shadowed JAR but declared as a runtime dependency in `META-INF/MANIFEST.MF`
  // file's `Class-Path` entry, and also in the POM file.
  shadow "com.squareup.retrofit2:converter-java8:<version>"
  // This will be excluded from the shadowed JAR and not declared in the POM or `META-INF/MANIFEST.MF`.
  compileOnly "com.squareup.retrofit2:converter-scalars:<version>"
}

publishing {
  publications {
    shadow(MavenPublication) {
      from components.shadow

      // Optionally, you can add extra dependencies to the POM file like the following:
      pom.withXml { xml ->
        def dependenciesNode = xml.asNode().get('dependencies') ?: xml.asNode().appendNode('dependencies')
        def node = dependenciesNode.appendNode('dependency')
        node.appendNode('groupId', 'com.squareup.retrofit2')
        node.appendNode('artifactId', 'converter-gson')
        node.appendNode('version', '<version>')
        node.appendNode('scope', 'runtime')
      }
    }
  }
  repositories {
    maven { url = 'https://repo.myorg.com' }
  }
}
```

Publishing the Shadowed JAR instead of the Original JAR

You may want to publish the shadowed JAR instead of the original JAR. This can be done by trimming
the archiveClassifier of the shadowed JAR like the following:

=== "Kotlin"

text
```kotlin
plugins {
  java
  `maven-publish`
  id("com.gradleup.shadow")
}

tasks.shadowJar {
  archiveClassifier = ""
}

publishing {
  publications {
    create<MavenPublication>("shadow")
  }
  repositories {
    maven("https://repo.myorg.com")
  }
}
```

=== "Groovy"

text
```groovy
plugins {
  id 'java'
  id 'maven-publish'
  id 'com.gradleup.shadow'
}

tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) {
  archiveClassifier = ''
}

publishing {
  publications {
    shadow(MavenPublication)
  }
  repositories {
    maven { url = 'https://repo.myorg.com' }
  }
}
```

Because the default archiveClassifier of Jar is "" (empty), setting the archiveClassifier of
ShadowJar to "" (empty) will make collisions between the outputs of these two tasks in some cases.
If you don't need the standard JAR, you can disable the jar task like:

=== "Kotlin"

text
```kotlin
tasks.jar {
  enabled = false
}
```

=== "Groovy"

text
```groovy
tasks.named('jar', Jar) {
  enabled = false
}
```

Or set a different archiveClassifier for the standard Jar like:

=== "Kotlin"

text
```kotlin
tasks.jar {
  archiveClassifier = "ignored"
}
```

=== "Groovy"

text
```groovy
tasks.named('jar', Jar) {
  archiveClassifier = 'ignored'
}
```

Publishing the Shadowed Gradle Plugins

The Gradle Publish Plugin introduced support for plugins packaged with Shadow in version 1.0.0.
Starting with this version, plugin projects that apply both Shadow and the Gradle Plugin Publish plugin will be
automatically configured to publish the output of the ShadowJar tasks as the consumable artifact for the
plugin. See the
Gradle Plugin Publish docs
for details. The only thing you need to do from the Shadow side is to empty the archiveClassifier like:

=== "Kotlin"

text
```kotlin
plugins {
  id("com.gradle.plugin-publish") version "latest"
  id("com.gradleup.shadow")
}

dependencies {
  // Your plugin dependencies.
}

tasks.shadowJar {
  archiveClassifier = ""
}
```

=== "Groovy"

text
```groovy
plugins {
  id 'com.gradle.plugin-publish' version 'latest'
  id 'com.gradleup.shadow'
}

dependencies {
  // Your plugin dependencies.
}

tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) {
  archiveClassifier = ''
}
```

Publishing Custom ShadowJar Task Outputs

It is possible to publish a custom ShadowJar task's output via the
MavenPublication.artifact() method.

=== "Kotlin"

text
```kotlin
plugins {
  java
  `maven-publish`
  id("com.gradleup.shadow")
}

val testShadowJar = tasks.register<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar>("testShadowJar") {
  description = "Create a combined JAR of project and test dependencies"
  archiveClassifier = "tests"
  from(sourceSets.test.map { it.output })
  configurations = project.configurations.testRuntimeClasspath.map { listOf(it) }
}

dependencies {
  testImplementation("junit:junit:3.8.2")
}

publishing {
  publications {
    create<MavenPublication>("shadow") {
      artifact(testShadowJar)
    }
  }
  repositories {
    maven("https://repo.myorg.com")
  }
}
```

=== "Groovy"

text
```groovy
plugins {
  id 'java'
  id 'maven-publish'
  id 'com.gradleup.shadow'
}

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'
  from sourceSets.named('test').map { it.output }
  configurations = project.configurations.named('testRuntimeClasspath').map { [it] }
}

dependencies {
  testImplementation 'junit:junit:3.8.2'
}

publishing {
  publications {
    shadow(MavenPublication) {
      artifact(testShadowJar)
    }
  }
  repositories {
    maven { url = 'https://repo.myorg.com' }
  }
}
```

Publishing the Shadowed JAR with Custom Artifact Name

It is possible to configure the artifact name of the shadowed JAR via properties like archiveBaseName, see more
customizable properties listed in Configuring Output Name. e.g.

=== "Kotlin"

text
```kotlin
plugins {
  java
  `maven-publish`
  id("com.gradleup.shadow")
}

group = "my-group"
version = "1.0"

tasks.shadowJar {
  archiveClassifier = "my-classifier"
  archiveExtension = "my-ext"
  archiveBaseName = "maven-all"
}

publishing {
  publications {
    create<MavenPublication>("shadow") {
      from(components["shadow"])
      // This will override `archiveBaseName`.
      artifactId = "my-artifact"
    }
  }
  repositories {
    maven("https://repo.myorg.com")
  }
}
```

=== "Groovy"

text
```groovy
plugins {
  id 'java'
  id 'maven-publish'
  id 'com.gradleup.shadow'
}

group = 'my-group'
version = '1.0'

tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) {
  archiveClassifier = 'my-classifier'
  archiveExtension = 'my-ext'
  archiveBaseName = 'maven-all'
}

publishing {
  publications {
    shadow(MavenPublication) {
      from components.shadow
      // This will override `archiveBaseName`.
      artifactId = 'my-artifact'
    }
  }
  repositories {
    maven { url = 'https://repo.myorg.com' }
  }
}
```

We modified archiveClassifier, archiveExtension and archiveBaseName in this example, the published artifact will
be named my-artifact-2.0-my-classifier.my-ext instead of 1.0-all.jar.


File: docs/multi-project/README.md

Using Shadow in Multi-Project Builds

When using Shadow in a multi-project build, project dependencies will be treated the same as
external dependencies.
That is a project dependency will be merged into the ShadowJar output of the project that
is applying the Shadow plugin.

Depending on the Shadow Jar from Another Project

In a multi-project build there may be one project that applies Shadow and another that
requires the shadowed JAR as a dependency.
In this case, use Gradle's normal dependency declaration mechanism to depend on the shadow
configuration of the shadowed project.

=== "Kotlin"

text
```kotlin
dependencies {
  implementation(project(path = ":api", configuration = "shadow"))
}
```

=== "Groovy"

text
```groovy
dependencies {
  implementation project(path: ':api', configuration: 'shadow')
}
```

Making the Shadowed JAR the Default Artifact

When a project needs to expose the shadowed JAR as its default output โ€” so that consumers can depend on it without
specifying the shadow configuration explicitly โ€” you can reconfigure the consumable configurations apiElements and
runtimeElements to publish the shadowed JAR instead of the regular JAR.

As a reminder, configurations like api and implementation are where dependencies are declared (declarable), while
apiElements and runtimeElements are what Gradle consumes when projects depend on each other.

By tuning these consumable configurations, a simple declaration like implementation(project(":api")) will resolve to
the shadowed JAR by default, preventing accidental consumption of the unshadowed artifact.

In the shadowed project (:api):

=== "Kotlin"

text
```kotlin
plugins {
  `java-library`
  id("com.gradleup.shadow")
}

configurations {
  named("apiElements") {
    outgoing.artifacts.clear()
    outgoing.variants.clear()
    outgoing.artifact(tasks.shadowJar)
  }
  named("runtimeElements") {
    outgoing.artifacts.clear()
    outgoing.variants.clear()
    outgoing.artifact(tasks.shadowJar)
  }
}
```

=== "Groovy"

text
```groovy
plugins {
  id 'java-library'
  id 'com.gradleup.shadow'
}

configurations {
  apiElements {
    outgoing.artifacts.clear()
    outgoing.variants.clear()
    outgoing.artifact(tasks.named('shadowJar'))
  }
  runtimeElements {
    outgoing.artifacts.clear()
    outgoing.variants.clear()
    outgoing.artifact(tasks.named('shadowJar'))
  }
}
```

!!! important

text
Clearing `outgoing.variants` ensures Gradle doesn't select the unshadowed `classes` variant by default during compilation.

Consuming projects can then depend on :api without specifying the shadow configuration:

=== "Kotlin"

text
```kotlin
dependencies {
  implementation(project(":api"))
}
```

=== "Groovy"

text
```groovy
dependencies {
  implementation project(':api')
}
```

Excluding Transitive Dependencies

If you want to exclude transitive dependencies that were bundled into the shadow JAR, you can add exclude rules to the
configurations as well:

=== "Kotlin"

text
```kotlin
configurations {
  named("apiElements") {
    outgoing.artifacts.clear()
    outgoing.variants.clear()
    outgoing.artifact(tasks.shadowJar)
    exclude(group = "com.example", module = "bundled-library")
  }
  named("runtimeElements") {
    outgoing.artifacts.clear()
    outgoing.variants.clear()
    outgoing.artifact(tasks.shadowJar)
    exclude(group = "com.example", module = "bundled-library")
  }
}
```

=== "Groovy"

text
```groovy
configurations {
  apiElements {
    outgoing.artifacts.clear()
    outgoing.variants.clear()
    outgoing.artifact(tasks.named('shadowJar'))
    exclude group: 'com.example', module: 'bundled-library'
  }
  runtimeElements {
    outgoing.artifacts.clear()
    outgoing.variants.clear()
    outgoing.artifact(tasks.named('shadowJar'))
    exclude group: 'com.example', module: 'bundled-library'
  }
}
```

File: docs/kotlin-plugins/README.md

Integrating with Kotlin Plugins

Kotlin standard libraries (stdlib) are added by Kotlin plugins by default via implementation (runtimeClasspath),
they will be bundled into the shadowed JARs automatically.
If you don't need a standard library at all, you can add the following Gradle property to your gradle.properties file:

properties
kotlin.stdlib.default.dependency=false

Kotlin compilations may still require the standard libraries, you can add them into compileOnly (compileClasspath)
to make sure compilations success and avoid shadowing as follows:

=== "Kotlin"

text
```kotlin
dependencies {
  compileOnly("org.jetbrains.kotlin:kotlin-stdlib:<version>")
}
```

=== "Groovy"

text
```groovy
dependencies {
  compileOnly 'org.jetbrains.kotlin:kotlin-stdlib:<version>'
}
```

See more information about
Dependency on the standard library.

For Kotlin JVM Plugin

Shadow works well for Kotlin JVM projects like Java projects. Here is an example:

=== "Kotlin"

text
```kotlin
plugins {
  id("org.jetbrains.kotlin.jvm")
  id("com.gradleup.shadow")
}

dependencies {
  implementation("io.ktor:ktor-client-okhttp:3.1.0")
}
```

=== "Groovy"

text
```groovy
plugins {
  id 'org.jetbrains.kotlin.jvm'
  id 'com.gradleup.shadow'
}

dependencies {
  implementation 'io.ktor:ktor-client-okhttp:3.1.0'
}
```

You can mix the Kotlin JVM plugin with java-gradle-plugin, application, and other Java plugins,
easily organize your build logic for Publishing Libraries,
Running Applications, and so on.

For Kotlin Multiplatform Plugin

Shadow honors Kotlin's org.jetbrains.kotlin.multiplatform plugin and will
automatically configure additional tasks for bundling the shadowed JAR for its jvm target.

=== "Kotlin"

text
```kotlin
plugins {
  id("org.jetbrains.kotlin.multiplatform")
  id("com.gradleup.shadow")
}

kotlin {
  jvm().mainRun {
    // Optionally, set the main class for `runJvm`.
    mainClass = "myapp.MainKt"
  }
  sourceSets {
    commonMain {
      dependencies {
        implementation("io.ktor:ktor-client-core:<version>")
      }
    }
    jvmMain {
      dependencies {
        implementation("io.ktor:ktor-client-okhttp:<version>")
      }
    }
  }
}

// TODO: Gradle doesn't generate accessors for this use case, so we can't call `tasks.shadowJar` directly like the other examples.
tasks.named<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar>("shadowJar") {
  manifest {
    // Optionally, set the main class for the shadowed JAR.
    attributes["Main-Class"] = "com.example.MainKt"
  }
}
```

=== "Groovy"

text
```groovy
plugins {
  id 'org.jetbrains.kotlin.multiplatform'
  id 'com.gradleup.shadow'
}

kotlin {
  jvm().mainRun {
    // Optionally, set the main class for `runJvm`.
    it.mainClass.set('myapp.MainKt')
  }
  sourceSets {
    commonMain {
      dependencies {
        implementation "io.ktor:ktor-client-core:<version>"
      }
    }
    jvmMain {
      dependencies {
        implementation "io.ktor:ktor-client-okhttp:<version>"
      }
    }
  }
}

tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) {
  manifest {
    // Optionally, set the main class for the shadowed JAR.
    attributes 'Main-Class': 'com.example.MainKt'
  }
}
```

Kotlin Module Metadata Remapping

Kotlin module metadata (.kotlin_module) files contain information about package parts and facades. When relocating
classes, Shadow automatically relocates the packages and facades inside the Kotlin module metadata files. This feature
is enabled by default via the deprecated enableKotlinModuleRemapping property.

To explicitly apply this remapping (recommended for future compatibility), add
KotlinModuleMetadataTransformer to your task configuration:

=== "Kotlin"

text
```kotlin
tasks.shadowJar {
  transform(com.github.jengelman.gradle.plugins.shadow.transformers.KotlinModuleMetadataTransformer::class.java)
}
```

=== "Groovy"

text
```groovy
tasks.shadowJar {
  transform(com.github.jengelman.gradle.plugins.shadow.transformers.KotlinModuleMetadataTransformer)
}
```

File: docs/groovy-and-scala-plugins/README.md

Integrating with Groovy and Scala Plugins

Shadow also works well for Groovy and Scala, here are integration examples:

For Groovy:

=== "Kotlin"

text
```kotlin
plugins {
  groovy
  id("com.gradleup.shadow")
}

tasks.shadowJar {
  manifest {
    // Optionally, set the main class for the shadowed JAR.
    attributes["Main-Class"] = "com.example.Main"
  }
}
```

=== "Groovy"

text
```groovy
plugins {
  id 'groovy'
  id 'com.gradleup.shadow'
}

tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) {
  manifest {
    // Optionally, set the main class for the shadowed JAR.
    attributes 'Main-Class': 'com.example.Main'
  }
}
```

For Scala:

=== "Kotlin"

text
```kotlin
plugins {
  scala
  id("com.gradleup.shadow")
}

tasks.shadowJar {
  manifest {
    // Optionally, set the main class for the shadowed JAR.
    attributes["Main-Class"] = "com.example.Main"
  }
}
```

=== "Groovy"

text
```groovy
plugins {
  id 'scala'
  id 'com.gradleup.shadow'
}

tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) {
  manifest {
    // Optionally, set the main class for the shadowed JAR.
    attributes 'Main-Class': 'com.example.Main'
  }
}
```

You can customize the other configurations of the shadowJar task as needed, just like with Java projects.


File: docs/getting-started/README.md

Getting Started

=== "Kotlin"

text
```kotlin
plugins {
  java
  id("com.gradleup.shadow") version "<version>"
}
```

=== "Groovy"

text
```groovy
plugins {
  id 'java'
  id 'com.gradleup.shadow' version '<version>'
}
```

Alternatively, the plugin can be added to the buildscript classpath and applied:

=== "Kotlin"

text
```kotlin
buildscript {
  repositories {
    mavenCentral()
    gradlePluginPortal()
  }
  dependencies {
    classpath("com.gradleup.shadow:shadow-gradle-plugin:<version>")
  }
}
// `apply plugin` stuff are used with `buildscript`.
apply(plugin = "java")
apply(plugin = "com.gradleup.shadow")
```

=== "Groovy"

text
```groovy
buildscript {
  repositories {
    mavenCentral()
    gradlePluginPortal()
  }
  dependencies {
    classpath 'com.gradleup.shadow:shadow-gradle-plugin:<version>'
  }
}
// `apply plugin` stuff are used with `buildscript`.
apply plugin: 'java'
apply plugin: 'com.gradleup.shadow'
```

===! "Kotlin"

text
Snapshots of the development version are available in Central Portal Snapshots.



```kotlin
buildscript {
  repositories {
    mavenCentral()
    maven("https://central.sonatype.com/repository/maven-snapshots/")
  }
  dependencies {
    // You can get the latest snapshot version from `VERSION_NAME` declared in https://github.com/GradleUp/shadow/blob/main/gradle.properties
    classpath("com.gradleup.shadow:shadow-gradle-plugin:<version>")
  }
}
// `apply plugin` stuff are used with `buildscript`.
apply(plugin = "java")
apply(plugin = "com.gradleup.shadow")
```

=== "Groovy"

text
Snapshots of the development version are available in Central Portal Snapshots.



```groovy
buildscript {
  repositories {
    mavenCentral()
    maven { url = 'https://central.sonatype.com/repository/maven-snapshots/' }
  }
  dependencies {
    // You can get the latest snapshot version from `VERSION_NAME` declared in https://github.com/GradleUp/shadow/blob/main/gradle.properties
    classpath 'com.gradleup.shadow:shadow-gradle-plugin:<version>'
  }
}
// `apply plugin` stuff are used with `buildscript`.
apply plugin: 'java'
apply plugin: 'com.gradleup.shadow'
```

!!! note

text
The correct maven coordinates for each version of Shadow can be found by referencing the Gradle Plugin
documentation [here](https://plugins.gradle.org/plugin/com.gradleup.shadow).

Shadow is a reactive plugin.
This means that applying Shadow by itself will perform no configuration on your project.
Instead, Shadow reacts
This means, that for most users, the java or groovy plugins must be explicitly applied
to have the desired effect.

Default Java/Kotlin/Groovy Tasks

In the presence of the java, org.jetbrains.kotlin.jvm or groovy plugins (that apply JavaPlugin
in their build logic), Shadow will automatically configure the following behavior:

  • Adds a ShadowJar task to the project.
  • Adds a shadow configuration to the project.
  • Adds a shadow variant to the project.
  • Adds a shadow component to the project.
  • Configures the ShadowJar task to include all sources from the project's main sourceSet.
  • Configures the ShadowJar task to bundle all dependencies from the runtimeClasspath configuration.
  • Configures the classifier attribute of the ShadowJar task to be 'all' .
  • Configures the ShadowJar task to generate a Manifest with:
    • Inheriting all configuration from the standard Jar task.
    • Adds a Class-Path attribute to the Manifest that appends all dependencies from the shadow configuration
  • Configures the ShadowJar task to exclude any JAR index or cryptographic signature files matching the
    following patterns:
    • META-INF/INDEX.LIST
    • META-INF/*.SF
    • META-INF/*.DSA
    • META-INF/*.RSA
    • META-INF/versions/**/module-info.class
    • module-info.class
  • Creates and registers the shadow component in the project (used for integrating with
    maven-publish).

ShadowJar Command Line options

Sometimes, a user wants to declare the value of an exposed task property on the command line instead of the
build script. Passing property values on the command line is particularly helpful if they change more frequently.
Here are the options that can be passed to the shadowJar:

text
--add-multi-release-attribute         Adds the multi-release attribute to the manifest if any dependencies contain it.
--no-add-multi-release-attribute      Disables option --add-multi-release-attribute.
--enable-auto-relocation              Enables auto relocation of packages in the dependencies.
--no-enable-auto-relocation           Disables option --enable-auto-relocation.
--enable-kotlin-module-remapping      Enables remapping of Kotlin module metadata files.
--no-enable-kotlin-module-remapping   Disables option --enable-kotlin-module-remapping.
--fail-on-duplicate-entries           Fails build if the ZIP entries in the shadowed JAR are duplicate.
--no-fail-on-duplicate-entries        Disables option --fail-on-duplicate-entries.
--main-class                          Main class attribute to add to manifest.
--minimize-jar                        Minimizes the jar by removing unused classes.
--no-minimize-jar                     Disables option --minimize-jar.
--relocation-prefix                   Prefix used for auto relocation of packages in the dependencies.
--rerun                               Causes the task to be re-run even if up-to-date.

Also, you can view more information about the ShadowJar task by running the following command:

shell
./gradlew -q help --task shadowJar

Refer to
listing command line options.


File: docs/custom-tasks/README.md

Creating a Custom ShadowJar Task

The built in ShadowJar task only provides an output for the main source set of the project.
It is possible to add arbitrary ShadowJar tasks to a project. When doing so, ensure that the
configurations property is specified to inform Shadow which dependencies to merge into
the output.

=== "Kotlin"

text
```kotlin
val testShadowJar = tasks.register<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar>("testShadowJar") {
  description = "Create a combined JAR of project and test dependencies"

  archiveClassifier = "test"
  from(sourceSets.test.map { it.output })
  configurations = project.configurations.testRuntimeClasspath.map { listOf(it) }

  manifest {
    // Optionally, set the main class for the JAR.
    attributes(mapOf("Main-Class" to "test.Main"))
    // You can also set other attributes here.
  }
}

// Optionally, make the `assemble` task depend on the new task.
tasks.assemble {
  dependsOn(testShadowJar)
}
```

=== "Groovy"

text
```groovy
def testShadowJar = tasks.register('testShadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) {
  description = 'Create a combined JAR of project and test dependencies'

  archiveClassifier = 'test'
  from sourceSets.named('test').map { it.output }
  configurations = project.configurations.named('testRuntimeClasspath').map { [it] }

  manifest {
    // Optionally, set the main class for the JAR.
    attributes 'Main-Class': 'test.Main'
    // You can also set other attributes here.
  }
}

// Optionally, make the `assemble` task depend on the new task.
tasks.named('assemble') {
  dependsOn testShadowJar
}
```

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/-<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 task and configuring the
configurations property, but not adding any project sources with from(...).

=== "Kotlin"

text
```kotlin
val dependencyShadowJar = tasks.register<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar>("dependencyShadowJar") {
  description = "Create a shadow JAR of all dependencies"
  archiveClassifier = "dep"
  configurations = project.configurations.runtimeClasspath.map { listOf(it) }
}
```

=== "Groovy"

text
```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.


File: docs/configuration/minimizing/README.md

Minimizing

Shadow can automatically remove all JARs and classes of dependencies that are not used by the project, thereby
minimizing the resulting shadowed JAR.

=== "Kotlin"

text
```kotlin
tasks.shadowJar {
  minimize()
}
```

=== "Groovy"

text
```groovy
tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) {
  minimize()
}
```

A dependency can be excluded from the minimization process, thereby forcing its inclusion the shadow JAR.
This is useful when the dependency analyzer cannot find the usage of a class programmatically, for example if the class
is loaded dynamically via Class.forName(String). Each of the group, name and version fields separated by : of
a dependency is interpreted as a regular expression.

=== "Kotlin"

text
```kotlin
tasks.shadowJar {
  minimize {
    exclude(dependency("org.scala-lang:.*:.*"))
  }
}
```

=== "Groovy"

text
```groovy
tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) {
  minimize {
    exclude(dependency('org.scala-lang:.*:.*'))
  }
}
```

!!! note

text
Dependencies scoped as `api` will be automatically excluded from minimization and used as "entry points" on
minimization.

Similar to ShadowJar.dependencies, projects can also be excluded.

=== "Kotlin"

text
```kotlin
tasks.shadowJar {
  minimize {
    exclude(project(":api"))
  }
}
```

=== "Groovy"

text
```groovy
tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) {
  minimize {
    exclude(project(":api"))
  }
}
```

!!! note

text
When excluding a `project`, all dependencies of the excluded `project` are automatically excluded from
minimization as well.

Minimizing with R8

Shadow can also run R8 over the final shadowed JAR. This is useful when you want
whole-program shrinking instead of the default dependency analyzer. R8 runs after Shadow has merged, transformed, and
relocated the JAR, so service descriptors in META-INF/services are used to keep service providers.

The default R8 configuration only shrinks unused code. It disables name minification and optimization.
R8 also applies rules published in dependency JARs, for example under META-INF/proguard.

=== "Kotlin"

text
```kotlin
repositories {
  google()
}

tasks.shadowJar {
  minimize {
    r8 {
      // Optional extra configuration
      proguardRules.add("-keep class com.example.ReflectiveApi { *; }")
      proguardRuleFiles.from(layout.projectDirectory.file("r8-rules.pro"))
      configurationFile.set(layout.buildDirectory.file("r8/configuration.txt"))
    }
  }
}
```

=== "Groovy"

text
```groovy
repositories {
  google()
}

tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) {
  minimize {
    r8 {
      // Optional extra configuration
      proguardRules.add('-keep class com.example.ReflectiveApi { *; }')
      proguardRuleFiles.from(layout.projectDirectory.file('r8-rules.pro'))
      configurationFile.set(layout.buildDirectory.file('r8/configuration.txt'))
    }
  }
}
```

R8 writes the collective ProGuard configuration it used to build/shadowJar/r8/configuration.txt by default. Shadow
passes this location to R8 with --pg-conf-output. Set configurationFile to retain it elsewhere.

R8 also supports ProGuard reporting options such as

Add them as proguardRules when you want to retain name mappings, matched keep rules, or removed code:

=== "Kotlin"

text
```kotlin
repositories {
  google()
}

tasks.shadowJar {
  minimize {
    r8 {
      enableObfuscation()
      configurationFile.set(layout.buildDirectory.file("r8/configuration.txt"))
      proguardRules.addAll(
        "-printmapping reports/mapping.txt",
        "-printseeds reports/seeds.txt",
        "-printusage reports/usage.txt",
      )
    }
  }
}
```

=== "Groovy"

text
```groovy
repositories {
  google()
}

tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) {
  minimize {
    r8 {
      enableObfuscation()
      configurationFile.set(layout.buildDirectory.file('r8/configuration.txt'))
      proguardRules.addAll(
        '-printmapping reports/mapping.txt',
        '-printseeds reports/seeds.txt',
        '-printusage reports/usage.txt',
      )
    }
  }
}
```

Relative report paths are resolved from the directory containing configurationFile. The example above writes the
reports under build/r8/reports. Use absolute paths if the reports must be written independently of the configuration
file location. This behavior follows
R8's configuration parser.
-printmapping only contains renamed items, so call enableObfuscation() when you need a useful mapping.

These reporting options belong in the build's R8 configuration, not in rules published inside a dependency JAR.
Android's
library optimization guidance
lists them among the global options that library authors should not publish as consumer keep rules.

Shadow resolves R8 from the shadowR8 configuration. The default dependency is com.android.tools:r8, which is
published by Google Maven rather than Maven Central. Add google() to your repositories or override the dependency:

=== "Kotlin"

text
```kotlin
dependencies {
  shadowR8("com.android.tools:r8:9.1.31")
}
```

=== "Groovy"

text
```groovy
dependencies {
  shadowR8 'com.android.tools:r8:9.1.31'
}
```

Advanced R8 command line arguments can be added with args. Replacing the default args value removes Shadow's
default command line arguments, so prefer the helper functions for common obfuscation and optimization toggles. These
helpers are independent and can be used together.

For example, to downgrade R8 warnings to info:

=== "Kotlin"

text
```kotlin
repositories {
  google()
}

tasks.shadowJar {
  minimize {
    r8 {
      args.addAll(listOf("--map-diagnostics", "warning", "info"))
    }
  }
}
```

=== "Groovy"

text
```groovy
repositories {
  google()
}

tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) {
  minimize {
    r8 {
      args.addAll(['--map-diagnostics', 'warning', 'info'])
    }
  }
}
```

To enable name obfuscation:

=== "Kotlin"

text
```kotlin
repositories {
  google()
}

tasks.shadowJar {
  minimize {
    r8 {
      enableObfuscation()
    }
  }
}
```

=== "Groovy"

text
```groovy
repositories {
  google()
}

tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) {
  minimize {
    r8 {
      enableObfuscation()
    }
  }
}
```

To enable optimization:

=== "Kotlin"

text
```kotlin
repositories {
  google()
}

tasks.shadowJar {
  minimize {
    r8 {
      enableOptimization()
    }
  }
}
```

=== "Groovy"

text
```groovy
repositories {
  google()
}

tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) {
  minimize {
    r8 {
      enableOptimization()
    }
  }
}
```

To enable both:

=== "Kotlin"

text
```kotlin
repositories {
  google()
}

tasks.shadowJar {
  minimize {
    r8 {
      enableObfuscation()
      enableOptimization()
    }
  }
}
```

=== "Groovy"

text
```groovy
repositories {
  google()
}

tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) {
  minimize {
    r8 {
      enableObfuscation()
      enableOptimization()
    }
  }
}
```

File: docs/configuration/merging/README.md

Controlling JAR Content Merging

Shadow allows for customizing the process by which the output JAR is generated through the
ResourceTransformer interface. This is a concept that has been carried over from the original
Maven Shade implementation. A ResourceTransformer is invoked for each entry in the JAR before
being written to the final output JAR. This allows a ResourceTransformer to determine if it
should process a particular entry and apply any modifications before writing the stream to the output.

!!! important "Guaranteed Processing Order"

text
[`ResourceTransformer`][ResourceTransformer] follows a guaranteed processing order:

1. **Project files first**: All files in projects are processed before any dependency files.
2. **Dependency files second**: Files from configurations (runtime dependencies) or added via [`ShadowJar.from`][ShadowJar.from] are processed after project files.

This ordering is crucial when merging configuration files where you want to preserve project-specific values while
merging in additional data from dependencies.

Handling Duplicates Strategy

ShadowJar is a subclass of org.gradle.api.tasks.AbstractCopyTask, which means it honors the
duplicatesStrategy property as its parent classes do. There are several strategies to handle:

  • EXCLUDE: Do not allow duplicates by ignoring subsequent items to be created at the same path.
  • FAIL: Throw a DuplicateFileCopyingException when subsequent items are to be created at the same path.
  • INCLUDE: Do not attempt to prevent duplicates.
  • INHERIT: Use the same strategy as the parent copy specification.
  • WARN: Do not attempt to prevent duplicates, but log a warning message when multiple items are to be created at the
    same path.

see more details about them in DuplicatesStrategy.

ShadowJar recognizes EXCLUDE as the default, if you want to change the strategy, you can override it like:

=== "Kotlin"

text
```kotlin
tasks.shadowJar {
  duplicatesStrategy = DuplicatesStrategy.INCLUDE // Or something else.
}
```

=== "Groovy"

text
```groovy
tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) {
  duplicatesStrategy = DuplicatesStrategy.INCLUDE // Or something else.
}
```

Different strategies will lead to different results for foo/bar files in the JARs to be merged:

  • EXCLUDE: The first foo/bar file will be included in the final JAR.
  • FAIL: Fail the build with a DuplicateFileCopyingException if there are duplicate foo/bar files.
  • INCLUDE: Duplicate foo/bar entries will be included in the final JAR.
  • INHERIT: Fail the build with an exception like
    Entry .* is a duplicate but no duplicate handling strategy has been set.
  • WARN: Warn about duplicates in the build log, this behaves exactly as INHERIT otherwise.

!!! note "Precedence of DuplicatesStrategy"

text
The `duplicatesStrategy` evaluation takes precedence over transforming and relocating.
Because `ShadowJar` is a subclass of Gradle's `AbstractCopyTask`, duplicate filtering configured via
`duplicatesStrategy` is performed at Gradle's `CopySpec` processing layer **before** entries are passed to Shadow's
internal [`ResourceTransformer`][ResourceTransformer] engine.

If you mix the usages of duplicatesStrategy = DuplicatesStrategy.EXCLUDE and
ResourceTransformer like below:

=== "Kotlin"

text
```kotlin
tasks.shadowJar {
  duplicatesStrategy = DuplicatesStrategy.EXCLUDE // The default strategy.
  mergeServiceFiles()
}
```

=== "Groovy"

text
```groovy
tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) {
  duplicatesStrategy = DuplicatesStrategy.EXCLUDE // The default strategy.
  mergeServiceFiles()
}
```

The ResourceTransformers like ServiceFileTransformer will not work
as expected because duplicate resource files are filtered out and dropped by Gradle before reaching the transformer.

If Shadow detects a resource matched by a built-in ResourceTransformer while its
duplicatesStrategy is EXCLUDE, it will log a warning during the build:

text
'META-INF/services/foo' is matched by com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer but its DuplicatesStrategy is EXCLUDE โ€” duplicates may be silently dropped before the transformer processes them.
Set it to INCLUDE or WARN to ensure all duplicates are processed by the transformer.

Want ResourceTransformers and duplicatesStrategy to work together? There are several common
steps to take:

  1. Set the default strategy to INCLUDE or WARN.
  2. Apply your ResourceTransformers.
  3. Remove duplicate entries by

Alternatively, you can follow these steps:

  1. Set the default strategy to EXCLUDE or FAIL.
  2. Apply your ResourceTransformers.
  3. Bypass the duplicate entries which should be handled by the ResourceTransformers using
    filesMatching, filesNotMatching, or eachFile functions
    to set their duplicatesStrategy to INCLUDE or WARN.

!!! warning "Build Cache Impact"

text
Functions inherited from [`CopySpec`][CopySpec], such as [`filesMatching`][Jar.filesMatching], [`filesNotMatching`][Jar.filesNotMatching], [`eachFile`][Jar.eachFile], or others, disable the output caching.

Optional steps:

Here are some examples:

=== "Kotlin"

text
```kotlin
tasks.shadowJar {
  // Step 1.
  duplicatesStrategy = DuplicatesStrategy.INCLUDE // Or WARN.
  // Step 2.
  mergeServiceFiles()
  // Step 3. Using `filesNotMatching`:
  filesNotMatching("META-INF/services/**") {
    duplicatesStrategy = DuplicatesStrategy.EXCLUDE // Or FAIL.
  }
  // Step 3. Using `PreserveFirstFoundResourceTransformer`:
  transform<com.github.jengelman.gradle.plugins.shadow.transformers.PreserveFirstFoundResourceTransformer>() {
    resources.add("META-INF/foo/**") // Or something else where the first occurrence should be preserved.
  }
}

tasks.shadowJar {
  // Step 1.
  duplicatesStrategy = DuplicatesStrategy.EXCLUDE // Or FAIL.
  // Step 2.
  mergeServiceFiles()
  // Step 3. Using `filesMatching`:
  filesMatching("META-INF/services/**") {
    duplicatesStrategy = DuplicatesStrategy.INCLUDE // Or WARN.
  }
  // Step 3. Using `eachFile`:
  eachFile {
    if (path.startsWith("META-INF/services/")) {
      duplicatesStrategy = DuplicatesStrategy.INCLUDE // Or WARN.
    }
  }
}

tasks.shadowJar {
  // Optional step.
  failOnDuplicateEntries = true
}
```

=== "Groovy"

text
```groovy
tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) {
  // Step 1.
  duplicatesStrategy = DuplicatesStrategy.INCLUDE // Or WARN.
  // Step 2.
  mergeServiceFiles()
  // Step 3. Using `filesNotMatching`:
  filesNotMatching('META-INF/services/**') {
    duplicatesStrategy = DuplicatesStrategy.EXCLUDE // Or FAIL.
  }
  // Step 3. Using `PreserveFirstFoundResourceTransformer`:
  transform(com.github.jengelman.gradle.plugins.shadow.transformers.PreserveFirstFoundResourceTransformer) {
    resources.add('META-INF/foo/**') // Or something else where the first occurrence should be preserved.
  }
}

tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) {
  // Step 1.
  duplicatesStrategy = DuplicatesStrategy.EXCLUDE // Or FAIL.
  // Step 2.
  mergeServiceFiles()
  // Step 3. Using `filesMatching`:
  filesMatching('META-INF/services/**') {
    duplicatesStrategy = DuplicatesStrategy.INCLUDE // Or WARN.
  }
  // Step 3. Using `eachFile`:
  eachFile {
    if (it.path.startsWith('META-INF/services/')) {
      it.duplicatesStrategy = DuplicatesStrategy.INCLUDE // Or WARN.
    }
  }
}

tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) {
  // Optional step.
  failOnDuplicateEntries = true
}
```

Basic ResourceTransformer Usage

For simpler use cases, you can create a basic transformer:

=== "Kotlin"

text
```kotlin
import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer
import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext
import org.apache.tools.zip.ZipOutputStream
import org.gradle.api.file.FileTreeElement

class MyTransformer : ResourceTransformer {
  override fun canTransformResource(element: FileTreeElement): Boolean = true
  override fun transform(context: TransformerContext) {}
  override fun hasTransformedResource(): Boolean = true
  override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) {}
}

tasks.shadowJar {
  transform<MyTransformer>()
}
```

=== "Groovy"

text
```groovy
import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer
import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext
import org.apache.tools.zip.ZipOutputStream
import org.gradle.api.file.FileTreeElement

class MyTransformer implements ResourceTransformer {
  @Override boolean canTransformResource(FileTreeElement element) { return true }
  @Override void transform(TransformerContext context) {}
  @Override boolean hasTransformedResource() { return true }
  @Override void modifyOutputStream(ZipOutputStream os, boolean preserveFileTimestamps) {}
}

tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) {
  transform(MyTransformer)
}
```

Additionally, a ResourceTransformer can accept a closure to configure the provided
ResourceTransformer. An instantiated instance of a ResourceTransformer
can also be provided.

=== "Kotlin"

text
```kotlin
import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer
import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext
import org.apache.tools.zip.ZipOutputStream
import org.gradle.api.file.FileTreeElement

class MyTransformer(@get:Input var enabled: Boolean = false) : ResourceTransformer {
  override fun canTransformResource(element: FileTreeElement): Boolean = enabled
  override fun transform(context: TransformerContext) {}
  override fun hasTransformedResource(): Boolean = enabled
  override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) {}
}

tasks.shadowJar {
  // Initialize with default constructor and configure with closure.
  transform<MyTransformer>() {
    enabled = true
  }

  // Or use the instantiated instance with closure.
  transform(MyTransformer(false)) {
    enabled = true
  }
}
```

=== "Groovy"

text
```groovy
import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer
import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext
import org.apache.tools.zip.ZipOutputStream
import org.gradle.api.file.FileTreeElement

class MyTransformer implements ResourceTransformer {
  @Input boolean enabled
  MyTransformer(boolean enabled = false) { this.enabled = enabled }
  @Override boolean canTransformResource(FileTreeElement element) { return enabled }
  @Override void transform(TransformerContext context) {}
  @Override boolean hasTransformedResource() { return enabled }
  @Override void modifyOutputStream(ZipOutputStream os, boolean preserveFileTimestamps) {}
}

tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) {
  // Initialize with default constructor and configure with closure.
  transform(MyTransformer) {
    enabled = true
  }

  // Or use the instantiated instance with closure.
  transform(new MyTransformer(false)) {
    enabled = true
  }
}
```

Merging Service Descriptor Files

Java libraries often contain service descriptors files in the META-INF/services directory of the JAR.
A service descriptor typically contains a line delimited list of classes that are supported for a particular service.
At runtime, this file is read and used to configure library or application behavior.

Multiple dependencies may use the same service descriptor file name.
In this case, it is generally desired to merge the content of each instance of the file into a single output file.
The ServiceFileTransformer class is used to perform this merging.
By default, it will merge each copy of a file under META-INF/services into a single file in the output JAR.
You can use either the short syntax method mergeServiceFiles() or the full syntax
method transform to add the ServiceFileTransformer:

=== "Kotlin"

text
```kotlin
tasks.shadowJar {
  // Short syntax.
  mergeServiceFiles()

  // Full syntax.
  transform<com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer>()
}
```

=== "Groovy"

text
```groovy
tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) {
  // Short syntax.
  mergeServiceFiles()

  // Full syntax.
  transform(com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer)
}
```

!!! note "Groovy Extension Modules"

text
Groovy Extension Module descriptor files (located at `META-INF/services/org.codehaus.groovy.runtime.ExtensionModule`)
are ignored by the [`ServiceFileTransformer`][ServiceFileTransformer].
This is due to these files having a different syntax than standard service descriptor files.
Use the [`mergeGroovyExtensionModules()`][mergeGroovyExtensionModules] method to merge
these files if your dependencies contain them.

Configuring the Location of Service Descriptor Files

By default, the ServiceFileTransformer is configured to merge files in META-INF/services.
This directory can be overridden to merge descriptor files in a different location.

=== "Kotlin"

text
```kotlin
tasks.shadowJar {
  // Short syntax.
  mergeServiceFiles {
    path = "META-INF/custom"
  }

  // Full syntax.
  transform<com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer>() {
    path = "META-INF/custom"
  }
}
```

=== "Groovy"

text
```groovy
tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) {
  // Short syntax.      
  mergeServiceFiles {
    path = 'META-INF/custom'
  }

  // Full syntax.
  transform(com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer) {
    path = 'META-INF/custom'
  }
}
```

Excluding/Including Specific Service Descriptor Files From Merging

The ServiceFileTransformer class supports specifying specific files to include or exclude
from merging.

=== "Kotlin"

text
```kotlin
tasks.shadowJar {
  // Short syntax.
  mergeServiceFiles {
    exclude("META-INF/services/com.acme.*")
  }

  // Full syntax.
  transform<com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer>() {
    exclude("META-INF/services/com.acme.*")
  }
}
```

=== "Groovy"

text
```groovy
tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) {
  // Short syntax.
  mergeServiceFiles {
    exclude 'META-INF/services/com.acme.*'
  }

  // Full syntax.
  transform(com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer) {
    exclude 'META-INF/services/com.acme.*'
  }
}
```

Merging Groovy Extension Modules

Shadow provides a specific transformer for dealing with Groovy extension module files.
This is due to their special syntax and how they need to be merged together.
The GroovyExtensionModuleTransformer will handle these files.
The ShadowJar task also provides a short syntax method to add this transformer.

=== "Kotlin"

text
```kotlin
tasks.shadowJar {
  // Short syntax.
  mergeGroovyExtensionModules()

  // Full syntax.
  transform<com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer>()
}
```

=== "Groovy"

text
```groovy
tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) {
  // Short syntax.
  mergeGroovyExtensionModules()

  // Full syntax.
  transform(com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer)
}
```

Merging Log4j2 Plugin Cache Files (`Log4j2Plugins.dat`)

Log4j2PluginsCacheFileTransformer is a
ResourceTransformer that merges
META-INF/org/apache/logging/log4j/core/config/plugins/Log4j2Plugins.dat plugin caches from all the jars
containing Log4j 2.x Core components. It's a Gradle equivalent of
Log4j Plugin Descriptor Transformer.

=== "Kotlin"

text
```kotlin
tasks.shadowJar {
  transform<com.github.jengelman.gradle.plugins.shadow.transformers.Log4j2PluginsCacheFileTransformer>()
}
```

=== "Groovy"

text
```groovy
tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) {
  transform(com.github.jengelman.gradle.plugins.shadow.transformers.Log4j2PluginsCacheFileTransformer)
}
```

Appending Text Files

Generic text files can be appended together using the AppendingTransformer.
Each file is appended using separators (defaults to \n) to separate content.
The ShadowJar task provides a short syntax method of append(String) to configure
this transformer.

=== "Kotlin"

text
```kotlin
tasks.shadowJar {
  append("test.properties")
}
```

=== "Groovy"

text
```groovy
tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) {
  append 'test.properties'
}
```

=== "Kotlin"

text
```kotlin
tasks.shadowJar {
  // short syntax
  append("resources/application.yml", "\n---\n")
  // full syntax
  transform<com.github.jengelman.gradle.plugins.shadow.transformers.AppendingTransformer>() {
    resource = "resources/custom-config/application.yml"
    separator = "\n---\n"
  }
}
```

=== "Groovy"

text
```groovy
tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) {
  // short syntax
  append('resources/application.yml', '\n---\n')
  // full syntax
  transform(com.github.jengelman.gradle.plugins.shadow.transformers.AppendingTransformer) {
    resource = 'resources/custom-config/application.yml'
    separator = '\n---\n'
  }
}
```

Appending XML Files

XML files require a special transformer for merging. The XmlAppendingTransformer
reads each XML document and merges each root element into a single document.
There is no short syntax method for the XmlAppendingTransformer.
It must be added using the transform methods.

=== "Kotlin"

text
```kotlin
tasks.shadowJar {
  transform<com.github.jengelman.gradle.plugins.shadow.transformers.XmlAppendingTransformer>() {
    resource = "properties.xml"
  }
}
```

=== "Groovy"

text
```groovy
tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) {
  transform(com.github.jengelman.gradle.plugins.shadow.transformers.XmlAppendingTransformer) {
    resource = 'properties.xml'
  }
}
```

Merging R8/ProGuard Rule Files

Dependencies may publish ProGuard or R8 rules under META-INF/proguard.
When multiple dependencies have files with the same name under META-INF/proguard,
the ProGuardFilesResourceTransformer merges them into a single file in the
output JAR, while retaining distinct file names for non-conflicting rules. It also relocates matched class names
and package patterns within the rules according to configured relocators.

You can add this transformer using transform:

=== "Kotlin"

text
```kotlin
tasks.shadowJar {
  transform<com.github.jengelman.gradle.plugins.shadow.transformers.ProGuardFilesResourceTransformer>()
}
```

=== "Groovy"

text
```groovy
tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) {
  transform(com.github.jengelman.gradle.plugins.shadow.transformers.ProGuardFilesResourceTransformer)
}
```

Configuring Resource Transformer Filtering by Pattern

There are lots of built-in ResourceTransformers provided by Shadow. Some of them extend
PatternFilterableResourceTransformer, which extends
PatternFilterable to provide include/exclude pattern filtering capabilities. e.g.

You can use include/exclude and more methods to configure the patterns for those
ResourceTransformers that support it. For example:

=== "Kotlin"

text
```kotlin
tasks.shadowJar {
  transform<com.github.jengelman.gradle.plugins.shadow.transformers.ApacheLicenseResourceTransformer>() {
    include("META-INF/LICENSE.*")
    exclude("META-INF/LICENSE.log")
  }
}
```

=== "Groovy"

text
```groovy
tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) {
  transform(com.github.jengelman.gradle.plugins.shadow.transformers.ApacheLicenseResourceTransformer) {
    include 'META-INF/LICENSE.*'
    exclude 'META-INF/LICENSE.log'
  }
}
```

Finding Resources in the Classpath

When dealing with resource merge conflicts, it can be helpful to find which dependencies contain the conflicting resources.
Shadow provides a FindResourceInClasspath helper task for this purpose.

To scan for resources, register a FindResourceInClasspath task in your build script and configure its classpath and the resource patterns to look for:

=== "Kotlin"

text
```kotlin
tasks.register<com.github.jengelman.gradle.plugins.shadow.tasks.FindResourceInClasspath>("findResources") {
  classpath.from(configurations.runtimeClasspath)
  include("META-INF/services/org.codehaus.groovy.runtime.ExtensionModule")
}
```

=== "Groovy"

text
```groovy
tasks.register('findResources', com.github.jengelman.gradle.plugins.shadow.tasks.FindResourceInClasspath) {
  classpath.from(configurations.runtimeClasspath)
  include 'META-INF/services/org.codehaus.groovy.runtime.ExtensionModule'
}
```

You can then run the task to scan each entry on the classpath and print any matched resources to the console:

shell
./gradlew findResources

2. Official Technical Reference & Guides (GradleUp/GradleUp.github.io)




This repository hosts a GradleUp website.
It includes the styles and some top-level content.
Project documentation and other information are sourced from other repositories.

What is GradleUp?

GradleUp is an umbrella GitHub organization created by, and for, the community.
GradleUp focuses on simplifying building Java, Kotlin and Android projects.
On our menu: Gradle plugins, developer tools and best practices.

GradleUp is not affiliated with, or sponsored by Gradle, Inc. Gradle Up is an independent organization and its projects, plugins, and products are not affiliated with or sponsored by Gradle, Inc. in any way.

License

This site by the GradleUp team and contributors is licensed under CC BY 4.0

Imported content from external repositories might have other licenses.

Contributing

Any contributions, typo fixes and patches are welcome!
The content is located inside the project repositories.
If you want to contribute to the GradleUp site,
see the guidelines here