Replace SetProperty<Configuration> with ConfigurableFileCollection on ShadowJar#2003
Draft
tresat wants to merge 1 commit intoGradleUp:mainfrom
Draft
Replace SetProperty<Configuration> with ConfigurableFileCollection on ShadowJar#2003tresat wants to merge 1 commit intoGradleUp:mainfrom
tresat wants to merge 1 commit intoGradleUp:mainfrom
Conversation
… ShadowJar Add addConfiguration(), setConfigurations(), clearConfigurations() methods that maintain both the CC-safe ConfigurableFileCollection and an internal Configuration list for DependencyFilter compatibility.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Replacing
SetProperty<Configuration>withConfigurableFileCollectionSummary of Changes
This change replaces the
configurationsproperty onShadowJarfromSetProperty<Configuration>toConfigurableFileCollection, and introduces three new public methods as the sole API for managing which Gradle configurations are shadowed:addConfiguration(config: Configuration)— add a configuration to shadowsetConfigurations(configs: Iterable<Configuration>)— replace all configurationsclearConfigurations()— remove all configurationsAn internal
MutableList<Configuration>(_sourceConfigurations) tracks the actualConfigurationobjects for dependency-level filtering, while theConfigurableFileCollectionserves as the configuration-cache-safe input property.Files Modified
ShadowJar.kttoMinimizeandincludedDependenciesproviders to reference internal listShadowJavaPlugin.kttask.configurations.convention(...)replaced withtask.addConfiguration(runtimeConfiguration)ShadowKmpPlugin.kttask.addConfiguration(...)replaces lazy conventionShadowPropertiesTest.ktconfigurations.get()toconfigurations.filesJavaPluginsTest.ktsetConfigurations([...])RelocationTest.ktclearConfigurations()PublishingTest.ktsetConfigurations([...])The Problem:
ConfigurationCannot Survive a CC Round-TripConfigurationcannot survive a configuration cache serialization round-trip. When the CC serializes aConfigurationobject, Gradle's codec produces aFileCollection(ResolvingFileCollection) on deserialization — not aConfiguration. This causes a task reconstruction failure at CC load time, before the task ever executes.The failure occurs when Gradle tries to restore a previously cached task. The CC deserializes the stored value (a
ResolvingFileCollection) and attempts to inject it back into theSetProperty<Configuration>. The property performs a type check: "IsResolvingFileCollectionassignable toConfiguration?" It is not —ResolvingFileCollectionimplementsFileCollectionbut notConfiguration. The build fails immediately:This is not a
.get()call failure or aClassCastException. The CC cannot even reconstruct the task object. The deserialization process fails during task restoration, which happens before any task action runs. Even if reconstruction somehow succeeded, calling.get()would returnFileCollectionobjects whereConfigurationis expected — Shadow'sDependencyFilterrelies onConfiguration.resolvedConfiguration.firstLevelModuleDependenciesto filter by module coordinates, which would fail immediately.Since Gradle 8.0, the configuration cache always performs a store-then-load round-trip, even on the first build (a cache miss). Gradle configures the build, serializes the task graph, and then runs from the deserialized state — never from the live configuration objects. A type that cannot survive a round-trip will cause the build to fail on the very first invocation with the CC enabled.
The Gradle PR: gradle/gradle#37466
gradle/gradle#37466 addresses gradle/gradle#19122 by moving the failure from a confusing load-time type mismatch to a clear store-time rejection.
The PR adds validation in the property codec encode paths (
PropertyCodec.encodeThis(),SetPropertyCodec.encodeThis(),ListPropertyCodec.encodeThis(),MapPropertyCodec.encodeThis()) that checks whether the property's type argument isConfigurationorSourceDirectorySet. If so, the build fails immediately at CC store time with:Key details
@Input,@Internal,@InputFiles, and@Classpath. The@Classpathannotation does not bypass the check.SetProperty<Configuration>has never been set, its backing field is null, a null marker is written directly, andSetPropertyCodec.encodeThis()is never invoked.UnsupportedPropertyValueExceptionWhy Shadow Works Today
Despite the round-trip problem described above, Gradle's own Shadow plugin smoke test runs a full CC store-then-load cycle (build
shadowJar,clean, buildshadowJaragain) and passes with the configuration cache enabled. The smoke test uses the plugin's default configuration — it does not set theconfigurationsproperty explicitly.The most likely explanation is that the
@Classpathannotation causes the CC to handle this property through the file-input serialization path rather than throughSetPropertyCodec. In this path, eachConfiguration(which extendsFileCollection) is resolved to its constituent file paths at store time. On load, only the file-input tracking is restored — the CC does not attempt to reconstruct theSetProperty<Configuration>value by injecting a deserializedFileCollectionback into it, so the type mismatch never occurs.This is consistent with the PR's design: it adds rejection logic inside
SetPropertyCodec.encodeThis(). If@Classpathproperties bypass this codec entirely, the new rejection would not fire for Shadow's current property — but the PR's intent is to reject this pattern regardless, and future Gradle changes may close this bypass.How This Change Makes Shadow Compatible
The property replacement
ConfigurableFileCollectionis the type Gradle's own error message recommends. The configuration cache serializes it as resolved file paths — no type mismatch, no round-trip problem. This resolves both the existing latent CC load failure and the new store-time rejection that gradle/gradle#37466 introduces.Preserving dependency filtering
The
DependencyFiltercontract requires actualConfigurationobjects to walk the dependency tree:An internal
MutableList<Configuration>(_sourceConfigurations) tracks theConfigurationobjects. The three public methods (addConfiguration,setConfigurations,clearConfigurations) serve as a chokepoint that keeps the file collection and the internal list in sync:Why the internal list is safe on cache restore
_sourceConfigurationsis annotated@Transientand is not a Gradle managed property — the configuration cache does not serialize it. After a cache restore, it's an empty list. This is safe because:includedDependenciesandtoMinimizeareConfigurableFileCollectionproperties whose providers reference_sourceConfigurationsDependencyFilter.resolve(_sourceConfigurations), and produce resolved file setsConfigurableFileCollections with their resolved files_sourceConfigurationsis never accessedDSL migration
The old
SetPropertysupported Groovy's property assignment syntax and Gradle's provider-awareset(Provider<Iterable<T>>):The new API uses explicit method calls:
This is a breaking public API change for users who configure
configurationsdirectly in their build scripts.