Make Android ProGuard/R8 rules configurable from pyproject.toml - #6741
Open
FeodorFitsner wants to merge 1 commit into
Open
Make Android ProGuard/R8 rules configurable from pyproject.toml#6741FeodorFitsner wants to merge 1 commit into
FeodorFitsner wants to merge 1 commit into
Conversation
The generated Android project's app/proguard-rules.pro was a fixed template
file, so an app that needed an extra keep rule had no way to add one short of
downloading the published build template, patching the file and passing
--template.
This matters for pyjnius. `autoclass()` resolves Java classes by their
fully-qualified name at runtime, and R8 renames anything in the APK that is not
kept, so `autoclass()` on a class bundled by a Flutter plugin or by the app's
own Java/Kotlin fails in release builds. It fails hard: JNI FindClass returns
null and the process aborts -- "JNI DETECTED ERROR IN APPLICATION: obj == null"
followed by SIGABRT on a debuggable build, and usually no message at all on a
production one -- rather than raising a catchable Python exception. Because R8
only runs in release builds it never reproduces in debug. Android framework
classes (android.os.Build and friends) live outside the APK and never needed a
rule.
Adds two settings, because adding and removing rules are different problems:
[tool.flet.android]
proguard_rules = ["-keep class com.example.mylib.** { *; }"]
proguard_default_rules = false
`proguard_rules` appends to the defaults, since R8 has no directive that undoes
a keep -- a user rule can only ever widen what is kept. Removing a default
therefore needs its own switch, and `proguard_default_rules = false` is the
only way to shed `-keepnames class * { *; }`. That rule keeps every class and
member name in the app, switching off obfuscation app-wide and blocking the R8
passes that rely on renaming; dropping it takes Flet Studio's classes.dex from
5,885,600 to 3,351,712 bytes (-43%), measured with everything else held
constant.
Dropping the defaults is safe for pyjnius's PythonActivity access:
serious_python_android 4.1.0+ ships the same keep rule in its own
consumer-rules.pro, so it applies whether or not the template repeats it.
Verified on device -- with the template rule removed, PythonActivity.mActivity
still resolves under 8,209 renamed classes.
The two settings are kept separate on purpose. If the only option were to
replace, users would paste today's defaults into their pyproject.toml and
silently keep them after the defaults change.
Follows the existing pattern for android.permission / android.meta_data /
android.gradle_properties: defaults in setup_template_data(), combined with the
pyproject value, passed through template_data["options"], rendered by the
template. With no settings the template renders byte-identically to the old
static file, so existing builds are unaffected.
Deploying flet-website-v2 with
|
| Latest commit: |
25b5933
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://6c3b1866.flet-website-v2.pages.dev |
| Branch Preview URL: | https://feat-android-proguard-rules.flet-website-v2.pages.dev |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The generated Android project's
app/proguard-rules.prois a fixed template file, so an app that needs an extra keep rule has no way to add one short of downloading the published build template, patching the file and passing--template.Why this matters
R8 renames classes in release builds. Pyjnius's
autoclass()resolves Java classes by their fully-qualified name at runtime, soautoclass()on a class bundled by a Flutter plugin — or by the app's own Java/Kotlin — fails once R8 has renamed it.It fails hard. JNI
FindClassreturns null and the process aborts:That's a debuggable build, where CheckJNI supplies the message; on a production build there is usually no message at all, just a crash. It is not a catchable Python exception, so a
try/exceptaroundautoclass()does not help. And because R8 only runs in release builds, it never reproduces in debug.Android framework classes (
android.os.Buildand friends) live outside the APK and never needed a rule.What's added
Two settings, because adding and removing rules are different problems:
proguard_rulesappends to the defaults. R8 has no directive that undoes a keep, so a user rule can only ever widen what is kept.proguard_default_rules = falsedrops the defaults. This is the only way to shed-keepnames class * { *; }.They're deliberately separate. If the only option were to replace, users would paste today's defaults into their
pyproject.tomland silently hold them after the defaults change.On dropping the defaults
-keepnames class * { *; }keeps every class and member name in the app, switching off obfuscation app-wide and blocking the R8 passes that rely on renaming. Measured on Flet Studio with everything else held constant (samebuild/fluttertree, only Gradle re-run):classes.dex-keepnamesNote the AAB total barely moves, because
proguard.mapgrows by a similar amount — but that lives underBUNDLE-METADATA/and is never delivered to devices, so the on-device saving is the full 2.5 MB.Dropping the defaults is safe for pyjnius's
PythonActivityaccess:serious_python_android4.1.0+ ships-keep class com.flet.serious_python_android.** { *; }in its ownconsumer-rules.pro, so it applies whether or not the template repeats it. Verified on a device (moto g15, Android 15) — with the template rule removed,PythonActivity.mActivitystill resolves under 8,209 renamed classes.Testing
Real builds against this branch's template, checking the generated
proguard-rules.pro:proguard_rules = [...]proguard_default_rules = false+ rulesAlso exercised the failure this unblocks: a release build with
-keepnamesnarrowed crashes with the SIGABRT above onautoclass("io.flutter.embedding.android.FlutterActivity")(renamed tox2.d), and passes with a keep rule added through the new setting.Defaults are unchanged, so existing builds are unaffected.
Follows existing patterns
Same shape as
android.permission/android.meta_data/android.gradle_properties: defaults insetup_template_data(), combined with the pyproject value, passed throughtemplate_data["options"], rendered by the template.Summary by Sourcery
Make Android ProGuard/R8 rules in generated projects configurable via pyproject.toml while preserving existing defaults by default.
New Features:
[tool.flet.android].proguard_rulesand[tool.flet.android].proguard_default_rulesto control default and custom keep rules.Enhancements:
proguard-rules.profrom the configured rule set instead of a fixed static file.Documentation:
pyproject.toml, including when Pyjnius-based apps need keep rules and the effect of dropping default rules.