KotlinToolingMetadata: Add stronger typing for target extras

This commit is contained in:
sebastian.sellmair
2021-02-03 18:51:40 +01:00
committed by TeamCityServer
parent 896e77cd32
commit 0a59301f8a
7 changed files with 274 additions and 112 deletions
@@ -69,7 +69,7 @@ class KotlinToolingMetadataIT : BaseGradleIT() {
val metadata = KotlinToolingMetadata.parseJsonOrThrow(projectDir.resolve(defaultKotlinToolingMetadataJsonPath).readText())
assertEquals(
listOf(KonanTarget.LINUX_X64.name, KonanTarget.MACOS_X64.name).sorted(),
metadata.projectTargets.mapNotNull { it.extras["konanTarget"] }.sorted()
metadata.projectTargets.mapNotNull { it.extras.native?.konanTarget }.sorted()
)
}
@@ -98,8 +98,8 @@ class BuildKotlinToolingMetadataTest {
val androidTargetMetadata = getKotlinToolingMetadata()
.projectTargets.single { it.platformType == androidJvm.name }
assertEquals("1.6", androidTargetMetadata.extras["sourceCompatibility"])
assertEquals("1.8", androidTargetMetadata.extras["targetCompatibility"])
assertEquals("1.6", androidTargetMetadata.extras.android?.sourceCompatibility)
assertEquals("1.8", androidTargetMetadata.extras.android?.targetCompatibility)
}
@Test
@@ -112,7 +112,7 @@ class BuildKotlinToolingMetadataTest {
assertEquals(
"12", getKotlinToolingMetadata().projectTargets
.single { it.platformType == KotlinPlatformType.jvm.name }.extras["jvmTarget"],
.single { it.platformType == KotlinPlatformType.jvm.name }.extras.jvm?.jvmTarget,
"Expected jvmTarget of main compilation"
)
}
@@ -125,9 +125,9 @@ class BuildKotlinToolingMetadataTest {
val metadata = getKotlinToolingMetadata()
val linuxTarget = metadata.projectTargets.single { it.platformType == native.name }
assertEquals(KonanTarget.LINUX_X64.name, linuxTarget.extras["konanTarget"])
assertEquals(project.konanVersion.toString(), linuxTarget.extras["konanVersion"])
assertEquals(KotlinAbiVersion.CURRENT.toString(), linuxTarget.extras["abiVersion"])
assertEquals(KonanTarget.LINUX_X64.name, linuxTarget.extras.native?.konanTarget)
assertEquals(project.konanVersion.toString(), linuxTarget.extras.native?.konanVersion)
assertEquals(KotlinAbiVersion.CURRENT.toString(), linuxTarget.extras.native?.konanAbiVersion)
}
@Test
@@ -140,8 +140,8 @@ class BuildKotlinToolingMetadataTest {
assertEquals(org.jetbrains.kotlin.gradle.plugin.KotlinJsPluginWrapper::class.java.canonicalName, metadata.buildPlugin)
val jsTarget = metadata.projectTargets.single { it.platformType == js.name }
assertEquals("true", jsTarget.extras["isNodejsConfigured"])
assertEquals("false", jsTarget.extras["isBrowserConfigured"])
assertEquals(true, jsTarget.extras.js?.isNodejsConfigured)
assertEquals(false, jsTarget.extras.js?.isBrowserConfigured)
}
@Test
@@ -102,29 +102,46 @@ private fun buildTargetMetadata(target: KotlinTarget): KotlinToolingMetadata.Pro
)
}
private fun buildTargetMetadataExtras(target: KotlinTarget): Map<String, String> {
val extras = mutableMapOf<String, String>()
when (target) {
is KotlinJvmTarget -> {
extras["withJavaEnabled"] = target.withJavaEnabled.toString()
target.compilations.findByName(KotlinCompilation.MAIN_COMPILATION_NAME)?.let { mainCompilation ->
extras["jvmTarget"] = mainCompilation.kotlinOptions.jvmTarget
}
}
is KotlinAndroidTarget -> {
val androidExtension = target.project.extensions.findByType(BaseExtension::class.java)
extras["sourceCompatibility"] = androidExtension?.compileOptions?.sourceCompatibility.toString()
extras["targetCompatibility"] = androidExtension?.compileOptions?.targetCompatibility.toString()
}
is KotlinJsSubTargetContainerDsl -> {
extras["isBrowserConfigured"] = target.isBrowserConfigured.toString()
extras["isNodejsConfigured"] = target.isNodejsConfigured.toString()
}
is KotlinNativeTarget -> {
extras["konanTarget"] = target.konanTarget.name
extras["konanVersion"] = target.project.konanVersion.toString()
extras["abiVersion"] = KotlinAbiVersion.CURRENT.toString()
}
}
return extras.toMap()
private fun buildTargetMetadataExtras(target: KotlinTarget): KotlinToolingMetadata.ProjectTargetMetadata.Extras {
return KotlinToolingMetadata.ProjectTargetMetadata.Extras(
jvm = buildJvmExtrasOrNull(target),
android = buildAndroidExtrasOrNull(target),
js = buildJsExtrasOrNull(target),
native = buildNativeExtrasOrNull(target)
)
}
private fun buildJvmExtrasOrNull(target: KotlinTarget): KotlinToolingMetadata.ProjectTargetMetadata.JvmExtras? {
if (target !is KotlinJvmTarget) return null
return KotlinToolingMetadata.ProjectTargetMetadata.JvmExtras(
withJavaEnabled = target.withJavaEnabled,
jvmTarget = target.compilations.findByName(KotlinCompilation.MAIN_COMPILATION_NAME)?.kotlinOptions?.jvmTarget
)
}
private fun buildAndroidExtrasOrNull(target: KotlinTarget): KotlinToolingMetadata.ProjectTargetMetadata.AndroidExtras? {
if (target !is KotlinAndroidTarget) return null
val androidExtension = target.project.extensions.findByType(BaseExtension::class.java)
return KotlinToolingMetadata.ProjectTargetMetadata.AndroidExtras(
sourceCompatibility = androidExtension?.compileOptions?.sourceCompatibility.toString(),
targetCompatibility = androidExtension?.compileOptions?.targetCompatibility.toString()
)
}
private fun buildJsExtrasOrNull(target: KotlinTarget): KotlinToolingMetadata.ProjectTargetMetadata.JsExtras? {
if (target !is KotlinJsSubTargetContainerDsl) return null
return KotlinToolingMetadata.ProjectTargetMetadata.JsExtras(
isBrowserConfigured = target.isBrowserConfigured,
isNodejsConfigured = target.isNodejsConfigured
)
}
private fun buildNativeExtrasOrNull(target: KotlinTarget): KotlinToolingMetadata.ProjectTargetMetadata.NativeExtras? {
if (target !is KotlinNativeTarget) return null
return KotlinToolingMetadata.ProjectTargetMetadata.NativeExtras(
konanTarget = target.konanTarget.name,
konanVersion = target.project.konanVersion.toString(),
konanAbiVersion = KotlinAbiVersion.CURRENT.toString()
)
}
@@ -25,14 +25,14 @@ internal fun KotlinToolingMetadata.toJsonObject(): JsonObject {
}
}
internal fun KotlinToolingMetadata.ProjectSettings.toJsonObject(): JsonObject {
private fun KotlinToolingMetadata.ProjectSettings.toJsonObject(): JsonObject {
return JsonObject().apply {
addProperty("isHmppEnabled", isHmppEnabled)
addProperty("isCompatibilityMetadataVariantEnabled", isCompatibilityMetadataVariantEnabled)
}
}
internal fun List<KotlinToolingMetadata.ProjectTargetMetadata>.toJsonArray(): JsonArray {
private fun List<KotlinToolingMetadata.ProjectTargetMetadata>.toJsonArray(): JsonArray {
return JsonArray().apply {
this@toJsonArray.forEach { targetMetadata ->
add(targetMetadata.toJsonObject())
@@ -40,20 +40,54 @@ internal fun List<KotlinToolingMetadata.ProjectTargetMetadata>.toJsonArray(): Js
}
}
internal fun KotlinToolingMetadata.ProjectTargetMetadata.toJsonObject(): JsonObject {
private fun KotlinToolingMetadata.ProjectTargetMetadata.toJsonObject(): JsonObject {
return JsonObject().apply {
addProperty("target", target)
addProperty("platformType", platformType)
if (extras.isNotEmpty()) {
add("extras", JsonObject().apply {
for (extra in extras) {
addProperty(extra.key, extra.value)
}
})
extras.toJsonObject().takeIf { it.size() > 0 }?.let { extrasJsonObject ->
add("extras", extrasJsonObject)
}
}
}
private fun KotlinToolingMetadata.ProjectTargetMetadata.Extras.toJsonObject(): JsonObject {
return JsonObject().apply {
jvm?.let { add("jvm", it.toJsonObject()) }
android?.let { add("android", it.toJsonObject()) }
js?.let { add("js", it.toJsonObject()) }
native?.let { add("native", it.toJsonObject()) }
}
}
private fun KotlinToolingMetadata.ProjectTargetMetadata.JvmExtras.toJsonObject(): JsonObject {
return JsonObject().apply {
jvmTarget?.let { addProperty("jvmTarget", it) }
addProperty("withJavaEnabled", withJavaEnabled)
}
}
private fun KotlinToolingMetadata.ProjectTargetMetadata.JsExtras.toJsonObject(): JsonObject {
return JsonObject().apply {
addProperty("isBrowserConfigured", isBrowserConfigured)
addProperty("isNodejsConfigured", isNodejsConfigured)
}
}
private fun KotlinToolingMetadata.ProjectTargetMetadata.NativeExtras.toJsonObject(): JsonObject {
return JsonObject().apply {
addProperty("konanTarget", konanTarget)
addProperty("konanVersion", konanVersion)
addProperty("konanAbiVersion", konanAbiVersion)
}
}
private fun KotlinToolingMetadata.ProjectTargetMetadata.AndroidExtras.toJsonObject(): JsonObject {
return JsonObject().apply {
addProperty("sourceCompatibility", sourceCompatibility)
addProperty("targetCompatibility", targetCompatibility)
}
}
sealed class KotlinToolingMetadataParsingResult {
data class Success(val value: KotlinToolingMetadata) : KotlinToolingMetadataParsingResult()
data class Failure(val reason: String) : KotlinToolingMetadataParsingResult()
@@ -104,19 +138,47 @@ private fun JsonObject.toTargetMetadataOrThrow(): KotlinToolingMetadata.ProjectT
return KotlinToolingMetadata.ProjectTargetMetadata(
target = getOrThrow("target").asString,
platformType = getOrThrow("platformType").asString,
extras = (get("extras") as? JsonObject)?.toTargetMetadataExtrasOrThrow() ?: emptyMap()
extras = (get("extras") as? JsonObject)?.toTargetMetadataExtrasOrThrow()
?: KotlinToolingMetadata.ProjectTargetMetadata.Extras()
)
}
private fun JsonObject.toTargetMetadataExtrasOrThrow(): Map<String, String> {
val map = mutableMapOf<String, String>()
this.keySet().forEach { key ->
val primitive = this[key] as? JsonPrimitive
if (primitive != null) {
map[key] = primitive.asString
}
}
return map.toMap()
private fun JsonObject.toTargetMetadataExtrasOrThrow(): KotlinToolingMetadata.ProjectTargetMetadata.Extras {
return KotlinToolingMetadata.ProjectTargetMetadata.Extras(
jvm = get("jvm")?.asJsonObject?.toJvmExtrasOrThrow(),
android = get("android")?.asJsonObject?.toAndroidExtrasOrThrow(),
js = get("js")?.asJsonObject?.toJsExtrasOrThrow(),
native = get("native")?.asJsonObject?.toNativeExtrasOrThrow()
)
}
private fun JsonObject.toJvmExtrasOrThrow(): KotlinToolingMetadata.ProjectTargetMetadata.JvmExtras {
return KotlinToolingMetadata.ProjectTargetMetadata.JvmExtras(
jvmTarget = get("jvmTarget")?.asString,
withJavaEnabled = getOrThrow("withJavaEnabled").asBoolean
)
}
private fun JsonObject.toJsExtrasOrThrow(): KotlinToolingMetadata.ProjectTargetMetadata.JsExtras {
return KotlinToolingMetadata.ProjectTargetMetadata.JsExtras(
isBrowserConfigured = getOrThrow("isBrowserConfigured").asBoolean,
isNodejsConfigured = getOrThrow("isNodejsConfigured").asBoolean
)
}
private fun JsonObject.toNativeExtrasOrThrow(): KotlinToolingMetadata.ProjectTargetMetadata.NativeExtras {
return KotlinToolingMetadata.ProjectTargetMetadata.NativeExtras(
konanTarget = getOrThrow("konanTarget").asString,
konanVersion = getOrThrow("konanVersion").asString,
konanAbiVersion = getOrThrow("konanAbiVersion").asString
)
}
private fun JsonObject.toAndroidExtrasOrThrow(): KotlinToolingMetadata.ProjectTargetMetadata.AndroidExtras {
return KotlinToolingMetadata.ProjectTargetMetadata.AndroidExtras(
sourceCompatibility = getOrThrow("sourceCompatibility").asString,
targetCompatibility = getOrThrow("targetCompatibility").asString
)
}
private fun JsonObject.getOrThrow(key: String): JsonElement {
@@ -18,6 +18,7 @@ data class KotlinToolingMetadata(
* - org.jetbrains.kotlin.gradle.plugin.mpp.KotlinMultiplatformPlugin
* - org.jetbrains.kotlin.gradle.targets.js.KotlinJsPlugin
* - ...
* )
*/
val buildPlugin: String,
val buildPluginVersion: String,
@@ -34,8 +35,36 @@ data class KotlinToolingMetadata(
data class ProjectTargetMetadata(
val target: String,
val platformType: String,
val extras: Map<String, String>
)
val extras: Extras
) {
data class Extras(
val jvm: JvmExtras? = null,
val android: AndroidExtras? = null,
val js: JsExtras? = null,
val native: NativeExtras? = null
)
data class JvmExtras(
val jvmTarget: String?,
val withJavaEnabled: Boolean
)
data class AndroidExtras(
val sourceCompatibility: String,
val targetCompatibility: String,
)
data class JsExtras(
val isBrowserConfigured: Boolean,
val isNodejsConfigured: Boolean,
)
data class NativeExtras(
val konanTarget: String,
val konanVersion: String,
val konanAbiVersion: String
)
}
companion object
}
@@ -10,6 +10,8 @@ import junit.framework.Assert.assertTrue
import org.intellij.lang.annotations.Language
import org.junit.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertNull
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
@@ -20,48 +22,64 @@ class DeserializeStringTest {
@Test
fun sample1() {
@Language("JSON") val json =
"""
{
"buildSystem": "Gradle",
"buildSystemVersion": "6.7",
"buildPlugin": "org.jetbrains.kotlin.gradle.plugin.KotlinMultiplatformPluginWrapper",
"buildPluginVersion": "1.5.255-SNAPSHOT",
"projectSettings": {
"isHmppEnabled": false,
"isCompatibilityMetadataVariantEnabled": true
},
"projectTargets": [
{
"target": "org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget",
"platformType": "androidJvm",
"extras": {
@Language("JSON") val json = """
{
"buildSystem": "Gradle",
"buildSystemVersion": "6.7",
"buildPlugin": "org.jetbrains.kotlin.gradle.plugin.KotlinMultiplatformPluginWrapper",
"buildPluginVersion": "1.5.255-SNAPSHOT",
"projectSettings": {
"isHmppEnabled": false,
"isCompatibilityMetadataVariantEnabled": true
},
"projectTargets": [
{
"target": "org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget",
"platformType": "androidJvm",
"extras": {
"android": {
"sourceCompatibility": "1.7",
"targetCompatibility": "1.7"
}
},
{
"target": "org.jetbrains.kotlin.gradle.targets.js.KotlinJsTarget_Decorated",
"platformType": "js",
"extras": {
"isBrowserConfigured": "true",
"isNodejsConfigured": "true"
}
},
{
"target": "org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget_Decorated",
"platformType": "jvm",
"extras": {
"withJavaEnabled": "false",
"jvmTarget": "1.8"
}
},
{
"target": "org.jetbrains.kotlin.gradle.plugin.mpp.KotlinMetadataTarget_Decorated",
"platformType": "common"
}
]
}
},
{
"target": "org.jetbrains.kotlin.gradle.targets.js.KotlinJsTarget_Decorated",
"platformType": "js",
"extras": {
"js": {
"isBrowserConfigured": true,
"isNodejsConfigured": true
}
}
},
{
"target": "org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget_Decorated",
"platformType": "jvm",
"extras": {
"jvm": {
"jvmTarget": "1.8",
"withJavaEnabled": false
}
}
},
{
"target": "org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTargetWithHostTests_Decorated",
"platformType": "native",
"extras": {
"native": {
"konanTarget": "linux_x64",
"konanVersion": "1.5-dev-17775",
"konanAbiVersion": "1.4.2"
}
}
},
{
"target": "org.jetbrains.kotlin.gradle.plugin.mpp.KotlinMetadataTarget_Decorated",
"platformType": "common"
}
]
}
""".trimIndent()
val metadata = KotlinToolingMetadata.parseJsonOrThrow(json)
@@ -71,29 +89,48 @@ class DeserializeStringTest {
assertEquals("1.5.255-SNAPSHOT", metadata.buildPluginVersion)
assertFalse(metadata.projectSettings.isHmppEnabled)
assertTrue(metadata.projectSettings.isCompatibilityMetadataVariantEnabled)
assertEquals(4, metadata.projectTargets.size, "Expected exactly 4 targets")
assertEquals(5, metadata.projectTargets.size, "Expected exactly 4 targets")
val androidJvmTarget = metadata.projectTargets.single { it.platformType == "androidJvm" }
assertEquals("org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget", androidJvmTarget.target)
assertEquals(2, androidJvmTarget.extras.size, "Expected exactly two extras")
assertEquals("1.7", androidJvmTarget.extras["sourceCompatibility"])
assertEquals("1.7", androidJvmTarget.extras["targetCompatibility"])
assertEquals("1.7", androidJvmTarget.extras.android?.sourceCompatibility)
assertEquals("1.7", androidJvmTarget.extras.android?.targetCompatibility)
assertNull(androidJvmTarget.extras.jvm)
assertNull(androidJvmTarget.extras.js)
assertNull(androidJvmTarget.extras.native)
val jsTarget = metadata.projectTargets.single { it.platformType == "js" }
assertEquals("org.jetbrains.kotlin.gradle.targets.js.KotlinJsTarget_Decorated", jsTarget.target)
assertEquals(2, jsTarget.extras.size, "Expected exactly two extras")
assertEquals("true", jsTarget.extras["isBrowserConfigured"])
assertEquals("true", jsTarget.extras["isNodejsConfigured"])
assertEquals(true, jsTarget.extras.js?.isBrowserConfigured)
assertEquals(true, jsTarget.extras.js?.isNodejsConfigured)
assertNull(jsTarget.extras.android)
assertNull(jsTarget.extras.jvm)
assertNull(jsTarget.extras.native)
val jvmTarget = metadata.projectTargets.single { it.platformType == "jvm" }
assertEquals("org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget_Decorated", jvmTarget.target)
assertEquals(2, jvmTarget.extras.size, "Expected exactly two extras")
assertEquals("false", jvmTarget.extras["withJavaEnabled"])
assertEquals("1.8", jvmTarget.extras["jvmTarget"])
assertEquals(false, jvmTarget.extras.jvm?.withJavaEnabled)
assertEquals("1.8", jvmTarget.extras.jvm?.jvmTarget)
assertNull(jvmTarget.extras.android)
assertNull(jvmTarget.extras.js)
assertNull(jvmTarget.extras.native)
val nativeTarget = metadata.projectTargets.single { it.platformType == "native" }
assertEquals("org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTargetWithHostTests_Decorated", nativeTarget.target)
val nativeExtras = assertNotNull(nativeTarget.extras.native)
assertEquals("linux_x64", nativeExtras.konanTarget)
assertEquals("1.5-dev-17775", nativeExtras.konanVersion)
assertEquals("1.4.2", nativeExtras.konanAbiVersion)
assertNull(nativeTarget.extras.android)
assertNull(nativeTarget.extras.jvm)
assertNull(nativeTarget.extras.js)
val commonTarget = metadata.projectTargets.single { it.platformType == "common" }
assertEquals("org.jetbrains.kotlin.gradle.plugin.mpp.KotlinMetadataTarget_Decorated", commonTarget.target)
assertEquals(0, commonTarget.extras.size, "Expected zero extras")
assertNull(commonTarget.extras.android)
assertNull(commonTarget.extras.jvm)
assertNull(commonTarget.extras.js)
assertNull(commonTarget.extras.native)
}
}
@@ -28,10 +28,7 @@ class SerializeAndDeserializeTest {
ProjectTargetMetadata(
target = "generic target",
platformType = "generic platform type",
extras = mapOf(
"extra0" to "extra value0",
"extra1" to "extra value1"
)
extras = ProjectTargetMetadata.Extras()
)
)
)
@@ -44,15 +41,35 @@ class SerializeAndDeserializeTest {
ProjectTargetMetadata(
target = "generic target",
platformType = "generic platform type",
extras = mapOf(
"extra0" to "{ some extra value %\" with, chars to escape",
"extra1" to "extra value1"
extras = ProjectTargetMetadata.Extras(
jvm = ProjectTargetMetadata.JvmExtras(
jvmTarget = null,
withJavaEnabled = true
),
android = ProjectTargetMetadata.AndroidExtras(
sourceCompatibility = "1.8",
targetCompatibility = "1.6"
),
js = ProjectTargetMetadata.JsExtras(
isBrowserConfigured = true,
isNodejsConfigured = false
),
native = ProjectTargetMetadata.NativeExtras(
konanTarget = "linuxX64",
konanVersion = "1.0-generic",
konanAbiVersion = "1.4.2"
)
)
),
ProjectTargetMetadata(
target = "generic target 2 (with no extras)",
platformType = "generic platform type 2",
extras = emptyMap()
extras = ProjectTargetMetadata.Extras(
jvm = ProjectTargetMetadata.JvmExtras(
jvmTarget = "1.8",
withJavaEnabled = true
)
)
)
)
)