diff --git a/docs/multi-project/README.md b/docs/multi-project/README.md index f961f44d3..1062e2c64 100644 --- a/docs/multi-project/README.md +++ b/docs/multi-project/README.md @@ -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 diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt index f2d39283b..98dad41c1 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt @@ -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() {