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
IDcom.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'smaven-publish plugin.
The plugin provides the shadow component to configure the publication with the necessary
artifact and dependencies in the POM file.
=== "Kotlin"
```kotlin
plugins {
java
`maven-publish`
id("com.gradleup.shadow")
}
publishing {
publications {
create<MavenPublication>("shadow") {
from(components["shadow"])
}
}
repositories {
maven("https://repo.myorg.com")
}
}
```=== "Groovy"
```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"
```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"
```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"
```kotlin
shadow {
addTargetJvmVersionAttribute = false
}
```=== "Groovy"
```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"
```kotlin
shadow {
// Per description of the attribute, you should set it to either `Bundling.SHADOWED` or `Bundling.EMBEDDED`.
bundlingAttribute = Bundling.EMBEDDED
}
```=== "Groovy"
```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 specifyingshadowJar.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"
```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"
```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"
```kotlin
plugins {
java
`maven-publish`
id("com.gradleup.shadow")
}
tasks.shadowJar {
archiveClassifier = ""
}
publishing {
publications {
create<MavenPublication>("shadow")
}
repositories {
maven("https://repo.myorg.com")
}
}
```=== "Groovy"
```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 ofShadowJar 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"
```kotlin
tasks.jar {
enabled = false
}
```=== "Groovy"
```groovy
tasks.named('jar', Jar) {
enabled = false
}
```Or set a different archiveClassifier for the standard Jar like:
=== "Kotlin"
```kotlin
tasks.jar {
archiveClassifier = "ignored"
}
```=== "Groovy"
```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"
```kotlin
plugins {
id("com.gradle.plugin-publish") version "latest"
id("com.gradleup.shadow")
}
dependencies {
// Your plugin dependencies.
}
tasks.shadowJar {
archiveClassifier = ""
}
```=== "Groovy"
```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 theMavenPublication.artifact() method.
=== "Kotlin"
```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"
```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"
```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"
```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"
```kotlin
dependencies {
implementation(project(path = ":api", configuration = "shadow"))
}
```=== "Groovy"
```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 andruntimeElements to publish the shadowed JAR instead of the regular JAR.
As a reminder, configurations like api and implementation are where dependencies are declared (declarable), whileapiElements 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"
```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"
```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
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"
```kotlin
dependencies {
implementation(project(":api"))
}
```=== "Groovy"
```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"
```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"
```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:
kotlin.stdlib.default.dependency=falseKotlin 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"
```kotlin
dependencies {
compileOnly("org.jetbrains.kotlin:kotlin-stdlib:<version>")
}
```=== "Groovy"
```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"
```kotlin
plugins {
id("org.jetbrains.kotlin.jvm")
id("com.gradleup.shadow")
}
dependencies {
implementation("io.ktor:ktor-client-okhttp:3.1.0")
}
```=== "Groovy"
```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"
```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"
```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), addKotlinModuleMetadataTransformer to your task configuration:
=== "Kotlin"
```kotlin
tasks.shadowJar {
transform(com.github.jengelman.gradle.plugins.shadow.transformers.KotlinModuleMetadataTransformer::class.java)
}
```=== "Groovy"
```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"
```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"
```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"
```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"
```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"
```kotlin
plugins {
java
id("com.gradleup.shadow") version "<version>"
}
```=== "Groovy"
```groovy
plugins {
id 'java'
id 'com.gradleup.shadow' version '<version>'
}
```Alternatively, the plugin can be added to the buildscript classpath and applied:
=== "Kotlin"
```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"
```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"
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"
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
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
ShadowJartask to the project. - Adds a
shadowconfiguration to the project. - Adds a
shadowvariant to the project. - Adds a
shadowcomponent to the project. - Configures the
ShadowJartask to include all sources from the project'smainsourceSet. - Configures the
ShadowJartask to bundle all dependencies from theruntimeClasspathconfiguration. - Configures the classifier attribute of the
ShadowJartask to be'all'. - Configures the
ShadowJartask to generate aManifestwith:- Inheriting all configuration from the standard
Jartask. - Adds a
Class-Pathattribute to theManifestthat appends all dependencies from theshadowconfiguration
- Inheriting all configuration from the standard
- Configures the
ShadowJartask to exclude any JAR index or cryptographic signature files matching the
following patterns:META-INF/INDEX.LISTMETA-INF/*.SFMETA-INF/*.DSAMETA-INF/*.RSAMETA-INF/versions/**/module-info.classmodule-info.class
- Creates and registers the
shadowcomponent in the project (used for integrating withmaven-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:
--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:
./gradlew -q help --task shadowJarRefer 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 theconfigurations property is specified to inform Shadow which dependencies to merge into
the output.
=== "Kotlin"
```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"
```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 alltestRuntimeOnly and testImplementation dependencies. The file is output tobuild/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 theconfigurations property, but not adding any project sources with from(...).
=== "Kotlin"
```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"
```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"
```kotlin
tasks.shadowJar {
minimize()
}
```=== "Groovy"
```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"
```kotlin
tasks.shadowJar {
minimize {
exclude(dependency("org.scala-lang:.*:.*"))
}
}
```=== "Groovy"
```groovy
tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) {
minimize {
exclude(dependency('org.scala-lang:.*:.*'))
}
}
```!!! note
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"
```kotlin
tasks.shadowJar {
minimize {
exclude(project(":api"))
}
}
```=== "Groovy"
```groovy
tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) {
minimize {
exclude(project(":api"))
}
}
```!!! note
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"
```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"
```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"
```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"
```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"
```kotlin
dependencies {
shadowR8("com.android.tools:r8:9.1.31")
}
```=== "Groovy"
```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"
```kotlin
repositories {
google()
}
tasks.shadowJar {
minimize {
r8 {
args.addAll(listOf("--map-diagnostics", "warning", "info"))
}
}
}
```=== "Groovy"
```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"
```kotlin
repositories {
google()
}
tasks.shadowJar {
minimize {
r8 {
enableObfuscation()
}
}
}
```=== "Groovy"
```groovy
repositories {
google()
}
tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) {
minimize {
r8 {
enableObfuscation()
}
}
}
```To enable optimization:
=== "Kotlin"
```kotlin
repositories {
google()
}
tasks.shadowJar {
minimize {
r8 {
enableOptimization()
}
}
}
```=== "Groovy"
```groovy
repositories {
google()
}
tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) {
minimize {
r8 {
enableOptimization()
}
}
}
```To enable both:
=== "Kotlin"
```kotlin
repositories {
google()
}
tasks.shadowJar {
minimize {
r8 {
enableObfuscation()
enableOptimization()
}
}
}
```=== "Groovy"
```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 theResourceTransformer 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"
[`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 theduplicatesStrategy 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 aDuplicateFileCopyingExceptionwhen 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"
```kotlin
tasks.shadowJar {
duplicatesStrategy = DuplicatesStrategy.INCLUDE // Or something else.
}
```=== "Groovy"
```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 firstfoo/barfile will be included in the final JAR.FAIL: Fail the build with aDuplicateFileCopyingExceptionif there are duplicatefoo/barfiles.INCLUDE: Duplicatefoo/barentries will be included in the final JAR.INHERIT: Fail the build with an exception likeEntry .* is a duplicate but no duplicate handling strategy has been set.WARN: Warn about duplicates in the build log, this behaves exactly asINHERITotherwise.
!!! note "Precedence of DuplicatesStrategy"
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 andResourceTransformer like below:
=== "Kotlin"
```kotlin
tasks.shadowJar {
duplicatesStrategy = DuplicatesStrategy.EXCLUDE // The default strategy.
mergeServiceFiles()
}
```=== "Groovy"
```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 itsduplicatesStrategy is EXCLUDE, it will log a warning during the build:
'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:
- Set the default strategy to
INCLUDEorWARN. - Apply your
ResourceTransformers. - Remove duplicate entries by
- overriding the default strategy for specific files to
EXCLUDEorFAILusing
filesMatching,filesNotMatching, oreachFilefunctions - or applying
PreserveFirstFoundResourceTransformerfor specific files - or write your own
ResourceTransformerto handle duplicates - or mechanism similar.
- overriding the default strategy for specific files to
Alternatively, you can follow these steps:
- Set the default strategy to
EXCLUDEorFAIL. - Apply your
ResourceTransformers. - Bypass the duplicate entries which should be handled by the
ResourceTransformers using
filesMatching,filesNotMatching, oreachFilefunctions
to set theirduplicatesStrategytoINCLUDEorWARN.
!!! warning "Build Cache Impact"
Functions inherited from [`CopySpec`][CopySpec], such as [`filesMatching`][Jar.filesMatching], [`filesNotMatching`][Jar.filesNotMatching], [`eachFile`][Jar.eachFile], or others, disable the output caching.Optional steps:
- Enable
ShadowJar.failOnDuplicateEntriesto check duplicate entries in the final JAR.
This can also ensure the regressions are caught in the future. - Use Diffuse to diff the JARs.
Here are some examples:
=== "Kotlin"
```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"
```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"
```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"
```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 providedResourceTransformer. An instantiated instance of a ResourceTransformer
can also be provided.
=== "Kotlin"
```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"
```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"
```kotlin
tasks.shadowJar {
// Short syntax.
mergeServiceFiles()
// Full syntax.
transform<com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer>()
}
```=== "Groovy"
```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"
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"
```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"
```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"
```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"
```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"
```kotlin
tasks.shadowJar {
// Short syntax.
mergeGroovyExtensionModules()
// Full syntax.
transform<com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer>()
}
```=== "Groovy"
```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 aResourceTransformer that mergesMETA-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"
```kotlin
tasks.shadowJar {
transform<com.github.jengelman.gradle.plugins.shadow.transformers.Log4j2PluginsCacheFileTransformer>()
}
```=== "Groovy"
```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"
```kotlin
tasks.shadowJar {
append("test.properties")
}
```=== "Groovy"
```groovy
tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) {
append 'test.properties'
}
```=== "Kotlin"
```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"
```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"
```kotlin
tasks.shadowJar {
transform<com.github.jengelman.gradle.plugins.shadow.transformers.XmlAppendingTransformer>() {
resource = "properties.xml"
}
}
```=== "Groovy"
```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"
```kotlin
tasks.shadowJar {
transform<com.github.jengelman.gradle.plugins.shadow.transformers.ProGuardFilesResourceTransformer>()
}
```=== "Groovy"
```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 extendPatternFilterableResourceTransformer, which extendsPatternFilterable to provide include/exclude pattern filtering capabilities. e.g.
ApacheLicenseResourceTransformerApacheNoticeResourceTransformerProGuardFilesResourceTransformerServiceFileTransformer- ...
You can use include/exclude and more methods to configure the patterns for thoseResourceTransformers that support it. For example:
=== "Kotlin"
```kotlin
tasks.shadowJar {
transform<com.github.jengelman.gradle.plugins.shadow.transformers.ApacheLicenseResourceTransformer>() {
include("META-INF/LICENSE.*")
exclude("META-INF/LICENSE.log")
}
}
```=== "Groovy"
```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"
```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"
```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:
./gradlew findResources2. 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