Skip to content

Commit 7d456d4

Browse files
authored
[gradle] New Compose Compiler gradle plugin checks. (JetBrains#4604)
If KGP >= 2.0.0-RC2 new Compose gradle plugin has to be applied. To throw a configuration error if it's not.
1 parent 644c7c3 commit 7d456d4

13 files changed

+162
-32
lines changed

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

+48-1
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,58 @@ package org.jetbrains.compose
88
import org.gradle.api.Project
99
import org.gradle.api.provider.Provider
1010
import org.jetbrains.compose.internal.ComposeCompilerArtifactProvider
11+
import org.jetbrains.compose.internal.KOTLIN_JVM_PLUGIN_ID
12+
import org.jetbrains.compose.internal.KOTLIN_MPP_PLUGIN_ID
13+
import org.jetbrains.compose.internal.Version
14+
import org.jetbrains.compose.internal.ideaIsInSyncProvider
1115
import org.jetbrains.compose.internal.mppExtOrNull
1216
import org.jetbrains.compose.internal.service.ConfigurationProblemReporterService
1317
import org.jetbrains.compose.internal.webExt
14-
import org.jetbrains.kotlin.gradle.plugin.*
18+
import org.jetbrains.kotlin.gradle.plugin.KotlinBasePlugin
19+
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
20+
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilerPluginSupportPlugin
21+
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
22+
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
23+
import org.jetbrains.kotlin.gradle.plugin.SubpluginArtifact
24+
import org.jetbrains.kotlin.gradle.plugin.SubpluginOption
25+
import org.jetbrains.kotlin.gradle.plugin.getKotlinPluginVersion
1526
import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrTarget
27+
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
28+
29+
internal fun Project.configureComposeCompilerPlugin() {
30+
plugins.withId(KOTLIN_MPP_PLUGIN_ID) { plugin ->
31+
configureComposeCompilerPlugin(plugin as KotlinBasePlugin)
32+
}
33+
plugins.withId(KOTLIN_JVM_PLUGIN_ID) { plugin ->
34+
configureComposeCompilerPlugin(plugin as KotlinBasePlugin)
35+
}
36+
}
37+
38+
internal const val newCompilerIsAvailableVersion = "2.0.0-RC2"
39+
internal const val newComposeCompilerKotlinSupportPluginId = "org.jetbrains.kotlin.plugin.compose"
40+
internal const val newComposeCompilerError =
41+
"Since Kotlin $newCompilerIsAvailableVersion to use Compose Multiplatform " +
42+
"you must apply \"$newComposeCompilerKotlinSupportPluginId\" plugin." +
43+
"\nSee the migration guide https://www.jetbrains.com/help/kotlin-multiplatform-dev/compose-compiler/compose-compiler.html#migrating-a-compose-multiplatform-project"
44+
45+
private fun Project.configureComposeCompilerPlugin(kgp: KotlinBasePlugin) {
46+
val kgpVersion = kgp.pluginVersion
47+
48+
if (Version.fromString(kgpVersion) < Version.fromString(newCompilerIsAvailableVersion)) {
49+
logger.info("Apply ComposeCompilerKotlinSupportPlugin (KGP version = $kgpVersion)")
50+
project.plugins.apply(ComposeCompilerKotlinSupportPlugin::class.java)
51+
} else {
52+
//There is no other way to check that the plugin WASN'T applied!
53+
afterEvaluate {
54+
logger.info("Check that new '$newComposeCompilerKotlinSupportPluginId' was applied")
55+
if (!project.plugins.hasPlugin(newComposeCompilerKotlinSupportPluginId)) {
56+
val ideaIsInSync = project.ideaIsInSyncProvider().get()
57+
if (ideaIsInSync) logger.error("e: Configuration problem: $newComposeCompilerError")
58+
else error("e: Configuration problem: $newComposeCompilerError")
59+
}
60+
}
61+
}
62+
}
1663

1764
class ComposeCompilerKotlinSupportPlugin : KotlinCompilerPluginSupportPlugin {
1865
private lateinit var composeCompilerArtifactProvider: ComposeCompilerArtifactProvider

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

+3
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ abstract class ComposeExtension @Inject constructor(
3131
* ```
3232
* (see available versions here: https://developer.android.com/jetpack/androidx/releases/compose-kotlin#pre-release_kotlin_compatibility)
3333
*/
34+
@Deprecated("Since Kotlin $newCompilerIsAvailableVersion Compose Compiler configuration is moved to the \"$newComposeCompilerKotlinSupportPluginId\" plugin")
3435
val kotlinCompilerPlugin: Property<String?> = objects.nullableProperty()
3536

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

4648
/**
@@ -51,6 +53,7 @@ abstract class ComposeExtension @Inject constructor(
5153
* platformTypes.set(platformTypes.get() - KotlinPlatformType.native)
5254
* ```
5355
*/
56+
@Deprecated("Since Kotlin $newCompilerIsAvailableVersion Compose Compiler configuration is moved to the \"$newComposeCompilerKotlinSupportPluginId\" plugin")
5457
val platformTypes: SetProperty<KotlinPlatformType> = objects.setProperty(KotlinPlatformType::class.java).apply {
5558
set(KotlinPlatformType.values().toMutableSet())
5659
}

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

+2-1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import org.jetbrains.compose.internal.utils.currentTarget
2727
import org.jetbrains.compose.resources.ResourcesExtension
2828
import org.jetbrains.compose.resources.configureComposeResources
2929
import org.jetbrains.compose.web.WebExtension
30+
import org.jetbrains.kotlin.com.github.gundy.semver4j.SemVer
3031
import org.jetbrains.kotlin.gradle.dsl.KotlinCompile
3132
import org.jetbrains.kotlin.gradle.dsl.KotlinJsCompile
3233
import org.jetbrains.kotlin.gradle.plugin.*
@@ -57,7 +58,7 @@ abstract class ComposePlugin : Plugin<Project> {
5758
project.initializePreview(desktopExtension)
5859
composeExtension.extensions.create("web", WebExtension::class.java)
5960

60-
project.plugins.apply(ComposeCompilerKotlinSupportPlugin::class.java)
61+
project.configureComposeCompilerPlugin()
6162
project.configureNativeCompilerCaching()
6263

6364
project.configureComposeResources(resourcesExtension)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package org.jetbrains.compose.internal
2+
3+
import org.gradle.api.DefaultTask
4+
import org.gradle.api.Project
5+
import org.gradle.api.provider.Provider
6+
import org.gradle.api.tasks.Input
7+
import org.gradle.api.tasks.TaskAction
8+
9+
internal fun Project.ideaIsInSyncProvider(): Provider<Boolean> = provider {
10+
System.getProperty("idea.sync.active", "false").toBoolean()
11+
}
12+
13+
/**
14+
* This task should be FAST and SAFE! Because it is being run during IDE import.
15+
*/
16+
internal abstract class IdeaImportTask : DefaultTask() {
17+
@get:Input
18+
val ideaIsInSync: Provider<Boolean> = project.ideaIsInSyncProvider()
19+
20+
@TaskAction
21+
fun run() {
22+
try {
23+
safeAction()
24+
} catch (e: Exception) {
25+
//message must contain two ':' symbols to be parsed by IDE UI!
26+
logger.error("e: $name task was failed:", e)
27+
if (!ideaIsInSync.get()) throw e
28+
}
29+
}
30+
31+
abstract fun safeAction()
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package org.jetbrains.compose.internal
2+
3+
internal data class Version(
4+
val major: Int,
5+
val minor: Int,
6+
val patch: Int,
7+
val meta: String
8+
): Comparable<Version> {
9+
override fun compareTo(other: Version): Int = when {
10+
major != other.major -> major - other.major
11+
minor != other.minor -> minor - other.minor
12+
patch != other.patch -> patch - other.patch
13+
else -> {
14+
if (meta.isEmpty()) 1
15+
else if (other.meta.isEmpty()) -1
16+
else meta.compareTo(other.meta)
17+
}
18+
}
19+
20+
companion object {
21+
private val SEMVER_REGEXP = """^(\d+)(?:\.(\d*))?(?:\.(\d*))?(?:-(.*))?${'$'}""".toRegex()
22+
fun fromString(versionString: String): Version {
23+
val matchResult: MatchResult = SEMVER_REGEXP.matchEntire(versionString) ?: return Version(0,0,0, "")
24+
val major: Int = matchResult.groups[1]?.value?.toInt() ?: 0
25+
val minor: Int = matchResult.groups[2]?.value?.toInt() ?: 0
26+
val patch: Int = matchResult.groups[3]?.value?.toInt() ?: 0
27+
val meta: String = matchResult.groups[4]?.value ?: ""
28+
return Version(major, minor, patch, meta)
29+
}
30+
}
31+
}

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

+2
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,5 @@ internal const val KOTLIN_MPP_PLUGIN_ID = "org.jetbrains.kotlin.multiplatform"
99
internal const val KOTLIN_JVM_PLUGIN_ID = "org.jetbrains.kotlin.jvm"
1010
internal const val KOTLIN_JS_PLUGIN_ID = "org.jetbrains.kotlin.js"
1111
internal const val COMPOSE_PLUGIN_ID = "org.jetbrains.compose"
12+
13+
internal const val IDEA_IMPORT_TASK_NAME = "prepareKotlinIdeaImport"

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

+5-3
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import org.jetbrains.compose.internal.KOTLIN_JVM_PLUGIN_ID
1313
import org.jetbrains.compose.internal.KOTLIN_MPP_PLUGIN_ID
1414
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
1515
import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension
16+
import org.jetbrains.kotlin.gradle.plugin.KotlinBasePlugin
17+
import org.jetbrains.kotlin.gradle.plugin.KotlinMultiplatformPluginWrapper
1618
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
1719
import org.jetbrains.kotlin.gradle.plugin.extraProperties
1820
import java.io.File
@@ -29,11 +31,11 @@ private val androidPluginIds = listOf(
2931

3032
internal fun Project.configureComposeResources(extension: ResourcesExtension) {
3133
val config = provider { extension }
32-
plugins.withId(KOTLIN_MPP_PLUGIN_ID) { onKgpApplied(config) }
34+
plugins.withId(KOTLIN_MPP_PLUGIN_ID) { onKgpApplied(config, it as KotlinBasePlugin) }
3335
plugins.withId(KOTLIN_JVM_PLUGIN_ID) { onKotlinJvmApplied(config) }
3436
}
3537

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

3941
val hasKmpResources = extraProperties.has(KMP_RES_EXT)
@@ -47,7 +49,7 @@ private fun Project.onKgpApplied(config: Provider<ResourcesExtension>) {
4749
if (!hasKmpResources) logger.info(
4850
"""
4951
Compose resources publication requires Kotlin Gradle Plugin >= 2.0
50-
Current Kotlin Gradle Plugin is ${kotlinExtension.coreLibrariesVersion}
52+
Current Kotlin Gradle Plugin is ${kgp.pluginVersion}
5153
""".trimIndent()
5254
)
5355
if (currentGradleVersion < minGradleVersion) logger.info(

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

+3-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package org.jetbrains.compose.resources
33
import org.gradle.api.Project
44
import org.gradle.api.provider.Provider
55
import org.jetbrains.compose.ComposePlugin
6+
import org.jetbrains.compose.internal.IDEA_IMPORT_TASK_NAME
7+
import org.jetbrains.compose.internal.IdeaImportTask
68
import org.jetbrains.compose.internal.utils.uppercaseFirstChar
79
import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension
810
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
@@ -67,7 +69,7 @@ internal fun Project.configureComposeResourcesGeneration(
6769

6870
//setup task execution during IDE import
6971
tasks.configureEach { importTask ->
70-
if (importTask.name == "prepareKotlinIdeaImport") {
72+
if (importTask.name == IDEA_IMPORT_TASK_NAME) {
7173
importTask.dependsOn(tasks.withType(IdeaImportTask::class.java))
7274
}
7375
}

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

+1-26
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,13 @@
11
package org.jetbrains.compose.resources
22

3-
import org.gradle.api.DefaultTask
43
import org.gradle.api.file.DirectoryProperty
54
import org.gradle.api.provider.Property
6-
import org.gradle.api.provider.Provider
75
import org.gradle.api.tasks.Input
86
import org.gradle.api.tasks.Optional
97
import org.gradle.api.tasks.OutputDirectory
10-
import org.gradle.api.tasks.TaskAction
8+
import org.jetbrains.compose.internal.IdeaImportTask
119
import java.io.File
1210

13-
/**
14-
* This task should be FAST and SAFE! Because it is being run during IDE import.
15-
*/
16-
internal abstract class IdeaImportTask : DefaultTask() {
17-
@get:Input
18-
val ideaIsInSync: Provider<Boolean> = project.provider {
19-
System.getProperty("idea.sync.active", "false").toBoolean()
20-
}
21-
22-
@TaskAction
23-
fun run() {
24-
try {
25-
safeAction()
26-
} catch (e: Exception) {
27-
//message must contain two ':' symbols to be parsed by IDE UI!
28-
logger.error("e: $name task was failed:", e)
29-
if (!ideaIsInSync.get()) throw e
30-
}
31-
}
32-
33-
abstract fun safeAction()
34-
}
35-
3611
internal abstract class GenerateResClassTask : IdeaImportTask() {
3712
companion object {
3813
private const val RES_FILE_NAME = "Res"

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

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import org.gradle.api.tasks.PathSensitive
1010
import org.gradle.api.tasks.PathSensitivity
1111
import org.gradle.api.tasks.SkipWhenEmpty
1212
import org.gradle.api.tasks.TaskAction
13+
import org.jetbrains.compose.internal.IdeaImportTask
1314
import java.io.File
1415
import java.nio.file.Path
1516
import kotlin.io.path.relativeTo

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

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import org.gradle.api.tasks.OutputDirectory
1414
import org.gradle.api.tasks.OutputFiles
1515
import org.gradle.api.tasks.SkipWhenEmpty
1616
import org.gradle.api.tasks.TaskProvider
17+
import org.jetbrains.compose.internal.IdeaImportTask
1718
import org.jetbrains.compose.internal.utils.uppercaseFirstChar
1819
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
1920
import org.w3c.dom.Node

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

+14
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

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

8+
import org.jetbrains.compose.newCompilerIsAvailableVersion
9+
import org.jetbrains.compose.newComposeCompilerError
810
import org.jetbrains.compose.test.utils.GradlePluginTestBase
911
import org.jetbrains.compose.test.utils.TestProjects
1012
import org.jetbrains.compose.test.utils.checks
@@ -46,4 +48,16 @@ class KotlinCompatibilityTest : GradlePluginTestBase() {
4648
check.taskSuccessful(":compileKotlinJs")
4749
}
4850
}
51+
52+
/* TODO uncomment the test when Kotlin RC2 will be published
53+
@Test
54+
fun testNewCompilerPluginError() {
55+
val testProject = testProject(
56+
TestProjects.mpp,
57+
testEnvironment = defaultTestEnvironment.copy(kotlinVersion = newCompilerIsAvailableVersion)
58+
)
59+
testProject.gradleFailure("tasks").checks {
60+
check.logContains(newComposeCompilerError)
61+
}
62+
}*/
4963
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package org.jetbrains.compose.test.tests.unit
2+
3+
import org.jetbrains.compose.internal.Version
4+
import kotlin.test.Test
5+
6+
class SemVerTest {
7+
@Test
8+
fun testSemVersionParser() {
9+
assert(Version.fromString("0") < Version.fromString("1.2.3"))
10+
assert(Version.fromString("2") > Version.fromString("1.2.3"))
11+
assert(Version.fromString("1.1") > Version.fromString("1-abc"))
12+
assert(Version.fromString("1.1") > Version.fromString("1"))
13+
assert(Version.fromString("2.0.0-RC1") > Version.fromString("2.0.0-Beta5"))
14+
assert(Version.fromString("2.0.0-RC2") > Version.fromString("2.0.0-RC1"))
15+
assert(Version.fromString("2.0.0-RC1") > Version.fromString("1.9.23"))
16+
assert(Version.fromString("2.0.0") > Version.fromString("2.0.0-RC1"))
17+
assert(Version.fromString("2.0.0-RC1") == Version.fromString("2.0.0-RC1"))
18+
}
19+
}

0 commit comments

Comments
 (0)