From a44c215d5d2d19710bf9d7ee0f8907891587b4f3 Mon Sep 17 00:00:00 2001 From: Kevin Schlosser Date: Wed, 11 Dec 2024 23:43:04 -0700 Subject: [PATCH 1/2] test updating lvgl to master --- ext_mod/lvgl/micropython.cmake | 6 +- ext_mod/lvgl/micropython.mk | 4 +- gen/python_api_gen_mpy.py | 133 ++++++++++++++++++++------------- 3 files changed, 89 insertions(+), 54 deletions(-) diff --git a/ext_mod/lvgl/micropython.cmake b/ext_mod/lvgl/micropython.cmake index e2d53e21..94c8da02 100644 --- a/ext_mod/lvgl/micropython.cmake +++ b/ext_mod/lvgl/micropython.cmake @@ -13,7 +13,8 @@ list(APPEND LV_CFLAGS separate_arguments(SECOND_BUILD_ENV UNIX_COMMAND $ENV{SECOND_BUILD}) -set(LVGL_HEADER "${BINDING_DIR}/build/lvgl_header.h") +set(LVGL_DIR "${BINDING_DIR}/lib/lvgl") + file(GLOB_RECURSE LVGL_HEADERS ${BINDING_DIR}/lib/lvgl/src/*.h ${BINDING_DIR}/lib/lv_conf.h) @@ -25,9 +26,10 @@ file(GLOB_RECURSE LVGL_HEADERS ${BINDING_DIR}/lib/lvgl/src/*.h ${BINDING_DIR}/li # found to go about doing it. if(${SECOND_BUILD_ENV} EQUAL "0") + execute_process( COMMAND - ${Python3_EXECUTABLE} ${BINDING_DIR}/gen/$ENV{GEN_SCRIPT}_api_gen_mpy.py ${LV_CFLAGS} --output=${CMAKE_BINARY_DIR}/lv_mp.c --include=${BINDING_DIR}/lib --include=${BINDING_DIR}/lib/lvgl --board=$ENV{LV_PORT} --module_name=lvgl --module_prefix=lv --metadata=${CMAKE_BINARY_DIR}/lv_mp.c.json --header_file=${LVGL_HEADER} + ${Python3_EXECUTABLE} ${BINDING_DIR}/gen/$ENV{GEN_SCRIPT}_api_gen_mpy.py ${LV_CFLAGS} --output=${CMAKE_BINARY_DIR}/lv_mp.c --include=${BINDING_DIR}/lib --include=${BINDING_DIR}/lib/lvgl --board=$ENV{LV_PORT} --module_name=lvgl --module_prefix=lv --metadata=${CMAKE_BINARY_DIR}/lv_mp.c.json --header_file=${LVGL_DIR}/lvgl.h WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} diff --git a/ext_mod/lvgl/micropython.mk b/ext_mod/lvgl/micropython.mk index 0b074fa0..b1c0c1a5 100644 --- a/ext_mod/lvgl/micropython.mk +++ b/ext_mod/lvgl/micropython.mk @@ -8,7 +8,6 @@ LVGL_BINDING_DIR = $(subst /ext_mod/lvgl,,$(MOD_DIR)) LIB_DIR = $(LVGL_BINDING_DIR)/lib LVGL_DIR = $(LVGL_BINDING_DIR)/lib/lvgl -LVGL_HEADER = $(LVGL_BINDING_DIR)/build/lvgl_header.h CURRENT_DIR = $(LVGL_BINDING_DIR)/ext_mod/lvgl CFLAGS_USERMOD += -I$(LVGL_DIR) @@ -37,7 +36,8 @@ SRC_USERMOD_C += $(LVGL_MPY) $(LVGL_MPY): $(ALL_LVGL_SRC) $(LVGL_BINDING_DIR)/gen/$(GEN_SCRIPT)_api_gen_mpy.py $(ECHO) "LVGL-GEN $@" $(Q)mkdir -p $(dir $@) - $(Q)$(PYTHON) $(LVGL_BINDING_DIR)/gen/$(GEN_SCRIPT)_api_gen_mpy.py $(LV_CFLAGS) --board=$(LV_PORT) --output=$(LVGL_MPY) --include=$(LIB_DIR) --include=$(LVGL_DIR) --module_name=lvgl --module_prefix=lv --metadata=$(LVGL_MPY_METADATA) --header_file=$(LVGL_HEADER) + + $(Q)$(PYTHON) $(LVGL_BINDING_DIR)/gen/$(GEN_SCRIPT)_api_gen_mpy.py $(LV_CFLAGS) --board=$(LV_PORT) --output=$(LVGL_MPY) --include=$(LIB_DIR) --include=$(LVGL_DIR) --module_name=lvgl --module_prefix=lv --metadata=$(LVGL_MPY_METADATA) --header_file=$(LVGL_DIR)/lvgl.h .PHONY: LVGL_MPY LVGL_MPY: $(LVGL_MPY) diff --git a/gen/python_api_gen_mpy.py b/gen/python_api_gen_mpy.py index 83a1e24d..e399b890 100644 --- a/gen/python_api_gen_mpy.py +++ b/gen/python_api_gen_mpy.py @@ -25,7 +25,6 @@ def eprint(*args, **kwargs): # from pudb.remote import set_trace # set_trace(term_size=(180, 50)) -from sys import argv import sys import os @@ -35,7 +34,8 @@ def eprint(*args, **kwargs): from os.path import commonprefix script_path = dirname(abspath(__file__)) -pycparser_path = os.path.join(script_path, '..', 'lib', 'pycparser') +project_path = os.path.abspath(os.path.join(script_path, '..')) +pycparser_path = os.path.join(project_path, 'lib', 'pycparser') sys.path.insert(0, os.path.abspath(pycparser_path)) @@ -93,8 +93,7 @@ def Node__repr__(self): return result -c_ast._repr = _repr -setattr(c_ast.Node, '__repr__', Node__repr__) + # @@ -110,15 +109,47 @@ def Node__repr__(self): argParser.add_argument('--board', dest='board', help='Board or OS', metavar='', action='store', default='') argParser.add_argument('--output', dest='output', help='Output file path', metavar='', action='store') argParser.add_argument('--debug', dest='debug', help='enable debugging output', action='store_true') -argParser.add_argument('--header_file', dest='input', action='append', default=[]) +argParser.add_argument('--header_file', dest='header', action='store', default=None) args, unknownargs = argParser.parse_known_args() module_name = args.module_name module_prefix = args.module_prefix if args.module_prefix else args.module_name -input_headers = args.input[:] +input_header = args.header DEBUG = args.debug +lvgl_path = os.path.dirname(input_header) +private_header = os.path.join(lvgl_path, 'lv_private.h') + +lv_config_path = os.path.abspath(os.path.join(lvgl_path, '..', 'lv_conf.h')) +gen_json_path = os.path.join(lvgl_path, 'scripts/gen_json') + +sys.path.insert(0, gen_json_path) + +original_nodes = {} + +import inspect + +for key, value in c_ast.__dict__.items(): + if inspect.isclass(value): + original_nodes[key] = value + + +import gen_json + + +json_ast = gen_json.run(None, lv_config_path, False, os.path.join(project_path, 'build', 'lvgl_header.h'), False) +lvgl_json = json_ast.to_dict() + + +for key, value in original_nodes.items(): + setattr(c_ast, key, value) + + +c_ast._repr = _repr +setattr(c_ast.Node, '__repr__', Node__repr__) + + pp_file = args.output.rsplit('.', 1)[0] + '.pp' if DEBUG: @@ -269,7 +300,7 @@ def get_path(name: str, p: str) -> str: cpp_cmd.extend([f'-D{define}' for define in args.define]) cpp_cmd.extend(['-DPYCPARSER', '-E', f'-I{fake_libc_path}']) cpp_cmd.extend([f'-I{include}' for include in args.include]) -cpp_cmd.append(f'"{input_headers[0]}"') +cpp_cmd.append(f'"{input_header}"') if sys.platform.startswith('win'): @@ -439,7 +470,7 @@ def function_prototype(func): create_obj_pattern = re.compile('^{prefix}_(.+)_create$'.format(prefix=module_prefix)) lv_method_pattern = re.compile('^{prefix}_[^_]+_(.+)'.format(prefix=module_prefix), re.IGNORECASE) lv_base_obj_pattern = re.compile('^(struct _){{0,1}}{prefix}_{base_name}_t( [*]){{0,1}}'.format(prefix=module_prefix, base_name = base_obj_name)) -lv_str_enum_pattern = re.compile('^_{prefix}_STR_(.+)'.format(prefix=module_prefix.upper())) +lv_str_enum_pattern = re.compile('^_?{prefix}_STR_(.+)'.format(prefix=module_prefix.upper())) lv_callback_type_pattern = re.compile('({prefix}_){{0,1}}(.+)_cb(_t){{0,1}}'.format(prefix=module_prefix)) lv_global_callback_pattern = re.compile('.*g_cb_t') lv_func_returns_array = re.compile('.*_array$') @@ -620,7 +651,7 @@ def is_struct(type): pp_data = f.read() cparser = pycparser.CParser() -ast = cparser.parse(pp_data, input_headers[0]) +ast = cparser.parse(pp_data, input_header) forward_struct_decls = {} @@ -733,18 +764,17 @@ def my_excepthook(exc_type, exc_value, tb): synonym[t.declname] = t.type.name # eprint('%s === struct %s' % (t.declname, t.type.name)) struct_typedefs = [typedef for typedef in typedefs if is_struct(typedef.type)] -structs = collections.OrderedDict((typedef.declname, typedef.type) for typedef in struct_typedefs if typedef.declname and typedef.type.decls) # and not lv_base_obj_pattern.match(typedef.declname)) structs_without_typedef = collections.OrderedDict((decl.type.name, decl.type) for decl in ast.ext if hasattr(decl, 'type') and is_struct(decl.type)) -# -# # for typedefs that referenced to a forward declaration struct, replace it with the real definition. -# for typedef in struct_typedefs: -# if typedef.type.decls is None: # None means it's a forward declaration -# struct_name = typedef.type.name -# # check if it's found in `structs_without_typedef`. It actually has the typedef. Replace type with it. -# if typedef.type.name in structs_without_typedef: -# typedef.type = structs_without_typedef[struct_name] -# -# structs = collections.OrderedDict((typedef.declname, typedef.type) for typedef in struct_typedefs if typedef.declname and typedef.type.decls) # and not lv_base_obj_pattern.match(typedef.declname)) + +# for typedefs that referenced to a forward declaration struct, replace it with the real definition. +for typedef in struct_typedefs: + if typedef.type.decls is None: # None means it's a forward declaration + struct_name = typedef.type.name + # check if it's found in `structs_without_typedef`. It actually has the typedef. Replace type with it. + if typedef.type.name in structs_without_typedef: + typedef.type = structs_without_typedef[struct_name] + +structs = collections.OrderedDict((typedef.declname, typedef.type) for typedef in struct_typedefs if typedef.declname and typedef.type.decls) # and not lv_base_obj_pattern.match(typedef.declname)) structs.update(structs_without_typedef) # This is for struct without typedef explicit_structs = collections.OrderedDict((typedef.type.name, typedef.declname) for typedef in struct_typedefs if typedef.type.name) # and not lv_base_obj_pattern.match(typedef.type.name)) opaque_structs = collections.OrderedDict((typedef.declname, c_ast.Struct(name=typedef.declname, decls=[])) for typedef in typedefs if isinstance(typedef.type, c_ast.Struct) and typedef.type.decls == None) @@ -1202,6 +1232,8 @@ def register_int_ptr_type(convertor, *types): # Emit Header # +input_headers = [input_header, private_header] + print (""" /* * Auto-Generated file, DO NOT EDIT! @@ -1237,7 +1269,7 @@ def register_int_ptr_type(convertor, *types): {lv_headers} """.format( module_name = module_name, - cmd_line=' '.join(argv), + cmd_line=' '.join(sys.argv), pp_cmd=pp_cmd, objs=", ".join(['%s(%s)' % (objname, parent_obj_names[objname]) for objname in obj_names]), lv_headers='\n'.join('#include "%s"' % header for header in input_headers))) @@ -2218,7 +2250,7 @@ def register_int_ptr_type(convertor, *types): enums[enum_name] = enum for enum in [enum for enum in enums if len(enums[enum]) == 1 and enum.startswith('ENUM')]: - int_constants.append('%s_%s' % (enum, list(enums[enum].keys())[0])) + int_constants.append('%s_%s' % (enum, next(iter(enums[enum])))) enum_name = '%s_%s' % (enum, list(enums[enum].keys())[0]) constant_metadata[enum_name.replace('ENUM_', '').replace('LV_', '')] = {'py_type': 'int', 'c_type': enum_name.replace('ENUM_', '')} del enums[enum] @@ -2327,14 +2359,17 @@ def get_user_data(func, func_name = None, containing_struct = None, containing_s user_data = 'user_data' user_data_found = user_data in [decl.name for decl in flatten_struct_decls] # print('/* --> callback: user_data=%s user_data_found=%s containing_struct=%s */' % (user_data, user_data_found, containing_struct)) - - ud_accessors = get_user_data_accessors(containing_struct, containing_struct_name) - if user_data_found: - res = (user_data, ) - else: - res = (None, ) - - return res + ud_accessors + if not user_data_found and lvgl_json is not None: + containing_struct_j = next((struct for struct in lvgl_json["structures"] if struct["name"] == struct_arg_type_name), None) + if (containing_struct_j is None + and struct_arg_type_name.startswith("lv_") + and None is not next((fwd_decl for fwd_decl in lvgl_json["forward_decls"] if fwd_decl["name"] == struct_arg_type_name), None) + ): + struct_arg_type_name_with_underscore = "_" + struct_arg_type_name + containing_struct_j = next((struct for struct in lvgl_json["structures"] if struct["name"] == struct_arg_type_name_with_underscore), None) + if containing_struct_j is not None: + user_data_found = any(user_data == field["name"] for field in containing_struct_j["fields"]) + return (user_data if user_data_found else None), *get_user_data_accessors(containing_struct, containing_struct_name) # # Generate structs when needed @@ -2438,12 +2473,10 @@ def try_generate_struct(struct_name, struct): gen_func_error(decl, "Missing 'user_data' as a field of the first parameter of the callback function '%s_%s_callback'" % (struct_name, func_name)) else: gen_func_error(decl, "Missing 'user_data' member in struct '%s'" % struct_name) - - write_cases.append('case MP_QSTR_{field}: data->{field} = {cast}mp_lv_callback(dest[1], {lv_callback} ,MP_QSTR_{struct_name}_{field}, {user_data}, NULL, NULL, NULL); break; // converting to callback {type_name}'. - format(struct_name = struct_name, field = sanitize(decl.name), lv_callback = lv_callback, user_data = full_user_data_ptr, type_name = type_name, cast = cast)) - read_cases.append('case MP_QSTR_{field}: dest[0] = mp_lv_funcptr(&mp_{funcptr}_mpobj, {cast}data->{field}, {lv_callback} ,MP_QSTR_{struct_name}_{field}, {user_data}); break; // converting from callback {type_name}'. - format(struct_name = struct_name, field = sanitize(decl.name), lv_callback = lv_callback, funcptr = lv_to_mp_funcptr[type_name], user_data = full_user_data, type_name = type_name, cast = cast)) - attribute_meta[sanitize(decl.name)] = {'py_type': 'Callable', 'c_type': type_name, 'is_writeable': True, 'is_readable': True} + write_cases.append('case MP_QSTR_{field}: data->{decl_name} = {cast}mp_lv_callback(dest[1], {lv_callback} ,MP_QSTR_{struct_name}_{field}, {user_data}, NULL, NULL, NULL); break; // converting to callback {type_name}'. + format(struct_name = struct_name, field = sanitize(decl.name), decl_name = decl.name, lv_callback = lv_callback, user_data = full_user_data_ptr, type_name = type_name, cast = cast)) + read_cases.append('case MP_QSTR_{field}: dest[0] = mp_lv_funcptr(&mp_{funcptr}_mpobj, {cast}data->{decl_name}, {lv_callback} ,MP_QSTR_{struct_name}_{field}, {user_data}); break; // converting from callback {type_name}'. + format(struct_name = struct_name, field = sanitize(decl.name), decl_name = decl.name, lv_callback = lv_callback, funcptr = lv_to_mp_funcptr[type_name], user_data = full_user_data, type_name = type_name, cast = cast)) else: user_data = None # Only allow write to non-const members @@ -2455,20 +2488,16 @@ def try_generate_struct(struct_name, struct): if isinstance(decl.type, c_ast.ArrayDecl): memcpy_size = 'sizeof(%s)*%s' % (gen.visit(decl.type.type), gen.visit(decl.type.dim)) if is_writeable: - write_cases.append('case MP_QSTR_{field}: memcpy((void*)&data->{field}, {cast}{convertor}(dest[1]), {size}); break; // converting to {type_name}'. - format(field = sanitize(decl.name), convertor = mp_to_lv_convertor, type_name = type_name, cast = cast, size = memcpy_size)) - - - read_cases.append('case MP_QSTR_{field}: dest[0] = {convertor}({cast}data->{field}); break; // converting from {type_name}'. - format(field = sanitize(decl.name), convertor = lv_to_mp_convertor, type_name = type_name, cast = cast)) + write_cases.append('case MP_QSTR_{field}: memcpy((void*)&data->{decl_name}, {cast}{convertor}(dest[1]), {size}); break; // converting to {type_name}'. + format(field = sanitize(decl.name), decl_name = decl.name, convertor = mp_to_lv_convertor, type_name = type_name, cast = cast, size = memcpy_size)) + read_cases.append('case MP_QSTR_{field}: dest[0] = {convertor}({cast}data->{decl_name}); break; // converting from {type_name}'. + format(field = sanitize(decl.name), decl_name = decl.name, convertor = lv_to_mp_convertor, type_name = type_name, cast = cast)) else: if is_writeable: - write_cases.append('case MP_QSTR_{field}: data->{field} = {cast}{convertor}(dest[1]); break; // converting to {type_name}'. - format(field = sanitize(decl.name), convertor = mp_to_lv_convertor, type_name = type_name, cast = cast)) - - read_cases.append( - 'case MP_QSTR_{field}: dest[0] = {convertor}({cast}data->{field}); break; // converting from {type_name}'. - format(field=sanitize(decl.name), convertor=lv_to_mp_convertor, type_name=type_name, cast=cast)) + write_cases.append('case MP_QSTR_{field}: data->{decl_name} = {cast}{convertor}(dest[1]); break; // converting to {type_name}'. + format(field = sanitize(decl.name), decl_name = decl.name, convertor = mp_to_lv_convertor, type_name = type_name, cast = cast)) + read_cases.append('case MP_QSTR_{field}: dest[0] = {convertor}({cast}data->{decl_name}); break; // converting from {type_name}'. + format(field = sanitize(decl.name), decl_name = decl.name, convertor = lv_to_mp_convertor, type_name = type_name, cast = cast)) print(''' /* * Struct {struct_name} @@ -3082,7 +3111,7 @@ def gen_mp_func(func, obj_name): prototype_str = gen.visit(function_prototype(func)) if prototype_str in func_prototypes: original_func = func_prototypes[prototype_str] - if not original_func.name.endswith('cb_t') and generated_funcs[original_func.name] == True: + if generated_funcs[original_func.name] == True: print("/* Reusing %s for %s */" % (original_func.name, func.name)) emit_func_obj(func.name, original_func.name, param_count, func.name, is_static_member(func, base_obj_type)) @@ -3158,7 +3187,11 @@ def gen_mp_func(func, obj_name): func=func.name, func_ptr=prototype_str, print_func=gen.visit(func), - build_args="\n ".join(build_args), + build_args="\n ".join([build_mp_func_arg(arg, i, func, obj_name) for i,arg in enumerated_args + if isinstance(arg, c_ast.EllipsisParam) or + (not isinstance(arg.type, c_ast.TypeDecl)) or + (not isinstance(arg.type.type, c_ast.IdentifierType)) or + 'void' not in arg.type.type.names]), # Handle the case of 'void' param which should be ignored send_args=", ".join([(arg.name if (hasattr(arg, 'name') and arg.name) else ("arg%d" % i)) for i,arg in enumerate(args)]), build_result=build_result, build_return_value=build_return_value)) From 96aac818c37ce771e2f65fa7d25f656a236752e9 Mon Sep 17 00:00:00 2001 From: Kevin Schlosser Date: Thu, 12 Dec 2024 01:33:08 -0700 Subject: [PATCH 2/2] Step 1 of LVGL update complete. --- gen/fixed_gen_json.py | 120 ++++++++++++++++++++++++ gen/python_api_gen_mpy.py | 193 +++++++++++++++++++------------------- 2 files changed, 218 insertions(+), 95 deletions(-) create mode 100644 gen/fixed_gen_json.py diff --git a/gen/fixed_gen_json.py b/gen/fixed_gen_json.py new file mode 100644 index 00000000..cf6e638b --- /dev/null +++ b/gen/fixed_gen_json.py @@ -0,0 +1,120 @@ +import os +import sys +import shutil +import subprocess +import tempfile + +base_path = os.path.abspath(os.path.dirname(__file__)) + +project_path = os.path.abspath(os.path.join(base_path, '..', 'lib', 'lvgl')) +docs_path = os.path.join(project_path, 'docs') +sys.path.insert(0, docs_path) + + +import create_fake_lib_c # NOQA +import pycparser_monkeypatch # NOQA +import pycparser # NOQA + +temp_directory = tempfile.mkdtemp(suffix='.lvgl_json') + + +def run(lvgl_config_path, target_header, filter_private): + + pycparser_monkeypatch.FILTER_PRIVATE = filter_private + + lvgl_path = project_path + lvgl_src_path = os.path.join(lvgl_path, 'src') + temp_lvgl = os.path.join(temp_directory, 'lvgl') + target_header_base_name = ( + os.path.splitext(os.path.split(target_header)[-1])[0] + ) + + os.mkdir(temp_lvgl) + shutil.copytree(lvgl_src_path, os.path.join(temp_lvgl, 'src')) + shutil.copyfile(os.path.join(lvgl_path, 'lvgl.h'), os.path.join(temp_lvgl, 'lvgl.h')) + + pp_file = os.path.join(temp_directory, target_header_base_name + '.pp') + + src = lvgl_config_path + dst = os.path.join(temp_directory, 'lv_conf.h') + shutil.copyfile(src, dst) + + include_dirs = [temp_directory, project_path] + + if sys.platform.startswith('darwin'): + include_path_env_key = 'C_INCLUDE_PATH' + cpp_cmd = [ + 'clang', '-std=c11', '-E', '-DINT32_MIN=0x80000000', + ] + output_pp = f' >> "{pp_file}"' + else: + include_path_env_key = 'C_INCLUDE_PATH' + cpp_cmd = [ + 'gcc', '-std=c11', '-E', '-Wno-incompatible-pointer-types', + ] + output_pp = f' >> "{pp_file}"' + + fake_libc_path = create_fake_lib_c.run(temp_directory) + + if include_path_env_key not in os.environ: + os.environ[include_path_env_key] = '' + + os.environ[include_path_env_key] = ( + f'{fake_libc_path}{os.pathsep}{os.environ[include_path_env_key]}' + ) + + if 'PATH' not in os.environ: + os.environ['PATH'] = '' + + os.environ['PATH'] = ( + f'{fake_libc_path}{os.pathsep}{os.environ["PATH"]}' + ) + + cpp_cmd.extend([ + '-DLV_LVGL_H_INCLUDE_SIMPLE', + '-DLV_CONF_INCLUDE_SIMPLE', + '-DLV_USE_DEV_VERSION' + ]) + + cpp_cmd.extend(['-DPYCPARSER', f'"-I{fake_libc_path}"']) + cpp_cmd.extend([f'"-I{item}"' for item in include_dirs]) + cpp_cmd.append(f'"{target_header}"') + + if sys.platform.startswith('win'): + cpp_cmd.insert(len(cpp_cmd) - 2, output_pp) + else: + cpp_cmd.append(output_pp) + + cpp_cmd = ' '.join(cpp_cmd) + + p = subprocess.Popen( + cpp_cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + env=os.environ, + shell=True + ) + out, err = p.communicate() + exit_code = p.returncode + + if not os.path.exists(pp_file): + sys.stdout.write(out.decode('utf-8').strip() + '\n') + sys.stdout.write('EXIT CODE: ' + str(exit_code) + '\n') + sys.stderr.write(err.decode('utf-8').strip() + '\n') + sys.stdout.flush() + sys.stderr.flush() + + raise RuntimeError('Unknown Failure') + + with open(pp_file, 'r') as f: + pp_data = f.read() + + cparser = pycparser.CParser() + ast = cparser.parse(pp_data, target_header) + + ast.setup_docs(temp_directory) + + shutil.rmtree(temp_directory) + + return ast + diff --git a/gen/python_api_gen_mpy.py b/gen/python_api_gen_mpy.py index e399b890..634ee8d8 100644 --- a/gen/python_api_gen_mpy.py +++ b/gen/python_api_gen_mpy.py @@ -7,40 +7,40 @@ # - Implement inheritance instead of embed base methods (how? seems it's not supported, see https://github.com/micropython/micropython/issues/1159) # - When converting mp to ptr (and vice versa), verify that types are compatible. Now all pointers are casted to void*. -from __future__ import print_function import collections import copy -from functools import lru_cache +import functools import json +import inspect +import sys +import os +import argparse +import subprocess +import re + def memoize(func): - @lru_cache(maxsize=1000000) + @functools.lru_cache(maxsize=1000000) def memoized(*args, **kwargs): return func(*args, **kwargs) return memoized + def eprint(*args, **kwargs): print(*args, file=sys.stderr, **kwargs) + # from pudb.remote import set_trace # set_trace(term_size=(180, 50)) -import sys -import os - -from argparse import ArgumentParser -import subprocess, re -from os.path import dirname, abspath -from os.path import commonprefix - -script_path = dirname(abspath(__file__)) +script_path = os.path.dirname(os.path.abspath(__file__)) project_path = os.path.abspath(os.path.join(script_path, '..')) pycparser_path = os.path.join(project_path, 'lib', 'pycparser') sys.path.insert(0, os.path.abspath(pycparser_path)) -from pycparser import c_parser, c_ast, c_generator -import pycparser +from pycparser import c_parser, c_ast, c_generator # NOQA +import pycparser # NOQA fake_libc_path = os.path.join(script_path, 'fake_libc') @@ -93,14 +93,10 @@ def Node__repr__(self): return result - - - # # Argument parsing # - -argParser = ArgumentParser() +argParser = argparse.ArgumentParser() argParser.add_argument('-I', '--include', dest='include', help='Preprocesor include path', metavar='', action='append', default=[]) argParser.add_argument('-D', '--define', dest='define', help='Define preprocessor macro', metavar='', action='append', default=[]) argParser.add_argument('--module_name', dest='module_name', help='Module name', metavar='', action='store') @@ -119,7 +115,7 @@ def Node__repr__(self): DEBUG = args.debug lvgl_path = os.path.dirname(input_header) -private_header = os.path.join(lvgl_path, 'lv_private.h') +private_header = os.path.join(lvgl_path, 'src', 'lvgl_private.h') lv_config_path = os.path.abspath(os.path.join(lvgl_path, '..', 'lv_conf.h')) gen_json_path = os.path.join(lvgl_path, 'scripts/gen_json') @@ -128,17 +124,17 @@ def Node__repr__(self): original_nodes = {} -import inspect for key, value in c_ast.__dict__.items(): if inspect.isclass(value): original_nodes[key] = value -import gen_json +import fixed_gen_json # NOQA + +json_ast = fixed_gen_json.run(lv_config_path, os.path.join(project_path, 'build', 'lvgl_header.h'), False) -json_ast = gen_json.run(None, lv_config_path, False, os.path.join(project_path, 'build', 'lvgl_header.h'), False) lvgl_json = json_ast.to_dict() @@ -332,10 +328,10 @@ def get_path(name: str, p: str) -> str: raise RuntimeError('Unknown Failure') + # # AST parsing helper functions # - @memoize def remove_declname(ast): if hasattr(ast, 'declname'): @@ -347,6 +343,7 @@ def remove_declname(ast): child = ast.children()[i] remove_declname(child) + @memoize def add_default_declname(ast, name): if hasattr(ast, 'declname'): @@ -359,6 +356,7 @@ def add_default_declname(ast, name): child = ast.children()[i] add_default_declname(child, name) + @memoize def convert_array_to_ptr(ast): if hasattr(ast, 'type') and isinstance(ast.type, c_ast.ArrayDecl): @@ -369,6 +367,7 @@ def convert_array_to_ptr(ast): child = ast.children()[i] convert_array_to_ptr(child) + @memoize def remove_quals(ast): if hasattr(ast,'quals'): @@ -382,6 +381,7 @@ def remove_quals(ast): if not isinstance(child, c_ast.FuncDecl): # Don't remove quals which change function prototype remove_quals(child) + @memoize def remove_explicit_struct(ast): if isinstance(ast, c_ast.TypeDecl) and isinstance(ast.type, c_ast.Struct): @@ -398,6 +398,7 @@ def remove_explicit_struct(ast): child = ast.children()[i] remove_explicit_struct(child) + @memoize def get_type(arg, **kwargs): if isinstance(arg, str): @@ -408,6 +409,7 @@ def get_type(arg, **kwargs): if remove_quals_arg: remove_quals(arg_ast) return gen.visit(arg_ast) + @memoize def get_name(type): if isinstance(type, c_ast.Decl): @@ -429,6 +431,7 @@ def get_name(type): else: return gen.visit(type) + @memoize def remove_arg_names(ast): if isinstance(ast, c_ast.TypeDecl): @@ -439,6 +442,7 @@ def remove_arg_names(ast): elif isinstance(ast, c_ast.ParamList): for param in ast.params: remove_arg_names(param) + # Create a function prototype AST from a function AST @memoize def function_prototype(func): @@ -476,6 +480,7 @@ def function_prototype(func): lv_func_returns_array = re.compile('.*_array$') lv_enum_name_pattern = re.compile('^(ENUM_){{0,1}}({prefix}_){{0,1}}(.*)'.format(prefix=module_prefix.upper())) + # Prevent identifier names which are Python reserved words (add underscore in such case) def sanitize(id, kwlist = ['False', 'None', 'True', 'and', 'as', 'assert', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', @@ -490,35 +495,44 @@ def sanitize(id, kwlist = result = result.replace('*','_ptr') return result + @memoize def simplify_identifier(id): match_result = lv_func_pattern.match(id) return match_result.group(1) if match_result else id + def obj_name_from_ext_name(ext_name): return lv_ext_pattern.match(ext_name).group(1) + def obj_name_from_func_name(func_name): return lv_obj_pattern.match(func_name).group(1) + def ctor_name_from_obj_name(obj_name): return '{prefix}_{obj}_create'.format(prefix=module_prefix, obj=obj_name) + def is_method_of(func_name, obj_name): return func_name.lower().startswith('{prefix}_{obj}_'.format(prefix=module_prefix, obj=obj_name).lower()) + def method_name_from_func_name(func_name): res = lv_method_pattern.match(func_name).group(1) return res if res != "del" else "delete" # del is a reserved name, don't use it + def get_enum_name(enum): match_result = lv_enum_name_pattern.match(enum) return match_result.group(3) if match_result else enum + def str_enum_to_str(str_enum): res = lv_str_enum_pattern.match(str_enum).group(1) return ('%s_' % module_prefix.upper()) + res + def is_obj_ctor(func): # ctor name must match pattern if not create_obj_pattern.match(func.name): return False @@ -685,7 +699,6 @@ def is_struct(type): forward_struct_decls[item.type.type.name].append(item) - # ast_file = open(os.path.join(os.path.dirname(__file__), 'ast.txt'), 'w') # ast_file.write(str(ast)) # ast_file.close() @@ -801,26 +814,31 @@ def my_excepthook(exc_type, exc_value, tb): funcs.remove(obj_ctor) obj_names = [create_obj_pattern.match(ctor.name).group(1) for ctor in obj_ctors] + def has_ctor(obj_name): return ctor_name_from_obj_name(obj_name) in [ctor.name for ctor in obj_ctors] + def get_ctor(obj_name): global obj_ctors return next(ctor for ctor in obj_ctors if ctor.name == ctor_name_from_obj_name(obj_name)) + def get_methods(obj_name): global funcs return [func for func in funcs \ if is_method_of(func.name,obj_name) and \ (not func.name == ctor_name_from_obj_name(obj_name))] + @memoize def noncommon_part(member_name, stem_name): - common_part = commonprefix([member_name, stem_name]) + common_part = os.path.commonprefix([member_name, stem_name]) n = len(common_part) - 1 while n > 0 and member_name[n] != '_': n-=1 return member_name[n+1:] + @memoize def get_first_arg(func): if not func.type.args: @@ -831,6 +849,7 @@ def get_first_arg(func): return None return func.type.args.params[0].type + @memoize def get_first_arg_type(func): first_arg = get_first_arg(func) @@ -840,9 +859,11 @@ def get_first_arg_type(func): return None return get_type(first_arg.type, remove_quals = True) + def get_base_struct_name(struct_name): return struct_name[:-2] if struct_name.endswith('_t') else struct_name + # "struct function" starts with struct name (without _t), and their first argument is a pointer to the struct # Need also to take into account struct functions of aliases of current struct. @memoize @@ -872,6 +893,7 @@ def get_struct_functions(struct_name): return res + @memoize def is_struct_function(func): return func in get_struct_functions(get_first_arg_type(func)) @@ -879,6 +901,7 @@ def is_struct_function(func): # is_static_member returns true if function does not receive the obj as the first argument # and the object is not a struct function + @memoize def is_static_member(func, obj_type=base_obj_type): first_arg = get_first_arg(func) @@ -891,6 +914,7 @@ def is_static_member(func, obj_type=base_obj_type): first_arg_type = get_first_arg_type(func) return (first_arg_type == None) or (first_arg_type != obj_type) + # All object should inherit directly from base_obj, and not according to lv_ext, as disccussed on https://github.com/littlevgl/lv_binding_micropython/issues/19 parent_obj_names = {child_name: base_obj_name for child_name in obj_names if child_name != base_obj_name} parent_obj_names[base_obj_name] = None @@ -910,19 +934,21 @@ def is_static_member(func, obj_type=base_obj_type): enum_defs = [x for x in ast.ext if hasattr(x,'type') and isinstance(x.type, c_ast.Enum)] enum_defs += [x.type for x in ast.ext if hasattr(x, 'type') and hasattr(x.type, 'type') and isinstance(x.type, c_ast.TypeDecl) and isinstance(x.type.type, c_ast.Enum)] -# Enum member access functions. +# Enum member access functions. def get_enum_members(obj_name): global enums if not obj_name in enums: return [] return [enum_member_name for enum_member_name, value in enums[obj_name].items()] + def get_enum_member_name(enum_member): if enum_member[0].isdigit(): enum_member = '_' + enum_member # needs to be a valid attribute name return enum_member + def get_enum_value(obj_name, enum_member): return enums[obj_name][enum_member] @@ -943,13 +969,14 @@ def get_enum_value(obj_name, enum_member): int_constants = [] + # # Type convertors # - class MissingConversionException(ValueError): pass + mp_to_lv = { 'mp_obj_t' : '(mp_obj_t)', 'va_list' : None, @@ -1168,10 +1195,10 @@ class MissingConversionException(ValueError): lv_to_mp_byref = {} lv_to_mp_funcptr = {} + # Add native array supported types # These types would be converted automatically to/from array type. # Supported array (pointer) types are signed/unsigned int: 8bit, 16bit, 32bit and 64bit. - def register_int_ptr_type(convertor, *types): for ptr_type in types: for qualified_ptr_type in [ptr_type, 'const '+ptr_type]: @@ -1179,6 +1206,7 @@ def register_int_ptr_type(convertor, *types): lv_to_mp[qualified_ptr_type] = 'mp_array_from_%s' % (convertor) lv_mp_type[qualified_ptr_type] = 'void*' + register_int_ptr_type('u8ptr', 'unsigned char *', 'uint8_t *') @@ -1862,12 +1890,6 @@ def register_int_ptr_type(convertor, *types): // Blob is a wrapper for void* -static void mp_blob_print(const mp_print_t *print, - mp_obj_t self_in, - mp_print_kind_t kind) -{ - mp_printf(print, "Blob"); -} static mp_int_t mp_blob_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) { (void)flags; @@ -1912,7 +1934,6 @@ def register_int_ptr_type(convertor, *types): MP_QSTR_Blob, MP_TYPE_FLAG_NONE, binary_op, lv_struct_binary_op, - print, mp_blob_print, locals_dict, &mp_blob_locals_dict, buffer, mp_blob_get_buffer ); @@ -2128,6 +2149,7 @@ def register_int_ptr_type(convertor, *types): static MP_DEFINE_CONST_DICT(mp_base_struct_locals_dict, mp_base_struct_locals_dict_table); + static MP_DEFINE_CONST_OBJ_TYPE( mp_lv_base_struct_type, MP_QSTR_Struct, @@ -2217,7 +2239,6 @@ def register_int_ptr_type(convertor, *types): # # Add regular enums with integer values # - enums = collections.OrderedDict() for enum_def in enum_defs: # Skip stdatomic.h memory_order, no bindings needed. @@ -2228,7 +2249,7 @@ def register_int_ptr_type(convertor, *types): while hasattr(enum_def.type, 'name') and not enum_def.type.values: enum_def = next(e for e in enum_defs if hasattr(e.type, 'name') and e.type.name == enum_def.type.name and e.type.values) member_names = [member.name for member in enum_def.type.values.enumerators if not member.name.startswith('_')] - enum_name = commonprefix(member_names) + enum_name = os.path.commonprefix(member_names) enum_name = "_".join(enum_name.split("_")[:-1]) # remove suffix enum = collections.OrderedDict() for member in enum_def.type.values.enumerators: @@ -2249,14 +2270,15 @@ def register_int_ptr_type(convertor, *types): else: enums[enum_name] = enum + for enum in [enum for enum in enums if len(enums[enum]) == 1 and enum.startswith('ENUM')]: int_constants.append('%s_%s' % (enum, next(iter(enums[enum])))) enum_name = '%s_%s' % (enum, list(enums[enum].keys())[0]) constant_metadata[enum_name.replace('ENUM_', '').replace('LV_', '')] = {'py_type': 'int', 'c_type': enum_name.replace('ENUM_', '')} del enums[enum] -# Add special string enums +# Add special string enums print (''' /* * LVGL string constants @@ -2267,7 +2289,7 @@ def register_int_ptr_type(convertor, *types): if not enum_def.type.values: continue member_names = [str_enum_to_str(member.name) for member in enum_def.type.values.enumerators if lv_str_enum_pattern.match(member.name)] - enum_name = commonprefix(member_names) + enum_name = os.path.commonprefix(member_names) enum_name = "_".join(enum_name.split("_")[:-1]) # remove suffix enum = collections.OrderedDict() if enum_name: @@ -2286,7 +2308,6 @@ def register_int_ptr_type(convertor, *types): # eprint('--> enums: \n%s' % enums) - # eprint(',\n'.join(sorted('%s : %s' % (name, get_type(blobs[name])) for name in blobs))) # @@ -2314,6 +2335,7 @@ def decl_to_callback(decl): # print('/* callback: ADDED CALLBACK: %s\n%s */' % (func_typedef_name, func_typedefs[func_typedef_name])) else: return None + def get_user_data_accessors(containing_struct, containing_struct_name = None): if not containing_struct_name and containing_struct and containing_struct.name: containing_struct_name = containing_struct.name @@ -2332,6 +2354,7 @@ def get_user_data_accessors(containing_struct, containing_struct_name = None): else: return None, None + def get_user_data(func, func_name = None, containing_struct = None, containing_struct_name = None): args = func.args.params if not func_name: func_name = get_arg_name(func.type) @@ -2371,15 +2394,16 @@ def get_user_data(func, func_name = None, containing_struct = None, containing_s user_data_found = any(user_data == field["name"] for field in containing_struct_j["fields"]) return (user_data if user_data_found else None), *get_user_data_accessors(containing_struct, containing_struct_name) + # # Generate structs when needed # - generated_structs = collections.OrderedDict() generated_struct_functions = collections.OrderedDict() struct_aliases = collections.OrderedDict() callbacks_used_on_structs = [] + def flatten_struct(struct_decls): result = [] if not struct_decls: return result @@ -2390,6 +2414,7 @@ def flatten_struct(struct_decls): result.append(decl) return result + def try_generate_struct(struct_name, struct): global lv_to_mp global mp_to_lv @@ -2503,11 +2528,11 @@ def try_generate_struct(struct_name, struct): * Struct {struct_name} */ -static inline const mp_obj_type_t *get_mp_{sanitized_struct_name}_type(); +static const mp_obj_type_t mp_{sanitized_struct_name}_type; static inline void* mp_write_ptr_{sanitized_struct_name}(mp_obj_t self_in) {{ - mp_lv_struct_t *self = MP_OBJ_TO_PTR(cast(self_in, get_mp_{sanitized_struct_name}_type())); + mp_lv_struct_t *self = MP_OBJ_TO_PTR(cast(self_in, &mp_{sanitized_struct_name}_type)); return ({struct_tag}{struct_name}*)self->data; }} @@ -2515,7 +2540,7 @@ def try_generate_struct(struct_name, struct): static inline mp_obj_t mp_read_ptr_{sanitized_struct_name}(void *field) {{ - return lv_to_mp_struct(get_mp_{sanitized_struct_name}_type(), field); + return lv_to_mp_struct(&mp_{sanitized_struct_name}_type, field); }} #define mp_read_{sanitized_struct_name}(field) mp_read_ptr_{sanitized_struct_name}(copy_buffer(&field, sizeof({struct_tag}{struct_name}))) @@ -2548,20 +2573,12 @@ def try_generate_struct(struct_name, struct): }} }} -static void mp_{sanitized_struct_name}_print(const mp_print_t *print, - mp_obj_t self_in, - mp_print_kind_t kind) -{{ - mp_printf(print, "struct {struct_name}"); -}} - static const mp_obj_dict_t mp_{sanitized_struct_name}_locals_dict; static MP_DEFINE_CONST_OBJ_TYPE( mp_{sanitized_struct_name}_type, MP_QSTR_{sanitized_struct_name}, MP_TYPE_FLAG_NONE, - print, mp_{sanitized_struct_name}_print, make_new, make_new_lv_struct, binary_op, lv_struct_binary_op, subscr, lv_struct_subscr, @@ -2570,12 +2587,7 @@ def try_generate_struct(struct_name, struct): buffer, mp_blob_get_buffer, parent, &mp_lv_base_struct_type ); - -static inline const mp_obj_type_t *get_mp_{sanitized_struct_name}_type() -{{ - return &mp_{sanitized_struct_name}_type; -}} - '''.format( +'''.format( sanitized_struct_name = sanitized_struct_name, struct_name = struct_name, struct_tag = 'struct ' if struct_name in structs_without_typedef.keys() else '', @@ -2601,8 +2613,6 @@ def try_generate_struct(struct_name, struct): # # Generate Array Types when needed # - - def try_generate_array_type(type_ast): arr_name = get_name(type_ast) if arr_name in mp_to_lv: @@ -2697,10 +2707,10 @@ def try_generate_array_type(type_ast): json_c_to_py_types[arr_to_c_convertor_name] = new_array_name return arr_to_c_convertor_name + # # Generate types from typedefs when needed # - def get_arg_name(arg): if isinstance(arg, c_ast.PtrDecl) or isinstance(arg, c_ast.FuncDecl): return get_arg_name(arg.type) @@ -2708,8 +2718,10 @@ def get_arg_name(arg): if hasattr(arg, 'name'): return name return 'unnamed_arg' + # print("// Typedefs: " + ", ".join(get_arg_name(t) for t in typedefs)) + def try_generate_type(type_ast): # eprint(' --> try_generate_type %s : %s' % (get_name(type_ast), gen.visit(type_ast))) # print('/* --> try_generate_type %s: %s */' % (get_name(type_ast), type_ast)) @@ -2810,10 +2822,10 @@ def try_generate_type(type_ast): return mp_to_lv[type] return None + # # Helper structs # - def create_helper_struct(struct_str): print(struct_str) struct_str_ast = parser.parse(struct_str).ext[0].type @@ -2825,6 +2837,7 @@ def create_helper_struct(struct_str): except MissingConversionException as exp: print ('/* Helper structs NOT generated:\n %s\n*/' % (repr(exp))) + print(''' /* * Helper Structs @@ -2840,12 +2853,13 @@ def create_helper_struct(struct_str): } C_Pointer; ''') + # # Emit C callback functions # - generated_callbacks = collections.OrderedDict() + def build_callback_func_arg(arg, index, func, func_name = None): arg_type = get_type(arg.type, remove_quals = True) cast = '(void*)' if isinstance(arg.type, c_ast.PtrDecl) else '' # needed when field is const. casting to void overrides it @@ -2962,12 +2976,13 @@ def gen_callback_func(func, func_name = None, user_data_argument = False): return_value='' if return_type == 'void' else ' %s(callback_result)' % mp_to_lv[return_type])) generated_callbacks[func_name] = True + # # Emit Mpy function definitions # - generated_funcs = collections.OrderedDict() + def build_mp_func_arg(arg, index, func, obj_name): if isinstance(arg, c_ast.EllipsisParam): raise MissingConversionException("Cannot convert ellipsis param") @@ -3066,6 +3081,7 @@ def build_mp_func_arg(arg, index, func, obj_name): convertor = mp_to_lv[arg_type], i = index), arg_metadata + def emit_func_obj(func_obj_name, func_name, param_count, func_ptr, is_static): print(""" static {builtin_macro}(mp_{func_obj_name}_mpobj, {param_count}, mp_{func_name}, {func_ptr}); @@ -3076,6 +3092,7 @@ def emit_func_obj(func_obj_name, func_name, param_count, func_ptr, is_static): param_count = param_count, builtin_macro='MP_DEFINE_CONST_LV_FUN_OBJ_STATIC_VAR' if is_static else 'MP_DEFINE_CONST_LV_FUN_OBJ_VAR')) + def gen_mp_func(func, obj_name): # print('/* gen_mp_func: %s : %s */' % (obj_name, func)) if func.name in generated_funcs: @@ -3187,11 +3204,7 @@ def gen_mp_func(func, obj_name): func=func.name, func_ptr=prototype_str, print_func=gen.visit(func), - build_args="\n ".join([build_mp_func_arg(arg, i, func, obj_name) for i,arg in enumerated_args - if isinstance(arg, c_ast.EllipsisParam) or - (not isinstance(arg.type, c_ast.TypeDecl)) or - (not isinstance(arg.type.type, c_ast.IdentifierType)) or - 'void' not in arg.type.type.names]), # Handle the case of 'void' param which should be ignored + build_args="\n ".join(build_args), # Handle the case of 'void' param which should be ignored send_args=", ".join([(arg.name if (hasattr(arg, 'name') and arg.name) else ("arg%d" % i)) for i,arg in enumerate(args)]), build_result=build_result, build_return_value=build_return_value)) @@ -3204,7 +3217,6 @@ def gen_mp_func(func, obj_name): # is_struct_function(func), is_static_member(func, base_obj_type), get_first_arg_type(func), base_obj_type)) - def gen_func_error(method, exp): global funcs print(""" @@ -3223,10 +3235,8 @@ def gen_func_error(method, exp): # # Emit Mpy objects definitions # - enum_referenced = collections.OrderedDict() -from copy import deepcopy def gen_obj_methods(obj_name): global enums @@ -3258,9 +3268,9 @@ def gen_obj_methods(obj_name): obj_metadata[obj_name]['members'].update({method_name_from_func_name(enum_name): {'c_type': obj_metadata[obj_name]['c_type'] + enum_name if enum_name.startswith('_') else obj_metadata[obj_name]['c_type'] + '_' + enum_name, 'py_type': 'int'} for enum_name in obj_enums}) for enum_name in obj_enums: - obj_metadata[obj_name]['classes'][method_name_from_func_name(enum_name)] = deepcopy(obj_metadata[obj_name]['members'][method_name_from_func_name(enum_name)]) + obj_metadata[obj_name]['classes'][method_name_from_func_name(enum_name)] = copy.deepcopy(obj_metadata[obj_name]['members'][method_name_from_func_name(enum_name)]) del obj_metadata[obj_name]['members'][method_name_from_func_name(enum_name)] - obj_metadata[obj_name]['classes'][method_name_from_func_name(enum_name)].update(deepcopy(obj_metadata[enum_name])) + obj_metadata[obj_name]['classes'][method_name_from_func_name(enum_name)].update(copy.deepcopy(obj_metadata[enum_name])) enum_referenced[enum_name] = True obj_metadata[obj_name]['classes'][method_name_from_func_name(enum_name)]['c_type'] = 'enum' @@ -3323,20 +3333,12 @@ def gen_obj(obj_name): static MP_DEFINE_CONST_DICT({obj}_locals_dict, {obj}_locals_dict_table); -static void {obj}_print(const mp_print_t *print, - mp_obj_t self_in, - mp_print_kind_t kind) -{{ - mp_printf(print, "{module_name} {obj}"); -}} - {ctor} static MP_DEFINE_CONST_OBJ_TYPE( mp_lv_{obj}_type_base, MP_QSTR_{obj}, MP_TYPE_FLAG_NONE, - print, {obj}_print, {make_new} {binary_op} attr, call_parent_methods, @@ -3372,18 +3374,16 @@ def gen_obj(obj_name): del obj_metadata[obj_name]['members'][meta_func_name] - # # Generate Enum objects # - for enum_name in list(enums.keys()): gen_obj(enum_name) + # # Generate all other objects. Generate parent objects first # - generated_obj_names = collections.OrderedDict() for obj_name in obj_names: # eprint("--> %s [%s]" % (obj_name, ", ".join([name for name in generated_obj_names]))) @@ -3399,13 +3399,13 @@ def gen_obj(obj_name): gen_obj(obj_name) generated_obj_names[obj_name] = True + # # Generate structs which contain function members # First argument of a function could be it's parent struct # Need to make sure these structs are generated *before* struct-functions are # Otherwise we will not know of all the structs when generating struct-functions # - def try_generate_structs_from_first_argument(): for func in funcs: if func.name in generated_funcs: continue @@ -3422,12 +3422,12 @@ def try_generate_structs_from_first_argument(): */ '''.format(struct=arg_type, err=e)) -# -# Generate globals -# # eprint("/* Generating globals */") +# +# Generate globals +# def gen_global(global_name, global_type_ast): global_type = get_type(global_type_ast, remove_quals=True) generated_global = try_generate_type(global_type_ast) @@ -3481,13 +3481,14 @@ def gen_global(global_name, global_type_ast): except MissingConversionException as exp: gen_func_error(global_name, exp) + # # Generate struct-functions # - # eprint("/* Generating struct-functions */") try_generate_structs_from_first_argument() + def generate_struct_functions(struct_list): # print('/* List of structs: %s */' % repr(struct_list)) for struct_name in struct_list: @@ -3506,7 +3507,7 @@ def generate_struct_functions(struct_list): if get_py_type(struct_name).replace('"', '') not in struct_metadata: struct_metadata[get_py_type(struct_name).replace('"', '')] = {'class_attributes': collections.OrderedDict(), 'attributes': collections.OrderedDict(), 'py_type': 'class', 'c_type': struct_name, 'methods': collections.OrderedDict()} - struct_metadata[get_py_type(struct_name).replace('"', '')]['methods'][sanitize(noncommon_part(struct_func.name, struct_name))] = deepcopy(func_metadata[struct_func.name]) + struct_metadata[get_py_type(struct_name).replace('"', '')]['methods'][sanitize(noncommon_part(struct_func.name, struct_name))] = copy.deepcopy(func_metadata[struct_func.name]) except MissingConversionException as exp: gen_func_error(struct_func, exp) struct_funcs.remove(struct_func) @@ -3566,6 +3567,7 @@ def generate_struct_functions(struct_list): gen_func_error(module_func, exp) module_funcs.remove(module_func) + functions_not_generated = [func.name for func in funcs if func.name not in generated_funcs] if len(functions_not_generated) > 0: print(""" @@ -3591,12 +3593,13 @@ def generate_struct_functions(struct_list): # lv_to_mp[func_name] = lv_to_mp['void *'] # mp_to_lv[func_name] = mp_to_lv['void *'] + # eprint("/* Generating callback functions */") for (func_name, func, struct_name) in callbacks_used_on_structs: try: # print('/* --> gen_callback_func %s */' % func_name) gen_callback_func(func, func_name = '%s_%s' % (struct_name, func_name)) - # struct_metadata[struct_name]['methods'][func_name] = deepcopy(callback_metadata[func.name]) + # struct_metadata[struct_name]['methods'][func_name] = copy.deepcopy(callback_metadata[func.name]) except MissingConversionException as exp: gen_func_error(func, exp) # func_name = get_arg_name(func.type) @@ -3663,8 +3666,8 @@ def generate_struct_functions(struct_list): module_name = module_name, )) -# Add an array of all object types +# Add an array of all object types if len(obj_names) > 0: print(''' static const mp_lv_obj_type_t *mp_lv_obj_types[] = {{ @@ -3698,7 +3701,7 @@ def _iter_metadata(d, indent=0): for obj_name in obj_names: parent = obj_metadata[obj_name]['parent_class'] - obj_metadata[obj_name]['methods'].update(deepcopy(obj_metadata[obj_name]['members'])) + obj_metadata[obj_name]['methods'].update(copy.deepcopy(obj_metadata[obj_name]['members'])) del obj_metadata[obj_name]['members'] if parent not in obj_metadata: