I’m trying to integrate some other modules into my first Gradle plugin. These modules require Commons-io 2.4. I’ve discovered that Gradle forces the loading of commons-io 1.4, so any references to the same class later in the classpath won’t be found, causing compile errors in any code using commons-io 2.4.
Even with knowledgeable people on StackOverflow, I haven’t found a solution to this in simple configuration changes. It appears that I simply can’t use commons-io 2.4, and will have to hack on the code I’m trying to integrate to make it not use commons-io 2.4.
If it matters, here’s the SO discussion I started.
Note that this commons-io 1.4 dependency isn’t seen in “gradle dependencies”. When I add “–debug” to the command line, I see that it’s getting this jar from the Gradle distribution, not from the Gradle cache. Looking further down the command line, I see the 2.4 reference, which ends up being ignored as a result.
I was able to whack (worse than hack) around this by manually copying six different classes from the commons-io 2.4 distribution, giving them slightly different names and changing the references to point to those copied classes. This is not a reasonable solution.
Hey David, you pretty much hit GRADLE-2516. We want to solve the general issue that gradleApi is leaking classes into the plugin classloader but havn’t scheduled a fix for it at the moment.
Related, but I’m not even including the “gradleApi” dependency. I was originally, but I’ve commented it out, and it made no difference.
gradleApi() dependencies are purely compile time, if I recall correctly. At runtime, some of the Gradle core implementation dependencies are exposed to plugins which can cause issues. That may be it’s own JIRA but in general, plugin classpath isolation and dependency confliction is something we are aware of.
David, in general we suggest to ensure you are using the exact necessary plugin dependencies to package them with your plugin using something like jajar or the Gradle Shadow Plugin.
Thanks Mark, but I don’t see how that helps me. I need commons-io 2.4. I specified that as a dependency. Gradle overrides that with the commons-io 1.4 jar, breaking my compile. I see no way to un-override that.
Assuming it is your plugin code that is using the dependency you could repackage commons-io 2.4 under a custom package name, and use that in your code.
Edit: This is specifically to solve the runtime problem.
And is it possible for that strategy to handle the “build-time” problem?
You would have to do it in conjunction with a fix to exclude commons-io 1.4 from the compile classpath.
compileClasspath = configurations.compile.minus files("$gradle.gradleHomeDir/lib/commons-io-1.4.jar")
Interesting. I just verified that this last change does what you describe, removing commons-io 1.4 from the classpath used for compilation of both the main and integTest sourcesets, leaving the sole commons-io 2.4 reference. You appear to be saying that I should be doing this “in conjunction” with something else. This appears to fix my entire problem. Why would I need to do anything else? I haven’t yet converted my whacked code to reference the regular commons-io classes yet, but I don’t see why that wouldn’t work now.
I wasn’t sure if this would fix the runtime problem, as commons-io 1.4 may be exposed to your plugin’s classloader. That said, in the runtime case, we may be properly giving precedence to 2.4. I only bring up the repacking option as it is the only way to be absolutely sure your plugin is using the correct version. There can still be issues with conflicts with other plugins, etc.
I guess the decision on whether to fully hide dependent packages would depend on how widely you expect a plugin to be reused. If it’s intended to be used by the same team in a set of related projects, hiding those dependencies is probably less important. If it’s going in the plugin portal for intended wide use, then that would be a more important consideration.