diff --git a/buildSrc/src/main/kotlin/tasks.kt b/buildSrc/src/main/kotlin/tasks.kt
index ce08cbde17c..65c12117f2c 100644
--- a/buildSrc/src/main/kotlin/tasks.kt
+++ b/buildSrc/src/main/kotlin/tasks.kt
@@ -41,6 +41,7 @@ val kotlinGradlePluginAndItsRequired = arrayOf(
":kotlin-gradle-plugin-api",
":kotlin-gradle-plugin",
":kotlin-gradle-plugin-model",
+ ":kotlin-tooling-metadata",
":kotlin-reflect",
":kotlin-annotation-processing-gradle",
":kotlin-test",
@@ -59,7 +60,8 @@ val kotlinGradlePluginAndItsRequired = arrayOf(
":kotlin-serialization",
":kotlin-test-js-runner",
":native:kotlin-klib-commonizer-embeddable",
- ":native:kotlin-klib-commonizer-api"
+ ":native:kotlin-klib-commonizer-api",
+ ":native:kotlin-native-utils"
)
fun Task.dependsOnKotlinGradlePluginInstall() {
diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml
index 7e629b7e649..1f517bd4039 100644
--- a/gradle/verification-metadata.xml
+++ b/gradle/verification-metadata.xml
@@ -19,6 +19,7 @@
+
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/build.gradle.kts
index 833b662804c..d17261454af 100644
--- a/libraries/tools/kotlin-gradle-plugin-integration-tests/build.gradle.kts
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/build.gradle.kts
@@ -15,6 +15,7 @@ val kotlinGradlePluginTest = project(":kotlin-gradle-plugin").sourceSets.named("
dependencies {
testImplementation(project(":kotlin-gradle-plugin"))
+ testImplementation(project(":kotlin-tooling-metadata"))
testImplementation(kotlinGradlePluginTest)
testImplementation(project(":kotlin-gradle-subplugin-example"))
testImplementation(project(":kotlin-allopen"))
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/KotlinToolingMetadataIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/KotlinToolingMetadataIT.kt
new file mode 100644
index 00000000000..6dce3d1a159
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/KotlinToolingMetadataIT.kt
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2010-2021 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 org.jetbrains.kotlin.gradle
+
+import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
+import org.jetbrains.kotlin.gradle.tooling.BuildKotlinToolingMetadataTask
+import org.jetbrains.kotlin.konan.target.KonanTarget
+import org.jetbrains.kotlin.tooling.KotlinToolingMetadata
+import org.jetbrains.kotlin.tooling.parseJsonOrThrow
+import org.junit.Test
+import kotlin.test.assertEquals
+
+class KotlinToolingMetadataIT : BaseGradleIT() {
+ override val defaultGradleVersion: GradleVersionRequired = GradleVersionRequired.FOR_MPP_SUPPORT
+
+ private val defaultKotlinToolingMetadataJsonPath = "build/kotlinToolingMetadata/kotlin-tooling-metadata.json"
+
+ @Test
+ fun `new-mpp-published`() = with(transformProjectWithPluginsDsl("new-mpp-published")) {
+ build("publish") {
+ assertSuccessful()
+ assertTasksExecuted(
+ ":${BuildKotlinToolingMetadataTask.defaultTaskName}"
+ )
+ /* Check metadata file in build dir */
+ assertFileExists(defaultKotlinToolingMetadataJsonPath)
+ val metadataJson = projectDir.resolve(defaultKotlinToolingMetadataJsonPath).readText()
+ val metadata = KotlinToolingMetadata.parseJsonOrThrow(metadataJson)
+ assertEquals(
+ listOf(
+ KotlinPlatformType.common.name, KotlinPlatformType.jvm.name,
+ KotlinPlatformType.js.name, KotlinPlatformType.native.name
+ ).sorted(),
+ metadata.projectTargets.map { it.platformType }.sorted()
+ )
+
+ /* Check metadata file in published repository */
+ val publishedMetadataJson = projectDir.parentFile.resolve(
+ "repo/com/example/bar/my-lib-bar/1.0/my-lib-bar-1.0-kotlin-tooling-metadata.json"
+ ).readText()
+
+ assertEquals(
+ metadataJson, publishedMetadataJson,
+ "Expected published kotlin-tooling-metadata.json to contain same content as in buildDir"
+ )
+ }
+
+ /* Checking UP-TO-DATE behaviour */
+ build("publish") {
+ assertSuccessful()
+ // Nothing changed. Should be up-to-date
+ assertTasksUpToDate(":${BuildKotlinToolingMetadataTask.defaultTaskName}")
+ }
+
+ // Adding macos target
+ val buildFile = projectDir.resolve("build.gradle.kts")
+ buildFile.writeText(
+ buildFile.readText().replace(
+ "linuxX64()", "linuxX64()\nmacosX64()"
+ )
+ )
+
+ build("publish") {
+ assertSuccessful()
+ assertTasksExecuted(":${BuildKotlinToolingMetadataTask.defaultTaskName}")
+ 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()
+ )
+ }
+
+ }
+
+ @Test
+ fun `new-mpp-published with KotlinToolingMetadataArtifact disabled`() = with(transformProjectWithPluginsDsl("new-mpp-published")) {
+ projectDir.resolve("gradle.properties").appendText("\nkotlin.mpp.enableKotlinToolingMetadataArtifact=false")
+ build("publish") {
+ assertSuccessful()
+ assertNoSuchFile(defaultKotlinToolingMetadataJsonPath)
+ assertTasksNotRealized(
+ ":${BuildKotlinToolingMetadataTask.defaultTaskName}"
+ )
+ }
+ }
+
+ @Test
+ fun `kotlin-js-browser-project`() = with(transformProjectWithPluginsDsl("kotlin-js-browser-project")) {
+ build(BuildKotlinToolingMetadataTask.defaultTaskName) {
+ assertSuccessful()
+ assertTasksExecuted(":app:${BuildKotlinToolingMetadataTask.defaultTaskName}")
+ assertTasksExecuted(":base:${BuildKotlinToolingMetadataTask.defaultTaskName}")
+ assertTasksExecuted(":lib:${BuildKotlinToolingMetadataTask.defaultTaskName}")
+ }
+ }
+}
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-published/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-published/build.gradle.kts
new file mode 100644
index 00000000000..b9e7e30a0c4
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-published/build.gradle.kts
@@ -0,0 +1,94 @@
+plugins {
+ kotlin("multiplatform").version("")
+ `maven-publish`
+}
+
+repositories {
+ mavenLocal()
+ maven("../repo")
+ jcenter()
+}
+
+group = "com.example.bar"
+version = "1.0"
+
+kotlin {
+ jvm()
+ js()
+ linuxX64()
+
+ sourceSets {
+ val commonMain by getting {
+ dependencies {
+ implementation(kotlin("stdlib-common"))
+ }
+ }
+
+ val commonTest by getting {
+ dependencies {
+ implementation(kotlin("test-common"))
+ implementation(kotlin("test-annotations-common"))
+ }
+ }
+
+ val jvmAndJsMain by creating {
+ dependsOn(commonMain)
+ }
+
+ val jvmAndJsTest by creating {
+ dependsOn(commonTest)
+ }
+
+ val linuxAndJsMain by creating {
+ dependsOn(commonMain)
+ }
+
+ val linuxAndJsTest by creating {
+ dependsOn(commonTest)
+ }
+
+ jvm().compilations["main"].defaultSourceSet {
+ dependsOn(jvmAndJsMain)
+ dependencies {
+ implementation(kotlin("stdlib"))
+ }
+ }
+
+ jvm().compilations["test"].defaultSourceSet {
+ dependsOn(jvmAndJsTest)
+ dependencies {
+ implementation(kotlin("test-junit"))
+ }
+ }
+
+ js().compilations["main"].defaultSourceSet {
+ dependsOn(jvmAndJsMain)
+ dependsOn(linuxAndJsMain)
+ dependencies {
+ implementation(kotlin("stdlib-js"))
+ }
+ }
+
+ js().compilations["test"].defaultSourceSet {
+ dependsOn(jvmAndJsTest)
+ dependsOn(linuxAndJsTest)
+ dependencies {
+ implementation(kotlin("test-js"))
+ }
+ }
+
+ linuxX64().compilations["main"].defaultSourceSet {
+ dependsOn(linuxAndJsMain)
+ }
+
+ linuxX64().compilations["test"].defaultSourceSet {
+ dependsOn(linuxAndJsTest)
+ }
+ }
+}
+
+publishing {
+ repositories {
+ maven("../repo")
+ }
+}
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-published/gradle.properties b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-published/gradle.properties
new file mode 100644
index 00000000000..0ae1e640ff1
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-published/gradle.properties
@@ -0,0 +1,2 @@
+kotlin.mpp.enableGranularSourceSetsMetadata=true
+kotlin.mpp.enableCompatibilityMetadataVariant=true
\ No newline at end of file
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-published/settings.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-published/settings.gradle.kts
new file mode 100644
index 00000000000..17a7c7b3b1d
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-published/settings.gradle.kts
@@ -0,0 +1,10 @@
+pluginManagement {
+ repositories {
+ mavenLocal()
+ gradlePluginPortal()
+ }
+}
+
+rootProject.name = "my-lib-bar"
+
+enableFeaturePreview("GRADLE_METADATA")
\ No newline at end of file
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-published/src/commonMain/kotlin/Bar.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-published/src/commonMain/kotlin/Bar.kt
new file mode 100644
index 00000000000..91879d1ab0b
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-published/src/commonMain/kotlin/Bar.kt
@@ -0,0 +1,3 @@
+package com.example.bar
+
+fun stub() = ""
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-published/src/commonTest/kotlin/BarTest.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-published/src/commonTest/kotlin/BarTest.kt
new file mode 100644
index 00000000000..c30bf1a51d2
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-published/src/commonTest/kotlin/BarTest.kt
@@ -0,0 +1,10 @@
+package com.example.bar
+
+import kotlin.test.Test
+
+class BarTest {
+ @Test
+ fun testBar() {
+
+ }
+}
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-published/src/jvmAndJsMain/kotlin/BarJvmAndJs.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-published/src/jvmAndJsMain/kotlin/BarJvmAndJs.kt
new file mode 100644
index 00000000000..d3164ac157f
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-published/src/jvmAndJsMain/kotlin/BarJvmAndJs.kt
@@ -0,0 +1,3 @@
+package com.example.bar
+
+fun jvmAndJsStub() = ""
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-published/src/jvmAndJsTest/kotlin/BarJvmAndJsTest.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-published/src/jvmAndJsTest/kotlin/BarJvmAndJsTest.kt
new file mode 100644
index 00000000000..3ad9f4da706
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-published/src/jvmAndJsTest/kotlin/BarJvmAndJsTest.kt
@@ -0,0 +1,15 @@
+package com.example.bar
+
+import kotlin.test.Test
+
+class BarJvmAndJsTest {
+ @Test
+ fun testBarJvmAndJs() {
+
+ }
+
+ @Test
+ fun testThirdParty() {
+
+ }
+}
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-published/src/linuxAndJsMain/kotlin/BarLinuxAndJs.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-published/src/linuxAndJsMain/kotlin/BarLinuxAndJs.kt
new file mode 100644
index 00000000000..258106a65e3
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-published/src/linuxAndJsMain/kotlin/BarLinuxAndJs.kt
@@ -0,0 +1,3 @@
+package com.example.bar
+
+fun linuxAndJsStub() = ""
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-published/src/linuxAndJsTest/kotlin/BarLinuxAndJsTest.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-published/src/linuxAndJsTest/kotlin/BarLinuxAndJsTest.kt
new file mode 100644
index 00000000000..5befd85dac4
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-published/src/linuxAndJsTest/kotlin/BarLinuxAndJsTest.kt
@@ -0,0 +1,10 @@
+package com.example.bar
+
+import kotlin.test.Test
+
+class BarLinuxAndJsTest {
+ @Test
+ fun testBarLinuxAndJs() {
+
+ }
+}
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-published/src/linuxX64Main/kotlin/BarLinux.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-published/src/linuxX64Main/kotlin/BarLinux.kt
new file mode 100644
index 00000000000..4b0a2158778
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-published/src/linuxX64Main/kotlin/BarLinux.kt
@@ -0,0 +1,3 @@
+package com.example.bar
+
+fun linuxStub() =""
diff --git a/libraries/tools/kotlin-gradle-plugin/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin/build.gradle.kts
index 7d02a807506..ba9e92f08b9 100644
--- a/libraries/tools/kotlin-gradle-plugin/build.gradle.kts
+++ b/libraries/tools/kotlin-gradle-plugin/build.gradle.kts
@@ -43,6 +43,8 @@ dependencies {
implementation(kotlinStdlib())
implementation(project(":kotlin-util-klib"))
implementation(project(":native:kotlin-klib-commonizer-api"))
+ implementation(project(":kotlin-tooling-metadata"))
+ compileOnly(project(":native:kotlin-native-utils"))
compileOnly(project(":kotlin-reflect-api"))
compileOnly(project(":kotlin-android-extensions"))
compileOnly(project(":kotlin-build-common"))
diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/BuildKotlinToolingMetadataTest.kt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/BuildKotlinToolingMetadataTest.kt
new file mode 100644
index 00000000000..cafaf12dfed
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/BuildKotlinToolingMetadataTest.kt
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2010-2021 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("FunctionName")
+
+package org.jetbrains.kotlin.gradle
+
+import com.android.build.gradle.BaseExtension
+import org.gradle.api.JavaVersion
+import org.gradle.api.internal.project.ProjectInternal
+import org.gradle.testfixtures.ProjectBuilder
+import org.jetbrains.kotlin.gradle.dsl.KotlinJsProjectExtension
+import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
+import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
+import org.jetbrains.kotlin.gradle.plugin.KotlinMultiplatformPluginWrapper
+import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
+import org.jetbrains.kotlin.gradle.plugin.getKotlinPluginVersion
+import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinMetadataTarget
+import org.jetbrains.kotlin.gradle.tooling.BuildKotlinToolingMetadataTask
+import org.jetbrains.kotlin.konan.target.KonanTarget
+import org.jetbrains.kotlin.tooling.KotlinToolingMetadata
+import org.jetbrains.kotlin.tooling.toJsonString
+import kotlin.test.Test
+import kotlin.test.assertEquals
+import kotlin.test.assertTrue
+
+class BuildKotlinToolingMetadataTest {
+
+ private val project = ProjectBuilder.builder().build() as ProjectInternal
+ private val multiplatform get() = project.extensions.getByType(KotlinMultiplatformExtension::class.java)
+ private val js get() = project.extensions.getByType(KotlinJsProjectExtension::class.java)
+
+ @Test
+ fun `multiplatform empty setup`() {
+ project.plugins.apply("kotlin-multiplatform")
+
+ val metadata = getKotlinToolingMetadata()
+
+ assertEquals("Gradle", metadata.buildSystem)
+ assertEquals(project.gradle.gradleVersion, metadata.buildSystemVersion)
+ assertEquals(KotlinMultiplatformPluginWrapper::class.java.canonicalName, metadata.buildPlugin)
+ assertEquals(project.getKotlinPluginVersion().toString(), metadata.buildPluginVersion)
+ assertEquals(1, metadata.projectTargets.size, "Expected one target (metadata)")
+ assertTrue(
+ KotlinMetadataTarget::class.java.isAssignableFrom(Class.forName(metadata.projectTargets.single().target)),
+ "Expect target to be implement ${KotlinMetadataTarget::class.simpleName}"
+ )
+ assertEquals(
+ KotlinPlatformType.common.name, metadata.projectTargets.single().platformType
+ )
+ assertTrue(metadata.toJsonString().isNotBlank(), "Expected non blank json representation")
+ }
+
+ @Test
+ fun `multiplatform JS JVM Android setup`() {
+ project.plugins.apply("com.android.application")
+ project.plugins.apply("kotlin-multiplatform")
+
+ val android = project.extensions.getByType(BaseExtension::class.java)
+ val kotlin = multiplatform
+
+ android.compileSdkVersion(28)
+ kotlin.android()
+ kotlin.jvm()
+ kotlin.js {
+ nodejs()
+ browser()
+ }
+
+ project.evaluate()
+
+ val metadata = getKotlinToolingMetadata()
+ assertEquals(KotlinMultiplatformPluginWrapper::class.java.canonicalName, metadata.buildPlugin)
+
+ assertEquals(
+ listOf(KotlinPlatformType.common, KotlinPlatformType.androidJvm, KotlinPlatformType.jvm, KotlinPlatformType.js)
+ .map { it.name }.sorted(),
+ metadata.projectTargets.map { it.platformType }.sorted()
+ )
+ }
+
+ @Test
+ fun `multiplatform Android target with different source and target compatibility`() {
+ project.plugins.apply("kotlin-multiplatform")
+ project.plugins.apply("com.android.application")
+ val android = project.extensions.getByType(BaseExtension::class.java)
+ val kotlin = multiplatform
+ android.compileSdkVersion(28)
+ kotlin.android()
+ android.compileOptions.setSourceCompatibility(JavaVersion.VERSION_1_6)
+ android.compileOptions.setTargetCompatibility(JavaVersion.VERSION_1_8)
+ project.evaluate()
+
+ val androidTargetMetadata = getKotlinToolingMetadata()
+ .projectTargets.single { it.platformType == KotlinPlatformType.androidJvm.name }
+
+ assertEquals("1.6", androidTargetMetadata.extras["sourceCompatibility"])
+ assertEquals("1.8", androidTargetMetadata.extras["targetCompatibility"])
+ }
+
+ @Test
+ fun `multiplatform JVM with different targets`() {
+ project.plugins.apply("kotlin-multiplatform")
+ val kotlin = multiplatform
+ val jvm = kotlin.jvm()
+ jvm.compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME).kotlinOptions.jvmTarget = "12"
+ jvm.compilations.getByName(KotlinCompilation.TEST_COMPILATION_NAME).kotlinOptions.jvmTarget = "10"
+
+ assertEquals(
+ "12", getKotlinToolingMetadata().projectTargets
+ .single { it.platformType == KotlinPlatformType.jvm.name }.extras["jvmTarget"],
+ "Expected jvmTarget of main compilation"
+ )
+ }
+
+ @Test
+ fun `multiplatform with native target`() {
+ project.plugins.apply("kotlin-multiplatform")
+ val kotlin = multiplatform
+ kotlin.linuxX64()
+
+ val metadata = getKotlinToolingMetadata()
+ val linuxTarget = metadata.projectTargets.single { it.platformType == KotlinPlatformType.native.name }
+ assertEquals(KonanTarget.LINUX_X64.name, linuxTarget.extras["konanTarget"])
+ }
+
+ @Test
+ fun js() {
+ project.plugins.apply("org.jetbrains.kotlin.js")
+ val kotlin = js
+ kotlin.js { nodejs() }
+
+ val metadata = getKotlinToolingMetadata()
+ assertEquals(org.jetbrains.kotlin.gradle.plugin.KotlinJsPluginWrapper::class.java.canonicalName, metadata.buildPlugin)
+
+ val jsTarget = metadata.projectTargets.single { it.platformType == KotlinPlatformType.js.name }
+ assertEquals("true", jsTarget.extras["isNodejsConfigured"])
+ assertEquals("false", jsTarget.extras["isBrowserConfigured"])
+ }
+
+ @Test
+ fun jvm() {
+ project.plugins.apply("org.jetbrains.kotlin.jvm")
+ val metadata = getKotlinToolingMetadata()
+ assertEquals(org.jetbrains.kotlin.gradle.plugin.KotlinPluginWrapper::class.java.canonicalName, metadata.buildPlugin)
+ }
+
+ private fun getKotlinToolingMetadata(): KotlinToolingMetadata {
+ val task = project.tasks.named(BuildKotlinToolingMetadataTask.defaultTaskName, BuildKotlinToolingMetadataTask::class.java).get()
+ return task.getKotlinToolingMetadata()
+ }
+}
diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt
index 294a8ba0796..89e7fe99d44 100755
--- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt
+++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt
@@ -24,7 +24,6 @@ import org.gradle.api.plugins.MavenPluginConvention
import org.gradle.api.provider.Provider
import org.gradle.api.publish.PublishingExtension
import org.gradle.api.publish.maven.MavenPom
-import org.gradle.api.artifacts.maven.MavenPom as OldMavenPom
import org.gradle.api.publish.maven.MavenPublication
import org.gradle.api.tasks.*
import org.gradle.api.tasks.compile.AbstractCompile
@@ -49,6 +48,7 @@ import java.io.File
import java.net.URL
import java.util.concurrent.Callable
import java.util.jar.Manifest
+import org.gradle.api.artifacts.maven.MavenPom as OldMavenPom
const val PLUGIN_CLASSPATH_CONFIGURATION_NAME = "kotlinCompilerPluginClasspath"
const val NATIVE_COMPILER_PLUGIN_CLASSPATH_CONFIGURATION_NAME = "kotlinNativeCompilerPluginClasspath"
diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt
index 065af6fe07f..cd0ad69148d 100644
--- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt
+++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt
@@ -39,6 +39,7 @@ import org.jetbrains.kotlin.gradle.tasks.KOTLIN_COMPILER_EMBEDDABLE
import org.jetbrains.kotlin.gradle.tasks.KOTLIN_KLIB_COMMONIZER_EMBEDDABLE
import org.jetbrains.kotlin.gradle.tasks.KOTLIN_MODULE_GROUP
import org.jetbrains.kotlin.gradle.testing.internal.KotlinTestsRegistry
+import org.jetbrains.kotlin.gradle.tooling.BuildKotlinToolingMetadataTask
import org.jetbrains.kotlin.gradle.utils.checkGradleCompatibility
import org.jetbrains.kotlin.gradle.utils.loadPropertyFromResources
import org.jetbrains.kotlin.statistics.metrics.StringMetrics
@@ -90,6 +91,7 @@ abstract class KotlinBasePluginWrapper : Plugin {
}
project.extensions.add(KotlinTestsRegistry.PROJECT_EXTENSION_NAME, createTestRegistry(project))
+ project.tasks.register(BuildKotlinToolingMetadataTask.defaultTaskName, BuildKotlinToolingMetadataTask::class.java)
val plugin = getPlugin(project, kotlinGradleBuildServices)
@@ -210,13 +212,9 @@ open class KotlinJsPluginWrapper @Inject constructor(
}
open class KotlinMultiplatformPluginWrapper @Inject constructor(
- private val featurePreviews: FeaturePreviews
) : KotlinBasePluginWrapper() {
override fun getPlugin(project: Project, kotlinGradleBuildServices: KotlinGradleBuildServices): Plugin =
- KotlinMultiplatformPlugin(
- kotlinPluginVersion,
- featurePreviews
- )
+ KotlinMultiplatformPlugin(kotlinPluginVersion)
override val projectExtensionClass: KClass
get() = KotlinMultiplatformExtension::class
diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinProperties.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinProperties.kt
index d24d378bbc1..bc561074fe0 100644
--- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinProperties.kt
+++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinProperties.kt
@@ -126,6 +126,9 @@ internal class PropertiesProvider private constructor(private val project: Proje
val enableCompatibilityMetadataVariant: Boolean
get() = booleanProperty("kotlin.mpp.enableCompatibilityMetadataVariant") ?: true
+ val enableKotlinToolingMetadataArtifact: Boolean
+ get() = booleanProperty("kotlin.mpp.enableKotlinToolingMetadataArtifact") ?: true
+
val mppStabilityNoWarn: Boolean?
get() = booleanProperty(KotlinMultiplatformPlugin.STABILITY_NOWARN_FLAG)
diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMultiplatformPlugin.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMultiplatformPlugin.kt
index de8372e63ff..e0d1fa365bf 100644
--- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMultiplatformPlugin.kt
+++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMultiplatformPlugin.kt
@@ -44,8 +44,7 @@ import org.jetbrains.kotlin.konan.target.presetName
import org.jetbrains.kotlin.statistics.metrics.StringMetrics
class KotlinMultiplatformPlugin(
- private val kotlinPluginVersion: String,
- private val featurePreviews: FeaturePreviews // TODO get rid of this internal API usage once we don't need it
+ private val kotlinPluginVersion: String
) : Plugin {
private class TargetFromPresetExtension(val targetsContainer: KotlinTargetsContainerWithPresets) {
diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/Publishing.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/Publishing.kt
index 4b6556bb3e8..781de3a1912 100644
--- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/Publishing.kt
+++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/Publishing.kt
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.gradle.plugin.sources.KotlinDependencyScope
import org.jetbrains.kotlin.gradle.plugin.sources.sourceSetDependencyConfigurationByScope
import org.jetbrains.kotlin.gradle.plugin.whenEvaluated
import org.jetbrains.kotlin.gradle.targets.metadata.isKotlinGranularMetadataEnabled
+import org.jetbrains.kotlin.gradle.tooling.BuildKotlinToolingMetadataTask
internal fun configurePublishingWithMavenPublish(project: Project) = project.pluginManager.withPlugin("maven-publish") {
project.extensions.configure(PublishingExtension::class.java) { publishing ->
@@ -43,6 +44,20 @@ private fun createRootPublication(project: Project, publishing: PublishingExtens
kotlinSoftwareComponent.sourcesArtifacts.forEach { sourceArtifact ->
artifact(sourceArtifact)
}
+
+ addKotlinToolingMetadataArtifactIfNeeded(project)
+ }
+}
+
+private fun MavenPublication.addKotlinToolingMetadataArtifactIfNeeded(project: Project) {
+ if (!PropertiesProvider(project).enableKotlinToolingMetadataArtifact) return
+ val buildKotlinToolingMetadataTask = project.tasks.withType(BuildKotlinToolingMetadataTask::class.java)
+ .named(BuildKotlinToolingMetadataTask.defaultTaskName) ?: return
+
+
+ artifact(buildKotlinToolingMetadataTask.flatMap { it.outputFile }) { artifact ->
+ artifact.classifier = "kotlin-tooling-metadata"
+ artifact.builtBy(buildKotlinToolingMetadataTask)
}
}
diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tooling/BuildKotlinToolingMetadataTask.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tooling/BuildKotlinToolingMetadataTask.kt
new file mode 100644
index 00000000000..509ba1a9249
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tooling/BuildKotlinToolingMetadataTask.kt
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2010-2021 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 org.jetbrains.kotlin.gradle.tooling
+
+import com.android.build.gradle.BaseExtension
+import org.gradle.api.DefaultTask
+import org.gradle.api.provider.Property
+import org.gradle.api.tasks.Input
+import org.gradle.api.tasks.Internal
+import org.gradle.api.tasks.OutputFile
+import org.gradle.api.tasks.TaskAction
+import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
+import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension
+import org.jetbrains.kotlin.gradle.dsl.KotlinSingleTargetExtension
+import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
+import org.jetbrains.kotlin.gradle.plugin.KotlinBasePluginWrapper
+import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
+import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
+import org.jetbrains.kotlin.gradle.plugin.getKotlinPluginVersion
+import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget
+import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
+import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsSubTargetContainerDsl
+import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget
+import org.jetbrains.kotlin.gradle.targets.metadata.isCompatibilityMetadataVariantEnabled
+import org.jetbrains.kotlin.gradle.targets.metadata.isKotlinGranularMetadataEnabled
+import org.jetbrains.kotlin.tooling.KotlinToolingMetadata
+import org.jetbrains.kotlin.tooling.toJsonString
+import java.io.File
+
+open class BuildKotlinToolingMetadataTask : DefaultTask() {
+
+ companion object {
+ const val defaultTaskName: String = "buildKotlinToolingMetadata"
+ }
+
+ init {
+ group = "build"
+ description = "Build metadata json file containing information about the used Kotlin tooling"
+ }
+
+
+ @get:OutputFile
+ val outputFile: Property = project.objects.property(File::class.java)
+ .convention(project.buildDir.resolve("kotlinToolingMetadata").resolve("kotlin-tooling-metadata.json"))
+
+ @Internal
+ fun getKotlinToolingMetadata(): KotlinToolingMetadata {
+ return project.kotlinExtension.getKotlinToolingMetadata()
+ }
+
+ @Input
+ internal fun getKotlinToolingMetadataJson(): String = getKotlinToolingMetadata().toJsonString()
+
+ @TaskAction
+ internal fun createToolingMetadataFile() {
+ val outputFile = outputFile.orNull ?: return
+ outputFile.parentFile.mkdirs()
+ outputFile.writeText(getKotlinToolingMetadataJson())
+ }
+}
+
+private fun KotlinProjectExtension.getKotlinToolingMetadata(): KotlinToolingMetadata {
+ return KotlinToolingMetadata(
+ buildSystem = "Gradle",
+ buildSystemVersion = project.gradle.gradleVersion,
+ buildPlugin = project.plugins.withType(KotlinBasePluginWrapper::class.java).joinToString(";") { it.javaClass.canonicalName },
+ buildPluginVersion = project.getKotlinPluginVersion() ?: throw IllegalStateException(
+ "Failed to infer Kotlin Plugin version"
+ ),
+ projectSettings = buildProjectSettings(),
+ projectTargets = buildProjectTargets()
+ )
+}
+
+private fun KotlinProjectExtension.buildProjectSettings(): KotlinToolingMetadata.ProjectSettings {
+ return KotlinToolingMetadata.ProjectSettings(
+ isHmppEnabled = project.isKotlinGranularMetadataEnabled,
+ isCompatibilityMetadataVariantEnabled = project.isCompatibilityMetadataVariantEnabled
+ )
+}
+
+private fun KotlinProjectExtension.buildProjectTargets(): List {
+ val targets = when (this) {
+ is KotlinMultiplatformExtension -> this.targets.toSet()
+ is KotlinSingleTargetExtension -> setOf(this.target)
+ else -> emptySet()
+ }
+
+ return targets.map { target -> buildTargetMetadata(target) }
+}
+
+private fun buildTargetMetadata(target: KotlinTarget): KotlinToolingMetadata.ProjectTargetMetadata {
+ return KotlinToolingMetadata.ProjectTargetMetadata(
+ target = target.javaClass.canonicalName,
+ platformType = target.platformType.name,
+ extras = buildTargetMetadataExtras(target)
+ )
+}
+
+private fun buildTargetMetadataExtras(target: KotlinTarget): Map {
+ val extras = mutableMapOf()
+ 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
+ }
+ }
+ return extras.toMap()
+}
diff --git a/libraries/tools/kotlin-tooling-metadata/build.gradle.kts b/libraries/tools/kotlin-tooling-metadata/build.gradle.kts
new file mode 100644
index 00000000000..f4e9a0df2d9
--- /dev/null
+++ b/libraries/tools/kotlin-tooling-metadata/build.gradle.kts
@@ -0,0 +1,15 @@
+plugins {
+ java
+ kotlin("jvm")
+ id("jps-compatible")
+}
+
+publish()
+sourcesJar()
+javadocJar()
+
+dependencies {
+ implementation(kotlinStdlib())
+ implementation("com.google.code.gson:gson:${rootProject.extra["versions.jar.gson"]}")
+ testImplementation(project(":kotlin-test:kotlin-test-junit"))
+}
diff --git a/libraries/tools/kotlin-tooling-metadata/src/main/kotlin/org/jetbrains/kotlin/tooling/JsonConversion.kt b/libraries/tools/kotlin-tooling-metadata/src/main/kotlin/org/jetbrains/kotlin/tooling/JsonConversion.kt
new file mode 100644
index 00000000000..2fe74043106
--- /dev/null
+++ b/libraries/tools/kotlin-tooling-metadata/src/main/kotlin/org/jetbrains/kotlin/tooling/JsonConversion.kt
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2010-2021 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 org.jetbrains.kotlin.tooling
+
+import com.google.gson.*
+import org.jetbrains.kotlin.tooling.KotlinToolingMetadataParsingResult.Failure
+import org.jetbrains.kotlin.tooling.KotlinToolingMetadataParsingResult.Success
+
+fun KotlinToolingMetadata.toJsonString(): String {
+ val gson = GsonBuilder().setPrettyPrinting().create()
+ return gson.toJson(toJsonObject())
+}
+
+internal fun KotlinToolingMetadata.toJsonObject(): JsonObject {
+ return JsonObject().apply {
+ addProperty("buildSystem", buildSystem)
+ addProperty("buildSystemVersion", buildSystemVersion)
+ addProperty("buildPlugin", buildPlugin)
+ addProperty("buildPluginVersion", buildPluginVersion)
+ add("projectSettings", projectSettings.toJsonObject())
+ add("projectTargets", projectTargets.toJsonArray())
+ }
+}
+
+internal fun KotlinToolingMetadata.ProjectSettings.toJsonObject(): JsonObject {
+ return JsonObject().apply {
+ addProperty("isHmppEnabled", isHmppEnabled)
+ addProperty("isCompatibilityMetadataVariantEnabled", isCompatibilityMetadataVariantEnabled)
+ }
+}
+
+internal fun List.toJsonArray(): JsonArray {
+ return JsonArray().apply {
+ this@toJsonArray.forEach { targetMetadata ->
+ add(targetMetadata.toJsonObject())
+ }
+ }
+}
+
+internal 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)
+ }
+ })
+ }
+ }
+}
+
+sealed class KotlinToolingMetadataParsingResult {
+ data class Success(val value: KotlinToolingMetadata) : KotlinToolingMetadataParsingResult()
+ data class Failure(val reason: String) : KotlinToolingMetadataParsingResult()
+}
+
+fun KotlinToolingMetadata.Companion.parseJson(json: String): KotlinToolingMetadataParsingResult {
+ val jsonElement = try {
+ JsonParser.parseString(json)
+ } catch (e: JsonParseException) {
+ return Failure("Invalid json: ${e.message}")
+ }
+ if (jsonElement !is JsonObject) {
+ return Failure("Expected JsonObject. Found ${json::class.java.canonicalName}")
+ }
+ return runCatching { jsonElement.toKotlinToolingMetadataOrThrow() }
+ .fold(
+ onSuccess = { Success(it) },
+ onFailure = { Failure("Failed parsing JsonObject: ${it.message}") }
+ )
+}
+
+fun KotlinToolingMetadata.Companion.parseJsonOrThrow(value: String): KotlinToolingMetadata {
+ return when (val result = parseJson(value)) {
+ is Success -> result.value
+ is Failure -> throw IllegalArgumentException(result.reason)
+ }
+}
+
+private fun JsonObject.toKotlinToolingMetadataOrThrow(): KotlinToolingMetadata {
+ return KotlinToolingMetadata(
+ buildSystem = getOrThrow("buildSystem").asString,
+ buildSystemVersion = getOrThrow("buildSystemVersion").asString,
+ buildPlugin = getOrThrow("buildPlugin").asString,
+ buildPluginVersion = getOrThrow("buildPluginVersion").asString,
+ projectSettings = getOrThrow("projectSettings").asJsonObject.toProjectSettingsOrThrow(),
+ projectTargets = getOrThrow("projectTargets").asJsonArray.map { it.asJsonObject.toTargetMetadataOrThrow() }
+ )
+}
+
+private fun JsonObject.toProjectSettingsOrThrow(): KotlinToolingMetadata.ProjectSettings {
+ return KotlinToolingMetadata.ProjectSettings(
+ isHmppEnabled = getOrThrow("isHmppEnabled").asBoolean,
+ isCompatibilityMetadataVariantEnabled = getOrThrow("isCompatibilityMetadataVariantEnabled").asBoolean
+ )
+}
+
+private fun JsonObject.toTargetMetadataOrThrow(): KotlinToolingMetadata.ProjectTargetMetadata {
+ return KotlinToolingMetadata.ProjectTargetMetadata(
+ target = getOrThrow("target").asString,
+ platformType = getOrThrow("platformType").asString,
+ extras = (get("extras") as? JsonObject)?.toTargetMetadataExtrasOrThrow() ?: emptyMap()
+ )
+}
+
+private fun JsonObject.toTargetMetadataExtrasOrThrow(): Map {
+ val map = mutableMapOf()
+ this.keySet().forEach { key ->
+ val primitive = this[key] as? JsonPrimitive
+ if (primitive != null) {
+ map[key] = primitive.asString
+ }
+ }
+ return map.toMap()
+}
+
+private fun JsonObject.getOrThrow(key: String): JsonElement {
+ return get(key) ?: throw IllegalArgumentException("Missing key: $key")
+}
diff --git a/libraries/tools/kotlin-tooling-metadata/src/main/kotlin/org/jetbrains/kotlin/tooling/KotlinToolingMetadata.kt b/libraries/tools/kotlin-tooling-metadata/src/main/kotlin/org/jetbrains/kotlin/tooling/KotlinToolingMetadata.kt
new file mode 100644
index 00000000000..e55233daf9c
--- /dev/null
+++ b/libraries/tools/kotlin-tooling-metadata/src/main/kotlin/org/jetbrains/kotlin/tooling/KotlinToolingMetadata.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2010-2021 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 org.jetbrains.kotlin.tooling
+
+
+data class KotlinToolingMetadata(
+ /**
+ * Build System used (e.g. Gradle, Maven, ...)
+ */
+ val buildSystem: String,
+ val buildSystemVersion: String,
+
+ /**
+ * Plugin used to build (e.g.
+ * - org.jetbrains.kotlin.gradle.plugin.mpp.KotlinMultiplatformPlugin
+ * - org.jetbrains.kotlin.gradle.targets.js.KotlinJsPlugin
+ * - ...
+ */
+ val buildPlugin: String,
+ val buildPluginVersion: String,
+
+ val projectSettings: ProjectSettings,
+ val projectTargets: List,
+) {
+
+ data class ProjectSettings(
+ val isHmppEnabled: Boolean,
+ val isCompatibilityMetadataVariantEnabled: Boolean,
+ )
+
+ data class ProjectTargetMetadata(
+ val target: String,
+ val platformType: String,
+ val extras: Map
+ )
+
+ companion object
+}
+
+
diff --git a/libraries/tools/kotlin-tooling-metadata/src/test/kotlin/org/jetbrains/kotlin/tooling/DeserializationFailureTest.kt b/libraries/tools/kotlin-tooling-metadata/src/test/kotlin/org/jetbrains/kotlin/tooling/DeserializationFailureTest.kt
new file mode 100644
index 00000000000..c58e3fac7dc
--- /dev/null
+++ b/libraries/tools/kotlin-tooling-metadata/src/test/kotlin/org/jetbrains/kotlin/tooling/DeserializationFailureTest.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2010-2021 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 org.jetbrains.kotlin.tooling
+
+import org.intellij.lang.annotations.Language
+import org.junit.Test
+import kotlin.test.assertFailsWith
+import kotlin.test.assertTrue
+
+
+class DeserializationFailureTest {
+ @Test
+ fun sample1() {
+ val result = KotlinToolingMetadata.parseJson("")
+ assertTrue(
+ result is KotlinToolingMetadataParsingResult.Failure,
+ "Expected empty String to produce Failure. Actual: $result"
+ )
+
+ assertFailsWith { KotlinToolingMetadata.parseJsonOrThrow("") }
+ }
+
+ @Test
+ fun `sample2 missing build pluginVersion`() {
+ @Language("JSON") val json =
+ """
+ {
+ "buildSystem": "Gradle",
+ "buildSystemVersion": "6.7",
+ "buildPlugin": "org.jetbrains.kotlin.gradle.plugin.KotlinMultiplatformPluginWrapper",
+ "projectSettings": {
+ "isHmppEnabled": false,
+ "isCompatibilityMetadataVariantEnabled": true
+ },
+ "projectTargets": []
+ }
+ """.trimIndent()
+
+ val result = KotlinToolingMetadata.parseJson(json)
+ assertTrue(
+ result is KotlinToolingMetadataParsingResult.Failure,
+ "Expected parsing failure, because of missing pluginVersion. Actual: $result"
+ )
+
+ assertFailsWith { KotlinToolingMetadata.parseJsonOrThrow(json) }
+ }
+}
diff --git a/libraries/tools/kotlin-tooling-metadata/src/test/kotlin/org/jetbrains/kotlin/tooling/DeserializeStringTest.kt b/libraries/tools/kotlin-tooling-metadata/src/test/kotlin/org/jetbrains/kotlin/tooling/DeserializeStringTest.kt
new file mode 100644
index 00000000000..b6340468e6c
--- /dev/null
+++ b/libraries/tools/kotlin-tooling-metadata/src/test/kotlin/org/jetbrains/kotlin/tooling/DeserializeStringTest.kt
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2010-2021 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 org.jetbrains.kotlin.tooling
+
+import junit.framework.Assert.assertFalse
+import junit.framework.Assert.assertTrue
+import org.intellij.lang.annotations.Language
+import org.junit.Test
+import kotlin.test.assertEquals
+
+/*
+ * Copyright 2010-2021 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 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": {
+ "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"
+ }
+ ]
+ }
+ """.trimIndent()
+
+ val metadata = KotlinToolingMetadata.parseJsonOrThrow(json)
+ assertEquals("Gradle", metadata.buildSystem)
+ assertEquals("6.7", metadata.buildSystemVersion)
+ assertEquals("org.jetbrains.kotlin.gradle.plugin.KotlinMultiplatformPluginWrapper", metadata.buildPlugin)
+ assertEquals("1.5.255-SNAPSHOT", metadata.buildPluginVersion)
+ assertFalse(metadata.projectSettings.isHmppEnabled)
+ assertTrue(metadata.projectSettings.isCompatibilityMetadataVariantEnabled)
+ assertEquals(4, 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"])
+
+ 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"])
+
+ 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"])
+
+ 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")
+ }
+}
+
diff --git a/libraries/tools/kotlin-tooling-metadata/src/test/kotlin/org/jetbrains/kotlin/tooling/SerializeAndDeserializeTest.kt b/libraries/tools/kotlin-tooling-metadata/src/test/kotlin/org/jetbrains/kotlin/tooling/SerializeAndDeserializeTest.kt
new file mode 100644
index 00000000000..e8dc9d7e587
--- /dev/null
+++ b/libraries/tools/kotlin-tooling-metadata/src/test/kotlin/org/jetbrains/kotlin/tooling/SerializeAndDeserializeTest.kt
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2010-2021 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 org.jetbrains.kotlin.tooling
+
+import org.jetbrains.kotlin.tooling.KotlinToolingMetadata.ProjectTargetMetadata
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+/*
+ * Copyright 2010-2021 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 SerializeAndDeserializeTest {
+
+ @Test
+ fun sample1() = assertDeserializedMatchesOrigin(
+ defaultKotlinToolingMetadata()
+ )
+
+ @Test
+ fun sample2() = assertDeserializedMatchesOrigin(
+ defaultKotlinToolingMetadata().copy(
+ projectTargets = listOf(
+ ProjectTargetMetadata(
+ target = "generic target",
+ platformType = "generic platform type",
+ extras = mapOf(
+ "extra0" to "extra value0",
+ "extra1" to "extra value1"
+ )
+ )
+ )
+ )
+ )
+
+ @Test
+ fun sample3() = assertDeserializedMatchesOrigin(
+ defaultKotlinToolingMetadata().copy(
+ projectTargets = listOf(
+ ProjectTargetMetadata(
+ target = "generic target",
+ platformType = "generic platform type",
+ extras = mapOf(
+ "extra0" to "{ some extra value %\" with, chars to escape",
+ "extra1" to "extra value1"
+ )
+ ),
+ ProjectTargetMetadata(
+ target = "generic target 2 (with no extras)",
+ platformType = "generic platform type 2",
+ extras = emptyMap()
+ )
+ )
+ )
+ )
+}
+
+private fun assertDeserializedMatchesOrigin(origin: KotlinToolingMetadata) {
+ val json = origin.toJsonString()
+ val deserialized = KotlinToolingMetadata.parseJsonOrThrow(json)
+ assertEquals(origin, deserialized)
+}
+
+private fun defaultKotlinToolingMetadata(): KotlinToolingMetadata {
+ return KotlinToolingMetadata(
+ buildSystem = "generic build system",
+ buildSystemVersion = "v1.0 (build system)",
+ buildPlugin = "generic build plugin",
+ buildPluginVersion = "v1.0 (build plugin)",
+ projectSettings = KotlinToolingMetadata.ProjectSettings(
+ isHmppEnabled = false,
+ isCompatibilityMetadataVariantEnabled = true
+ ),
+ projectTargets = emptyList()
+ )
+}
diff --git a/settings.gradle b/settings.gradle
index 0065ce39b96..2b2e6ff975c 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -253,6 +253,7 @@ include ":benchmarks",
":kotlin-gradle-plugin-model",
":kotlin-gradle-plugin-test-utils-embeddable",
":kotlin-gradle-plugin-integration-tests",
+ ":kotlin-tooling-metadata",
":kotlin-allopen",
":kotlin-noarg",
":kotlin-sam-with-receiver",
@@ -520,6 +521,7 @@ project(':kotlin-gradle-plugin').projectDir = "$rootDir/libraries/tools/kotlin-g
project(':kotlin-gradle-plugin-model').projectDir = "$rootDir/libraries/tools/kotlin-gradle-plugin-model" as File
project(':kotlin-gradle-plugin-test-utils-embeddable').projectDir = "$rootDir/libraries/tools/kotlin-gradle-plugin-test-utils-embeddable" as File
project(':kotlin-gradle-plugin-integration-tests').projectDir = "$rootDir/libraries/tools/kotlin-gradle-plugin-integration-tests" as File
+project(':kotlin-tooling-metadata').projectDir = "$rootDir/libraries/tools/kotlin-tooling-metadata" as File
project(':kotlin-allopen').projectDir = "$rootDir/libraries/tools/kotlin-allopen" as File
project(':kotlin-noarg').projectDir = "$rootDir/libraries/tools/kotlin-noarg" as File
project(':kotlin-sam-with-receiver').projectDir = "$rootDir/libraries/tools/kotlin-sam-with-receiver" as File