This message was deleted.
# community-support
s
This message was deleted.
👎 2
p
a
See that blog posted above. ☝️ So more declarative approach is on the roadmap, but we don't plan to use YAML or TOML. We are currently exploring a format that is a subset of Kotlin, that will be also fast to parse.
👍 1
j
Can you share more details about “subset of Kotlin”?
p
If you want to declare your build using YAML, take a look about JetBrains Amper project: https://blog.jetbrains.com/blog/2023/11/09/amper-improving-the-build-tooling-user-experience/
👍 1
a
Can you share more details about “subset of Kotlin”?
The idea that it will support just assignments + some specialized methods (e.g.
mavenCentral()
or
implementation("group:name:value")
), also no loops and ifs. So it will be really declarative. But right now it's still in the experimental phase, so the format it's still evolving
j
ni loops and ifs
Please support control flow. Easy setups will work with that, rest will be a frankenstein of mixing gradle old style with the new one. Compose UI is a declarative UI and it allows using the fully power of Kotlin. I can already share an example of why it will not be a good idea as I already have my own Amper like convention plugin but with Kotlin
Is there any channel about declarative Gradle to share my use case?
a
I don't think there is any at the moment. Might be a good idea to open one, once we can share something publicly. But I think we are not there yet
j
I can share it here with you anyway. But the main reason for me to use Kotlin over a configuration language is not being blocked by simple use cases, if those simple use cases are blocked by a subset of Kotlin, then there is no reason to use it over any other configuration language.
Context and a real use case of how it started: • Create a project containing a project for a Gradle plugin and another for a version catalog. • Both projects will be published to MavenCentral and/or Plugin portal. • They will be open source, so building a site, docs, and so on easily is "mandatory".
• Gradle plugin project:
Copy code
hubdle {
    config {
        analysis() // detekt and sonar
        coverage() // Kover
        documentation {
            api() // Dokka
        }
        publishing { // support the publication to MavenCentral, snapshot, and Gradle portal repositories
            maven { 
                repositories {
                    mavenLocalTest() // add a custom custom repository for testing purposes
                }
            }
        }
        versioning {
            semver() // automatic versioning with git tags
        }
    }

    kotlin {
        jvm {
            features {
                jvmVersion(JavaVersion.VERSION_11)

                gradle {
                    plugin {
                        gradlePlugin {
                            plugins {
                                create("hubdle") {
                                    id = "com.javiersc.hubdle"
                                    displayName = "Hubdle"
                                    // rest of the configuration ...
                                }
                            }
                        }

                        pluginUnderTestDependencies(
                            hubdle.android.tools.build.gradle,
                            hubdle.jetbrains.kotlin.gradle.plugin,
                        )
                    }
                }
            }

            main {
                dependencies {
                     // ...
                }
            }

            testFixtures()
            testFunctional()
            testIntegration()
        }
    }
}
• Version catalog project
Copy code
hubdle {
    config {
        analysis()
        coverage()
        documentation {
            changelog() // handle automatically the changelog for the plugin updates
        }
        publishing {
            maven {
                repositories {
                    mavenLocalTest()
                }
            }
        }
        versioning {
            semver()
        }
    }

    kotlin {
        jvm {
            features {
                gradle {
                    versionCatalogs {
                        catalog {
                            toml(rootDir.resolve("gradle/hubdle.libs.versions.toml"))
                        }
                    }
                }
            }
        }
    }
}
• Root project
Copy code
hubdle {
    config {
        analysis()
        binaryCompatibilityValidator() // create `.api` files
        coverage()
        documentation {
            api()
            changelog() // automatically patch the general changelog for the project
            readme {
                badges() // automatically update readme badges (Kotlin version, Sonar, etc)
            }
            site() // mkdocs web page
        }
        nexus() // requirement to support publishing to MavenCentral/Snapshot repositories
    }
}
Even though there are a lot of lines there, there is not a single if-else and it does tons of things in a declarative way • Detekt • Sonar (Sonarcloud setup) • Code coverage (and its integration with Sonarcloud) • Mkdocs webpage • Dokka • Automatic patching of changelogs and the readme files • Automatic semver with git tags • Creating a Kotlin JVM project which supports the creation of a Gradle plugin or a version catalog • Being able to publish them to Gradle plugin portal and Maven Central • Multiple source sets for integration and functional tests And more that are based on custom tastes under the hood, like the usage of
main/kotlin
instead of
src/main/kotlin
, etc
New use case: • I have started playing with Kotlin compiler plugins, so I need to support using custom Kotlin
dev
versions I want my Gradle plugin support Kotlin Gradle plugin
2.+
versions What could I do if I had a subset of Kotlin or Yaml? Crying a lot if the configuration that someone has created for me is not enough, and creating a Frankenstein elsewhere. This perfect configuration will not ever exist. I am creating my own DSL for my purposes, and even so, I have to write the next workaround. What are the chances of someone creating a very limited DSL for a random person on the internet that fits all needs? With normal Kotlin plus Gradle, I can work around it
Copy code
hubdle {
    config {
    // ...
        versioning {
            semver { //
                val hasSameTagPrefix: Boolean = providedTagPrefix == tagPrefix
                if (kotlinVersion.isNotBlank() && hasSameTagPrefix) {
                    mapVersion { gradleVersion ->
                        gradleVersion.mapIfKotlinVersionIsProvided(kotlinVersion)
                    }
                }
            }
        }
        testing {
            test { //
                systemProperties["KOTLIN_VERSION"] = kotlinVersion.orEmpty()
            }
        }
    }
}

fun GradleVersion.mapIfKotlinVersionIsProvided(kotlinVersion: String): String {
    // checks to avoid wrong provided Kotlin versions, wrong publiccations, and so on 
}
Do I need a Compose setup in KMP?
Copy code
hubdle {
    config {
        ...
    }
    kotlin {
        multiplatform {
            features {
                compose() // add compose
            }

            common {
                main {
                    dependencies {
                        implementation(hubdle.jetbrains.compose.ui)
                        ...
                    }
                }
            }

            android {
                  main {
                      implementation(hubdle.androidx.random.lib)
                  }
            }
            jvm()
            jvmAndAndroid()
            ...
        }
    }
}
Those are real use cases for a declarative plugin that applies a lot of plugins in each project (10 or more per project). I have created more and more "wrappers" Kotlin projects: • JVM • Android (library and app) • KMP Features to support things like: • Java Application • Gradle plugins • Version catalogs • IntelliJ plugins • Java versions and toolchains • Coroutines • Compose • Serialization • SqlDelight • Molecule And more and more I don't even remember. Even so, each simple setup can be easily understood, and at the same time, it supports any level of complexity and fallbacks
Copy code
plugins {
    alias(libs.plugins.some.random.plugin)
}

hubdle {
    kotlin {
        multiplatform {
            // fallback configuration
            kotlin { // KotlinMultiplatformExtension from KGP
                ...
            }
        }
    }
}

randomExtension {
     ...
}
I have created this custom DSL for me, with my tastes, fixing a lot of general workarounds under the hood. And even so... There are too many specific configurations in any project that are impossible to fix with a configuration-like language without creating a big snowball and/or a Frankenstein of mixed files and/or included builds like
buildSrc
or
build-logic
just to do a simple
if/else
and binds it via custom plugin or whatever with a limited Kotlin DSL, Amper, or whatever configuration language. Currently: • Someone or a team who knows Gradle creates the convention plugin and maintains it. • No-build engineers consume it Future: • Someone or a team who knows Gradle creates the whole Gradle logic with conventional plugins or custom functions and maintains it. • Someone or a team who knows Gradle creates the bindings for the Kotlin DSL and/or Amper and maintains it. • No-build engineers consume it In my opinion: • The tools to create declarative convention plugins exist. The Slack open-source plugin does that and my plugin was inspired by the Slack one too. • It is a bit annoying dealing with ordering issues, but as you are the owner of the convention, you can decide the order of everything and it is safe. • Except for non-lazy configuration third-party plugins you need to use and the abuse of
afterEvaluate
. Non-lazy configuration breaks creating those convention plugins easily (you need to deal with ordering problems).
afterEvaluate
must die, because it is a 2x1: it is a hack to deal with plugins that are old/wrong (non-lazy). This provokes old plugins can keep doing wrong things, and a new good convention plugin is forced to use it, so they are not "good" anymore.
@Anze Sodja I want to share this: https://pkl-lang.org But just to mention it is almost a 1:1 copy of Kotlin with Arrow Analysis applied. I would like that the Kotlin subset would have, at least, the same functionalities pkl has. I think it is a really good opportunity for one of the next options: • Creating a kotlin.conf.kts which should be that, Kotlin + Arrow analysis with the features of pkl and bindings to generate any config file or gradle files (anyone should be able to pass a parser to generate anything). • Pushing pkl to be an standard in the Kotlin ecosystem. I prefer the first, but the second is getting some of the work done and looks nice.
a
Thanks, I know that some guys already looked in to it, not sure if there was any decision about it
👍 1
j
Is there anything public about those decisions? I have seen some times public Google docs
a
What is public can be found here: https://drive.google.com/drive/folders/1JM7OQwdHGOuoptVtfrwRpzEuzRnCbWXZ About pkl it was just an internal discussion, so there is no doc anywhere
👍 1
o
@Javi Creating #declarative-gradle and also a topic on the community Discourse. It's up to you which channel you prefer though Discourse offers better UX for long lasting discussions (Slack history is 90 days though archived)
j
Hey, thank you for pinging. Sorry but I don’t know what is Discourse 🤔
o
#C06JG95HREY FTR Discourse is https://discuss.gradle.org/
j
Ah okay, I knew them as Gradle forums hahah, sorry!
o
I guess it is a right way to name them 🙂
I cannot create Categories on Gradle Forums. Fix in progress
👍 1
a
@Javi We won't necessarily disallow conditional logic in the declarative format. We're just starting with the assumption that we don't need it and collecting use cases where that assumption may be broken. If (when) we get some good use cases, then we can add some solutions to make those use cases work well. There are a bunch of options for this. One option is support for conditionals in the declarative format, but there are other options too. So, in your example above, it's not clear where values such as
kotlinVersion
are coming from. Is this something you'd expect a developer to pass on the command-line using
-D
, for example?
👍 1
j
kotlinVersion
comes from
-P
. • The complete use case is to override the Kotlin version in the settings file:
Copy code
versionCatalogs {
    create("hubdle") {
        if (kotlinVersion != null) {
            version("kotlin") { //
                strictly(kotlinVersion)
            }
        }
        from(files(rootDir.resolve("gradle/hubdle.libs.versions.toml")))
    }
}
• Publish the Gradle and version catalog plugins with the Kotlin version as the suffix, for example:
0.6.3+2.0.0-dev-14242-SNAPSHOT
(same as KSP does, without SNAPSHOT, to know the Kotlin version easily).
Copy code
versioning {
    semver { //
        if (kotlinVersion.isNotNullNorBlank() && hasSameTagPrefix) {
            mapVersion { gradleVersion ->
                gradleVersion.mapIfKotlinVersionIsProvided(kotlinVersion)
            }
        }
    }
}
If all the power of Kotlin is allowed then there is no problem from my side. I guess it will be based more on functions than properties. But I am missing the point on why plugin authors are not being able to already do that with the tools we have. Picking this image from the recent talk from Louis Jacomet, the DSL on it is already possible to be created without any problem, but that depends on the authors and their tastes. For example, it is really similar to what I have:
Copy code
hubdle {
    kotlin {
        multiplatform {
            apple {
                main { ... }
                ios {
                    main { ... }
                    test { ... }
                }
            }
            jvm {
                main { ... }
                test { ... }
            }
        }
    }
}
I kept the main/test approach which is not in the image, but I think it is necessary, as you could want to put dependencies only for the test set and not the main set. I understand you can have something like:
Copy code
jvm {
    dependencies { ... } // main suppressed to avoid nesting for simple projects
    test {
         dependencies { ... }
    }
    integrationTest { ... }
    functionalTest { ... }
}
But everything is currently possible, just a matter of taste and/or trying to follow a missing standard on how to name or do some things. I still have to read the doc as I had no time, so I am just guessing.