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

Thanks, @CedricChampeau – that makes sense.

I’m looking forward to alignment. Do you think that will land in 4.8 as well?

The other form of alignment I was hoping for is across subprojects. For example, I might have a subproject :common:auth for authentication code, :common:concurrent for thread pools, and :foo:service for a service using both :common:auth and :common:concurrent. Now suppose they all depend on com.google.guava:guava, either directly or through transitive dependencies. If any of them use certain APIs from Guava version 20, then we have to make sure we use that version for everyone at compile time, test compile time, test runtime, and runtime (because v19 and v21 are both incompatible in different ways).

So I end up with a file buildSrc/src/main/kotlin/thirdParty/thirdParty.kt that looks like

val guavaVersion = "20.0"
val guava = "com.google.guava:guava:$guavaVersion"

and build.gradle.kts files in a subset of those projects with some variation of

dependencies {
  compile(thirdParty.guava)
}

where compile might actually be implementation, api, testCompile, runtimeClasspath, etc.

If all subprojects mention the thirdParty.guava dependency explicitly, then we know that’s the version everyone’s getting. If they don’t, we might still have a project compiling against a wrong version. So we alter that thirdParty.kt file to say:

val allDeps = mutableListOf<String>()

fun dep(s: String): String {
  allDeps.add(s)
  return s
}

val guavaVersion = "20.0"
val guava = dep("com.google.guava:guava:$guavaVersion")

// other versioned third-party definitions also using the dep function...

and add this to the root project build.gradle.kts:

// Don't manipulate versions for gradle internals: https://github.com/typesafehub/zinc/issues/87
val blacklistConfigs = setOf("zinc")

allprojects {
  configurations.all {
    if (!blacklistConfigs.contains(name)) {
        for (thirdPartyDep in thirdParty.allDeps) {
          force(thirdPartyDep)
        }
      }
    }
  }
}

So far that’s working, but I wonder if there’s a way to say in a central place “if any subproject uses Guava, directly or indirectly, in any configuration other than internal Gradle plugins, it’s version 20.0” in a simpler way.