|
| 1 | +#!/usr/bin/env python |
| 2 | + |
| 3 | +# This pre-processor parses provided objects' c files for |
| 4 | +# MP_REGISTER_MODULE(module_name, obj_module, enabled_define) |
| 5 | +# These are used to generate a header with the required entries for |
| 6 | +# "mp_rom_map_elem_t mp_builtin_module_table[]" in py/objmodule.c |
| 7 | + |
| 8 | +from __future__ import print_function |
| 9 | + |
| 10 | +import re |
| 11 | +import os |
| 12 | +import argparse |
| 13 | + |
| 14 | + |
| 15 | +pattern = re.compile( |
| 16 | + r"[\n;]\s*MP_REGISTER_MODULE\((.*?),\s*(.*?),\s*(.*?)\);", |
| 17 | + flags=re.DOTALL |
| 18 | +) |
| 19 | + |
| 20 | + |
| 21 | +def find_c_file(obj_file, vpath): |
| 22 | + """ Search vpaths for the c file that matches the provided object_file. |
| 23 | +
|
| 24 | + :param str obj_file: object file to find the matching c file for |
| 25 | + :param List[str] vpath: List of base paths, similar to gcc vpath |
| 26 | + :return: str path to c file or None |
| 27 | + """ |
| 28 | + c_file = None |
| 29 | + relative_c_file = os.path.splitext(obj_file)[0] + ".c" |
| 30 | + relative_c_file = relative_c_file.lstrip('/\\') |
| 31 | + for p in vpath: |
| 32 | + possible_c_file = os.path.join(p, relative_c_file) |
| 33 | + if os.path.exists(possible_c_file): |
| 34 | + c_file = possible_c_file |
| 35 | + break |
| 36 | + |
| 37 | + return c_file |
| 38 | + |
| 39 | + |
| 40 | +def find_module_registrations(c_file): |
| 41 | + """ Find any MP_REGISTER_MODULE definitions in the provided c file. |
| 42 | +
|
| 43 | + :param str c_file: path to c file to check |
| 44 | + :return: List[(module_name, obj_module, enabled_define)] |
| 45 | + """ |
| 46 | + global pattern |
| 47 | + |
| 48 | + if c_file is None: |
| 49 | + # No c file to match the object file, skip |
| 50 | + return set() |
| 51 | + |
| 52 | + with open(c_file) as c_file_obj: |
| 53 | + return set(re.findall(pattern, c_file_obj.read())) |
| 54 | + |
| 55 | + |
| 56 | +def generate_module_table_header(modules): |
| 57 | + """ Generate header with module table entries for builtin modules. |
| 58 | +
|
| 59 | + :param List[(module_name, obj_module, enabled_define)] modules: module defs |
| 60 | + :return: None |
| 61 | + """ |
| 62 | + |
| 63 | + # Print header file for all external modules. |
| 64 | + mod_defs = [] |
| 65 | + print("// Automatically generated by makemoduledefs.py.\n") |
| 66 | + for module_name, obj_module, enabled_define in modules: |
| 67 | + mod_def = "MODULE_DEF_{}".format(module_name.upper()) |
| 68 | + mod_defs.append(mod_def) |
| 69 | + print(( |
| 70 | + "#if ({enabled_define})\n" |
| 71 | + " extern const struct _mp_obj_module_t {obj_module};\n" |
| 72 | + " #define {mod_def} {{ MP_ROM_QSTR({module_name}), MP_ROM_PTR(&{obj_module}) }},\n" |
| 73 | + "#else\n" |
| 74 | + " #define {mod_def}\n" |
| 75 | + "#endif\n" |
| 76 | + ).format(module_name=module_name, obj_module=obj_module, |
| 77 | + enabled_define=enabled_define, mod_def=mod_def) |
| 78 | + ) |
| 79 | + |
| 80 | + print("\n#define MICROPY_REGISTERED_MODULES \\") |
| 81 | + |
| 82 | + for mod_def in mod_defs: |
| 83 | + print(" {mod_def} \\".format(mod_def=mod_def)) |
| 84 | + |
| 85 | + print("// MICROPY_REGISTERED_MODULES") |
| 86 | + |
| 87 | + |
| 88 | +def main(): |
| 89 | + parser = argparse.ArgumentParser() |
| 90 | + parser.add_argument("--vpath", default=".", |
| 91 | + help="comma separated list of folders to search for c files in") |
| 92 | + parser.add_argument("files", nargs="*", |
| 93 | + help="list of c files to search") |
| 94 | + args = parser.parse_args() |
| 95 | + |
| 96 | + vpath = [p.strip() for p in args.vpath.split(',')] |
| 97 | + |
| 98 | + modules = set() |
| 99 | + for obj_file in args.files: |
| 100 | + c_file = find_c_file(obj_file, vpath) |
| 101 | + modules |= find_module_registrations(c_file) |
| 102 | + |
| 103 | + generate_module_table_header(sorted(modules)) |
| 104 | + |
| 105 | + |
| 106 | +if __name__ == '__main__': |
| 107 | + main() |
0 commit comments