This message was deleted.
# community-support
s
This message was deleted.
j
That's due to how Java's classloading mechanism works. When it loads a class, all types referenced in it need to be available (even if they are never touched during execution later). You need to isolate all references to the Android types into a separate Class. And then if you do not load that Class, it works. Here is an example from one of my plugins doing that with the Dependency-Analysis-Plugin: Condition: https://github.com/gradlex-org/java-module-dependencies/blob/main/src/main/java/or[…]adlex/javamodule/dependencies/JavaModuleDependenciesPlugin.java Code referring Plugin types: https://github.com/gradlex-org/java-module-dependencies/blob/main/src/main/java/or[…]ule/dependencies/internal/bridges/DependencyAnalysisBridge.java
(alternatively you can do everything via reflection... which can get ugly quickly)
e
I tried isolating the references in another class like the example you showed but I'm getting the same result. Is it because you're using
withId
and I'm using
hasPlugin
to gate against it?
j
I can't see how that would cause the problem. But you probably want to use
withId
. With
hasPlugin
, your code will only work if the Android plugin is applied before your own plugin.
For the error: The stacktrace should (hopefully) give a hint what triggered the loading of the Android specific class.
e
I ended up going with reflection (it was a pretty simple case). I think the issue was related to classloaders not working across AGP and build services
e
also that code depends on plugin load order. better use
Copy code
project.pluginManager.withPlugin("com.android.base") {
    ...
}
t
Fwiw @Jendrik Johannes, your note about classloading is not accurate. I used to do just that in earlier versions of the net.ltgt.errorprone project and it just worked™. I've been told it depends on the JVM being used though: it would work with HotSpot but possibly not with other JVMs (J9, etc.) Either that or the behavior of HotSpot changed since then 🤷 Here, it looks more like a cross-configuration issue, where the plugin accesses all projects in the build: https://github.com/spdx/spdx-gradle-plugin/pull/83/files
v
The problem with the not found classes is most probably because your plugin is in a higher class loader. If the android plugin is for example applied in the root build script but your plugin is coming from
buildSrc
or settings script classpath, your plugin is in a higher class loader and thus cannot see the AGP classes.
e
I think a larger refactor of the project is needed to start using
withPlugin
but for now I don't think it could be used. It seems to be working though, because this is all happening at serialization time because it's in a provider used as an input to a task.
it looks more like a cross-configuration issue
I'd like to not do that in the future, but how does it affect classloading? @Vampire both plugins are applied in the plugins block at the same level (i.e. in the script for the specific project)
t
> it looks more like a cross-configuration issue
I'd like to not do that in the future, but how does it affect classloading?
IIUC, the plugin is reaching to all projects in the build, so one project can have the Android plugin applied (e.g. the "app" subproject in one of the tests) but the plugin instance computing the value executes in another project where the Android plugin is not available (the "lib" subproject in the same test). Using reflection like you did, you reach for the "android" extension of that other project and use reflection on it, but never actually use Class.forName or similar, which would have the same issue as directly using the class like you did initially (unless you used Class.forName with the proper classloader).
Oops, no, that was the reverse: the "app" subproject has both Android and SPDX plugins applied, but then you reach for the "lib" subproject which also has the Android plugin applied, but that one is in a different classloader (this could be fixed by adding the Android plugin to the root project with
apply false
). That wouldn't explain the ClassNotFound though.
The ClassNotFound would likely be explained by the setup for your tests though (assuming it happened in those tests): you're using withPluginClasspath which will add the plugin to a classloader that's a parent to the whole build, and that doesn't contain the Android plugin. Workarounds include: • adding the Android plugin to that "plugin classpath" by configuring the
pluginUnderTestMetadata
task's
pluginClasspath
; the plugin would always be available in the classloader even if not applied to a project, so it might not detect some bugs that you'd only see later in a real project • not using withPluginClasspath with instead either using the current build as an included build of the test project (assuming a compatible Gradle version is used for the tests) or publishing the plugin to a local Maven repository and configuring the test projects to load it from there
e
It was
withPluginClasspath
thanks
v
Yes, that causes exactly what I said. The plugin classes are then on a class loader higher than where AGP is found and thus the classes cannot be found in the tests. To fix this without reflection you either need to also inject AGP via
withPluginClasspath
for example by adding it to
testRuntimeOnly
additionally to
compileOnly
, or by not using
withPluginClasspath
but for example deploying to some locale file-repository (something exclusive to the test execution, not
mavenLocal()
) and then consuming the plugin under test from that repository.
👍 1