Closed ScottPierce closed 1 month ago
Would love to see R8 support for code obfuscation. This is currently blocking me from publishing my app.
@Thomas-Vos look at the link I posted where a gradle project has a task to run r8 on it.
You should be able to do it yourself in 30 minutes or so:
@ScottPierce Is there a way to run R8 selectively on jars e.g. not to obfuscate (certain) third-parties instead of user-jar way?
This has to be somewhat easily hackable to achieve that, just the 30 minutes was not enough for me with my gradle-fu. Generally we would all appreciate if one would share sample gist gradle.kts file for CfD app so that we don't have to arrive to the same solution independently 🙏 .
R8 has similar functionality and accepts the same configs as Proguard. You can tell it to ignore certain packages.
I finally managed to do that with ProGuard (example setup), without uberjar though. It's actually not that hard. I guess it should be similar for R8.
No uber jar has benefits imo. While doing updates, that would allow you to only swap out the file that's been changed. Uber jars with compose can be 50+ MB
Another point that was brought up about R8 - R8 has the ability to read rules directly from jars, meaning that its a significantly better user experience to build a library ecosystem with.
Taking into account all the recent problems with ProGuard, e.g., https://github.com/JetBrains/compose-multiplatform/issues/3387 https://github.com/JetBrains/compose-multiplatform/pull/3408#discussion_r1274012736 https://github.com/square/okio/issues/1298 it may be worthwile to reconsider supporting R8 on desktops again.
Dropping this tweet here. It might be useful.
bump
Compose Desktop seems to be speed-running the problems of Android from a decade past by not using R8.
I think right now the biggest problem is that whilst the Compose/Desktop documentation states that proguard is supported it is not really being fully used even on official-ish examples. Even if rules are not auto-bundled, just enabling proguard (including obfuscation) for release jvm builds would uncover many (if not most) issues.
Another point that was brought up about R8 - R8 has the ability to read rules directly from jars, meaning that its a significantly better user experience to build a library ecosystem with.
This feature is usually easy to implement. Use JarFile
to read the fat JAR file. You will usually find the rules of the supported libraries in META-INF/proguard
of the JAR file
Which will be included by libraries even if you don't use Proguard.
This approach is less flexible and could produce issues later. For most use cases and projects, it works as expected.
Unless I'm mistaken, R8 uses the rules from META-INF/com.android.tools/r8
and fallback to META-INF/proguard
Examples:
JarFile(shadowJarFile.get().asFile).use { jarFile ->
val generatedProguardFile =
project.layout.buildDirectory.file("proguard/generated-proguard-libraries-rules.pro").get()
.asFile
if (!generatedProguardFile.exists()) {
generatedProguardFile.parentFile.mkdir()
}
val generatedRulesFiles =
jarFile.entries().asSequence()
.filter { it.name.startsWith("META-INF/proguard") && !it.isDirectory }
.map { entry ->
jarFile.getInputStream(entry).bufferedReader().use { reader ->
Pair(reader.readText(), entry)
}
}
.toList()
generatedProguardFile.bufferedWriter().use { bufferedWriter ->
bufferedWriter.appendLine("# GENERATED FILE - manual changes will be overwritten")
bufferedWriter.appendLine()
generatedRulesFiles.forEach { (rulesContent, rulesFileEntry) ->
bufferedWriter.appendLine("# START of ($rulesFileEntry)")
bufferedWriter.appendLine()
bufferedWriter.appendLine(rulesContent)
bufferedWriter.appendLine("# END of ($rulesFileEntry)")
bufferedWriter.appendLine()
}
}
configuration(generatedProguardFile)
}
Which will have all rules from all libraries in a single file.
Or separate the rules for each library:
JarFile(shadowJarFile.get().asFile).use { jarFile ->
val generatedRulesFiles =
jarFile.entries().asSequence()
.filter { it.name.startsWith("META-INF/proguard") && !it.isDirectory }
.map { entry ->
jarFile.getInputStream(entry).bufferedReader().use { reader ->
Pair(reader.readText(), entry)
}
}
.toList()
val buildProguardDirectory = project.layout.buildDirectory.dir("proguard").get().asFile
if (!buildProguardDirectory.exists()) {
buildProguardDirectory.mkdir()
}
generatedRulesFiles.forEach { (rulesContent, rulesFileEntry) ->
val rulesFileNameWithExtension = rulesFileEntry.name.substringAfterLast("/")
val generatedProguardFile = File(buildProguardDirectory, "generated-$rulesFileNameWithExtension")
if (!generatedProguardFile.exists()) {
generatedProguardFile.createNewFile()
}
generatedProguardFile.bufferedWriter().use { bufferedWriter ->
bufferedWriter.appendLine("# Generated file from ($rulesFileEntry) - manual changes will be overwritten")
bufferedWriter.appendLine()
bufferedWriter.appendLine(rulesContent)
}
configuration(generatedProguardFile)
}
}
Please check the following ticket on YouTrack for follow-ups to this issue. GitHub issues will be closed in the coming weeks.
Please check the following ticket on YouTrack for follow-ups to this issue. GitHub issues will be closed in the coming weeks.
Please check the following ticket on YouTrack for follow-ups to this issue. GitHub issues will be closed in the coming weeks.
The performance benefits for R8 and Compose on Android is significant (supposedly because of its optimizations of Lambdas). I recently found out that R8 can just be run on Java class files. It'd be nice to be able to add support to use R8 directly from the compose Gradle plugin on Compose Desktop projects, so that people can easily take advantage of the performance improvements on Compose Desktop.