Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 56908bf6 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge changes Icc5d023c,I3a2246c1 into main

* changes:
  Add script to parse VehicleProperty.aidl to CSV.
  Minor update on access annotation.
parents a1560add 8ddd65db
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -421,6 +421,7 @@ enum VehicleProperty {
     *
     * @change_mode VehiclePropertyChangeMode.CONTINUOUS
     * @access VehiclePropertyAccess.READ_WRITE
     * @access VehiclePropertyAccess.READ
     * @unit VehicleUnit:METER
     */
    RANGE_REMAINING = 0x0308 + 0x10000000 + 0x01000000
@@ -4476,6 +4477,9 @@ enum VehicleProperty {
     * powers on the vehicle. VEHICLE_IN_USE is set to true. After a driving session, user powers
     * off the vehicle, VEHICLE_IN_USE is set to false.
     *
     * <p>This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
     * implement it as VehiclePropertyAccess.READ only.
     *
     * @change_mode VehiclePropertyChangeMode.ON_CHANGE
     * @access VehiclePropertyAccess.READ_WRITE
     * @access VehiclePropertyAccess.READ
+132 −34
Original line number Diff line number Diff line
@@ -49,6 +49,8 @@ RE_COMMENT_BEGIN = re.compile('\s*\/\*\*?')
RE_COMMENT_END = re.compile('\s*\*\/')
RE_CHANGE_MODE = re.compile('\s*\* @change_mode (\S+)\s*')
RE_ACCESS = re.compile('\s*\* @access (\S+)\s*')
RE_DATA_ENUM = re.compile('\s*\* @data_enum (\S+)\s*')
RE_UNIT = re.compile('\s*\* @unit (\S+)\s+')
RE_VALUE = re.compile('\s*(\w+)\s*=(.*)')

LICENSE = """/*
@@ -166,55 +168,121 @@ ACCESS_JAVA_FOOTER = """
"""


class Converter:
class PropertyConfig:
    """Represents one VHAL property definition in VehicleProperty.aidl."""

    def __init__(self, name, annotation_re):
        self.name = name
        self.annotation_re = annotation_re
    def __init__(self):
        self.name = None
        self.description = None
        self.change_mode = None
        self.access_modes = []
        self.enum_types = []
        self.unit_type = None

    def convert(self, input, output, header, footer, cpp):
    def __repr__(self):
        return self.__str__()

    def __str__(self):
        return ('PropertyConfig{{' +
            'name: {}, description: {}, change_mode: {}, access_modes: {}, enum_types: {}' +
            ', unit_type: {}}}').format(self.name, self.description, self.change_mode,
                self.access_modes, self.enum_types, self.unit_type)


class FileParser:

    def __init__(self):
        self.configs = None

    def parseFile(self, input_file):
        """Parses the input VehicleProperty.aidl file into a list of property configs."""
        processing = False
        in_comment = False
        content = LICENSE + header
        annotation = None
        id = 0
        with open(input, 'r') as f:
        configs = []
        config = None
        with open(input_file, 'r') as f:
            for line in f.readlines():
                if RE_ENUM_START.match(line):
                    processing = True
                    annotation = None
                elif RE_ENUM_END.match(line):
                    processing = False
                if not processing:
                    continue
                if RE_COMMENT_BEGIN.match(line):
                    in_comment = True
                    annotation = None
                    config = PropertyConfig()
                    description = ''
                if RE_COMMENT_END.match(line):
                    in_comment = False
                if in_comment:
                    match = self.annotation_re.match(line)
                    if match and not annotation:
                        annotation = match.group(1)
                    if not config.description:
                        sline = line.strip()
                        # Skip the first line of comment
                        if sline.startswith('*'):
                            # Remove the '*'.
                            sline = sline[1:].strip()
                            # We reach an empty line of comment, the description part is ending.
                            if sline == '':
                                config.description = description
                            else:
                                if description != '':
                                    description += ' '
                                description += sline
                    match = RE_CHANGE_MODE.match(line)
                    if match:
                        config.change_mode = match.group(1).replace('VehiclePropertyChangeMode.', '')
                    match = RE_ACCESS.match(line)
                    if match:
                        config.access_modes.append(match.group(1).replace('VehiclePropertyAccess.', ''))
                    match = RE_UNIT.match(line)
                    if match:
                        config.unit_type = match.group(1)
                    match = RE_DATA_ENUM.match(line)
                    if match:
                        config.enum_types.append(match.group(1))
                else:
                    match = RE_VALUE.match(line)
                    if match:
                        prop_name = match.group(1)
                        if prop_name == 'INVALID':
                            continue
                        if not annotation:
                        if not config.change_mode:
                            raise Exception(
                                    'No change_mode annotation for property: ' + prop_name)
                        if not config.access_modes:
                            raise Exception(
                                    'No @' + self.name + ' annotation for property: ' + prop_name)
                        if id != 0:
                                    'No access_mode annotation for property: ' + prop_name)
                        config.name = prop_name
                        configs.append(config)

        self.configs = configs

    def convert(self, output, header, footer, cpp, field):
        """Converts the property config file to C++/Java output file."""
        counter = 0
        content = LICENSE + header
        for config in self.configs:
            if field == 'change_mode':
                if cpp:
                    annotation = "VehiclePropertyChangeMode::" + config.change_mode
                else:
                    annotation = "VehiclePropertyChangeMode." + config.change_mode
            elif field == 'access_mode':
                if cpp:
                    annotation = "VehiclePropertyAccess::" + config.access_modes[0]
                else:
                    annotation = "VehiclePropertyAccess." + config.access_modes[0]
            else:
                raise Exception('Unknown field: ' + field)
            if counter != 0:
                content += '\n'
            if cpp:
                            annotation = annotation.replace('.', '::')
                            content += (TAB + TAB + '{VehicleProperty::' + prop_name + ', ' +
                content += (TAB + TAB + '{VehicleProperty::' + config.name + ', ' +
                            annotation + '},')
            else:
                            content += (TAB + TAB + 'Map.entry(VehicleProperty.' + prop_name + ', ' +
                content += (TAB + TAB + 'Map.entry(VehicleProperty.' + config.name + ', ' +
                            annotation + '),')
                        id += 1
            counter += 1

        # Remove the additional ',' at the end for the Java file.
        if not cpp:
@@ -225,6 +293,30 @@ class Converter:
        with open(output, 'w') as f:
            f.write(content)

    def outputAsCsv(self, output):
        content = 'name,description,change mode,access mode,enum type,unit type\n'
        for config in self.configs:
            enum_types = None
            if not config.enum_types:
                enum_types = '/'
            else:
                enum_types = '/'.join(config.enum_types)
            unit_type = config.unit_type
            if not unit_type:
                unit_type = '/'
            access_modes = ''
            content += '"{}","{}","{}","{}","{}","{}"\n'.format(
                    config.name,
                    # Need to escape quote as double quote.
                    config.description.replace('"', '""'),
                    config.change_mode,
                    '/'.join(config.access_modes),
                    enum_types,
                    unit_type)

        with open(output, 'w+') as f:
            f.write(content)


def createTempFile():
    f = tempfile.NamedTemporaryFile(delete=False);
@@ -239,6 +331,8 @@ def main():
    parser.add_argument('--preupload_files', nargs='+', required=False, help='modified files')
    parser.add_argument('--check_only', required=False, action='store_true',
            help='only check whether the generated files need update')
    parser.add_argument('--output_csv', required=False,
            help='Path to the parsing result in CSV style, useful for doc generation')
    args = parser.parse_args();
    android_top = None
    output_folder = None
@@ -258,6 +352,12 @@ def main():
            'at the android root')

    aidl_file = os.path.join(android_top, PROP_AIDL_FILE_PATH)
    f = FileParser();
    f.parseFile(aidl_file)

    if args.output_csv:
        f.outputAsCsv(args.output_csv)
        return

    change_mode_cpp_file = os.path.join(android_top, CHANGE_MODE_CPP_FILE_PATH);
    access_cpp_file = os.path.join(android_top, ACCESS_CPP_FILE_PATH);
@@ -281,14 +381,12 @@ def main():
        temp_files.append(access_java_output)

    try:
        c = Converter('change_mode', RE_CHANGE_MODE);
        c.convert(aidl_file, change_mode_cpp_output, CHANGE_MODE_CPP_HEADER, CHANGE_MODE_CPP_FOOTER,
                True)
        c.convert(aidl_file, change_mode_java_output, CHANGE_MODE_JAVA_HEADER,
                CHANGE_MODE_JAVA_FOOTER, False)
        c = Converter('access', RE_ACCESS)
        c.convert(aidl_file, access_cpp_output, ACCESS_CPP_HEADER, ACCESS_CPP_FOOTER, True)
        c.convert(aidl_file, access_java_output, ACCESS_JAVA_HEADER, ACCESS_JAVA_FOOTER, False)
        f.convert(change_mode_cpp_output, CHANGE_MODE_CPP_HEADER, CHANGE_MODE_CPP_FOOTER,
                True, 'change_mode')
        f.convert(change_mode_java_output, CHANGE_MODE_JAVA_HEADER,
                CHANGE_MODE_JAVA_FOOTER, False, 'change_mode')
        f.convert(access_cpp_output, ACCESS_CPP_HEADER, ACCESS_CPP_FOOTER, True, 'access_mode')
        f.convert(access_java_output, ACCESS_JAVA_HEADER, ACCESS_JAVA_FOOTER, False, 'access_mode')

        if not args.check_only:
            return