Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consider leveraging the possibilities with Settings-Plugins #121

Open
klu2 opened this issue Nov 14, 2022 · 0 comments
Open

Consider leveraging the possibilities with Settings-Plugins #121

klu2 opened this issue Nov 14, 2022 · 0 comments

Comments

@klu2
Copy link
Contributor

klu2 commented Nov 14, 2022

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:

class AutoConfigureSettingsPlugin : Plugin<Settings> {
    override fun apply(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") as KotlinProjectExtension
                    kotlin.jvmToolchain {
                        (it as JavaToolchainSpec).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)})

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

@klu2 klu2 closed this as completed Dec 14, 2022
@klu2 klu2 reopened this Dec 14, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant