1d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)# Copyright 2014 The Chromium Authors. All rights reserved.
2d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
3d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)# found in the LICENSE file.
4d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)
5d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)"""Utility functions (file reading, simple IDL parsing by regexes) for IDL build.
6d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)
7d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)Design doc: http://www.chromium.org/developers/design-documents/idl-build
8d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)"""
9d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)
10d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)import os
11d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)import cPickle as pickle
12d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)import re
13d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)import string
147242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucciimport subprocess
157242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci
167242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci
177242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano TucciKNOWN_COMPONENTS = frozenset(['core', 'modules'])
18d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)
19d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)
20d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)class IdlBadFilenameError(Exception):
21d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)    """Raised if an IDL filename disagrees with the interface name in the file."""
22d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)    pass
23d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)
24d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)
25f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)def idl_filename_to_interface_name(idl_filename):
26f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)    # interface name is the root of the basename: InterfaceName.idl
27f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)    return os.path.splitext(os.path.basename(idl_filename))[0]
28f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)
29f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)
307242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tuccidef idl_filename_to_component(idl_filename):
317242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci    path = os.path.dirname(os.path.realpath(idl_filename))
327242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci    while path:
337242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci        dirname, basename = os.path.split(path)
347242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci        if basename.lower() in KNOWN_COMPONENTS:
357242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci            return basename.lower()
367242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci        path = dirname
377242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci    raise 'Unknown component type for %s' % idl_filename
387242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci
397242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci
40d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)################################################################################
41d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)# Basic file reading/writing
42d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)################################################################################
43d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)
44d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)def get_file_contents(filename):
45d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)    with open(filename) as f:
46d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)        return f.read()
47d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)
48d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)
49f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)def read_file_to_list(filename):
50f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)    """Returns a list of (stripped) lines for a given filename."""
51f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)    with open(filename) as f:
52f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)        return [line.rstrip('\n') for line in f]
53f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)
54f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)
557242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tuccidef resolve_cygpath(cygdrive_names):
567242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci    if not cygdrive_names:
577242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci        return []
587242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci    cmd = ['cygpath', '-f', '-', '-wa']
597242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci    process = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
607242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci    idl_file_names = []
617242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci    for file_name in cygdrive_names:
627242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci        process.stdin.write('%s\n' % file_name)
637242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci        process.stdin.flush()
647242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci        idl_file_names.append(process.stdout.readline().rstrip())
657242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci    process.stdin.close()
667242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci    process.wait()
677242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci    return idl_file_names
687242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci
697242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci
707242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tuccidef read_idl_files_list_from_file(filename):
717242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci    """Similar to read_file_to_list, but also resolves cygpath."""
727242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci    with open(filename) as input_file:
737242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci        file_names = sorted([os.path.realpath(line.rstrip('\n'))
747242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci                             for line in input_file])
757242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci        idl_file_names = [file_name for file_name in file_names
767242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci                          if not file_name.startswith('/cygdrive')]
777242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci        cygdrive_names = [file_name for file_name in file_names
787242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci                          if file_name.startswith('/cygdrive')]
797242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci        idl_file_names.extend(resolve_cygpath(cygdrive_names))
807242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci        return idl_file_names
817242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci
827242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci
83f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)def read_pickle_files(pickle_filenames):
84f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)    for pickle_filename in pickle_filenames:
85f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)        with open(pickle_filename) as pickle_file:
86f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)            yield pickle.load(pickle_file)
87f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)
88f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)
89d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)def write_file(new_text, destination_filename, only_if_changed):
90d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)    if only_if_changed and os.path.isfile(destination_filename):
91d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)        with open(destination_filename) as destination_file:
92d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)            if destination_file.read() == new_text:
93d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)                return
947242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci    destination_dirname = os.path.dirname(destination_filename)
957242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci    if not os.path.exists(destination_dirname):
967242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci        os.makedirs(destination_dirname)
97d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)    with open(destination_filename, 'w') as destination_file:
98d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)        destination_file.write(new_text)
99d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)
100d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)
101d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)def write_pickle_file(pickle_filename, data, only_if_changed):
102d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)    if only_if_changed and os.path.isfile(pickle_filename):
103d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)        with open(pickle_filename) as pickle_file:
104d6cdb82654e8f3343a693ca752d5c4cee0324e17Torne (Richard Coles)            try:
105d6cdb82654e8f3343a693ca752d5c4cee0324e17Torne (Richard Coles)                if pickle.load(pickle_file) == data:
106d6cdb82654e8f3343a693ca752d5c4cee0324e17Torne (Richard Coles)                    return
107d6cdb82654e8f3343a693ca752d5c4cee0324e17Torne (Richard Coles)            except (EOFError, pickle.UnpicklingError):
108d6cdb82654e8f3343a693ca752d5c4cee0324e17Torne (Richard Coles)                # If trouble unpickling, overwrite
109d6cdb82654e8f3343a693ca752d5c4cee0324e17Torne (Richard Coles)                pass
110d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)    with open(pickle_filename, 'w') as pickle_file:
111d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)        pickle.dump(data, pickle_file)
112d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)
113d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)
114d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)################################################################################
115d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)# IDL parsing
116d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)#
117d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)# We use regular expressions for parsing; this is incorrect (Web IDL is not a
118d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)# regular language), but simple and sufficient in practice.
119d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)# Leading and trailing context (e.g. following '{') used to avoid false matches.
120d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)################################################################################
121d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)
122d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)def get_partial_interface_name_from_idl(file_contents):
123d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)    match = re.search(r'partial\s+interface\s+(\w+)\s*{', file_contents)
124d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)    return match and match.group(1)
125d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)
126d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)
127323480423219ecd77329f8326dc5e0e3b50926d4Torne (Richard Coles)def get_implements_from_idl(file_contents, interface_name):
128323480423219ecd77329f8326dc5e0e3b50926d4Torne (Richard Coles)    """Returns lists of implementing and implemented interfaces.
129d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)
130323480423219ecd77329f8326dc5e0e3b50926d4Torne (Richard Coles)    Rule is: identifier-A implements identifier-B;
131323480423219ecd77329f8326dc5e0e3b50926d4Torne (Richard Coles)    i.e., implement*ing* implements implement*ed*;
132323480423219ecd77329f8326dc5e0e3b50926d4Torne (Richard Coles)    http://www.w3.org/TR/WebIDL/#idl-implements-statements
133323480423219ecd77329f8326dc5e0e3b50926d4Torne (Richard Coles)
134323480423219ecd77329f8326dc5e0e3b50926d4Torne (Richard Coles)    Returns two lists of interfaces: identifier-As and identifier-Bs.
135323480423219ecd77329f8326dc5e0e3b50926d4Torne (Richard Coles)    An 'implements' statements can be present in the IDL file for either the
136323480423219ecd77329f8326dc5e0e3b50926d4Torne (Richard Coles)    implementing or the implemented interface, but not other files.
137323480423219ecd77329f8326dc5e0e3b50926d4Torne (Richard Coles)    """
138d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)    implements_re = (r'^\s*'
139d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)                     r'(\w+)\s+'
140d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)                     r'implements\s+'
141d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)                     r'(\w+)\s*'
142d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)                     r';')
143d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)    implements_matches = re.finditer(implements_re, file_contents, re.MULTILINE)
144323480423219ecd77329f8326dc5e0e3b50926d4Torne (Richard Coles)    implements_pairs = [match.groups() for match in implements_matches]
145323480423219ecd77329f8326dc5e0e3b50926d4Torne (Richard Coles)
146323480423219ecd77329f8326dc5e0e3b50926d4Torne (Richard Coles)    foreign_implements = [pair for pair in implements_pairs
147323480423219ecd77329f8326dc5e0e3b50926d4Torne (Richard Coles)                          if interface_name not in pair]
148323480423219ecd77329f8326dc5e0e3b50926d4Torne (Richard Coles)    if foreign_implements:
149323480423219ecd77329f8326dc5e0e3b50926d4Torne (Richard Coles)        left, right = foreign_implements.pop()
150323480423219ecd77329f8326dc5e0e3b50926d4Torne (Richard Coles)        raise IdlBadFilenameError(
151323480423219ecd77329f8326dc5e0e3b50926d4Torne (Richard Coles)                'implements statement found in unrelated IDL file.\n'
152323480423219ecd77329f8326dc5e0e3b50926d4Torne (Richard Coles)                'Statement is:\n'
153323480423219ecd77329f8326dc5e0e3b50926d4Torne (Richard Coles)                '    %s implements %s;\n'
154323480423219ecd77329f8326dc5e0e3b50926d4Torne (Richard Coles)                'but filename is unrelated "%s.idl"' %
155323480423219ecd77329f8326dc5e0e3b50926d4Torne (Richard Coles)                (left, right, interface_name))
156323480423219ecd77329f8326dc5e0e3b50926d4Torne (Richard Coles)
157323480423219ecd77329f8326dc5e0e3b50926d4Torne (Richard Coles)    return (
158323480423219ecd77329f8326dc5e0e3b50926d4Torne (Richard Coles)        [left for left, right in implements_pairs if right == interface_name],
159323480423219ecd77329f8326dc5e0e3b50926d4Torne (Richard Coles)        [right for left, right in implements_pairs if left == interface_name])
160d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)
161d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)
162d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)def is_callback_interface_from_idl(file_contents):
163d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)    match = re.search(r'callback\s+interface\s+\w+\s*{', file_contents)
164d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)    return bool(match)
165d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)
166d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)
167c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)def is_dictionary_from_idl(file_contents):
168c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)    match = re.search(r'dictionary\s+\w+\s*{', file_contents)
169c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)    return bool(match)
170c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)
171c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)
172d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)def get_parent_interface(file_contents):
173d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)    match = re.search(r'interface\s+'
174d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)                      r'\w+\s*'
175d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)                      r':\s*(\w+)\s*'
176d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)                      r'{',
177d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)                      file_contents)
178d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)    return match and match.group(1)
179d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)
180d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)
181d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)def get_interface_extended_attributes_from_idl(file_contents):
182323480423219ecd77329f8326dc5e0e3b50926d4Torne (Richard Coles)    # Strip comments
183323480423219ecd77329f8326dc5e0e3b50926d4Torne (Richard Coles)    # re.compile needed b/c Python 2.6 doesn't support flags in re.sub
184323480423219ecd77329f8326dc5e0e3b50926d4Torne (Richard Coles)    single_line_comment_re = re.compile(r'//.*$', flags=re.MULTILINE)
185323480423219ecd77329f8326dc5e0e3b50926d4Torne (Richard Coles)    block_comment_re = re.compile(r'/\*.*?\*/', flags=re.MULTILINE | re.DOTALL)
186323480423219ecd77329f8326dc5e0e3b50926d4Torne (Richard Coles)    file_contents = re.sub(single_line_comment_re, '', file_contents)
187323480423219ecd77329f8326dc5e0e3b50926d4Torne (Richard Coles)    file_contents = re.sub(block_comment_re, '', file_contents)
188323480423219ecd77329f8326dc5e0e3b50926d4Torne (Richard Coles)
189d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)    match = re.search(r'\[(.*)\]\s*'
190d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)                      r'((callback|partial)\s+)?'
191d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)                      r'(interface|exception)\s+'
192d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)                      r'\w+\s*'
193d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)                      r'(:\s*\w+\s*)?'
194d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)                      r'{',
195d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)                      file_contents, flags=re.DOTALL)
196d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)    if not match:
197d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)        return {}
198323480423219ecd77329f8326dc5e0e3b50926d4Torne (Richard Coles)
199323480423219ecd77329f8326dc5e0e3b50926d4Torne (Richard Coles)    extended_attributes_string = match.group(1)
200d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)    extended_attributes = {}
201e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)    # FIXME: this splitting is WRONG: it fails on extended attributes where lists of
202e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)    # multiple values are used, which are seperated by a comma and a space.
203d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)    parts = [extended_attribute.strip()
204e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)             for extended_attribute in re.split(',\s+', extended_attributes_string)
205d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)             # Discard empty parts, which may exist due to trailing comma
206d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)             if extended_attribute.strip()]
207d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)    for part in parts:
208d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)        name, _, value = map(string.strip, part.partition('='))
209d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)        extended_attributes[name] = value
210d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)    return extended_attributes
211d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)
212d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)
213d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)def get_put_forward_interfaces_from_idl(file_contents):
214d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)    put_forwards_pattern = (r'\[[^\]]*PutForwards=[^\]]*\]\s+'
215d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)                            r'readonly\s+'
216d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)                            r'attribute\s+'
217d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)                            r'(\w+)')
218d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)    return sorted(set(match.group(1)
219d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)                      for match in re.finditer(put_forwards_pattern,
220d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)                                               file_contents,
221d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)                                               flags=re.DOTALL)))
222