diff --git a/libraries/tools/kotlin-compose-compiler/src/common/kotlin/org/jetbrains/kotlin/compose/compiler/gradle/ComposeCompilerGradlePluginExtension.kt b/libraries/tools/kotlin-compose-compiler/src/common/kotlin/org/jetbrains/kotlin/compose/compiler/gradle/ComposeCompilerGradlePluginExtension.kt index a2278def1780a..d7bc543056551 100644 --- a/libraries/tools/kotlin-compose-compiler/src/common/kotlin/org/jetbrains/kotlin/compose/compiler/gradle/ComposeCompilerGradlePluginExtension.kt +++ b/libraries/tools/kotlin-compose-compiler/src/common/kotlin/org/jetbrains/kotlin/compose/compiler/gradle/ComposeCompilerGradlePluginExtension.kt @@ -230,4 +230,14 @@ abstract class ComposeCompilerGradlePluginExtension @Inject internal constructor if (nonSkippingGroupsOptimization) features + ComposeFeatureFlag.OptimizeNonSkippingGroups else features } ) + + /** + * Enable addition of Compose-specific entries to the proguard mapping file + * produced by optimizers (such as R8) when compiling Android applications. + * These entries are used to deobfuscate group key based Compose stack traces + * in minified applications. + * + * Enabled by default. + */ + val includeComposeMappingFile: Property = objectFactory.property(Boolean::class.java).convention(true) } diff --git a/libraries/tools/kotlin-compose-compiler/src/common/kotlin/org/jetbrains/kotlin/compose/compiler/gradle/ComposeCompilerSubplugin.kt b/libraries/tools/kotlin-compose-compiler/src/common/kotlin/org/jetbrains/kotlin/compose/compiler/gradle/ComposeCompilerSubplugin.kt index 77db44ceff21e..18d4d07a42aa0 100644 --- a/libraries/tools/kotlin-compose-compiler/src/common/kotlin/org/jetbrains/kotlin/compose/compiler/gradle/ComposeCompilerSubplugin.kt +++ b/libraries/tools/kotlin-compose-compiler/src/common/kotlin/org/jetbrains/kotlin/compose/compiler/gradle/ComposeCompilerSubplugin.kt @@ -32,7 +32,7 @@ class ComposeCompilerGradleSubplugin : KotlinCompilerPluginSupportPlugin { override fun apply(target: Project) { composeExtension = target.extensions.create("composeCompiler", ComposeCompilerGradlePluginExtension::class.java) - target.configureComposeMappingFile() + target.configureComposeMappingFile(enabled = composeExtension.includeComposeMappingFile) } override fun isApplicable(kotlinCompilation: KotlinCompilation<*>): Boolean { diff --git a/libraries/tools/kotlin-compose-compiler/src/common/kotlin/org/jetbrains/kotlin/compose/compiler/gradle/internal/ComposeAgpMappingFile.kt b/libraries/tools/kotlin-compose-compiler/src/common/kotlin/org/jetbrains/kotlin/compose/compiler/gradle/internal/ComposeAgpMappingFile.kt index fc2772b990242..13c69f472449c 100644 --- a/libraries/tools/kotlin-compose-compiler/src/common/kotlin/org/jetbrains/kotlin/compose/compiler/gradle/internal/ComposeAgpMappingFile.kt +++ b/libraries/tools/kotlin-compose-compiler/src/common/kotlin/org/jetbrains/kotlin/compose/compiler/gradle/internal/ComposeAgpMappingFile.kt @@ -19,6 +19,7 @@ import org.gradle.api.file.Directory import org.gradle.api.file.RegularFile import org.gradle.api.file.RegularFileProperty import org.gradle.api.provider.ListProperty +import org.gradle.api.provider.Property import org.gradle.api.tasks.* import org.gradle.kotlin.dsl.findByType import org.gradle.kotlin.dsl.register @@ -33,7 +34,9 @@ import java.util.zip.ZipFile import javax.inject.Inject -internal fun Project.configureComposeMappingFile() { +internal fun Project.configureComposeMappingFile( + enabled: Property +) { plugins.withId("com.android.application") { val configuration = configurations.maybeCreate("composeMappingProducerClasspath") .also { @@ -46,6 +49,7 @@ internal fun Project.configureComposeMappingFile() { } extensions.findByType()?.onVariants { variant -> + if (!enabled.get()) return@onVariants if (!variant.isMinifyEnabled) return@onVariants val name = variant.name.capitalize() diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/build.gradle.kts index 02848f71f1ffe..0bfb9df265cb1 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/build.gradle.kts +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/build.gradle.kts @@ -130,6 +130,8 @@ dependencies { testApi(project(":compiler:tests-mutes:mutes-junit5")) testCompileOnly(libs.intellij.asm) + + testImplementation(project(":compose-compiler-gradle-plugin")) } tasks.register("cleanTestKitCache") { diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/ComposeIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/ComposeIT.kt index f21f123ee3105..16c69f5ec2ec1 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/ComposeIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/ComposeIT.kt @@ -7,9 +7,9 @@ package org.jetbrains.kotlin.gradle import com.android.build.api.variant.ApplicationAndroidComponentsExtension import org.gradle.api.logging.LogLevel -import org.gradle.api.logging.configuration.WarningMode import org.gradle.kotlin.dsl.getByType import org.gradle.util.GradleVersion +import org.jetbrains.kotlin.compose.compiler.gradle.ComposeCompilerGradlePluginExtension import org.jetbrains.kotlin.gradle.testbase.* import org.jetbrains.kotlin.test.TestMetadata import org.junit.jupiter.api.DisplayName @@ -715,6 +715,46 @@ class ComposeIT : KGPBaseTest() { } } + @DisplayName("Minified app with disabled mapping does not run Compose tasks.") + @AndroidGradlePluginTests + @GradleAndroidTest + @DisabledOnOs( + OS.WINDOWS, disabledReason = "AGP contains a bug that prevents test output files from being cleaned up on Windows. " + + "See: https://issuetracker.google.com/issues/445967244" + ) + @TestMetadata("AndroidSimpleComposeApp") + fun testMinifyWithComposeDisabled( + gradleVersion: GradleVersion, + agpVersion: String, + providedJdk: JdkVersions.ProvidedJdk + ) { + project( + projectName = "AndroidSimpleComposeApp", + gradleVersion = gradleVersion, + buildJdk = providedJdk.location, + buildOptions = defaultBuildOptions + .copy(androidVersion = agpVersion) + ) { + buildScriptInjection { + val appExtension = project.extensions.getByType() + appExtension.beforeVariants { + if (it.name == "release") { + it.isMinifyEnabled = true + } + } + val composeExtension = project.extensions.getByType() + composeExtension.includeComposeMappingFile.set(false) + } + + build("assembleRelease") { + assertTasksAreNotInTaskGraph( + ":produceReleaseComposeMapping", + ":mergeReleaseComposeMapping" + ) + } + } + } + private fun Path.appendComposePlugin() { modify { originalBuildScript -> """