Document dist-cache gotcha for python_depends changes, Settings label bug

CLAUDE.md was out of date after this session's two on-device fixes
(commit d276f88): still described the kivy recipe as python_depends=[]
(filetype is back), and didn't mention the p4a dist-caching trap that
made the filetype fix silently ineffective until the stale dist
directory was deleted, or the Settings popup label wrapping bug.
Cross-referenced with android-app-template's CLAUDE.md, which hit the
same label bug in more places.
This commit is contained in:
AI_Assistant 2026-08-22 13:22:59 +00:00
commit 4a617469b8
No known key found for this signature in database

View file

@ -9,11 +9,13 @@ Context for future Claude sessions working on this repo.
relocatable data path, taskbar icon, "Time Since" column.
- Repo is pushed to `repo.tas-tech.net/tas-tech.net/event-tracker`
(private — will go public once there's a tagged release).
- **Android build (`android/`) works end-to-end** — `./build.sh android
debug` produces `android/bin/eventtracker-0.1-arm64-v8a-debug.apk`.
Confirmed via a full `BUILD SUCCESSFUL` Gradle run, not just "recipes
compiled." Three real, distinct bugs had to be fixed to get here (all
now baked into `build.sh`/`buildozer.spec`, nothing left to redo):
- **Android build (`android/`) works end-to-end AND is confirmed running
on-device** — `./build.sh android debug` produces
`android/bin/eventtracker-0.1-arm64-v8a-debug.apk`, installs, and
launches clean (`adb logcat` shows `Start application main loop`, no
crash). Five real, distinct bugs had to be fixed to get here (all now
baked into `build.sh`/`buildozer.spec`/`main.py`, nothing left to
redo):
1. p4a's `kivy` recipe pulling in unused `requests`/`charset-normalizer`
that couldn't actually install — `android/p4a-recipes/kivy`.
2. p4a's own internal pymodules venv corrupting itself on every
@ -21,9 +23,19 @@ Context for future Claude sessions working on this repo.
3. Gradle's build-script compiler rejecting this machine's default JDK
(java-26) — `build.sh` pins `JAVA_HOME` to `java-17-openjdk` for
just this build.
4. Fix #1 above was applied too aggressively and dropped `filetype`
too, which `kivy/core/image` genuinely needs — on-device startup
crash (`ModuleNotFoundError: No module named 'filetype'`). Restored
just `filetype`.
5. p4a caches a built dist by recipe-name match and never re-derives
a recipe's `python_depends` for an existing dist, so fix #4 above
silently had zero effect until the stale dist directory
(`.buildozer/android/platform/build-<arch>/dists/eventtracker`) was
deleted to force a re-derive. Traced end-to-end through
`pythonforandroid/distribution.py`/`graph.py`, not guessed — see
the dedicated entry below.
See "Android companion — design decisions" below for the full
root-cause writeup on each. Not yet installed/tested on an actual
phone — that's the next real checkpoint, not another build fix.
root-cause writeup on each.
- **Outstanding, agreed but not implemented**: split `config.json` into
two files — event list (safe to sync between machines) vs. data file
path (must stay local per-device, same principle as the Android app's
@ -158,6 +170,18 @@ android/
- **No matplotlib on Android** — unreliable through Kivy's garden
packaging. `BarRow` in `main.py` hand-draws a proportional bar with
Kivy's own `Color`/`Rectangle` canvas primitives instead.
- **Wrapping `Label`s bind `text_size` to their own `width`, never set
it once from `self.width` in a `Popup.__init__`** — `SettingsPopup`'s
info label originally did `text_size=(self.width, None)` at
construction time, but the Popup hasn't been laid out yet at that
point, so `self.width` is Kivy's ~100px pre-layout default rather
than the popup's real size. That locked the label into wrapping at
~100px forever — rendered on-device as a 1-character-wide vertical
column of text. Fixed by binding instead:
`label.bind(width=lambda inst, w: setattr(inst, "text_size", (w, None)))`
after construction. Same bug, same fix, also applied to
`android-app-template`'s `SettingsPopup`/`LegalPopup` (that repo had
it in four places) — see that repo's `CLAUDE.md`.
- **Config path is local-only, same principle as desktop's `CONFIG_PATH`**
— each device's app-private storage holds only that device's CSV
path; it is never itself synced. Only the CSV — the actual data — goes
@ -192,11 +216,11 @@ android/
- **`p4a-recipes/kivy` — local override, not a workaround** — p4a's own
built-in `kivy` recipe declares
`python_depends = ['certifi', 'chardet', 'idna', 'requests', 'urllib3', 'filetype']`
(for `kivy.network.UrlRequest`, which this app never calls).
`requests` transitively needs `charset-normalizer`, whose newest
release is the first to ship a prebuilt Android-tagged wheel (new,
tied to CPython 3.14's official Android build target). p4a's own
dependency resolver (`process_python_modules` in
(most of that list only for `kivy.network.UrlRequest`, which this app
never calls). `requests` transitively needs `charset-normalizer`,
whose newest release is the first to ship a prebuilt Android-tagged
wheel (new, tied to CPython 3.14's official Android build target).
p4a's own dependency resolver (`process_python_modules` in
`pythonforandroid/build.py`) finds that wheel correctly during its
`pip --dry-run --platform=android_24_arm64_v8a` pass and pins the
exact URL into a generated `requirements.txt` — but the *next* stage
@ -206,11 +230,43 @@ android/
p4a bug, confirmed by reading `pythonforandroid/build.py` directly
(not guessed) — and `python_depends` isn't something `buildozer.spec`
can override, so `p4a-recipes/kivy/__init__.py` is a full copy of the
upstream recipe (patches included) with just `python_depends = []`.
`buildozer.spec` points at it via `p4a.local_recipes = p4a-recipes`.
If a future Kivy release actually needs one of those network extras,
re-diff against p4a's current `pythonforandroid/recipes/kivy/__init__.py`
before blindly re-adding them.
upstream recipe (patches included) keeping only `filetype` —
`kivy/core/image/__init__.py` imports it directly for image format
sniffing, confirmed by an on-device startup crash
(`ModuleNotFoundError: No module named 'filetype'`) the one time it
got dropped along with the rest. `filetype` is pure Python with zero
dependencies of its own, so it doesn't reintroduce the
requests/charset-normalizer chain. `buildozer.spec` points at the
override via `p4a.local_recipes = p4a-recipes`. If a future Kivy
release actually needs one of the other four back, re-diff against
p4a's current `pythonforandroid/recipes/kivy/__init__.py` before
blindly re-adding them.
- **Editing `p4a-recipes/kivy/__init__.py` alone is not enough — p4a
caches the built dist and may never re-read it.** `Distribution.get_
distribution()` (`pythonforandroid/distribution.py`) matches an
existing dist purely by name (`eventtracker`) and recipe-name list
(`python3`, `kivy`, etc. — from `buildozer.spec`'s `requirements =`
line). `python_depends` isn't a recipe name, so it's never consulted
during that match; if a dist with the same name+recipes already
exists, it's reused wholesale with `needs_build = False`, and
`build_dist_from_args()` — the only function that reads
`recipe.python_depends` and pip-installs pymodules
(`graph.py:334` → `run_pymodules_install`) — never runs at all. This
is exactly what happened restoring `filetype`: the recipe file was
correct on disk, but the next build reused the stale dist and the
APK still crashed identically. Confirmed two ways: unpacked the built
APK's `libpybundle.so` and found no `filetype` anywhere in it, then
traced the caching logic in `distribution.py`/`graph.py` line by
line. Fix: delete the stale dist directory so
`Distribution.get_distributions()`'s glob finds nothing and
`needs_build` falls back to `True` —
`rm -rf android/.buildozer/android/platform/build-<arch>/dists/<package.name>`
— then rebuild. This only redoes packaging (pymodule install +
Gradle assembly), not the expensive native recipe compilation, so
it's fast (single-digit seconds of Gradle work in practice). Anytime
a *recipe's* `python_depends` changes (not `buildozer.spec`'s
`requirements =` line, which p4a does check), delete the dist before
the next build or the change silently won't take effect.
- **Previously-tried and reverted**: `--system-site-packages` on the
build venv. It fixed the `--user`/pip-in-venv error above, but leaked
the real system Python's installed packages into what p4a decided the