unify
unify

Reputation: 6351

Gradle Transitive dependency exclusion is not working as expected. (How do I get rid of com.google.guava:guava-jdk5:13.0 ?)

here is a snippet of my build.gradle:

compile 'com.google.api-client:google-api-client:1.19.0'
compile 'com.google.apis:google-api-services-oauth2:v2-rev77-1.19.0'
compile 'com.google.apis:google-api-services-plus:v1-rev155-1.19.0'
compile 'com.google.appengine.tools:appengine-gcs-client:0.4.1'
compile 'com.google.appengine.tools:appengine-mapreduce:0.8'

which imports multiple version of guava as you can see with dependencyInsight:

com.google.guava:guava:15.0 (conflict resolution)

com.google.guava:guava:14.0.1 -> 15.0
+--- com.googlecode.objectify:objectify:4.1.3
|    \--- default
\--- net.eusashead.spring:spring-cache-gae:1.0.0.RELEASE
     \--- default

com.google.guava:guava:[15.0,15.99] -> 15.0
+--- com.google.appengine.tools:appengine-gcs-client:0.4.1
|    +--- default
|    +--- com.google.appengine.tools:appengine-mapreduce:0.8
|    |    \--- default
|    \--- com.google.appengine.tools:appengine-pipeline:0.2.10
|         \--- com.google.appengine.tools:appengine-mapreduce:0.8 (*)
+--- com.google.appengine.tools:appengine-mapreduce:0.8 (*)
\--- com.google.appengine.tools:appengine-pipeline:0.2.10 (*)

com.google.guava:guava-jdk5:13.0
\--- com.google.api-client:google-api-client:1.19.0
     +--- default
     +--- com.google.apis:google-api-services-oauth2:v2-rev77-1.19.0
     |    \--- default
     +--- com.google.apis:google-api-services-plus:v1-rev155-1.19.0
     |    \--- default
     +--- com.google.appengine.tools:appengine-gcs-client:0.4.1
     |    +--- default
     |    +--- com.google.appengine.tools:appengine-mapreduce:0.8
     |    |    \--- default
     |    \--- com.google.appengine.tools:appengine-pipeline:0.2.10
     |         \--- com.google.appengine.tools:appengine-mapreduce:0.8 (*)
     +--- com.google.api-client:google-api-client-appengine:1.17.0-rc
     |    \--- com.google.appengine.tools:appengine-gcs-client:0.4.1 (*)
     +--- com.google.apis:google-api-services-storage:v1-rev1-1.18.0-rc
     |    \--- com.google.appengine.tools:appengine-gcs-client:0.4.1 (*)
     +--- com.google.apis:google-api-services-bigquery:v2-rev154-1.19.0
     |    \--- com.google.appengine.tools:appengine-mapreduce:0.8 (*)
     \--- com.google.api-client:google-api-client-servlet:1.17.0-rc
          \--- com.google.api-client:google-api-client-appengine:1.17.0-rc (*)

(*) - dependencies omitted (listed previously)

I have tried removing the dependency to : by doing:

compile ('com.google.api-client:google-api-client:1.19.0'){
        exclude group: 'com.google.guava', module: 'guava-jdk5'
    }
compile ('com.google.api-client:google-api-client:1.19.0'){
        exclude group: 'com.google.guava', 
    }

but the dependencyInsight remains the same. I also tried

compile ('com.google.guava:guava:15.0'){force = true}

but again dependency insight remains the same. How do I get rid of com.google.guava:guava-jdk5:13.0 ?

Details: I've tried gradle 1.2 and 2.1 on a windows 8.1 box

The reason for me to try this is to get rid of this exception:

java.lang.NoSuchMethodError: com.google.common.base.Stopwatch.createStarted()Lcom/google/common/base/Stopwatch;

Upvotes: 29

Views: 36044

Answers (5)

Ilya Serbis
Ilya Serbis

Reputation: 22273

Seems like Gradle works in such a way that every dependency brings all its transitive dependencies. And if you exclude some of them from one dependency they might been brought by the others.

So if you have

compile 'com.google.api-client:google-api-client:1.19.0'
compile 'com.google.apis:google-api-services-oauth2:v2-rev77-1.19.0'
compile 'com.google.apis:google-api-services-plus:v1-rev155-1.19.0'

in order to exclude com.google.guava:guava-jdk5 from the build process you have to exclude it from each of them:

compile ('com.google.api-client:google-api-client:1.19.0') {
    exclude group: 'com.google.guava', module: 'guava-jdk5'
}
compile ('com.google.apis:google-api-services-oauth2:v2-rev77-1.19.0') {
    exclude group: 'com.google.guava', module: 'guava-jdk5'
}
compile ('com.google.apis:google-api-services-plus:v1-rev155-1.19.0') {
    exclude group: 'com.google.guava', module: 'guava-jdk5'
}

dependencyInsight can help to find out where to put exclude. It shows a list of dependencies that have specific dependency in its transitive dependencies (unfortunately it doesn't show which of them have already excluded it in configuration)


More simple approach is to exclude a dependency from whole configuration (or from all configurations):

configurations.all {
    exclude group: 'com.google.guava', module: 'guava-jdk5'
}

Reference: https://docs.gradle.org/current/userguide/resolution_rules.html#excluding_a_dependency_from_a_configuration_completely

Upvotes: 7

Rusi Popov
Rusi Popov

Reputation: 394

I met several causes of this strange behavior of gradle dependencies:

  • it turned out that Gradle 4.10.x caches some interim results in its daemon, so just running the command might not be up to date. Thus, disable the Gradle daemon.
  • there are other tools and IDEs (Eclipse,etc.) that can run the Gradle daemon for their own integration and project build. Thus, check for other daemon processes and kill them
  • there seems to be some additional caching even with disabled daemon, therefore run gradle clean dependencies instead
  • consider setting environment variable GRADLE_OPTS=-Dorg.gradle.daemon=false -Dorg.gradle.caching=false to avoid the caching or provide these options in the command line
  • the dependencies report itself is deceptive as it may not show (neither indicate) the next occurrences of the same the unwanted dependency, reached through other paths. Then excluding the transitive the unwanted dependency from the first path does not work and still shows it on that path, no matter that it came through another (invisible) path.

Identify all paths the the unwanted dependency is reached through and exclude it from all of them.

Comment out the first path / dependency run gradle clean dependencies to find the next path the dependency is reached through. Repeat until all such paths found. Then uncomment and exclude the unwanted dependency from all paths identified.

Upvotes: 3

Jon Thoms
Jon Thoms

Reputation: 10739

Building on @thoutbeckers answer due to a special case, where I didn't think that his answer applied, but it actually did. Hopefully, this answer can help others who shared my special case problem. Originally I thought that the bad transitive dependency was only referenced by one dependency in the build.gradle file but it was actually referenced by two dependencies. This was because both dependencies where the the bad transitive dependency was referenced from had a parent/child relationship, but I only noticed the relationship with the child dependency, and not the parent dependency.

Consider the following dependency tree (produced by the command gradle <my-project-name>:dependencies):

compileClasspath - Compile classpath for source set 'main'.
+--- my.org:com.my.pkg.parent:6.+ -> 6.0.4
|    +--- # misc. dependencies
|    +--- my.org:com.my.pkg.child:6.0.4
|    |    +--- # misc. dependencies
|    |    +--- other.org:bad.transitive.dependency:0.9.1 FAILED
|    |    +--- # misc. dependencies
|    |--- # misc. dependencies
+--- # misc. dependencies

From the dependency tree, it looks like the other.org:bad.transitive:dependency:0.9.1 is only referenced by one dependency in your build file, not two. However, suppose your Gradle file looks like this:

// ... misc. ...
dependencies {
    // ... misc. dependencies ...
    compile 'my.org:com.my.pkg.parent:6.+'
    // ... misc. dependencies ...
    compile ('my.org:com.my.pkg.child:6.0.4') {
        exclude group: 'other.org', module: 'bad.transitive.dependency'
}

For a Gradle file like the one above, the error will persist even though the transitive dependency you wanted to exclude occurs only in the child dependency, not the parent dependency. However, because both the parent and child projects are referenced by the build.gradle file, the bad transitive dependency must be excluded from both dependencies, as @thoutbeckers stated above.

Note that if you don't want to add the exclusion at the configuration level (as @thoutbeckers showed in their answer), you can always just exclude the transitive dependency from both dependencies where it is referenced, explicitly.

Upvotes: 8

thoutbeckers
thoutbeckers

Reputation: 2668

It seems a dependency will not be excluded if there is another dependency somewhere that points to that same dependency without any of the excludes.

You can exclude a dependency through configuration however:

configurations {
  all*.exclude group: 'com.google.guava', module:'guava-jdk5'
}

Upvotes: 47

unify
unify

Reputation: 6351

It turns out that guava-jdk5 is still being maintained.

So I changed this:

compile ('com.google.guava:guava:15.0'){force = true}

for this:

compile('com.google.guava:guava-jdk5:17.0') { force = true }

And that fixed my issues, I can now use classes from the 'com.google.common' package in Google App Engine project with all the described dependencies

Upvotes: 5

Related Questions