From 57578b569e150c9076b9780f6f7eff2e733ded5d Mon Sep 17 00:00:00 2001 From: Yahor Berdnikau Date: Thu, 20 Oct 2022 14:38:22 +0200 Subject: [PATCH] Substitute stdlib-jdk7{,8} dependencies with stdlib Only when dependencies contain kotlin-stdlib-1.8+ dependency. Such substitution could be disabled via 'gradle.properties' by adding 'kotlin .stdlib.jdk.variants.substitution=false'. ^KT-54136 Fixed --- .../gradle/KotlinSpecificDependenciesIT.kt | 97 +++++++++++++++++++ .../testProject/AndroidSimpleApp/build.gradle | 32 ++++++ .../src/androidTest/kotlin/Main.kt | 10 ++ .../src/main/AndroidManifest.xml | 6 ++ .../src/main/kotlin/com/example/AppDummy.kt | 8 ++ .../main/kotlin/com/example/KotlinActivity.kt | 10 ++ .../AndroidSimpleApp/src/test/kotlin/Main.kt | 10 ++ .../src/test/kotlin/com/example/UnitTest.kt | 15 +++ .../internal/KotlinDependenciesManagement.kt | 4 + .../internal/stdlibDependencyManagement.kt | 40 ++++++++ .../gradle/plugin/PropertiesProvider.kt | 6 +- 11 files changed, 237 insertions(+), 1 deletion(-) create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidSimpleApp/build.gradle create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidSimpleApp/src/androidTest/kotlin/Main.kt create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidSimpleApp/src/main/AndroidManifest.xml create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidSimpleApp/src/main/kotlin/com/example/AppDummy.kt create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidSimpleApp/src/main/kotlin/com/example/KotlinActivity.kt create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidSimpleApp/src/test/kotlin/Main.kt create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidSimpleApp/src/test/kotlin/com/example/UnitTest.kt 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 7c6d522adf5..a3facb0e633 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 @@ -146,6 +146,103 @@ class KotlinSpecificDependenciesIT : KGPBaseTest() { } } + @AndroidGradlePluginTests + @DisplayName("stdlib-jdk7, stdlib-jdk8 are substituted with stdlib:1.8+") + @GradleAndroidTest + fun testStdlibSubstitutionAndroid( + gradleVersion: GradleVersion, + agpVersion: String, + jdkVersion: JdkVersions.ProvidedJdk + ) { + project( + "AndroidSimpleApp", + gradleVersion, + buildOptions = defaultBuildOptions.copy( + androidVersion = agpVersion + ), + buildJdk = jdkVersion.location, + ) { + // Adding dependency that pulls transitively older versions + // of stdlib-jdk8 + buildGradle.appendText( + """ + | + |dependencies { + | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2" + |} + """.trimMargin() + ) + + build("checkDebugDuplicateClasses") + } + } + + @AndroidGradlePluginTests + @DisplayName("stdlib-jdk7, stdlib-jdk8 substitution with stdlib:1.8+ is possible to disable") + @GradleAndroidTest + fun testDisableStdlibSubstitutionAndroid( + gradleVersion: GradleVersion, + agpVersion: String, + jdkVersion: JdkVersions.ProvidedJdk + ) { + project( + "AndroidSimpleApp", + gradleVersion, + buildOptions = defaultBuildOptions.copy( + androidVersion = agpVersion + ), + buildJdk = jdkVersion.location, + ) { + // Adding dependency that pulls transitively older versions + // of stdlib-jdk8 + buildGradle.appendText( + """ + | + |dependencies { + | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2" + |} + """.trimMargin() + ) + + gradleProperties.appendText( + """ + | + |kotlin.stdlib.jdk.variants.substitution=false + """.trimMargin() + ) + + buildAndFail("checkDebugDuplicateClasses", forceOutput = true) { + assertOutputContains("Duplicate class kotlin.internal.jdk8.JDK8PlatformImplementations") + } + } + } + + @JvmGradlePluginTests + @DisplayName("stdlib-jdk7, stdlib-jdk8 substitution with stdlib:1.8+ in Kotlin DSL") + @GradleTest + fun stdlibJdkVariantsSubstitutionKotlinDsl(gradleVersion: GradleVersion) { + project("sourceSetsKotlinDsl", gradleVersion) { + removeDependencies(buildGradleKts) + + buildGradleKts.appendText( + """ + | + |dependencies { + | implementation(kotlin("stdlib")) + | implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2") + |} + """.trimMargin() + ) + + checkTaskCompileClasspath( + "compileKotlin", + listOf("kotlin-stdlib"), + checkModulesNotInClasspath = listOf("kotlin-stdlib-jdk7", "kotlin-stdlib-jdk8"), + isBuildGradleKts = true + ) + } + } + @MppGradlePluginTests @DisplayName("MPP: kotlin-stdlib is added by default") @GradleTest diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidSimpleApp/build.gradle b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidSimpleApp/build.gradle new file mode 100644 index 00000000000..32c3c5a1a39 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidSimpleApp/build.gradle @@ -0,0 +1,32 @@ +plugins { + id "com.android.application" + id "org.jetbrains.kotlin.android" +} + +repositories { + mavenLocal() + mavenCentral() +} + +android { + compileSdkVersion 33 + + defaultConfig { + applicationId "com.example" + minSdkVersion 24 + targetSdkVersion 33 + versionCode 1 + versionName "1.0" + } + + // Needed for older AGP. It can be removed when the lowest supported AGP version is 4.2.0 or newer. + compileOptions { + sourceCompatibility 1.8 + targetCompatibility 1.8 + } +} + +dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" + testImplementation "junit:junit:4.13.2" +} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidSimpleApp/src/androidTest/kotlin/Main.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidSimpleApp/src/androidTest/kotlin/Main.kt new file mode 100644 index 00000000000..da7228576f7 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidSimpleApp/src/androidTest/kotlin/Main.kt @@ -0,0 +1,10 @@ +import com.example.AppDummy + +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +fun main(args: Array) { + println(AppDummy()) +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidSimpleApp/src/main/AndroidManifest.xml b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidSimpleApp/src/main/AndroidManifest.xml new file mode 100644 index 00000000000..d193b52ecd8 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidSimpleApp/src/main/AndroidManifest.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidSimpleApp/src/main/kotlin/com/example/AppDummy.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidSimpleApp/src/main/kotlin/com/example/AppDummy.kt new file mode 100644 index 00000000000..e67916da9aa --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidSimpleApp/src/main/kotlin/com/example/AppDummy.kt @@ -0,0 +1,8 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package com.example + +class AppDummy \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidSimpleApp/src/main/kotlin/com/example/KotlinActivity.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidSimpleApp/src/main/kotlin/com/example/KotlinActivity.kt new file mode 100644 index 00000000000..cf1bc3270ac --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidSimpleApp/src/main/kotlin/com/example/KotlinActivity.kt @@ -0,0 +1,10 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package com.example + +import android.app.Activity + +open class KotlinActivity : Activity() \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidSimpleApp/src/test/kotlin/Main.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidSimpleApp/src/test/kotlin/Main.kt new file mode 100644 index 00000000000..da7228576f7 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidSimpleApp/src/test/kotlin/Main.kt @@ -0,0 +1,10 @@ +import com.example.AppDummy + +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +fun main(args: Array) { + println(AppDummy()) +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidSimpleApp/src/test/kotlin/com/example/UnitTest.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidSimpleApp/src/test/kotlin/com/example/UnitTest.kt new file mode 100644 index 00000000000..22621d46ce8 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidSimpleApp/src/test/kotlin/com/example/UnitTest.kt @@ -0,0 +1,15 @@ +package com.example + +import org.junit.Test +import org.junit.Assert.* +/* + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +class UnitTest { + @Test + fun isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file 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 d218c1a9710..a4f31bd1180 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 @@ -41,6 +41,10 @@ internal fun customizeKotlinDependencies(project: Project) { coreLibrariesVersion ) + if (propertiesProvider.stdlibJdkVariantsSubstitution) { + project.configurations.configureStdlibSubstitution(project.dependencies) + } + 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 160f3b1f5f0..ea2bd4eee93 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 @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.gradle.internal import org.gradle.api.Project import org.gradle.api.artifacts.ConfigurationContainer +import org.gradle.api.artifacts.ExternalDependency import org.gradle.api.artifacts.dsl.DependencyHandler import org.gradle.api.provider.Provider import org.jetbrains.kotlin.gradle.dsl.KotlinJsProjectExtension @@ -28,6 +29,8 @@ import org.jetbrains.kotlin.gradle.plugin.sources.android.AndroidVariantType import org.jetbrains.kotlin.gradle.plugin.sources.android.androidSourceSetInfoOrNull import org.jetbrains.kotlin.gradle.plugin.sources.dependsOnClosure import org.jetbrains.kotlin.gradle.plugin.sources.sourceSetDependencyConfigurationByScope +import org.jetbrains.kotlin.gradle.targets.js.npm.SemVer +import org.jetbrains.kotlin.gradle.utils.withType internal fun Project.configureStdlibDefaultDependency( topLevelExtension: KotlinTopLevelExtension, @@ -51,6 +54,41 @@ 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 -> + configuration.withDependencies { dependencySet -> + dependencySet + .withType() + .configureEach { dependency -> + if (dependency.group == KOTLIN_MODULE_GROUP && + dependency.name == "kotlin-stdlib" && + 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" + ) + } + } + } + } + } +} + private fun addStdlibToKpmProject( project: Project, coreLibrariesVersion: Provider @@ -170,6 +208,8 @@ private fun KotlinPlatformType.stdlibPlatformType( private val androidTestVariants = setOf(AndroidVariantType.UnitTest, AndroidVariantType.InstrumentedTest) +private val kotlin180Version = SemVer(1.toBigInteger(), 8.toBigInteger(), 0.toBigInteger()) + private fun KotlinSourceSet.isRelatedToAndroidTestSourceSet(): Boolean { val androidVariant = androidSourceSetInfoOrNull?.androidVariantType ?: return false return androidVariant in androidTestVariants diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/PropertiesProvider.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/PropertiesProvider.kt index 5f32466bafd..ffe708c667c 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/PropertiesProvider.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/PropertiesProvider.kt @@ -24,7 +24,7 @@ import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLI import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_MPP_HIERARCHICAL_STRUCTURE_SUPPORT import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_NATIVE_DEPENDENCY_PROPAGATION import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_STDLIB_DEFAULT_DEPENDENCY -import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinMultiplatformPlugin +import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_STDLIB_JDK_VARIANTS_SUBSTITUTION import org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsService import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinIrJsGeneratedTSValidationStrategy import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrOutputGranularity @@ -407,6 +407,9 @@ internal class PropertiesProvider private constructor(private val project: Proje val stdlibDefaultDependency: Boolean get() = booleanProperty(KOTLIN_STDLIB_DEFAULT_DEPENDENCY) ?: true + val stdlibJdkVariantsSubstitution: Boolean + get() = booleanProperty(KOTLIN_STDLIB_JDK_VARIANTS_SUBSTITUTION) ?: true + val kotlinTestInferJvmVariant: Boolean get() = booleanProperty("kotlin.test.infer.jvm.variant") ?: true @@ -479,6 +482,7 @@ internal class PropertiesProvider private constructor(private val project: Proje object PropertyNames { const val KOTLIN_STDLIB_DEFAULT_DEPENDENCY = "kotlin.stdlib.default.dependency" + const val KOTLIN_STDLIB_JDK_VARIANTS_SUBSTITUTION = "kotlin.stdlib.jdk.variants.substitution" const val KOTLIN_MPP_ENABLE_GRANULAR_SOURCE_SETS_METADATA = "kotlin.mpp.enableGranularSourceSetsMetadata" const val KOTLIN_MPP_ENABLE_CINTEROP_COMMONIZATION = "kotlin.mpp.enableCInteropCommonization" const val KOTLIN_MPP_HIERARCHICAL_STRUCTURE_BY_DEFAULT = "kotlin.internal.mpp.hierarchicalStructureByDefault"