Hi. I'm having trouble understanding the differenc...
# community-support
a
Hi. I'm having trouble understanding the difference in plugin's behavior depending on whether a
buildscript
block is used or not (or, at least, that seems to be the difference). I'm sharing the post from #CJYS1DAP5 for visibility because I assume the problem may be more generic and not necessarily Android related. https://gradle-community.slack.com/archives/CJYS1DAP5/p1753877427121339
v
What happens if in your second version • you move the
buildscript
block from the root build script to the
app
buildscript • or you add the plugin version in the version catalog, and replace the
buildscript
block in the root project with
plugins { alias(libs.plugins.room) apply false }
Does it work with the first and not work with the second?
a
> What happens if in your second version > • you move the
buildscript
block from the root build script to the
app
buildscript
Copy code
FAILURE: Build failed with an exception.

* Where:
Build file '/blablabla/app/build.gradle.kts' line: 13

* What went wrong:
Plugin [id: 'androidx.room'] was not found in any of the following sources:

- Gradle Core Plugins (plugin is not in 'org.gradle' namespace)
- Included Builds (No included builds contain this plugin)
- Plugin Repositories (plugin dependency must include a version number for this source)
---- > • or you add the plugin version in the version catalog, and replace the
buildscript
block in the root project with
plugins { alias(libs.plugins.room) apply false }
Same as before the change:
Copy code
> Task :app:kspDebugKotlin
w: [ksp] Schema export directory was not provided to the annotation processor (etc.)
---- However, let me add I've just realized something new. Initially, I observed the problem in a different project (obviously) and started preparing a minimum reproduction project, and I noticed a strange difference between them. In the initial project, besides the KSP warning, the database schema file was not being generated (that's why my original post says so). But in the reproduction project it is being generated despite the warning, which is even more surprising...
Hmm, no, it's not working better in the repro project. I'm starting to think it had something to do with the cache. Double-checking now.
v
Ah sorry, for the first of my bullet-points also remove it from
plugins
and use
apply(plugin = "androidx.room")
on top-level instead
a
Will check in a moment 🫔 Let me clarify my last message now. This is what I observed: 1. on branch where everything works normally ("method 1" from original post) 2.
git clean -xdf
3.
./gradlew :app:assembleDebug --rerun-tasks --no-build-cache --no-configuration-cache
4. all good 5.
rm -r app/schemas
to remove the generated DB schema file 6. switching to the branch where I'm getting the warning ("method 2") 7. IF a. I do run
git clean -xdf
now i.
./gradlew :app:assembleDebug --rerun-tasks --no-build-cache --no-configuration-cache
ii. schema file is not generated by the plugin b. I don't run
git clean -xdf
now i.
./gradlew :app:assembleDebug --rerun-tasks --no-build-cache --no-configuration-cache
ii. schema file somehow comes back 🤯
Ah sorry, for the first of my bullet-points also remove it from
plugins
and use
apply(plugin = "androidx.room")
on top-level instead
Not sure if I understood it perfectly - here's how the
app/build.gradle.kts
looks now (part of it)
Copy code
buildscript {
    repositories {
        google()
        mavenCentral()
    }
    dependencies {
        classpath(libs.room.plugin)
    }
}

plugins {
    // no more Room plugin here
}

apply(plugin = "androidx.room") // added this

// same as before
android {
    room {
        schemaDirectory("$projectDir/schemas")
    }
}
and root
build.gradle.kts
is deleted Then I'm getting:
Copy code
> Configure project :app
e: file:///blablabla/app/build.gradle.kts:55:5: Unresolved reference: room
e: file:///blablabla/app/build.gradle.kts:56:9: Unresolved reference: schemaDirectory

FAILURE: Build failed with an exception.

* Where:
Build file '/Users/azabost/projects/quests/modularization-quest/app/build.gradle.kts' line: 55

* What went wrong:
Script compilation errors:

  Line 55:     room {
               ^ Unresolved reference: room

  Line 56:         schemaDirectory("$projectDir/schemas")
                   ^ Unresolved reference: schemaDirectory
v
Argh, yeah, you also miss the accessors then of course, one moment
Btw.
room { ... }
inside
android { ... }
is just visual clutter.
room
is an extension on
Project
, not on the android extension, so you should anyway move it to top-level.
šŸ‘ 1
a
Here's the whole repro if anyone is interested: https://github.com/azabost/room-gradle-plugin-issue-repro
v
And additionally replace
room {
by
configure<RoomExtension> {
, then the configuraiton works also with the
apply
🫔 1
a
Btw.
room { ... }
inside
android { ... }
is just visual clutter.
Yeah, I figured out. I was following the official guide, which is obviously far from ideal...
v
If the official guide recommends that, it is buggy imho. Either it should add the extension to the android extension, or document to configure it on top-level. But adding the extension to top-level but then documenting to configure it within the android block is very inconsistent and wrong.
šŸ‘ 1
a
> And additionally replace
room {
by
configure<RoomExtension> {
, then the configuraiton works also with the
apply
With this, everything works fine, i.e. no KSP warning, and the schema gets generated correctly
v
Yeah, so I'd say it is a classloading topic.
Because two non-working cases are when you add the room plugin to the root project classpath classloader which is a parent to the subproject classpath classloader
a
But adding the extension to top-level but then documenting to configure it within the android block is very inconsistent and wrong.
Fully agreed. Can't count how many times I was mislead by this kind of documentation back in the groovy times, when it was less obvious.
v
In the working examples you add the plugin to the subproject classpath classloader
Probably the plugin needs to access some classes from other plugins that it cannot access as they are only in the subrpoject classloader
Probably some android integration or similar
a
That's interesting, thanks for improving my understanding. Do you happen to know how could this be happening? I was under impression that the plugin will get applied, i.e. its apply(T) will be invoked, so it's internal logic will run, during the subproject's
plugins {}
block execution e.g.
Copy code
// app/build.gradle.kts

plugins {
    id("com.android.application")
    // a few others
    id("androidx.room") // <--- now the plugin's apply(T) runs, no?
}
So my assumption would be that adding the plugin to root project's
buildscript
/ classpath wouldn't make any difference, because the plugin wouldn't really run at that point šŸ¤”
btw I guess I will report that strange behavior as a bug in the plugin
v
Your assumption is correct, but that does not change anything in the classloader logic. With adding the plugin to the root project classloader it does not see the classes from the child classloader.
If you would also move whatever plugin it requires - probably the android plugin - to the root build script classloader it would probably also work.
So if you for example have
Copy code
plugins {
    alias(libs.plugins.android.application) apply false
    alias(libs.plugins.room) apply false
}
in the root project and
Copy code
plugins {
    alias(libs.plugins.android.application)
    alias(libs.plugins.room)
}
in the subproject
Btw. why do you add it to the root project via buildscript block at all? Is that also what that plugin suggests? If so, that's also pretty non-sense.
Either just use
Copy code
plugins {
    alias(libs.plugins.room) apply false
}
for that, or just don't do it at all if you have no technical reason to do so
alias(libs.plugins.room) apply false
is like using the
buildscript
block to add the plugin to the classpath
alias(libs.plugins.room)
with the plugin already being in the classpath by some parent classloader is like just calling
apply(...)
alias(libs.plugins.room)
with the plugin not being in the classpath yet is like a combination of the above two
a
Btw. why do you add it to the root project via buildscript block at all?
I did it because in the other project they still have a lot of plugins in the root
buildscript
and I was exploring my options. I wanted to see if I can add the plugin and keep their old convention until they migrate away the whole root
build.gradle.kts
from
buildscript
usage
šŸ‘Œ 1
v
Plus you get type-safe accessors for Kotlin DSL when you use the
plugins { ... }
block to apply and it is the idiomatic and recommended way.
a
>
alias(libs.plugins.room) apply false
is like using the
buildscript
block to add the plugin to the classpath Yes, and the documentation for that plugin suggests doing so i.e. the
apply false
approach in root - that's why I was experimenting with
classpath
in
buildscript
as a replacement for that
v
I see, yeah, those two are equivalent and imho you should never do either unless technically necessary, otherwise it just uglifies the build logic.
But that room plugin is anyway full of bad-practices šŸ˜„
a
If you would also move whatever plugin it requires - probably the android plugin - to the root build script classloader it would probably also work.
If you see the complete example, there actually should be Android plugin in root, inherited from `buildSrc/build.gradle.kts`(correct me if I'm wrong that root's classloader's parent is the
buildSrc
's classloader 😵 )
v
Hm, indeed, well, then it is not Android I guess, but something else
But sounds very much like classloader problem
Just an educated guess though at this point
a
(if anyone reads it in the future and wonders how I know, here's the blog post I read some time ago)
OK, I will report this problem to Google and will see what they say šŸ¤·ā€ā™‚ļø
Thanks again @Vampire šŸ»
šŸ‘Œ 1
One more thing: do you happen to know why running
git clean -xdf
makes a difference when switching branches? How come the schema file gets restored despite the
--rerun-tasks --no-build-cache --no-configuration-cache
?
v
As I have no idea where it is coming from at all, no
a
ā¤ļø gradlephant 🄲
If anyone is interested in following this issue, here's the link: https://issuetracker.google.com/issues/435139152
šŸ‘Œ 1
Here's one more interesting finding: If I add
Copy code
plugins {
    alias(libs.plugins.room) apply false
}
in root
build.gradle.kts
on top of the working example then it stops working 🫠
v
That's not interesting, but expected
šŸ˜‚ 1
You again add the plugin to the root project classloader, whehter you also add it to the child classloader is irrelevant, because classes that are present on a parent classloader are used from there.
Or rather it just further supports that it is a classloader problem
a
Yeah, but then it means their documentation is completely wrong. Previously, I could justify the problem by thinking: "OK, they don't say to use buildscript block, so if I do it, then it's only my fault". but now I can see it doesn't even work in their scenario.
v
As I said, both are functionally equivalent
Maybe they expect you have all plugins in the root build script or something like that, or at least the relevant ones
🤷
Usually with classloading problems you get something obvious to the knowing eye like classnotfoundexceptions, classversionerrors and so on
This silent behaviorchange indeed seems to be untypical, that's why classloading issue is jsut an educated guess, as some symptoms match, but some are asymptomatic