From 1f5c4e213a14a869145fd254439f51cd87257e71 Mon Sep 17 00:00:00 2001 From: Tom Tresansky Date: Fri, 10 Apr 2026 09:43:23 -0400 Subject: [PATCH 1/5] Replace SetProperty with ConfigurableFileCollection on ShadowJar Add addConfiguration(), setConfigurations(), clearConfigurations() methods that maintain both the CC-safe ConfigurableFileCollection and an internal Configuration list for DependencyFilter compatibility. --- .../gradle/plugins/shadow/JavaPluginsTest.kt | 8 ++--- .../gradle/plugins/shadow/PublishingTest.kt | 2 +- .../gradle/plugins/shadow/RelocationTest.kt | 2 +- .../gradle/plugins/shadow/ShadowJavaPlugin.kt | 2 +- .../gradle/plugins/shadow/ShadowKmpPlugin.kt | 6 ++-- .../gradle/plugins/shadow/tasks/ShadowJar.kt | 33 ++++++++++++++++--- .../plugins/shadow/ShadowPropertiesTest.kt | 2 +- 7 files changed, 39 insertions(+), 16 deletions(-) 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 7123ba48a..1d05b6109 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 @@ -714,7 +714,7 @@ class JavaPluginsTest : BasePluginTest() { 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] } + setConfigurations([project.configurations.testRuntimeClasspath]) manifest { attributes '$mainClassAttributeKey': 'my.Main' } @@ -751,7 +751,7 @@ class JavaPluginsTest : BasePluginTest() { 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] } + setConfigurations([project.configurations.testRuntimeClasspath]) manifest { attributes '$mainClassAttributeKey': 'my.Main' } @@ -795,7 +795,7 @@ class JavaPluginsTest : BasePluginTest() { def $dependencyShadowJar = tasks.register('$dependencyShadowJar', ${ShadowJar::class.java.name}) { description = 'Create a shadow JAR of all dependencies' archiveClassifier = 'dep' - configurations = project.configurations.named('runtimeClasspath').map { [it] } + setConfigurations([project.configurations.runtimeClasspath]) } """ .trimIndent() @@ -1185,7 +1185,7 @@ class JavaPluginsTest : BasePluginTest() { } $shadowJarTask { - configurations = [project.configurations.runtimeClasspath] + setConfigurations([project.configurations.runtimeClasspath]) } configurations.runtimeClasspath { 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 754ce1caf..1a419d9f5 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 @@ -305,7 +305,7 @@ class PublishingTest : BasePluginTest() { 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] } + setConfigurations([project.configurations.testRuntimeClasspath]) } """ .trimIndent(), diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt index 68d23bc75..ee2a853ea 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt @@ -466,7 +466,7 @@ class RelocationTest : BasePluginTest() { implementation 'junit:junit:3.8.2' } $shadowJarTask { - configurations = [] + clearConfigurations() relocate('', 'foo/') } """ diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt index 68aae0281..c164b08e7 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt @@ -42,7 +42,7 @@ constructor(private val softwareComponentFactory: SoftwareComponentFactory) : Pl val taskProvider = registerShadowJarCommon(tasks.named("jar", Jar::class.java)) { task -> task.from(sourceSets.named("main").map { it.output }) - task.configurations.convention(provider { listOf(runtimeConfiguration) }) + task.addConfiguration(runtimeConfiguration) } artifacts.add(configurations.shadow.name, taskProvider) } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowKmpPlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowKmpPlugin.kt index 5cb4fe242..eb2209ccd 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowKmpPlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowKmpPlugin.kt @@ -36,10 +36,8 @@ public abstract class ShadowKmpPlugin : Plugin { val kotlinJvmMain = target.compilations.named("main") registerShadowJarCommon(tasks.named(target.artifactsTaskName, Jar::class.java)) { task -> task.from(kotlinJvmMain.map { it.output.allOutputs }) - task.configurations.convention( - provider { - listOf(configurations.getByName(kotlinJvmMain.get().runtimeDependencyConfigurationName)) - } + task.addConfiguration( + configurations.getByName(kotlinJvmMain.get().runtimeDependencyConfigurationName) ) if (!isAtLeastKgp("1.9.0")) return@registerShadowJarCommon diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 3bb828c73..ae7e8a849 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -72,6 +72,9 @@ public abstract class ShadowJar : Jar() { project.configurations.findByName(ShadowBasePlugin.CONFIGURATION_NAME) ?: project.files() } + @Transient + private val _sourceConfigurations = mutableListOf() + init { group = LifecycleBasePlugin.BUILD_GROUP description = "Create a combined JAR of project and runtime dependencies" @@ -101,7 +104,7 @@ public abstract class ShadowJar : Jar() { @get:Classpath public open val toMinimize: ConfigurableFileCollection = objectFactory.fileCollection { minimizeJar.map { - if (it) (dependencyFilterForMinimize.resolve(configurations.get()) - apiJars) else emptySet() + if (it) (dependencyFilterForMinimize.resolve(_sourceConfigurations) - apiJars) else emptySet() } } @@ -132,12 +135,34 @@ public abstract class ShadowJar : Jar() { @get:Nested public open val relocators: SetProperty = objectFactory.setProperty() /** - * The configurations to include dependencies from. + * The resolved files from configurations to include dependencies from. + * + * Do not add to this file collection directly. Use [addConfiguration], [setConfigurations], + * or [clearConfigurations] instead, so that dependency filtering works correctly. * * Defaults to a set that contains `runtimeClasspath` or `runtime` configuration. */ @get:Classpath - public open val configurations: SetProperty = objectFactory.setProperty() + public open val configurations: ConfigurableFileCollection = objectFactory.fileCollection() + + /** Add a [Configuration] whose dependencies should be shadowed. */ + public open fun addConfiguration(config: Configuration) { + _sourceConfigurations.add(config) + configurations.from(config) + } + + /** Replace all configurations with the given set. */ + public open fun setConfigurations(configs: Iterable) { + _sourceConfigurations.clear() + configurations.setFrom() + configs.forEach { addConfiguration(it) } + } + + /** Remove all configurations. */ + public open fun clearConfigurations() { + _sourceConfigurations.clear() + configurations.setFrom() + } @get:Input public open val dependencyFilter: Property = @@ -146,7 +171,7 @@ public abstract class ShadowJar : Jar() { /** Final dependencies to be shadowed. */ @get:Classpath public open val includedDependencies: ConfigurableFileCollection = objectFactory.fileCollection { - dependencyFilter.zip(configurations) { df, cs -> df.resolve(cs) } + dependencyFilter.map { df -> df.resolve(_sourceConfigurations) } } /** diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPropertiesTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPropertiesTest.kt index d5bf5e8f4..fb05e4b74 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPropertiesTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPropertiesTest.kt @@ -154,7 +154,7 @@ class ShadowPropertiesTest { assertThat(mainClass.orNull).isNull() assertThat(relocationPrefix.get()).isEqualTo(ShadowBasePlugin.SHADOW) - assertThat(configurations.get()).containsOnly(runtimeConfiguration) + assertThat(configurations.files).isEqualTo(runtimeConfiguration.files) } } From ea3e9a52fbc17247c49d0c9a34b50b77ded74bdb Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 15 Apr 2026 10:55:14 +0800 Subject: [PATCH 2/5] Rework --- api/shadow.api | 4 ++- docs/configuration/dependencies/README.md | 6 ++-- docs/configuration/relocation/README.md | 6 ++-- docs/custom-tasks/README.md | 8 ++--- docs/publishing/README.md | 6 ++-- .../gradle/plugins/shadow/JavaPluginsTest.kt | 8 ++--- .../gradle/plugins/shadow/PublishingTest.kt | 2 +- .../gradle/plugins/shadow/RelocationTest.kt | 3 +- .../gradle/plugins/shadow/ShadowJavaPlugin.kt | 2 +- .../gradle/plugins/shadow/ShadowKmpPlugin.kt | 6 ++-- .../plugins/shadow/tasks/DependencyFilter.kt | 26 +++++++++++++++ .../gradle/plugins/shadow/tasks/ShadowJar.kt | 32 ++----------------- .../plugins/shadow/ShadowPropertiesTest.kt | 4 ++- 13 files changed, 61 insertions(+), 52 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index 46f49e233..43ec17f8c 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -160,6 +160,7 @@ public abstract interface class com/github/jengelman/gradle/plugins/shadow/tasks public abstract fun project (Ljava/lang/Object;)Lorg/gradle/api/specs/Spec; public abstract fun resolve (Ljava/util/Collection;)Lorg/gradle/api/file/FileCollection; public abstract fun resolve (Lorg/gradle/api/artifacts/Configuration;)Lorg/gradle/api/file/FileCollection; + public abstract fun resolve (Lorg/gradle/api/file/ConfigurableFileCollection;)Lorg/gradle/api/file/FileCollection; } public abstract class com/github/jengelman/gradle/plugins/shadow/tasks/DependencyFilter$AbstractDependencyFilter : com/github/jengelman/gradle/plugins/shadow/tasks/DependencyFilter { @@ -175,6 +176,7 @@ public abstract class com/github/jengelman/gradle/plugins/shadow/tasks/Dependenc public fun resolve (Ljava/util/Collection;)Lorg/gradle/api/file/FileCollection; protected abstract fun resolve (Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;)V public fun resolve (Lorg/gradle/api/artifacts/Configuration;)Lorg/gradle/api/file/FileCollection; + public fun resolve (Lorg/gradle/api/file/ConfigurableFileCollection;)Lorg/gradle/api/file/FileCollection; } public abstract class com/github/jengelman/gradle/plugins/shadow/tasks/FindResourceInClasspath : org/gradle/api/DefaultTask, org/gradle/api/tasks/util/PatternFilterable { @@ -225,7 +227,7 @@ public abstract class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar public fun getAddMultiReleaseAttribute ()Lorg/gradle/api/provider/Property; public fun getApiJars ()Lorg/gradle/api/file/ConfigurableFileCollection; protected abstract fun getArchiveOperations ()Lorg/gradle/api/file/ArchiveOperations; - public fun getConfigurations ()Lorg/gradle/api/provider/SetProperty; + public fun getConfigurations ()Lorg/gradle/api/file/ConfigurableFileCollection; public fun getDependencyFilter ()Lorg/gradle/api/provider/Property; public fun getDuplicatesStrategy ()Lorg/gradle/api/file/DuplicatesStrategy; public fun getEnableAutoRelocation ()Lorg/gradle/api/provider/Property; diff --git a/docs/configuration/dependencies/README.md b/docs/configuration/dependencies/README.md index 2b62f575f..f837189d4 100644 --- a/docs/configuration/dependencies/README.md +++ b/docs/configuration/dependencies/README.md @@ -9,7 +9,7 @@ merging can be configured using the [`configurations`][ShadowJar.configurations] ```kotlin tasks.shadowJar { - configurations = project.configurations.compileClasspath.map { listOf(it) } + configurations.setFrom(project.configurations.compileClasspath) } ``` @@ -17,7 +17,7 @@ merging can be configured using the [`configurations`][ShadowJar.configurations] ```groovy tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - configurations = project.configurations.named('compileClasspath').map { [it] } + configurations.setFrom project.configurations.named('compileClasspath') } ``` @@ -27,7 +27,7 @@ This means any dependency declared in the `runtimeOnly` configuration would be * > Note the literal use of [`project.configurations`][Project.configurations] when setting the > [`configurations`][ShadowJar.configurations] attribute of a [`ShadowJar`][ShadowJar] task. -> This is **required**. It may be tempting to specify `configurations = [configurations.compileClasspath]` but this will +> This is **required**. It may be tempting to specify `configurations.setFrom(configurations.compileClasspath)` but this will > not have the intended effect, as `configurations.compile` will try to delegate to the > [`configurations`][ShadowJar.configurations] property of the [`ShadowJar`][ShadowJar] task instead of the `project` diff --git a/docs/configuration/relocation/README.md b/docs/configuration/relocation/README.md index 0f64e9b6a..991ef5b93 100644 --- a/docs/configuration/relocation/README.md +++ b/docs/configuration/relocation/README.md @@ -227,7 +227,8 @@ relocating), you can try out the trick like: ```kotlin tasks.shadowJar { // Empty configurations list will exclude all dependencies. - configurations = emptyList() + configurations.unset() + configurations.unsetConvention() relocate("com.example", "shadow.com.example") } ``` @@ -237,7 +238,8 @@ relocating), you can try out the trick like: ```groovy tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { // Empty configurations list will exclude all dependencies. - configurations = [] + configurations.unset() + configurations.unsetConvention() relocate 'com.example', 'shadow.com.example' } ``` diff --git a/docs/custom-tasks/README.md b/docs/custom-tasks/README.md index a9e56b138..3b30fa80c 100644 --- a/docs/custom-tasks/README.md +++ b/docs/custom-tasks/README.md @@ -13,7 +13,7 @@ the output. archiveClassifier = "test" from(sourceSets.test.map { it.output }) - configurations = project.configurations.testRuntimeClasspath.map { listOf(it) } + configurations.setFrom(project.configurations.testRuntimeClasspath) manifest { // Optionally, set the main class for the JAR. @@ -36,7 +36,7 @@ the output. archiveClassifier = 'test' from sourceSets.named('test').map { it.output } - configurations = project.configurations.named('testRuntimeClasspath').map { [it] } + configurations.setFrom project.configurations.named('testRuntimeClasspath') manifest { // Optionally, set the main class for the JAR. @@ -67,7 +67,7 @@ source code. This is accomplished by creating a custom [`ShadowJar`][ShadowJar] tasks.registering(com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar::class) { description = "Create a shadow JAR of all dependencies" archiveClassifier = "dep" - configurations = project.configurations.runtimeClasspath.map { listOf(it) } + configurations.setFrom(project.configurations.runtimeClasspath) } ``` @@ -77,7 +77,7 @@ source code. This is accomplished by creating a custom [`ShadowJar`][ShadowJar] 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] } + configurations.setFrom project.configurations.named('runtimeClasspath') } ``` diff --git a/docs/publishing/README.md b/docs/publishing/README.md index 06112ad68..3ddcd7082 100644 --- a/docs/publishing/README.md +++ b/docs/publishing/README.md @@ -160,7 +160,7 @@ 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 -`shadowJar.configurations = [configurations.myConfiguration]`, there is no automatic +`shadowJar.configurations.setFrom(configurations.myConfiguration)`, there is no automatic configuration of the POM file. This automatic configuration occurs _only_ when using the above methods for @@ -402,7 +402,7 @@ It is possible to publish a custom [`ShadowJar`][ShadowJar] task's output via th 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) } + configurations.setFrom(project.configurations.testRuntimeClasspath) } dependencies { @@ -434,7 +434,7 @@ It is possible to publish a custom [`ShadowJar`][ShadowJar] task's output via th 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] } + configurations.setFrom project.configurations.named('testRuntimeClasspath') } dependencies { 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 1d05b6109..e9c7f2813 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 @@ -714,7 +714,7 @@ class JavaPluginsTest : BasePluginTest() { description = 'Create a combined JAR of project and test dependencies' archiveClassifier = 'test' from sourceSets.named('test').map { it.output } - setConfigurations([project.configurations.testRuntimeClasspath]) + configurations.setFrom project.configurations.named('testRuntimeClasspath') manifest { attributes '$mainClassAttributeKey': 'my.Main' } @@ -751,7 +751,7 @@ class JavaPluginsTest : BasePluginTest() { description = 'Create a combined JAR of project and test dependencies' archiveClassifier = 'test' from sourceSets.named('test').map { it.output } - setConfigurations([project.configurations.testRuntimeClasspath]) + configurations.setFrom project.configurations.named('testRuntimeClasspath') manifest { attributes '$mainClassAttributeKey': 'my.Main' } @@ -795,7 +795,7 @@ class JavaPluginsTest : BasePluginTest() { def $dependencyShadowJar = tasks.register('$dependencyShadowJar', ${ShadowJar::class.java.name}) { description = 'Create a shadow JAR of all dependencies' archiveClassifier = 'dep' - setConfigurations([project.configurations.runtimeClasspath]) + configurations.setFrom project.configurations.named('runtimeClasspath') } """ .trimIndent() @@ -1185,7 +1185,7 @@ class JavaPluginsTest : BasePluginTest() { } $shadowJarTask { - setConfigurations([project.configurations.runtimeClasspath]) + configurations.setFrom project.configurations.runtimeClasspath } configurations.runtimeClasspath { 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 1a419d9f5..881777969 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 @@ -305,7 +305,7 @@ class PublishingTest : BasePluginTest() { description = 'Create a combined JAR of project and test dependencies' archiveClassifier = 'tests' from sourceSets.named('test').map { it.output } - setConfigurations([project.configurations.testRuntimeClasspath]) + configurations.setFrom project.configurations.named('testRuntimeClasspath') } """ .trimIndent(), diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt index ee2a853ea..a3fad6456 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt @@ -466,7 +466,8 @@ class RelocationTest : BasePluginTest() { implementation 'junit:junit:3.8.2' } $shadowJarTask { - clearConfigurations() + configurations.unset() + configurations.unsetConvention() relocate('', 'foo/') } """ diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt index c164b08e7..1ebd78d04 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt @@ -42,7 +42,7 @@ constructor(private val softwareComponentFactory: SoftwareComponentFactory) : Pl val taskProvider = registerShadowJarCommon(tasks.named("jar", Jar::class.java)) { task -> task.from(sourceSets.named("main").map { it.output }) - task.addConfiguration(runtimeConfiguration) + task.configurations.convention(provider { runtimeConfiguration }) } artifacts.add(configurations.shadow.name, taskProvider) } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowKmpPlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowKmpPlugin.kt index eb2209ccd..104913ae7 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowKmpPlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowKmpPlugin.kt @@ -36,8 +36,10 @@ public abstract class ShadowKmpPlugin : Plugin { val kotlinJvmMain = target.compilations.named("main") registerShadowJarCommon(tasks.named(target.artifactsTaskName, Jar::class.java)) { task -> task.from(kotlinJvmMain.map { it.output.allOutputs }) - task.addConfiguration( - configurations.getByName(kotlinJvmMain.get().runtimeDependencyConfigurationName) + task.configurations.convention( + provider { + configurations.getByName(kotlinJvmMain.get().runtimeDependencyConfigurationName) + } ) if (!isAtLeastKgp("1.9.0")) return@registerShadowJarCommon diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DependencyFilter.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DependencyFilter.kt index 93291853a..b22ab7af4 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DependencyFilter.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DependencyFilter.kt @@ -7,6 +7,7 @@ import org.gradle.api.artifacts.Dependency import org.gradle.api.artifacts.ProjectDependency import org.gradle.api.artifacts.ResolvedArtifact import org.gradle.api.artifacts.ResolvedDependency +import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.file.FileCollection import org.gradle.api.provider.Provider import org.gradle.api.specs.Spec @@ -22,6 +23,13 @@ public interface DependencyFilter : Serializable { */ public fun resolve(configurations: Collection): FileCollection + /** + * Resolve a [ConfigurableFileCollection] against the [include]/[exclude] rules in the filter. + * + * Any [Configuration] sources within the collection are resolved individually and combined. + */ + public fun resolve(configurations: ConfigurableFileCollection): FileCollection + /** Exclude dependencies that match the provided [spec]. */ public fun exclude(spec: Spec) @@ -68,6 +76,24 @@ public interface DependencyFilter : Serializable { .reduceOrNull { acc, fileCollection -> acc + fileCollection } ?: project.files() } + override fun resolve(configurations: ConfigurableFileCollection): FileCollection { + val extracted = configurations.from.flatMap { source -> extractConfigurations(source) } + return resolve(extracted) + } + + private fun extractConfigurations(source: Any): List = + when (source) { + is Configuration -> listOf(source) + is Provider<*> -> + when (val value = source.orNull) { + is Configuration -> listOf(value) + is Iterable<*> -> value.filterIsInstance() + else -> emptyList() + } + is Iterable<*> -> source.filterIsInstance() + else -> emptyList() + } + override fun exclude(spec: Spec) { excludeSpecs.add(spec) } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index ae7e8a849..4c80e9ecf 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -34,7 +34,6 @@ import org.apache.tools.zip.Zip64Mode import org.apache.tools.zip.ZipOutputStream import org.gradle.api.Action import org.gradle.api.Project -import org.gradle.api.artifacts.Configuration import org.gradle.api.file.ArchiveOperations import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.file.CopySpec @@ -72,9 +71,6 @@ public abstract class ShadowJar : Jar() { project.configurations.findByName(ShadowBasePlugin.CONFIGURATION_NAME) ?: project.files() } - @Transient - private val _sourceConfigurations = mutableListOf() - init { group = LifecycleBasePlugin.BUILD_GROUP description = "Create a combined JAR of project and runtime dependencies" @@ -104,7 +100,7 @@ public abstract class ShadowJar : Jar() { @get:Classpath public open val toMinimize: ConfigurableFileCollection = objectFactory.fileCollection { minimizeJar.map { - if (it) (dependencyFilterForMinimize.resolve(_sourceConfigurations) - apiJars) else emptySet() + if (it) (dependencyFilterForMinimize.resolve(configurations) - apiJars) else emptySet() } } @@ -135,35 +131,13 @@ public abstract class ShadowJar : Jar() { @get:Nested public open val relocators: SetProperty = objectFactory.setProperty() /** - * The resolved files from configurations to include dependencies from. - * - * Do not add to this file collection directly. Use [addConfiguration], [setConfigurations], - * or [clearConfigurations] instead, so that dependency filtering works correctly. + * The configurations to include dependencies from. * * Defaults to a set that contains `runtimeClasspath` or `runtime` configuration. */ @get:Classpath public open val configurations: ConfigurableFileCollection = objectFactory.fileCollection() - /** Add a [Configuration] whose dependencies should be shadowed. */ - public open fun addConfiguration(config: Configuration) { - _sourceConfigurations.add(config) - configurations.from(config) - } - - /** Replace all configurations with the given set. */ - public open fun setConfigurations(configs: Iterable) { - _sourceConfigurations.clear() - configurations.setFrom() - configs.forEach { addConfiguration(it) } - } - - /** Remove all configurations. */ - public open fun clearConfigurations() { - _sourceConfigurations.clear() - configurations.setFrom() - } - @get:Input public open val dependencyFilter: Property = objectFactory.property(DefaultDependencyFilter(project)) @@ -171,7 +145,7 @@ public abstract class ShadowJar : Jar() { /** Final dependencies to be shadowed. */ @get:Classpath public open val includedDependencies: ConfigurableFileCollection = objectFactory.fileCollection { - dependencyFilter.map { df -> df.resolve(_sourceConfigurations) } + dependencyFilter.map { df -> df.resolve(configurations) } } /** diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPropertiesTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPropertiesTest.kt index fb05e4b74..abdab567d 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPropertiesTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPropertiesTest.kt @@ -33,6 +33,7 @@ import org.gradle.api.plugins.JavaPlugin import org.gradle.api.plugins.JavaPlugin.API_CONFIGURATION_NAME import org.gradle.api.plugins.JavaPlugin.COMPILE_ONLY_API_CONFIGURATION_NAME import org.gradle.api.plugins.JavaPlugin.COMPILE_ONLY_CONFIGURATION_NAME +import org.gradle.api.provider.Provider import org.gradle.api.tasks.TaskContainer import org.gradle.api.tasks.TaskProvider import org.gradle.api.tasks.bundling.AbstractArchiveTask @@ -154,7 +155,8 @@ class ShadowPropertiesTest { assertThat(mainClass.orNull).isNull() assertThat(relocationPrefix.get()).isEqualTo(ShadowBasePlugin.SHADOW) - assertThat(configurations.files).isEqualTo(runtimeConfiguration.files) + assertThat(configurations.from.map { (it as Provider<*>).get() }) + .containsOnly(runtimeConfiguration) } } From 131bec5ebfb5e511edde3e03714e25ad37c0456f Mon Sep 17 00:00:00 2001 From: Goooler Date: Fri, 28 Aug 2026 11:51:04 +0800 Subject: [PATCH 3/5] Introduce mergedDependencies and deprecate configurations on ShadowJar --- api/shadow.api | 3 +- docs/configuration/README.md | 2 +- docs/configuration/dependencies/README.md | 21 +++-------- docs/configuration/relocation/README.md | 10 +++--- docs/custom-tasks/README.md | 14 ++++---- docs/publishing/README.md | 6 ++-- .../gradle/plugins/shadow/JavaPluginsTest.kt | 35 ++++++++++++++++--- .../gradle/plugins/shadow/PublishingTest.kt | 2 +- .../gradle/plugins/shadow/RelocationTest.kt | 2 +- .../gradle/plugins/shadow/ShadowJavaPlugin.kt | 3 +- .../gradle/plugins/shadow/ShadowKmpPlugin.kt | 5 ++- .../plugins/shadow/tasks/DependencyFilter.kt | 23 +++++++++--- .../gradle/plugins/shadow/tasks/ShadowJar.kt | 21 +++++++++-- .../plugins/shadow/ShadowPropertiesTest.kt | 6 ++-- 14 files changed, 102 insertions(+), 51 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index 4b1aba460..02e5170e7 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -261,7 +261,7 @@ public abstract class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar public fun getAddMultiReleaseAttribute ()Lorg/gradle/api/provider/Property; public fun getApiJars ()Lorg/gradle/api/file/ConfigurableFileCollection; protected abstract fun getArchiveOperations ()Lorg/gradle/api/file/ArchiveOperations; - public fun getConfigurations ()Lorg/gradle/api/file/ConfigurableFileCollection; + public fun getConfigurations ()Lorg/gradle/api/provider/SetProperty; public fun getDependencyFilter ()Lorg/gradle/api/provider/Property; public fun getDuplicatesStrategy ()Lorg/gradle/api/file/DuplicatesStrategy; public fun getEnableAutoRelocation ()Lorg/gradle/api/provider/Property; @@ -275,6 +275,7 @@ public abstract class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar public fun getMainClass ()Lorg/gradle/api/provider/Property; public fun getManifest ()Lcom/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest; public synthetic fun getManifest ()Lorg/gradle/api/java/archives/Manifest; + public fun getMergedDependencies ()Lorg/gradle/api/file/ConfigurableFileCollection; public fun getMinimizeJar ()Lorg/gradle/api/provider/Property; public fun getMinimizeSpec ()Lcom/github/jengelman/gradle/plugins/shadow/tasks/MinimizeSpec; public fun getR8Classpath ()Lorg/gradle/api/file/ConfigurableFileCollection; diff --git a/docs/configuration/README.md b/docs/configuration/README.md index 07f3475eb..f426dd16b 100644 --- a/docs/configuration/README.md +++ b/docs/configuration/README.md @@ -40,7 +40,7 @@ flowchart TD D4 -->|"No"| D6 end - click A2 href "../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/configurations.html" "ShadowJar.configurations" + click A2 href "../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/merged-dependencies.html" "ShadowJar.mergedDependencies" click A3 href "../api/shadow/com.github.jengelman.gradle.plugins.shadow/-shadow-base-plugin/-companion/-c-o-n-f-i-g-u-r-a-t-i-o-n_-n-a-m-e.html" "ShadowBasePlugin.CONFIGURATION_NAME" click A5 href "../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/included-dependencies.html" "ShadowJar.includedDependencies" click A4 href "../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/dependencies.html" "ShadowJar.dependencies" diff --git a/docs/configuration/dependencies/README.md b/docs/configuration/dependencies/README.md index cb9bc3120..d747059b1 100644 --- a/docs/configuration/dependencies/README.md +++ b/docs/configuration/dependencies/README.md @@ -1,15 +1,15 @@ # Configuring Shadowed Dependencies Shadow configures the default [`ShadowJar`][ShadowJar] task to merge all dependencies from the project's -`runtimeClasspath` configuration into the final JAR. The configurations from which to source dependencies for the -merging can be configured using the [`configurations`][ShadowJar.configurations] property of the +`runtimeClasspath` configuration into the final JAR. The dependencies and files from which to source dependencies for the +merging can be configured using the [`mergedDependencies`][ShadowJar.mergedDependencies] property of the [`ShadowJar`][ShadowJar] task type. === ":material-language-kotlin: build.gradle.kts" ```kotlin tasks.shadowJar { - configurations.setFrom(project.configurations.compileClasspath) + mergedDependencies.setFrom(project.configurations.compileClasspath) } ``` @@ -17,7 +17,7 @@ merging can be configured using the [`configurations`][ShadowJar.configurations] ```groovy tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - configurations.setFrom project.configurations.named('compileClasspath') + mergedDependencies.setFrom project.configurations.named('compileClasspath') } ``` @@ -25,17 +25,6 @@ The above code sample would configure the [`ShadowJar`][ShadowJar] task to merge `compileClasspath` configuration. This means any dependency declared in the `runtimeOnly` configuration would be **not** be included in the final JAR. -> [!WARNING] -> **Required Configuration** -> -> Note the literal use of [`project.configurations`][Project.configurations] when setting the -> [`configurations`][ShadowJar.configurations] attribute of a [`ShadowJar`][ShadowJar] task. -> -> This is **required**. It may be tempting to specify `configurations.setFrom(configurations.compileClasspath)` but -> this will not have the intended effect, as `configurations.compile` will try to delegate to the -> [`configurations`][ShadowJar.configurations] property of the [`ShadowJar`][ShadowJar] task instead of the -> `project`. - ## Embedding Local Jar Files into Your Shadowed JAR The [`ShadowJar`][ShadowJar] task is a subclass of the [`Jar`][Jar] task, which means that the [`Jar.from`][Jar.from] @@ -486,7 +475,7 @@ block provides a method that accepts a `Closure` for selecting dependencies. [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 -[ShadowJar.configurations]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/configurations.html +[ShadowJar.mergedDependencies]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/merged-dependencies.html [ShadowJar.dependencies]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/dependencies.html [ShadowJar]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html [Project.configurations]: https://docs.gradle.org/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project:configurations diff --git a/docs/configuration/relocation/README.md b/docs/configuration/relocation/README.md index 3f649ed76..5528dbc76 100644 --- a/docs/configuration/relocation/README.md +++ b/docs/configuration/relocation/README.md @@ -231,8 +231,8 @@ relocating), you can try out the trick like: ```kotlin tasks.shadowJar { - // Empty configurations list will exclude all dependencies. - configurations.setFrom() + // Empty mergedDependencies will exclude all dependencies. + mergedDependencies.setFrom() relocate("com.example", "shadow.com.example") } ``` @@ -241,14 +241,14 @@ relocating), you can try out the trick like: ```groovy tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - // Empty configurations list will exclude all dependencies. - configurations.setFrom() + // Empty mergedDependencies will exclude all dependencies. + mergedDependencies.setFrom() relocate 'com.example', 'shadow.com.example' } ``` This is useful in some cases, as mentioned in [#759]. See -[Configuring Shadowed Dependencies][configuring-shadowed-dependencies] for more information about `configurations`. +[Configuring Shadowed Dependencies][configuring-shadowed-dependencies] for more information about `mergedDependencies`. ## Relocating with R8 diff --git a/docs/custom-tasks/README.md b/docs/custom-tasks/README.md index 6bfd71faa..b10051090 100644 --- a/docs/custom-tasks/README.md +++ b/docs/custom-tasks/README.md @@ -2,7 +2,7 @@ The built in [`ShadowJar`][ShadowJar] task only provides an output for the `main` source set of the project. It is possible to add arbitrary [`ShadowJar`][ShadowJar] tasks to a project. When doing so, ensure that the -[`configurations`][ShadowJar.configurations] property is specified to inform Shadow which dependencies to merge into the +[`mergedDependencies`][ShadowJar.mergedDependencies] property is specified to inform Shadow which dependencies to merge into the output. === ":material-language-kotlin: build.gradle.kts" @@ -13,7 +13,7 @@ output. archiveClassifier = "test" from(sourceSets.test.map { it.output }) - configurations.setFrom(project.configurations.testRuntimeClasspath) + mergedDependencies.setFrom(project.configurations.testRuntimeClasspath) manifest { // Optionally, set the main class for the JAR. @@ -36,7 +36,7 @@ output. archiveClassifier = 'test' from sourceSets.named('test').map { it.output } - configurations.setFrom project.configurations.named('testRuntimeClasspath') + mergedDependencies.setFrom project.configurations.named('testRuntimeClasspath') manifest { // Optionally, set the main class for the JAR. @@ -59,7 +59,7 @@ The code snippet above will generate a shadowed JAR containing both the `main` a 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`][ShadowJar] task and configuring the -[`configurations`][ShadowJar.configurations] property, but **not** adding any project sources with `from(...)`. +[`mergedDependencies`][ShadowJar.mergedDependencies] property, but **not** adding any project sources with `from(...)`. === ":material-language-kotlin: build.gradle.kts" @@ -67,7 +67,7 @@ code. This is accomplished by creating a custom [`ShadowJar`][ShadowJar] task an val dependencyShadowJar = tasks.register("dependencyShadowJar") { description = "Create a shadow JAR of all dependencies" archiveClassifier = "dep" - configurations.setFrom(project.configurations.runtimeClasspath) + mergedDependencies.setFrom(project.configurations.runtimeClasspath) } ``` @@ -77,7 +77,7 @@ code. This is accomplished by creating a custom [`ShadowJar`][ShadowJar] task an tasks.register('dependencyShadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { description = 'Create a shadow JAR of all dependencies' archiveClassifier = 'dep' - configurations.setFrom project.configurations.named('runtimeClasspath') + mergedDependencies.setFrom project.configurations.named('runtimeClasspath') } ``` @@ -86,5 +86,5 @@ configuration. The standard `jar` task will still produce a JAR with only the pr [Jar]: https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html -[ShadowJar.configurations]: ../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/configurations.html +[ShadowJar.mergedDependencies]: ../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/merged-dependencies.html [ShadowJar]: ../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html diff --git a/docs/publishing/README.md b/docs/publishing/README.md index 30742d483..34303520b 100644 --- a/docs/publishing/README.md +++ b/docs/publishing/README.md @@ -156,7 +156,7 @@ The Shadow plugin provides a custom configuration (`configurations.shadow`) to s 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 -`shadowJar.configurations.setFrom(configurations.myConfiguration)`, there is no automatic configuration of the POM file. +`shadowJar.mergedDependencies.setFrom(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. @@ -392,7 +392,7 @@ It is possible to publish a custom [`ShadowJar`][ShadowJar] task's output via th description = "Create a combined JAR of project and test dependencies" archiveClassifier = "tests" from(sourceSets.test.map { it.output }) - configurations.setFrom(project.configurations.testRuntimeClasspath) + mergedDependencies.setFrom(project.configurations.testRuntimeClasspath) } dependencies { @@ -424,7 +424,7 @@ It is possible to publish a custom [`ShadowJar`][ShadowJar] task's output via th description = 'Create a combined JAR of project and test dependencies' archiveClassifier = 'tests' from sourceSets.named('test').map { it.output } - configurations.setFrom project.configurations.named('testRuntimeClasspath') + mergedDependencies.setFrom project.configurations.named('testRuntimeClasspath') } dependencies { 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 621c2da9d..10b06965f 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 @@ -693,7 +693,7 @@ class JavaPluginsTest : BasePluginTest() { | description = 'Create a combined JAR of project and test dependencies' | archiveClassifier = 'test' | from sourceSets.named('test').map { it.output } - | configurations.setFrom project.configurations.named('testRuntimeClasspath') + | mergedDependencies.from project.configurations.named('testRuntimeClasspath') | manifest { | attributes '$mainClassAttributeKey': 'my.Main' | } @@ -729,7 +729,7 @@ class JavaPluginsTest : BasePluginTest() { | description = 'Create a combined JAR of project and test dependencies' | archiveClassifier = 'test' | from sourceSets.named('test').map { it.output } - | configurations.setFrom project.configurations.named('testRuntimeClasspath') + | mergedDependencies.from project.configurations.named('testRuntimeClasspath') | manifest { | attributes '$mainClassAttributeKey': 'my.Main' | } @@ -772,7 +772,7 @@ class JavaPluginsTest : BasePluginTest() { |def $dependencyShadowJar = tasks.register('$dependencyShadowJar', ${ShadowJar::class.java.name}) { | description = 'Create a shadow JAR of all dependencies' | archiveClassifier = 'dep' - | configurations.setFrom project.configurations.named('runtimeClasspath') + | mergedDependencies.from project.configurations.named('runtimeClasspath') |} """ .trimMargin() @@ -790,6 +790,33 @@ class JavaPluginsTest : BasePluginTest() { } } + @Test + fun registerCustomShadowJarUsingDeprecatedConfigurations() { + val mainClassEntry = writeClass() + val legacyShadowJar = "legacyShadowJar" + + projectScript.appendText( + """ + |dependencies { + | implementation 'junit:junit:3.8.2' + |} + |def $legacyShadowJar = tasks.register('$legacyShadowJar', ${ShadowJar::class.java.name}) { + | description = 'Create a shadow JAR of all dependencies using deprecated configurations' + | archiveClassifier = 'legacy' + | configurations = project.configurations.named('runtimeClasspath').map { [it] } + |} + """ + .trimMargin() + ) + + runWithSuccess("jar", legacyShadowJar) + + assertThat(jarPath("build/libs/my-1.0-legacy.jar")).useAll { + containsOnly(*junitEntries, *manifestEntries) + transform { it.mainAttrSize }.isEqualTo(1) + } + } + @Test fun registerCustomShadowJarWithoutShadowR8Configuration() { val customShadowJar = "customShadowJar" @@ -1180,7 +1207,7 @@ class JavaPluginsTest : BasePluginTest() { |} | |$shadowJarTask { - | configurations.setFrom project.configurations.runtimeClasspath + | mergedDependencies.from project.configurations.runtimeClasspath |} | |configurations.runtimeClasspath { 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 cfbe34d79..4fe103880 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 @@ -295,7 +295,7 @@ class PublishingTest : BasePluginTest() { | description = 'Create a combined JAR of project and test dependencies' | archiveClassifier = 'tests' | from sourceSets.named('test').map { it.output } - | configurations.setFrom project.configurations.named('testRuntimeClasspath') + | mergedDependencies.from project.configurations.named('testRuntimeClasspath') |} """ .trimMargin(), diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt index afa60205b..a6b50d9bc 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt @@ -460,7 +460,7 @@ class RelocationTest : BasePluginTest() { | implementation 'junit:junit:3.8.2' |} |$shadowJarTask { - | configurations.setFrom() + | mergedDependencies.setFrom() | relocate('', 'foo/') |} """ diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt index 00dcf810a..2c8e8e6a5 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt @@ -39,7 +39,8 @@ constructor(private val softwareComponentFactory: SoftwareComponentFactory) : Pl val taskProvider = registerShadowJarCommon(tasks.named("jar", Jar::class.java)) { task -> task.from(sourceSets.named("main").map { it.output }) - task.configurations.convention(provider { runtimeConfiguration }) + @Suppress("DEPRECATION") + task.configurations.convention(provider { listOf(runtimeConfiguration) }) } artifacts.add(configurations.shadow.name, taskProvider) } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowKmpPlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowKmpPlugin.kt index cbd73d493..c066de1ff 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowKmpPlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowKmpPlugin.kt @@ -36,8 +36,11 @@ public abstract class ShadowKmpPlugin : Plugin { val kotlinJvmMain = target.compilations.named("main") registerShadowJarCommon(tasks.named(target.artifactsTaskName, Jar::class.java)) { task -> task.from(kotlinJvmMain.map { it.output.allOutputs }) + @Suppress("DEPRECATION") task.configurations.convention( - kotlinJvmMain.flatMap { configurations.named(it.runtimeDependencyConfigurationName) } + kotlinJvmMain + .flatMap { configurations.named(it.runtimeDependencyConfigurationName) } + .map { listOf(it) } ) if (!isAtLeastKgp("1.9.0")) return@registerShadowJarCommon diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DependencyFilter.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DependencyFilter.kt index b6a0ba9a8..b79f259e7 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DependencyFilter.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DependencyFilter.kt @@ -29,7 +29,7 @@ public interface DependencyFilter { * * Any [Configuration] sources within the collection are resolved individually and combined. */ - public fun resolve(configurations: ConfigurableFileCollection): FileCollection + public fun resolve(dependencies: ConfigurableFileCollection): FileCollection /** Exclude dependencies that match the provided [spec]. */ public fun exclude(spec: Spec) @@ -81,9 +81,24 @@ public interface DependencyFilter { .reduceOrNull { acc, fileCollection -> acc + fileCollection } ?: project.files() } - override fun resolve(configurations: ConfigurableFileCollection): FileCollection { - val extracted = configurations.from.flatMap { source -> extractConfigurations(source) } - return resolve(extracted) + override fun resolve(dependencies: ConfigurableFileCollection): FileCollection { + val extracted = dependencies.from.flatMap { source -> extractConfigurations(source) } + val excludedFiles = + project.files( + project.provider { + val includes = mutableSetOf() + val excludes = mutableSetOf() + extracted.forEach { config -> + resolve( + dependencies = config.resolvedConfiguration.firstLevelModuleDependencies, + includedDependencies = includes, + excludedDependencies = excludes, + ) + } + excludes.flatMap { it.moduleArtifacts.map(ResolvedArtifact::getFile) } + } + ) + return dependencies - excludedFiles } private fun extractConfigurations(source: Any): List = diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 0646db30a..1a60c0275 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -41,6 +41,7 @@ import kotlin.reflect.full.hasAnnotation import org.gradle.api.Action import org.gradle.api.GradleException import org.gradle.api.Project +import org.gradle.api.artifacts.Configuration import org.gradle.api.file.ArchiveOperations import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.file.CopySpec @@ -118,7 +119,7 @@ public abstract class ShadowJar : Jar() { @get:Classpath public open val toMinimize: ConfigurableFileCollection = objectFactory.fileCollection { _minimizeJar.map { - if (it) (_minimizeSpec.resolve(configurations) - apiJars) else emptySet() + if (it) (_minimizeSpec.resolve(mergedDependencies) - apiJars) else emptySet() } } @@ -174,8 +175,22 @@ public abstract class ShadowJar : Jar() { * * Defaults to a set that contains `runtimeClasspath` or `runtime` configuration. */ + @Deprecated( + message = "Use `mergedDependencies` instead. This property will be removed in Shadow 10.", + replaceWith = ReplaceWith("mergedDependencies.from(configurations)"), + ) + @get:Classpath + public open val configurations: SetProperty = objectFactory.setProperty() + + /** + * The dependencies and files to be merged into the shadow JAR. + * + * Defaults to a file collection that contains `runtimeClasspath` or `runtime` configuration. + */ @get:Classpath - public open val configurations: ConfigurableFileCollection = objectFactory.fileCollection() + public open val mergedDependencies: ConfigurableFileCollection = objectFactory.fileCollection { + @Suppress("DEPRECATION") configurations + } @get:Internal // The resolved result is tracked by includedDependencies. public open val dependencyFilter: Property = @@ -184,7 +199,7 @@ public abstract class ShadowJar : Jar() { /** Final dependencies to be shadowed. */ @get:Classpath public open val includedDependencies: ConfigurableFileCollection = objectFactory.fileCollection { - dependencyFilter.map { df -> df.resolve(configurations) } + dependencyFilter.map { df -> df.resolve(mergedDependencies) } } /** diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPropertiesTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPropertiesTest.kt index 97bebd4d3..c3004ca85 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPropertiesTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPropertiesTest.kt @@ -33,7 +33,6 @@ import org.gradle.api.plugins.JavaPlugin import org.gradle.api.plugins.JavaPlugin.API_CONFIGURATION_NAME import org.gradle.api.plugins.JavaPlugin.COMPILE_ONLY_API_CONFIGURATION_NAME import org.gradle.api.plugins.JavaPlugin.COMPILE_ONLY_CONFIGURATION_NAME -import org.gradle.api.provider.Provider import org.gradle.api.tasks.TaskContainer import org.gradle.api.tasks.TaskProvider import org.gradle.api.tasks.bundling.AbstractArchiveTask @@ -162,8 +161,9 @@ class ShadowPropertiesTest { ) assertThat(relocationPrefix.get()).isEqualTo(ShadowBasePlugin.SHADOW) - assertThat(configurations.from.map { (it as Provider<*>).get() }) - .containsOnly(runtimeConfiguration) + assertThat(mergedDependencies.files) + .containsOnly(*runtimeConfiguration.files.toTypedArray()) + assertThat(@Suppress("DEPRECATION") configurations.get()).containsOnly(runtimeConfiguration) } } From abbe3d302d267a9b0f8f0c8e45a29b32b1fd9cc5 Mon Sep 17 00:00:00 2001 From: Goooler Date: Fri, 28 Aug 2026 12:15:52 +0800 Subject: [PATCH 4/5] Use assignment syntax in Groovy DSL documentation examples --- docs/configuration/dependencies/README.md | 2 +- docs/custom-tasks/README.md | 4 ++-- docs/publishing/README.md | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/configuration/dependencies/README.md b/docs/configuration/dependencies/README.md index d747059b1..d7bbdcfb3 100644 --- a/docs/configuration/dependencies/README.md +++ b/docs/configuration/dependencies/README.md @@ -17,7 +17,7 @@ merging can be configured using the [`mergedDependencies`][ShadowJar.mergedDepen ```groovy tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - mergedDependencies.setFrom project.configurations.named('compileClasspath') + mergedDependencies = project.configurations.compileClasspath } ``` diff --git a/docs/custom-tasks/README.md b/docs/custom-tasks/README.md index b10051090..08337a723 100644 --- a/docs/custom-tasks/README.md +++ b/docs/custom-tasks/README.md @@ -36,7 +36,7 @@ output. archiveClassifier = 'test' from sourceSets.named('test').map { it.output } - mergedDependencies.setFrom project.configurations.named('testRuntimeClasspath') + mergedDependencies = project.configurations.testRuntimeClasspath manifest { // Optionally, set the main class for the JAR. @@ -77,7 +77,7 @@ code. This is accomplished by creating a custom [`ShadowJar`][ShadowJar] task an tasks.register('dependencyShadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { description = 'Create a shadow JAR of all dependencies' archiveClassifier = 'dep' - mergedDependencies.setFrom project.configurations.named('runtimeClasspath') + mergedDependencies = project.configurations.runtimeClasspath } ``` diff --git a/docs/publishing/README.md b/docs/publishing/README.md index 34303520b..4ec7b6543 100644 --- a/docs/publishing/README.md +++ b/docs/publishing/README.md @@ -156,7 +156,7 @@ The Shadow plugin provides a custom configuration (`configurations.shadow`) to s 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 -`shadowJar.mergedDependencies.setFrom(configurations.myConfiguration)`, there is no automatic configuration of the POM file. +`shadowJar.mergedDependencies = 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. @@ -424,7 +424,7 @@ It is possible to publish a custom [`ShadowJar`][ShadowJar] task's output via th description = 'Create a combined JAR of project and test dependencies' archiveClassifier = 'tests' from sourceSets.named('test').map { it.output } - mergedDependencies.setFrom project.configurations.named('testRuntimeClasspath') + mergedDependencies = project.configurations.testRuntimeClasspath } dependencies { From f3c30669a6e2e382bc997fb1d9f1436541a4a748 Mon Sep 17 00:00:00 2001 From: Goooler Date: Fri, 28 Aug 2026 13:33:40 +0800 Subject: [PATCH 5/5] Revert "Use assignment syntax in Groovy DSL documentation examples" This reverts commit abbe3d302d267a9b0f8f0c8e45a29b32b1fd9cc5. --- docs/configuration/dependencies/README.md | 2 +- docs/custom-tasks/README.md | 4 ++-- docs/publishing/README.md | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/configuration/dependencies/README.md b/docs/configuration/dependencies/README.md index d7bbdcfb3..d747059b1 100644 --- a/docs/configuration/dependencies/README.md +++ b/docs/configuration/dependencies/README.md @@ -17,7 +17,7 @@ merging can be configured using the [`mergedDependencies`][ShadowJar.mergedDepen ```groovy tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - mergedDependencies = project.configurations.compileClasspath + mergedDependencies.setFrom project.configurations.named('compileClasspath') } ``` diff --git a/docs/custom-tasks/README.md b/docs/custom-tasks/README.md index 08337a723..b10051090 100644 --- a/docs/custom-tasks/README.md +++ b/docs/custom-tasks/README.md @@ -36,7 +36,7 @@ output. archiveClassifier = 'test' from sourceSets.named('test').map { it.output } - mergedDependencies = project.configurations.testRuntimeClasspath + mergedDependencies.setFrom project.configurations.named('testRuntimeClasspath') manifest { // Optionally, set the main class for the JAR. @@ -77,7 +77,7 @@ code. This is accomplished by creating a custom [`ShadowJar`][ShadowJar] task an tasks.register('dependencyShadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { description = 'Create a shadow JAR of all dependencies' archiveClassifier = 'dep' - mergedDependencies = project.configurations.runtimeClasspath + mergedDependencies.setFrom project.configurations.named('runtimeClasspath') } ``` diff --git a/docs/publishing/README.md b/docs/publishing/README.md index 4ec7b6543..34303520b 100644 --- a/docs/publishing/README.md +++ b/docs/publishing/README.md @@ -156,7 +156,7 @@ The Shadow plugin provides a custom configuration (`configurations.shadow`) to s 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 -`shadowJar.mergedDependencies = configurations.myConfiguration`, there is no automatic configuration of the POM file. +`shadowJar.mergedDependencies.setFrom(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. @@ -424,7 +424,7 @@ It is possible to publish a custom [`ShadowJar`][ShadowJar] task's output via th description = 'Create a combined JAR of project and test dependencies' archiveClassifier = 'tests' from sourceSets.named('test').map { it.output } - mergedDependencies = project.configurations.testRuntimeClasspath + mergedDependencies.setFrom project.configurations.named('testRuntimeClasspath') } dependencies {