chore(gdb): refactor generators into shared enum_parser and unified generate_all entry

This commit is contained in:
Benign X
2026-03-06 17:51:48 +08:00
committed by VIFEX
parent 6d07a02966
commit a23e23099c
18 changed files with 339 additions and 464 deletions
+6
View File
@@ -0,0 +1,6 @@
*.egg-info/
dist/
build/
__pycache__/
.idea/
.DS_Store
@@ -1,8 +1,8 @@
"""
Auto-generated indev constants from LVGL headers.
Do not edit manually. Regenerate with:
python3 scripts/gen_indev_consts.py
Do not edit manually. Regenerate from the GDB script root with:
python3 scripts/generate_all.py
"""
INDEV_TYPE_NAMES = {
@@ -1,8 +1,8 @@
"""
Auto-generated observer constants from LVGL headers.
Do not edit manually. Regenerate with:
python3 scripts/gen_subject_consts.py
Do not edit manually. Regenerate from the GDB script root with:
python3 scripts/generate_all.py
"""
SUBJECT_TYPE_NAMES = {
@@ -1,8 +1,8 @@
"""
Auto-generated draw constants from LVGL headers.
Do not edit manually. Regenerate with:
python3 scripts/gen_draw_consts.py
Do not edit manually. Regenerate from the GDB script root with:
python3 scripts/generate_all.py
"""
DRAW_TASK_TYPE_NAMES = {
@@ -1,8 +1,8 @@
"""
Auto-generated event constants from LVGL headers.
Do not edit manually. Regenerate with:
python3 scripts/gen_event_consts.py
Do not edit manually. Regenerate from the GDB script root with:
python3 scripts/generate_all.py
"""
EVENT_CODE_NAMES = {
@@ -1,8 +1,8 @@
"""
Auto-generated style constants from LVGL headers.
Do not edit manually. Regenerate with:
python3 scripts/gen_style_consts.py
Do not edit manually. Regenerate from the GDB script root with:
python3 scripts/generate_all.py
"""
STYLE_PROP_NAMES = {
+105
View File
@@ -0,0 +1,105 @@
#!/usr/bin/env python3
"""
Shared utilities for LVGL constant generators.
Provides:
- parse_enum(): Parse a C typedef enum from a header file.
- generate_dict_module(): Generate a Python module with dict constants.
"""
import re
from pathlib import Path
def parse_enum(path: Path, enum_type: str, prefix: str,
skip: set[str] | None = None) -> dict[int, str]:
"""Parse a C typedef enum from a header file.
Args:
path: Path to the C header file.
enum_type: The typedef name (e.g. "lv_indev_type_t").
prefix: Enum member prefix to strip (e.g. "LV_INDEV_TYPE_").
skip: Optional set of full enum member names to skip.
Returns:
Dict mapping int value -> short name string.
"""
text = path.read_text()
skip = skip or set()
pattern = rf"\}}\s*{re.escape(enum_type)}\s*;"
m = re.search(rf"typedef\s+enum\s*\{{(.*?){pattern}", text, re.DOTALL)
if not m:
raise RuntimeError(f"Cannot find {enum_type} enum in {path}")
entries = {}
current_val = 0
for line in m.group(1).splitlines():
line = line.strip().rstrip(",")
if (
not line
or line.startswith("/*")
or line.startswith("//")
or line.startswith("*")
or line.startswith("#")
):
continue
match = re.match(
rf"({re.escape(prefix)}\w+)\s*=\s*(0x[\da-fA-F]+|\d+)", line
)
if match:
name = match.group(1)
current_val = int(match.group(2), 0)
else:
match = re.match(rf"({re.escape(prefix)}\w+)", line)
if not match:
continue
name = match.group(1)
if name in skip:
current_val += 1
continue
short = name.removeprefix(prefix)
entries[current_val] = short
current_val += 1
return entries
def generate_dict_module(
description: str,
dicts: dict[str, dict],
) -> str:
"""Generate a Python module containing one or more dict constants.
Args:
description: Short description for the module docstring.
dicts: Mapping of variable_name -> dict to emit.
Returns:
Python source code string.
"""
lines = [
'"""',
f"Auto-generated {description}.",
"",
"Do not edit manually. Regenerate from the GDB script root with:",
" python3 scripts/generate_all.py",
'"""',
"",
]
for var_name, data in dicts.items():
lines.append(f"{var_name} = {{")
for k in sorted(data):
v = data[k]
if isinstance(k, int):
lines.append(f' {k}: "{v}",')
else:
lines.append(f' "{k}": "{v}",')
lines.append("}")
lines.append("")
return "\n".join(lines)
-141
View File
@@ -1,141 +0,0 @@
#!/usr/bin/env python3
"""
Generate draw constant tables from LVGL header and source files.
Parses lv_draw.h for task type/state enums, and scans draw unit source
files for name-to-struct-type mappings.
Usage:
python3 scripts/gen_draw_consts.py
"""
import re
import sys
from pathlib import Path
SCRIPT_DIR = Path(__file__).parent
GDB_ROOT = SCRIPT_DIR.parent
LVGL_SRC = GDB_ROOT.parent.parent / "src"
OUTPUT = GDB_ROOT / "lvglgdb" / "lvgl" / "draw" / "lv_draw_consts.py"
DRAW_H = LVGL_SRC / "draw" / "lv_draw.h"
DRAW_DIR = LVGL_SRC / "draw"
sys.path.insert(0, str(SCRIPT_DIR))
def parse_enum(path: Path, enum_type: str, prefix: str) -> dict[int, str]:
"""Parse a C enum from a header file."""
text = path.read_text()
pattern = rf"\}}\s*{re.escape(enum_type)}\s*;"
m = re.search(rf"typedef\s+enum\s*\{{(.*?){pattern}", text, re.DOTALL)
if not m:
raise RuntimeError(f"Cannot find {enum_type} enum in {path}")
entries = {}
current_val = 0
for line in m.group(1).splitlines():
line = line.strip().rstrip(",")
if (
not line
or line.startswith("/*")
or line.startswith("//")
or line.startswith("*")
or line.startswith("#")
):
continue
match = re.match(rf"({re.escape(prefix)}\w+)\s*=\s*(0x[\da-fA-F]+|\d+)", line)
if match:
name = match.group(1)
current_val = int(match.group(2), 0)
else:
match = re.match(rf"({re.escape(prefix)}\w+)", line)
if not match:
continue
name = match.group(1)
short = name.removeprefix(prefix)
entries[current_val] = short
current_val += 1
return entries
def parse_draw_unit_types(draw_dir: Path) -> dict[str, str]:
"""Scan draw unit .c files for name-to-struct-type mappings.
Looks for patterns like: unit->base_unit.name = "SW";
Then finds the corresponding struct type from the variable declaration.
"""
mappings = {}
for c_file in draw_dir.rglob("*.c"):
text = c_file.read_text()
for m in re.finditer(r'(\w+)->base_unit\.name\s*=\s*"(\w+)"', text):
var_name = m.group(1)
unit_name = m.group(2)
decl = re.search(
rf"(lv_draw_\w+_unit_t)\s*\*\s*{re.escape(var_name)}\b", text
)
if decl:
mappings[unit_name] = decl.group(1)
return mappings
def generate(
task_types: dict[int, str],
task_states: dict[int, str],
unit_types: dict[str, str],
) -> str:
"""Generate Python source for the draw constants module."""
lines = [
'"""',
"Auto-generated draw constants from LVGL headers.",
"",
"Do not edit manually. Regenerate with:",
" python3 scripts/gen_draw_consts.py",
'"""',
"",
]
lines.append("DRAW_TASK_TYPE_NAMES = {")
for k in sorted(task_types):
lines.append(f' {k}: "{task_types[k]}",')
lines.append("}")
lines.append("")
lines.append("DRAW_TASK_STATE_NAMES = {")
for k in sorted(task_states):
lines.append(f' {k}: "{task_states[k]}",')
lines.append("}")
lines.append("")
lines.append("DRAW_UNIT_TYPE_NAMES = {")
for name in sorted(unit_types):
lines.append(f' "{name}": "{unit_types[name]}",')
lines.append("}")
lines.append("")
return "\n".join(lines)
def main():
task_types = parse_enum(DRAW_H, "lv_draw_task_type_t", "LV_DRAW_TASK_TYPE_")
task_states = parse_enum(DRAW_H, "lv_draw_task_state_t", "LV_DRAW_TASK_STATE_")
unit_types = parse_draw_unit_types(DRAW_DIR)
src = generate(task_types, task_states, unit_types)
OUTPUT.write_text(src)
print(
f"Generated {OUTPUT} ({len(task_types)} task types, "
f"{len(task_states)} task states, {len(unit_types)} unit types)"
)
if __name__ == "__main__":
main()
-95
View File
@@ -1,95 +0,0 @@
#!/usr/bin/env python3
"""
Generate event constant tables from LVGL header files.
Parses lv_event.h for the lv_event_code_t enum.
Usage:
python3 scripts/gen_event_consts.py
"""
import re
from pathlib import Path
SCRIPT_DIR = Path(__file__).parent
GDB_ROOT = SCRIPT_DIR.parent
LVGL_SRC = GDB_ROOT.parent.parent / "src"
OUTPUT = GDB_ROOT / "lvglgdb" / "lvgl" / "misc" / "lv_event_consts.py"
EVENT_H = LVGL_SRC / "misc" / "lv_event.h"
def parse_event_codes(path: Path) -> dict[int, str]:
"""Parse lv_event_code_t enum from lv_event.h."""
text = path.read_text()
m = re.search(r"typedef\s+enum\s*\{(.*?)\}\s*lv_event_code_t", text, re.DOTALL)
if not m:
raise RuntimeError("Cannot find lv_event_code_t enum")
codes = {}
current_val = 0
for line in m.group(1).splitlines():
line = line.strip().rstrip(",")
if (
not line
or line.startswith("/*")
or line.startswith("//")
or line.startswith("*")
or line.startswith("#")
):
continue
# Match: LV_EVENT_XXX = value
match = re.match(r"(LV_EVENT_\w+)\s*=\s*(0x[\da-fA-F]+|\d+)", line)
if match:
name = match.group(1)
current_val = int(match.group(2), 0)
else:
match = re.match(r"(LV_EVENT_\w+)", line)
if not match:
continue
name = match.group(1)
# Skip meta entries
if name in ("LV_EVENT_LAST", "LV_EVENT_PREPROCESS", "LV_EVENT_MARKED_DELETING"):
current_val += 1
continue
short = name.removeprefix("LV_EVENT_")
codes[current_val] = short
current_val += 1
return codes
def generate(codes: dict[int, str]) -> str:
"""Generate Python source for the event constants module."""
lines = [
'"""',
"Auto-generated event constants from LVGL headers.",
"",
"Do not edit manually. Regenerate with:",
" python3 scripts/gen_event_consts.py",
'"""',
"",
]
lines.append("EVENT_CODE_NAMES = {")
for k in sorted(codes):
lines.append(f' {k}: "{codes[k]}",')
lines.append("}")
lines.append("")
return "\n".join(lines)
def main():
codes = parse_event_codes(EVENT_H)
src = generate(codes)
OUTPUT.write_text(src)
print(f"Generated {OUTPUT} ({len(codes)} event codes)")
if __name__ == "__main__":
main()
-89
View File
@@ -1,89 +0,0 @@
#!/usr/bin/env python3
"""
Generate indev constant tables from LVGL header files.
Parses lv_indev.h for indev type enum.
Usage:
python3 scripts/gen_indev_consts.py
"""
import re
from pathlib import Path
SCRIPT_DIR = Path(__file__).parent
GDB_ROOT = SCRIPT_DIR.parent
LVGL_SRC = GDB_ROOT.parent.parent / "src"
OUTPUT = GDB_ROOT / "lvglgdb" / "lvgl" / "core" / "lv_indev_consts.py"
INDEV_H = LVGL_SRC / "indev" / "lv_indev.h"
def parse_enum(path: Path, enum_type: str, prefix: str) -> dict[int, str]:
"""Parse a C enum from a header file."""
text = path.read_text()
pattern = rf"\}}\s*{re.escape(enum_type)}\s*;"
m = re.search(rf"typedef\s+enum\s*\{{(.*?){pattern}", text, re.DOTALL)
if not m:
raise RuntimeError(f"Cannot find {enum_type} enum in {path}")
entries = {}
current_val = 0
for line in m.group(1).splitlines():
line = line.strip().rstrip(",")
if (
not line
or line.startswith("/*")
or line.startswith("//")
or line.startswith("*")
or line.startswith("#")
):
continue
match = re.match(rf"({re.escape(prefix)}\w+)\s*=\s*(0x[\da-fA-F]+|\d+)", line)
if match:
name = match.group(1)
current_val = int(match.group(2), 0)
else:
match = re.match(rf"({re.escape(prefix)}\w+)", line)
if not match:
continue
name = match.group(1)
short = name.removeprefix(prefix)
entries[current_val] = short
current_val += 1
return entries
def generate(indev_types: dict[int, str]) -> str:
"""Generate Python source for the indev constants module."""
lines = [
'"""',
"Auto-generated indev constants from LVGL headers.",
"",
"Do not edit manually. Regenerate with:",
" python3 scripts/gen_indev_consts.py",
'"""',
"",
"INDEV_TYPE_NAMES = {",
]
for k in sorted(indev_types):
lines.append(f' {k}: "{indev_types[k]}",')
lines.append("}")
lines.append("")
return "\n".join(lines)
def main():
indev_types = parse_enum(INDEV_H, "lv_indev_type_t", "LV_INDEV_TYPE_")
src = generate(indev_types)
OUTPUT.write_text(src)
print(f"Generated {OUTPUT} ({len(indev_types)} indev types)")
if __name__ == "__main__":
main()
-53
View File
@@ -1,53 +0,0 @@
#!/usr/bin/env python3
"""
Generate subject type constant table from LVGL header files.
Parses lv_observer.h for subject type enum.
Usage:
python3 scripts/gen_subject_consts.py
"""
import sys
from pathlib import Path
SCRIPT_DIR = Path(__file__).parent
GDB_ROOT = SCRIPT_DIR.parent
LVGL_SRC = GDB_ROOT.parent.parent / "src"
OUTPUT = GDB_ROOT / "lvglgdb" / "lvgl" / "core" / "lv_observer_consts.py"
OBSERVER_H = LVGL_SRC / "core" / "lv_observer.h"
sys.path.insert(0, str(SCRIPT_DIR))
from gen_indev_consts import parse_enum
def generate(subject_types: dict[int, str]) -> str:
"""Generate Python source for the observer constants module."""
lines = [
'"""',
"Auto-generated observer constants from LVGL headers.",
"",
"Do not edit manually. Regenerate with:",
" python3 scripts/gen_subject_consts.py",
'"""',
"",
"SUBJECT_TYPE_NAMES = {",
]
for k in sorted(subject_types):
lines.append(f' {k}: "{subject_types[k]}",')
lines.append("}")
lines.append("")
return "\n".join(lines)
def main():
subject_types = parse_enum(OBSERVER_H, "lv_subject_type_t", "LV_SUBJECT_TYPE_")
src = generate(subject_types)
OUTPUT.write_text(src)
print(f"Generated {OUTPUT} ({len(subject_types)} subject types)")
if __name__ == "__main__":
main()
+44
View File
@@ -0,0 +1,44 @@
#!/usr/bin/env python3
"""
Unified entry point for all LVGL GDB constant generators.
Scans the generators/ subdirectory for gen_*.py modules and calls
each module's main() function.
Usage (from the GDB script root):
python3 scripts/generate_all.py
"""
import importlib
import sys
from pathlib import Path
SCRIPT_DIR = Path(__file__).parent
GENERATORS_DIR = SCRIPT_DIR / "generators"
# Make enum_parser importable from generators
sys.path.insert(0, str(SCRIPT_DIR))
def main():
gen_files = sorted(GENERATORS_DIR.glob("gen_*.py"))
if not gen_files:
print("No generators found.")
return
print(f"Running {len(gen_files)} generator(s)...\n")
for gen_file in gen_files:
module_name = gen_file.stem
print(f" [{module_name}]")
spec = importlib.util.spec_from_file_location(module_name, gen_file)
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
mod.main()
print()
print("All generators completed.")
if __name__ == "__main__":
main()
@@ -0,0 +1 @@
# generators package
@@ -0,0 +1,59 @@
#!/usr/bin/env python3
"""Generate draw constant tables from LVGL headers and source files."""
import re
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
from enum_parser import parse_enum, generate_dict_module
LVGL_SRC = Path(__file__).parent.parent.parent.parent.parent / "src"
OUTPUT = Path(__file__).parent.parent.parent / "lvglgdb" / "lvgl" / "draw" / "lv_draw_consts.py"
DRAW_H = LVGL_SRC / "draw" / "lv_draw.h"
DRAW_DIR = LVGL_SRC / "draw"
def parse_draw_unit_types(draw_dir: Path) -> dict[str, str]:
"""Scan draw unit .c files for name-to-struct-type mappings.
Looks for patterns like: unit->base_unit.name = "SW";
Then finds the corresponding struct type from the variable declaration.
"""
mappings = {}
for c_file in draw_dir.rglob("*.c"):
text = c_file.read_text()
for m in re.finditer(r'(\w+)->base_unit\.name\s*=\s*"(\w+)"', text):
var_name = m.group(1)
unit_name = m.group(2)
decl = re.search(
rf"(lv_draw_\w+_unit_t)\s*\*\s*{re.escape(var_name)}\b", text
)
if decl:
mappings[unit_name] = decl.group(1)
return mappings
def main():
task_types = parse_enum(DRAW_H, "lv_draw_task_type_t", "LV_DRAW_TASK_TYPE_")
task_states = parse_enum(DRAW_H, "lv_draw_task_state_t", "LV_DRAW_TASK_STATE_")
unit_types = parse_draw_unit_types(DRAW_DIR)
src = generate_dict_module(
"draw constants from LVGL headers",
{
"DRAW_TASK_TYPE_NAMES": task_types,
"DRAW_TASK_STATE_NAMES": task_states,
"DRAW_UNIT_TYPE_NAMES": unit_types,
},
)
OUTPUT.write_text(src)
print(
f"Generated {OUTPUT.name} ({len(task_types)} task types, "
f"{len(task_states)} task states, {len(unit_types)} unit types)"
)
if __name__ == "__main__":
main()
@@ -0,0 +1,32 @@
#!/usr/bin/env python3
"""Generate event constant tables from LVGL headers."""
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
from enum_parser import parse_enum, generate_dict_module
LVGL_SRC = Path(__file__).parent.parent.parent.parent.parent / "src"
OUTPUT = Path(__file__).parent.parent.parent / "lvglgdb" / "lvgl" / "misc" / "lv_event_consts.py"
SKIP_EVENTS = {"LV_EVENT_LAST", "LV_EVENT_PREPROCESS", "LV_EVENT_MARKED_DELETING"}
def main():
event_codes = parse_enum(
LVGL_SRC / "misc" / "lv_event.h",
"lv_event_code_t",
"LV_EVENT_",
skip=SKIP_EVENTS,
)
src = generate_dict_module(
"event constants from LVGL headers",
{"EVENT_CODE_NAMES": event_codes},
)
OUTPUT.write_text(src)
print(f"Generated {OUTPUT.name} ({len(event_codes)} event codes)")
if __name__ == "__main__":
main()
@@ -0,0 +1,29 @@
#!/usr/bin/env python3
"""Generate indev constant tables from LVGL headers."""
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
from enum_parser import parse_enum, generate_dict_module
LVGL_SRC = Path(__file__).parent.parent.parent.parent.parent / "src"
OUTPUT = Path(__file__).parent.parent.parent / "lvglgdb" / "lvgl" / "core" / "lv_indev_consts.py"
def main():
indev_types = parse_enum(
LVGL_SRC / "indev" / "lv_indev.h",
"lv_indev_type_t",
"LV_INDEV_TYPE_",
)
src = generate_dict_module(
"indev constants from LVGL headers",
{"INDEV_TYPE_NAMES": indev_types},
)
OUTPUT.write_text(src)
print(f"Generated {OUTPUT.name} ({len(indev_types)} indev types)")
if __name__ == "__main__":
main()
@@ -1,32 +1,29 @@
#!/usr/bin/env python3
"""
Generate style constant tables from LVGL header files.
Parses lv_style.h, lv_obj_style.h, and lv_style_gen.h to produce
lv_style_consts.py used by the lvglgdb GDB plugin.
Usage:
python3 scripts/gen_style_consts.py
"""
"""Generate style constant tables from LVGL headers."""
import re
from pathlib import Path
SCRIPT_DIR = Path(__file__).parent
GDB_ROOT = SCRIPT_DIR.parent
LVGL_SRC = GDB_ROOT.parent.parent / "src"
LVGL_SRC = Path(__file__).parent.parent.parent.parent.parent / "src"
GDB_ROOT = Path(__file__).parent.parent.parent
OUTPUT = GDB_ROOT / "lvglgdb" / "lvgl" / "misc" / "lv_style_consts.py"
STYLE_H = LVGL_SRC / "misc" / "lv_style.h"
OBJ_STYLE_H = LVGL_SRC / "core" / "lv_obj_style.h"
STYLE_GEN_H = LVGL_SRC / "misc" / "lv_style_gen.h"
SKIP_PROPS = {
"LV_STYLE_PROP_INV",
"LV_STYLE_PROP_ANY",
"LV_STYLE_PROP_CONST",
"LV_STYLE_LAST_BUILT_IN_PROP",
"LV_STYLE_NUM_BUILT_IN_PROPS",
}
def parse_style_props(path: Path) -> dict[int, str]:
"""Parse _lv_style_id_t enum from lv_style.h."""
text = path.read_text()
# Extract enum block
m = re.search(r"enum\s+_lv_style_id_t\s*\{(.*?)\}", text, re.DOTALL)
if not m:
raise RuntimeError("Cannot find _lv_style_id_t enum")
@@ -35,41 +32,21 @@ def parse_style_props(path: Path) -> dict[int, str]:
current_val = 0
for line in m.group(1).splitlines():
line = line.strip().rstrip(",")
if (
not line
or line.startswith("/*")
or line.startswith("//")
or line.startswith("*")
):
if not line or line.startswith(("/*", "//", "*")):
continue
# Match: LV_STYLE_XXX = value or LV_STYLE_XXX (auto-increment)
match = re.match(r"(LV_STYLE_\w+)\s*=\s*(0x[\da-fA-F]+|\d+)", line)
if match:
name = match.group(1)
val_str = match.group(2)
current_val = int(val_str, 0)
name, current_val = match.group(1), int(match.group(2), 0)
else:
match = re.match(r"(LV_STYLE_\w+)", line)
if not match:
continue
name = match.group(1)
# Skip meta entries
if name in (
"LV_STYLE_PROP_INV",
"LV_STYLE_PROP_ANY",
"LV_STYLE_PROP_CONST",
"LV_STYLE_LAST_BUILT_IN_PROP",
"LV_STYLE_NUM_BUILT_IN_PROPS",
):
if name in SKIP_PROPS:
current_val += 1
continue
short = name.removeprefix("LV_STYLE_")
props[current_val] = short
props[current_val] = name.removeprefix("LV_STYLE_")
current_val += 1
return props
@@ -79,18 +56,13 @@ def parse_parts(path: Path) -> dict[int, str]:
m = re.search(r"typedef\s+enum\s*\{(.*?)\}\s*lv_part_t", text, re.DOTALL)
if not m:
raise RuntimeError("Cannot find lv_part_t enum")
parts = {}
for line in m.group(1).splitlines():
match = re.match(r"\s*(LV_PART_\w+)\s*=\s*(0x[\da-fA-F]+|\d+)", line)
if not match:
continue
name = match.group(1)
val = int(match.group(2), 0)
short = name.removeprefix("LV_PART_")
# Selector uses bits [23:16], shift down to get the key
parts[val >> 16] = short
parts[val >> 16] = match.group(1).removeprefix("LV_PART_")
return parts
@@ -112,25 +84,17 @@ def parse_states(path: Path) -> dict[int, str]:
m = re.search(r"typedef\s+enum\s*\{(.*?)\}\s*lv_state_t", text, re.DOTALL)
if not m:
raise RuntimeError("Cannot find lv_state_t enum")
states = {}
for line in m.group(1).splitlines():
match = re.match(r"\s*(LV_STATE_\w+)\s*=\s*(.+?)(?:,|/)", line)
if not match:
continue
name = match.group(1)
expr = match.group(2).strip()
# Skip DEFAULT (0) and ANY (0xFFFF)
if name in ("LV_STATE_DEFAULT", "LV_STATE_ANY"):
continue
val = _parse_int_expr(expr)
if val is None:
continue
short = name.removeprefix("LV_STATE_")
states[val] = short
val = _parse_int_expr(match.group(2))
if val is not None:
states[val] = name.removeprefix("LV_STATE_")
return states
@@ -138,7 +102,6 @@ def parse_color_props(style_gen_h: Path, prop_map: dict[int, str]) -> set[int]:
"""Identify color properties from lv_style_gen.h setter signatures."""
text = style_gen_h.read_text()
name_to_id = {v: k for k, v in prop_map.items()}
color_ids = set()
for match in re.finditer(
r"void\s+lv_style_set_(\w+)\s*\([^,]+,\s*lv_color_t", text
@@ -146,7 +109,6 @@ def parse_color_props(style_gen_h: Path, prop_map: dict[int, str]) -> set[int]:
prop_name = match.group(1).upper()
if prop_name in name_to_id:
color_ids.add(name_to_id[prop_name])
return color_ids
@@ -154,7 +116,6 @@ def parse_pointer_props(style_gen_h: Path, prop_map: dict[int, str]) -> set[int]
"""Identify pointer properties from lv_style_gen.h setter signatures."""
text = style_gen_h.read_text()
name_to_id = {v: k for k, v in prop_map.items()}
ptr_ids = set()
for match in re.finditer(
r"void\s+lv_style_set_(\w+)\s*\([^,]+,\s*(?:const\s+)?(?:void|lv_\w+)\s*\*",
@@ -163,50 +124,38 @@ def parse_pointer_props(style_gen_h: Path, prop_map: dict[int, str]) -> set[int]
prop_name = match.group(1).upper()
if prop_name in name_to_id:
ptr_ids.add(name_to_id[prop_name])
return ptr_ids
def generate(
props: dict[int, str],
parts: dict[int, str],
states: dict[int, str],
color_ids: set[int],
pointer_ids: set[int],
) -> str:
"""Generate Python source for the constants module."""
def generate(props, parts, states, color_ids, pointer_ids) -> str:
"""Generate Python source for the style constants module."""
lines = [
'"""',
"Auto-generated style constants from LVGL headers.",
"",
"Do not edit manually. Regenerate with:",
" python3 scripts/gen_style_consts.py",
"Do not edit manually. Regenerate from the GDB script root with:",
" python3 scripts/generate_all.py",
'"""',
"",
]
# STYLE_PROP_NAMES
lines.append("STYLE_PROP_NAMES = {")
for k in sorted(props):
lines.append(f' {k}: "{props[k]}",')
lines.append("}")
lines.append("")
# PART_NAMES
lines.append("PART_NAMES = {")
for k in sorted(parts):
lines.append(f' 0x{k:02X}: "{parts[k]}",')
lines.append("}")
lines.append("")
# STATE_FLAGS
lines.append("STATE_FLAGS = {")
for k in sorted(states):
lines.append(f' 0x{k:04X}: "{states[k]}",')
lines.append("}")
lines.append("")
# COLOR_PROPS
if color_ids:
lines.append("COLOR_PROPS = {")
for v in sorted(color_ids):
@@ -216,7 +165,6 @@ def generate(
lines.append("COLOR_PROPS = set()")
lines.append("")
# POINTER_PROPS
if pointer_ids:
lines.append("POINTER_PROPS = {")
for v in sorted(pointer_ids):
@@ -239,7 +187,7 @@ def main():
src = generate(props, parts, states, color_ids, pointer_ids)
OUTPUT.write_text(src)
print(
f"Generated {OUTPUT} ({len(props)} props, {len(parts)} parts, "
f"Generated {OUTPUT.name} ({len(props)} props, {len(parts)} parts, "
f"{len(states)} states, {len(color_ids)} color, {len(pointer_ids)} pointer)"
)
@@ -0,0 +1,29 @@
#!/usr/bin/env python3
"""Generate subject type constant table from LVGL headers."""
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
from enum_parser import parse_enum, generate_dict_module
LVGL_SRC = Path(__file__).parent.parent.parent.parent.parent / "src"
OUTPUT = Path(__file__).parent.parent.parent / "lvglgdb" / "lvgl" / "core" / "lv_observer_consts.py"
def main():
subject_types = parse_enum(
LVGL_SRC / "core" / "lv_observer.h",
"lv_subject_type_t",
"LV_SUBJECT_TYPE_",
)
src = generate_dict_module(
"observer constants from LVGL headers",
{"SUBJECT_TYPE_NAMES": subject_types},
)
OUTPUT.write_text(src)
print(f"Generated {OUTPUT.name} ({len(subject_types)} subject types)")
if __name__ == "__main__":
main()