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
This commit is contained in:
Yahor Berdnikau
2022-10-26 14:32:49 +02:00
committed by Space Team
parent b429e2f34f
commit e15d8fc56f
3 changed files with 106 additions and 18 deletions
@@ -211,7 +211,7 @@ class KotlinSpecificDependenciesIT : KGPBaseTest() {
""".trimMargin() """.trimMargin()
) )
buildAndFail("checkDebugDuplicateClasses", forceOutput = true) { buildAndFail("checkDebugDuplicateClasses") {
assertOutputContains("Duplicate class kotlin.internal.jdk8.JDK8PlatformImplementations") 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 @MppGradlePluginTests
@DisplayName("MPP: kotlin-stdlib is added by default") @DisplayName("MPP: kotlin-stdlib is added by default")
@GradleTest @GradleTest
@@ -48,7 +48,7 @@ internal fun customizeKotlinDependencies(project: Project) {
) )
if (propertiesProvider.stdlibJdkVariantsSubstitution) { if (propertiesProvider.stdlibJdkVariantsSubstitution) {
project.configurations.configureStdlibSubstitution(project.dependencies) project.configurations.configureStdlibSubstitution()
} }
excludeStdlibAndKotlinTestCommonFromPlatformCompilations(project) excludeStdlibAndKotlinTestCommonFromPlatformCompilations(project)
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.gradle.internal package org.jetbrains.kotlin.gradle.internal
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.ConfigurationContainer import org.gradle.api.artifacts.ConfigurationContainer
import org.gradle.api.artifacts.ExternalDependency import org.gradle.api.artifacts.ExternalDependency
import org.gradle.api.artifacts.dsl.DependencyHandler 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 * Replacing kotlin-stdlib-jdk8 and kotlin-stdlib-jdk7 artifacts with kotlin-stdlib
* when project stdlib version is >= 1.8.0 * when project stdlib version is >= 1.8.0
*/ */
internal fun ConfigurationContainer.configureStdlibSubstitution( internal fun ConfigurationContainer.configureStdlibSubstitution() = all { configuration ->
dependencies: DependencyHandler
) = all { configuration ->
configuration.withDependencies { dependencySet -> configuration.withDependencies { dependencySet ->
dependencySet dependencySet
.withType<ExternalDependency>() .withType<ExternalDependency>()
@@ -65,25 +64,33 @@ internal fun ConfigurationContainer.configureStdlibSubstitution(
dependency.version != null && dependency.version != null &&
SemVer.from(dependency.version!!) >= kotlin180Version SemVer.from(dependency.version!!) >= kotlin180Version
) { ) {
dependencies.modules { componentModuleMetadataHandler -> if (configuration.isCanBeResolved) configuration.substitudeStdlibJvmVariants(dependency)
componentModuleMetadataHandler.module("org.jetbrains.kotlin:kotlin-stdlib-jdk7") {
it.replacedBy( // dependency substitution only works for resolvable configuration,
"org.jetbrains.kotlin:kotlin-stdlib", // so we need to find all configuration that extends current one
"kotlin-stdlib-jdk7 is now part of kotlin-stdlib" filter {
) it.isCanBeResolved && it.hierarchy.contains(configuration)
} }.forEach {
componentModuleMetadataHandler.module("org.jetbrains.kotlin:kotlin-stdlib-jdk8") { it.substitudeStdlibJvmVariants(dependency)
it.replacedBy(
"org.jetbrains.kotlin:kotlin-stdlib",
"kotlin-stdlib-jdk8 is now part of kotlin-stdlib"
)
}
} }
} }
} }
} }
} }
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( private fun addStdlibToKpmProject(
project: Project, project: Project,
coreLibrariesVersion: Provider<String> coreLibrariesVersion: Provider<String>