Add kotlin-build-tools-enum-compat workaround

It acts as a workaround for the case when build tools or dependencies
are compiled with latest 'kotlin-stdlib' version, but at a runtime older
 'kotlin-stdlib' is provided, which does not know about new
 `EnumEntries`.

 ^KT-57317 Fixed
This commit is contained in:
Yahor Berdnikau
2023-04-17 11:46:09 +02:00
committed by Space Team
parent 1f649b698c
commit eb4e96a113
25 changed files with 174 additions and 128 deletions
+1
View File
@@ -277,6 +277,7 @@
/libraries/tools/binary-compatibility-validator/ "Kotlin Libraries"
/libraries/tools/dukat/ "Kotlin JS"
/libraries/kotlin-dom-api-compat/ "Kotlin JS"
/libraries/tools/kotlin-build-tools-enum-compat/ "Kotlin Build Tools"
/libraries/tools/gradle/ "Kotlin Build Tools"
/libraries/tools/kotlin-allopen/ "Kotlin Build Tools"
/libraries/tools/kotlin-annotations-jvm/ "Kotlin Libraries"
-10
View File
@@ -1,6 +1,3 @@
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion as GradleKotlinVersion
import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask
description = "Kotlin Build Common"
plugins {
@@ -50,10 +47,3 @@ projectTest(parallel = true)
projectTest("testJUnit5", jUnitMode = JUnitMode.JUnit5, parallel = true) {
useJUnitPlatform()
}
// 1.9 level breaks Kotlin Gradle plugins via changes in enums (KT-48872)
// We limit api and LV until KGP will stop using Kotlin compiler directly (KT-56574)
tasks.withType<KotlinCompilationTask<*>>().configureEach {
compilerOptions.apiVersion.value(GradleKotlinVersion.KOTLIN_1_8).finalizeValueOnRead()
compilerOptions.languageVersion.value(GradleKotlinVersion.KOTLIN_1_8).finalizeValueOnRead()
}
+1
View File
@@ -230,6 +230,7 @@ extra["compilerModules"] =
// They are embedded just because we don't publish those dependencies as separate Maven artifacts (yet)
extra["kotlinJpsPluginEmbeddedDependencies"] = listOf(
":compiler:cli-common",
":kotlin-build-tools-enum-compat",
":kotlin-compiler-runner-unshaded",
":daemon-common",
":core:compiler.common",
+23 -3
View File
@@ -13,7 +13,6 @@ import org.gradle.api.attributes.*
import org.gradle.api.attributes.java.TargetJvmEnvironment
import org.gradle.api.attributes.java.TargetJvmVersion
import org.gradle.api.attributes.plugin.GradlePluginApiVersion
import org.gradle.api.file.FileCollection
import org.gradle.api.component.AdhocComponentWithVariants
import org.gradle.api.plugins.JavaLibraryPlugin
import org.gradle.api.plugins.JavaPlugin
@@ -31,12 +30,10 @@ import org.jetbrains.dokka.gradle.DokkaTask
import org.jetbrains.dokka.gradle.GradleExternalDocumentationLinkBuilder
import org.jetbrains.kotlin.gradle.dsl.KotlinSingleJavaTargetExtension
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import plugins.configureDefaultPublishing
import plugins.configureKotlinPomAttributes
import java.net.URL
import java.util.*
/**
* Gradle's plugins common variants.
@@ -251,6 +248,7 @@ fun Project.wireGradleVariantToCommonGradleVariant(
from(wireSourceSet.output, commonSourceSet.output)
setupPublicJar(archiveBaseName.get())
addEmbeddedRuntime()
addEmbeddedRuntime(wireSourceSet.embeddedConfigurationName)
} else if (name == wireSourceSet.sourcesJarTaskName) {
from(wireSourceSet.allSource, commonSourceSet.allSource)
}
@@ -302,6 +300,15 @@ fun Project.reconfigureMainSourcesSetForGradlePlugin(
withJavadocJar()
}
}
configurations.create(sourceSets.getByName("main").embeddedConfigurationName) {
isCanBeConsumed = false
isCanBeResolved = true
attributes {
attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_RUNTIME))
attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements.JAR))
}
}
}
// Workaround for https://youtrack.jetbrains.com/issue/KT-52987
@@ -423,10 +430,20 @@ fun Project.createGradlePluginVariant(
configurations.named(variantSourceSet.apiElementsConfigurationName, commonVariantAttributes())
configurations.named(variantSourceSet.runtimeElementsConfigurationName, commonVariantAttributes())
configurations.create(variantSourceSet.embeddedConfigurationName) {
isCanBeConsumed = false
isCanBeResolved = true
attributes {
attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_RUNTIME))
attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements.JAR))
}
}
}
tasks.named<Jar>(variantSourceSet.sourcesJarTaskName) {
addEmbeddedSources()
addEmbeddedSources(variantSourceSet.embeddedConfigurationName)
}
}
@@ -513,6 +530,7 @@ fun Project.publishShadowedJar(
jarTask.flatMap { it.archiveClassifier }
)
addEmbeddedRuntime()
addEmbeddedRuntime(sourceSet.embeddedConfigurationName)
from(sourceSet.output)
from(commonSourceSet.output)
@@ -701,3 +719,5 @@ private fun GradleExternalDocumentationLinkBuilder.addWorkaroundForElementList(p
packageListUrl.set(URL("${pluginVariant.gradleApiJavadocUrl}element-list"))
}
}
private val SourceSet.embeddedConfigurationName get() = "${name}Embedded"
+6 -4
View File
@@ -73,8 +73,9 @@ fun Project.noDefaultJar() {
configurations.named("archives", removeJarTaskArtifact(jarTask))
}
fun Jar.addEmbeddedRuntime() {
project.configurations.findByName("embedded")?.let { embedded ->
@JvmOverloads
fun Jar.addEmbeddedRuntime(embeddedConfigurationName: String = "embedded") {
project.configurations.findByName(embeddedConfigurationName)?.let { embedded ->
dependsOn(embedded)
val archiveOperations = project.serviceOf<ArchiveOperations>()
from {
@@ -203,8 +204,9 @@ fun Project.sourcesJarWithSourcesFromEmbedded(
return sourcesJarTask
}
fun Jar.addEmbeddedSources() {
project.configurations.findByName("embedded")?.let { embedded ->
@JvmOverloads
fun Jar.addEmbeddedSources(configurationName: String = "embedded") {
project.configurations.findByName(configurationName)?.let { embedded ->
val allSources by lazy {
embedded.resolvedConfiguration
.resolvedArtifacts
-10
View File
@@ -1,6 +1,3 @@
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask
plugins {
kotlin("jvm")
id("jps-compatible")
@@ -33,10 +30,3 @@ tasks.getByName<Jar>("jar") {
//excludes unused bunch files
exclude("META-INF/extensions/*.xml.**")
}
// 1.9 level breaks Kotlin Gradle plugins via changes in enums (KT-48872)
// We limit api and LV until KGP will stop using Kotlin compiler directly (KT-56574)
tasks.withType<KotlinCompilationTask<*>>().configureEach {
compilerOptions.apiVersion.value(KotlinVersion.KOTLIN_1_8).finalizeValueOnRead()
compilerOptions.languageVersion.value(KotlinVersion.KOTLIN_1_8).finalizeValueOnRead()
}
-10
View File
@@ -1,6 +1,3 @@
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask
plugins {
kotlin("jvm")
id("jps-compatible")
@@ -16,10 +13,3 @@ sourceSets {
"main" { projectDefault() }
"test" { }
}
// 1.9 level breaks Kotlin Gradle plugins via changes in enums (KT-48872)
// We limit api and LV until KGP will stop using Kotlin compiler directly (KT-56574)
tasks.withType<KotlinCompilationTask<*>>().configureEach {
compilerOptions.apiVersion.value(KotlinVersion.KOTLIN_1_8).finalizeValueOnRead()
compilerOptions.languageVersion.value(KotlinVersion.KOTLIN_1_8).finalizeValueOnRead()
}
@@ -1,6 +1,3 @@
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask
plugins {
kotlin("jvm")
id("jps-compatible")
@@ -16,11 +13,3 @@ sourceSets {
"main" { projectDefault() }
"test" {}
}
// 1.9 level breaks Kotlin Gradle plugins via changes in enums (KT-48872)
// We limit api and LV until KGP will stop using Kotlin compiler directly (KT-56574)
tasks.withType<KotlinCompilationTask<*>>().configureEach {
compilerOptions.apiVersion.value(KotlinVersion.KOTLIN_1_8).finalizeValueOnRead()
compilerOptions.languageVersion.value(KotlinVersion.KOTLIN_1_8).finalizeValueOnRead()
}
-11
View File
@@ -1,7 +1,3 @@
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask
plugins {
kotlin("jvm")
id("jps-compatible")
@@ -18,10 +14,3 @@ sourceSets {
"main" { projectDefault() }
"test" {}
}
// 1.9 level breaks Kotlin Gradle plugins via changes in enums (KT-48872)
// We limit api and LV until KGP will stop using Kotlin compiler directly (KT-56574)
tasks.withType<KotlinCompilationTask<*>>().configureEach {
compilerOptions.apiVersion.value(KotlinVersion.KOTLIN_1_8).finalizeValueOnRead()
compilerOptions.languageVersion.value(KotlinVersion.KOTLIN_1_8).finalizeValueOnRead()
}
@@ -1,6 +1,3 @@
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask
plugins {
kotlin("jvm")
id("jps-compatible")
@@ -56,10 +53,3 @@ projectTest("testJvmICWithJdk11", parallel = true) {
}
testsJar()
// 1.9 level breaks Kotlin Gradle plugins via changes in enums (KT-48872)
// We limit api and LV until KGP will stop using Kotlin compiler directly (KT-56574)
tasks.withType<KotlinCompilationTask<*>>().configureEach {
compilerOptions.apiVersion.value(KotlinVersion.KOTLIN_1_8).finalizeValueOnRead()
compilerOptions.languageVersion.value(KotlinVersion.KOTLIN_1_8).finalizeValueOnRead()
}
-10
View File
@@ -1,6 +1,3 @@
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion as GradleKotlinVersion
import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask
plugins {
kotlin("jvm")
id("jps-compatible")
@@ -24,10 +21,3 @@ sourceSets {
}
"test" {}
}
// 1.9 level breaks Kotlin Gradle plugins via changes in enums (KT-48872)
// We limit api and LV until KGP will stop using Kotlin compiler directly (KT-56574)
tasks.withType<KotlinCompilationTask<*>>().configureEach {
compilerOptions.apiVersion.value(GradleKotlinVersion.KOTLIN_1_8).finalizeValueOnRead()
compilerOptions.languageVersion.value(GradleKotlinVersion.KOTLIN_1_8).finalizeValueOnRead()
}
@@ -1,6 +1,3 @@
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask
plugins {
kotlin("jvm")
id("jps-compatible")
@@ -18,10 +15,3 @@ sourceSets {
"main" { projectDefault() }
"test" {}
}
// 1.9 level breaks Kotlin Gradle plugins via changes in enums (KT-48872)
// We limit api and LV until KGP will stop using Kotlin compiler directly (KT-56574)
tasks.withType<KotlinCompilationTask<*>>().configureEach {
compilerOptions.apiVersion.value(KotlinVersion.KOTLIN_1_8).finalizeValueOnRead()
compilerOptions.languageVersion.value(KotlinVersion.KOTLIN_1_8).finalizeValueOnRead()
}
-10
View File
@@ -1,6 +1,3 @@
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask
plugins {
kotlin("jvm")
id("jps-compatible")
@@ -16,10 +13,3 @@ sourceSets {
"main" { projectDefault() }
"test" {}
}
// 1.9 level breaks Kotlin Gradle plugins via changes in enums (KT-48872)
// We limit api and LV until KGP will stop using Kotlin compiler directly (KT-56574)
tasks.withType<KotlinCompilationTask<*>>().configureEach {
compilerOptions.apiVersion.value(KotlinVersion.KOTLIN_1_8).finalizeValueOnRead()
compilerOptions.languageVersion.value(KotlinVersion.KOTLIN_1_8).finalizeValueOnRead()
}
+1
View File
@@ -17,6 +17,7 @@
<trust group="org.jetbrains.kotlin" name="kotlin-annotation-processing-embeddable" version="1.9.[0-9](-.+)?" regex="true"/>
<trust group="org.jetbrains.kotlin" name="kotlin-annotation-processing-gradle" version="1.9.[0-9](-.+)?" regex="true"/>
<trust group="org.jetbrains.kotlin" name="kotlin-build-common" version="1.9.[0-9](-.+)?" regex="true"/>
<trust group="org.jetbrains.kotlin" name="kotlin-build-tools-enum-compat" version="1.9.[0-9](-.+)?" regex="true"/>
<trust group="org.jetbrains.kotlin" name="kotlin-build-tools-api" version="1.9.[0-9](-.+)?" regex="true"/>
<trust group="org.jetbrains.kotlin" name="kotlin-build-tools-impl" version="1.9.[0-9](-.+)?" regex="true"/>
<trust group="org.jetbrains.kotlin" name="kotlin-compiler-embeddable" version="1.9.[0-9](-.+)?" regex="true"/>
+1 -1
View File
@@ -48,4 +48,4 @@ runtimeJar()
tasks.withType<KotlinCompilationTask<*>>().configureEach {
compilerOptions.apiVersion.value(KotlinVersion.KOTLIN_1_8).finalizeValueOnRead()
compilerOptions.languageVersion.value(KotlinVersion.KOTLIN_1_8).finalizeValueOnRead()
}
}
@@ -1,6 +1,3 @@
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask
plugins {
kotlin("jvm")
id("jps-compatible")
@@ -30,10 +27,3 @@ publish()
runtimeJar()
sourcesJar()
javadocJar()
// 1.9 level breaks Kotlin Gradle plugins via changes in enums (KT-48872)
// We limit api and LV until KGP will stop using Kotlin compiler directly (KT-56574)
tasks.withType<KotlinCompilationTask<*>>().configureEach {
compilerOptions.apiVersion.value(KotlinVersion.KOTLIN_1_8).finalizeValueOnRead()
compilerOptions.languageVersion.value(KotlinVersion.KOTLIN_1_8).finalizeValueOnRead()
}
@@ -0,0 +1,7 @@
## Description
Backport of `EnumEntries` runtime support API ([KT-48872](https://youtrack.jetbrains.com/issue/KT-48872))
This should help with the case when code was compiled using newer `kotlin-stdlib` version as a `compileOnly` dependency, but at a runtime
older version of `kotlin-stdlib` is provided ([KT-57317](https://youtrack.jetbrains.com/issue/KT-57317)). Mostly it is needed for Kotlin
build tools artifacts.
@@ -0,0 +1,17 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile
plugins {
id("org.jetbrains.kotlin.jvm")
id("jps-compatible")
}
configureKotlinCompileTasksGradleCompatibility()
extensions.extraProperties["kotlin.stdlib.default.dependency"] = "false"
dependencies {
compileOnly(project(":kotlin-stdlib"))
}
tasks.withType<KotlinJvmCompile>().configureEach {
compilerOptions.freeCompilerArgs.add("-Xallow-kotlin-package")
}
@@ -0,0 +1,79 @@
/*
* Copyright 2010-2023 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 kotlin.enums
import java.io.Serializable
/**
* A specialized immutable implementation of [List] interface that
* contains all enum entries of the specified enum type [E].
* [EnumEntries] contains all enum entries in the order they are declared in the source code,
* consistently with the corresponding [Enum.ordinal] values.
*
* An instance of this interface can only be obtained from `EnumClass.entries` property.
*/
@ExperimentalStdlibApi
interface EnumEntries<E : Enum<E>> : List<E>
@Suppress("unused")
@PublishedApi
@ExperimentalStdlibApi
internal fun <E : Enum<E>> enumEntries(entriesProvider: () -> Array<E>): EnumEntries<E> = EnumEntriesList(entriesProvider())
@PublishedApi
@ExperimentalStdlibApi
internal fun <E : Enum<E>> enumEntries(entries: Array<E>): EnumEntries<E> = EnumEntriesList(entries)
@ExperimentalStdlibApi
private class EnumEntriesList<T : Enum<T>>(private val entries: Array<T>) : EnumEntries<T>, AbstractList<T>(), Serializable {
// WA for JS IR bug:
// class type parameter name MUST be different from E (AbstractList<E> type parameter),
// otherwise the bridge names for contains() and indexOf() will be clashed with the original method names,
// and produced JS code will not contain type checks and will not work correctly.
override val size: Int
get() = entries.size
override fun get(index: Int): T {
checkElementIndex(index, entries.size)
return entries[index]
}
// Ported from kotlin-stdlib-common AbstractList.kt
private fun checkElementIndex(index: Int, size: Int) {
if (index < 0 || index >= size) {
throw IndexOutOfBoundsException("index: $index, size: $size")
}
}
// By definition, EnumEntries contains **all** enums in declaration order,
// thus we are able to short-circuit the implementation here
override fun contains(element: T): Boolean {
@Suppress("SENSELESS_COMPARISON")
if (element === null) return false // WA for JS IR bug
// Check identity due to UnsafeVariance
val target = entries.getOrNull(element.ordinal)
return target === element
}
override fun indexOf(element: T): Int {
@Suppress("SENSELESS_COMPARISON")
if (element === null) return -1 // WA for JS IR bug
// Check identity due to UnsafeVariance
val ordinal = element.ordinal
val target = entries.getOrNull(ordinal)
return if (target === element) ordinal else -1
}
override fun lastIndexOf(element: T): Int = indexOf(element)
@Suppress("unused")
private fun writeReplace(): Any {
// Used for Java serialization: EESP ensures that deserialized object **always** reflects the state of the enum on the target classpath
return EnumEntriesSerializationProxy(entries)
}
}
@@ -0,0 +1,24 @@
/*
* Copyright 2010-2023 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.
*/
@file:Suppress("EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE") // for building kotlin-stdlib-jvm-minimal-for-test
package kotlin.enums
import java.io.Serializable
@Suppress("UNCHECKED_CAST", "unused")
internal class EnumEntriesSerializationProxy<E : Enum<E>>(entries: Array<E>) : Serializable {
private val c: Class<E> = entries.javaClass.componentType!! as Class<E>
private companion object {
private const val serialVersionUID: Long = 0L
}
@OptIn(ExperimentalStdlibApi::class)
private fun readResolve(): Any {
return enumEntries(c.enumConstants)
}
}
@@ -89,6 +89,14 @@ dependencies {
exclude(group = "*")
}
// Adding workaround KT-57317 for Gradle versions where Kotlin runtime <1.8.0
mainEmbedded(project(":kotlin-build-tools-enum-compat"))
gradle70Embedded(project(":kotlin-build-tools-enum-compat"))
gradle71Embedded(project(":kotlin-build-tools-enum-compat"))
gradle74Embedded(project(":kotlin-build-tools-enum-compat"))
gradle75Embedded(project(":kotlin-build-tools-enum-compat"))
gradle76Embedded(project(":kotlin-build-tools-enum-compat"))
testCompileOnly(project(":compiler"))
testCompileOnly(project(":kotlin-annotation-processing"))
-10
View File
@@ -1,6 +1,3 @@
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask
plugins {
kotlin("jvm")
}
@@ -39,10 +36,3 @@ projectTest(parallel = false) {
runtimeJar()
sourcesJar()
javadocJar()
// 1.9 level breaks Kotlin Gradle plugins via changes in enums (KT-48872)
// We limit api and LV until KGP will stop using Kotlin compiler directly (KT-56574)
tasks.withType<KotlinCompilationTask<*>>().configureEach {
compilerOptions.apiVersion.value(KotlinVersion.KOTLIN_1_8).finalizeValueOnRead()
compilerOptions.languageVersion.value(KotlinVersion.KOTLIN_1_8).finalizeValueOnRead()
}
@@ -51,10 +51,3 @@ val unpill by tasks.creating {
dependsOn(jar)
doLast { runPillTask("unpill") }
}
// 1.9 level breaks Kotlin Gradle plugins via changes in enums (KT-48872)
tasks.withType<KotlinCompilationTask<*>>().configureEach {
compilerOptions.apiVersion.value(KotlinVersion.KOTLIN_1_8).finalizeValueOnRead()
compilerOptions.languageVersion.value(KotlinVersion.KOTLIN_1_8).finalizeValueOnRead()
}
+4 -1
View File
@@ -16,4 +16,7 @@ dependencies {
@Suppress("UNCHECKED_CAST")
val embeddedDependencies = rootProject.extra["kotlinJpsPluginEmbeddedDependencies"] as List<String>
publishProjectJars(embeddedDependencies + listOf(":jps:jps-plugin", ":jps:jps-common"), libraryDependencies = listOf(protobufFull()))
publishProjectJars(
embeddedDependencies + listOf(":jps:jps-plugin", ":jps:jps-common"),
libraryDependencies = listOf(protobufFull())
)
+2
View File
@@ -185,6 +185,7 @@ include ":kotlin-imports-dumper-compiler-plugin",
":generators:ide-iml-to-gradle-generator",
":generators:test-generator",
":tools:kotlinp",
":kotlin-build-tools-enum-compat",
":kotlin-project-model",
":kotlin-project-model-tests-generator",
":kotlin-gradle-compiler-types",
@@ -710,6 +711,7 @@ project(':kotlin-assignment-compiler-plugin.cli').projectDir = "$rootDir/plugins
project(':kotlin-assignment-compiler-plugin.embeddable').projectDir = "$rootDir/plugins/assign-plugin/assign-plugin.embeddable" as File
project(':tools:kotlinp').projectDir = "$rootDir/libraries/tools/kotlinp" as File
project(":kotlin-build-tools-enum-compat").projectDir = "$rootDir/libraries/tools/kotlin-build-tools-enum-compat" as File
project(':kotlin-project-model').projectDir = "$rootDir/libraries/tools/kotlin-project-model" as File
project(':kotlin-project-model-tests-generator').projectDir = "$rootDir/libraries/tools/kotlin-project-model-tests-generator" as File
project(':kotlin-gradle-compiler-types').projectDir = "$rootDir/libraries/tools/kotlin-gradle-compiler-types" as File