Tried to pen something about using Kotlin + Gradle...
# gradle
e
Tried to pen something about using Kotlin + Gradle (starting from a Spring initializr template). Happy to receive feedback on it 🙂 https://kantis.github.io/posts/gradle/
v
I just very roughly skimmed over the code examples: • do not use the Spring dependency management plugin, it is a relict from times when Gradle had no built-in BOM support. Even the maintainer of that plugin recommends to use the built-in BOM support instead • Consider using Kotlin 1.8.10, not 1.7, then you have proper toolchain support and do not need to with with
...Compatibility
, and you can use the new lazy-enabled
compileOptions
instead of the eager
kotlinOptions
. • Better not set the free compiler args unless you really want to overwrite all existing values but instead add to them • When adding authenticated repository you might consider using the Gradle credentials support, so that you don't have the credentials in pain text in the build script
e
Will update the article tonight, thanks Björn 🙂
Added a new article where I address the things you mentioned instead (original articles goal was to break down what the content generated by start.spring.io was) 🙂 https://kantis.github.io/posts/gradle-improvements/
If you want me to remove your name, or link to you somehow, let me know
h
Love this article! Now waiting for the multi module one. I’ve one working with spring boot where the root project is like a holder for the sub projects, and I use spring boot dependencyManagement to configure all dependency versions in the sub projects.
v
You've read that above I wrote that even the author of the Spring dependency managament plugin recommends not to use it? 😉
h
The build in BOM support, that is for the dependency management right? By using Imports { mavenBom(SpringBootPlugin.BOM_COORDINATES) } In the dependencyManagement block?!
v
That is the spring dependency management you shouldn't use anymore. The built-in is the one with
platform(...)
.
h
I’ll take a look at it, do you have a link to documentation?
e
The follow-up article included how to use the BOM 🙂 https://kantis.github.io/posts/gradle-improvements/
and thanks for the feedback, gives me some extra energy to get working on the multi-module article 😄
h
I've the following settings:
Copy code
plugins {
    id("org.springframework.boot") version "3.0.4" apply false
    id("io.spring.dependency-management") version "1.1.0"
    kotlin("jvm") version "1.8.0" apply false
    kotlin("plugin.spring") version "1.8.0" apply false
}

subprojects {

    plugins.apply("java-library")
    plugins.apply("org.jetbrains.kotlin.jvm")
    plugins.apply("org.jetbrains.kotlin.plugin.spring")
    plugins.apply("io.spring.dependency-management")

    dependencyManagement {
        imports {
            mavenBom(SpringBootPlugin.BOM_COORDINATES)
        }
    }

    dependencies{
        implementation()
    }
}
But then I don't get implementation, it will be marked in red. But dependencyManagement works...
v
Don't use
subprojects { ... }
, it is bad practice and has several drawbacks. Use convention plugins to share build configuration. You only get type-safe accessors like
implementation
when you apply plugins priority using the
plugins { ... }
block. If you insist on following the bad practice you could use
"implementation"(...)
instead.
h
Sorry was in a meeting, but I would like to have the root project to be a kind of holder for the subprojects. One sub-project could be like a library with domain objects pojo and do not need Spring Boot dependency, and another subproject is importing the first one and adding Spring Boot functionality to it. And maybe another project is doing something completely different. How to achieve that? I would like to manage all dependencies (and versions) from one place (project root).
v
You shouldn't. To manage versions in one place, I'd recommend to use a version catalog. For the build logic you want to share, you should create convention plugins, e.g. in
buildSrc
or an included build, and then apply those convention plugins directly in the projects where you want these conventions to be in effect. If you e.g. have 5 projects, where 3 are Java libraries and 2 are Java applications, you could e.g. have two convention plugins, one with the settings you want in Java library projects and one with the settings you want in Java application projects and then you apply the respecitve convention plugin in the respective projects directly. Using
allprojects { ... }
or
subprojects { ... }
is so-called cross-project configuration which introduces project coupling which works against more sophisticated features like configure-on-demand or configuration cache, or project isolation, ... And besides that it makes builds harder to understand and harder to maintain, as project's build script is not the source of truth, but you have to know and check places where you inject settings from outside.
h
How would my root project look like? Which plugins should be applied there? Like I would only want to have spring boot applied to the two applications sub projects and not to the other 3 libraries from your example.
e
My root project typically only contains report aggregation tasks and the like. Your application convention plugin would apply the Spring Boot plugin, while your library conventions wouldn't
h
Looks very complicated setup. Subproject pointing back to ../../gradle/settings folder.
v
You probably don't need all from there. Just an example where you could look or watch the video of Jendrik. 🙂
e
Johannes also puts convention plugins in
./gradle/plugins
, how does that compare to using
buildSrc
or an included build?
h
Which video from Jendrik?
v
It is linked in the description of that repository. He often makes pretty popular YT videos explaining Gradle features.
h
v
Johannes
Jendrik. Johannes is the last name. 😄 (I made that error myself too)
also puts convention plugins in
./gradle/plugins
, how does that compare to using
buildSrc
or an included build?
I usually put them in
./gradle/build-logic
. Comparing to "an included build" is hard, because it most probably IS an included build.
buildSrc
vs. an included build is in the meantime for the most part a philosophical question. Since Gradle 8,
buildSrc
changed in some aspects to be even more similar to an included build. But I personally still prefer included builds. The most significant difference comes into play if you have several different convention plugins that are not applied everywhere. If you have those in an included build in multiple projects in that build (or in multiple included builds), then only projects that use them get their classpath changed on a change in the plugin and thus only those need to be re-executed.
buildSrc
result is prepended to all build script classpaths and thus every change invalidates everything. In small projects or if you only have convention plugins you anyway apply everwhere or almost everywhere, this difference is usually neglectible. Another difference is, that with
buildSrc
you get type-safe accessors for the plugins for usage in Kotlin DSL, while coming from an included build (except when using a little hack) you have to use their ID to apply them, which I don't see as major drawback. You can also add them to a version catalog and use it from there. Another difference where you must use
buildSrc
due to the classpath prepending is, if you have the rare need to monkey-patch a plugin class. This only works when doing it in
buildSrc
, but I'm not going to elaborate on that here as you hopefully never need that.