| #!/usr/bin/python3 |
| |
| # |
| # Script for generation of VHAL properties metadata .json from AIDL interface |
| # |
| # This metadata is used to display human property names, names of enum |
| # data types for their values, change and access modes and other information, |
| # available from AIDL block comments, but not at runtime. |
| # |
| # Usage example: |
| # ./emu_metadata/generate_emulator_metadata.py android/hardware/automotive/vehicle $OUT/android.hardware.automotive.vehicle-types-meta.json |
| # (Note, that the resulting file has to match a '*types-meta.json' pattern to be parsed by the emulator). |
| # |
| |
| import json |
| import os |
| import re |
| import sys |
| |
| from pathlib import Path |
| |
| RE_ENUM = re.compile(r"\s*enum\s+(\w*) {\n(.*)}", re.MULTILINE | re.DOTALL) |
| RE_COMMENT = re.compile(r"(?:(?:\/\*\*)((?:.|\n)*?)(?:\*\/))?(?:\n|^)\s*(\w*)(?:\s+=\s*)?((?:[a-zA-Z0-9]|\s|\+|)*),", re.DOTALL) |
| RE_BLOCK_COMMENT_TITLE = re.compile("^(?:\s|\*)*((?:\w|\s|\.)*)\n(?:\s|\*)*(?:\n|$)") |
| RE_BLOCK_COMMENT_ANNOTATION = re.compile("^(?:\s|\*)*@(\w*)\s+((?:\w|:)*)", re.MULTILINE) |
| RE_HEX_NUMBER = re.compile("([0-9A-Fa-fxX]+)") |
| |
| |
| class JEnum: |
| def __init__(self, name): |
| self.name = name |
| self.values = [] |
| |
| |
| class Converter: |
| # Only addition is supported for now, but that covers all existing properties except |
| # OBD diagnostics, which use bitwise shifts |
| def calculateValue(self, expression, default_value): |
| numbers = RE_HEX_NUMBER.findall(expression) |
| if len(numbers) == 0: |
| return default_value |
| result = 0 |
| base = 10 |
| if numbers[0].lower().startswith("0x"): |
| base = 16 |
| for number in numbers: |
| result += int(number, base) |
| return result |
| |
| def parseBlockComment(self, value, blockComment): |
| titles = RE_BLOCK_COMMENT_TITLE.findall(blockComment) |
| for title in titles: |
| value['name'] = title |
| break |
| annots_res = RE_BLOCK_COMMENT_ANNOTATION.findall(blockComment) |
| for annot in annots_res: |
| value[annot[0]] = annot[1] |
| |
| def parseEnumContents(self, enum: JEnum, enumValue): |
| matches = RE_COMMENT.findall(enumValue) |
| defaultValue = 0 |
| for match in matches: |
| value = dict() |
| value['name'] = match[1] |
| value['value'] = self.calculateValue(match[2], defaultValue) |
| defaultValue = value['value'] + 1 |
| if enum.name == "VehicleProperty": |
| block_comment = match[0] |
| self.parseBlockComment(value, block_comment) |
| enum.values.append(value) |
| |
| def convert(self, input): |
| text = Path(input).read_text() |
| matches = RE_ENUM.findall(text) |
| jenums = [] |
| for match in matches: |
| enum = JEnum(match[0]) |
| self.parseEnumContents(enum, match[1]) |
| jenums.append(enum) |
| return jenums |
| |
| def main(): |
| if (len(sys.argv) != 3): |
| print("Usage: ", sys.argv[0], " INPUT_PATH OUTPUT") |
| sys.exit(1) |
| aidl_path = sys.argv[1] |
| out_path = sys.argv[2] |
| result = [] |
| for file in os.listdir(aidl_path): |
| result.extend(Converter().convert(os.path.join(aidl_path, file))) |
| json_result = json.dumps(result, default=vars, indent=2) |
| with open(out_path, 'w') as f: |
| f.write(json_result) |
| |
| |
| if __name__ == "__main__": |
| main() |