From 7a47ba8dd809a68239d4f52ace7df11a77d5b4cf Mon Sep 17 00:00:00 2001 From: Alexey Tsvetkov Date: Wed, 28 Apr 2021 12:24:50 +0300 Subject: [PATCH] Support Kotlin JS targets in Compose Gradle plugin --- .../src/main/kotlin/BuildProperties.kt | 2 + gradle-plugins/compose/build.gradle.kts | 1 + .../ComposeCompilerKotlinSupportPlugin.kt | 41 ++++++++++--- .../org/jetbrains/compose/ComposePlugin.kt | 15 ++++- .../compose/internal/projectExtensions.kt | 20 +++++++ .../org/jetbrains/compose/web/WebExtension.kt | 60 +++++++++++++++++++ .../compose/web/internal/configureWeb.kt | 16 +++++ gradle-plugins/gradle.properties | 1 + 8 files changed, 147 insertions(+), 9 deletions(-) create mode 100644 gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/internal/projectExtensions.kt create mode 100644 gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/web/WebExtension.kt create mode 100644 gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/web/internal/configureWeb.kt diff --git a/gradle-plugins/buildSrc/src/main/kotlin/BuildProperties.kt b/gradle-plugins/buildSrc/src/main/kotlin/BuildProperties.kt index 0729a2bc8e..b9bdc8fee7 100644 --- a/gradle-plugins/buildSrc/src/main/kotlin/BuildProperties.kt +++ b/gradle-plugins/buildSrc/src/main/kotlin/BuildProperties.kt @@ -17,4 +17,6 @@ object BuildProperties { fun deployVersion(project: Project): String = System.getenv("COMPOSE_GRADLE_PLUGIN_VERSION") ?: project.findProperty("deploy.version") as String + fun isComposeWithWeb(project: Project): Boolean = + project.findProperty("compose.with.web") == "true" } diff --git a/gradle-plugins/compose/build.gradle.kts b/gradle-plugins/compose/build.gradle.kts index 4e6f3018f5..93475c9290 100644 --- a/gradle-plugins/compose/build.gradle.kts +++ b/gradle-plugins/compose/build.gradle.kts @@ -25,6 +25,7 @@ buildConfig { packageName = "org.jetbrains.compose" clsName = "ComposeBuildConfig" buildConfigField("String", "composeVersion", BuildProperties.composeVersion(project)) + buildConfigField("Boolean", "isComposeWithWeb", BuildProperties.isComposeWithWeb(project).toString()) } val embedded by configurations.creating diff --git a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/ComposeCompilerKotlinSupportPlugin.kt b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/ComposeCompilerKotlinSupportPlugin.kt index 4b389dd75d..2f200c1dd8 100644 --- a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/ComposeCompilerKotlinSupportPlugin.kt +++ b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/ComposeCompilerKotlinSupportPlugin.kt @@ -6,23 +6,48 @@ package org.jetbrains.compose import org.gradle.api.provider.Provider +import org.jetbrains.compose.internal.webExt import org.jetbrains.kotlin.gradle.plugin.* +import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrTarget class ComposeCompilerKotlinSupportPlugin : KotlinCompilerPluginSupportPlugin { override fun getCompilerPluginId(): String = - "org.jetbrains.compose" + "androidx.compose.compiler.plugins.kotlin" - override fun isApplicable(kotlinCompilation: KotlinCompilation<*>): Boolean { - val targetPlatform = kotlinCompilation.target.platformType - return targetPlatform != KotlinPlatformType.js - && targetPlatform != KotlinPlatformType.native - } + override fun isApplicable(kotlinCompilation: KotlinCompilation<*>): Boolean = + when (kotlinCompilation.target.platformType) { + KotlinPlatformType.common -> true + KotlinPlatformType.jvm -> true + KotlinPlatformType.js -> isApplicableJsTarget(kotlinCompilation.target) + KotlinPlatformType.androidJvm -> true + KotlinPlatformType.native -> false + } - override fun applyToCompilation(kotlinCompilation: KotlinCompilation<*>): Provider> = - kotlinCompilation.target.project.provider { emptyList() } + override fun applyToCompilation(kotlinCompilation: KotlinCompilation<*>): Provider> { + val target = kotlinCompilation.target + return target.project.provider { + platformPluginOptions[target.platformType] ?: emptyList() + } + } override fun getPluginArtifact(): SubpluginArtifact = SubpluginArtifact( groupId = "org.jetbrains.compose.compiler", artifactId = "compiler", version = composeVersion ) + + private fun isApplicableJsTarget(kotlinTarget: KotlinTarget): Boolean { + if (kotlinTarget !is KotlinJsIrTarget) return false + + val project = kotlinTarget.project + val webExt = project.webExt ?: return false + + return kotlinTarget in webExt.targetsToConfigure(project) + } + + private val platformPluginOptions = mapOf( + KotlinPlatformType.js to options("generateDecoys" to "true") + ) + + private fun options(vararg options: Pair): List = + options.map { SubpluginOption(it.first, it.second) } } \ No newline at end of file diff --git a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/ComposePlugin.kt b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/ComposePlugin.kt index d36f513956..f5c6c5ae88 100644 --- a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/ComposePlugin.kt +++ b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/ComposePlugin.kt @@ -16,6 +16,7 @@ import org.jetbrains.compose.desktop.DesktopExtension import org.jetbrains.compose.desktop.application.internal.configureApplicationImpl import org.jetbrains.compose.desktop.application.internal.currentTarget import org.jetbrains.compose.desktop.preview.internal.initializePreview +import org.jetbrains.compose.web.internal.initializeWeb import org.jetbrains.kotlin.gradle.plugin.KotlinDependencyHandler import org.jetbrains.kotlin.gradle.tasks.KotlinCompile @@ -37,8 +38,11 @@ class ComposePlugin : Plugin { } project.initializePreview() + if (ComposeBuildConfig.isComposeWithWeb) { + project.initializeWeb(composeExtension) + } - project.pluginManager.apply(ComposeCompilerKotlinSupportPlugin::class.java) + project.plugins.apply(ComposeCompilerKotlinSupportPlugin::class.java) project.afterEvaluate { if (desktopExtension._isApplicationInitialized) { @@ -112,6 +116,9 @@ class ComposePlugin : Plugin { val runtime get() = composeDependency("org.jetbrains.compose.runtime:runtime") val ui get() = composeDependency("org.jetbrains.compose.ui:ui") val materialIconsExtended get() = composeDependency("org.jetbrains.compose.material:material-icons-extended") + val web: WebDependencies = + if (ComposeBuildConfig.isComposeWithWeb) WebDependencies + else error("This version of Compose plugin does not support 'compose.web.*' dependencies") } object DesktopDependencies { @@ -141,6 +148,12 @@ class ComposePlugin : Plugin { composeDependency("org.jetbrains.compose.desktop:desktop-jvm-${currentTarget.id}") } } + + object WebDependencies { + val web by lazy { + composeDependency("org.jetbrains.compose.web:web") + } + } } fun KotlinDependencyHandler.compose(groupWithArtifact: String) = composeDependency(groupWithArtifact) diff --git a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/internal/projectExtensions.kt b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/internal/projectExtensions.kt new file mode 100644 index 0000000000..9a09e04fc4 --- /dev/null +++ b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/internal/projectExtensions.kt @@ -0,0 +1,20 @@ +/* + * Copyright 2020-2021 JetBrains s.r.o. and respective authors and developers. + * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. + */ + +package org.jetbrains.compose.internal + +import org.gradle.api.Project +import org.jetbrains.compose.ComposeExtension +import org.jetbrains.compose.web.WebExtension +import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension + +internal val Project.composeExt: ComposeExtension? + get() = extensions.findByType(ComposeExtension::class.java) + +internal val Project.webExt: WebExtension? + get() = composeExt?.extensions?.findByType(WebExtension::class.java) + +internal val Project.mppExt: KotlinMultiplatformExtension? + get() = extensions.findByType(KotlinMultiplatformExtension::class.java) \ No newline at end of file diff --git a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/web/WebExtension.kt b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/web/WebExtension.kt new file mode 100644 index 0000000000..079fac26ec --- /dev/null +++ b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/web/WebExtension.kt @@ -0,0 +1,60 @@ +/* + * Copyright 2020-2021 JetBrains s.r.o. and respective authors and developers. + * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. + */ + +package org.jetbrains.compose.web + +import org.gradle.api.Project +import org.gradle.api.plugins.ExtensionAware +import org.jetbrains.compose.internal.mppExt +import org.jetbrains.kotlin.gradle.plugin.KotlinTarget +import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrTarget + +abstract class WebExtension : ExtensionAware { + private var requestedTargets: Set? = null + private var targetsToConfigure: Set? = null + + // public api + @Suppress("unused") + fun targets(vararg targets: KotlinTarget) { + check(requestedTargets == null) { + "compose.web.targets() was already set!" + } + + val jsIrTargets = linkedSetOf() + for (target in targets) { + check(target is KotlinJsIrTarget) { + """|'${target.name}' is not a JS(IR) target: + |* add `kotlin.js.compiler=ir` to gradle properties; + |* define target as `kotlin { js(IR) { ... } }` + """.trimMargin() + } + jsIrTargets.add(target) + } + requestedTargets = jsIrTargets + } + + internal fun targetsToConfigure(project: Project): Set { + targetsToConfigure = + targetsToConfigure + ?: requestedTargets + ?: defaultJsTargetsToConfigure(project) + + return targetsToConfigure!! + } + + private fun defaultJsTargetsToConfigure(project: Project): Set { + val mppTargets = project.mppExt?.targets?.asMap?.values ?: emptySet() + val jsIRTargets = mppTargets.filterIsInstanceTo(LinkedHashSet()) + + return if (jsIRTargets.size > 1) { + project.logger.error( + "w: Default configuration for Compose Web is disabled: " + + "multiple Kotlin JS IR targets are defined. " + + "Specify Compose Web Kotlin targets by using `compose.web.targets()`" + ) + emptySet() + } else jsIRTargets + } +} \ No newline at end of file diff --git a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/web/internal/configureWeb.kt b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/web/internal/configureWeb.kt new file mode 100644 index 0000000000..faf3b86477 --- /dev/null +++ b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/web/internal/configureWeb.kt @@ -0,0 +1,16 @@ +/* + * Copyright 2020-2021 JetBrains s.r.o. and respective authors and developers. + * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. + */ + +package org.jetbrains.compose.web.internal + +import org.gradle.api.Project +import org.jetbrains.compose.ComposeExtension +import org.jetbrains.compose.web.WebExtension + +internal fun Project.initializeWeb(composeExtension: ComposeExtension) { + project.plugins.withId("org.jetbrains.kotlin.multiplatform") { + composeExtension.extensions.create("web", WebExtension::class.java) + } +} \ No newline at end of file diff --git a/gradle-plugins/gradle.properties b/gradle-plugins/gradle.properties index 7e55e9320a..1ae65a0fd7 100644 --- a/gradle-plugins/gradle.properties +++ b/gradle-plugins/gradle.properties @@ -7,6 +7,7 @@ kotlin.code.style=official # # __LATEST_COMPOSE_RELEASE_VERSION__ compose.version=0.4.0-build182 +compose.with.web=false # A version of Gradle plugin, that will be published, # unless overridden by COMPOSE_GRADLE_PLUGIN_VERSION env var.