Files
wadamesh/scripts/build/gen-lua-builtin.py
T
Kaj SchittecatandClaude Opus 4.8 de9d9f252d lua: ship the catalog's apps inside the image for boards without a dependable Store
Kaj's idea, and better than restoring the deleted native pages: the Lua host
already had the mechanism. luaAppLaunchFile() is file-first with an embedded
fallback — it just never had anything seeded into it.

gen-lua-builtin.py generates src/ui-touch/lua_builtin.h from out/firmware/apps/,
the same .lua the Store serves, as C++ raw strings (Snake, RF Monitor, Airtime;
10 KB). luaStoreScanInstalled() now wraps the filesystem scan and seeds any
built-in the scan did not find — after it, so a downloaded copy keeps its own
entry and version, and seeding still happens when there is no filesystem at all.
Tiles, the drawer and the Store's Installed list all work through the existing
paths; launching passes the embedded source, and a downloaded file still wins.

CAP_BUILTIN_LUA_APPS is ON for the V4 only. Flash: V4 85.5% -> 85.8%,
T-Deck 75.5% and Pager 78.1% unchanged. No RAM cost.

So the V4 now has the apps and all 13 languages out of the box; the only thing
it loses without a working Store is browsing for new ones.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-04 21:53:43 +02:00

70 lines
2.9 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Bake the catalog's Lua apps into the firmware as seeded built-ins.
The Lua host already supports this: luaAppLaunchFile(id, title, embedded, len)
tries <data>/apps/<id>.lua first and falls back to an embedded source when the
file is not there. That file-first order is what makes a seeded app updatable —
download a newer version from the Store and the file simply wins.
This generates src/ui-touch/lua_builtin.h from out/firmware/apps/, i.e. exactly
the sources the Store serves, so a board that cannot reach the Store still ships
with the apps rather than an empty drawer.
Run from the repo root: python3 scripts/build/gen-lua-builtin.py
"""
import json, os, re, sys
ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
APPS = os.path.join(ROOT, 'out/firmware/apps')
OUT = os.path.join(ROOT, 'src/ui-touch/lua_builtin.h')
DELIM = 'WADALUA' # raw-string delimiter: keeps Lua source verbatim
def main():
cat_path = os.path.join(APPS, 'apps.json')
if not os.path.exists(cat_path):
sys.exit('no catalog at ' + cat_path)
cat = json.load(open(cat_path, encoding='utf-8'))['apps']
entries = []
for app in cat:
aid, ver, name = app['id'], app['ver'], app['name']
src = os.path.join(APPS, aid, ver, aid + '.lua')
if not os.path.exists(src):
print(' skip %s: no %s' % (aid, os.path.relpath(src, ROOT)))
continue
code = open(src, encoding='utf-8').read()
if (')' + DELIM + '"') in code:
sys.exit('%s contains the raw-string delimiter' % aid)
entries.append((aid, name, ver, code))
if not entries:
sys.exit('no app sources found under ' + APPS)
w = ['// Generated by scripts/build/gen-lua-builtin.py — DO NOT EDIT.',
'// Source: out/firmware/apps/ (the same .lua the Lua Store serves).',
'// Seeded built-ins: a downloaded <data>/apps/<id>.lua always wins.',
'#pragma once', '',
'struct LuaBuiltinApp { const char* id; const char* name; const char* ver; const char* src; };', '']
for aid, name, ver, code in entries:
w.append('static const char kLuaSrc_%s[] = R"%s(%s)%s";' % (aid, DELIM, code, DELIM))
w.append('')
w.append('static const LuaBuiltinApp kLuaBuiltin[] = {')
for aid, name, ver, _ in entries:
w.append(' { "%s", "%s", "%s", kLuaSrc_%s },' % (aid, name, ver, aid))
w.append('};')
w.append('static const int kLuaBuiltinCount = (int)(sizeof(kLuaBuiltin)/sizeof(kLuaBuiltin[0]));')
w.append('')
open(OUT, 'w', encoding='utf-8').write('\n'.join(w))
total = sum(len(c) for _, _, _, c in entries)
print('%s: %d apps, %d bytes of Lua' %
(os.path.relpath(OUT, ROOT), len(entries), total))
for aid, _, ver, code in entries:
print(' %-10s v%-4s %6d B' % (aid, ver, len(code)))
if __name__ == '__main__':
main()