1*61046927SAndroid Build Coastguard Worker#!/usr/bin/env python3 2*61046927SAndroid Build Coastguard Worker# 3*61046927SAndroid Build Coastguard Worker# Copyright © 2024 Igalia S.L. 4*61046927SAndroid Build Coastguard Worker# SPDX-License-Identifier: MIT 5*61046927SAndroid Build Coastguard Worker 6*61046927SAndroid Build Coastguard Workerimport argparse 7*61046927SAndroid Build Coastguard Workerimport ctypes 8*61046927SAndroid Build Coastguard Workerimport logging 9*61046927SAndroid Build Coastguard Workerimport re 10*61046927SAndroid Build Coastguard Workerfrom datetime import datetime 11*61046927SAndroid Build Coastguard Workerfrom mako.template import Template 12*61046927SAndroid Build Coastguard Workerfrom pycparser import parse_file, c_ast 13*61046927SAndroid Build Coastguard Worker 14*61046927SAndroid Build Coastguard Workerlogger = logging.getLogger('hwdb') 15*61046927SAndroid Build Coastguard Worker 16*61046927SAndroid Build Coastguard Workertemplate = """/* 17*61046927SAndroid Build Coastguard Worker * Copyright © 2024 Igalia S.L. 18*61046927SAndroid Build Coastguard Worker * SPDX-License-Identifier: MIT 19*61046927SAndroid Build Coastguard Worker */ 20*61046927SAndroid Build Coastguard Worker 21*61046927SAndroid Build Coastguard Worker#pragma once 22*61046927SAndroid Build Coastguard Worker 23*61046927SAndroid Build Coastguard Worker#include <stdint.h> 24*61046927SAndroid Build Coastguard Worker 25*61046927SAndroid Build Coastguard Workertypedef struct 26*61046927SAndroid Build Coastguard Worker{ 27*61046927SAndroid Build Coastguard Worker% for type, name in struct: 28*61046927SAndroid Build Coastguard Worker ${type} ${name}; 29*61046927SAndroid Build Coastguard Worker% endfor 30*61046927SAndroid Build Coastguard Worker} gcsFEATURE_DATABASE; 31*61046927SAndroid Build Coastguard Worker 32*61046927SAndroid Build Coastguard Workerstatic gcsFEATURE_DATABASE gChipInfo[] = { 33*61046927SAndroid Build Coastguard Worker% for entry in entries: 34*61046927SAndroid Build Coastguard Worker { 35*61046927SAndroid Build Coastguard Worker% for name, value in entry: 36*61046927SAndroid Build Coastguard Worker ${value}, /* ${name} */ 37*61046927SAndroid Build Coastguard Worker% endfor 38*61046927SAndroid Build Coastguard Worker }, 39*61046927SAndroid Build Coastguard Worker% endfor 40*61046927SAndroid Build Coastguard Worker}; 41*61046927SAndroid Build Coastguard Worker 42*61046927SAndroid Build Coastguard Workerstatic inline gcsFEATURE_DATABASE * 43*61046927SAndroid Build Coastguard WorkergcQueryFeatureDB( 44*61046927SAndroid Build Coastguard Worker uint32_t ChipID, 45*61046927SAndroid Build Coastguard Worker uint32_t ChipVersion, 46*61046927SAndroid Build Coastguard Worker uint32_t ProductID, 47*61046927SAndroid Build Coastguard Worker uint32_t EcoID, 48*61046927SAndroid Build Coastguard Worker uint32_t CustomerID 49*61046927SAndroid Build Coastguard Worker ) 50*61046927SAndroid Build Coastguard Worker{ 51*61046927SAndroid Build Coastguard Worker int entryNum = sizeof(gChipInfo) / sizeof(gChipInfo[0]); 52*61046927SAndroid Build Coastguard Worker 53*61046927SAndroid Build Coastguard Worker /* check formal release entries first */ 54*61046927SAndroid Build Coastguard Worker for (int i = 0; i < entryNum; ++i) 55*61046927SAndroid Build Coastguard Worker { 56*61046927SAndroid Build Coastguard Worker if ((gChipInfo[i].chipID == ChipID) 57*61046927SAndroid Build Coastguard Worker && (gChipInfo[i].chipVersion == ChipVersion) 58*61046927SAndroid Build Coastguard Worker && (gChipInfo[i].productID == ProductID) 59*61046927SAndroid Build Coastguard Worker && (gChipInfo[i].ecoID == EcoID) 60*61046927SAndroid Build Coastguard Worker && (gChipInfo[i].customerID == CustomerID) 61*61046927SAndroid Build Coastguard Worker && (gChipInfo[i].formalRelease) 62*61046927SAndroid Build Coastguard Worker ) 63*61046927SAndroid Build Coastguard Worker { 64*61046927SAndroid Build Coastguard Worker return &gChipInfo[i]; 65*61046927SAndroid Build Coastguard Worker } 66*61046927SAndroid Build Coastguard Worker } 67*61046927SAndroid Build Coastguard Worker 68*61046927SAndroid Build Coastguard Worker /* check informal release entries if we dont find in formal entries */ 69*61046927SAndroid Build Coastguard Worker for (int i = 0; i < entryNum; ++i) 70*61046927SAndroid Build Coastguard Worker { 71*61046927SAndroid Build Coastguard Worker if ((gChipInfo[i].chipID == ChipID) 72*61046927SAndroid Build Coastguard Worker && ((gChipInfo[i].chipVersion & 0xFFF0) == (ChipVersion & 0xFFF0)) 73*61046927SAndroid Build Coastguard Worker && (gChipInfo[i].productID == ProductID) 74*61046927SAndroid Build Coastguard Worker && (gChipInfo[i].ecoID == EcoID) 75*61046927SAndroid Build Coastguard Worker && (gChipInfo[i].customerID == CustomerID) 76*61046927SAndroid Build Coastguard Worker && (!gChipInfo[i].formalRelease) 77*61046927SAndroid Build Coastguard Worker ) 78*61046927SAndroid Build Coastguard Worker { 79*61046927SAndroid Build Coastguard Worker return &gChipInfo[i]; 80*61046927SAndroid Build Coastguard Worker } 81*61046927SAndroid Build Coastguard Worker } 82*61046927SAndroid Build Coastguard Worker 83*61046927SAndroid Build Coastguard Worker return 0; 84*61046927SAndroid Build Coastguard Worker} 85*61046927SAndroid Build Coastguard Worker""" 86*61046927SAndroid Build Coastguard Worker 87*61046927SAndroid Build Coastguard Worker 88*61046927SAndroid Build Coastguard Workerclass HeaderFile(c_ast.NodeVisitor): 89*61046927SAndroid Build Coastguard Worker """Class representing a complete header file""" 90*61046927SAndroid Build Coastguard Worker 91*61046927SAndroid Build Coastguard Worker # Regular expression to match the date and time in the comment 92*61046927SAndroid Build Coastguard Worker _DATE_RE = re.compile(r'/\*Auto created on (\d{4}-\d{2}-\d{2} \d{2}:\d{2})\*/') 93*61046927SAndroid Build Coastguard Worker 94*61046927SAndroid Build Coastguard Worker def __init__(self, filename): 95*61046927SAndroid Build Coastguard Worker self.filename = filename 96*61046927SAndroid Build Coastguard Worker self.structs = {} 97*61046927SAndroid Build Coastguard Worker self.data = [] 98*61046927SAndroid Build Coastguard Worker self.date_time = None 99*61046927SAndroid Build Coastguard Worker self.database_struct = None 100*61046927SAndroid Build Coastguard Worker 101*61046927SAndroid Build Coastguard Worker self._read_date() 102*61046927SAndroid Build Coastguard Worker self._parse() 103*61046927SAndroid Build Coastguard Worker 104*61046927SAndroid Build Coastguard Worker logger.debug('Parsed %s (autogenerated at %s, %u struct members, %u entries)', self.filename, self.date_time, len(self.database_struct._fields_), len(self.data)) 105*61046927SAndroid Build Coastguard Worker 106*61046927SAndroid Build Coastguard Worker def _read_date(self): 107*61046927SAndroid Build Coastguard Worker """Function parsing the creation date with re.""" 108*61046927SAndroid Build Coastguard Worker # Read the content of the file and search for pattern 109*61046927SAndroid Build Coastguard Worker with open(self.filename, 'r', encoding="utf-8") as file: 110*61046927SAndroid Build Coastguard Worker file_content = file.read() 111*61046927SAndroid Build Coastguard Worker 112*61046927SAndroid Build Coastguard Worker match = self._DATE_RE.search(file_content) 113*61046927SAndroid Build Coastguard Worker 114*61046927SAndroid Build Coastguard Worker if match: 115*61046927SAndroid Build Coastguard Worker self.date_time = datetime.strptime(match.group(1), '%Y-%m-%d %H:%M') 116*61046927SAndroid Build Coastguard Worker 117*61046927SAndroid Build Coastguard Worker def _parse(self): 118*61046927SAndroid Build Coastguard Worker ast = parse_file(self.filename, use_cpp=True, cpp_args=['-E', r'-I./utils/fake_libc_include', '-DgctUINT32=unsigned int', '-DgctINT=int']) 119*61046927SAndroid Build Coastguard Worker self.visit(ast) 120*61046927SAndroid Build Coastguard Worker 121*61046927SAndroid Build Coastguard Worker self.database_struct = self.structs['gcsFEATURE_DATABASE'] 122*61046927SAndroid Build Coastguard Worker 123*61046927SAndroid Build Coastguard Worker def visit_Typedef(self, node): 124*61046927SAndroid Build Coastguard Worker if isinstance(node.type, c_ast.TypeDecl) and isinstance(node.type.type, c_ast.Struct): 125*61046927SAndroid Build Coastguard Worker struct_node = node.type.type 126*61046927SAndroid Build Coastguard Worker struct_name = node.name # Typedef name as the struct name 127*61046927SAndroid Build Coastguard Worker fields = self._extract_fields_from_struct(struct_node) 128*61046927SAndroid Build Coastguard Worker if fields is not None: 129*61046927SAndroid Build Coastguard Worker # Create the ctypes.Structure subclass and add it to the structures dictionary 130*61046927SAndroid Build Coastguard Worker self.structs[struct_name] = type(struct_name, (ctypes.Structure,), {'_fields_': fields}) 131*61046927SAndroid Build Coastguard Worker 132*61046927SAndroid Build Coastguard Worker def _extract_fields_from_struct(self, struct_node): 133*61046927SAndroid Build Coastguard Worker """Function returning all fields of struct.""" 134*61046927SAndroid Build Coastguard Worker fields = [] 135*61046927SAndroid Build Coastguard Worker for decl in (struct_node.decls or []): 136*61046927SAndroid Build Coastguard Worker if isinstance(decl.type, c_ast.TypeDecl) or isinstance(decl.type, c_ast.PtrDecl): 137*61046927SAndroid Build Coastguard Worker field_name = decl.name 138*61046927SAndroid Build Coastguard Worker field_type = self._map_type_to_ctypes(decl.type.type, decl.bitsize) 139*61046927SAndroid Build Coastguard Worker if field_type: 140*61046927SAndroid Build Coastguard Worker fields.append((field_name, field_type)) 141*61046927SAndroid Build Coastguard Worker elif isinstance(decl.type, c_ast.ArrayDecl): 142*61046927SAndroid Build Coastguard Worker # Handle array type 143*61046927SAndroid Build Coastguard Worker field_name = decl.type.type.declname 144*61046927SAndroid Build Coastguard Worker element_type = self._map_type_to_ctypes(decl.type.type.type) 145*61046927SAndroid Build Coastguard Worker array_size = int(decl.type.dim.value) # Assuming dim is a Constant node with the size as its value 146*61046927SAndroid Build Coastguard Worker if element_type: 147*61046927SAndroid Build Coastguard Worker fields.append((field_name, element_type * array_size)) 148*61046927SAndroid Build Coastguard Worker 149*61046927SAndroid Build Coastguard Worker return fields if fields else None 150*61046927SAndroid Build Coastguard Worker 151*61046927SAndroid Build Coastguard Worker def _map_type_to_ctypes(self, type_node, bitsize=None): 152*61046927SAndroid Build Coastguard Worker """Function returning a ctype type based node type.""" 153*61046927SAndroid Build Coastguard Worker type_mappings = { 154*61046927SAndroid Build Coastguard Worker 'bool': ctypes.c_bool, 155*61046927SAndroid Build Coastguard Worker 'unsigned int': ctypes.c_uint, 156*61046927SAndroid Build Coastguard Worker 'int': ctypes.c_int, 157*61046927SAndroid Build Coastguard Worker } 158*61046927SAndroid Build Coastguard Worker 159*61046927SAndroid Build Coastguard Worker if isinstance(type_node, c_ast.IdentifierType): 160*61046927SAndroid Build Coastguard Worker c_type = ' '.join(type_node.names) 161*61046927SAndroid Build Coastguard Worker 162*61046927SAndroid Build Coastguard Worker if bitsize and bitsize.value == '1': 163*61046927SAndroid Build Coastguard Worker c_type = 'bool' 164*61046927SAndroid Build Coastguard Worker 165*61046927SAndroid Build Coastguard Worker return type_mappings.get(c_type) 166*61046927SAndroid Build Coastguard Worker elif isinstance(type_node, c_ast.TypeDecl): 167*61046927SAndroid Build Coastguard Worker return ctypes.c_char_p 168*61046927SAndroid Build Coastguard Worker 169*61046927SAndroid Build Coastguard Worker return None 170*61046927SAndroid Build Coastguard Worker 171*61046927SAndroid Build Coastguard Worker def visit_Decl(self, node): 172*61046927SAndroid Build Coastguard Worker # Check if the node is a declaration of an array of structs 173*61046927SAndroid Build Coastguard Worker if isinstance(node.type, c_ast.ArrayDecl) and isinstance(node.type.type, c_ast.TypeDecl): 174*61046927SAndroid Build Coastguard Worker struct_name = node.type.type.type.names[0] 175*61046927SAndroid Build Coastguard Worker if struct_name in self.structs: 176*61046927SAndroid Build Coastguard Worker elements = self._parse_array_initializer(node.init, self.structs[struct_name]) 177*61046927SAndroid Build Coastguard Worker self.data.extend(elements) 178*61046927SAndroid Build Coastguard Worker 179*61046927SAndroid Build Coastguard Worker def _parse_initializer(self, initializer, struct): 180*61046927SAndroid Build Coastguard Worker """Function returning one parsed struct initializer.""" 181*61046927SAndroid Build Coastguard Worker return [ 182*61046927SAndroid Build Coastguard Worker (param if not isinstance(param, str) else param.encode('utf-8')) 183*61046927SAndroid Build Coastguard Worker for index, expr in enumerate(initializer.exprs) 184*61046927SAndroid Build Coastguard Worker for param in [self._parse_expr(expr, getattr(struct._fields_[index][1], '_length_', None))] 185*61046927SAndroid Build Coastguard Worker ] 186*61046927SAndroid Build Coastguard Worker 187*61046927SAndroid Build Coastguard Worker def _parse_array_initializer(self, init, struct_class): 188*61046927SAndroid Build Coastguard Worker """Function returning a fully processed struct initializer list.""" 189*61046927SAndroid Build Coastguard Worker assert (isinstance(init, c_ast.InitList)) 190*61046927SAndroid Build Coastguard Worker return [struct_class(*self._parse_initializer(initializer, struct_class)) for initializer in init.exprs] 191*61046927SAndroid Build Coastguard Worker 192*61046927SAndroid Build Coastguard Worker def _parse_expr(self, expr, expected_size=None): 193*61046927SAndroid Build Coastguard Worker """Function returning parsed expression.""" 194*61046927SAndroid Build Coastguard Worker # Direct handling of constant types 195*61046927SAndroid Build Coastguard Worker if isinstance(expr, c_ast.Constant): 196*61046927SAndroid Build Coastguard Worker if expr.type == "int": 197*61046927SAndroid Build Coastguard Worker # Base 0 automatically handles decimal, hex, and octal 198*61046927SAndroid Build Coastguard Worker return int(expr.value, 0) 199*61046927SAndroid Build Coastguard Worker elif expr.type == "string": 200*61046927SAndroid Build Coastguard Worker return expr.value.strip('"') 201*61046927SAndroid Build Coastguard Worker 202*61046927SAndroid Build Coastguard Worker # Handling arrays with potential conversion to ctypes arrays 203*61046927SAndroid Build Coastguard Worker elif isinstance(expr, c_ast.InitList) and expected_size is not None: 204*61046927SAndroid Build Coastguard Worker element_list = [self._parse_expr(e) for e in expr.exprs] 205*61046927SAndroid Build Coastguard Worker 206*61046927SAndroid Build Coastguard Worker # Ensure the list matches expected size, filling with zeros if necessary 207*61046927SAndroid Build Coastguard Worker if len(element_list) < expected_size: 208*61046927SAndroid Build Coastguard Worker element_list.extend([0] * (expected_size - len(element_list))) 209*61046927SAndroid Build Coastguard Worker 210*61046927SAndroid Build Coastguard Worker # Convert to ctypes array, dynamically adjusting type based on context if needed 211*61046927SAndroid Build Coastguard Worker return (ctypes.c_uint * expected_size)(*element_list) 212*61046927SAndroid Build Coastguard Worker 213*61046927SAndroid Build Coastguard Worker # Fallback or default return for unhandled cases 214*61046927SAndroid Build Coastguard Worker return None 215*61046927SAndroid Build Coastguard Worker 216*61046927SAndroid Build Coastguard Worker 217*61046927SAndroid Build Coastguard Workerdef merge_structures(structures): 218*61046927SAndroid Build Coastguard Worker """Function creating a new type by merging provided types.""" 219*61046927SAndroid Build Coastguard Worker combined_fields = [] 220*61046927SAndroid Build Coastguard Worker for struct in structures: 221*61046927SAndroid Build Coastguard Worker for field_name, field_type in struct._fields_: 222*61046927SAndroid Build Coastguard Worker # Check if the field name already exists 223*61046927SAndroid Build Coastguard Worker if field_name not in [field[0] for field in combined_fields]: 224*61046927SAndroid Build Coastguard Worker combined_fields.append((field_name, field_type)) 225*61046927SAndroid Build Coastguard Worker 226*61046927SAndroid Build Coastguard Worker # Create a new structure dynamically 227*61046927SAndroid Build Coastguard Worker return type("MergedStructure", (ctypes.Structure,), {'_fields_': combined_fields}) 228*61046927SAndroid Build Coastguard Worker 229*61046927SAndroid Build Coastguard Worker 230*61046927SAndroid Build Coastguard Workerdef get_field_type(c, field_name): 231*61046927SAndroid Build Coastguard Worker """Function returning the type of a field type based on its name.""" 232*61046927SAndroid Build Coastguard Worker for field in c._fields_: 233*61046927SAndroid Build Coastguard Worker if field[0] == field_name: 234*61046927SAndroid Build Coastguard Worker return field[1] 235*61046927SAndroid Build Coastguard Worker 236*61046927SAndroid Build Coastguard Worker return None 237*61046927SAndroid Build Coastguard Worker 238*61046927SAndroid Build Coastguard Worker 239*61046927SAndroid Build Coastguard Workerdef create_merged_struct(c, entry): 240*61046927SAndroid Build Coastguard Worker """Function returning a fully populate instance of MergedStructure.""" 241*61046927SAndroid Build Coastguard Worker fields = [] 242*61046927SAndroid Build Coastguard Worker for field_name, field_type in c._fields_: 243*61046927SAndroid Build Coastguard Worker # We might need to 'upgrade' to an array - check field type too. 244*61046927SAndroid Build Coastguard Worker # e.g. VIP_SRAM_SIZE_ARRAY -> VIP_SRAM_SIZE_ARRAY[9] 245*61046927SAndroid Build Coastguard Worker 246*61046927SAndroid Build Coastguard Worker if hasattr(entry, field_name) and get_field_type(entry, field_name) is field_type: 247*61046927SAndroid Build Coastguard Worker fields.append(getattr(entry, field_name)) 248*61046927SAndroid Build Coastguard Worker else: 249*61046927SAndroid Build Coastguard Worker # Add a suitable default value 250*61046927SAndroid Build Coastguard Worker if field_type == ctypes.c_uint or field_type == ctypes.c_bool: 251*61046927SAndroid Build Coastguard Worker fields.append(0) 252*61046927SAndroid Build Coastguard Worker else: 253*61046927SAndroid Build Coastguard Worker # It must be an array 254*61046927SAndroid Build Coastguard Worker expected_size = getattr(field_type, '_length_') 255*61046927SAndroid Build Coastguard Worker fields.append((ctypes.c_uint * expected_size)(*[0] * expected_size)) 256*61046927SAndroid Build Coastguard Worker 257*61046927SAndroid Build Coastguard Worker return c(*fields) 258*61046927SAndroid Build Coastguard Worker 259*61046927SAndroid Build Coastguard Worker 260*61046927SAndroid Build Coastguard Workerdef enumerate_struct(obj): 261*61046927SAndroid Build Coastguard Worker for field_name, field_type in obj._fields_: 262*61046927SAndroid Build Coastguard Worker type = 'uint32_t' 263*61046927SAndroid Build Coastguard Worker 264*61046927SAndroid Build Coastguard Worker if field_type == ctypes.c_char_p: 265*61046927SAndroid Build Coastguard Worker type = 'const char *' 266*61046927SAndroid Build Coastguard Worker 267*61046927SAndroid Build Coastguard Worker if field_type == ctypes.c_bool: 268*61046927SAndroid Build Coastguard Worker field_name = field_name + ':1' 269*61046927SAndroid Build Coastguard Worker 270*61046927SAndroid Build Coastguard Worker if hasattr(field_type, '_length_'): 271*61046927SAndroid Build Coastguard Worker field_name = f'{field_name}[{field_type._length_}]' 272*61046927SAndroid Build Coastguard Worker 273*61046927SAndroid Build Coastguard Worker yield type, field_name 274*61046927SAndroid Build Coastguard Worker 275*61046927SAndroid Build Coastguard Worker 276*61046927SAndroid Build Coastguard Workerdef enumerate_values(obj): 277*61046927SAndroid Build Coastguard Worker for field_name, field_type in obj._fields_: 278*61046927SAndroid Build Coastguard Worker value = getattr(obj, field_name) 279*61046927SAndroid Build Coastguard Worker 280*61046927SAndroid Build Coastguard Worker if field_type in {ctypes.c_uint, ctypes.c_bool}: 281*61046927SAndroid Build Coastguard Worker value = hex(value) 282*61046927SAndroid Build Coastguard Worker elif field_type == ctypes.c_char_p: 283*61046927SAndroid Build Coastguard Worker value = '"{}"'.format(value.decode('utf-8')) 284*61046927SAndroid Build Coastguard Worker elif isinstance(value, ctypes.Array): 285*61046927SAndroid Build Coastguard Worker value = '{{{}}}'.format(', '.join(str(element) for element in value)) 286*61046927SAndroid Build Coastguard Worker 287*61046927SAndroid Build Coastguard Worker yield field_name, value 288*61046927SAndroid Build Coastguard Worker 289*61046927SAndroid Build Coastguard Worker 290*61046927SAndroid Build Coastguard Workerdef main(): 291*61046927SAndroid Build Coastguard Worker parser = argparse.ArgumentParser() 292*61046927SAndroid Build Coastguard Worker parser.add_argument('--output', required=True, type=str, action="store", 293*61046927SAndroid Build Coastguard Worker help='output C header file') 294*61046927SAndroid Build Coastguard Worker parser.add_argument('headers', metavar='header', type=str, nargs='+', 295*61046927SAndroid Build Coastguard Worker help='gc database header to process') 296*61046927SAndroid Build Coastguard Worker parser.add_argument('--verbose', "-v", action="store_true", 297*61046927SAndroid Build Coastguard Worker help='be verbose') 298*61046927SAndroid Build Coastguard Worker args = parser.parse_args() 299*61046927SAndroid Build Coastguard Worker 300*61046927SAndroid Build Coastguard Worker if args.verbose: 301*61046927SAndroid Build Coastguard Worker logging.basicConfig(level=logging.DEBUG) 302*61046927SAndroid Build Coastguard Worker 303*61046927SAndroid Build Coastguard Worker files = [HeaderFile(header) for header in args.headers] 304*61046927SAndroid Build Coastguard Worker structs = [file.database_struct for file in files] 305*61046927SAndroid Build Coastguard Worker merged = merge_structures(structs) 306*61046927SAndroid Build Coastguard Worker logger.debug('merged struct: %u members', len(merged._fields_)) 307*61046927SAndroid Build Coastguard Worker 308*61046927SAndroid Build Coastguard Worker entries = [] 309*61046927SAndroid Build Coastguard Worker for file in files: 310*61046927SAndroid Build Coastguard Worker logger.debug('Processing %s', file.filename) 311*61046927SAndroid Build Coastguard Worker for entry in file.data: 312*61046927SAndroid Build Coastguard Worker merged_entry = create_merged_struct(merged, entry) 313*61046927SAndroid Build Coastguard Worker entry_data = list(enumerate_values(merged_entry)) 314*61046927SAndroid Build Coastguard Worker entries.append(entry_data) 315*61046927SAndroid Build Coastguard Worker 316*61046927SAndroid Build Coastguard Worker logger.debug('Total entries: %u', len(entries)) 317*61046927SAndroid Build Coastguard Worker 318*61046927SAndroid Build Coastguard Worker with open(args.output, "w", encoding="UTF-8") as fh: 319*61046927SAndroid Build Coastguard Worker print(Template(template).render(struct=enumerate_struct(merged), entries=entries), file=fh) 320*61046927SAndroid Build Coastguard Worker 321*61046927SAndroid Build Coastguard Worker 322*61046927SAndroid Build Coastguard Workerif __name__ == '__main__': 323*61046927SAndroid Build Coastguard Worker main() 324