Gradle doesn't add the same dependencies to classpath when applying plugins

I was working on a Gradle plugin, so I added gradleApi() and localGroovy() to the dependencies of my plugin project. After this the Gradle API is available in my plugin project, together with a lot of other dependencies used by Gradle. As an example, this also adds joda-time-2.7 to the classpath, and using it from the plugins (without an additional explicit dependency on it) compiles just fine.

The problems come up when trying to use such a plugin - at runtime after applying the plugin using the classes from joda time for example will throw a ClassNotFoundException because Gradle doesn’t add the complete gradleApi() to the classpath as it does when creating (and compiling) a plugin. Adding a classpath dependency for the buildscript on gradleApi() like below fixes it, however that doesn’t work when using the new plugins syntax obviously:

buildscript {
    dependencies {
        classpath gradleApi()
        classpath 'com.example:my-gradle-plugin:1.0'
    }
}

Is that intended behaviour? In my opinion, every dependency added by Gradle at compile time when making the plugin should also be available at runtime when applying the plugin. Otherwise Gradle should only add the dependencies really available at runtime at compile time for Gradle plugins.

(Sure, I could simply add an explicit dependency on joda-time - however that would cause additional download time and this post is more a question if this is intended behaviour as I couldn’t find any documentation about this topic.

+1

I faced the same problem (see Using the maven-publish plugin = no dependencies in pom.xml).
The solutions I found were:

  • to systematically added gradleApi() in the consumer script’s buildscript block
  • to add the missing classpath 'foo:bar:baz' dependency only (if I know which ones I need, and if they are not too many)
  • to tweak the generated ivy.xml / mavem.pom of my plugin to reference the needed libraries (as they are not added, which kind of make sense, since otherwise the whole gradleApi content shall be referenced)
  • to systematically added gradleApi() in the consumer script’s buildscript block

That’s basically the best way, it doesn’t download any additional dependencies, and makes the classpath look like in the project. The only problem is that it always needs to be added manually and doesn’t work well together with Gradle’s plugin portal.

  • to add the missing classpath ‘foo:bar:baz’ dependency only (if I know which ones I need, and if they are not too many)
  • to tweak the generated ivy.xml / mavem.pom of my plugin to reference
    the needed libraries (as they are not added, which kind of make sense,
    since otherwise the whole gradleApi content shall be referenced)

Ye, that would work but would require an explicit dependency on the libraries so Gradle would download them separately when using the plugin even though they’re already bundled in each Gradle distribution. That may be the intended behaviour but would be confusing because then Gradle should only add the really available libraries to the classpath in the plugin project.

I have also come across something similar - This seems to happen when your code is dependent on JARs in the ${gradle.gradleUserHomeDir}/lib/plugins directory. Only the JARs in the lib folder seem to be made available to the buildscript block.

The gradleTest plugin helps you catch these kind of things before release, but does not solve it.

So yes, it is confusing and painful.

1 Like

Thanks! That would make sense, the dependencies are probably only loaded when the plugins are also applied. However, I do think these dependencies either shouldn’t be added when using gradleApi() or should be made available at runtime.

Here’s another workaround you can include inside your plugin code:

FileCollection gradleApi = project.getConfigurations().detachedConfiguration(project.getDependencies().gradleApi());
1 Like

As of Gradle 2.14, gradleApi() no longer imposes Gradle’s internal dependencies. That is, it only exposes what you will actually have access to at runtime.