You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
As we are now using the concept a Plugin<Settings> with the AutoConfigureSettingsPlugin that opens some new possibilities we should consider.
The current architecture has some flaws:
All third-party-plugins (Kotlin, Node, Swagger...) come as transitive dependencies of the AutoConfigure-Plugin, that means the initial download takes some time, and plugins that might never be applied are still being downloaded although they maybe never get used.
Users have no (easy) chance to override the versions of those plugins.
After creating this presentation with a comparison of the AutoConfigure-Plugin to the architecture of Spring Boot, I realized that we're not just auto-configuring plugins (like Spring Boot does) but also auto-applying plugins (which Spring Boot does not do, you have to "apply" modules manually by adding the starter-packages to the classpath).
Some best practices including that one from @marcelkliemannel advice to never apply another plugin from a custom plugin as this violates clean code principles, and I mostly agree with that (possible exceptions see further below)
Now having the possibilities to be applied as Settings-Plugin, before projects are loaded, allows us to i.e. configure the pluginManagement block there, specifically pre-configuring version-numbers of plugin centrally. That could then be the equivalent of Spring Boot's BOM, so that users don't need to care about the versions of third-party libraries anymore.
Basically, in a settings.gradle.kts you can write code like this:
pluginManagement {
val kotlinVersion:String? by settings
val safeKotlinVersion = kotlinVersion ?:"1.7.20"
plugins {
kotlin("jvm") version safeKotlinVersion
kotlin("kapt") version safeKotlinVersion
kotlin("plugin.serialization") version safeKotlinVersion
}
}
This allows to define a default version for all Kotlin-Plugins (1.7.20 in that case) and users might override that version by adding a kotlinVersion property to gradle.properties.
In the build.gradle.kts it is then sufficient for the user to just specify the plugin without a version:
plugin {
kotlin("jvm")
}
Still, that would mean, that users would need to apply the plugins by themselves then (instead of us applying automatically if src/main/kotlin exists) and we'd just jump in and autoconfiguring when the plugin is applied.
Spring Boot does that that by adding 3rd-party dependencies to the compileOnly scope and then working with annotations like @ConditionalOnClass to configure those libraries and beans.
Now I've created a playground-repository to try if that would work with Gradle plugins as well. In the AutoConfigureSettingsPlugin you find code like that:
classAutoConfigureSettingsPlugin : Plugin<Settings> {
overridefunapply(settings:Settings) {
val kotlinVersion ="1.7.21"// TODO optionally read from Settings
settings.pluginManagement.plugins { p ->
p.id("org.jetbrains.kotlin.jvm").version(kotlinVersion)
p.id("org.jetbrains.kotlin.kapt").version(kotlinVersion)
p.id("org.jetbrains.kotlin.plugin.serialization").version(kotlinVersion)
}
settings.gradle.projectsLoaded {
it.gradle.allprojects { p ->
p.pluginManager.withPlugin("org.jetbrains.kotlin.jvm") {
p.logger.quiet("Applied KOTLIN in ${p.name}")
p.logger.quiet(p.extensions.findByName("kotlin")!!.javaClass.classLoader.toString())
p.logger.quiet(this.javaClass.classLoader.toString())
val kotlin = p.extensions.getByName("kotlin") asKotlinProjectExtension
kotlin.jvmToolchain {
(it asJavaToolchainSpec).languageVersion.set(javaVersion)
}
}
}
}
}
}
So that would be quite the equivalent to how Spring Boot works. The user decides which plugins to apply and we then step in and configure those.
The problem is: it does not work this way. You will find the log Applied KOTLIN in the code, but the problem is that in the next line you will get a NoClassDefFoundError for KotlinProjectExtension. The thing is that this class basically is on the classpath of the build script (as it is loaded in the build.gradle) but it's inside another ClassLoader. The output is:
Applied KOTLIN in single-kotlin-module
VisitableURLClassLoader(ClassLoaderScopeIdentifier.Id{coreAndPlugins:settings[:]:buildSrc[:]:root-project[:](export)})
VisitableURLClassLoader(ClassLoaderScopeIdentifier.Id{coreAndPlugins:injected-plugin(local)})
Don't know if we could manage to fix that, but even if, we'd need to ask ourselves how to handle the case that for some cases we do want plugins to be applied automatically (i.e. Jacoco or the License-Plugin).
I also checked the possibilities of convention-plugins but also here the official documentation tells you that in case you're applying 3rd-party plugins you need to add them as implementation dependencies (and not compileOnly) to your classpath, and that would end us up in the same situation as we are already.
Still I believe it's worth digging into that topic and the possibilities with Settings-Plugins more deeper.
The text was updated successfully, but these errors were encountered:
As we are now using the concept a
Plugin<Settings>
with theAutoConfigureSettingsPlugin
that opens some new possibilities we should consider.The current architecture has some flaws:
Now having the possibilities to be applied as Settings-Plugin, before projects are loaded, allows us to i.e. configure the
pluginManagement
block there, specifically pre-configuring version-numbers of plugin centrally. That could then be the equivalent of Spring Boot's BOM, so that users don't need to care about the versions of third-party libraries anymore.Basically, in a
settings.gradle.kts
you can write code like this:This allows to define a default version for all Kotlin-Plugins (
1.7.20
in that case) and users might override that version by adding akotlinVersion
property togradle.properties
.In the
build.gradle.kts
it is then sufficient for the user to just specify the plugin without a version:plugin { kotlin("jvm") }
Still, that would mean, that users would need to apply the plugins by themselves then (instead of us applying automatically if
src/main/kotlin
exists) and we'd just jump in and autoconfiguring when the plugin is applied.Spring Boot does that that by adding 3rd-party dependencies to the
compileOnly
scope and then working with annotations like@ConditionalOnClass
to configure those libraries and beans.Now I've created a playground-repository to try if that would work with Gradle plugins as well. In the
AutoConfigureSettingsPlugin
you find code like that:So that would be quite the equivalent to how Spring Boot works. The user decides which plugins to apply and we then step in and configure those.
The problem is: it does not work this way. You will find the log
Applied KOTLIN
in the code, but the problem is that in the next line you will get aNoClassDefFoundError
forKotlinProjectExtension
. The thing is that this class basically is on the classpath of the build script (as it is loaded in thebuild.gradle
) but it's inside another ClassLoader. The output is:Just clone the playground repository to dig deeper into that.
Don't know if we could manage to fix that, but even if, we'd need to ask ourselves how to handle the case that for some cases we do want plugins to be applied automatically (i.e. Jacoco or the License-Plugin).
I also checked the possibilities of convention-plugins but also here the official documentation tells you that in case you're applying 3rd-party plugins you need to add them as
implementation
dependencies (and notcompileOnly
) to your classpath, and that would end us up in the same situation as we are already.Still I believe it's worth digging into that topic and the possibilities with Settings-Plugins more deeper.
The text was updated successfully, but these errors were encountered: