Skip to content

Commit

Permalink
[Gradle, Compose] Allow to specify for which Kotlin platforms compose…
Browse files Browse the repository at this point in the history
… plugin should be applied

According to JB Compose team, it is a useful feature requested by their
users and a new plugin should also support it. For example:
JetBrains/compose-multiplatform#3695

^KT-67253 Verification Pending
  • Loading branch information
Tapchicoma authored and qodana-bot committed Apr 22, 2024
1 parent 2cbdf68 commit 6d12bb7
Show file tree
Hide file tree
Showing 6 changed files with 195 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.Property
import org.gradle.api.provider.SetProperty
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
import javax.inject.Inject

abstract class ComposeCompilerGradlePluginExtension @Inject constructor(objectFactory: ObjectFactory) {
Expand Down Expand Up @@ -114,4 +116,31 @@ abstract class ComposeCompilerGradlePluginExtension @Inject constructor(objectFa
* - [composition tracing blog post](https://medium.com/androiddevelopers/jetpack-compose-composition-tracing-9ec2b3aea535)
*/
val includeTraceMarkers: Property<Boolean> = objectFactory.property(Boolean::class.java).convention(false)

/**
* A set of Kotlin platforms to which the Compose plugin will be applied.
*
* By default, all Kotlin platforms are enabled.
*
* To enable only one specific Kotlin platform:
* ```
* composeCompiler {
* targetKotlinPlatforms.set(setOf(KotlinPlatformType.jvm))
* }
* ```
*
* To disable the Compose plugin for one or more Kotlin platforms:
* ```
* composeCompiler {
* targetKotlinPlatforms.set(
* KotlinPlatformType.values()
* .filterNot { it == KotlinPlatformType.native || it == KotlinPlatformType.js }
* .asIterable()
* )
* }
* ```
*/
val targetKotlinPlatforms: SetProperty<KotlinPlatformType> = objectFactory
.setProperty(KotlinPlatformType::class.java)
.convention(KotlinPlatformType.values().asIterable())
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,27 +19,26 @@ class ComposeCompilerGradleSubplugin
) : KotlinCompilerPluginSupportPlugin {

companion object {
fun getComposeCompilerGradlePluginExtension(project: Project): ComposeCompilerGradlePluginExtension {
return project.extensions.getByType(ComposeCompilerGradlePluginExtension::class.java)
}

private const val COMPOSE_COMPILER_ARTIFACT_NAME = "kotlin-compose-compiler-plugin-embeddable"

private val EMPTY_OPTION = SubpluginOption("", "")
}

private lateinit var composeExtension: ComposeCompilerGradlePluginExtension

override fun apply(target: Project) {
target.extensions.create("composeCompiler", ComposeCompilerGradlePluginExtension::class.java)
composeExtension = target.extensions.create("composeCompiler", ComposeCompilerGradlePluginExtension::class.java)
registry.register(ComposeCompilerModelBuilder())
}

override fun isApplicable(kotlinCompilation: KotlinCompilation<*>): Boolean = true
override fun isApplicable(kotlinCompilation: KotlinCompilation<*>): Boolean {
return composeExtension.targetKotlinPlatforms.get().contains(kotlinCompilation.platformType)
}

override fun applyToCompilation(
kotlinCompilation: KotlinCompilation<*>,
): Provider<List<SubpluginOption>> {
val project = kotlinCompilation.target.project
val composeCompilerExtension = getComposeCompilerGradlePluginExtension(project)

// It is ok to check AGP existence without using `plugins.withId` wrapper as `applyToCompilation` will be called in `afterEvaluate`
if (project.isAgpComposeEnabled) {
Expand All @@ -59,34 +58,34 @@ class ComposeCompilerGradleSubplugin
val allPluginProperties = project.objects
.listProperty(SubpluginOption::class.java)
.apply {
add(composeCompilerExtension.generateFunctionKeyMetaClasses.map {
add(composeExtension.generateFunctionKeyMetaClasses.map {
SubpluginOption("generateFunctionKeyMetaClasses", it.toString())
})
if (!project.isAgpComposeEnabled) {
add(composeCompilerExtension.includeSourceInformation.map {
add(composeExtension.includeSourceInformation.map {
SubpluginOption("sourceInformation", it.toString())
})
}
add(composeCompilerExtension.metricsDestination.map<SubpluginOption> {
add(composeExtension.metricsDestination.map<SubpluginOption> {
FilesSubpluginOption("metricsDestination", listOf(it.asFile))
}.orElse(EMPTY_OPTION))
add(composeCompilerExtension.reportsDestination.map<SubpluginOption> {
add(composeExtension.reportsDestination.map<SubpluginOption> {
FilesSubpluginOption("reportsDestination", listOf(it.asFile))
}.orElse(EMPTY_OPTION))
add(composeCompilerExtension.enableIntrinsicRemember.map {
add(composeExtension.enableIntrinsicRemember.map {
SubpluginOption("intrinsicRemember", it.toString())
})
add(composeCompilerExtension.enableNonSkippingGroupOptimization.map {
add(composeExtension.enableNonSkippingGroupOptimization.map {
SubpluginOption("nonSkippingGroupOptimization", it.toString())
})
add(composeCompilerExtension.enableStrongSkippingMode.map {
add(composeExtension.enableStrongSkippingMode.map {
// Rename once the option in Compose compiler is also renamed
SubpluginOption("experimentalStrongSkipping", it.toString())
})
add(composeCompilerExtension.stabilityConfigurationFile.map<SubpluginOption> {
add(composeExtension.stabilityConfigurationFile.map<SubpluginOption> {
FilesSubpluginOption("stabilityConfigurationPath", listOf(it.asFile))
}.orElse(EMPTY_OPTION))
add(composeCompilerExtension.includeTraceMarkers.map {
add(composeExtension.includeTraceMarkers.map {
SubpluginOption("traceMarkersEnabled", it.toString())
})
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/*
* Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/

package org.jetbrains.kotlin.compose.compiler.gradle

import org.gradle.kotlin.dsl.getByType
import org.jetbrains.kotlin.compose.compiler.gradle.testUtils.buildProjectWithMPP
import org.jetbrains.kotlin.compose.compiler.gradle.testUtils.composeOptions
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl
import org.jetbrains.kotlin.gradle.tasks.BaseKotlinCompile
import org.jetbrains.kotlin.gradle.tasks.KotlinNativeCompile
import org.jetbrains.kotlin.gradle.utils.named
import org.junit.jupiter.api.Test
import kotlin.test.assertTrue

class EnabledPlatformsConfigurationTest {

@OptIn(ExperimentalWasmDsl::class)
@Test
fun allKotlinPlatformsAreUsedByDefault() {
val project = buildProjectWithMPP {
extensions.getByType<KotlinMultiplatformExtension>().apply {
jvm()
js { nodejs() }
wasmJs()
wasmWasi()
linuxX64()

applyDefaultHierarchyTemplate()
}
}

project.evaluate()

listOf(
"compileKotlinMetadata",
"compileCommonMainKotlinMetadata",
"compileKotlinJs",
"compileKotlinJvm",
"compileKotlinWasmJs",
"compileKotlinWasmWasi"
).forEach {
assertTrue(
project.tasks.named<BaseKotlinCompile>(it).get().composeOptions().isNotEmpty(),
"'$it' task does not contain compose plugin options"
)
}

assertTrue(
project.tasks.named<KotlinNativeCompile>("compileKotlinLinuxX64").get().composeOptions().isNotEmpty(),
"'compileKotlinLinuxX64' task does not contain compose plugin options"
)
}

@OptIn(ExperimentalWasmDsl::class)
@Test
fun disableKotlinPlatforms() {
val project = buildProjectWithMPP {
extensions.getByType<KotlinMultiplatformExtension>().apply {
jvm()
js { nodejs() }
wasmJs()
wasmWasi()
linuxX64()

applyDefaultHierarchyTemplate()
}

extensions.getByType<ComposeCompilerGradlePluginExtension>()
.targetKotlinPlatforms
.set(setOf(KotlinPlatformType.jvm))
}

project.evaluate()

assertTrue(
project.tasks.named<BaseKotlinCompile>("compileKotlinJvm").get().composeOptions().isNotEmpty(),
"'compileKotlinJvm' task does not contain compose plugin options"
)

listOf(
"compileKotlinMetadata",
"compileCommonMainKotlinMetadata",
"compileKotlinJs",
"compileKotlinWasmJs",
"compileKotlinWasmWasi"
).forEach {
assertTrue(
project.tasks.named<BaseKotlinCompile>(it).get().composeOptions().isEmpty(),
"'$it' task contains compose plugin options"
)
}

assertTrue(
project.tasks.named<KotlinNativeCompile>("compileKotlinLinuxX64").get().composeOptions().isEmpty(),
"'compileKotlinLinuxX64' task contains compose plugin options"
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import org.gradle.kotlin.dsl.named
import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile
import org.junit.jupiter.api.Test
import org.jetbrains.kotlin.compose.compiler.gradle.testUtils.buildProjectWithJvm
import org.jetbrains.kotlin.compose.compiler.gradle.testUtils.composeOptions
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue
Expand Down Expand Up @@ -109,15 +110,10 @@ class ExtensionConfigurationTest {
project.evaluate()

val jvmTask = project.tasks.named<KotlinJvmCompile>("compileKotlin").get()
val composeOptions = jvmTask.pluginOptions.get()
.flatMap { compilerPluginConfig ->
compilerPluginConfig.allOptions().filter { it.key == "androidx.compose.compiler.plugins.kotlin" }.values
}
.flatten()
.map { it.key to it.value }
val composeOptions = jvmTask.composeOptions()

assertions(composeOptions, project)
}

fun Project.simulateAgpPresence() = configurations.resolvable("kotlin-extension")
private fun Project.simulateAgpPresence() = configurations.resolvable("kotlin-extension")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/

package org.jetbrains.kotlin.compose.compiler.gradle.testUtils

import org.jetbrains.kotlin.gradle.tasks.BaseKotlinCompile
import org.jetbrains.kotlin.gradle.tasks.KotlinNativeCompile

fun BaseKotlinCompile.composeOptions() = pluginOptions.get()
.flatMap { compilerPluginConfig ->
compilerPluginConfig.allOptions().filter { it.key == "androidx.compose.compiler.plugins.kotlin" }.values
}
.flatten()
.map { it.key to it.value }

fun KotlinNativeCompile.composeOptions() = compilerPluginOptions
.allOptions()
.filter { it.key == "androidx.compose.compiler.plugins.kotlin" }.values
.flatten()
.map { it.key to it.value }
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import org.gradle.internal.service.scopes.ProjectScopeServices
import org.gradle.testfixtures.ProjectBuilder
import org.gradle.tooling.events.OperationCompletionListener
import org.jetbrains.kotlin.compose.compiler.gradle.ComposeCompilerGradleSubplugin
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.plugin.KotlinPluginWrapper
import java.lang.reflect.Field
import java.util.concurrent.atomic.AtomicReference
Expand Down Expand Up @@ -45,6 +46,17 @@ fun buildProjectWithJvm(
code()
}

fun buildProjectWithMPP(
projectBuilder: ProjectBuilder.() -> Unit = { },
preApplyCode: Project.() -> Unit = {},
code: Project.() -> Unit = {}
) = buildProject(projectBuilder) {
preApplyCode()
project.applyMultiplatformPlugin()
project.applyKotlinComposePlugin()
code()
}

/**
* In Gradle 6.7-rc-1 BuildEventsListenerRegistry service is not created in we need it in order
* to instantiate AGP. This creates a fake one and injects it - http://b/168630734.
Expand Down Expand Up @@ -96,4 +108,15 @@ fun Project.applyKotlinJvmPlugin() {

fun Project.applyKotlinComposePlugin() {
project.plugins.apply(ComposeCompilerGradleSubplugin::class.java)
}

fun Project.applyMultiplatformPlugin(): KotlinMultiplatformExtension {
addBuildEventsListenerRegistryMock(this)
disableLegacyWarning(project)
plugins.apply("kotlin-multiplatform")
return extensions.getByName("kotlin") as KotlinMultiplatformExtension
}

internal fun disableLegacyWarning(project: Project) {
project.extraProperties.set("kotlin.js.compiler.nowarn", "true")
}

0 comments on commit 6d12bb7

Please sign in to comment.