Fix transitive dependencies overriding default Kotlin version (KT-28820)

In Gradle 5.0, the 'prefer' function semantics has changed, and now a
transitive dependency version wins over the 'preferred' one. Instead,
'require' has been introduced in 5.0 with the old semantics.

Our users expect that the default Kotlin dependency version will be
at least as new as the plugin version, so we now need to use 'require'
with Gradle 5.0+

Issue KT-28820 Fixed
This commit is contained in:
Sergey Igushkin
2019-01-10 22:35:45 +03:00
parent f9c45c30a2
commit 82f13b90e6
4 changed files with 31 additions and 3 deletions
@@ -862,6 +862,20 @@ class KotlinGradleIT : BaseGradleIT() {
}
}
@Test
fun testDefaultKotlinVersionIsNotAffectedByTransitiveDependencies() =
with(Project("simpleProject", GradleVersionRequired.AtLeast("4.4"))) {
setupWorkingDir()
// Add a dependency with an explicit lower Kotlin version that has a kotlin-stdlib transitive dependency:
gradleBuildScript().appendText("\ndependencies { compile 'org.jetbrains.kotlin:kotlin-reflect:1.2.71' }")
testResolveAllConfigurations {
assertSuccessful()
assertContains(">> :compile --> kotlin-reflect-1.2.71.jar")
// Check that the default newer Kotlin version still wins for 'kotlin-stdlib':
assertContains(">> :compile --> kotlin-stdlib-${defaultBuildOptions().kotlinVersion}.jar")
}
}
@Test
fun testNoTaskConfigurationForcing() {
val gradleVersionRequirement = GradleVersionRequired.AtLeast("4.9")
@@ -73,7 +73,10 @@ class VariantAwareDependenciesIT : BaseGradleIT() {
val outerProject = Project("sample-lib", gradleVersion, "new-mpp-lib-and-app")
val innerProject = Project("simpleProject").apply {
setupWorkingDir()
gradleBuildScript().modify { it.replace("apply plugin: \"kotlin\"", "") }
gradleBuildScript().modify {
it.replace("apply plugin: \"kotlin\"", "")
.replace("\"org.jetbrains.kotlin:kotlin-stdlib\"", "\"org.jetbrains.kotlin:kotlin-stdlib:\$kotlin_version\"")
}
}
with(outerProject) {
@@ -24,7 +24,7 @@ dependencies {
compile 'com.google.guava:guava:12.0'
deployCompile 'com.google.guava:guava:12.0'
testCompile 'org.testng:testng:6.8'
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
compile "org.jetbrains.kotlin:kotlin-stdlib"
}
test {
@@ -6,6 +6,7 @@ import com.android.builder.model.SourceProvider
import groovy.lang.Closure
import org.gradle.api.*
import org.gradle.api.artifacts.ExternalDependency
import org.gradle.api.artifacts.MutableVersionConstraint
import org.gradle.api.attributes.Usage
import org.gradle.api.file.FileCollection
import org.gradle.api.file.SourceDirectorySet
@@ -502,11 +503,21 @@ internal abstract class AbstractKotlinPlugin(
internal fun configureDefaultVersionsResolutionStrategy(project: Project, kotlinPluginVersion: String) {
project.configurations.all { configuration ->
if (isGradleVersionAtLeast(4, 4)) {
fun MutableVersionConstraint.chooseVersion(version: String) {
if (isGradleVersionAtLeast(5, 0)) {
// In Gradle 5.0, the semantics of 'prefer' has changed to be much less imperative, and now it's 'require' that we need:
val requireMethod = javaClass.getMethod("require", String::class.java)
requireMethod(this, version)
} else {
prefer(version)
}
}
// Use the API introduced in Gradle 4.4 to modify the dependencies directly before they are resolved:
configuration.withDependencies { dependencySet ->
dependencySet.filterIsInstance<ExternalDependency>()
.filter { it.group == "org.jetbrains.kotlin" && it.version.isNullOrEmpty() }
.forEach { it.version { constraint -> constraint.prefer(kotlinPluginVersion) } }
.forEach { it.version { constraint -> constraint.chooseVersion(kotlinPluginVersion) } }
}
} else {
configuration.resolutionStrategy.eachDependency { details ->