Fix Gradle 9.4 variant resolution cycle with gradle-consistent-versions#155
Open
Fix Gradle 9.4 variant resolution cycle with gradle-consistent-versions#155
gradle-consistent-versions#155Conversation
Generate changelog in
|
✅ Successfully generated changelog entry!Need to regenerate?Simply interact with the changelog bot comment again to regenerate these entries. 🔄 Changelog entries were re-generated at Fri, 13 Mar 2026 13:25:25 UTC!📋Changelog Preview💡 Improvements
|
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.
Before this PR
On Gradle 9.4.0, applying
gradle-idea-language-injectoralongsidegradle-consistent-versions(GCV) fails with:Root cause
gradle/gradle#36245 changed
extendsFromfrom eagerly storing parent configs in aSet<Configuration>to lazily storing them asProvider<Configuration>. This means that during variant model computation, evaluating a consumable config's state can now trigger dependency graph traversal as a side effect — previously iteratingextendsFromwas just reading a pre-populated set with no side effects.The old
IdeaLanguageInjectorProjectPluginpublished resolved JAR files as artifacts of the consumable configuration via a provider:where
sourceSetArtifactsresolved each source set'scompileClasspathand collected the resultingFileobjects. When Gradle evaluates this artifact provider during variant model computation, it triggerscompileClasspathresolution. GCV makes all configs extendrootConfiguration, which has aProjectDependencyon the root project. Resolving that dependency requires the root project's variant model — which is already being computed. This creates the cycle:This was architecturally wrong: a consumable configuration was eagerly resolving another configuration (
compileClasspath) inside an artifact provider.Approaches explored
Three fixes were explored:
Fix Gradle 9.4.0 variant resolution failure with
gradle-consistent-versions#149: CallVersionRecommendationsExtension.excludeConfigurations(...)to opt our config out of GCV. Works, but requires a compile-time dependency on GCV and treats the symptom rather than the cause.GCV-side fix: Skip
extendsFromfor consumable-only configs in GCV. This changes how GCV works and the real issue is thatidea-language-injectorshouldn't be resolving a configuration inside an artifact provider.Fix Gradle 9.4 variant model cycle without GCV workaround #150: Stop applying the project plugin to the root project (
allprojects→subprojects) and scan root-project dependencies via an artifact view directly. Avoids the cycle since the root no longer has a consumable config. However this special-cases the root project and introduces two code paths.After this PR
Takes a different approach: instead of having the consumable config publish pre-resolved JAR files, it now participates properly in Gradle's dependency graph.
Each subproject's consumable config extends from the same declarable configurations that
compileClasspathextends from (api,implementation,compileOnly, etc.). This means the consumable config carries dependency metadata, not resolved files — so evaluating the extends chain during variant model computation has no side effects.Since external libraries only publish standard variants (
java-api,java-runtime) and don't know about our customUsageattribute (idea-language-injector-outgoing), anAttributeCompatibilityRuleis registered to tell Gradle thatjava-apivariants are compatible when the consumer requestsidea-language-injector-outgoing. This lets transitive dependencies resolve through theirjava-apivariant.Testing
compatible_with_consistent_versionstest verifiescompileJavasucceeds with GCV applied