I’ve seen people using included builds for build l...
# gradle
p
I’ve seen people using included builds for build logic instead of
buildSrc
. What’s the difference?
m
More generally,
build-logic
is less "special" than
buildSrc
. I need included builds anyways so if I can spare my brain having to remember the intricacies of
buildSrc
, I'm all for it (although there seem to be a small resurgence of
buildSrc
as of lately I must say)
p
I see, thanks! is there a difference in how well it works in the IDE (asking for included builds in general)?
m
buil-logic
also allows you to have more granularity, define multiple submodules and plugins and only invalidate those parts of the build that actually change.
buildSrc
is a huge switch. Any change in there will invalidate all your build
Re: IDE, it "works for me" 😄
p
ok, so probably not a big deal in case of small build logic modules - I won’t throw everything to rewrite my build logic 🙂 but I get your points, thanks!
m
The change should be pretty straightforward if you wanted to
h
Another advantage: the plugins are precompiled and the
check
task of buildSrc isn't called when using build-logic
p
I must learn how to correctly consume it - I don’t have much experience with included builds either. I’ve seen several different ways on GitHub
m
I think in a first step, it's a matter of this in settings.gradle.kts
Copy code
includeBuild("build-logic")
And then in your root build.gradle.kts:
Copy code
buildscript {
  dependencies {
    classpath("com.example.build-logic:build-logic")
  }
}
Or something like this
Of course nothing is easy when it comes to classloaders 😅
t
included builds work better with the build cache, as changing it does not invalidate the cache for the whole project
p
are there any advantages of
buildSrc
whatsoever?
m
It saves you a couple of lines mostly I would say
t
not I am aware of, but buildSrc was before, that is why people use it (I think)
m
And yea, you'll find documentation about it
a
I find
buildSrc
much more simple, and there are edgecases that still don’t work with
build-logic
m
@Adam S do you have any example in mind? It's really vey similar
a
sometimes Kotlin DSL accessors aren’t generated https://github.com/gradle/gradle/issues/23795
m
Ah right, accessors...
I gave up on those 😄
I write my build logic in plain Kotlin usually, no accessors
a
it can also get complicated when there’s a settings plugins included build https://kotlinlang.slack.com/archives/C19FD9681/p1675514666105869
there’s nothing wrong with replacing buildSrc with included-build, but I think it should only be done if it’s necessary. buildSrc downsides aren’t relevant for most projects.
m
I'm dreaming of a world where there's no more
buildSrc
and
includeBuild()
, just everything is an
include()
a
that would indeed be ideal!
p
thanks @Adam S for chiming in, especially that you contributed
buildSrc
in https://github.com/krzema12/github-workflows-kt/ 😄
I’m dreaming of a world where there’s no more
buildSrc
and
includeBuild()
, just everything is an
include()
I remember from my days in Amazon that we had this Brazil build system. What was awesome about it was that a build-time dependency (or rather, a piece of logic) wasn’t different itself from a runtime or compile-time one. Only the way it was included by subsequent modules mattered. I miss it in Gradle
a
the main point is that buildSrc or included-build plugins are a million times better than
allprojects {}
or
subprojects {}
p
Absolutely! I have to struggle a lot with projects with huge
subprojects {}
sections, where a lot of configuration is applied to all submodules even they does not need it.
v
Regarding the differences between
buildSrc
and an included build, many are straightened out with Gradle 8 where
buildSrc
is made much more similar to an included build while it is still technically not an included build. For example starting with Gradle 8 you can call
buildSrc
tasks from the commandline, tests are not run automatically,
buildSrc
now can include other builds, init scripts are also applied to
buildSrc
build, .... All these are not true for
buildSrc
up to now. So with the changes in Gradle 8, the differences are way less significant. Differences that still apply that I have in mind are: • you do not get Kotlin DSL accessors for precompiled script plugins in included builds that you get for
buildSrc
buildSrc
classes and dependencies are automatically added to a parent class loader of all build scripts and thus there is no conflict resolution with dependencies you have in actual build scripts in case you do not move all build logic to
buildSrc
• the location of
buildSrc
is fixed and cannot be configured I usually prefer included builds over
buildSrc
because I can have the included build in
gradle/build-logic
, can have included builds with common logic for the main build and the build logic build (should not be true anymore with Gradle 8 ), have proper conflict resolution for dependencies, and do not invalidate all build scripts just because one change in the build logic. I personally only use
buildSrc
for cases where it is necessary, for example to monkey-patch classes of plugins I use. Because
buildSrc
classes are in a parent class loader of all build scripts, classes included in it win over the same classes in a plugin and thus can be used to fix problems like missing input / output annotations that cannot be fixed otherwise without a fixed plugin version.
Switching to an included build instead of
buildSrc
should usually not be more than renaming or moving the directory, adding an
includeBuild
and maybe replacing the plugin accessors with applying by
id
for precompiled script plugins. Everything else should basically work the same.
p
For me
buildSrc
is always the starting point and depending on the project, I transform it to
includeBuild
a
I personally only use buildSrc for cases where it is necessary, for example to monkey-patch classes of plugins I use.
This sounds amazing. I didn’t know this was possible. Do you have an example?
v
Nothing public right now. But it is just having the class to monkey-patch in
buildSrc
, with the exact same package and name. So copy over the original file, do the necessary changes and you are done.
buildSrc
is in the parent class loader of the build scripts, so the classes from there win due to the classloader delegation rules of Java that say that you first have to ask your parent classloader for a class before you load it yourself.
Additionally, of course, you need all dependencies for compilng that class, but
compileOnly
should usually be enough for that iirc.
m
> I personally only use buildSrc for cases where it is necessary, for example to monkey-patch classes of plugins I use.
> This sounds amazing. I didn’t know this was possible. Do you have an example?
For a recent example, you could use monkey-patching to make Kotlin 1.7.20 compatible with Gradle 7.6 (there was a binary change that made MPP tests crash): See the code in this Youtrack
v
But keep in mind, that those monkey-patchings should be one of the last resorts 😉
a
I make no promises
h
whats the difference between using
buildScript.dependency.classpath("")
to overwrite a (bad) dependency?
v
Between that and what?
monkey-patching?
m
I think both works? Only
buildSrc
might be a classloader above
buildScript.dependency.classpath
so can override more stuff?
h
yes
v
The monkey-patching is for cases where there is no updated dependency, but you have to patch the class yourself as last resort
If there is an updated dependency with a fix / that patch, by all means just use the newer version
h
Ahhh, got it, okay.
150 Views