Kotlin Dependencies in Gradle Projects


#1

Hello Kotlin Forum,

first off, I love Kotlin thank you Jetbrains!

So I keep coming across this problem of dependencies in my project are polluting dependencies with their dependencies on different (usually older) kotlin stdlib etc and so on versions. I realize that once the new Gradle implementation scope has caught on this will stop to be a problem but in the meantime I was wondering if the correct and cannonical solution is to simply block all transitive kotlin dependencies like this:

configurations {
implementation {
exclude group: ‘org.jetbrains.kotlin’ , module: ‘kotlin-std-lib’
exclude group: ‘org.jetbrains.kotlin’ , module: ‘kotlin-std-lib-common’
exclude group: ‘org.jetbrains’ , module: ‘annotations’
}
}

I think it would help if Intellij (I love my new Ultimate Edition btw!) would generate this into the gradle file when you start a kotlin/gradle project. I guess with the coroutines being in a different dependency still this problem will get worse before it gets better.

Thanks and kind Regards,
Wulf


#2

If you block the transitive dependencies you will likely have problems when running your application or testing your library.

Why do you think these dependencies are polluting? Could you describe a problem they cause in more detail?


#3

I think these dependencies are polluting because Intellij is telling me that I have different versions of these liberaries on the class path and that this could be causing problem. If the excellent IDE made by the creators of kotlin warns me about something, I really take it seriously :slightly_smiling_face:


#4

Ive been having the same problem, took me several days to fix 5 builds.
Some causes of getting old kotlin libs came from public libs that have not been updated, like jackson-kotlin-module which was referencing kotlin 1.2

Whats working for me right now is a combination of heavy hammers, also used to solve some related but different issues with gradle 5.0 updates.

  • First remove all references to all kotlin libraries except the jvm – add them back only if needed.
  • Delete all caches of everything everywhere
  • move dependencies from custom configurations to configuration extensions.

I.e change:

configuration {
   custom
}

dependencies {
    custom  "module:vers"
...
}

a child project …

    dependencies {
        compile  configurations.custom   // used to work
    }

to

child project

configurations  {
   implementation.extendsFrom custom
}

Force kotlin version alignment
root project

ext {
  kotlinVersion = "1.3.10"
}
plugins {
  id "org.jetbrains.kotlin.jvm" version "1.3.10"
}
allprojects {
  apply plugin: 'java'
  configurations.forEach { config ->
    config.resolutionStrategy.eachDependency { DependencyResolveDetails details ->
      if (details.requested.group == "org.jetbrains.kotlin"
          && details.requested.name.startsWith("kotlin-")) {
        println("forcing $details to $kotlinVersion")
        details.useVersion kotlinVersion
      }
    }
  }

Then for extra points I had to do this due to various libraries pulling in different jackson modules
The same trick should work for kotlin library transitive dependencies
// HACK

  configurations.all {
    resolutionStrategy {
      preferProjectModules()
      force 'com.fasterxml.jackson.core:jackson-core:2.9.7',
          'com.fasterxml.jackson.core:jackson-databind:2.9.7'
      dependencySubstitution {
        substitute module('com.fasterxml.jackson.core:jackson-core:2.6.6') with module('com.fasterxml.jackson.core:jacks
on-core:2.9.7')
        substitute module('com.fasterxml.jackson.core:jackson-databind:2.6.6') with module('com.fasterxml.jackson.core:j
ackson-databind:2.9.7')
        substitute module('com.fasterxml.jackson.dataformat:jackson-dataformat-cbor:2.6.6') with module('com.fasterxml.j
ackson.dataformat:jackson-dataformat-cbor:2.9.7')
      }
    }
  }

I dont fully understand the difference between the last 2, i.e. is there a difference between substituting a module with ‘substitute’ vs changing the resolution details object ? don’t know – worked – eventually.


#5

Gradle has a mechanism of dependency version conflict resolution: when two dependencies require different versions of the third dependency, the higher one is selected.

This should work fine with the Kotlin standard library dependency, it’s ok to substitute 1.2.x with a more recent 1.3.x version.

@dlee, @wulf0r, Could you investigate why this mechanism doesn’t give a satisfactory result in your case?


#6

In my case the default behaviour did NOT prevent a transitive dependency on kotlin 1.2 when I upgraded to kotlin 1.3 , I had to force the above resolutionStrategy to do so.
I really dont know why, or how to determine why exactly, gradles inner workings are still a mystery to me after 3 years … .
I know ONE of the culprits was the jackson-kotlin-module which compiled against kotlin 1.2
when I did a “gradle dependencies” that showed up, and adding the above removed that.
Another was referencing an old kotinx.coroutines library in a transitive dependency.