Files
esp32-lua-api/tools/embed.py
T
evan 6a9b082f97 feat(runtime): embed platform modules as bytecode
ui.lua and hints.lua are compiled to LUA_32BITS bytecode (matching the
firmware's Lua build) and linked into the binary. A new package.searchers
entry checks them as the fallback after the SD card, so a local
/.lua/lib/ui.lua still shadows the packaged one for debugging.

Bytecode is ~40% smaller than source and loads without parsing. A fresh
SD card with no make sdcard now has the platform available.
2026-08-03 21:08:35 -04:00

68 lines
2.0 KiB
Python

#!/usr/bin/env python3
"""Compiles lua/lib/*.lua to LUA_32BITS bytecode and generates a C++ source file embedding
the arrays. The runtime's module searcher checks these as a fallback after the SD card, so
a local copy always shadows the packaged one.
Usage:
tools/embed.py <luac32> <lua_lib_dir> <output.cpp> [--check]
"""
import subprocess
import sys
import pathlib
HEADER = """\
// GENERATED by tools/embed.py — do not edit. Run `make embed` to regenerate.
#include <lua/embedded_modules.h>
namespace esp32lua {
"""
FOOTER = """
} // namespace esp32lua
"""
def main():
check = "--check" in sys.argv
args = [a for a in sys.argv[1:] if a != "--check"]
if len(args) != 3:
sys.exit("usage: embed.py <luac32> <lua_lib_dir> <output.cpp> [--check]")
luac32, lib_dir, out_path = args
lib_dir = pathlib.Path(lib_dir)
out_path = pathlib.Path(out_path)
sources = sorted(lib_dir.glob("*.lua"))
arrays = []
rows = []
for src in sources:
import tempfile
with tempfile.NamedTemporaryFile(suffix=".luac", delete=False) as tmp:
tmp_path = tmp.name
subprocess.run([luac32, str(src), tmp_path], check=True)
bytecode = pathlib.Path(tmp_path).read_bytes()
pathlib.Path(tmp_path).unlink()
name = src.stem
comma = ", ".join(f"0x{b:02x}" for b in bytecode)
arrays.append(f"static const unsigned char {name}[] = {{{comma}}};\n")
rows.append(f' {{"{name}", {name}, sizeof({name})}},')
footer = "const EmbeddedModule embedded_modules[] = {{\n{rows}\n}};\nconst size_t embedded_modules_count = {count};\n"
generated = HEADER + "".join(arrays) + footer.format(rows="\n".join(rows), count=len(sources)) + FOOTER
if check:
existing = out_path.read_text() if out_path.exists() else ""
if existing != generated:
sys.exit(f"{out_path} is stale; run `make embed`")
return
out_path.write_text(generated)
print(f"wrote {len(sources)} modules to {out_path}")
if __name__ == "__main__":
main()