diff --git a/docs/configuration/dependencies/README.md b/docs/configuration/dependencies/README.md index 6ff120ecf..60e584562 100644 --- a/docs/configuration/dependencies/README.md +++ b/docs/configuration/dependencies/README.md @@ -483,6 +483,46 @@ block provides a method that accepts a `Closure` for selecting dependencies. } ``` +### Declaring Excluded Dependencies for Consumers + +Dependency filtering only controls the content of the shadowed JAR. Excluded dependencies are dropped entirely, they are +**not** automatically added to the [`shadow` configuration][shadow-configuration], so they won't appear in the +published POM file, Gradle Module Metadata, or the `Class-Path` attribute of the manifest. + +This is intentional. A dependency is often excluded because the target environment already provides it (e.g. the +Gradle API for a Gradle plugin), so exposing it to consumers would be wrong. Moreover, the filter only sees resolved +dependencies, so declared versions, scopes, and exclusions can't be restored from them. + +If some dependencies should not be bundled but still be required at runtime, declare them in the `shadow` configuration +explicitly. For example, to bundle `log4j-core` only, while keeping its transitive dependency `log4j-api` as a runtime +dependency: + +=== ":material-language-kotlin: build.gradle.kts" + + ```kotlin + dependencies { + // Bundled into the shadowed JAR without its transitive dependencies. + implementation("org.apache.logging.log4j:log4j-core:2.11.1") { + isTransitive = false + } + // Not bundled, but declared as a runtime dependency of the shadowed JAR. + shadow("org.apache.logging.log4j:log4j-api:2.11.1") + } + ``` + +=== ":simple-apachegroovy: build.gradle" + + ```groovy + dependencies { + // Bundled into the shadowed JAR without its transitive dependencies. + implementation('org.apache.logging.log4j:log4j-core:2.11.1') { + transitive = false + } + // Not bundled, but declared as a runtime dependency of the shadowed JAR. + shadow 'org.apache.logging.log4j:log4j-api:2.11.1' + } + ``` + [Jar.from]: https://docs.gradle.org/current/dsl/org.gradle.jvm.tasks.Jar.html#org.gradle.jvm.tasks.Jar:from(java.lang.Object,%20org.gradle.api.Action) [Jar]: https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html @@ -495,3 +535,4 @@ block provides a method that accepts a `Closure` for selecting dependencies. [pom-files]: https://repo1.maven.org/maven2/org/graalvm/polyglot/js-community/24.2.2/ [so-files]: https://repo1.maven.org/maven2/io/github/ganadist/sqlite4java/libsqlite4java-osx-aarch64/1.0.392/ [minimizing]: ../minimizing/README.md +[shadow-configuration]: ../../publishing/README.md#shadow-configuration-and-publishing diff --git a/docs/publishing/README.md b/docs/publishing/README.md index e6f91aba1..cc756a35f 100644 --- a/docs/publishing/README.md +++ b/docs/publishing/README.md @@ -155,7 +155,8 @@ The Shadow plugin provides a custom configuration (`configurations.shadow`) to s `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 +**not** automatically added to the POM file (see [Declaring Excluded Dependencies for Consumers][excluded-dependencies]) +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 @@ -1000,3 +1001,4 @@ If using [Dokka][dokka] for Kotlin projects, you can extract the shadowed source [gradle-plugin-publish-docs]: https://docs.gradle.org/current/userguide/publishing_gradle_plugins.html#shadow_dependencies [configuring-output-name]: ../configuration/README.md#configuring-output-name [dokka]: https://kotlinlang.org/docs/dokka-introduction.html +[excluded-dependencies]: ../configuration/dependencies/README.md#declaring-excluded-dependencies-for-consumers diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt index 4298ae2e4..bd7c5a002 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt @@ -277,6 +277,35 @@ class PublishingTest : BasePluginTest() { assertShadowVariantCommon("$artifactRoot/maven-1.0.module") } + @Test // #265 + fun publishBundledDependencyWithTransitiveDependencyInShadowConfiguration() { + projectScript.appendText( + publishConfiguration( + dependenciesBlock = + """ + |implementation('my:d:1.0') { + | transitive = false + |} + |shadow 'my:c:1.0' + """ + .trimMargin(), + ), + ) + + publish() + + val artifactRoot = "my/maven-all/1.0" + assertThat(repoJarPath("$artifactRoot/maven-all-1.0-all.jar")).useAll { + containsOnly("d.properties", "META-INF/", "META-INF/MANIFEST.MF") + getMainAttr(classPathAttributeKey).isEqualTo("c-1.0.jar") + } + assertPomCommon("$artifactRoot/maven-all-1.0.pom", coordinates = arrayOf("my:c:1.0")) + assertShadowVariantCommon( + "$artifactRoot/maven-all-1.0.module", + coordinates = arrayOf("my:c:1.0"), + ) + } + @Test fun publishShadowJarWithSourcesWhenWithSourcesJarEnabled() { projectScript.appendText(