/*
* Copyright 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.room.gradle.integration
import androidx.room.gradle.RoomArgumentProvider
import androidx.room.gradle.RoomExtension
import androidx.room.gradle.RoomGradlePlugin.Companion.check
import androidx.room.gradle.RoomGradlePlugin.Companion.findPair
import com.google.devtools.ksp.gradle.KspTask
import org.gradle.api.Project
import org.gradle.api.Task
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.plugin.KotlinCompilation
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
internal class KotlinMultiplatformPluginIntegration(
private val common: CommonIntegration
) {
private val kgpPluginIds = listOf(
"org.jetbrains.kotlin.jvm",
"org.jetbrains.kotlin.android",
"org.jetbrains.kotlin.multiplatform"
)
fun withKotlin(project: Project, roomExtension: RoomExtension) {
kgpPluginIds.forEach { kgpPluginId ->
project.plugins.withId(kgpPluginId) {
val kotlin = project.extensions.getByName("kotlin") as KotlinProjectExtension
when (kotlin) {
is KotlinSingleTargetExtension<*> ->
configureRoomForKotlin(project, roomExtension, kotlin.target)
is KotlinMultiplatformExtension ->
kotlin.targets.configureEach {
configureRoomForKotlin(project, roomExtension, it)
}
}
}
}
}
private fun configureRoomForKotlin(
project: Project,
roomExtension: RoomExtension,
target: KotlinTarget
) {
// Android KSP tasks are configured through the Android Gradle Plugin variant APIs.
if (target.platformType == KotlinPlatformType.androidJvm) return
val configureTask: (Task) -> RoomArgumentProvider = { task ->
val schemaDirectories = roomExtension.schemaDirectories
val matchedPair = schemaDirectories.findPair(target.targetName)
?: schemaDirectories.findPair(RoomExtension.ALL_MATCH.actual)
project.check(matchedPair != null, isFatal = true) {
"No matching Room schema directory for the KSP target '${target.targetName}'."
}
val (matchedName, schemaDirectoryProvider) = matchedPair
val schemaDirectory = schemaDirectoryProvider.get()
project.check(schemaDirectory.isNotEmpty()) {
"The Room schema directory path for the KSP target '${target.targetName}' must " +
"not be empty."
}
common.configureTaskWithSchema(
project, roomExtension, matchedName, schemaDirectoryProvider.get(), task
)
}
target.compilations.configureEach { kotlinCompilation ->
configureKspTasks(project, kotlinCompilation, configureTask)
}
}
private fun configureKspTasks(
project: Project,
kotlinCompilation: KotlinCompilation<*>,
configureBlock: (Task) -> RoomArgumentProvider
) = project.plugins.withId("com.google.devtools.ksp") {
project.tasks.withType(KspTask::class.java) { task ->
// Same naming strategy as KSP, based off Kotlin compile task.
// https://github.com/google/ksp/blob/main/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/KspAATask.kt#L151
val kspTaskName = kotlinCompilation.compileKotlinTaskName.replace("compile", "ksp")
if (task.name == kspTaskName) {
val argProvider = configureBlock.invoke(task)
task.commandLineArgumentProviders.add(argProvider)
}
}
}
}