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