How to force a dependency version while also substituting a transitive dependency?

@CedricChampeau Moving https://github.com/gradle/gradle/issues/5190 to forums.

My gradle project depends on third-party packages that transitively depend on various implementations of JSR-305 annotations:

  • com.google.code.findbugs:jsr305
  • net.jcip:jcip-annotations
  • com.github.stephenc.jcip:jcip-annotations
  • com.google.code.findbugs:annotations

I’d like to avoid duplicate classes at runtime, so I’m keeping just the last one and substituting the others.

I also need to make sure that certain dependencies or groups of dependencies use some exact versions:

  • io.grpc:grpc-core, io.grpc:grpc-netty, io.grpc:grpc-auth, etc all need to be 1.11.0
  • com.fasterxml.jackson.core:jackson-databind, com.fasterxml.jackson.datatype:jackson-datatype-joda, com.fasterxml.jackson.core:jackson-core, etc all need to be 2.8.10
  • other groups of packages
  • junit:junit needs to be 4.12, javax.ws.rs:javax.ws.rs-api needs to be 2.0.1, etc

To ensure I compile against those versions and end up with them at runtime as well, I’m using force directives.

Here’s what my resolutionStrategy ends up looking like:

allprojects {
  configurations.all {
    resolutionStrategy {
      preferProjectModules()
      dependencySubstitution {
        // clashes with com.google.code.findbugs:annotations which includes jsr305 and more
        substitute(module("com.google.code.findbugs:jsr305")).with(module("com.google.code.findbugs:annotations:3.0.1"))
        substitute(module("net.jcip:jcip-annotations")).with(module("com.google.code.findbugs:annotations:3.0.1"))
        substitute(module("com.github.stephenc.jcip:jcip-annotations")).with(module("com.google.code.findbugs:annotations:3.0.1"))
       ...
     }

     force("io.grpc:grpc-core:1.11.0")
     force("io.grpc:grpc-netty:1.11.0")
     ... // and so on for a bunch of packages

Expected Behavior

I’d expect packages that I asked to be substituted like “com.google.code.findbugs:jsr305” to actually end up substituted.

Current Behavior

The dependency graph shows them sticking around. For example:

|    |    |    |    +--- com.twitter:util-cache_2.12:18.3.0
|    |    |    |    |    +--- org.scala-lang:scala-library:2.12.4 -> 2.12.5
|    |    |    |    |    +--- com.twitter:util-core_2.12:18.3.0 (*)
|    |    |    |    |    +--- com.github.ben-manes.caffeine:caffeine:2.3.4 -> 2.6.2
|    |    |    |    |    \--- com.google.code.findbugs:jsr305:2.0.1 -> 3.0.2

Context

Consequently I’m having a difficult time locking down my dependencies while at the same time applying necessary substitutions.

Is there a way to apply both force and substitute without one disabling the other?

1 Like