Browse Source

ProGuard: require obfuscation be enabled explicitly (#2384)

pull/1997/merge
Alexey Tsvetkov 2 years ago committed by GitHub
parent
commit
d5af9623f5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/dsl/ProguardSettings.kt
  2. 9
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/internal/configureJvmApplication.kt
  3. 8
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/tasks/AbstractProguardTask.kt
  4. 37
      gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/DesktopApplicationTest.kt
  5. 23
      gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/utils/assertUtils.kt
  6. 4
      gradle-plugins/compose/src/test/test-projects/application/proguard/rules.pro

1
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<String?> = objects.nullableProperty() val maxHeapSize: Property<String?> = objects.nullableProperty()
val configurationFiles: ConfigurableFileCollection = objects.fileCollection() val configurationFiles: ConfigurableFileCollection = objects.fileCollection()
val isEnabled: Property<Boolean> = objects.notNullProperty(false) val isEnabled: Property<Boolean> = objects.notNullProperty(false)
val obfuscate: Property<Boolean> = objects.notNullProperty(false)
} }

9
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) mainClass.set(app.mainClass)
proguardVersion.set(settings.version) proguardVersion.set(settings.version)
configurationFiles.from(settings.configurationFiles) 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) dependsOn(unpackDefaultResources)
defaultComposeRulesFile.set(unpackDefaultResources.flatMap { it.resources.defaultComposeProguardRules }) defaultComposeRulesFile.set(unpackDefaultResources.flatMap { it.resources.defaultComposeProguardRules })

8
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/tasks/AbstractProguardTask.kt

@ -40,6 +40,10 @@ abstract class AbstractProguardTask : AbstractComposeDesktopTask() {
@get:InputFiles @get:InputFiles
val configurationFiles: ConfigurableFileCollection = objects.fileCollection() val configurationFiles: ConfigurableFileCollection = objects.fileCollection()
@get:Optional
@get:Input
val dontobfuscate: Property<Boolean?> = objects.nullableProperty()
// todo: DSL for excluding default rules // todo: DSL for excluding default rules
// also consider pulling coroutines rules from coroutines artifact // 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 // 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 -> rootConfigurationFile.ioFile.bufferedWriter().use { writer ->
if (dontobfuscate.orNull == true) {
writer.writeLn("-dontobfuscate")
}
writer.writeLn(""" writer.writeLn("""
-keep public class ${mainClass.get()} { -keep public class ${mainClass.get()} {
public static void main(java.lang.String[]); public static void main(java.lang.String[]);

37
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 java.util.jar.JarFile
import kotlin.collections.HashSet import kotlin.collections.HashSet
import org.junit.jupiter.api.Assertions.assertEquals 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.Assumptions
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
@ -77,17 +78,43 @@ class DesktopApplicationTest : GradlePluginTestBase() {
@Test @Test
fun proguard(): Unit = with(testProject(TestProjects.proguard)) { fun proguard(): Unit = with(testProject(TestProjects.proguard)) {
gradle(":runReleaseDistributable").build().checks { check -> val enableObfuscation = """
check.taskOutcome(":proguardReleaseJars", TaskOutcome.SUCCESS) 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") fun checkImageBeforeBuild() {
val expectedMainImage = file("main-image.expected.png") assertFalse(actualMainImage.exists(), "'$actualMainImage' exists")
}
fun checkImageAfterBuild() {
assert(actualMainImage.readBytes().contentEquals(expectedMainImage.readBytes())) { assert(actualMainImage.readBytes().contentEquals(expectedMainImage.readBytes())) {
"The actual image '$actualMainImage' does not match the expected image '$expectedMainImage'" "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 @Test

23
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) { 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( Assertions.assertEquals(
expectedText, expected.normalizedText(),
actualText, actual.normalizedText(),
"Expected file '$expected' differs from actual file '$actual'" "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'"
) )
} }
private fun File.normalizedText() =
readLines().joinToString("\n") { it.trim() }

4
gradle-plugins/compose/src/test/test-projects/application/proguard/rules.pro

@ -1,7 +1,3 @@
-keep public class Main { -keep public class Main {
public void keptByKeepRule(...); public void keptByKeepRule(...);
}
-keepclassmembernames public class Main {
*;
} }
Loading…
Cancel
Save