OLD | NEW |
(Empty) | |
| 1 # Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. |
| 4 |
| 5 """Utility functions (file reading, simple IDL parsing by regexes) for IDL build
. |
| 6 |
| 7 Design doc: http://www.chromium.org/developers/design-documents/idl-build |
| 8 """ |
| 9 |
| 10 import os |
| 11 import cPickle as pickle |
| 12 import re |
| 13 import string |
| 14 |
| 15 |
| 16 class IdlBadFilenameError(Exception): |
| 17 """Raised if an IDL filename disagrees with the interface name in the file."
"" |
| 18 pass |
| 19 |
| 20 |
| 21 def idl_filename_to_interface_name(idl_filename): |
| 22 # interface name is the root of the basename: InterfaceName.idl |
| 23 return os.path.splitext(os.path.basename(idl_filename))[0] |
| 24 |
| 25 |
| 26 ################################################################################ |
| 27 # Basic file reading/writing |
| 28 ################################################################################ |
| 29 |
| 30 def get_file_contents(filename): |
| 31 with open(filename) as f: |
| 32 return f.read() |
| 33 |
| 34 |
| 35 def read_file_to_list(filename): |
| 36 """Returns a list of (stripped) lines for a given filename.""" |
| 37 with open(filename) as f: |
| 38 return [line.rstrip('\n') for line in f] |
| 39 |
| 40 |
| 41 def read_pickle_files(pickle_filenames): |
| 42 for pickle_filename in pickle_filenames: |
| 43 with open(pickle_filename) as pickle_file: |
| 44 yield pickle.load(pickle_file) |
| 45 |
| 46 |
| 47 def write_file(new_text, destination_filename, only_if_changed): |
| 48 if only_if_changed and os.path.isfile(destination_filename): |
| 49 with open(destination_filename) as destination_file: |
| 50 if destination_file.read() == new_text: |
| 51 return |
| 52 with open(destination_filename, 'w') as destination_file: |
| 53 destination_file.write(new_text) |
| 54 |
| 55 |
| 56 def write_pickle_file(pickle_filename, data, only_if_changed): |
| 57 if only_if_changed and os.path.isfile(pickle_filename): |
| 58 with open(pickle_filename) as pickle_file: |
| 59 try: |
| 60 if pickle.load(pickle_file) == data: |
| 61 return |
| 62 except (EOFError, pickle.UnpicklingError): |
| 63 # If trouble unpickling, overwrite |
| 64 pass |
| 65 with open(pickle_filename, 'w') as pickle_file: |
| 66 pickle.dump(data, pickle_file) |
| 67 |
| 68 |
| 69 ################################################################################ |
| 70 # IDL parsing |
| 71 # |
| 72 # We use regular expressions for parsing; this is incorrect (Web IDL is not a |
| 73 # regular language), but simple and sufficient in practice. |
| 74 # Leading and trailing context (e.g. following '{') used to avoid false matches. |
| 75 ################################################################################ |
| 76 |
| 77 def get_partial_interface_name_from_idl(file_contents): |
| 78 match = re.search(r'partial\s+interface\s+(\w+)\s*{', file_contents) |
| 79 return match and match.group(1) |
| 80 |
| 81 |
| 82 def get_implements_from_idl(file_contents, interface_name): |
| 83 """Returns lists of implementing and implemented interfaces. |
| 84 |
| 85 Rule is: identifier-A implements identifier-B; |
| 86 i.e., implement*ing* implements implement*ed*; |
| 87 http://www.w3.org/TR/WebIDL/#idl-implements-statements |
| 88 |
| 89 Returns two lists of interfaces: identifier-As and identifier-Bs. |
| 90 An 'implements' statements can be present in the IDL file for either the |
| 91 implementing or the implemented interface, but not other files. |
| 92 """ |
| 93 implements_re = (r'^\s*' |
| 94 r'(\w+)\s+' |
| 95 r'implements\s+' |
| 96 r'(\w+)\s*' |
| 97 r';') |
| 98 implements_matches = re.finditer(implements_re, file_contents, re.MULTILINE) |
| 99 implements_pairs = [match.groups() for match in implements_matches] |
| 100 |
| 101 foreign_implements = [pair for pair in implements_pairs |
| 102 if interface_name not in pair] |
| 103 if foreign_implements: |
| 104 left, right = foreign_implements.pop() |
| 105 raise IdlBadFilenameError( |
| 106 'implements statement found in unrelated IDL file.\n' |
| 107 'Statement is:\n' |
| 108 ' %s implements %s;\n' |
| 109 'but filename is unrelated "%s.idl"' % |
| 110 (left, right, interface_name)) |
| 111 |
| 112 return ( |
| 113 [left for left, right in implements_pairs if right == interface_name], |
| 114 [right for left, right in implements_pairs if left == interface_name]) |
| 115 |
| 116 |
| 117 def is_callback_interface_from_idl(file_contents): |
| 118 match = re.search(r'callback\s+interface\s+\w+\s*{', file_contents) |
| 119 return bool(match) |
| 120 |
| 121 |
| 122 def get_parent_interface(file_contents): |
| 123 match = re.search(r'interface\s+' |
| 124 r'\w+\s*' |
| 125 r':\s*(\w+)\s*' |
| 126 r'{', |
| 127 file_contents) |
| 128 return match and match.group(1) |
| 129 |
| 130 |
| 131 def get_interface_extended_attributes_from_idl(file_contents): |
| 132 # Strip comments |
| 133 # re.compile needed b/c Python 2.6 doesn't support flags in re.sub |
| 134 single_line_comment_re = re.compile(r'//.*$', flags=re.MULTILINE) |
| 135 block_comment_re = re.compile(r'/\*.*?\*/', flags=re.MULTILINE | re.DOTALL) |
| 136 file_contents = re.sub(single_line_comment_re, '', file_contents) |
| 137 file_contents = re.sub(block_comment_re, '', file_contents) |
| 138 |
| 139 match = re.search(r'\[(.*)\]\s*' |
| 140 r'((callback|partial)\s+)?' |
| 141 r'(interface|exception)\s+' |
| 142 r'\w+\s*' |
| 143 r'(:\s*\w+\s*)?' |
| 144 r'{', |
| 145 file_contents, flags=re.DOTALL) |
| 146 if not match: |
| 147 return {} |
| 148 |
| 149 extended_attributes_string = match.group(1) |
| 150 extended_attributes = {} |
| 151 # FIXME: this splitting is WRONG: it fails on ExtendedAttributeArgList like |
| 152 # 'NamedConstructor=Foo(a, b)' |
| 153 parts = [extended_attribute.strip() |
| 154 for extended_attribute in extended_attributes_string.split(',') |
| 155 # Discard empty parts, which may exist due to trailing comma |
| 156 if extended_attribute.strip()] |
| 157 for part in parts: |
| 158 name, _, value = map(string.strip, part.partition('=')) |
| 159 extended_attributes[name] = value |
| 160 return extended_attributes |
| 161 |
| 162 |
| 163 def get_put_forward_interfaces_from_idl(file_contents): |
| 164 put_forwards_pattern = (r'\[[^\]]*PutForwards=[^\]]*\]\s+' |
| 165 r'readonly\s+' |
| 166 r'attribute\s+' |
| 167 r'(\w+)') |
| 168 return sorted(set(match.group(1) |
| 169 for match in re.finditer(put_forwards_pattern, |
| 170 file_contents, |
| 171 flags=re.DOTALL))) |
OLD | NEW |