[GRADLE-1715] gradleApi() pollutes compile time with runtime dependencies Created: 03/Aug/11  Updated: 04/May/16  Resolved: 04/May/16

Status: Resolved
Project: Gradle
Affects Version/s: 1.0-milestone-4
Fix Version/s: 2.14-rc-1

Type: Improvement
Reporter: Hani Suleiman Assignee: Unassigned
Resolution: Fixed Votes: 24


When using gradleApi() as a compile time dependency, all of Gradle's runtime dependencies are also included. This causes problems for example in the case of trying to compile against log4j (when using a custom appender as part of the project), since gradle includes log4j-over-slf4j.jar which provides a small subset of the log4j API that shadows the log4j jar.

Ideally, compiling against gradleApi() should only add the gradle specific jars to the compile time class path, not all of its transitive dependencies.

Comment by Ben Jansen [ 07/Jun/12 ]

Is there any way to work around this issue? I am building a plugin and attempting to use hamcrest in some tests. gradleApi() puts junit on the compile classpath. The junit jar contains some outdated hamcrest classes, which conflict with my hamcrest dependency. This results in NoSuchMethodErrors.

I have tried various exclude incantations to no avail.

Comment by Luke Daley [ 17/Jan/13 ]

Related forum post: http://forums.gradle.org/gradle/topics/using_guava_13_0_1_with_custom_plugin

Comment by Jesper Thuun-Petersen.dk [ 29/Apr/13 ]

I'm having a similar problem with the bnd library. The version bundled with Gradle is 1.50 and we're running the latest release 2.1. Everything works at build-time, but havings tests in buildSrc fails miserably. Is there any workaround available you know of?

Comment by Luke Daley [ 02/May/13 ]

None that I know of sorry.

You could try manually crafting something similar to the gradleApi() dependency that excludes whatever is giving you grief.

Comment by Martin Odhelius [ 03/Sep/13 ]

I have a similar problem problem with gradle 1.6. When I add gradleApi(), in my unit tests I sporadically get errors like NoClassDefFoundError and IllegalStateException: Detected both log4j-over-slf4j.jar AND slf4j-log4j12.jar on the class path, preempting StackOverflowError. I also had problems crafting something similar. gradleApi() adds 129 dependencies. Do anyone have an example of a manually crafted workaround?

Comment by Bruce Edge [ 28/Oct/13 ]

Is this the same issue that causes gradle dependencies to be prepended to the buildSrc compile classpath?
If so, it gets my vote. This is turning out to be a real pain to work around. It's limiting us to only the versions of jars listed in the gradle deps. IOW one cannot use a newer version of any of the jars that are on the gradle dep list.

Comment by Tamsin Slinn [ 17/Jul/14 ]

Just spent a good few hours trying to work out why commons-io 1.4 was being included in my dependencies, conflicting with commons-io 2.4 which we require.

The "gradle dependencies" command does not output the dependencies added by gradleApi(), so it was a bit confusing!

I'm hoping we can depend directly on gradle-core to enable plugin development...

Comment by Abel Salgado Romero [ 20/Sep/14 ]

I'm having exactly the same problem as Tamsin with commons-io and I cannot change the dependencies because they are needed by a third component. Is this going to be fixed?

Also, I would like to suggest to change the classification from improvement to bug. In my opinion, the fact that the dependency resolver overwrite the projects direct dependencies is a malfunctions.

Edit: I tried to add gradle dependencies manually but I end up with the next error when executing the tests (even, when commons-io-1.4 is added as dependency), seems like commons-io-1.4 is validated somehow and cannot be changed.

Caused by: org.gradle.internal.service.ServiceCreationException: Could not create service of type DefaultClassLoaderRegistry using GlobalScopeServices.createClassLoaderRegistry().
at org.gradle.internal.service.DefaultServiceRegistry$FactoryMethodService.invokeMethod(DefaultServiceRegistry.java:639)
at org.gradle.internal.service.DefaultServiceRegistry$FactoryService.create(DefaultServiceRegistry.java:593)
at org.gradle.internal.service.DefaultServiceRegistry$ManagedObjectProvider.getInstance(DefaultServiceRegistry.java:435)
at org.gradle.internal.service.DefaultServiceRegistry$SingletonService.get(DefaultServiceRegistry.java:475)
at org.gradle.internal.service.DefaultServiceRegistry.findConfigureMethod(DefaultServiceRegistry.java:141)
at org.gradle.internal.service.DefaultServiceRegistry.findProviderMethods(DefaultServiceRegistry.java:115)
at org.gradle.internal.service.DefaultServiceRegistry.addProvider(DefaultServiceRegistry.java:223)
at org.gradle.internal.service.ServiceRegistryBuilder.build(ServiceRegistryBuilder.java:52)
at org.gradle.testfixtures.internal.ProjectBuilderImpl.<clinit>(ProjectBuilderImpl.java:43)
... 3 more
Caused by: java.lang.IllegalArgumentException: Cannot find JAR 'commons-io-1.4.jar' required by module 'gradle-core' using classpath.
at org.gradle.api.internal.classpath.DefaultModuleRegistry.findDependencyJar(DefaultModuleRegistry.java:248)
at org.gradle.api.internal.classpath.DefaultModuleRegistry.module(DefaultModuleRegistry.java:143)
at org.gradle.api.internal.classpath.DefaultModuleRegistry.loadModule(DefaultModuleRegistry.java:130)
at org.gradle.api.internal.classpath.DefaultModuleRegistry.getModule(DefaultModuleRegistry.java:108)
at org.gradle.api.internal.DynamicModulesClassPathProvider.findClassPath(DynamicModulesClassPathProvider.java:45)
at org.gradle.api.internal.DefaultClassPathRegistry.getClassPath(DefaultClassPathRegistry.java:34)
at org.gradle.initialization.DefaultClassLoaderRegistry.<init>(DefaultClassLoaderRegistry.java:37)
at org.gradle.internal.service.scopes.GlobalScopeServices.createClassLoaderRegistry(GlobalScopeServices.java:115)
at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:63)
at org.gradle.internal.service.DefaultServiceRegistry.invoke(DefaultServiceRegistry.java:324)
at org.gradle.internal.service.DefaultServiceRegistry.access$1200(DefaultServiceRegistry.java:54)
at org.gradle.internal.service.DefaultServiceRegistry$FactoryMethodService.invokeMethod(DefaultServiceRegistry.java:637)
... 11 more

Comment by Anja Papatola [ 06/Mar/15 ]

I am also facing this problem developing a plugin test.
I wanted to use FilenameUtils.directoryContains() which is unfortunately not contained in commons-io:1.4.

Funny: it is only failing in the test. The plugin itself works perfectly well.

Comment by Martin d'Anjou [ 24/Jul/15 ]

Same problem with httpclient-4.5, problem accessing AllowAllHostnameVerifier.INSTANCE in the test only. By default, I use the latest official releases, so it is natural for me to use httpclient-4.5. But the gradleApi() has a hidden dependency on httpclient-4.2.2, which creates a hidden conflict.

So plugin developers have no choice but to be aware of the hidden dependencies introduced by the gradleApi(). Those dependencies can be listed using (thanks to Vampire0):

task libtest << { configurations.testCompile.files.each { println it } }

The bug with Gradle is that gradleApi() does not participate in dependency resolution, so the dependency problem cannot be found by the user.

Trying to use a compile time dependency on gradle-core-4.5 instead of on gradleApi() does not help, as it appears to be missing what's needed for writing plugins. So replacing gradleApi() with say:

  compile 'org.gradle:gradle-core:2.5'
  compile 'org.codehaus.groovy:groovy-all:2.3.10'

Leads to:

 ./gradlew test
:compileJava UP-TO-DATE
:compileGroovy FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':compileGroovy'.
> Unable to load class org.gradle.api.internal.AbstractTask due to missing dependency org/gradle/model/internal/registry/ModelRegistryScope
Comment by Steve Saliman [ 26/Oct/15 ]

Plugin developers who use the cobertura plugin for test coverage are also reporting this problem. They get "SLF4J: Class path contains multiple SLF4J bindings." messages when they try to instrument the code, and the build ultimately fails due to a NoClassDefFoundError on org/slf4j/spi/LoggerFactoryBinder.

The cobertura plugin tries to exclude the slf4j dependency to avoid this problem, but that exclusion is ignored when the gradleApi() method is used. https://github.com/stevesaliman/gradle-cobertura-plugin/issues/89 has a test case that reproduces the problem.

Comment by Abel Salgado Romero [ 27/Oct/15 ]

I found a workarround for this issue. The idea behind it is using the features that Gradle provides to add the Gradle libraries from the distribution folder.
If you do so, you can just filter the libraries you don't want to add with a normal exclusion.

In order to apply the solution, add this script in the "buildSrc" folder https://github.com/jruby-gradle/jruby-gradle-plugin/blob/master/buildSrc/src/main/groovy/GradleDist.groovy.
Then, setup the proper exclusions you need in the build script (https://github.com/jruby-gradle/jruby-gradle-plugin/blob/master/build.gradle#L39) or in the previous script itself.

Generated at Wed Jun 30 12:02:32 CDT 2021 using Jira 8.4.2#804003-sha1:d21414fc212e3af190e92c2d2ac41299b89402cf.