Configuration phase ordering and laziness help with project extensions

Greetings,

I have a ResourceConfigurationsPlugin for adding new resources artifact variants to projects. It’s an enhancement of the plugin that originally came out of the discussions in this thread: How to properly append to the JavaExec and Test task classpaths? - #20 by ianbrandt.

The plugin now adds a ResourceConfigurations project extension with some nested types (including my own AttributeContainer implementation for declaration of the variant attributes, as all the ones that come with Gradle are internal for some reason) that can be used to declare and configure the variants to be added. For each of those, it adds a consumable configuration with the attributes for the variant, a project artifact via that configuration, and a dependency handler extension (similar to e.g. testFixtures(...)) to simplify declaring dependencies on the variant by those same attributes.

Example uses of the plugin are here and here.

I originally tried making the project extension a NamedDomainObjectContainer, and then using its configureEach { ... } from the plugin’s apply(...) method to do all that setup, but I ran into configuration phase ordering issues just as described here: Applying similar sets of dependencies to different source sets, using a custom plugin - #9 by dmh.

I was able to use the solution idea from the next post in that thread of not registering a NamedDomainObjectContainer, and instead just using the nested DSL functions of my own extension class to do the configuration directly.

While that approach works, I’m not clear on whether I may now be doing the configuration too eagerly, undermining configuration avoidance. For example, if I add some println()s to where I’m registering the consumable configuration, adding the project artifact variant, and adding dependency handler extension, I see their output even when just running gradlew help for the project. Maybe that’s necessary when working with dependency handler extensions and attribute containers and such, but I just don’t yet have a deep enough understanding of Gradle’s configuration phase requirements and ordering as it relates to variant production and consumption to know.

If anyone would be willing to take a look at my plugin, and tell me whether I’m doing things in the most optimal way with current Gradle (8.13), the feedback and learnings would be most appreciated.

2 Likes

Here are some build scans in case they’re of any help:

As an aside, in case they’re of any interest, I filed a few issues that came up during this effort:

An update: Based on feedback from Please supply a non-internal AttributeContainer implementation · Issue #32983 · gradle/gradle · GitHub, I’ve switched from using a custom MapBackedAttributeContainer to AttributeContainer.() -> Unit configure actions instead: Comparing d022a4885a3f32f3a1b2670878fc95f026e16ba9...3d973b897cda593a071671e55c7d3e008bb350a6 · sdkotlin/sd-kotlin-spring-talks · GitHub.

Updated browse commit and entry point: sd-kotlin-spring-talks/build-logic/src/main/kotlin/org/sdkotlin/buildlogic/plugins/resources/ResourceConfigurationsPlugin.kt at 3d973b897cda593a071671e55c7d3e008bb350a6 · sdkotlin/sd-kotlin-spring-talks · GitHub.

I tried making my dependency helper lazy, but it still gets executed with gradlew help. I’m not sure whether that’s fallout of eagerness elsewhere, or it’s just an unavoidable part of Gradle working out the project dependencies.

or it’s just an unavoidable part of Gradle working out the project dependencies.

When executing help?
That sounds quite unlikely.
But hard to say from abroad without hands-on.

Probably time to upgrade the debugging from System.out to breakpoint :slight_smile:

1 Like

Ah, here’s what I get with:

RuntimeException("Creating $notation...").printStackTrace()

Maybe Kotlin Gradle Plugin eagerness?

> Configure project :subprojects:custom-resources-intermediary
java.lang.RuntimeException: Creating project ':subprojects:custom-resources'...
	at org.sdkotlin.buildlogic.artifacts.dsl.AttributesDependencyCreationExtension$invoke$1.call(AttributesDependencyCreationExtension.kt:26)
	at org.sdkotlin.buildlogic.artifacts.dsl.AttributesDependencyCreationExtension$invoke$1.call(AttributesDependencyCreationExtension.kt:21)
	at org.gradle.api.internal.provider.DefaultProvider.calculateOwnValue(DefaultProvider.java:73)
	at org.gradle.api.internal.provider.AbstractMinimalProvider.calculateValue(AbstractMinimalProvider.java:117)
	at org.gradle.api.internal.provider.TransformBackedProvider.calculateOwnValue(TransformBackedProvider.java:82)
	at org.gradle.api.internal.provider.AbstractMinimalProvider.calculateValue(AbstractMinimalProvider.java:117)
	at org.gradle.api.internal.provider.Collectors$ElementFromProvider.collectEntries(Collectors.java:105)
	at org.gradle.api.internal.provider.Collectors$TypedCollector.collectEntries(Collectors.java:371)
	at org.gradle.api.internal.provider.Collectors$TypedCollector.collectInto(Collectors.java:366)
	at org.gradle.api.internal.collections.AbstractIterationOrderRetainingElementSource$Element.realize(AbstractIterationOrderRetainingElementSource.java:353)
	at org.gradle.api.internal.collections.AbstractIterationOrderRetainingElementSource.realizePending(AbstractIterationOrderRetainingElementSource.java:140)
	at org.gradle.api.internal.collections.IterationOrderRetainingSetElementSource.iterator(IterationOrderRetainingSetElementSource.java:47)
	at org.gradle.api.internal.DefaultDomainObjectCollection.iterator(DefaultDomainObjectCollection.java:152)
	at org.gradle.api.internal.DelegatingDomainObjectSet.iterator(DelegatingDomainObjectSet.java:145)
	at org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatisticsUtilsKt.reportLibrariesVersions(kotlinBuildStatisticsUtils.kt:247)
	at org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatisticsUtilsKt.collectProjectConfigurationTimeMetrics(kotlinBuildStatisticsUtils.kt:120)
	at org.jetbrains.kotlin.gradle.plugin.statistics.KotlinProjectConfigurationMetrics.collectMetrics$kotlin_gradle_plugin_common(FusMetrics.kt:283)
	at org.jetbrains.kotlin.gradle.plugin.statistics.FinalizeConfigurationFusMetricActionKt$FinalizeConfigurationFusMetricAction$1.invokeSuspend(FinalizeConfigurationFusMetricAction.kt:29)
Full Stacktrace
> Configure project :subprojects:custom-resources-intermediary
java.lang.RuntimeException: Creating project ':subprojects:custom-resources'...
	at org.sdkotlin.buildlogic.artifacts.dsl.AttributesDependencyCreationExtension$invoke$1.call(AttributesDependencyCreationExtension.kt:26)
	at org.sdkotlin.buildlogic.artifacts.dsl.AttributesDependencyCreationExtension$invoke$1.call(AttributesDependencyCreationExtension.kt:21)
	at org.gradle.api.internal.provider.DefaultProvider.calculateOwnValue(DefaultProvider.java:73)
	at org.gradle.api.internal.provider.AbstractMinimalProvider.calculateValue(AbstractMinimalProvider.java:117)
	at org.gradle.api.internal.provider.TransformBackedProvider.calculateOwnValue(TransformBackedProvider.java:82)
	at org.gradle.api.internal.provider.AbstractMinimalProvider.calculateValue(AbstractMinimalProvider.java:117)
	at org.gradle.api.internal.provider.Collectors$ElementFromProvider.collectEntries(Collectors.java:105)
	at org.gradle.api.internal.provider.Collectors$TypedCollector.collectEntries(Collectors.java:371)
	at org.gradle.api.internal.provider.Collectors$TypedCollector.collectInto(Collectors.java:366)
	at org.gradle.api.internal.collections.AbstractIterationOrderRetainingElementSource$Element.realize(AbstractIterationOrderRetainingElementSource.java:353)
	at org.gradle.api.internal.collections.AbstractIterationOrderRetainingElementSource.realizePending(AbstractIterationOrderRetainingElementSource.java:140)
	at org.gradle.api.internal.collections.IterationOrderRetainingSetElementSource.iterator(IterationOrderRetainingSetElementSource.java:47)
	at org.gradle.api.internal.DefaultDomainObjectCollection.iterator(DefaultDomainObjectCollection.java:152)
	at org.gradle.api.internal.DelegatingDomainObjectSet.iterator(DelegatingDomainObjectSet.java:145)
	at org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatisticsUtilsKt.reportLibrariesVersions(kotlinBuildStatisticsUtils.kt:247)
	at org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatisticsUtilsKt.collectProjectConfigurationTimeMetrics(kotlinBuildStatisticsUtils.kt:120)
	at org.jetbrains.kotlin.gradle.plugin.statistics.KotlinProjectConfigurationMetrics.collectMetrics$kotlin_gradle_plugin_common(FusMetrics.kt:283)
	at org.jetbrains.kotlin.gradle.plugin.statistics.FinalizeConfigurationFusMetricActionKt$FinalizeConfigurationFusMetricAction$1.invokeSuspend(FinalizeConfigurationFusMetricAction.kt:29)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
	at kotlin.coroutines.SafeContinuation.resumeWith(SafeContinuationJvm.kt:41)
	at org.jetbrains.kotlin.gradle.plugin.KotlinPluginLifecycleImpl$await$2$1.invoke(KotlinPluginLifecycleImpl.kt:180)
	at org.jetbrains.kotlin.gradle.plugin.KotlinPluginLifecycleImpl$await$2$1.invoke(KotlinPluginLifecycleImpl.kt:179)
	at org.jetbrains.kotlin.gradle.plugin.KotlinPluginLifecycleImpl.loopIfNecessary(KotlinPluginLifecycleImpl.kt:106)
	at org.jetbrains.kotlin.gradle.plugin.KotlinPluginLifecycleImpl.executeCurrentStageAndScheduleNext(KotlinPluginLifecycleImpl.kt:83)
	at org.jetbrains.kotlin.gradle.plugin.KotlinPluginLifecycleImpl.access$executeCurrentStageAndScheduleNext(KotlinPluginLifecycleImpl.kt:23)
	at org.jetbrains.kotlin.gradle.plugin.KotlinPluginLifecycleImpl$executeCurrentStageAndScheduleNext$3.execute(KotlinPluginLifecycleImpl.kt:95)
	at org.jetbrains.kotlin.gradle.plugin.KotlinPluginLifecycleImpl$executeCurrentStageAndScheduleNext$3.execute(KotlinPluginLifecycleImpl.kt:94)
	at org.gradle.internal.code.DefaultUserCodeApplicationContext$CurrentApplication$1.execute(DefaultUserCodeApplicationContext.java:124)
	at org.gradle.configuration.internal.DefaultListenerBuildOperationDecorator$BuildOperationEmittingAction$1.run(DefaultListenerBuildOperationDecorator.java:173)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:30)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:27)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:67)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:60)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:167)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:60)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:48)
	at org.gradle.configuration.internal.DefaultListenerBuildOperationDecorator$BuildOperationEmittingAction.execute(DefaultListenerBuildOperationDecorator.java:170)
	at org.gradle.internal.event.BroadcastDispatch$ActionInvocationHandler.dispatch(BroadcastDispatch.java:99)
	at org.gradle.internal.event.BroadcastDispatch$ActionInvocationHandler.dispatch(BroadcastDispatch.java:87)
	at org.gradle.internal.event.AbstractBroadcastDispatch.dispatch(AbstractBroadcastDispatch.java:44)
	at org.gradle.internal.event.BroadcastDispatch$SingletonDispatch.dispatch(BroadcastDispatch.java:268)
	at org.gradle.internal.event.BroadcastDispatch$SingletonDispatch.dispatch(BroadcastDispatch.java:170)
	at org.gradle.internal.event.ListenerBroadcast.dispatch(ListenerBroadcast.java:160)
	at org.gradle.internal.event.ListenerBroadcast.dispatch(ListenerBroadcast.java:37)
	at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:92)
	at jdk.proxy1/jdk.proxy1.$Proxy73.afterEvaluate(Unknown Source)
	at org.gradle.configuration.project.LifecycleProjectEvaluator$NotifyAfterEvaluate$1.execute(LifecycleProjectEvaluator.java:247)
	at org.gradle.configuration.project.LifecycleProjectEvaluator$NotifyAfterEvaluate$1.execute(LifecycleProjectEvaluator.java:244)
	at org.gradle.api.internal.project.DefaultProject.stepEvaluationListener(DefaultProject.java:1604)
	at org.gradle.configuration.project.LifecycleProjectEvaluator$NotifyAfterEvaluate.run(LifecycleProjectEvaluator.java:253)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:30)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:27)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:67)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:60)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:167)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:60)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:48)
	at org.gradle.configuration.project.LifecycleProjectEvaluator$EvaluateProject.lambda$run$0(LifecycleProjectEvaluator.java:114)
	at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.lambda$applyToMutableState$1(DefaultProjectStateRegistry.java:435)
	at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.fromMutableState(DefaultProjectStateRegistry.java:453)
	at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.applyToMutableState(DefaultProjectStateRegistry.java:434)
	at org.gradle.configuration.project.LifecycleProjectEvaluator$EvaluateProject.run(LifecycleProjectEvaluator.java:100)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:30)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:27)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:67)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:60)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:167)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:60)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:48)
	at org.gradle.configuration.project.LifecycleProjectEvaluator.evaluate(LifecycleProjectEvaluator.java:72)
	at org.gradle.api.internal.project.DefaultProject.evaluateUnchecked(DefaultProject.java:828)
	at org.gradle.api.internal.project.ProjectLifecycleController.lambda$ensureSelfConfigured$2(ProjectLifecycleController.java:89)
	at org.gradle.internal.model.StateTransitionController.lambda$doTransition$14(StateTransitionController.java:255)
	at org.gradle.internal.model.StateTransitionController.doTransition(StateTransitionController.java:266)
	at org.gradle.internal.model.StateTransitionController.doTransition(StateTransitionController.java:254)
	at org.gradle.internal.model.StateTransitionController.lambda$maybeTransitionIfNotCurrentlyTransitioning$10(StateTransitionController.java:199)
	at org.gradle.internal.work.DefaultSynchronizer.withLock(DefaultSynchronizer.java:36)
	at org.gradle.internal.model.StateTransitionController.maybeTransitionIfNotCurrentlyTransitioning(StateTransitionController.java:195)
	at org.gradle.api.internal.project.ProjectLifecycleController.ensureSelfConfigured(ProjectLifecycleController.java:89)
	at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.ensureConfigured(DefaultProjectStateRegistry.java:400)
	at org.gradle.execution.TaskPathProjectEvaluator.configure(TaskPathProjectEvaluator.java:70)
	at org.gradle.execution.TaskPathProjectEvaluator.configureHierarchy(TaskPathProjectEvaluator.java:86)
	at org.gradle.execution.DefaultTaskSelector.getSelection(DefaultTaskSelector.java:75)
	at org.gradle.execution.selection.DefaultBuildTaskSelector.resolveTaskName(DefaultBuildTaskSelector.java:109)
	at org.gradle.execution.commandline.CommandLineTaskParser.parseTasks(CommandLineTaskParser.java:49)
	at org.gradle.execution.TaskNameResolvingBuildTaskScheduler.scheduleRequestedTasks(TaskNameResolvingBuildTaskScheduler.java:58)
	at org.gradle.execution.DefaultTasksBuildTaskScheduler.scheduleRequestedTasks(DefaultTasksBuildTaskScheduler.java:72)
	at org.gradle.initialization.DefaultTaskExecutionPreparer.lambda$scheduleRequestedTasks$0(DefaultTaskExecutionPreparer.java:48)
	at org.gradle.internal.Factories$1.create(Factories.java:31)
	at org.gradle.internal.work.DefaultWorkerLeaseService.withReplacedLocks(DefaultWorkerLeaseService.java:359)
	at org.gradle.api.internal.project.DefaultProjectStateRegistry$DefaultBuildProjectRegistry.withMutableStateOfAllProjects(DefaultProjectStateRegistry.java:239)
	at org.gradle.api.internal.project.DefaultProjectStateRegistry$DefaultBuildProjectRegistry.withMutableStateOfAllProjects(DefaultProjectStateRegistry.java:232)
	at org.gradle.initialization.DefaultTaskExecutionPreparer.scheduleRequestedTasks(DefaultTaskExecutionPreparer.java:47)
	at org.gradle.initialization.VintageBuildModelController.lambda$scheduleRequestedTasks$0(VintageBuildModelController.java:76)
	at org.gradle.internal.model.StateTransitionController.lambda$inState$1(StateTransitionController.java:99)
	at org.gradle.internal.model.StateTransitionController.lambda$inState$2(StateTransitionController.java:114)
	at org.gradle.internal.work.DefaultSynchronizer.withLock(DefaultSynchronizer.java:46)
	at org.gradle.internal.model.StateTransitionController.inState(StateTransitionController.java:110)
	at org.gradle.internal.model.StateTransitionController.inState(StateTransitionController.java:98)
	at org.gradle.initialization.VintageBuildModelController.scheduleRequestedTasks(VintageBuildModelController.java:76)
	at org.gradle.internal.cc.impl.ConfigurationCacheAwareBuildModelController.scheduleRequestedTasks(ConfigurationCacheAwareBuildModelController.kt:55)
	at org.gradle.internal.build.DefaultBuildLifecycleController$DefaultWorkGraphBuilder.addRequestedTasks(DefaultBuildLifecycleController.java:404)
	at org.gradle.internal.buildtree.DefaultBuildTreeWorkPreparer.lambda$scheduleRequestedTasks$0(DefaultBuildTreeWorkPreparer.java:41)
	at org.gradle.internal.build.DefaultBuildLifecycleController.lambda$populateWorkGraph$7(DefaultBuildLifecycleController.java:189)
	at org.gradle.internal.build.DefaultBuildWorkPreparer.populateWorkGraph(DefaultBuildWorkPreparer.java:42)
	at org.gradle.internal.build.BuildOperationFiringBuildWorkPreparer$PopulateWorkGraph.populateTaskGraph(BuildOperationFiringBuildWorkPreparer.java:106)
	at org.gradle.internal.build.BuildOperationFiringBuildWorkPreparer$PopulateWorkGraph.run(BuildOperationFiringBuildWorkPreparer.java:92)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:30)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:27)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:67)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:60)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:167)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:60)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:48)
	at org.gradle.internal.build.BuildOperationFiringBuildWorkPreparer.populateWorkGraph(BuildOperationFiringBuildWorkPreparer.java:67)
	at org.gradle.internal.build.DefaultBuildLifecycleController.lambda$populateWorkGraph$8(DefaultBuildLifecycleController.java:189)
	at org.gradle.internal.model.StateTransitionController.lambda$inState$1(StateTransitionController.java:99)
	at org.gradle.internal.model.StateTransitionController.lambda$inState$2(StateTransitionController.java:114)
	at org.gradle.internal.work.DefaultSynchronizer.withLock(DefaultSynchronizer.java:46)
	at org.gradle.internal.model.StateTransitionController.inState(StateTransitionController.java:110)
	at org.gradle.internal.model.StateTransitionController.inState(StateTransitionController.java:98)
	at org.gradle.internal.build.DefaultBuildLifecycleController.populateWorkGraph(DefaultBuildLifecycleController.java:189)
	at org.gradle.internal.build.DefaultBuildWorkGraphController$DefaultBuildWorkGraph.populateWorkGraph(DefaultBuildWorkGraphController.java:169)
	at org.gradle.composite.internal.DefaultBuildController.populateWorkGraph(DefaultBuildController.java:76)
	at org.gradle.composite.internal.DefaultIncludedBuildTaskGraph$DefaultBuildTreeWorkGraphBuilder.withWorkGraph(DefaultIncludedBuildTaskGraph.java:153)
	at org.gradle.internal.buildtree.DefaultBuildTreeWorkPreparer.lambda$scheduleRequestedTasks$1(DefaultBuildTreeWorkPreparer.java:41)
	at org.gradle.composite.internal.DefaultIncludedBuildTaskGraph$DefaultBuildTreeWorkGraph$1.run(DefaultIncludedBuildTaskGraph.java:209)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:30)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:27)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:67)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:60)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:167)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:60)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:48)
	at org.gradle.composite.internal.DefaultIncludedBuildTaskGraph$DefaultBuildTreeWorkGraph.scheduleWork(DefaultIncludedBuildTaskGraph.java:204)
	at org.gradle.internal.buildtree.DefaultBuildTreeWorkPreparer.scheduleRequestedTasks(DefaultBuildTreeWorkPreparer.java:37)
	at org.gradle.internal.cc.impl.ConfigurationCacheAwareBuildTreeWorkController$scheduleAndRunRequestedTasks$executionResult$1$result$1.invoke(ConfigurationCacheAwareBuildTreeWorkController.kt:48)
	at org.gradle.internal.cc.impl.ConfigurationCacheAwareBuildTreeWorkController$scheduleAndRunRequestedTasks$executionResult$1$result$1.invoke(ConfigurationCacheAwareBuildTreeWorkController.kt:45)
	at org.gradle.internal.cc.impl.DefaultConfigurationCache$loadOrScheduleRequestedTasks$1.invoke(DefaultConfigurationCache.kt:241)
	at org.gradle.internal.cc.impl.DefaultConfigurationCache$loadOrScheduleRequestedTasks$1.invoke(DefaultConfigurationCache.kt:240)
	at org.gradle.internal.cc.impl.DefaultConfigurationCache.runWorkThatContributesToCacheEntry(DefaultConfigurationCache.kt:552)
	at org.gradle.internal.cc.impl.DefaultConfigurationCache.loadOrScheduleRequestedTasks(DefaultConfigurationCache.kt:240)
	at org.gradle.internal.cc.impl.ConfigurationCacheAwareBuildTreeWorkController$scheduleAndRunRequestedTasks$executionResult$1.apply(ConfigurationCacheAwareBuildTreeWorkController.kt:45)
	at org.gradle.internal.cc.impl.ConfigurationCacheAwareBuildTreeWorkController$scheduleAndRunRequestedTasks$executionResult$1.apply(ConfigurationCacheAwareBuildTreeWorkController.kt:44)
	at org.gradle.composite.internal.DefaultIncludedBuildTaskGraph.withNewWorkGraph(DefaultIncludedBuildTaskGraph.java:112)
	at org.gradle.internal.cc.impl.ConfigurationCacheAwareBuildTreeWorkController.scheduleAndRunRequestedTasks(ConfigurationCacheAwareBuildTreeWorkController.kt:44)
	at org.gradle.internal.buildtree.DefaultBuildTreeLifecycleController.lambda$scheduleAndRunTasks$1(DefaultBuildTreeLifecycleController.java:77)
	at org.gradle.internal.buildtree.DefaultBuildTreeLifecycleController.lambda$runBuild$4(DefaultBuildTreeLifecycleController.java:120)
	at org.gradle.internal.model.StateTransitionController.lambda$transition$6(StateTransitionController.java:169)
	at org.gradle.internal.model.StateTransitionController.doTransition(StateTransitionController.java:266)
	at org.gradle.internal.model.StateTransitionController.lambda$transition$7(StateTransitionController.java:169)
	at org.gradle.internal.work.DefaultSynchronizer.withLock(DefaultSynchronizer.java:46)
	at org.gradle.internal.model.StateTransitionController.transition(StateTransitionController.java:169)
	at org.gradle.internal.buildtree.DefaultBuildTreeLifecycleController.runBuild(DefaultBuildTreeLifecycleController.java:117)
	at org.gradle.internal.buildtree.DefaultBuildTreeLifecycleController.scheduleAndRunTasks(DefaultBuildTreeLifecycleController.java:77)
	at org.gradle.internal.buildtree.DefaultBuildTreeLifecycleController.scheduleAndRunTasks(DefaultBuildTreeLifecycleController.java:72)
	at org.gradle.tooling.internal.provider.runner.BuildModelActionRunner.run(BuildModelActionRunner.java:53)
	at org.gradle.launcher.exec.ChainingBuildActionRunner.run(ChainingBuildActionRunner.java:35)
	at org.gradle.internal.buildtree.ProblemReportingBuildActionRunner.run(ProblemReportingBuildActionRunner.java:49)
	at org.gradle.launcher.exec.BuildOutcomeReportingBuildActionRunner.run(BuildOutcomeReportingBuildActionRunner.java:71)
	at org.gradle.tooling.internal.provider.FileSystemWatchingBuildActionRunner.run(FileSystemWatchingBuildActionRunner.java:135)
	at org.gradle.launcher.exec.BuildCompletionNotifyingBuildActionRunner.run(BuildCompletionNotifyingBuildActionRunner.java:41)
	at org.gradle.launcher.exec.RootBuildLifecycleBuildActionExecutor.lambda$execute$0(RootBuildLifecycleBuildActionExecutor.java:54)
	at org.gradle.composite.internal.DefaultRootBuildState.run(DefaultRootBuildState.java:130)
	at org.gradle.launcher.exec.RootBuildLifecycleBuildActionExecutor.execute(RootBuildLifecycleBuildActionExecutor.java:54)
	at org.gradle.internal.buildtree.InitDeprecationLoggingActionExecutor.execute(InitDeprecationLoggingActionExecutor.java:62)
	at org.gradle.internal.buildtree.InitProblems.execute(InitProblems.java:36)
	at org.gradle.internal.buildtree.DefaultBuildTreeContext.execute(DefaultBuildTreeContext.java:40)
	at org.gradle.launcher.exec.BuildTreeLifecycleBuildActionExecutor.lambda$execute$0(BuildTreeLifecycleBuildActionExecutor.java:71)
	at org.gradle.internal.buildtree.BuildTreeState.run(BuildTreeState.java:60)
	at org.gradle.launcher.exec.BuildTreeLifecycleBuildActionExecutor.execute(BuildTreeLifecycleBuildActionExecutor.java:71)
	at org.gradle.launcher.exec.RunAsBuildOperationBuildActionExecutor$2.call(RunAsBuildOperationBuildActionExecutor.java:67)
	at org.gradle.launcher.exec.RunAsBuildOperationBuildActionExecutor$2.call(RunAsBuildOperationBuildActionExecutor.java:63)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:210)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:205)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:67)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:60)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:167)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:60)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:54)
	at org.gradle.launcher.exec.RunAsBuildOperationBuildActionExecutor.execute(RunAsBuildOperationBuildActionExecutor.java:63)
	at org.gradle.launcher.exec.RunAsWorkerThreadBuildActionExecutor.lambda$execute$0(RunAsWorkerThreadBuildActionExecutor.java:36)
	at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:263)
	at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:127)
	at org.gradle.launcher.exec.RunAsWorkerThreadBuildActionExecutor.execute(RunAsWorkerThreadBuildActionExecutor.java:36)
	at org.gradle.tooling.internal.provider.continuous.ContinuousBuildActionExecutor.execute(ContinuousBuildActionExecutor.java:110)
	at org.gradle.tooling.internal.provider.SubscribableBuildActionExecutor.execute(SubscribableBuildActionExecutor.java:64)
	at org.gradle.internal.session.DefaultBuildSessionContext.execute(DefaultBuildSessionContext.java:46)
	at org.gradle.internal.buildprocess.execution.BuildSessionLifecycleBuildActionExecutor$ActionImpl.apply(BuildSessionLifecycleBuildActionExecutor.java:92)
	at org.gradle.internal.buildprocess.execution.BuildSessionLifecycleBuildActionExecutor$ActionImpl.apply(BuildSessionLifecycleBuildActionExecutor.java:80)
	at org.gradle.internal.session.BuildSessionState.run(BuildSessionState.java:73)
	at org.gradle.internal.buildprocess.execution.BuildSessionLifecycleBuildActionExecutor.execute(BuildSessionLifecycleBuildActionExecutor.java:62)
	at org.gradle.internal.buildprocess.execution.BuildSessionLifecycleBuildActionExecutor.execute(BuildSessionLifecycleBuildActionExecutor.java:41)
	at org.gradle.internal.buildprocess.execution.StartParamsValidatingActionExecutor.execute(StartParamsValidatingActionExecutor.java:64)
	at org.gradle.internal.buildprocess.execution.StartParamsValidatingActionExecutor.execute(StartParamsValidatingActionExecutor.java:32)
	at org.gradle.internal.buildprocess.execution.SessionFailureReportingActionExecutor.execute(SessionFailureReportingActionExecutor.java:51)
	at org.gradle.internal.buildprocess.execution.SessionFailureReportingActionExecutor.execute(SessionFailureReportingActionExecutor.java:39)
	at org.gradle.internal.buildprocess.execution.SetupLoggingActionExecutor.execute(SetupLoggingActionExecutor.java:47)
	at org.gradle.internal.buildprocess.execution.SetupLoggingActionExecutor.execute(SetupLoggingActionExecutor.java:31)
	at org.gradle.launcher.daemon.server.exec.ExecuteBuild.doBuild(ExecuteBuild.java:70)
	at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:37)
	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
	at org.gradle.launcher.daemon.server.exec.WatchForDisconnection.execute(WatchForDisconnection.java:39)
	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
	at org.gradle.launcher.daemon.server.exec.ResetDeprecationLogger.execute(ResetDeprecationLogger.java:29)
	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
	at org.gradle.launcher.daemon.server.exec.RequestStopIfSingleUsedDaemon.execute(RequestStopIfSingleUsedDaemon.java:35)
	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
	at org.gradle.launcher.daemon.server.exec.ForwardClientInput.lambda$execute$0(ForwardClientInput.java:40)
	at org.gradle.internal.daemon.clientinput.ClientInputForwarder.forwardInput(ClientInputForwarder.java:80)
	at org.gradle.launcher.daemon.server.exec.ForwardClientInput.execute(ForwardClientInput.java:37)
	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
	at org.gradle.launcher.daemon.server.exec.LogAndCheckHealth.execute(LogAndCheckHealth.java:64)
	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
	at org.gradle.launcher.daemon.server.exec.LogToClient.doBuild(LogToClient.java:63)
	at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:37)
	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
	at org.gradle.launcher.daemon.server.exec.EstablishBuildEnvironment.doBuild(EstablishBuildEnvironment.java:84)
	at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:37)
	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
	at org.gradle.launcher.daemon.server.exec.StartBuildOrRespondWithBusy$1.run(StartBuildOrRespondWithBusy.java:52)
	at org.gradle.launcher.daemon.server.DaemonStateCoordinator.lambda$runCommand$0(DaemonStateCoordinator.java:321)
	at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
	at org.gradle.internal.concurrent.AbstractManagedExecutor$1.run(AbstractManagedExecutor.java:48)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
	at java.base/java.lang.Thread.run(Thread.java:1583)

Reproducer: GitHub - sdkotlin/sd-kotlin-spring-talks at ed5539919c7b0777d65c94fcf7278d79daa94b5a.

Did you look at the stacktrace? :slight_smile:
It’s quite obvious what causes this.

Ah, I overlooked that you already wrote “Maybe Kotlin Gradle Plugin eagerness?” :smiley:
Yeah, seems so.
It collects some statistics which causes your code to be executed it seems.
Whether this is a good idea and whether it can be disabled I have no idea.

1 Like

If I switch from KGP to just the Java Library Plugin for the subprojects where I use the dependency helper, I no longer get the eager invocation of it. Of course then they also can’t have any Kotlin code in them. I’ll look into filing a KGP issue for it.

1 Like

Reproducer created and issue filed:

That aside, I unexpectedly seem to be getting eager dependency realization and configuration with just the Java Library Plugin when running gradlew help unless I wrap project(notation) in a provider { }:

In the reproducer I minimized the dependency helper to just a passthrough that logs a stack trace when invoked: gradle-eager-dependency-realization/buildSrc/src/main/kotlin/com/ianbrandt/buildlogic/artifacts/dsl/StackLoggingEagerDependencyCreationExtension.kt at b94e7da5273ddeae30816344ff99cfff59a3bb4c · ianbrandt/gradle-eager-dependency-realization · GitHub.

So it would seem project dependencies aren’t lazily processed in general, or might that be a bug?

In related news, I see 8.14-rc-1 includes an improvement for lazy initialization of registered configurations:

I tried upgrading, but it appears my consumable configuration is still being configured eagerly:

It looks like this may be another KGP issue:

Why do people often think you need to throw an exception to log a stacktrace?
Just yesterday I showed to a colleague that he can simple give the new Exception() to the logging system to get the stacktrace logged. :slight_smile:

That aside, I unexpectedly seem to be getting eager dependency realization and configuration with just the Java Library Plugin when running gradlew help unless I wrap project(notation) in a provider { } :slight_smile:

So it would seem project dependencies aren’t lazily processed in general, or might that be a bug?

I don’t think that is unexpected, this is just code you run in the configuration phase.
With your eagerDependency extension you directly execute that logging code “to create” the dependency instance.
With your implementation(project(":subprojects:util")) { ... } you also directly execute code to configure the created dependency instance.
I don’t think those two are intended to be lazy in some way.
For laziness at that point you exactly have the possibility to use a provider.
This would also be sufficient to get the config code lazy:

implementation(provider { project(":subprojects:util") }) {
    // Debug logging.
    logger.lifecycle(
        "Configuring dependency $this for ${project.path}...",
        RuntimeException("Dependency config stacktrace")
    )
}

It looks like this may be another KGP issue

Yes, just like the base plugin used to, the KGP at https://github.com/JetBrains/kotlin/blob/v2.1.20/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/internal/stdlibDependencyManagement.kt#L58 uses configurations.all instead of configurations.configureEach and thus causes all configurations to be realized.

That part can be disabled by setting kotlin.stdlib.jdk.variants.version.alignment = false as project property, so you can test further for similar things.

Btw. the other spot you can disable using kotlin.internal.collectFUSMetrics = false, found that just beside the other one. :slight_smile:

And with the snippet I shown above in this post, the FUS metrics spot would also not cause the stacktrace, as the FUS metrics collection filters out all project dependencies and thus does not call the configuration action to be executed, that’s why you had to put the stacktrace printing directly inside the provider to reproduce as that is necessary to be executed to find whether the dependency is a project dependency or not.


Btw. some side-notes:

  • Instead of consumable(...).configure { ... } you can also do consumable(...) { ... } like for all NamedDomainObjectProviders.
  • eagerDependency is only “nicely” usable from Kotlin DSL. If you want to nicely support Groovy DSL, you also need to add a call function. The more idiomatic way would be to have a function in the extension that is called by the consumer, then it should work uniformly in all current and hopefully future DSLs
  • By only accepting Dependency you prevent other notations like eagerDependency(project) in both DSLs or eagerDependency(project(":subprojects:util")) in Groovy DSL.
  • It is usually better to let Gradle instantiate stuff and not do it yourself, this way it can add some of its magic, like making things ExtensionAware automatically, even if not declared explicitly so that you (or someone else) can add further extensions to that extension or other domain object.

So to summarize and ignoring that each DSL has to be supported explicitly:

diff --git a/buildSrc/src/main/kotlin/com/ianbrandt/buildlogic/artifacts/dsl/DependencyCreationExtensions.kt b/buildSrc/src/main/kotlin/com/ianbrandt/buildlogic/artifacts/dsl/DependencyCreationExtensions.kt
index d96caa5..9715d5d 100644
--- a/buildSrc/src/main/kotlin/com/ianbrandt/buildlogic/artifacts/dsl/DependencyCreationExtensions.kt
+++ b/buildSrc/src/main/kotlin/com/ianbrandt/buildlogic/artifacts/dsl/DependencyCreationExtensions.kt
@@ -2,12 +2,14 @@ package com.ianbrandt.buildlogic.artifacts.dsl

 import org.gradle.api.artifacts.Dependency
 import org.gradle.api.artifacts.dsl.DependencyHandler
+import org.gradle.api.plugins.ExtensionAware

 /**
  * An invokable interface for [DependencyHandler] extensions that eagerly
  * create dependencies. Facilitates calling the extensions as unqualified
  * helper functions.
  */
-fun interface EagerDependencyCreationExtension {
-    operator fun invoke(dependency: Dependency): Dependency
+interface EagerDependencyCreationExtension : ExtensionAware {
+    operator fun invoke(dependency: Any): Dependency
+    fun call(dependency: Any) = invoke(dependency)
 }
diff --git a/buildSrc/src/main/kotlin/com/ianbrandt/buildlogic/artifacts/dsl/StackLoggingEagerDependencyCreationExtension.kt b/buildSrc/src/main/kotlin/com/ianbrandt/buildlogic/artifacts/dsl/StackLoggingEagerDependencyCreationExtension.kt
index 27f0186..bfed146 100644
--- a/buildSrc/src/main/kotlin/com/ianbrandt/buildlogic/artifacts/dsl/StackLoggingEagerDependencyCreationExtension.kt
+++ b/buildSrc/src/main/kotlin/com/ianbrandt/buildlogic/artifacts/dsl/StackLoggingEagerDependencyCreationExtension.kt
@@ -8,14 +8,15 @@ import org.slf4j.LoggerFactory
  * An eager [DependencyHandler] extension that logs a stacktrace when the
  * [Dependency] is realized.
  */
-class StackLoggingEagerDependencyCreationExtension(
+abstract class StackLoggingEagerDependencyCreationExtension(
     private val projectPath: String,
+    private val dependencyHandler: DependencyHandler
 ) : EagerDependencyCreationExtension {

     private val logger =
         LoggerFactory.getLogger(StackLoggingEagerDependencyCreationExtension::class.java)

-    override fun invoke(dependency: Dependency): Dependency {
+    override fun invoke(dependency: Any): Dependency {

         // Debug logging.
         try {
@@ -27,6 +28,6 @@ class StackLoggingEagerDependencyCreationExtension(
             )
         }

-        return dependency
+        return dependencyHandler.create(dependency)
     }
 }
diff --git a/buildSrc/src/main/kotlin/com/ianbrandt/buildlogic/plugins/DependencyHelperPlugin.kt b/buildSrc/src/main/kotlin/com/ianbrandt/buildlogic/plugins/DependencyHelperPlugin.kt
index 6e535ec..693dcc7 100644
--- a/buildSrc/src/main/kotlin/com/ianbrandt/buildlogic/plugins/DependencyHelperPlugin.kt
+++ b/buildSrc/src/main/kotlin/com/ianbrandt/buildlogic/plugins/DependencyHelperPlugin.kt
@@ -4,18 +4,19 @@ import com.ianbrandt.buildlogic.artifacts.dsl.EagerDependencyCreationExtension
 import com.ianbrandt.buildlogic.artifacts.dsl.StackLoggingEagerDependencyCreationExtension
 import org.gradle.api.Plugin
 import org.gradle.api.Project
+import org.gradle.kotlin.dsl.create

 @Suppress("unused")
 class DependencyHelperPlugin : Plugin<Project> {

     override fun apply(project: Project) {

-        project.dependencies.extensions.add(
-            EagerDependencyCreationExtension::class.java,
+        project.dependencies.extensions.create(
+            EagerDependencyCreationExtension::class,
             "eagerDependency",
-            StackLoggingEagerDependencyCreationExtension(
-                projectPath = project.path,
-            )
+            StackLoggingEagerDependencyCreationExtension::class,
+            project.path,
+            project.dependencies
         )
     }
 }

:slight_smile:

1 Like

Thanks for the tips. I believe I’ve applied them all to my main prototyping project: Comparing 6128eca24c0446d999e46f1c890f6d2ba277dead...289df5ceb66a7e93b4df53f089befb035c702529 · sdkotlin/sd-kotlin-spring-talks · GitHub. It was already taking notation: Any and using dependencyHandler.create(notation) in its dependency handler extension. I only changed to passing through a dependency: Dependency with the eagerDependency() extension in the minimal reproducer project because I was just using it to log the call stack when invoked.

Adding kotlin.internal.collectFUSMetrics = false did work around the provider { dependency } issue: https://youtrack.jetbrains.com/issue/KT-76668/#focus=Comments-27-11870291.0-0.

Adding kotlin.stdlib.jdk.variants.version.alignment = false also worked around the configuration realization by the Kotlin Gradle Plugin issue in my prototyping project. That indeed revealed another culprit calling DefaultDomainObjectCollection.all, JvmDependencyConflictResolutionPlugin: Stack Trace.

I’ve started trying to create a minimal reproducer for this second KGP issue, and have hit an unexpected result. I do seem to get eager consumable configuration config action invocation for a Kotlin Gradle Plugin project and not for a Java Library Plugin project, but the stack trace for the former is all org.gradle, with no org.jetbrains.kotlin.gradle like in my prototype project:

I’ll keep working on trying to create a minimal reproducer for the KGP issue of StdlibDependencyManagementKt.configureStdlibVersionAlignment calling DefaultDomainObjectCollection.all, but I’m wondering if I should also file a Gradle issue for the eager configuration stack trace above?

:ok_hand:

I’ll keep working on trying to create a minimal reproducer for the KGP issue of StdlibDependencyManagementKt.configureStdlibVersionAlignment calling DefaultDomainObjectCollection.all , but I’m wondering if I should also file a Gradle issue for the eager configuration stack trace above?

I didn’t look at the case, but I guess you just let yourself get fooled. :slight_smile:
As a quick experiment, compare

1: configurations.all {}
2: configurations.consumable("testingKotlinLazyConfigurationRealization") {
       logger.lifecycle(
           "Kotlin Gradle Plugin project consumable configuration created:",
           RuntimeException("Configuration realization stacktrace")
       )
   }

with

2: configurations.consumable("testingKotlinLazyConfigurationRealization") {
       logger.lifecycle(
           "Kotlin Gradle Plugin project consumable configuration created:",
           RuntimeException("Configuration realization stacktrace")
       )
   }
3: configurations.all {}

You will find that in the second case you see the line 3 in the stacktrace, while in the first case you see the line 2 twice but you do not see line 1.

You have to make sure the all call is after your register call to get the actual culprit in the stacktrace.

The gory details if you mind:
Creating a configuration with configuration action (no matter whether you use register or create or consumable or …) you create or register the configuration and put the configuration action to be executed in a queue. Any all or configureEach or similar calls done before that affect it come first in the queue. So if the all call came before the register call, then the register call itself causes the configuration to be created immediately as there is an eager action registered and so the register call is in the stacktrace. If the all call comes after the register call, then the all call is actually causing the configuration to be created and lands in the stacktrace.

1 Like

Having looked at the stacktrace, yes, it is exactly that.
That’s why there is build.gradle.kts:6 twice in the stacktrace.

1 Like

Thank you for the gory details. That makes sense, and it led me to minimal reproducers for the Kotlin Gradle Plugin and JVM Dependency Conflict Detection and Resolution Plugin use of configurations.all:

Circling back to…

I had made the seemingly incorrect assumption that Gradle processed the project.configurations { }, project.dependencies { }, and project.artifacts { } configuration actions lazily. Now that I’ve looked more closely at their JavaDocs and implementations, I see that’s apparently not the case.

In particular, it appears that project.artifacts.add is eager, and triggers the configure action for my ConsumableConfiguration regardless of the KGP and JVM Dependency Conflict Detection and Resolution Plugin calls to configurations.all (Stack Trace).

As such, it would seem I’m registering and configuring everything as lazily as can be with my plugin at present, and configuration avoidance for dependency helpers and artifact additions by way of consumable configurations is just not available with the current version of Gradle.

Well, don’t use the project.artifacts.add which causes the configuration to immediately being looked up.
Instead lazily configure the configuration and there do artifacts.add(...) if you can get hold of a PublishArtifact.

If not, maybe try to just do the project.artifacts.add(name, ...) call in a configure closure for the configuration so that the artifact still is only added when the configuration is realized.

1 Like

KGP and JVM Dependency Conflict Plugin issues filed:

1 Like