1#!/usr/bin/python3 2 3# 4# Script for generation of VHAL properties metadata .json from AIDL interface 5# 6# This metadata is used to display human property names, names of enum 7# data types for their values, change and access modes and other information, 8# available from AIDL block comments, but not at runtime. 9# 10# Usage example: 11# ./emu_metadata/generate_emulator_metadata.py android/hardware/automotive/vehicle $OUT/android.hardware.automotive.vehicle-types-meta.json 12# (Note, that the resulting file has to match a '*types-meta.json' pattern to be parsed by the emulator). 13# 14 15import json 16import os 17import re 18import sys 19 20from pathlib import Path 21 22RE_ENUM = re.compile(r"\s*enum\s+(\w*) {\n(.*)}", re.MULTILINE | re.DOTALL) 23RE_COMMENT = re.compile(r"(?:(?:\/\*\*)((?:.|\n)*?)(?:\*\/))?(?:\n|^)\s*(\w*)(?:\s+=\s*)?((?:[a-zA-Z0-9]|\s|\+|)*),", re.DOTALL) 24RE_BLOCK_COMMENT_TITLE = re.compile("^(?:\s|\*)*((?:\w|\s|\.)*)\n(?:\s|\*)*(?:\n|$)") 25RE_BLOCK_COMMENT_ANNOTATION = re.compile("^(?:\s|\*)*@(\w*)\s+((?:\w|:)*)", re.MULTILINE) 26RE_HEX_NUMBER = re.compile("([0-9A-Fa-fxX]+)") 27 28 29class JEnum: 30 def __init__(self, name): 31 self.name = name 32 self.values = [] 33 34 35class Converter: 36 # Only addition is supported for now, but that covers all existing properties except 37 # OBD diagnostics, which use bitwise shifts 38 def calculateValue(self, expression, default_value): 39 numbers = RE_HEX_NUMBER.findall(expression) 40 if len(numbers) == 0: 41 return default_value 42 result = 0 43 base = 10 44 if numbers[0].lower().startswith("0x"): 45 base = 16 46 for number in numbers: 47 result += int(number, base) 48 return result 49 50 def parseBlockComment(self, value, blockComment): 51 titles = RE_BLOCK_COMMENT_TITLE.findall(blockComment) 52 for title in titles: 53 value['name'] = title 54 break 55 annots_res = RE_BLOCK_COMMENT_ANNOTATION.findall(blockComment) 56 for annot in annots_res: 57 value[annot[0]] = annot[1] 58 59 def parseEnumContents(self, enum: JEnum, enumValue): 60 matches = RE_COMMENT.findall(enumValue) 61 defaultValue = 0 62 for match in matches: 63 value = dict() 64 value['name'] = match[1] 65 value['value'] = self.calculateValue(match[2], defaultValue) 66 defaultValue = value['value'] + 1 67 if enum.name == "VehicleProperty": 68 block_comment = match[0] 69 self.parseBlockComment(value, block_comment) 70 enum.values.append(value) 71 72 def convert(self, input): 73 text = Path(input).read_text() 74 matches = RE_ENUM.findall(text) 75 jenums = [] 76 for match in matches: 77 enum = JEnum(match[0]) 78 self.parseEnumContents(enum, match[1]) 79 jenums.append(enum) 80 return jenums 81 82def main(): 83 if (len(sys.argv) != 3): 84 print("Usage: ", sys.argv[0], " INPUT_PATH OUTPUT") 85 sys.exit(1) 86 aidl_path = sys.argv[1] 87 out_path = sys.argv[2] 88 result = [] 89 for file in os.listdir(aidl_path): 90 result.extend(Converter().convert(os.path.join(aidl_path, file))) 91 json_result = json.dumps(result, default=vars, indent=2) 92 with open(out_path, 'w') as f: 93 f.write(json_result) 94 95 96if __name__ == "__main__": 97 main() 98