Browse Source

[gradle] New Compose Compiler gradle plugin checks. (#4604)

If KGP >= 2.0.0-RC2 new Compose gradle plugin has to be applied.

To throw a configuration error if it's not.
pull/4705/head
Konstantin 7 months ago committed by GitHub
parent
commit
7d456d4ade
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 49
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/ComposeCompilerKotlinSupportPlugin.kt
  2. 3
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/ComposeExtension.kt
  3. 3
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/ComposePlugin.kt
  4. 32
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/internal/IdeaImportTask.kt
  5. 31
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/internal/Version.kt
  6. 2
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/internal/constants.kt
  7. 8
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/ComposeResources.kt
  8. 4
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/ComposeResourcesGeneration.kt
  9. 27
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/GenerateResClassTask.kt
  10. 1
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/GenerateResourceAccessorsTask.kt
  11. 1
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/PrepareComposeResources.kt
  12. 14
      gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/KotlinCompatibilityTest.kt
  13. 19
      gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/unit/SemVerTest.kt

49
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/ComposeCompilerKotlinSupportPlugin.kt

@ -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

3
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/ComposeExtension.kt

@ -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()
/**
@ -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)
/**
@ -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())
}

3
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/ComposePlugin.kt

@ -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.*
@ -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)

32
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/internal/IdeaImportTask.kt

@ -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()
}

31
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/internal/Version.kt

@ -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)
}
}
}

2
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/internal/constants.kt

@ -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"

8
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/ComposeResources.kt

@ -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
@ -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)
@ -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(

4
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/ComposeResourcesGeneration.kt

@ -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
@ -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))
}
}

27
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/GenerateResClassTask.kt

@ -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"

1
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/GenerateResourceAccessorsTask.kt

@ -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

1
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/PrepareComposeResources.kt

@ -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

14
gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/KotlinCompatibilityTest.kt

@ -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
@ -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)
}
}*/
}

19
gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/unit/SemVerTest.kt

@ -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"))
}
}
Loading…
Cancel
Save