From 82f13b90e676bfe9d36e2a2ba086c20ea93ed9a3 Mon Sep 17 00:00:00 2001 From: Sergey Igushkin Date: Thu, 10 Jan 2019 22:35:45 +0300 Subject: [PATCH] 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 --- .../kotlin/gradle/KotlinGradlePluginIT.kt | 14 ++++++++++++++ .../kotlin/gradle/VariantAwareDependenciesIT.kt | 5 ++++- .../testProject/simpleProject/build.gradle | 2 +- .../jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt | 13 ++++++++++++- 4 files changed, 31 insertions(+), 3 deletions(-) diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/KotlinGradlePluginIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/KotlinGradlePluginIT.kt index 3d87b134d1f..7c35c6e2fc2 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/KotlinGradlePluginIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/KotlinGradlePluginIT.kt @@ -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") diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/VariantAwareDependenciesIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/VariantAwareDependenciesIT.kt index dde4fef46d9..64076a2c43f 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/VariantAwareDependenciesIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/VariantAwareDependenciesIT.kt @@ -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) { diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/simpleProject/build.gradle b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/simpleProject/build.gradle index ae40bfbe6ae..792377e6c29 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/simpleProject/build.gradle +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/simpleProject/build.gradle @@ -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 { diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt index cabe474ffb2..7ecfc58f003 100755 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt @@ -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() .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 ->