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()
)
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
@@ -48,7 +48,7 @@ internal fun customizeKotlinDependencies(project: Project) {
)
if (propertiesProvider.stdlibJdkVariantsSubstitution) {
project.configurations.configureStdlibSubstitution(project.dependencies)
project.configurations.configureStdlibSubstitution()
}
excludeStdlibAndKotlinTestCommonFromPlatformCompilations(project)
@@ -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<ExternalDependency>()
@@ -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<String>