diff --git a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/dsl/ProguardSettings.kt b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/dsl/ProguardSettings.kt index 685f23b667..cb1b2153c2 100644 --- a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/dsl/ProguardSettings.kt +++ b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/dsl/ProguardSettings.kt @@ -21,4 +21,5 @@ abstract class ProguardSettings @Inject constructor( val maxHeapSize: Property = objects.nullableProperty() val configurationFiles: ConfigurableFileCollection = objects.fileCollection() val isEnabled: Property = objects.notNullProperty(false) + val obfuscate: Property = objects.notNullProperty(false) } diff --git a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/internal/configureJvmApplication.kt b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/internal/configureJvmApplication.kt index 577da2bb8a..13e30ade3c 100644 --- a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/internal/configureJvmApplication.kt +++ b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/internal/configureJvmApplication.kt @@ -242,6 +242,15 @@ private fun JvmApplicationContext.configureProguardTask( mainClass.set(app.mainClass) proguardVersion.set(settings.version) configurationFiles.from(settings.configurationFiles) + // ProGuard uses -dontobfuscate option to turn off obfuscation, which is enabled by default + // We want to disable obfuscation by default, because often + // it is not needed, but makes troubleshooting much harder. + // If obfuscation is turned off by default, + // enabling (`isObfuscationEnabled.set(true)`) seems much better, + // than disabling obfuscation disabling (`dontObfuscate.set(false)`). + // That's why a task property is follows ProGuard design, + // when our DSL does the opposite. + dontobfuscate.set(settings.obfuscate.map { !it }) dependsOn(unpackDefaultResources) defaultComposeRulesFile.set(unpackDefaultResources.flatMap { it.resources.defaultComposeProguardRules }) diff --git a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/tasks/AbstractProguardTask.kt b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/tasks/AbstractProguardTask.kt index 859a8a978c..86482eaaef 100644 --- a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/tasks/AbstractProguardTask.kt +++ b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/tasks/AbstractProguardTask.kt @@ -40,6 +40,10 @@ abstract class AbstractProguardTask : AbstractComposeDesktopTask() { @get:InputFiles val configurationFiles: ConfigurableFileCollection = objects.fileCollection() + @get:Optional + @get:Input + val dontobfuscate: Property = objects.nullableProperty() + // todo: DSL for excluding default rules // also consider pulling coroutines rules from coroutines artifact // https://github.com/Kotlin/kotlinx.coroutines/blob/master/kotlinx-coroutines-core/jvm/resources/META-INF/proguard/coroutines.pro @@ -107,6 +111,10 @@ abstract class AbstractProguardTask : AbstractComposeDesktopTask() { } rootConfigurationFile.ioFile.bufferedWriter().use { writer -> + if (dontobfuscate.orNull == true) { + writer.writeLn("-dontobfuscate") + } + writer.writeLn(""" -keep public class ${mainClass.get()} { public static void main(java.lang.String[]); diff --git a/gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/DesktopApplicationTest.kt b/gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/DesktopApplicationTest.kt index 920425e3cc..152dfb6ea0 100644 --- a/gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/DesktopApplicationTest.kt +++ b/gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/DesktopApplicationTest.kt @@ -16,6 +16,7 @@ import java.util.* import java.util.jar.JarFile import kotlin.collections.HashSet import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertFalse import org.junit.jupiter.api.Assumptions import org.junit.jupiter.api.Test @@ -77,17 +78,43 @@ class DesktopApplicationTest : GradlePluginTestBase() { @Test fun proguard(): Unit = with(testProject(TestProjects.proguard)) { - gradle(":runReleaseDistributable").build().checks { check -> - check.taskOutcome(":proguardReleaseJars", TaskOutcome.SUCCESS) + val enableObfuscation = """ + compose.desktop { + application { + buildTypes.release.proguard { + obfuscate.set(true) + } + } + } + """.trimIndent() - assertEqualTextFiles(file("main-methods.actual.txt"), file("main-methods.expected.txt")) + val actualMainImage = file("main-image.actual.png") + val expectedMainImage = file("main-image.expected.png") - val actualMainImage = file("main-image.actual.png") - val expectedMainImage = file("main-image.expected.png") + fun checkImageBeforeBuild() { + assertFalse(actualMainImage.exists(), "'$actualMainImage' exists") + } + fun checkImageAfterBuild() { assert(actualMainImage.readBytes().contentEquals(expectedMainImage.readBytes())) { "The actual image '$actualMainImage' does not match the expected image '$expectedMainImage'" } } + + checkImageBeforeBuild() + gradle(":runReleaseDistributable").build().checks { check -> + check.taskOutcome(":proguardReleaseJars", TaskOutcome.SUCCESS) + checkImageAfterBuild() + assertEqualTextFiles(file("main-methods.actual.txt"), file("main-methods.expected.txt")) + } + + file("build.gradle").modify { "$it\n$enableObfuscation" } + actualMainImage.delete() + checkImageBeforeBuild() + gradle(":runReleaseDistributable").build().checks { check -> + check.taskOutcome(":proguardReleaseJars", TaskOutcome.SUCCESS) + checkImageAfterBuild() + assertNotEqualTextFiles(file("main-methods.actual.txt"), file("main-methods.expected.txt")) + } } @Test diff --git a/gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/utils/assertUtils.kt b/gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/utils/assertUtils.kt index 45535445df..765b5512e6 100644 --- a/gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/utils/assertUtils.kt +++ b/gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/utils/assertUtils.kt @@ -51,13 +51,20 @@ internal fun String.checkContains(substring: String) { } internal fun assertEqualTextFiles(actual: File, expected: File) { - fun File.normalizedText() = readLines().joinToString("\n") { it.trim() } - - val actualText = actual.normalizedText() - val expectedText = expected.normalizedText() Assertions.assertEquals( - expectedText, - actualText, - "Expected file '$expected' differs from actual file '$actual'" + expected.normalizedText(), + actual.normalizedText(), + "Content of '$expected' is not equal to content of '$actual'" + ) +} + +internal fun assertNotEqualTextFiles(actual: File, expected: File) { + Assertions.assertNotEquals( + expected.normalizedText(), + actual.normalizedText(), + "Content of '$expected' is equal to content of '$actual'" ) -} \ No newline at end of file +} + +private fun File.normalizedText() = + readLines().joinToString("\n") { it.trim() } diff --git a/gradle-plugins/compose/src/test/test-projects/application/proguard/rules.pro b/gradle-plugins/compose/src/test/test-projects/application/proguard/rules.pro index ae99cff552..4e0f5c6865 100644 --- a/gradle-plugins/compose/src/test/test-projects/application/proguard/rules.pro +++ b/gradle-plugins/compose/src/test/test-projects/application/proguard/rules.pro @@ -1,7 +1,3 @@ -keep public class Main { public void keptByKeepRule(...); -} - --keepclassmembernames public class Main { - *; } \ No newline at end of file