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.Project
import org.gradle.api.provider.Provider import org.gradle.api.provider.Provider
import org.jetbrains.compose.internal.ComposeCompilerArtifactProvider 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.mppExtOrNull
import org.jetbrains.compose.internal.service.ConfigurationProblemReporterService import org.jetbrains.compose.internal.service.ConfigurationProblemReporterService
import org.jetbrains.compose.internal.webExt 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.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 { class ComposeCompilerKotlinSupportPlugin : KotlinCompilerPluginSupportPlugin {
private lateinit var composeCompilerArtifactProvider: ComposeCompilerArtifactProvider 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) * (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() val kotlinCompilerPlugin: Property<String?> = objects.nullableProperty()
/** /**
@ -41,6 +42,7 @@ abstract class ComposeExtension @Inject constructor(
* See all available arguments here: * 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 * 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) val kotlinCompilerPluginArgs: ListProperty<String> = objects.listProperty(String::class.java)
/** /**
@ -51,6 +53,7 @@ abstract class ComposeExtension @Inject constructor(
* platformTypes.set(platformTypes.get() - KotlinPlatformType.native) * 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 { val platformTypes: SetProperty<KotlinPlatformType> = objects.setProperty(KotlinPlatformType::class.java).apply {
set(KotlinPlatformType.values().toMutableSet()) 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.ResourcesExtension
import org.jetbrains.compose.resources.configureComposeResources import org.jetbrains.compose.resources.configureComposeResources
import org.jetbrains.compose.web.WebExtension 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.KotlinCompile
import org.jetbrains.kotlin.gradle.dsl.KotlinJsCompile import org.jetbrains.kotlin.gradle.dsl.KotlinJsCompile
import org.jetbrains.kotlin.gradle.plugin.* import org.jetbrains.kotlin.gradle.plugin.*
@ -57,7 +58,7 @@ abstract class ComposePlugin : Plugin<Project> {
project.initializePreview(desktopExtension) project.initializePreview(desktopExtension)
composeExtension.extensions.create("web", WebExtension::class.java) composeExtension.extensions.create("web", WebExtension::class.java)
project.plugins.apply(ComposeCompilerKotlinSupportPlugin::class.java) project.configureComposeCompilerPlugin()
project.configureNativeCompilerCaching() project.configureNativeCompilerCaching()
project.configureComposeResources(resourcesExtension) 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_JVM_PLUGIN_ID = "org.jetbrains.kotlin.jvm"
internal const val KOTLIN_JS_PLUGIN_ID = "org.jetbrains.kotlin.js" internal const val KOTLIN_JS_PLUGIN_ID = "org.jetbrains.kotlin.js"
internal const val COMPOSE_PLUGIN_ID = "org.jetbrains.compose" 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.compose.internal.KOTLIN_MPP_PLUGIN_ID
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension 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.KotlinSourceSet
import org.jetbrains.kotlin.gradle.plugin.extraProperties import org.jetbrains.kotlin.gradle.plugin.extraProperties
import java.io.File import java.io.File
@ -29,11 +31,11 @@ private val androidPluginIds = listOf(
internal fun Project.configureComposeResources(extension: ResourcesExtension) { internal fun Project.configureComposeResources(extension: ResourcesExtension) {
val config = provider { extension } 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) } 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 kotlinExtension = project.extensions.getByType(KotlinMultiplatformExtension::class.java)
val hasKmpResources = extraProperties.has(KMP_RES_EXT) val hasKmpResources = extraProperties.has(KMP_RES_EXT)
@ -47,7 +49,7 @@ private fun Project.onKgpApplied(config: Provider<ResourcesExtension>) {
if (!hasKmpResources) logger.info( if (!hasKmpResources) logger.info(
""" """
Compose resources publication requires Kotlin Gradle Plugin >= 2.0 Compose resources publication requires Kotlin Gradle Plugin >= 2.0
Current Kotlin Gradle Plugin is ${kotlinExtension.coreLibrariesVersion} Current Kotlin Gradle Plugin is ${kgp.pluginVersion}
""".trimIndent() """.trimIndent()
) )
if (currentGradleVersion < minGradleVersion) logger.info( 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.Project
import org.gradle.api.provider.Provider import org.gradle.api.provider.Provider
import org.jetbrains.compose.ComposePlugin 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.compose.internal.utils.uppercaseFirstChar
import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
@ -67,7 +69,7 @@ internal fun Project.configureComposeResourcesGeneration(
//setup task execution during IDE import //setup task execution during IDE import
tasks.configureEach { importTask -> tasks.configureEach { importTask ->
if (importTask.name == "prepareKotlinIdeaImport") { if (importTask.name == IDEA_IMPORT_TASK_NAME) {
importTask.dependsOn(tasks.withType(IdeaImportTask::class.java)) 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 package org.jetbrains.compose.resources
import org.gradle.api.DefaultTask
import org.gradle.api.file.DirectoryProperty import org.gradle.api.file.DirectoryProperty
import org.gradle.api.provider.Property import org.gradle.api.provider.Property
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.Input import org.gradle.api.tasks.Input
import org.gradle.api.tasks.Optional import org.gradle.api.tasks.Optional
import org.gradle.api.tasks.OutputDirectory import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.TaskAction import org.jetbrains.compose.internal.IdeaImportTask
import java.io.File 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() { internal abstract class GenerateResClassTask : IdeaImportTask() {
companion object { companion object {
private const val RES_FILE_NAME = "Res" 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.PathSensitivity
import org.gradle.api.tasks.SkipWhenEmpty import org.gradle.api.tasks.SkipWhenEmpty
import org.gradle.api.tasks.TaskAction import org.gradle.api.tasks.TaskAction
import org.jetbrains.compose.internal.IdeaImportTask
import java.io.File import java.io.File
import java.nio.file.Path import java.nio.file.Path
import kotlin.io.path.relativeTo 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.OutputFiles
import org.gradle.api.tasks.SkipWhenEmpty import org.gradle.api.tasks.SkipWhenEmpty
import org.gradle.api.tasks.TaskProvider import org.gradle.api.tasks.TaskProvider
import org.jetbrains.compose.internal.IdeaImportTask
import org.jetbrains.compose.internal.utils.uppercaseFirstChar import org.jetbrains.compose.internal.utils.uppercaseFirstChar
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import org.w3c.dom.Node 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 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.GradlePluginTestBase
import org.jetbrains.compose.test.utils.TestProjects import org.jetbrains.compose.test.utils.TestProjects
import org.jetbrains.compose.test.utils.checks import org.jetbrains.compose.test.utils.checks
@ -46,4 +48,16 @@ class KotlinCompatibilityTest : GradlePluginTestBase() {
check.taskSuccessful(":compileKotlinJs") 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