[Gradle][KPM] Create BuiltKotlinMetadata task per KPM Module

Use main module for tests

KT-49704
This commit is contained in:
Anton Lakotka
2022-03-16 12:32:32 +01:00
parent b579bf20cf
commit 7af5a93767
6 changed files with 167 additions and 77 deletions
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.gradle
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinGradleModule
import org.jetbrains.kotlin.gradle.tooling.BuildKotlinToolingMetadataTask
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.tooling.KotlinToolingMetadata
@@ -17,7 +18,19 @@ import kotlin.test.assertEquals
class KotlinToolingMetadataMppIT : BaseGradleIT() {
override val defaultGradleVersion: GradleVersionRequired = GradleVersionRequired.FOR_MPP_SUPPORT
private val defaultKotlinToolingMetadataJsonPath = "build/kotlinToolingMetadata/kotlin-tooling-metadata.json"
private val defaultKotlinToolingMetadataJsonPath get() =
if (isKpmModelMappingEnabled) {
"build/kotlinToolingMetadata/main/kotlin-tooling-metadata.json"
} else {
"build/kotlinToolingMetadata/kotlin-tooling-metadata.json"
}
private val buildKotlinToolingMetadataTaskName get() =
if (isKpmModelMappingEnabled) {
BuildKotlinToolingMetadataTask.taskNameForKotlinModule(KotlinGradleModule.MAIN_MODULE_NAME)
} else {
BuildKotlinToolingMetadataTask.defaultTaskName
}
@Test
fun `new-mpp-published`() = with(transformProjectWithPluginsDsl("new-mpp-published")) {
@@ -25,17 +38,17 @@ class KotlinToolingMetadataMppIT : BaseGradleIT() {
build("publish") {
assertSuccessful()
assertTasksExecuted(
":${BuildKotlinToolingMetadataTask.defaultTaskName}"
)
assertTasksExecuted(":$buildKotlinToolingMetadataTaskName")
/* 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
listOfNotNull(
KotlinPlatformType.common.name.takeIf { !isKpmModelMappingEnabled },
KotlinPlatformType.jvm.name,
KotlinPlatformType.js.name,
KotlinPlatformType.native.name
).sorted(),
metadata.projectTargets.map { it.platformType }.sorted()
)
@@ -55,7 +68,7 @@ class KotlinToolingMetadataMppIT : BaseGradleIT() {
build("publish") {
assertSuccessful()
// Nothing changed. Should be up-to-date
assertTasksUpToDate(":${BuildKotlinToolingMetadataTask.defaultTaskName}")
assertTasksUpToDate(":$buildKotlinToolingMetadataTaskName")
}
// Adding macos target
@@ -68,7 +81,7 @@ class KotlinToolingMetadataMppIT : BaseGradleIT() {
build("publish") {
assertSuccessful()
assertTasksExecuted(":${BuildKotlinToolingMetadataTask.defaultTaskName}")
assertTasksExecuted(":$buildKotlinToolingMetadataTaskName")
val metadata = KotlinToolingMetadata.parseJsonOrThrow(projectDir.resolve(defaultKotlinToolingMetadataJsonPath).readText())
assertEquals(
listOf(KonanTarget.LINUX_X64.name, KonanTarget.MACOS_X64.name).sorted(),
@@ -85,7 +98,7 @@ class KotlinToolingMetadataMppIT : BaseGradleIT() {
assertSuccessful()
assertNoSuchFile(defaultKotlinToolingMetadataJsonPath)
assertTasksNotRealized(
":${BuildKotlinToolingMetadataTask.defaultTaskName}"
":$buildKotlinToolingMetadataTaskName"
)
}
}
@@ -96,9 +109,9 @@ class KotlinToolingMetadataMppIT : BaseGradleIT() {
projectDir.resolve("gradle.properties").appendText("\nkotlin.mpp.enableKotlinToolingMetadataArtifact=true")
build(BuildKotlinToolingMetadataTask.defaultTaskName) {
assertSuccessful()
assertTasksExecuted(":app:${BuildKotlinToolingMetadataTask.defaultTaskName}")
assertTasksExecuted(":base:${BuildKotlinToolingMetadataTask.defaultTaskName}")
assertTasksExecuted(":lib:${BuildKotlinToolingMetadataTask.defaultTaskName}")
assertTasksExecuted(":app:$buildKotlinToolingMetadataTaskName")
assertTasksExecuted(":base:$buildKotlinToolingMetadataTaskName")
assertTasksExecuted(":lib:$buildKotlinToolingMetadataTaskName")
}
}
}
@@ -29,6 +29,7 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.*
import org.jetbrains.kotlin.gradle.targets.js.KotlinJsTarget
import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget
import org.jetbrains.kotlin.gradle.tooling.BuildKotlinToolingMetadataTask
import org.jetbrains.kotlin.gradle.tooling.buildKotlinToolingMetadataForMainKpmModuleTask
import org.jetbrains.kotlin.gradle.tooling.buildKotlinToolingMetadataTask
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.library.KotlinAbiVersion
@@ -41,6 +42,7 @@ import org.junit.runner.RunWith
import org.junit.runners.Parameterized
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue
@RunWith(Parameterized::class)
@@ -73,18 +75,18 @@ class BuildKotlinToolingMetadataTest {
assertEquals(project.gradle.gradleVersion, metadata.buildSystemVersion)
assertEquals(KotlinMultiplatformPluginWrapper::class.java.canonicalName, metadata.buildPlugin)
assertEquals(project.getKotlinPluginVersion(), metadata.buildPluginVersion)
assertEquals(1, metadata.projectTargets.size, "Expected one target (metadata)")
val targetMetadata = metadata.projectTargets.single()
val targetClass = if (kpmModelMappingEnabled) {
KotlinGradleFragment::class
if (kpmModelMappingEnabled) {
assertEquals(0, metadata.projectTargets.size, "Expected no targets in KPM")
} else {
KotlinMetadataTarget::class
assertEquals(1, metadata.projectTargets.size, "Expected one target (metadata)")
val targetMetadata = metadata.projectTargets.single()
val targetClass = KotlinMetadataTarget::class
assertTrue(
targetClass.java.isAssignableFrom(Class.forName(targetMetadata.target)),
"Expect target to implement ${targetClass.simpleName}"
)
assertEquals(common.name, targetMetadata.platformType)
}
assertTrue(
targetClass.java.isAssignableFrom(Class.forName(targetMetadata.target)),
"Expect target to implement ${targetClass.simpleName}"
)
assertEquals(common.name, targetMetadata.platformType)
assertTrue(metadata.toJsonString().isNotBlank(), "Expected non blank json representation")
}
@@ -110,9 +112,14 @@ class BuildKotlinToolingMetadataTest {
val metadata = getKotlinToolingMetadata()
assertEquals(KotlinMultiplatformPluginWrapper::class.java.canonicalName, metadata.buildPlugin)
if (kpmModelMappingEnabled) {
assertFalse("Kotlin Tooling Metadata generated from KPM should not contain target of 'common' platform") {
metadata.projectTargets.any { it.platformType == common.name }
}
}
val expectedTargets = if (kpmModelMappingEnabled) {
mapOf(
common to KotlinGradleFragmentInternal::class,
androidJvm to LegacyMappedVariantWithRuntime::class,
jvm to KotlinJvmVariant::class,
js to LegacyMappedVariantWithRuntime::class,
@@ -257,8 +264,15 @@ class BuildKotlinToolingMetadataTest {
}
private fun getKotlinToolingMetadata(): KotlinToolingMetadata {
val task = project.buildKotlinToolingMetadataTask?.get() ?: error("No ${BuildKotlinToolingMetadataTask.defaultTaskName} task")
return task.kotlinToolingMetadata
if (!kpmModelMappingEnabled) {
val task = project.buildKotlinToolingMetadataTask?.get() ?: error("No ${BuildKotlinToolingMetadataTask.defaultTaskName} task")
return task.kotlinToolingMetadata
} else {
val task = project.buildKotlinToolingMetadataForMainKpmModuleTask
?.get()
?: error("No ${BuildKotlinToolingMetadataTask.defaultTaskName} for Main module task")
return task.kotlinToolingMetadata
}
}
companion object {
@@ -42,7 +42,7 @@ import org.jetbrains.kotlin.gradle.targets.js.npm.addNpmDependencyExtension
import org.jetbrains.kotlin.gradle.targets.native.internal.CInteropKlibLibraryElements
import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile
import org.jetbrains.kotlin.gradle.testing.internal.KotlinTestsRegistry
import org.jetbrains.kotlin.gradle.tooling.buildKotlinToolingMetadataTask
import org.jetbrains.kotlin.gradle.tooling.registerBuildKotlinToolingMetadataTask
import org.jetbrains.kotlin.gradle.utils.checkGradleCompatibility
import org.jetbrains.kotlin.gradle.utils.getOrPut
import org.jetbrains.kotlin.gradle.utils.loadPropertyFromResources
@@ -120,7 +120,7 @@ abstract class KotlinBasePluginWrapper : Plugin<Project> {
project.addNpmDependencyExtension()
project.buildKotlinToolingMetadataTask
project.registerBuildKotlinToolingMetadataTask()
}
private fun addKotlinCompilerConfiguration(project: Project) {
@@ -44,14 +44,14 @@ internal fun setupPublicationForModule(module: KotlinGradleModule) {
it.fromResolutionOf(metadataDependencyConfiguration)
}
}
publication.setupKotlinToolingMetadataIfNeeded(project)
publication.setupKotlinToolingMetadataIfNeeded(module)
}
}
}
}
private fun MavenPublication.setupKotlinToolingMetadataIfNeeded(project: Project) {
val buildKotlinToolingMetadataTask = project.buildKotlinToolingMetadataTask ?: return
private fun MavenPublication.setupKotlinToolingMetadataIfNeeded(module: KotlinGradleModule) {
val buildKotlinToolingMetadataTask = module.buildKotlinToolingMetadataTask ?: return
artifact(buildKotlinToolingMetadataTask.map { it.outputFile }) { artifact ->
artifact.classifier = "kotlin-tooling-metadata"
@@ -8,7 +8,9 @@ package org.jetbrains.kotlin.gradle.tooling
import com.android.build.gradle.BaseExtension
import org.gradle.api.DefaultTask
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.internal.GeneratedSubclass
import org.gradle.api.provider.Property
import org.gradle.api.tasks.*
import org.jetbrains.kotlin.compilerRunner.konanVersion
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
@@ -16,6 +18,7 @@ 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.*
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.Companion.kotlinPropertiesProvider
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.*
@@ -24,29 +27,92 @@ 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.gradle.tasks.locateTask
import org.jetbrains.kotlin.gradle.tasks.registerTask
import org.jetbrains.kotlin.gradle.tasks.dependsOn
import org.jetbrains.kotlin.gradle.tasks.locateOrRegisterTask
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
import org.jetbrains.kotlin.library.KotlinAbiVersion
import org.jetbrains.kotlin.tooling.KotlinToolingMetadata
import org.jetbrains.kotlin.tooling.toJsonString
import java.io.File
/**
* Register
*/
internal fun Project.registerBuildKotlinToolingMetadataTask() {
if (!project.kotlinPropertiesProvider.enableKotlinToolingMetadataArtifact) return
if (hasKpmModel) {
buildKotlinToolingMetadataForAllKpmModulesTask
} else {
buildKotlinToolingMetadataTask
}
}
internal val Project.buildKotlinToolingMetadataForMainKpmModuleTask: TaskProvider<BuildKotlinToolingMetadataTask.FromKpmModule>?
get() {
require(hasKpmModel)
val mainModule = kpmModules.getByName(KotlinGradleModule.MAIN_MODULE_NAME)
return mainModule.buildKotlinToolingMetadataTask
}
private val Project.buildKotlinToolingMetadataForAllKpmModulesTask
get() = locateOrRegisterTask<Task>(BuildKotlinToolingMetadataTask.defaultTaskName) { task ->
task.group = "build"
task.description = "Build metadata json file containing information about the used Kotlin tooling"
}
/**
* The default task managed by the Kotlin Gradle plugin or `null` if the task is disabled.
* @see [PropertiesProvider.enableKotlinToolingMetadataArtifact]
*/
internal val Project.buildKotlinToolingMetadataTask: TaskProvider<BuildKotlinToolingMetadataTask>?
internal val Project.buildKotlinToolingMetadataTask: TaskProvider<BuildKotlinToolingMetadataTask.FromKotlinExtension>?
get() {
if (!PropertiesProvider(this).enableKotlinToolingMetadataArtifact) return null
locateTask<BuildKotlinToolingMetadataTask>(BuildKotlinToolingMetadataTask.defaultTaskName)?.let { return it }
return registerTask(BuildKotlinToolingMetadataTask.defaultTaskName) { task ->
if (!kotlinPropertiesProvider.enableKotlinToolingMetadataArtifact) return null
val taskName = BuildKotlinToolingMetadataTask.defaultTaskName
return locateOrRegisterTask(taskName) { task ->
task.group = "build"
task.description = "Build metadata json file containing information about the used Kotlin tooling"
}
}
internal val KotlinGradleModule.buildKotlinToolingMetadataTask: TaskProvider<BuildKotlinToolingMetadataTask.FromKpmModule>?
get() {
if (!project.kotlinPropertiesProvider.enableKotlinToolingMetadataArtifact) return null
val taskName = BuildKotlinToolingMetadataTask.taskNameForKotlinModule(name)
return project.locateOrRegisterTask<BuildKotlinToolingMetadataTask.FromKpmModule>(taskName) { task ->
task.module.set(this)
task.group = "build"
task.description = "Build metadata json file containing information about the used Kotlin tooling in module $name"
}.also { task ->
project.buildKotlinToolingMetadataForAllKpmModulesTask.dependsOn(task)
}
}
abstract class BuildKotlinToolingMetadataTask : DefaultTask() {
abstract class FromKpmModule : BuildKotlinToolingMetadataTask() {
@get:Internal
abstract val module: Property<KotlinGradleModule>
override val outputDirectory: File
get() = project.buildDir.resolve("kotlinToolingMetadata").resolve(module.get().name)
override fun buildKotlinToolingMetadata() = module.get().getKotlinToolingMetadata()
}
abstract class FromKotlinExtension : BuildKotlinToolingMetadataTask() {
override val outputDirectory: File
get() = project.buildDir.resolve("kotlinToolingMetadata")
override fun buildKotlinToolingMetadata() = project.kotlinExtension.getKotlinToolingMetadata()
}
companion object {
/**
* The name of the default task managed by the Kotlin Gradle plugin.
@@ -54,27 +120,28 @@ abstract class BuildKotlinToolingMetadataTask : DefaultTask() {
* @see PropertiesProvider.enableKotlinToolingMetadataArtifact
*/
const val defaultTaskName: String = "buildKotlinToolingMetadata"
/**
* The name of the default [FromKpmModule] task of the given [KotlinGradleModule]'s name
*/
fun taskNameForKotlinModule(moduleName: String): String = lowerCamelCaseName(defaultTaskName, moduleName)
}
@get:OutputDirectory
val outputDirectory: File = project.buildDir.resolve("kotlinToolingMetadata")
abstract val outputDirectory: File
@get:Internal /* Covered by 'outputDirectory' */
val outputFile: File = outputDirectory.resolve("kotlin-tooling-metadata.json")
val outputFile: File get() = outputDirectory.resolve("kotlin-tooling-metadata.json")
@get:Internal
internal val kotlinToolingMetadata by lazy {
if (project.hasKpmModel) {
project.kpmModelContainer.getKotlinToolingMetadata(project)
} else {
project.kotlinExtension.getKotlinToolingMetadata()
}
}
internal val kotlinToolingMetadata by lazy { buildKotlinToolingMetadata() }
@get:Input
internal val kotlinToolingMetadataJson
get() = kotlinToolingMetadata.toJsonString()
protected abstract fun buildKotlinToolingMetadata(): KotlinToolingMetadata
@TaskAction
internal fun createToolingMetadataFile() {
/* Ensure output directory exists and is empty */
@@ -85,7 +152,7 @@ abstract class BuildKotlinToolingMetadataTask : DefaultTask() {
}
}
private fun KpmGradleProjectModelContainer.getKotlinToolingMetadata(project: Project): KotlinToolingMetadata {
private fun KotlinGradleModule.getKotlinToolingMetadata(): KotlinToolingMetadata {
return KotlinToolingMetadata(
schemaVersion = KotlinToolingMetadata.currentSchemaVersion,
buildSystem = "Gradle",
@@ -105,36 +172,26 @@ private fun Project.buildProjectSettings(): KotlinToolingMetadata.ProjectSetting
)
}
private fun KpmGradleProjectModelContainer.buildProjectTargets(): List<KotlinToolingMetadata.ProjectTargetMetadata> =
modules
.filter { it.isMain }
.flatMap { module ->
val commonTarget = KotlinToolingMetadata.ProjectTargetMetadata(
target = module.common.javaClass.canonicalName,
platformType = KotlinPlatformType.common.name,
extras = KotlinToolingMetadata.ProjectTargetMetadata.Extras()
private fun KotlinGradleModule.buildProjectTargets(): List<KotlinToolingMetadata.ProjectTargetMetadata> =
variants.map { variant ->
KotlinToolingMetadata.ProjectTargetMetadata(
target = variant.javaClass.canonicalName,
platformType = variant.platformType.name,
extras = KotlinToolingMetadata.ProjectTargetMetadata.Extras(
jvm = variant.jvmExtrasOrNull(),
android = variant.androidExtrasOrNull(),
js = variant.jsExtrasOrNull(),
native = variant.nativeExtrasOrNull()
)
module.variants.map { variant ->
KotlinToolingMetadata.ProjectTargetMetadata(
target = variant.javaClass.canonicalName,
platformType = variant.platformType.name,
extras = KotlinToolingMetadata.ProjectTargetMetadata.Extras(
jvm = variant.jvmExtrasOrNull(),
android = variant.androidExtrasOrNull(),
js = variant.jsExtrasOrNull(),
native = variant.nativeExtrasOrNull()
)
)
} + commonTarget
}.distinct()
)
}.distinct() // some variants may look identical. e.g. androidRelease and androidDebug, so just keep one of them.
private fun KotlinGradleVariant.jvmExtrasOrNull() =
when (this) {
is KotlinJvmVariant -> KotlinToolingMetadata.ProjectTargetMetadata.JvmExtras(
jvmTarget = compilationData.kotlinOptions.jvmTarget,
withJavaEnabled = false
)
jvmTarget = compilationData.kotlinOptions.jvmTarget,
withJavaEnabled = false
)
is LegacyMappedVariant -> buildJvmExtrasOrNull(compilation.target)
else -> null
}
@@ -154,10 +211,10 @@ private fun KotlinGradleVariant.jsExtrasOrNull() =
private fun KotlinGradleVariant.nativeExtrasOrNull() =
when (this) {
is KotlinNativeVariantInternal -> KotlinToolingMetadata.ProjectTargetMetadata.NativeExtras(
konanTarget = konanTarget.name,
konanVersion = project.konanVersion.toString(),
konanAbiVersion = KotlinAbiVersion.CURRENT.toString()
)
konanTarget = konanTarget.name,
konanVersion = project.konanVersion.toString(),
konanAbiVersion = KotlinAbiVersion.CURRENT.toString()
)
is LegacyMappedVariant -> buildNativeExtrasOrNull(compilation.target)
else -> null
}
@@ -7,10 +7,16 @@ package org.jetbrains.kotlin.gradle.tooling
import com.android.build.gradle.AppExtension
import org.gradle.api.Project
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.hasKpmModel
internal fun Project.includeKotlinToolingMetadataInApk() {
plugins.withId("com.android.application") {
val buildKotlinToolingMetadataTask = buildKotlinToolingMetadataTask ?: return@withId
val buildKotlinToolingMetadataTask = if (hasKpmModel) {
buildKotlinToolingMetadataForMainKpmModuleTask
} else {
buildKotlinToolingMetadataTask
} ?: return@withId
val android = project.extensions.getByName("android") as AppExtension
android.applicationVariants.configureEach { variant ->
if (!variant.buildType.isDebuggable) {