Files
agendula/scripts/check_r8_keeps.py
T
Jean-Luc Makiolaandmakiolaj 08c297f30a
Release — F-Droid repo + Gitea/Codeberg release + Play / detect (push) Successful in 8s
Renovate / renovate (push) Successful in 1m11s
Release — F-Droid repo + Gitea/Codeberg release + Play / release (push) Successful in 18m4s
Release — F-Droid repo + Gitea/Codeberg release + Play / play (push) Failing after 1m33s
Beta — Codeberg pre-release / detect (push) Successful in 5s
Beta — Codeberg pre-release / beta (push) Skipped
Release 1.2.0 (#103)
Stable 1.2.0, after 1.2.0-beta.1 and beta.2.

- Version bumped to 1.2.0 (1020099); the CHANGELOG entries moved from Unreleased to 1.2.0.
- What's New for every shipped locale, now including cs-CZ.
- Full store listing (title, short and full description) for every locale; new for cs-CZ, ar and fr-FR. Play only gets these through the `listing` lane.
- `main` is merged in with the branch side kept. #81 and the Weblate updates already came in through the #88 squash, and the only lines unique to `main` were string keys that #88 removed.

The #75 fix landed after beta.2, so no reporter has tested it yet.

Closes #72, closes #78, closes #75, closes #92

Co-authored-by: Jean-Luc Makiola <business@jeanlucmakiola.de>
Reviewed-on: https://codeberg.org/jlmakiola/agendula/pulls/103
2026-10-26 20:41:58 +01:00

56 lines
2.1 KiB
Python
Executable File

#!/usr/bin/env python3
"""Fail when R8 keeps one of our classes by name but strips its no-arg constructor.
A keep rule that names a class but no members keeps the class and lets R8 drop
the constructor nothing calls directly. Anything that then instantiates it by
reflection (Room, Glance's ActionCallback, WorkManager) fails at runtime only,
in release builds only (#1, #75). R8 reports both halves: seeds.txt lists what
keep rules pinned, usage.txt what it removed.
scripts/check_r8_keeps.py [mapping dir] default: fullReleaseTest's
"""
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
DEFAULT_DIR = ROOT / "app/build/outputs/mapping/fullReleaseTest"
PACKAGES = ("de.jeanlucmakiola.",)
def kept_classes(seeds: Path) -> set[str]:
return {
line.strip()
for line in seeds.read_text().splitlines()
if line.startswith(PACKAGES) and ":" not in line
}
def classes_without_default_ctor(usage: Path) -> set[str]:
stripped, current = set(), None
for line in usage.read_text().splitlines():
if not line.startswith(" "):
current = line.rstrip(":") if line.endswith(":") else None
elif current and line.strip().endswith(" <init>()"):
stripped.add(current)
return stripped
def main() -> int:
mapping = Path(sys.argv[1]) if len(sys.argv) > 1 else DEFAULT_DIR
seeds, usage = mapping / "seeds.txt", mapping / "usage.txt"
if not seeds.exists() or not usage.exists():
print(f"ERROR: no seeds.txt/usage.txt in {mapping}; build a minified variant first.", file=sys.stderr)
return 1
broken = sorted(kept_classes(seeds) & classes_without_default_ctor(usage))
for name in broken:
print(f"ERROR: {name} is kept by a rule but R8 removed its no-arg constructor.", file=sys.stderr)
if broken:
print("Keep the constructor too: -keep class ... { <init>(); }", file=sys.stderr)
return 1
print(f"OK: every kept class under {', '.join(PACKAGES)} still has its no-arg constructor.")
return 0
if __name__ == "__main__":
sys.exit(main())