circuitpython/py/makemoduledefs.py
Damien George 5ce1a03a78 py/makemoduledefs.py: Automatically declare delegation attr functions.
So that the delegation functions don't need to be put somewhere global,
like in mpconfigport.h.  That would otherwise make it hard for extension
modules to use delegation.

Signed-off-by: Damien George <damien@micropython.org>
2023-06-14 19:03:46 +10:00

136 lines
4.2 KiB
Python

"""
This pre-processor parses a single file containing a list of
`MP_REGISTER_MODULE(MP_QSTR_module_name, obj_module)` or
`MP_REGISTER_EXTENSIBLE_MODULE(MP_QSTR_module_name, obj_module)`
(i.e. the output of `py/makeqstrdefs.py cat module`).
The output is a header (typically moduledefs.h) which is included by
py/objmodule.c that contains entries to be included in the definition of
- mp_rom_map_elem_t mp_builtin_module_table[]
- mp_rom_map_elem_t mp_builtin_extensible_module_table[]
Extensible modules are modules that can be overridden from the filesystem, see
py/builtinimnport.c:process_import_at_level. Regular modules will always use
the built-in version.
"""
from __future__ import print_function
import sys
import re
import io
import argparse
register_pattern = re.compile(
r"\s*(MP_REGISTER_MODULE|MP_REGISTER_EXTENSIBLE_MODULE)\(MP_QSTR_(.*?),\s*(.*?)\);",
flags=re.DOTALL,
)
delegation_pattern = re.compile(
r"\s*(?:MP_REGISTER_MODULE_DELEGATION)\((.*?),\s*(.*?)\);",
flags=re.DOTALL,
)
def find_module_registrations(filename):
"""Find any MP_REGISTER_MODULE definitions in the provided file.
:param str filename: path to file to check
:return: List[(module_name, obj_module)]
"""
global pattern
with io.open(filename, encoding="utf-8") as c_file_obj:
c = c_file_obj.read()
return set(re.findall(register_pattern, c)), set(re.findall(delegation_pattern, c))
def generate_module_table_header(modules):
"""Generate header with module table entries for builtin modules.
:param List[(module_name, obj_module)] modules: module defs
:return: None
"""
# Print header file for all external modules.
mod_defs = set()
extensible_mod_defs = set()
for macro_name, module_name, obj_module in modules:
mod_def = "MODULE_DEF_{}".format(module_name.upper())
if macro_name == "MP_REGISTER_MODULE":
mod_defs.add(mod_def)
elif macro_name == "MP_REGISTER_EXTENSIBLE_MODULE":
extensible_mod_defs.add(mod_def)
if "," in obj_module:
print(
"ERROR: Call to {}({}, {}) should be {}({}, {})\n".format(
macro_name,
module_name,
obj_module,
macro_name,
module_name,
obj_module.split(",")[0],
),
file=sys.stderr,
)
sys.exit(1)
print(
(
"extern const struct _mp_obj_module_t {obj_module};\n"
"#undef {mod_def}\n"
"#define {mod_def} {{ MP_ROM_QSTR(MP_QSTR_{module_name}), MP_ROM_PTR(&{obj_module}) }},\n"
).format(
module_name=module_name,
obj_module=obj_module,
mod_def=mod_def,
)
)
print("\n#define MICROPY_REGISTERED_MODULES \\")
for mod_def in sorted(mod_defs):
print(" {mod_def} \\".format(mod_def=mod_def))
print("// MICROPY_REGISTERED_MODULES")
print("\n#define MICROPY_REGISTERED_EXTENSIBLE_MODULES \\")
for mod_def in sorted(extensible_mod_defs):
print(" {mod_def} \\".format(mod_def=mod_def))
print("// MICROPY_REGISTERED_EXTENSIBLE_MODULES")
def generate_module_delegations(delegations):
if not delegations:
return
print()
for obj_module, fun_name in delegations:
print("extern void {}(mp_obj_t self_in, qstr attr, mp_obj_t *dest);".format(fun_name))
print("#define MICROPY_MODULE_DELEGATIONS \\")
for obj_module, fun_name in delegations:
print(
" {{ MP_ROM_PTR(&{obj_module}), {fun_name} }}, \\".format(
obj_module=obj_module, fun_name=fun_name
)
)
print("// MICROPY_MODULE_DELEGATIONS")
def main():
parser = argparse.ArgumentParser()
parser.add_argument("file", nargs=1, help="file with MP_REGISTER_MODULE definitions")
args = parser.parse_args()
print("// Automatically generated by makemoduledefs.py.\n")
modules, delegations = find_module_registrations(args.file[0])
generate_module_table_header(sorted(modules))
generate_module_delegations(sorted(delegations))
if __name__ == "__main__":
main()