From 39dfc672a23f99a9ae798c76600771b96c79191a Mon Sep 17 00:00:00 2001 From: Brad Barnett <127794626+bdbarnett@users.noreply.github.com> Date: Fri, 6 Nov 2026 03:06:28 -0600 Subject: [PATCH] lv_conf.h: a build can leave out a built-in Montserrat size Every LV_FONT_MONTSERRAT_ in lv_conf.h is now an #ifndef default, and the generator wraps each Montserrat font's wrapper and module entry in #if on that switch, in the MicroPython, CircuitPython and CPython bindings. A firmware short of flash passes -DLV_FONT_MONTSERRAT_40=0 and the 40 px font drops out without lv_conf.h being edited or the binding regenerated. Ran ./regenerate_all.sh, then scripts/verify_bindings.sh (passes) and the generator, MicroPython-port and CircuitPython-port tests (128 passed). --- binding/emit_c_micropython_style.py | 31 ++++++++++++------- binding/emit_circuitpython_glue.py | 23 ++++++++++++--- binding/emit_cpython_glue.py | 16 ++++++---- binding/util.py | 29 ++++++++++++++++++ docs/reference/fonts.md | 14 +++++++++ generated/lvgl_circuitpython.c | 34 +++++++++++++++++++-- generated/lvgl_micropython.c | 20 +++++++++++++ generated/lvgl_python.c | 10 +++++++ lv_conf.h | 46 ++++++++++++++++++++++++++++- tests/test_font_guards.py | 37 +++++++++++++++++++++++ 10 files changed, 237 insertions(+), 23 deletions(-) create mode 100644 tests/test_font_guards.py diff --git a/binding/emit_c_micropython_style.py b/binding/emit_c_micropython_style.py index 082aec1..a42974b 100644 --- a/binding/emit_c_micropython_style.py +++ b/binding/emit_c_micropython_style.py @@ -73,7 +73,7 @@ target_banner, ) from .runtime_exports import filter_module_funcs_for_target, filter_registration_module_funcs -from .util import eprint, memoize +from .util import eprint, guarded, memoize from .context import EmitterResult @@ -2929,15 +2929,21 @@ def gen_global(global_name, global_type_ast): * {module_name} {global_name} global definitions */ -static const mp_lv_struct_t mp_{global_name} = {{ - {{ &mp_{struct_name}_type }}, - ({cast}*)&{global_name} -}}; +{definition} """.format( module_name=module_name, global_name=global_name, - struct_name=global_type, - cast=gen.visit(global_type_ast), + definition=guarded( + """static const mp_lv_struct_t mp_{global_name} = {{ + {{ &mp_{struct_name}_type }}, + ({cast}*)&{global_name} +}};""".format( + global_name=global_name, + struct_name=global_type, + cast=gen.visit(global_type_ast), + ), + global_name, + ), ) ) @@ -3157,10 +3163,15 @@ def gen_global(global_name, global_type_ast): ), blobs="".join( [ - "{{ MP_ROM_QSTR(MP_QSTR_{name}), MP_ROM_PTR(&mp_{global_name}) }},\n ".format( - name=export_name(global_name, "blob"), - global_name=global_name, + guarded( + "{{ MP_ROM_QSTR(MP_QSTR_{name}), MP_ROM_PTR(&mp_{global_name}) }},".format( + name=export_name(global_name, "blob"), + global_name=global_name, + ), + global_name, + indent=" ", ) + + "\n " # _nesting is deliberately kept here even though the # canonical API model marks it private: see the # comment in emit_backend.module_registration_plan and diff --git a/binding/emit_circuitpython_glue.py b/binding/emit_circuitpython_glue.py index 5375760..98b31f3 100644 --- a/binding/emit_circuitpython_glue.py +++ b/binding/emit_circuitpython_glue.py @@ -8,6 +8,7 @@ import collections from . import runtime +from .util import guarded from .emit_backend import enum_namespace_plan, module_registration_plan, public_enum_module_names, public_struct_c_names @@ -74,9 +75,12 @@ def _module_global_entries( ) for global_name in plan.generated_globals: entries.append( - " {{ MP_ROM_QSTR(MP_QSTR_{name}), MP_ROM_PTR(&mp_{global_name}) }}".format( - name=export_name(global_name, "blob"), - global_name=global_name, + guarded( + " {{ MP_ROM_QSTR(MP_QSTR_{name}), MP_ROM_PTR(&mp_{global_name}) }},".format( + name=export_name(global_name, "blob"), + global_name=global_name, + ), + global_name, ) ) if plan.enum_names: @@ -125,6 +129,17 @@ def _module_global_entries( return entries +def _table(entries): + """Join table entries; an entry that already ends in its comma keeps it. + + Entries wrapped in ``#if``/``#endif`` carry their comma inside the guard. + """ + return "\n".join( + entry if entry.endswith(",") or entry.endswith("#endif") else entry + "," + for entry in entries + ) + + def finish_cp_fragment(max_phase): """Emit mergeable module entry table for shared-bindings/lvgl/__init__.c.""" int_constants = runtime.get("int_constants", []) @@ -192,7 +207,7 @@ def finish_cp_fragment(max_phase): const size_t lvgl_module_entry_count = sizeof(lvgl_module_entries) / sizeof(lvgl_module_entries[0]); """.format( - table=",\n".join(all_entries) if all_entries else " /* no module entries */" + table=_table(all_entries) if all_entries else " /* no module entries */" ) ) diff --git a/binding/emit_cpython_glue.py b/binding/emit_cpython_glue.py index b3baeb7..2e81b24 100644 --- a/binding/emit_cpython_glue.py +++ b/binding/emit_cpython_glue.py @@ -8,6 +8,7 @@ import collections from . import runtime +from .util import guarded from .emit_backend import enum_namespace_plan, module_registration_plan, public_enum_module_names, public_struct_c_names @@ -424,12 +425,15 @@ def finish_py_module(max_phase): ) py_name = simplify_identifier(global_name) print( - ' {{ PyObject *obj = lv_to_mp_struct({type_object}, (void *)&{global_name});' - ' if (obj == NULL) return NULL;' - ' if (PyModule_AddObject(m, "{py_name}", obj) < 0) {{ Py_DECREF(obj); return NULL; }} }}'.format( - type_object=type_object, - global_name=global_name, - py_name=py_name, + guarded( + ' {{ PyObject *obj = lv_to_mp_struct({type_object}, (void *)&{global_name});' + ' if (obj == NULL) return NULL;' + ' if (PyModule_AddObject(m, "{py_name}", obj) < 0) {{ Py_DECREF(obj); return NULL; }} }}'.format( + type_object=type_object, + global_name=global_name, + py_name=py_name, + ), + global_name, ) ) diff --git a/binding/util.py b/binding/util.py index 1d9a1c5..bdd4929 100644 --- a/binding/util.py +++ b/binding/util.py @@ -5,6 +5,7 @@ """Small utilities: memoize and stderr logging for analyze/emit.""" from __future__ import print_function +import re import sys from functools import lru_cache @@ -29,3 +30,31 @@ def clear_memoized(): def eprint(*args, **kwargs): print(*args, file=sys.stderr, **kwargs) + + +_MONTSERRAT_FONT = re.compile(r"lv_font_montserrat_(\d+)$") + + +def config_guard(global_name): + """Return the ``lv_conf.h`` switch a generated global depends on, or None. + + A build can turn a built-in Montserrat size off (``-DLV_FONT_MONTSERRAT_40=0``) + without regenerating the binding, so the font's wrapper and its module entry + are emitted inside ``#if`` on that switch. + """ + match = _MONTSERRAT_FONT.match(global_name) + if match is None: + return None + return "LV_FONT_MONTSERRAT_%s" % match.group(1) + + +def guarded(text, global_name, indent=""): + """Wrap ``text`` in ``#if``/``#endif`` on `config_guard` when it has one. + + ``indent`` goes before ``text`` and ``#endif``, for a line that the caller's + template has already indented. + """ + guard = config_guard(global_name) + if guard is None: + return text + return "#if %s\n%s%s\n%s#endif" % (guard, indent, text, indent) diff --git a/docs/reference/fonts.md b/docs/reference/fonts.md index 0559609..3a46433 100644 --- a/docs/reference/fonts.md +++ b/docs/reference/fonts.md @@ -19,6 +19,20 @@ loaded font is parsed into the heap (roughly its file size: see the sizes in your everyday fonts compiled in; load the occasional large size, or a script you only sometimes need, at runtime. +## Leaving a built-in font out + +Each `LV_FONT_MONTSERRAT_` in `lv_conf.h` can be set from the build, so a +firmware short of flash can drop a compiled-in size without editing +`lv_conf.h` or regenerating the binding: + +```bash +make BOARD=... CFLAGS_EXTRA=-DLV_FONT_MONTSERRAT_40=0 +``` + +`lv.font_montserrat_40` is then missing from the module, and you can load +`montserrat_40.bin` at runtime instead. This only turns sizes off: a size that +`lv_conf.h` leaves out has no wrapper in the generated binding. + ## Getting a font onto the board ```bash diff --git a/generated/lvgl_circuitpython.c b/generated/lvgl_circuitpython.c index 66b4155..77d511c 100644 --- a/generated/lvgl_circuitpython.c +++ b/generated/lvgl_circuitpython.c @@ -27522,50 +27522,60 @@ static const mp_lv_struct_t mp_lv_tree_node_class = { * lvgl lv_font_montserrat_14 global definitions */ +#if LV_FONT_MONTSERRAT_14 static const mp_lv_struct_t mp_lv_font_montserrat_14 = { { &mp_lv_font_t_type }, (lv_font_t*)&lv_font_montserrat_14 }; +#endif /* * lvgl lv_font_montserrat_16 global definitions */ +#if LV_FONT_MONTSERRAT_16 static const mp_lv_struct_t mp_lv_font_montserrat_16 = { { &mp_lv_font_t_type }, (lv_font_t*)&lv_font_montserrat_16 }; +#endif /* * lvgl lv_font_montserrat_24 global definitions */ +#if LV_FONT_MONTSERRAT_24 static const mp_lv_struct_t mp_lv_font_montserrat_24 = { { &mp_lv_font_t_type }, (lv_font_t*)&lv_font_montserrat_24 }; +#endif /* * lvgl lv_font_montserrat_32 global definitions */ +#if LV_FONT_MONTSERRAT_32 static const mp_lv_struct_t mp_lv_font_montserrat_32 = { { &mp_lv_font_t_type }, (lv_font_t*)&lv_font_montserrat_32 }; +#endif /* * lvgl lv_font_montserrat_40 global definitions */ +#if LV_FONT_MONTSERRAT_40 static const mp_lv_struct_t mp_lv_font_montserrat_40 = { { &mp_lv_font_t_type }, (lv_font_t*)&lv_font_montserrat_40 }; +#endif /* Global struct wrapper for int */ @@ -48882,11 +48892,21 @@ static const mp_rom_map_elem_t lvgl_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_TABLE_CELL_NONE), MP_ROM_PTR(MP_ROM_INT(ENUM_LV_TABLE_CELL_NONE)) }, { MP_ROM_QSTR(MP_QSTR_color_filter_shade), MP_ROM_PTR(&mp_lv_color_filter_shade) }, { MP_ROM_QSTR(MP_QSTR_tree_node_class), MP_ROM_PTR(&mp_lv_tree_node_class) }, +#if LV_FONT_MONTSERRAT_14 { MP_ROM_QSTR(MP_QSTR_font_montserrat_14), MP_ROM_PTR(&mp_lv_font_montserrat_14) }, +#endif +#if LV_FONT_MONTSERRAT_16 { MP_ROM_QSTR(MP_QSTR_font_montserrat_16), MP_ROM_PTR(&mp_lv_font_montserrat_16) }, +#endif +#if LV_FONT_MONTSERRAT_24 { MP_ROM_QSTR(MP_QSTR_font_montserrat_24), MP_ROM_PTR(&mp_lv_font_montserrat_24) }, +#endif +#if LV_FONT_MONTSERRAT_32 { MP_ROM_QSTR(MP_QSTR_font_montserrat_32), MP_ROM_PTR(&mp_lv_font_montserrat_32) }, +#endif +#if LV_FONT_MONTSERRAT_40 { MP_ROM_QSTR(MP_QSTR_font_montserrat_40), MP_ROM_PTR(&mp_lv_font_montserrat_40) }, +#endif { MP_ROM_QSTR(MP_QSTR_style_const_prop_id_inv), MP_ROM_PTR(&mp_lv_style_const_prop_id_inv) }, { MP_ROM_QSTR(MP_QSTR_obj_class), MP_ROM_PTR(&mp_lv_obj_class) }, { MP_ROM_QSTR(MP_QSTR_binfont_font_class), MP_ROM_PTR(&mp_lv_binfont_font_class) }, @@ -49464,7 +49484,7 @@ static const mp_rom_map_elem_t lvgl_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_group_deinit), MP_ROM_PTR(&mp_lv_group_deinit_mpobj) }, { MP_ROM_QSTR(MP_QSTR_fs_init), MP_ROM_PTR(&mp_lv_fs_init_mpobj) }, { MP_ROM_QSTR(MP_QSTR_fs_deinit), MP_ROM_PTR(&mp_lv_fs_deinit_mpobj) }, - { MP_ROM_QSTR(MP_QSTR_event_mark_deleted), MP_ROM_PTR(&mp_lv_event_mark_deleted_mpobj) } + { MP_ROM_QSTR(MP_QSTR_event_mark_deleted), MP_ROM_PTR(&mp_lv_event_mark_deleted_mpobj) }, }; static MP_DEFINE_CONST_DICT(lvgl_module_globals, lvgl_module_globals_table); @@ -49509,11 +49529,21 @@ const mp_rom_map_elem_t lvgl_module_entries[] = { { MP_ROM_QSTR(MP_QSTR_TABLE_CELL_NONE), MP_ROM_PTR(MP_ROM_INT(ENUM_LV_TABLE_CELL_NONE)) }, { MP_ROM_QSTR(MP_QSTR_color_filter_shade), MP_ROM_PTR(&mp_lv_color_filter_shade) }, { MP_ROM_QSTR(MP_QSTR_tree_node_class), MP_ROM_PTR(&mp_lv_tree_node_class) }, +#if LV_FONT_MONTSERRAT_14 { MP_ROM_QSTR(MP_QSTR_font_montserrat_14), MP_ROM_PTR(&mp_lv_font_montserrat_14) }, +#endif +#if LV_FONT_MONTSERRAT_16 { MP_ROM_QSTR(MP_QSTR_font_montserrat_16), MP_ROM_PTR(&mp_lv_font_montserrat_16) }, +#endif +#if LV_FONT_MONTSERRAT_24 { MP_ROM_QSTR(MP_QSTR_font_montserrat_24), MP_ROM_PTR(&mp_lv_font_montserrat_24) }, +#endif +#if LV_FONT_MONTSERRAT_32 { MP_ROM_QSTR(MP_QSTR_font_montserrat_32), MP_ROM_PTR(&mp_lv_font_montserrat_32) }, +#endif +#if LV_FONT_MONTSERRAT_40 { MP_ROM_QSTR(MP_QSTR_font_montserrat_40), MP_ROM_PTR(&mp_lv_font_montserrat_40) }, +#endif { MP_ROM_QSTR(MP_QSTR_style_const_prop_id_inv), MP_ROM_PTR(&mp_lv_style_const_prop_id_inv) }, { MP_ROM_QSTR(MP_QSTR_obj_class), MP_ROM_PTR(&mp_lv_obj_class) }, { MP_ROM_QSTR(MP_QSTR_binfont_font_class), MP_ROM_PTR(&mp_lv_binfont_font_class) }, @@ -50091,7 +50121,7 @@ const mp_rom_map_elem_t lvgl_module_entries[] = { { MP_ROM_QSTR(MP_QSTR_group_deinit), MP_ROM_PTR(&mp_lv_group_deinit_mpobj) }, { MP_ROM_QSTR(MP_QSTR_fs_init), MP_ROM_PTR(&mp_lv_fs_init_mpobj) }, { MP_ROM_QSTR(MP_QSTR_fs_deinit), MP_ROM_PTR(&mp_lv_fs_deinit_mpobj) }, - { MP_ROM_QSTR(MP_QSTR_event_mark_deleted), MP_ROM_PTR(&mp_lv_event_mark_deleted_mpobj) } + { MP_ROM_QSTR(MP_QSTR_event_mark_deleted), MP_ROM_PTR(&mp_lv_event_mark_deleted_mpobj) }, }; const size_t lvgl_module_entry_count = sizeof(lvgl_module_entries) / sizeof(lvgl_module_entries[0]); diff --git a/generated/lvgl_micropython.c b/generated/lvgl_micropython.c index 67a2808..0b54672 100644 --- a/generated/lvgl_micropython.c +++ b/generated/lvgl_micropython.c @@ -28194,50 +28194,60 @@ static const mp_lv_struct_t mp_lv_tree_node_class = { * lvgl lv_font_montserrat_14 global definitions */ +#if LV_FONT_MONTSERRAT_14 static const mp_lv_struct_t mp_lv_font_montserrat_14 = { { &mp_lv_font_t_type }, (lv_font_t*)&lv_font_montserrat_14 }; +#endif /* * lvgl lv_font_montserrat_16 global definitions */ +#if LV_FONT_MONTSERRAT_16 static const mp_lv_struct_t mp_lv_font_montserrat_16 = { { &mp_lv_font_t_type }, (lv_font_t*)&lv_font_montserrat_16 }; +#endif /* * lvgl lv_font_montserrat_24 global definitions */ +#if LV_FONT_MONTSERRAT_24 static const mp_lv_struct_t mp_lv_font_montserrat_24 = { { &mp_lv_font_t_type }, (lv_font_t*)&lv_font_montserrat_24 }; +#endif /* * lvgl lv_font_montserrat_32 global definitions */ +#if LV_FONT_MONTSERRAT_32 static const mp_lv_struct_t mp_lv_font_montserrat_32 = { { &mp_lv_font_t_type }, (lv_font_t*)&lv_font_montserrat_32 }; +#endif /* * lvgl lv_font_montserrat_40 global definitions */ +#if LV_FONT_MONTSERRAT_40 static const mp_lv_struct_t mp_lv_font_montserrat_40 = { { &mp_lv_font_t_type }, (lv_font_t*)&lv_font_montserrat_40 }; +#endif /* Global struct wrapper for int */ @@ -49981,11 +49991,21 @@ static const mp_rom_map_elem_t lvgl_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_color_filter_shade), MP_ROM_PTR(&mp_lv_color_filter_shade) }, { MP_ROM_QSTR(MP_QSTR_tree_node_class), MP_ROM_PTR(&mp_lv_tree_node_class) }, + #if LV_FONT_MONTSERRAT_14 { MP_ROM_QSTR(MP_QSTR_font_montserrat_14), MP_ROM_PTR(&mp_lv_font_montserrat_14) }, + #endif + #if LV_FONT_MONTSERRAT_16 { MP_ROM_QSTR(MP_QSTR_font_montserrat_16), MP_ROM_PTR(&mp_lv_font_montserrat_16) }, + #endif + #if LV_FONT_MONTSERRAT_24 { MP_ROM_QSTR(MP_QSTR_font_montserrat_24), MP_ROM_PTR(&mp_lv_font_montserrat_24) }, + #endif + #if LV_FONT_MONTSERRAT_32 { MP_ROM_QSTR(MP_QSTR_font_montserrat_32), MP_ROM_PTR(&mp_lv_font_montserrat_32) }, + #endif + #if LV_FONT_MONTSERRAT_40 { MP_ROM_QSTR(MP_QSTR_font_montserrat_40), MP_ROM_PTR(&mp_lv_font_montserrat_40) }, + #endif { MP_ROM_QSTR(MP_QSTR_style_const_prop_id_inv), MP_ROM_PTR(&mp_lv_style_const_prop_id_inv) }, { MP_ROM_QSTR(MP_QSTR_obj_class), MP_ROM_PTR(&mp_lv_obj_class) }, { MP_ROM_QSTR(MP_QSTR_binfont_font_class), MP_ROM_PTR(&mp_lv_binfont_font_class) }, diff --git a/generated/lvgl_python.c b/generated/lvgl_python.c index 7faf0c2..264d944 100644 --- a/generated/lvgl_python.c +++ b/generated/lvgl_python.c @@ -97877,11 +97877,21 @@ PyMODINIT_FUNC PyInit_lvgl(void) if (PyType_Ready(&py_lv_draw_sw_mask_map_param_cfg_t_type) < 0) return NULL; { PyObject *obj = lv_to_mp_struct(&py_lv_color_filter_dsc_t_type, (void *)&lv_color_filter_shade); if (obj == NULL) return NULL; if (PyModule_AddObject(m, "color_filter_shade", obj) < 0) { Py_DECREF(obj); return NULL; } } { PyObject *obj = lv_to_mp_struct(&py_lv_tree_class_t_type, (void *)&lv_tree_node_class); if (obj == NULL) return NULL; if (PyModule_AddObject(m, "tree_node_class", obj) < 0) { Py_DECREF(obj); return NULL; } } +#if LV_FONT_MONTSERRAT_14 { PyObject *obj = lv_to_mp_struct(&py_lv_font_t_type, (void *)&lv_font_montserrat_14); if (obj == NULL) return NULL; if (PyModule_AddObject(m, "font_montserrat_14", obj) < 0) { Py_DECREF(obj); return NULL; } } +#endif +#if LV_FONT_MONTSERRAT_16 { PyObject *obj = lv_to_mp_struct(&py_lv_font_t_type, (void *)&lv_font_montserrat_16); if (obj == NULL) return NULL; if (PyModule_AddObject(m, "font_montserrat_16", obj) < 0) { Py_DECREF(obj); return NULL; } } +#endif +#if LV_FONT_MONTSERRAT_24 { PyObject *obj = lv_to_mp_struct(&py_lv_font_t_type, (void *)&lv_font_montserrat_24); if (obj == NULL) return NULL; if (PyModule_AddObject(m, "font_montserrat_24", obj) < 0) { Py_DECREF(obj); return NULL; } } +#endif +#if LV_FONT_MONTSERRAT_32 { PyObject *obj = lv_to_mp_struct(&py_lv_font_t_type, (void *)&lv_font_montserrat_32); if (obj == NULL) return NULL; if (PyModule_AddObject(m, "font_montserrat_32", obj) < 0) { Py_DECREF(obj); return NULL; } } +#endif +#if LV_FONT_MONTSERRAT_40 { PyObject *obj = lv_to_mp_struct(&py_lv_font_t_type, (void *)&lv_font_montserrat_40); if (obj == NULL) return NULL; if (PyModule_AddObject(m, "font_montserrat_40", obj) < 0) { Py_DECREF(obj); return NULL; } } +#endif { PyObject *obj = lv_to_mp_struct(&py_blob_type, (void *)&lv_style_const_prop_id_inv); if (obj == NULL) return NULL; if (PyModule_AddObject(m, "style_const_prop_id_inv", obj) < 0) { Py_DECREF(obj); return NULL; } } { PyObject *obj = lv_to_mp_struct(&py_lv_obj_class_t_type, (void *)&lv_obj_class); if (obj == NULL) return NULL; if (PyModule_AddObject(m, "obj_class", obj) < 0) { Py_DECREF(obj); return NULL; } } { PyObject *obj = lv_to_mp_struct(&py_lv_font_class_t_type, (void *)&lv_binfont_font_class); if (obj == NULL) return NULL; if (PyModule_AddObject(m, "binfont_font_class", obj) < 0) { Py_DECREF(obj); return NULL; } } diff --git a/lv_conf.h b/lv_conf.h index 7a5b325..908c88a 100644 --- a/lv_conf.h +++ b/lv_conf.h @@ -459,28 +459,72 @@ extern void mp_lv_deinit_gc(void); *===================*/ /*Montserrat fonts with ASCII range and some symbols using bpp = 4 - *https://fonts.google.com/specimen/Montserrat*/ + *https://fonts.google.com/specimen/Montserrat + *A build can turn an enabled size off with -DLV_FONT_MONTSERRAT_=0 to save + *flash; the binding leaves that font out without being regenerated.*/ +#ifndef LV_FONT_MONTSERRAT_8 #define LV_FONT_MONTSERRAT_8 0 +#endif +#ifndef LV_FONT_MONTSERRAT_10 #define LV_FONT_MONTSERRAT_10 0 +#endif +#ifndef LV_FONT_MONTSERRAT_12 #define LV_FONT_MONTSERRAT_12 0 +#endif +#ifndef LV_FONT_MONTSERRAT_14 #define LV_FONT_MONTSERRAT_14 1 +#endif +#ifndef LV_FONT_MONTSERRAT_16 #define LV_FONT_MONTSERRAT_16 1 +#endif +#ifndef LV_FONT_MONTSERRAT_18 #define LV_FONT_MONTSERRAT_18 0 +#endif +#ifndef LV_FONT_MONTSERRAT_20 #define LV_FONT_MONTSERRAT_20 0 +#endif +#ifndef LV_FONT_MONTSERRAT_22 #define LV_FONT_MONTSERRAT_22 0 +#endif +#ifndef LV_FONT_MONTSERRAT_24 #define LV_FONT_MONTSERRAT_24 1 +#endif +#ifndef LV_FONT_MONTSERRAT_26 #define LV_FONT_MONTSERRAT_26 0 +#endif +#ifndef LV_FONT_MONTSERRAT_28 #define LV_FONT_MONTSERRAT_28 0 +#endif +#ifndef LV_FONT_MONTSERRAT_30 #define LV_FONT_MONTSERRAT_30 0 +#endif +#ifndef LV_FONT_MONTSERRAT_32 #define LV_FONT_MONTSERRAT_32 1 +#endif +#ifndef LV_FONT_MONTSERRAT_34 #define LV_FONT_MONTSERRAT_34 0 +#endif +#ifndef LV_FONT_MONTSERRAT_36 #define LV_FONT_MONTSERRAT_36 0 +#endif +#ifndef LV_FONT_MONTSERRAT_38 #define LV_FONT_MONTSERRAT_38 0 +#endif +#ifndef LV_FONT_MONTSERRAT_40 #define LV_FONT_MONTSERRAT_40 1 +#endif +#ifndef LV_FONT_MONTSERRAT_42 #define LV_FONT_MONTSERRAT_42 0 +#endif +#ifndef LV_FONT_MONTSERRAT_44 #define LV_FONT_MONTSERRAT_44 0 +#endif +#ifndef LV_FONT_MONTSERRAT_46 #define LV_FONT_MONTSERRAT_46 0 +#endif +#ifndef LV_FONT_MONTSERRAT_48 #define LV_FONT_MONTSERRAT_48 0 +#endif /*Demonstrate special features*/ #define LV_FONT_MONTSERRAT_28_COMPRESSED 0 /*bpp = 3*/ diff --git a/tests/test_font_guards.py b/tests/test_font_guards.py new file mode 100644 index 0000000..769afe3 --- /dev/null +++ b/tests/test_font_guards.py @@ -0,0 +1,37 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + +"""A build can turn a Montserrat size off without regenerating the binding.""" + +import re +from pathlib import Path + +from binding.util import config_guard, guarded + +REPO = Path(__file__).resolve().parents[1] + + +def test_only_montserrat_fonts_are_guarded(): + assert config_guard("lv_font_montserrat_40") == "LV_FONT_MONTSERRAT_40" + assert config_guard("lv_font_montserrat_28_compressed") is None + assert config_guard("lv_tree_node_class") is None + assert guarded("x", "lv_tree_node_class") == "x" + assert guarded("x", "lv_font_montserrat_14") == "#if LV_FONT_MONTSERRAT_14\nx\n#endif" + assert guarded("x", "lv_font_montserrat_14", " ") == "#if LV_FONT_MONTSERRAT_14\n x\n #endif" + + +def test_lv_conf_lets_a_build_override_every_montserrat_size(): + conf = (REPO / "lv_conf.h").read_text() + for size in re.findall(r"#define LV_FONT_MONTSERRAT_(\d+) ", conf): + assert "#ifndef LV_FONT_MONTSERRAT_%s\n#define LV_FONT_MONTSERRAT_%s " % (size, size) in conf + + +def test_every_generated_font_is_guarded(): + for name in ("lvgl_micropython.c", "lvgl_circuitpython.c", "lvgl_python.c"): + text = (REPO / "generated" / name).read_text() + for line_no, line in enumerate(text.splitlines()): + if re.search(r"[&(]\s*\(?(lv_font_t\*\)\s*)?&?lv_font_montserrat_\d+\b", line) or "mp_lv_font_montserrat_" in line: + size = re.search(r"montserrat_(\d+)", line).group(1) + before = text.splitlines()[max(0, line_no - 3):line_no] + assert any(l.strip() == "#if LV_FONT_MONTSERRAT_%s" % size for l in before), (name, line)