Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 106 additions & 0 deletions docs/multi-project/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,113 @@ configuration of the shadowed project.
}
```

## 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][gradle-consumable-configs] `apiElements` and `runtimeElements` to
publish the shadowed JAR instead of the regular JAR.

In the `foo` project (that produces the shadowed JAR):

=== "Kotlin"

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

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

=== "Groovy"

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

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

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

=== "Kotlin"

```kotlin
dependencies {
implementation(project(":foo"))
}
```

=== "Groovy"

```groovy
dependencies {
implementation project(':foo')
}
```

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

=== "Kotlin"

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

=== "Groovy"

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



[gradle-consumable-configs]: https://docs.gradle.org/current/userguide/declaring_configurations.html#3_consumable_configurations
[gradle-exclude-rules]: https://docs.gradle.org/current/userguide/dependency_downgrade_and_exclude.html#sec:excluding-transitive-deps
[Jar]: https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html
[ShadowJar]: ../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,108 @@ class JavaPluginsTest : BasePluginTest() {
}
}

@Issue("https://github.com/GradleUp/shadow/issues/1893")
@Test
fun shadowJarIsDefaultArtifactInMultiProjectBuild() {
settingsScript.appendText("include 'foo', 'consumer'$lineSeparator")
projectScript.writeText("")

path("foo/build.gradle")
.writeText(
"""
${getDefaultProjectBuildScript("java-library")}
dependencies {
implementation 'my:a:1.0'
}
configurations {
named('apiElements') {
outgoing.artifacts.clear()
outgoing.artifact(tasks.named('shadowJar'))
}
named('runtimeElements') {
outgoing.artifacts.clear()
outgoing.artifact(tasks.named('shadowJar'))
}
}
"""
.trimIndent() + lineSeparator
)

path("consumer/build.gradle")
.writeText(
"""
${getDefaultProjectBuildScript("java")}
dependencies {
implementation project(':foo')
}
tasks.register('printClasspathFiles') {
doLast {
configurations.runtimeClasspath.files.each { println it.name }
}
}
"""
.trimIndent() + lineSeparator
)

val result = runWithSuccess(":consumer:printClasspathFiles")
assertThat(result.output).all {
contains("foo-1.0-all.jar")
doesNotContain("foo-1.0.jar")
}
}

@Issue("https://github.com/GradleUp/shadow/issues/1893")
@Test
fun excludeRulesPreventBundledDepsOnConsumerClasspath() {
settingsScript.appendText("include 'foo', 'consumer'$lineSeparator")
projectScript.writeText("")

path("foo/build.gradle")
.writeText(
"""
${getDefaultProjectBuildScript("java-library")}
dependencies {
implementation 'my:a:1.0'
}
configurations {
named('apiElements') {
outgoing.artifacts.clear()
outgoing.artifact(tasks.named('shadowJar'))
exclude(group: 'my', module: 'a')
}
named('runtimeElements') {
outgoing.artifacts.clear()
outgoing.artifact(tasks.named('shadowJar'))
exclude(group: 'my', module: 'a')
}
}
"""
.trimIndent() + lineSeparator
)

path("consumer/build.gradle")
.writeText(
"""
${getDefaultProjectBuildScript("java")}
dependencies {
implementation project(':foo')
}
tasks.register('printClasspathFiles') {
doLast {
configurations.runtimeClasspath.files.each { println it.name }
}
}
"""
.trimIndent() + lineSeparator
)

val result = runWithSuccess(":consumer:printClasspathFiles")
assertThat(result.output).all {
contains("foo-1.0-all.jar")
doesNotContain("a-1.0.jar")
}
}

@Issue("https://github.com/GradleUp/shadow/issues/1606")
@Test
fun shadowExposedCustomSourceSetOutput() {
Expand Down
Loading