OLD | NEW |
| (Empty) |
1 # Copyright (C) 2013 Google Inc. All rights reserved. | |
2 # | |
3 # Redistribution and use in source and binary forms, with or without | |
4 # modification, are permitted provided that the following conditions are | |
5 # met: | |
6 # | |
7 # * Redistributions of source code must retain the above copyright | |
8 # notice, this list of conditions and the following disclaimer. | |
9 # * Redistributions in binary form must reproduce the above | |
10 # copyright notice, this list of conditions and the following disclaimer | |
11 # in the documentation and/or other materials provided with the | |
12 # distribution. | |
13 # * Neither the name of Google Inc. nor the names of its | |
14 # contributors may be used to endorse or promote products derived from | |
15 # this software without specific prior written permission. | |
16 # | |
17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
18 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
19 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
20 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
21 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
28 | |
29 import optparse | |
30 import os | |
31 import os.path | |
32 import re | |
33 import string | |
34 | |
35 | |
36 def parse_options(): | |
37 parser = optparse.OptionParser() | |
38 parser.add_option('--event-names-file', help='output file') | |
39 parser.add_option('--idl-files-list', help='file listing all IDLs') | |
40 parser.add_option('--supplemental-dependency-file', help='output file') | |
41 parser.add_option('--window-constructors-file', help='output file') | |
42 parser.add_option('--workerglobalscope-constructors-file', help='output file
') | |
43 parser.add_option('--sharedworkerglobalscope-constructors-file', help='outpu
t file') | |
44 parser.add_option('--dedicatedworkerglobalscope-constructors-file', help='ou
tput file') | |
45 parser.add_option('--write-file-only-if-changed', type='int', help='if true,
do not write an output file if it would be identical to the existing one, which
avoids unnecessary rebuilds in ninja') | |
46 options, args = parser.parse_args() | |
47 if options.event_names_file is None: | |
48 parser.error('Must specify an output file using --event-names-file.') | |
49 if options.supplemental_dependency_file is None: | |
50 parser.error('Must specify an output file using --supplemental-dependenc
y-file.') | |
51 if options.window_constructors_file is None: | |
52 parser.error('Must specify an output file using --window-constructors-fi
le.') | |
53 if options.workerglobalscope_constructors_file is None: | |
54 parser.error('Must specify an output file using --workerglobalscope-cons
tructors-file.') | |
55 if options.workerglobalscope_constructors_file is None: | |
56 parser.error('Must specify an output file using --sharedworkerglobalscop
e-constructors-file.') | |
57 if options.workerglobalscope_constructors_file is None: | |
58 parser.error('Must specify an output file using --dedicatedworkerglobals
cope-constructors-file.') | |
59 if options.idl_files_list is None: | |
60 parser.error('Must specify the file listing all IDLs using --idl-files-l
ist.') | |
61 if options.write_file_only_if_changed is None: | |
62 parser.error('Must specify whether file is only written if changed using
--write-file-only-if-changed.') | |
63 options.write_file_only_if_changed = bool(options.write_file_only_if_changed
) | |
64 if args: | |
65 parser.error('No arguments taken, but "%s" given.' % ' '.join(args)) | |
66 return options | |
67 | |
68 | |
69 def get_file_contents(idl_filename): | |
70 with open(idl_filename) as idl_file: | |
71 lines = idl_file.readlines() | |
72 return ''.join(lines) | |
73 | |
74 | |
75 def write_file(new_lines, destination_filename, only_if_changed): | |
76 if only_if_changed and os.path.isfile(destination_filename): | |
77 with open(destination_filename) as destination_file: | |
78 old_lines = destination_file.readlines() | |
79 if old_lines == new_lines: | |
80 return | |
81 with open(destination_filename, 'w') as destination_file: | |
82 destination_file.write(''.join(new_lines)) | |
83 | |
84 | |
85 def get_partial_interface_name_from_idl(file_contents): | |
86 match = re.search(r'partial\s+interface\s+(\w+)', file_contents) | |
87 if match: | |
88 return match.group(1) | |
89 return None | |
90 | |
91 | |
92 # identifier-A implements identifier-B; | |
93 # http://www.w3.org/TR/WebIDL/#idl-implements-statements | |
94 def get_implemented_interfaces_from_idl(file_contents, interface_name): | |
95 implemented_interfaces = [] | |
96 for match in re.finditer(r'^\s*(\w+)\s+implements\s+(\w+)\s*;', file_content
s, re.MULTILINE): | |
97 # identifier-A must be the current interface | |
98 assert match.group(1) == interface_name, \ | |
99 "Identifier on the left of the 'implements' statement should be %s in %s.idl, bu
t found %s" % (interface_name, interface_name, match.group(1)) | |
100 implemented_interfaces.append(match.group(2)) | |
101 return implemented_interfaces | |
102 | |
103 def is_callback_interface_from_idl(file_contents): | |
104 match = re.search(r'callback\s+interface\s+\w+', file_contents) | |
105 return match is not None | |
106 | |
107 | |
108 def get_parent_interface(file_contents): | |
109 match = re.search(r'interface\s+\w+\s*:\s*(\w+)\s*', file_contents) | |
110 if match: | |
111 return match.group(1) | |
112 return None | |
113 | |
114 def get_interface_extended_attributes_from_idl(file_contents): | |
115 extended_attributes = {} | |
116 match = re.search(r'\[(.*)\]\s+(interface|exception)\s+(\w+)', | |
117 file_contents, flags=re.DOTALL) | |
118 if match: | |
119 parts = string.split(match.group(1), ',') | |
120 for part in parts: | |
121 # Match 'key = value' | |
122 match = re.match(r'([^=\s]*)(?:\s*=\s*(.*))?', part.strip()) | |
123 key = match.group(1) | |
124 value = match.group(2) or 'VALUE_IS_MISSING' | |
125 if not key: | |
126 continue | |
127 extended_attributes[key] = value | |
128 return extended_attributes | |
129 | |
130 | |
131 def generate_constructor_attribute_list(interface_name, extended_attributes): | |
132 extended_attributes_list = [] | |
133 for attribute_name, attribute_value in extended_attributes.iteritems(): | |
134 if attribute_name not in ['Conditional', 'EnabledAtRuntime', 'EnabledPer
Context']: | |
135 continue | |
136 extended_attribute = attribute_name | |
137 if attribute_value != 'VALUE_IS_MISSING': | |
138 extended_attribute += '=' + attribute_value | |
139 extended_attributes_list.append(extended_attribute) | |
140 if extended_attributes_list: | |
141 extended_string = '[%s] ' % ', '.join(extended_attributes_list) | |
142 else: | |
143 extended_string = '' | |
144 | |
145 attribute_string = 'attribute %(interface_name)sConstructor %(interface_name
)s' % {'interface_name': interface_name} | |
146 attributes_list = [extended_string + attribute_string] | |
147 | |
148 # In addition to the regular property, for every [NamedConstructor] | |
149 # extended attribute on an interface, a corresponding property MUST exist | |
150 # on the ECMAScript global object. | |
151 if 'NamedConstructor' in extended_attributes: | |
152 named_constructor = extended_attributes['NamedConstructor'] | |
153 # Extract function name, namely everything before opening '(' | |
154 constructor_name = re.sub(r'\(.*', '', named_constructor) | |
155 # Note the reduplicated 'ConstructorConstructor' | |
156 attribute_string = 'attribute %sConstructorConstructor %s' % (interface_
name, constructor_name) | |
157 attributes_list.append(extended_string + attribute_string) | |
158 | |
159 return attributes_list | |
160 | |
161 | |
162 def generate_event_names_file(destination_filename, event_names, only_if_changed
=False): | |
163 source_dir, _ = os.path.split(os.getcwd()) | |
164 lines = [] | |
165 lines.append('namespace="Event"\n') | |
166 lines.append('\n') | |
167 for filename in event_names: | |
168 attributes = [] | |
169 extended_attributes = event_names[filename] | |
170 for key in ('ImplementedAs', 'Conditional', 'EnabledAtRuntime'): | |
171 suffix = '' | |
172 if key == 'EnabledAtRuntime': | |
173 suffix = 'Enabled' | |
174 if key in extended_attributes: | |
175 attributes.append('%s=%s%s' % (key, extended_attributes[key], su
ffix)) | |
176 refined_filename, _ = os.path.splitext(os.path.relpath(filename, source_
dir)) | |
177 lines.append('%s %s\n' % (refined_filename, ', '.join(attributes))) | |
178 write_file(lines, destination_filename, only_if_changed) | |
179 | |
180 | |
181 def generate_global_constructors_partial_interface(interface_name, destination_f
ilename, constructor_attributes_list, only_if_changed=False): | |
182 lines = [] | |
183 lines.append('partial interface %s {\n' % interface_name) | |
184 for constructor_attribute in constructor_attributes_list: | |
185 lines.append(' %s;\n' % constructor_attribute) | |
186 lines.append('};\n') | |
187 write_file(lines, destination_filename, only_if_changed) | |
188 | |
189 | |
190 def parse_idl_files(idl_files, window_constructors_filename, workerglobalscope_c
onstructors_filename, sharedworkerglobalscope_constructors_filename, dedicatedwo
rkerglobalscope_constructors_filename, event_names_file, only_if_changed=False): | |
191 interface_name_to_idl_file = {} | |
192 idl_file_to_interface_name = {} | |
193 supplemental_dependencies = {} | |
194 supplementals = {} | |
195 event_names = {} | |
196 window_constructor_attributes_list = [] | |
197 workerglobalscope_constructor_attributes_list = [] | |
198 sharedworkerglobalscope_constructor_attributes_list = [] | |
199 dedicatedworkerglobalscope_constructor_attributes_list = [] | |
200 | |
201 parent_interface = {} | |
202 interface_extended_attribute = {} | |
203 interface_to_file = {} | |
204 | |
205 # Populate interface_name_to_idl_file first | |
206 for idl_file_name in idl_files: | |
207 full_path = os.path.realpath(idl_file_name) | |
208 interface_name, _ = os.path.splitext(os.path.basename(idl_file_name)) | |
209 interface_name_to_idl_file[interface_name] = full_path | |
210 | |
211 for idl_file_name in idl_files: | |
212 full_path = os.path.realpath(idl_file_name) | |
213 interface_name, _ = os.path.splitext(os.path.basename(idl_file_name)) | |
214 idl_file_contents = get_file_contents(full_path) | |
215 | |
216 if interface_name == 'Event': | |
217 event_names[idl_file_name] = get_interface_extended_attributes_from_
idl(idl_file_contents) | |
218 else: | |
219 parent = get_parent_interface(idl_file_contents) | |
220 if parent: | |
221 parent_interface[interface_name] = parent | |
222 interface_extended_attribute[interface_name] = get_interface_ext
ended_attributes_from_idl(idl_file_contents) | |
223 interface_to_file[interface_name] = idl_file_name | |
224 | |
225 # Handle partial interfaces | |
226 partial_interface_name = get_partial_interface_name_from_idl(idl_file_co
ntents) | |
227 if partial_interface_name: | |
228 supplemental_dependencies[full_path] = [partial_interface_name] | |
229 continue | |
230 | |
231 # Parse 'identifier-A implements identifier-B; statements | |
232 implemented_interfaces = get_implemented_interfaces_from_idl(idl_file_co
ntents, interface_name) | |
233 for implemented_interface in implemented_interfaces: | |
234 assert implemented_interface in interface_name_to_idl_file, \ | |
235 "Could not find a the IDL file where the following implemented interface is defi
ned: %s" % implemented_interface | |
236 supplemental_dependencies.setdefault(interface_name_to_idl_file[impl
emented_interface], []).append(interface_name) | |
237 # Handle [NoInterfaceObject] | |
238 if not is_callback_interface_from_idl(idl_file_contents): | |
239 extended_attributes = get_interface_extended_attributes_from_idl(idl
_file_contents) | |
240 if 'NoInterfaceObject' not in extended_attributes: | |
241 global_contexts = extended_attributes.get('GlobalContext', 'Wind
ow').split('&') | |
242 constructor_list = generate_constructor_attribute_list(interface
_name, extended_attributes) | |
243 if 'Window' in global_contexts: | |
244 window_constructor_attributes_list.extend(constructor_list) | |
245 if 'WorkerGlobalScope' in global_contexts: | |
246 workerglobalscope_constructor_attributes_list.extend(constru
ctor_list) | |
247 if 'SharedWorkerGlobalScope' in global_contexts: | |
248 sharedworkerglobalscope_constructor_attributes_list.extend(c
onstructor_list) | |
249 if 'DedicatedWorkerGlobalScope' in global_contexts: | |
250 dedicatedworkerglobalscope_constructor_attributes_list.exten
d(constructor_list) | |
251 idl_file_to_interface_name[full_path] = interface_name | |
252 supplementals[full_path] = [] | |
253 | |
254 for interface in parent_interface: | |
255 parent = parent_interface[interface] | |
256 while parent in parent_interface: | |
257 parent = parent_interface[parent] | |
258 if parent == 'Event': | |
259 event_names[interface_to_file[interface]] = interface_extended_attri
bute[interface] | |
260 generate_event_names_file(event_names_file, event_names, only_if_changed=onl
y_if_changed) | |
261 | |
262 # Generate Global constructors | |
263 if 'Window' in interface_name_to_idl_file: | |
264 generate_global_constructors_partial_interface("Window", window_construc
tors_filename, window_constructor_attributes_list, only_if_changed=only_if_chang
ed) | |
265 supplemental_dependencies[window_constructors_filename] = ['Window'] | |
266 if 'WorkerGlobalScope' in interface_name_to_idl_file: | |
267 generate_global_constructors_partial_interface("WorkerGlobalScope", work
erglobalscope_constructors_filename, workerglobalscope_constructor_attributes_li
st, only_if_changed=only_if_changed) | |
268 supplemental_dependencies[workerglobalscope_constructors_filename] = ['W
orkerGlobalScope'] | |
269 if 'SharedWorkerGlobalScope' in interface_name_to_idl_file: | |
270 generate_global_constructors_partial_interface("SharedWorkerGlobalScope"
, sharedworkerglobalscope_constructors_filename, sharedworkerglobalscope_constru
ctor_attributes_list, only_if_changed=only_if_changed) | |
271 supplemental_dependencies[sharedworkerglobalscope_constructors_filename]
= ['SharedWorkerGlobalScope'] | |
272 if 'DedicatedWorkerGlobalScope' in interface_name_to_idl_file: | |
273 generate_global_constructors_partial_interface("DedicatedWorkerGlobalSco
pe", dedicatedworkerglobalscope_constructors_filename, dedicatedworkerglobalscop
e_constructor_attributes_list, only_if_changed=only_if_changed) | |
274 supplemental_dependencies[dedicatedworkerglobalscope_constructors_filena
me] = ['DedicatedWorkerGlobalScope'] | |
275 | |
276 # Resolve partial interfaces dependencies | |
277 for idl_file, base_files in supplemental_dependencies.iteritems(): | |
278 for base_file in base_files: | |
279 target_idl_file = interface_name_to_idl_file[base_file] | |
280 supplementals[target_idl_file].append(idl_file) | |
281 if idl_file in supplementals: | |
282 # Should never occur. Might be needed in corner cases. | |
283 del supplementals[idl_file] | |
284 return supplementals | |
285 | |
286 | |
287 def write_dependency_file(filename, supplementals, only_if_changed=False): | |
288 """Outputs the dependency file. | |
289 | |
290 The format of a supplemental dependency file: | |
291 | |
292 Window.idl P.idl Q.idl R.idl | |
293 Document.idl S.idl | |
294 Event.idl | |
295 ... | |
296 | |
297 The above indicates that: | |
298 Window.idl is supplemented by P.idl, Q.idl and R.idl, | |
299 Document.idl is supplemented by S.idl, and | |
300 Event.idl is supplemented by no IDLs. | |
301 | |
302 An IDL that supplements another IDL (e.g. P.idl) does not have its own | |
303 lines in the dependency file. | |
304 """ | |
305 lines = [] | |
306 for idl_file, supplemental_files in sorted(supplementals.iteritems()): | |
307 lines.append('%s %s\n' % (idl_file, ' '.join(supplemental_files))) | |
308 write_file(lines, filename, only_if_changed) | |
309 | |
310 | |
311 def main(): | |
312 options = parse_options() | |
313 idl_files = [] | |
314 with open(options.idl_files_list) as idl_files_list_file: | |
315 for line in idl_files_list_file: | |
316 idl_files.append(string.rstrip(line, '\n')) | |
317 resolved_supplementals = parse_idl_files(idl_files, options.window_construct
ors_file, options.workerglobalscope_constructors_file, options.sharedworkergloba
lscope_constructors_file, options.dedicatedworkerglobalscope_constructors_file,
options.event_names_file, only_if_changed=options.write_file_only_if_changed) | |
318 write_dependency_file(options.supplemental_dependency_file, resolved_supplem
entals, only_if_changed=options.write_file_only_if_changed) | |
319 | |
320 | |
321 if __name__ == '__main__': | |
322 main() | |
OLD | NEW |