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. 
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 { }

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. 
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 NamedDomainObjectProvider
s.
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
)
}
}
