gradle/gradle

Apply Plugin in Apply From can't see outer Classpath because ClassLoader Scoping

warrenc5 opened this issue · 5 comments

Hi, Thanks for gradle - it's an interesting project - coming from maven and ant land etc. I'm having some trouble with something I thought would be straight forward with plugins.

When you have a build.gradle -> apply from sub.gradle -> apply plugin -> projects.plugins.any(p-> p instanceof )

Given T is the class of a plugin applied in build.gradle->apply plugin
It never matches because the Plugins classloaders are different.
It seems the 'apply from' gets it's own new classloader and passes it into 'apply plugin' which allows the Groovy to see the dependencies classpath in sub.gradle but not the dependencies in build.gradle.

Given that in java classes from different classloaders are not equal the instanceof fails to match.

And neither will project.extensions.getByType() from within the plugin

I've tested on gradle 4.5-rc1 and I'm trying to get the gradle-play-publisher plugin to register tasks in my cordova android project so I can run them with
cordova build --release android -- --gradleArg=--info --gradleArg=-publishListingArmv7Release

My constraint is that I'm unable to edit the top build.gradle because it's generated automatically. The only thing I can do is tell cordova to apply from and give it a sub.gradle get that to add tasks which I can invoke after cordova runs the gradle android build task.

I'm thinking cordova & android are gradle and this plugin is gradle so should be easy right?

I'm new to gradle but I've seen there are some serious design constraints around plugins and classloaders and I'm unable to try buildscript{plugins{}}, I'm unable to recommend a gradle based solution here.

I'm a JEE guy so I'm thinking parent first classloaders would be the way forward here.

I built a demo which logs out the classloaders used and looked at the org.gradle.internal.classloader.CachingClassLoader to confirm my assumptions.

Demo is here https://github.com/warrenc5/gradle-classloader/

This could be the same issue which was closed in the migration to github.

https://issues.gradle.org/browse/GRADLE-726

Looks similar/related to #3698

oehme commented

Applying a script is much like calling a library - it doesn't (and shouldn't) know about who is calling it and what classes are visible to its caller.

To make a plugin visible to all scripts (both project build scripts and script plugins), you can put the dependency into the dependencies block of buildSrc/build.gradle. Dependencies in there are visible to everyone. For a more convenient solution you could upvote #1370

Thanks Oehme, I really appreciate your explanation, and helped me to summarize this issue I am facing as..

My applied plugin is looking around in gradle for other plugins' configurations but can't see them.

Just to be clear this is not a "use" issue, this is a design limitation of gradle & plugins. Plugins can see other plugins (through project.plugins) but can't do any Type casting because of this classloader issue. This appears to be a use case which is not currently supported by gradle.

My restriction is that I can only apply plugins, and unfortunately, I cannot add dependencies to the "top level" gradle. (maybe settings I could)

The problem appears to be with classloader delegation for plugins (as they get a caching loader and not a parent first loader)

Thanks for the link, have upvoted.

oehme commented

The problem appears to be with classloader delegation for plugins

The delegation is working as expected, there is only one instance of each script, so it can't have a different parent each time it is applied. apply:from is not like a text include, but like a library call.

Why can't you add dependencies to buildSrc?