From e15d8fc56f4a5011a373cc9a0ce470f7a2b07ba8 Mon Sep 17 00:00:00 2001 From: Yahor Berdnikau Date: Wed, 26 Oct 2022 14:32:49 +0200 Subject: [PATCH] More fine-grained substitution for stdlib-jdk7{,8} dependencies Before if one of the configurations has 'kotlin-stdlib:1.8+' dependency - all other configurations will also apply substitution. But this may break build/tools that want to use specific set of dependencies to run itself. For example, Android Lint still uses older stdlib versions in classpath. Now substituion only applies to configuration (if it is resolvable) and its resolvable children. ^KT-54653 Fixed --- .../gradle/KotlinSpecificDependenciesIT.kt | 83 ++++++++++++++++++- .../internal/KotlinDependenciesManagement.kt | 2 +- .../internal/stdlibDependencyManagement.kt | 39 +++++---- 3 files changed, 106 insertions(+), 18 deletions(-) diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/KotlinSpecificDependenciesIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/KotlinSpecificDependenciesIT.kt index a3facb0e633..43647fe1cc6 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/KotlinSpecificDependenciesIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/KotlinSpecificDependenciesIT.kt @@ -211,7 +211,7 @@ class KotlinSpecificDependenciesIT : KGPBaseTest() { """.trimMargin() ) - buildAndFail("checkDebugDuplicateClasses", forceOutput = true) { + buildAndFail("checkDebugDuplicateClasses") { assertOutputContains("Duplicate class kotlin.internal.jdk8.JDK8PlatformImplementations") } } @@ -243,6 +243,87 @@ class KotlinSpecificDependenciesIT : KGPBaseTest() { } } + @JvmGradlePluginTests + @DisplayName("KT-54653: stdlib-jdk7, jdk8 substitution should not work for unrelated configuration") + @GradleTest + fun stdlibJdkVariantsSubstitutionOnlyConfiguration(gradleVersion: GradleVersion) { + project("simpleProject", gradleVersion) { + buildGradle.modify { + """ + |$it + | + |// Stdlib-jdk8 version should be <1.8.0 + |configurations.create("specificDeps") + | + |dependencies { + | specificDeps "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.20" + | implementation "org.jetbrains.kotlin:kotlin-stdlib:${buildOptions.kotlinVersion}" + |} + """.trimMargin() + } + + build("dependencies") { + assertOutputDoesNotContain( + """ + | + |specificDeps + |\--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.20 -> org.jetbrains.kotlin:kotlin-stdlib:1.7.20 + | +--- org.jetbrains.kotlin:kotlin-stdlib-common:1.7.20 + | \--- org.jetbrains:annotations:13.0 + """.trimMargin() + ) + } + } + } + + @JvmGradlePluginTests + @DisplayName("KT-54653: stdlib-jdk7, jdk8 substitution works for complex resolvable configurations hierarchy") + @GradleTest + fun stdlibJdkVariantSubstitutionComplexResolvableConfiguration(gradleVersion: GradleVersion) { + project("simpleProject", gradleVersion) { + buildGradle.modify { + """ + |$it + | + |// Stdlib-jdk8 version should be <1.8.0 + |def specificDepsConf = configurations.create("specificDeps") { + | setCanBeResolved(true) + |} + | + |configurations.create("specificDepsChild") { + | setCanBeResolved(true) + | extendsFrom specificDepsConf + |} + | + |dependencies { + | specificDeps "org.jetbrains.kotlin:kotlin-stdlib:${buildOptions.kotlinVersion}" + | // brings transitevly older stdlib-jdk7,8 dependencies + | specificDepsChild "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2" + |} + """.trimMargin() + } + + build("dependencies") { + assertOutputDoesNotContain( + """ + | + |specificDepsChild + |+--- org.jetbrains.kotlin:kotlin-stdlib:${buildOptions.kotlinVersion} + || +--- org.jetbrains.kotlin:kotlin-stdlib-common:${buildOptions.kotlinVersion} + || \--- org.jetbrains:annotations:13.0 + |\--- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2 + | \--- org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.5.2 + | +--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.5.30 + | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.5.30 -> ${buildOptions.kotlinVersion} (*) + | | \--- org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.5.30 + | | \--- org.jetbrains.kotlin:kotlin-stdlib:1.5.30 -> ${buildOptions.kotlinVersion} (*) + | \--- org.jetbrains.kotlin:kotlin-stdlib-common:1.5.30 -> ${buildOptions.kotlinVersion} + """.trimMargin() + ) + } + } + } + @MppGradlePluginTests @DisplayName("MPP: kotlin-stdlib is added by default") @GradleTest diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/internal/KotlinDependenciesManagement.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/internal/KotlinDependenciesManagement.kt index 2d1b7dd1635..b0b1677348c 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/internal/KotlinDependenciesManagement.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/internal/KotlinDependenciesManagement.kt @@ -48,7 +48,7 @@ internal fun customizeKotlinDependencies(project: Project) { ) if (propertiesProvider.stdlibJdkVariantsSubstitution) { - project.configurations.configureStdlibSubstitution(project.dependencies) + project.configurations.configureStdlibSubstitution() } excludeStdlibAndKotlinTestCommonFromPlatformCompilations(project) diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/internal/stdlibDependencyManagement.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/internal/stdlibDependencyManagement.kt index dced67e4665..fd3b90f3f47 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/internal/stdlibDependencyManagement.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/internal/stdlibDependencyManagement.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.gradle.internal import org.gradle.api.Project +import org.gradle.api.artifacts.Configuration import org.gradle.api.artifacts.ConfigurationContainer import org.gradle.api.artifacts.ExternalDependency import org.gradle.api.artifacts.dsl.DependencyHandler @@ -53,9 +54,7 @@ internal fun Project.configureStdlibDefaultDependency( * Replacing kotlin-stdlib-jdk8 and kotlin-stdlib-jdk7 artifacts with kotlin-stdlib * when project stdlib version is >= 1.8.0 */ -internal fun ConfigurationContainer.configureStdlibSubstitution( - dependencies: DependencyHandler -) = all { configuration -> +internal fun ConfigurationContainer.configureStdlibSubstitution() = all { configuration -> configuration.withDependencies { dependencySet -> dependencySet .withType() @@ -65,25 +64,33 @@ internal fun ConfigurationContainer.configureStdlibSubstitution( dependency.version != null && SemVer.from(dependency.version!!) >= kotlin180Version ) { - dependencies.modules { componentModuleMetadataHandler -> - componentModuleMetadataHandler.module("org.jetbrains.kotlin:kotlin-stdlib-jdk7") { - it.replacedBy( - "org.jetbrains.kotlin:kotlin-stdlib", - "kotlin-stdlib-jdk7 is now part of kotlin-stdlib" - ) - } - componentModuleMetadataHandler.module("org.jetbrains.kotlin:kotlin-stdlib-jdk8") { - it.replacedBy( - "org.jetbrains.kotlin:kotlin-stdlib", - "kotlin-stdlib-jdk8 is now part of kotlin-stdlib" - ) - } + if (configuration.isCanBeResolved) configuration.substitudeStdlibJvmVariants(dependency) + + // dependency substitution only works for resolvable configuration, + // so we need to find all configuration that extends current one + filter { + it.isCanBeResolved && it.hierarchy.contains(configuration) + }.forEach { + it.substitudeStdlibJvmVariants(dependency) } } } } } +private fun Configuration.substitudeStdlibJvmVariants( + kotlinStdlibDependency: ExternalDependency +) { + resolutionStrategy.dependencySubstitution { + it.substitute(it.module("org.jetbrains.kotlin:kotlin-stdlib-jdk7")) + .using(it.module("org.jetbrains.kotlin:kotlin-stdlib:${kotlinStdlibDependency.version}")) + .because("kotlin-stdlib-jdk7 is now part of kotlin-stdlib") + it.substitute(it.module("org.jetbrains.kotlin:kotlin-stdlib-jdk8")) + .using(it.module("org.jetbrains.kotlin:kotlin-stdlib:${kotlinStdlibDependency.version}")) + .because("kotlin-stdlib-jdk8 is now part of kotlin-stdlib") + } +} + private fun addStdlibToKpmProject( project: Project, coreLibrariesVersion: Provider