Skip to content

Commit

Permalink
[gradle] New Compose Compiler gradle plugin checks. (#4604)
Browse files Browse the repository at this point in the history
If KGP >= 2.0.0-RC2 new Compose gradle plugin has to be applied.

To throw a configuration error if it's not.
  • Loading branch information
terrakok committed Apr 25, 2024
1 parent 644c7c3 commit 7d456d4
Show file tree
Hide file tree
Showing 13 changed files with 162 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,58 @@ package org.jetbrains.compose
import org.gradle.api.Project
import org.gradle.api.provider.Provider
import org.jetbrains.compose.internal.ComposeCompilerArtifactProvider
import org.jetbrains.compose.internal.KOTLIN_JVM_PLUGIN_ID
import org.jetbrains.compose.internal.KOTLIN_MPP_PLUGIN_ID
import org.jetbrains.compose.internal.Version
import org.jetbrains.compose.internal.ideaIsInSyncProvider
import org.jetbrains.compose.internal.mppExtOrNull
import org.jetbrains.compose.internal.service.ConfigurationProblemReporterService
import org.jetbrains.compose.internal.webExt
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.KotlinBasePlugin
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilerPluginSupportPlugin
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
import org.jetbrains.kotlin.gradle.plugin.SubpluginArtifact
import org.jetbrains.kotlin.gradle.plugin.SubpluginOption
import org.jetbrains.kotlin.gradle.plugin.getKotlinPluginVersion
import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrTarget
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

internal fun Project.configureComposeCompilerPlugin() {
plugins.withId(KOTLIN_MPP_PLUGIN_ID) { plugin ->
configureComposeCompilerPlugin(plugin as KotlinBasePlugin)
}
plugins.withId(KOTLIN_JVM_PLUGIN_ID) { plugin ->
configureComposeCompilerPlugin(plugin as KotlinBasePlugin)
}
}

internal const val newCompilerIsAvailableVersion = "2.0.0-RC2"
internal const val newComposeCompilerKotlinSupportPluginId = "org.jetbrains.kotlin.plugin.compose"
internal const val newComposeCompilerError =
"Since Kotlin $newCompilerIsAvailableVersion to use Compose Multiplatform " +
"you must apply \"$newComposeCompilerKotlinSupportPluginId\" plugin." +
"\nSee the migration guide https://www.jetbrains.com/help/kotlin-multiplatform-dev/compose-compiler/compose-compiler.html#migrating-a-compose-multiplatform-project"

private fun Project.configureComposeCompilerPlugin(kgp: KotlinBasePlugin) {
val kgpVersion = kgp.pluginVersion

if (Version.fromString(kgpVersion) < Version.fromString(newCompilerIsAvailableVersion)) {
logger.info("Apply ComposeCompilerKotlinSupportPlugin (KGP version = $kgpVersion)")
project.plugins.apply(ComposeCompilerKotlinSupportPlugin::class.java)
} else {
//There is no other way to check that the plugin WASN'T applied!
afterEvaluate {
logger.info("Check that new '$newComposeCompilerKotlinSupportPluginId' was applied")
if (!project.plugins.hasPlugin(newComposeCompilerKotlinSupportPluginId)) {
val ideaIsInSync = project.ideaIsInSyncProvider().get()
if (ideaIsInSync) logger.error("e: Configuration problem: $newComposeCompilerError")
else error("e: Configuration problem: $newComposeCompilerError")
}
}
}
}

class ComposeCompilerKotlinSupportPlugin : KotlinCompilerPluginSupportPlugin {
private lateinit var composeCompilerArtifactProvider: ComposeCompilerArtifactProvider
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ abstract class ComposeExtension @Inject constructor(
* ```
* (see available versions here: https://developer.android.com/jetpack/androidx/releases/compose-kotlin#pre-release_kotlin_compatibility)
*/
@Deprecated("Since Kotlin $newCompilerIsAvailableVersion Compose Compiler configuration is moved to the \"$newComposeCompilerKotlinSupportPluginId\" plugin")
val kotlinCompilerPlugin: Property<String?> = objects.nullableProperty()

/**
Expand All @@ -41,6 +42,7 @@ abstract class ComposeExtension @Inject constructor(
* See all available arguments here:
* https://github.com/androidx/androidx/blob/androidx-main/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposePlugin.kt
*/
@Deprecated("Since Kotlin $newCompilerIsAvailableVersion Compose Compiler configuration is moved to the \"$newComposeCompilerKotlinSupportPluginId\" plugin")
val kotlinCompilerPluginArgs: ListProperty<String> = objects.listProperty(String::class.java)

/**
Expand All @@ -51,6 +53,7 @@ abstract class ComposeExtension @Inject constructor(
* platformTypes.set(platformTypes.get() - KotlinPlatformType.native)
* ```
*/
@Deprecated("Since Kotlin $newCompilerIsAvailableVersion Compose Compiler configuration is moved to the \"$newComposeCompilerKotlinSupportPluginId\" plugin")
val platformTypes: SetProperty<KotlinPlatformType> = objects.setProperty(KotlinPlatformType::class.java).apply {
set(KotlinPlatformType.values().toMutableSet())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import org.jetbrains.compose.internal.utils.currentTarget
import org.jetbrains.compose.resources.ResourcesExtension
import org.jetbrains.compose.resources.configureComposeResources
import org.jetbrains.compose.web.WebExtension
import org.jetbrains.kotlin.com.github.gundy.semver4j.SemVer
import org.jetbrains.kotlin.gradle.dsl.KotlinCompile
import org.jetbrains.kotlin.gradle.dsl.KotlinJsCompile
import org.jetbrains.kotlin.gradle.plugin.*
Expand Down Expand Up @@ -57,7 +58,7 @@ abstract class ComposePlugin : Plugin<Project> {
project.initializePreview(desktopExtension)
composeExtension.extensions.create("web", WebExtension::class.java)

project.plugins.apply(ComposeCompilerKotlinSupportPlugin::class.java)
project.configureComposeCompilerPlugin()
project.configureNativeCompilerCaching()

project.configureComposeResources(resourcesExtension)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package org.jetbrains.compose.internal

import org.gradle.api.DefaultTask
import org.gradle.api.Project
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.TaskAction

internal fun Project.ideaIsInSyncProvider(): Provider<Boolean> = provider {
System.getProperty("idea.sync.active", "false").toBoolean()
}

/**
* This task should be FAST and SAFE! Because it is being run during IDE import.
*/
internal abstract class IdeaImportTask : DefaultTask() {
@get:Input
val ideaIsInSync: Provider<Boolean> = project.ideaIsInSyncProvider()

@TaskAction
fun run() {
try {
safeAction()
} catch (e: Exception) {
//message must contain two ':' symbols to be parsed by IDE UI!
logger.error("e: $name task was failed:", e)
if (!ideaIsInSync.get()) throw e
}
}

abstract fun safeAction()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.jetbrains.compose.internal

internal data class Version(
val major: Int,
val minor: Int,
val patch: Int,
val meta: String
): Comparable<Version> {
override fun compareTo(other: Version): Int = when {
major != other.major -> major - other.major
minor != other.minor -> minor - other.minor
patch != other.patch -> patch - other.patch
else -> {
if (meta.isEmpty()) 1
else if (other.meta.isEmpty()) -1
else meta.compareTo(other.meta)
}
}

companion object {
private val SEMVER_REGEXP = """^(\d+)(?:\.(\d*))?(?:\.(\d*))?(?:-(.*))?${'$'}""".toRegex()
fun fromString(versionString: String): Version {
val matchResult: MatchResult = SEMVER_REGEXP.matchEntire(versionString) ?: return Version(0,0,0, "")
val major: Int = matchResult.groups[1]?.value?.toInt() ?: 0
val minor: Int = matchResult.groups[2]?.value?.toInt() ?: 0
val patch: Int = matchResult.groups[3]?.value?.toInt() ?: 0
val meta: String = matchResult.groups[4]?.value ?: ""
return Version(major, minor, patch, meta)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ internal const val KOTLIN_MPP_PLUGIN_ID = "org.jetbrains.kotlin.multiplatform"
internal const val KOTLIN_JVM_PLUGIN_ID = "org.jetbrains.kotlin.jvm"
internal const val KOTLIN_JS_PLUGIN_ID = "org.jetbrains.kotlin.js"
internal const val COMPOSE_PLUGIN_ID = "org.jetbrains.compose"

internal const val IDEA_IMPORT_TASK_NAME = "prepareKotlinIdeaImport"
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import org.jetbrains.compose.internal.KOTLIN_JVM_PLUGIN_ID
import org.jetbrains.compose.internal.KOTLIN_MPP_PLUGIN_ID
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension
import org.jetbrains.kotlin.gradle.plugin.KotlinBasePlugin
import org.jetbrains.kotlin.gradle.plugin.KotlinMultiplatformPluginWrapper
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import org.jetbrains.kotlin.gradle.plugin.extraProperties
import java.io.File
Expand All @@ -29,11 +31,11 @@ private val androidPluginIds = listOf(

internal fun Project.configureComposeResources(extension: ResourcesExtension) {
val config = provider { extension }
plugins.withId(KOTLIN_MPP_PLUGIN_ID) { onKgpApplied(config) }
plugins.withId(KOTLIN_MPP_PLUGIN_ID) { onKgpApplied(config, it as KotlinBasePlugin) }
plugins.withId(KOTLIN_JVM_PLUGIN_ID) { onKotlinJvmApplied(config) }
}

private fun Project.onKgpApplied(config: Provider<ResourcesExtension>) {
private fun Project.onKgpApplied(config: Provider<ResourcesExtension>, kgp: KotlinBasePlugin) {
val kotlinExtension = project.extensions.getByType(KotlinMultiplatformExtension::class.java)

val hasKmpResources = extraProperties.has(KMP_RES_EXT)
Expand All @@ -47,7 +49,7 @@ private fun Project.onKgpApplied(config: Provider<ResourcesExtension>) {
if (!hasKmpResources) logger.info(
"""
Compose resources publication requires Kotlin Gradle Plugin >= 2.0
Current Kotlin Gradle Plugin is ${kotlinExtension.coreLibrariesVersion}
Current Kotlin Gradle Plugin is ${kgp.pluginVersion}
""".trimIndent()
)
if (currentGradleVersion < minGradleVersion) logger.info(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package org.jetbrains.compose.resources
import org.gradle.api.Project
import org.gradle.api.provider.Provider
import org.jetbrains.compose.ComposePlugin
import org.jetbrains.compose.internal.IDEA_IMPORT_TASK_NAME
import org.jetbrains.compose.internal.IdeaImportTask
import org.jetbrains.compose.internal.utils.uppercaseFirstChar
import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
Expand Down Expand Up @@ -67,7 +69,7 @@ internal fun Project.configureComposeResourcesGeneration(

//setup task execution during IDE import
tasks.configureEach { importTask ->
if (importTask.name == "prepareKotlinIdeaImport") {
if (importTask.name == IDEA_IMPORT_TASK_NAME) {
importTask.dependsOn(tasks.withType(IdeaImportTask::class.java))
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,38 +1,13 @@
package org.jetbrains.compose.resources

import org.gradle.api.DefaultTask
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.provider.Property
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.Optional
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.TaskAction
import org.jetbrains.compose.internal.IdeaImportTask
import java.io.File

/**
* This task should be FAST and SAFE! Because it is being run during IDE import.
*/
internal abstract class IdeaImportTask : DefaultTask() {
@get:Input
val ideaIsInSync: Provider<Boolean> = project.provider {
System.getProperty("idea.sync.active", "false").toBoolean()
}

@TaskAction
fun run() {
try {
safeAction()
} catch (e: Exception) {
//message must contain two ':' symbols to be parsed by IDE UI!
logger.error("e: $name task was failed:", e)
if (!ideaIsInSync.get()) throw e
}
}

abstract fun safeAction()
}

internal abstract class GenerateResClassTask : IdeaImportTask() {
companion object {
private const val RES_FILE_NAME = "Res"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import org.gradle.api.tasks.PathSensitive
import org.gradle.api.tasks.PathSensitivity
import org.gradle.api.tasks.SkipWhenEmpty
import org.gradle.api.tasks.TaskAction
import org.jetbrains.compose.internal.IdeaImportTask
import java.io.File
import java.nio.file.Path
import kotlin.io.path.relativeTo
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.OutputFiles
import org.gradle.api.tasks.SkipWhenEmpty
import org.gradle.api.tasks.TaskProvider
import org.jetbrains.compose.internal.IdeaImportTask
import org.jetbrains.compose.internal.utils.uppercaseFirstChar
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import org.w3c.dom.Node
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

package org.jetbrains.compose.test.tests.integration

import org.jetbrains.compose.newCompilerIsAvailableVersion
import org.jetbrains.compose.newComposeCompilerError
import org.jetbrains.compose.test.utils.GradlePluginTestBase
import org.jetbrains.compose.test.utils.TestProjects
import org.jetbrains.compose.test.utils.checks
Expand Down Expand Up @@ -46,4 +48,16 @@ class KotlinCompatibilityTest : GradlePluginTestBase() {
check.taskSuccessful(":compileKotlinJs")
}
}

/* TODO uncomment the test when Kotlin RC2 will be published
@Test
fun testNewCompilerPluginError() {
val testProject = testProject(
TestProjects.mpp,
testEnvironment = defaultTestEnvironment.copy(kotlinVersion = newCompilerIsAvailableVersion)
)
testProject.gradleFailure("tasks").checks {
check.logContains(newComposeCompilerError)
}
}*/
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.jetbrains.compose.test.tests.unit

import org.jetbrains.compose.internal.Version
import kotlin.test.Test

class SemVerTest {
@Test
fun testSemVersionParser() {
assert(Version.fromString("0") < Version.fromString("1.2.3"))
assert(Version.fromString("2") > Version.fromString("1.2.3"))
assert(Version.fromString("1.1") > Version.fromString("1-abc"))
assert(Version.fromString("1.1") > Version.fromString("1"))
assert(Version.fromString("2.0.0-RC1") > Version.fromString("2.0.0-Beta5"))
assert(Version.fromString("2.0.0-RC2") > Version.fromString("2.0.0-RC1"))
assert(Version.fromString("2.0.0-RC1") > Version.fromString("1.9.23"))
assert(Version.fromString("2.0.0") > Version.fromString("2.0.0-RC1"))
assert(Version.fromString("2.0.0-RC1") == Version.fromString("2.0.0-RC1"))
}
}

0 comments on commit 7d456d4

Please sign in to comment.