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.
dependencies {
implementation(project(path = ":api", configuration = "shadow"))
}
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):
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)
}
}
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:
dependencies {
implementation(project(":api"))
}
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:
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")
}
}
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'
}
}