Why do transient dependencies of my test libraries downgrade my kotlin library depenendency?

this is my very simple gradle file:


val junit5Version = "5.3.2"
val junitPlatformVersion = "1.3.2"

plugins {
    java
    kotlin("jvm") version "1.3.11"
}

group = "group"
version = "1.0-SNAPSHOT"

repositories {
    jcenter()
    mavenCentral()
}

dependencies {
    compile(kotlin("stdlib-jdk8"))
    testImplementation("io.strikt:strikt-core:0.17.1")
}

it has a dependency on kotlin 1.3.11, and a test dependency on strikt, which has a dependency on kotlin 1.3.10.

the for me totally unexpected result of this is that in my test classpath my kotlin dependency is downgraded to 1.3.10

here’s runtime:

runtime - Runtime dependencies for compilation 'main' (target  (jvm)) (deprecated, use 'runtimeOnly ' instead).
\--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.11

and here is testCompileClasspath:

testCompileClasspath - Compile classpath for compilation 'test' (target  (jvm)).
+--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.11 -> 1.3.10
|    +--- org.jetbrains.kotlin:kotlin-stdlib:1.3.10

there is also testCompile which does not seem to be downgraded:

testCompile - Dependencies for compilation 'test' (target  (jvm)) (deprecated, use 'testImplementation ' instead).
\--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.11
     +--- org.jetbrains.kotlin:kotlin-stdlib:1.3.11
     |    +--- org.jetbrains.kotlin:kotlin-stdlib-common:1.3.11

so now I’m wondering why it acts that way, and whats the best practice here.

You should run the dependencyInsight task which can give a better idea of why the downgrade happens.

Something like: ./gradlew dependencyInsight --configuration testCompileClasspath --dependency kotlin-stdlib-jdk8

thanks for that, here’s the output:

~/Projects/mine/randolf% ./gradlew dependencyInsight --configuration testCompileClasspath --dependency kotlin-stdlib-jdk8

> Task :dependencyInsight
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.10
   variant "compile" [
      org.gradle.status                  = release (not requested)
      org.gradle.usage                   = java-api
      org.gradle.component.category      = library (not requested)

      Requested attributes not found in the selected variant:
         org.jetbrains.kotlin.platform.type = jvm
   ]

org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.10
\--- io.strikt:strikt-core:0.17.1
     \--- testCompileClasspath

org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.11 -> 1.3.10
\--- testCompileClasspath

it shows that the reason for the downgrade is like i expected. still its very unexpected, and probably not what most people want.

also i just checked the same gradle file with gradle 4.10 and there it does not happen. here’s what gradle 4.10 does:

~/Projects/mine/gradle-test% ./gradlew dependencyInsight --configuration testCompileClasspath --dependency kotlin-stdlib-jdk8

> Task :dependencyInsight
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.11
   variant "default+runtime" [
      org.gradle.status                  = release (not requested)
      Requested attributes not found in the selected variant:
         org.gradle.usage                   = java-api
         org.jetbrains.kotlin.platform.type = jvm
   ]
   Selection reasons:
      - Was requested
      - By conflict resolution : between versions 1.3.11 and 1.3.10

org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.11
\--- testCompileClasspath

org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.10 -> 1.3.11
\--- io.strikt:strikt-core:0.17.1
     \--- testCompileClasspath

and seems to be the better solution.

   Selection reasons:
      - Was requested

For which version of Gradle is this not working? Before 4.10? 5.0?

I can explain the behaviour. It comes from the Kotlin plugin that is extremely lenient with the Kotlin library versions to use.
As can be seen in the plugin code, they use a prefer constraint when they see a dependency without the version specified.

However the prefer version constraint has evolved in terms of meaning in the recent Gradle versions. It is now really meant to be used as: “If no one else has an opinion, pick this version” and is usually combined with a require or strictly version range declaration to pick a specific version in the range should no other dependency express an opinion.

In your case, as soon as another module with a version enters the dependency graph, it means the prefer gets ignored and thus you see a version downgrade.

The easiest solution is to add a version to your dependency declaration, by doing `compile(kotlin(“stdlib-jdk8”, “1.3.11”)) which effectively means your dependency has a version and it will be conflict resolved as expected.

ok thanks a lot. I will create a ticket in the kotlin youtrack instance then