Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(55)

Side by Side Diff: Source/bindings/scripts/compute_interfaces_info.py

Issue 173803006: Split generate_global_constructors.py out of compute_dependencies.py (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Cleaner Created 6 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 #!/usr/bin/python 1 #!/usr/bin/python
2 # 2 #
3 # Copyright (C) 2013 Google Inc. All rights reserved. 3 # Copyright (C) 2013 Google Inc. All rights reserved.
4 # 4 #
5 # Redistribution and use in source and binary forms, with or without 5 # Redistribution and use in source and binary forms, with or without
6 # modification, are permitted provided that the following conditions are 6 # modification, are permitted provided that the following conditions are
7 # met: 7 # met:
8 # 8 #
9 # * Redistributions of source code must retain the above copyright 9 # * Redistributions of source code must retain the above copyright
10 # notice, this list of conditions and the following disclaimer. 10 # notice, this list of conditions and the following disclaimer.
(...skipping 10 matching lines...) Expand all
21 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 30
31 """Compute global interface information, including public information, dependenc ies, and inheritance.
32
33 Computed data is stored in a global variable, |interfaces_info|, and written as
34 output (concretely, exported as a pickle). This is then used by the IDL compiler
35 itself, so it does not need to compute global information itself, and so that
36 inter-IDL dependencies are clear, since they are all computed here.
37
38 The |interfaces_info| pickle is a *global* dependency: any changes cause a full
39 rebuild. This is to avoid having to compute which public data is visible by
40 which IDL files on a file-by-file basis, which is very complex for little
41 benefit.
42 |interfaces_info| should thus only contain data about an interface that
43 contains paths or is needed by *other* interfaces, e.g., path data (to abstract
44 the compiler from OS-specific file paths) or public data (to avoid having to
45 read other interfaces unnecessarily).
46 It should *not* contain full information about an interface (e.g., all
47 extended attributes), as this would cause unnecessary rebuilds.
48
49 |interfaces_info| is a dict, keyed by |interface_name|.
50
51 Current keys are:
52 * dependencies:
53 'implements_interfaces': targets of 'implements' statements
54 'referenced_interfaces': reference interfaces that are introspected
55 (currently just targets of [PutForwards])
56
57 * inheritance:
58 'ancestors': all ancestor interfaces
59 'inherited_extended_attributes': inherited extended attributes
60 (all controlling memory management)
61
62 * public:
63 'is_callback_interface': bool, callback interface or not
64 'implemented_as': value of [ImplementedAs=...] on interface (C++ class name)
65
66 * paths:
67 'full_path': path to the IDL file, so can lookup an IDL by interface name
68 'include_path': path for use in C++ #include directives
69 'dependencies_full_paths': paths to dependencies (for merging into main)
70 'dependencies_include_paths': paths for use in C++ #include directives
71
72 Note that all of these are stable information, unlikely to change without
73 moving or deleting files (hence requiring a full rebuild anyway) or significant
74 code changes (for inherited extended attributes).
75
76 FIXME: also generates EventNames.in; factor out. http://crbug.com/341748
77 FIXME: also generates InterfaceDependencies.txt for Perl. http://crbug.com/2397 71
78
79 Design doc: http://www.chromium.org/developers/design-documents/idl-build
80 """
81
31 import optparse 82 import optparse
32 import os 83 import os
33 import cPickle as pickle
34 import posixpath 84 import posixpath
35 import re 85 import sys
36 import string 86
87 from utilities import get_file_contents, write_file, write_pickle_file, get_inte rface_extended_attributes_from_idl, is_callback_interface_from_idl, get_partial_ interface_name_from_idl, get_implemented_interfaces_from_idl, get_parent_interfa ce, get_put_forward_interfaces_from_idl
37 88
38 module_path = os.path.dirname(__file__) 89 module_path = os.path.dirname(__file__)
39 source_path = os.path.normpath(os.path.join(module_path, os.pardir, os.pardir)) 90 source_path = os.path.normpath(os.path.join(module_path, os.pardir, os.pardir))
40 91
41 INHERITED_EXTENDED_ATTRIBUTES = set([ 92 INHERITED_EXTENDED_ATTRIBUTES = set([
42 'ActiveDOMObject', 93 'ActiveDOMObject',
43 'DependentLifetime', 94 'DependentLifetime',
44 'WillBeGarbageCollected', 95 'WillBeGarbageCollected',
45 ]) 96 ])
46 97
47 98 # Main variable (filled in and exported)
48 # interfaces_info is *exported* (in a pickle), and should only contain data
49 # about an interface that contains paths or is needed by *other* interfaces,
50 # i.e., file layout data (to abstract the compiler from file paths) or
51 # public data (to avoid having to read other interfaces unnecessarily).
52 # It should *not* contain full information about an interface (e.g., all
53 # extended attributes), as this would cause unnecessary rebuilds.
54 interfaces_info = {} 99 interfaces_info = {}
55 100
56 # Auxiliary variables (not visible to future build steps) 101 # Auxiliary variables (not visible to future build steps)
57 partial_interface_files = {} 102 partial_interface_files = {}
58 parent_interfaces = {} 103 parent_interfaces = {}
59 extended_attributes_by_interface = {} # interface name -> extended attributes 104 extended_attributes_by_interface = {} # interface name -> extended attributes
60 105
61 106
62 class IdlBadFilenameError(Exception):
63 """Raised if an IDL filename disagrees with the interface name in the file." ""
64 pass
65
66
67 class IdlInterfaceFileNotFoundError(Exception): 107 class IdlInterfaceFileNotFoundError(Exception):
68 """Raised if the IDL file implementing an interface cannot be found.""" 108 """Raised if the IDL file implementing an interface cannot be found."""
69 pass 109 pass
70 110
71 111
72 def parse_options(): 112 def parse_options():
73 usage = 'Usage: %prog [options] [generated1.idl]...' 113 usage = 'Usage: %prog [options] [generated1.idl]...'
74 parser = optparse.OptionParser(usage=usage) 114 parser = optparse.OptionParser(usage=usage)
75 parser.add_option('--event-names-file', help='output file') 115 parser.add_option('--event-names-file', help='output file')
76 parser.add_option('--idl-files-list', help='file listing IDL files') 116 parser.add_option('--idl-files-list', help='file listing IDL files')
77 parser.add_option('--interface-dependencies-file', help='output file') 117 parser.add_option('--interface-dependencies-file', help='output file')
78 parser.add_option('--interfaces-info-file', help='output pickle file') 118 parser.add_option('--interfaces-info-file', help='output pickle file')
79 parser.add_option('--window-constructors-file', help='output file')
80 parser.add_option('--workerglobalscope-constructors-file', help='output file ')
81 parser.add_option('--sharedworkerglobalscope-constructors-file', help='outpu t file')
82 parser.add_option('--dedicatedworkerglobalscope-constructors-file', help='ou tput file')
83 parser.add_option('--serviceworkerglobalscope-constructors-file', help='outp ut file')
84 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') 119 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')
85 options, args = parser.parse_args() 120 options, args = parser.parse_args()
86 if options.event_names_file is None: 121 if options.event_names_file is None:
87 parser.error('Must specify an output file using --event-names-file.') 122 parser.error('Must specify an output file using --event-names-file.')
88 if options.interface_dependencies_file is None: 123 if options.interface_dependencies_file is None:
89 parser.error('Must specify an output file using --interface-dependencies -file.') 124 parser.error('Must specify an output file using --interface-dependencies -file.')
90 if options.interfaces_info_file is None: 125 if options.interfaces_info_file is None:
91 parser.error('Must specify an output file using --interfaces-info-file.' ) 126 parser.error('Must specify an output file using --interfaces-info-file.' )
92 if options.window_constructors_file is None:
93 parser.error('Must specify an output file using --window-constructors-fi le.')
94 if options.workerglobalscope_constructors_file is None:
95 parser.error('Must specify an output file using --workerglobalscope-cons tructors-file.')
96 if options.sharedworkerglobalscope_constructors_file is None:
97 parser.error('Must specify an output file using --sharedworkerglobalscop e-constructors-file.')
98 if options.dedicatedworkerglobalscope_constructors_file is None:
99 parser.error('Must specify an output file using --dedicatedworkerglobals cope-constructors-file.')
100 if options.serviceworkerglobalscope_constructors_file is None:
101 parser.error('Must specify an output file using --serviceworkerglobalsco pe-constructors-file.')
102 if options.idl_files_list is None: 127 if options.idl_files_list is None:
103 parser.error('Must specify a file listing IDL files using --idl-files-li st.') 128 parser.error('Must specify a file listing IDL files using --idl-files-li st.')
104 if options.write_file_only_if_changed is None: 129 if options.write_file_only_if_changed is None:
105 parser.error('Must specify whether file is only written if changed using --write-file-only-if-changed.') 130 parser.error('Must specify whether file is only written if changed using --write-file-only-if-changed.')
106 options.write_file_only_if_changed = bool(options.write_file_only_if_changed ) 131 options.write_file_only_if_changed = bool(options.write_file_only_if_changed )
107 return options, args 132 return options, args
108 133
109 134
110 ################################################################################ 135 ################################################################################
111 # Basic file reading/writing
112 ################################################################################
113
114 def get_file_contents(idl_filename):
115 with open(idl_filename) as idl_file:
116 lines = idl_file.readlines()
117 return ''.join(lines)
118
119
120 def write_file(new_lines, destination_filename, only_if_changed):
121 if only_if_changed and os.path.isfile(destination_filename):
122 with open(destination_filename) as destination_file:
123 if destination_file.readlines() == new_lines:
124 return
125 with open(destination_filename, 'w') as destination_file:
126 destination_file.write(''.join(new_lines))
127
128
129 def write_pickle_file(pickle_filename, data, only_if_changed):
130 if only_if_changed and os.path.isfile(pickle_filename):
131 with open(pickle_filename) as pickle_file:
132 if pickle.load(pickle_file) == data:
133 return
134 with open(pickle_filename, 'w') as pickle_file:
135 pickle.dump(data, pickle_file)
136
137
138 ################################################################################
139 # IDL parsing
140 #
141 # We use regular expressions for parsing; this is incorrect (Web IDL is not a
142 # regular language), but simple and sufficient in practice.
143 # Leading and trailing context (e.g. following '{') used to avoid false matches.
144 ################################################################################
145
146 def get_partial_interface_name_from_idl(file_contents):
147 match = re.search(r'partial\s+interface\s+(\w+)\s*{', file_contents)
148 return match and match.group(1)
149
150
151 # identifier-A implements identifier-B;
152 # http://www.w3.org/TR/WebIDL/#idl-implements-statements
153 def get_implemented_interfaces_from_idl(file_contents, interface_name):
154 def get_implemented(left_identifier, right_identifier):
155 # identifier-A must be the current interface
156 if left_identifier != interface_name:
157 raise IdlBadFilenameError("Identifier on the left of the 'implements ' statement should be %s in %s.idl, but found %s" % (interface_name, interface_n ame, left_identifier))
158 return right_identifier
159
160 implements_re = (r'^\s*'
161 r'(\w+)\s+'
162 r'implements\s+'
163 r'(\w+)\s*'
164 r';')
165 implements_matches = re.finditer(implements_re, file_contents, re.MULTILINE)
166 implements_pairs = [(match.group(1), match.group(2))
167 for match in implements_matches]
168 return [get_implemented(left, right) for left, right in implements_pairs]
169
170
171 def is_callback_interface_from_idl(file_contents):
172 match = re.search(r'callback\s+interface\s+\w+\s*{', file_contents)
173 return bool(match)
174
175
176 def get_parent_interface(file_contents):
177 match = re.search(r'interface\s+'
178 r'\w+\s*'
179 r':\s*(\w+)\s*'
180 r'{',
181 file_contents)
182 return match and match.group(1)
183
184
185 def get_interface_extended_attributes_from_idl(file_contents):
186 match = re.search(r'\[(.*)\]\s*'
187 r'((callback|partial)\s+)?'
188 r'(interface|exception)\s+'
189 r'\w+\s*'
190 r'(:\s*\w+\s*)?'
191 r'{',
192 file_contents, flags=re.DOTALL)
193 if not match:
194 return {}
195 # Strip comments
196 # re.compile needed b/c Python 2.6 doesn't support flags in re.sub
197 single_line_comment_re = re.compile(r'//.*$', flags=re.MULTILINE)
198 block_comment_re = re.compile(r'/\*.*?\*/', flags=re.MULTILINE | re.DOTALL)
199 extended_attributes_string = re.sub(single_line_comment_re, '', match.group( 1))
200 extended_attributes_string = re.sub(block_comment_re, '', extended_attribute s_string)
201 extended_attributes = {}
202 # FIXME: this splitting is WRONG: it fails on ExtendedAttributeArgList like
203 # 'NamedConstructor=Foo(a, b)'
204 parts = [extended_attribute.strip()
205 for extended_attribute in extended_attributes_string.split(',')
206 # Discard empty parts, which may exist due to trailing comma
207 if extended_attribute.strip()]
208 for part in parts:
209 name, _, value = map(string.strip, part.partition('='))
210 extended_attributes[name] = value
211 return extended_attributes
212
213
214 def get_put_forward_interfaces_from_idl(file_contents):
215 put_forwards_pattern = (r'\[[^\]]*PutForwards=[^\]]*\]\s+'
216 r'readonly\s+'
217 r'attribute\s+'
218 r'(\w+)')
219 return sorted(set(match.group(1)
220 for match in re.finditer(put_forwards_pattern,
221 file_contents,
222 flags=re.DOTALL)))
223
224
225 ################################################################################
226 # Write files 136 # Write files
227 ################################################################################ 137 ################################################################################
228 138
229 def write_dependencies_file(dependencies_filename, only_if_changed): 139 def write_dependencies_file(dependencies_filename, only_if_changed):
230 """Write the interface dependencies file. 140 """Write the interface dependencies file.
231 141
232 The format is as follows: 142 The format is as follows:
233 143
234 Document.idl P.idl 144 Document.idl P.idl
235 Event.idl 145 Event.idl
236 Window.idl Q.idl R.idl S.idl 146 Window.idl Q.idl R.idl S.idl
237 ... 147 ...
238 148
239 The above indicates that: 149 The above indicates that:
240 Document.idl depends on P.idl, 150 Document.idl depends on P.idl,
241 Event.idl depends on no other IDL files, and 151 Event.idl depends on no other IDL files, and
242 Window.idl depends on Q.idl, R.idl, and S.idl. 152 Window.idl depends on Q.idl, R.idl, and S.idl.
243 153
244 An IDL that is a dependency of another IDL (e.g. P.idl) does not have its 154 An IDL that is a dependency of another IDL (e.g. P.idl) does not have its
245 own line in the dependency file. 155 own line in the dependency file.
246 """ 156 """
247 # FIXME: remove text format once Perl gone (Python uses pickle) 157 # FIXME: remove this file once Perl is gone http://crbug.com/239771
248 dependencies_list = sorted( 158 dependencies_list = sorted(
249 (interface_info['full_path'], sorted(interface_info['dependencies_full_p aths'])) 159 (interface_info['full_path'], sorted(interface_info['dependencies_full_p aths']))
250 for interface_info in interfaces_info.values()) 160 for interface_info in interfaces_info.values())
251 lines = ['%s %s\n' % (idl_file, ' '.join(dependency_files)) 161 lines = ['%s %s\n' % (idl_file, ' '.join(dependency_files))
252 for idl_file, dependency_files in dependencies_list] 162 for idl_file, dependency_files in dependencies_list]
253 write_file(lines, dependencies_filename, only_if_changed) 163 write_file(lines, dependencies_filename, only_if_changed)
254 164
255 165
256 def write_event_names_file(destination_filename, only_if_changed): 166 def write_event_names_file(destination_filename, only_if_changed):
257 # Generate event names for all interfaces that inherit from Event, 167 # Generate event names for all interfaces that inherit from Event,
258 # including Event itself. 168 # including Event itself.
169 # FIXME: factor out. http://crbug.com/341748
259 event_names = set( 170 event_names = set(
260 interface_name 171 interface_name
261 for interface_name, interface_info in interfaces_info.iteritems() 172 for interface_name, interface_info in interfaces_info.iteritems()
262 if (interface_name == 'Event' or 173 if (interface_name == 'Event' or
263 ('ancestors' in interface_info and 174 ('ancestors' in interface_info and
264 interface_info['ancestors'][-1] == 'Event'))) 175 interface_info['ancestors'][-1] == 'Event')))
265 176
266 def extended_attribute_string(name): 177 def extended_attribute_string(name):
267 value = extended_attributes[name] 178 value = extended_attributes[name]
268 if name == 'RuntimeEnabled': 179 if name == 'RuntimeEnabled':
(...skipping 12 matching lines...) Expand all
281 refined_filename, _ = os.path.splitext(os.path.relpath(filename, source_ dir)) 192 refined_filename, _ = os.path.splitext(os.path.relpath(filename, source_ dir))
282 refined_filename = refined_filename.replace(os.sep, posixpath.sep) 193 refined_filename = refined_filename.replace(os.sep, posixpath.sep)
283 extended_attributes_list = [ 194 extended_attributes_list = [
284 extended_attribute_string(name) 195 extended_attribute_string(name)
285 for name in 'Conditional', 'ImplementedAs', 'RuntimeEnabled' 196 for name in 'Conditional', 'ImplementedAs', 'RuntimeEnabled'
286 if name in extended_attributes] 197 if name in extended_attributes]
287 lines.append('%s %s\n' % (refined_filename, ', '.join(extended_attribute s_list))) 198 lines.append('%s %s\n' % (refined_filename, ', '.join(extended_attribute s_list)))
288 write_file(lines, destination_filename, only_if_changed) 199 write_file(lines, destination_filename, only_if_changed)
289 200
290 201
291 def write_global_constructors_partial_interface(interface_name, destination_file name, constructor_attributes_list, only_if_changed):
292 lines = (['partial interface %s {\n' % interface_name] +
293 [' %s;\n' % constructor_attribute
294 for constructor_attribute in sorted(constructor_attributes_list)] +
295 ['};\n'])
296 write_file(lines, destination_filename, only_if_changed)
297
298
299 ################################################################################ 202 ################################################################################
300 # Dependency resolution 203 # Computations
301 ################################################################################ 204 ################################################################################
302 205
303 def include_path(idl_filename, implemented_as=None): 206 def include_path(idl_filename, implemented_as=None):
304 """Returns relative path to header file in POSIX format; used in includes. 207 """Returns relative path to header file in POSIX format; used in includes.
305 208
306 POSIX format is used for consistency of output, so reference tests are 209 POSIX format is used for consistency of output, so reference tests are
307 platform-independent. 210 platform-independent.
308 """ 211 """
309 relative_path_local = os.path.relpath(idl_filename, source_path) 212 relative_path_local = os.path.relpath(idl_filename, source_path)
310 relative_dir_local = os.path.dirname(relative_path_local) 213 relative_dir_local = os.path.dirname(relative_path_local)
311 relative_dir_posix = relative_dir_local.replace(os.path.sep, posixpath.sep) 214 relative_dir_posix = relative_dir_local.replace(os.path.sep, posixpath.sep)
312 215
313 idl_file_basename, _ = os.path.splitext(os.path.basename(idl_filename)) 216 idl_file_basename, _ = os.path.splitext(os.path.basename(idl_filename))
314 cpp_class_name = implemented_as or idl_file_basename 217 cpp_class_name = implemented_as or idl_file_basename
315 218
316 return posixpath.join(relative_dir_posix, cpp_class_name + '.h') 219 return posixpath.join(relative_dir_posix, cpp_class_name + '.h')
317 220
318 221
319 def add_paths_to_partials_dict(partial_interface_name, full_path, this_include_p ath=None): 222 def add_paths_to_partials_dict(partial_interface_name, full_path, this_include_p ath=None):
320 paths_dict = partial_interface_files.setdefault(partial_interface_name, 223 paths_dict = partial_interface_files.setdefault(partial_interface_name,
321 {'full_paths': [], 224 {'full_paths': [],
322 'include_paths': []}) 225 'include_paths': []})
323 paths_dict['full_paths'].append(full_path) 226 paths_dict['full_paths'].append(full_path)
324 if this_include_path: 227 if this_include_path:
325 paths_dict['include_paths'].append(this_include_path) 228 paths_dict['include_paths'].append(this_include_path)
326 229
327 230
328 def generate_dependencies(idl_filename): 231 def compute_individual_info(idl_filename):
329 """Compute dependencies for IDL file, returning True if main (non-partial) i nterface"""
330 full_path = os.path.realpath(idl_filename) 232 full_path = os.path.realpath(idl_filename)
331 idl_file_contents = get_file_contents(full_path) 233 idl_file_contents = get_file_contents(full_path)
332 234
333 extended_attributes = get_interface_extended_attributes_from_idl(idl_file_co ntents) 235 extended_attributes = get_interface_extended_attributes_from_idl(idl_file_co ntents)
334 implemented_as = extended_attributes.get('ImplementedAs') 236 implemented_as = extended_attributes.get('ImplementedAs')
335 # FIXME: remove [NoHeader] once switch to Python 237 # FIXME: remove [NoHeader] once switch to Python
336 this_include_path = (include_path(idl_filename, implemented_as) 238 this_include_path = (include_path(idl_filename, implemented_as)
337 if 'NoHeader' not in extended_attributes else None) 239 if 'NoHeader' not in extended_attributes else None)
338 240
339 # Handle partial interfaces 241 # Handle partial interfaces
340 partial_interface_name = get_partial_interface_name_from_idl(idl_file_conten ts) 242 partial_interface_name = get_partial_interface_name_from_idl(idl_file_conten ts)
341 if partial_interface_name: 243 if partial_interface_name:
342 add_paths_to_partials_dict(partial_interface_name, full_path, this_inclu de_path) 244 add_paths_to_partials_dict(partial_interface_name, full_path, this_inclu de_path)
343 return False 245 return
344 246
345 # If not a partial interface, the basename is the interface name 247 # If not a partial interface, the basename is the interface name
346 interface_name, _ = os.path.splitext(os.path.basename(idl_filename)) 248 interface_name, _ = os.path.splitext(os.path.basename(idl_filename))
347 249
348 interfaces_info[interface_name] = { 250 interfaces_info[interface_name] = {
349 'full_path': full_path, 251 'full_path': full_path,
350 'implements_interfaces': get_implemented_interfaces_from_idl(idl_file_co ntents, interface_name), 252 'implements_interfaces': get_implemented_interfaces_from_idl(idl_file_co ntents, interface_name),
351 'is_callback_interface': is_callback_interface_from_idl(idl_file_content s), 253 'is_callback_interface': is_callback_interface_from_idl(idl_file_content s),
352 # Interfaces that are referenced (used as types) and that we introspect 254 # Interfaces that are referenced (used as types) and that we introspect
353 # during code generation (beyond interface-level data ([ImplementedAs], 255 # during code generation (beyond interface-level data ([ImplementedAs],
354 # is_callback_interface, ancestors, and inherited extended attributes): 256 # is_callback_interface, ancestors, and inherited extended attributes):
355 # deep dependencies. 257 # deep dependencies.
356 # These cause rebuilds of referrers, due to the dependency, so these 258 # These cause rebuilds of referrers, due to the dependency, so these
357 # should be minimized; currently only targets of [PutForwards]. 259 # should be minimized; currently only targets of [PutForwards].
358 'referenced_interfaces': get_put_forward_interfaces_from_idl(idl_file_co ntents), 260 'referenced_interfaces': get_put_forward_interfaces_from_idl(idl_file_co ntents),
359 } 261 }
360 if this_include_path: 262 if this_include_path:
361 interfaces_info[interface_name]['include_path'] = this_include_path 263 interfaces_info[interface_name]['include_path'] = this_include_path
362 if implemented_as: 264 if implemented_as:
363 interfaces_info[interface_name]['implemented_as'] = implemented_as 265 interfaces_info[interface_name]['implemented_as'] = implemented_as
364 266
365 return True 267 # Record auxiliary information
366
367
368 def generate_constructor_attribute_list(interface_name, extended_attributes):
369 extended_attributes_list = [
370 name + '=' + extended_attributes[name]
371 for name in 'Conditional', 'PerContextEnabled', 'RuntimeEnabled'
372 if name in extended_attributes]
373 if extended_attributes_list:
374 extended_string = '[%s] ' % ', '.join(extended_attributes_list)
375 else:
376 extended_string = ''
377
378 attribute_string = 'attribute {interface_name}Constructor {interface_name}'. format(interface_name=interface_name)
379 attributes_list = [extended_string + attribute_string]
380
381 # In addition to the regular property, for every [NamedConstructor]
382 # extended attribute on an interface, a corresponding property MUST exist
383 # on the ECMAScript global object.
384 if 'NamedConstructor' in extended_attributes:
385 named_constructor = extended_attributes['NamedConstructor']
386 # Extract function name, namely everything before opening '('
387 constructor_name = re.sub(r'\(.*', '', named_constructor)
388 # Note the reduplicated 'ConstructorConstructor'
389 attribute_string = 'attribute %sConstructorConstructor %s' % (interface_ name, constructor_name)
390 attributes_list.append(extended_string + attribute_string)
391
392 return attributes_list
393
394
395 def record_global_constructors_and_extended_attributes(idl_filename, global_cons tructors):
396 interface_name, _ = os.path.splitext(os.path.basename(idl_filename))
397 full_path = os.path.realpath(idl_filename)
398 idl_file_contents = get_file_contents(full_path)
399 extended_attributes = get_interface_extended_attributes_from_idl(idl_file_co ntents)
400
401 # Record extended attributes
402 extended_attributes_by_interface[interface_name] = extended_attributes 268 extended_attributes_by_interface[interface_name] = extended_attributes
403
404 # Record global constructors
405 if (not is_callback_interface_from_idl(idl_file_contents) and
406 'NoInterfaceObject' not in extended_attributes):
407 global_contexts = extended_attributes.get('GlobalContext', 'Window').spl it('&')
408 new_constructor_list = generate_constructor_attribute_list(interface_nam e, extended_attributes)
409 for global_object in global_contexts:
410 global_constructors[global_object].extend(new_constructor_list)
411
412 # Record parents
413 parent = get_parent_interface(idl_file_contents) 269 parent = get_parent_interface(idl_file_contents)
414 if parent: 270 if parent:
415 parent_interfaces[interface_name] = parent 271 parent_interfaces[interface_name] = parent
416 272
417 273
418 def record_extended_attributes(idl_filename): 274 def compute_inheritance_info(interface_name):
419 interface_name, _ = os.path.splitext(os.path.basename(idl_filename)) 275 """Computes inheritance information, namely ancestors and inherited extended attributes."""
420 full_path = os.path.realpath(idl_filename)
421 idl_file_contents = get_file_contents(full_path)
422 extended_attributes = get_interface_extended_attributes_from_idl(idl_file_co ntents)
423 extended_attributes_by_interface[interface_name] = extended_attributes
424
425
426 def generate_ancestors_and_inherited_extended_attributes(interface_name):
427 interface_info = interfaces_info[interface_name] 276 interface_info = interfaces_info[interface_name]
428 interface_extended_attributes = extended_attributes_by_interface[interface_n ame] 277 interface_extended_attributes = extended_attributes_by_interface[interface_n ame]
429 inherited_extended_attributes = dict( 278 inherited_extended_attributes = dict(
430 (key, value) 279 (key, value)
431 for key, value in interface_extended_attributes.iteritems() 280 for key, value in interface_extended_attributes.iteritems()
432 if key in INHERITED_EXTENDED_ATTRIBUTES) 281 if key in INHERITED_EXTENDED_ATTRIBUTES)
433 282
434 def generate_ancestors(interface_name): 283 def generate_ancestors(interface_name):
435 while interface_name in parent_interfaces: 284 while interface_name in parent_interfaces:
436 interface_name = parent_interfaces[interface_name] 285 interface_name = parent_interfaces[interface_name]
437 yield interface_name 286 yield interface_name
438 287
439 ancestors = list(generate_ancestors(interface_name)) 288 ancestors = list(generate_ancestors(interface_name))
440 if not ancestors: 289 if not ancestors:
441 if inherited_extended_attributes: 290 if inherited_extended_attributes:
442 interface_info['inherited_extended_attributes'] = inherited_extended _attributes 291 interface_info['inherited_extended_attributes'] = inherited_extended _attributes
443 return 292 return
444 293
445 interface_info['ancestors'] = ancestors 294 interface_info['ancestors'] = ancestors
446 for ancestor in ancestors: 295 for ancestor in ancestors:
447 # Extended attributes are missing if an ancestor is an interface that 296 # Extended attributes are missing if an ancestor is an interface that
448 # we're not processing, notably real IDL files if only processing test 297 # we're not processing, namely real IDL files if only processing test
449 # IDL files, or generated support files. 298 # IDL files.
450 ancestor_extended_attributes = extended_attributes_by_interface.get(ance stor, {}) 299 ancestor_extended_attributes = extended_attributes_by_interface.get(ance stor, {})
451 inherited_extended_attributes.update(dict( 300 inherited_extended_attributes.update(dict(
452 (key, value) 301 (key, value)
453 for key, value in ancestor_extended_attributes.iteritems() 302 for key, value in ancestor_extended_attributes.iteritems()
454 if key in INHERITED_EXTENDED_ATTRIBUTES)) 303 if key in INHERITED_EXTENDED_ATTRIBUTES))
455 if inherited_extended_attributes: 304 if inherited_extended_attributes:
456 interface_info['inherited_extended_attributes'] = inherited_extended_att ributes 305 interface_info['inherited_extended_attributes'] = inherited_extended_att ributes
457 306
458 307
459 def parse_idl_files(idl_files, global_constructors_filenames): 308 def compute_interfaces_info(idl_files):
460 """Compute dependencies between IDL files, and return constructors on global objects. 309 """Compute information about IDL files.
461 310
462 Primary effect is computing info about main interfaces, stored in global 311 Information is stored in global interfaces_info.
463 interfaces_info. 312 """
464 The keys are the interfaces for which bindings are generated; 313 # Compute information for individual files
465 this does not include interfaces implemented by another interface. 314 for idl_filename in idl_files:
315 compute_individual_info(idl_filename)
466 316
467 Returns: 317 # Once all individual files handled, can compute inheritance information
468 global_constructors:
469 dict of global objects -> list of constructors on that object
470 """
471 global_constructors = dict([
472 (global_object, [])
473 for global_object in global_constructors_filenames])
474
475 # Generate dependencies, and (for main IDL files), record
476 # global_constructors and extended_attributes_by_interface.
477 for idl_filename in idl_files:
478 # Test skips partial interfaces
479 if generate_dependencies(idl_filename):
480 record_global_constructors_and_extended_attributes(idl_filename, glo bal_constructors)
481
482 for interface_name in interfaces_info: 318 for interface_name in interfaces_info:
483 generate_ancestors_and_inherited_extended_attributes(interface_name) 319 compute_inheritance_info(interface_name)
484
485 # Add constructors on global objects to partial interfaces
486 # These are all partial interfaces, but the files are dynamically generated,
487 # so they need to be handled separately from static partial interfaces.
488 for global_object, constructor_filename in global_constructors_filenames.ite ritems():
489 if global_object in interfaces_info:
490 # No include path needed, as already included in the header file
491 add_paths_to_partials_dict(global_object, constructor_filename)
492 320
493 # An IDL file's dependencies are partial interface files that extend it, 321 # An IDL file's dependencies are partial interface files that extend it,
494 # and files for other interfaces that this interfaces implements. 322 # and files for other interfaces that this interfaces implements.
495 for interface_name, interface_info in interfaces_info.iteritems(): 323 for interface_name, interface_info in interfaces_info.iteritems():
496 partial_interfaces_full_paths, partial_interfaces_include_paths = ( 324 partial_interfaces_full_paths, partial_interfaces_include_paths = (
497 (partial_interface_files[interface_name]['full_paths'], 325 (partial_interface_files[interface_name]['full_paths'],
498 partial_interface_files[interface_name]['include_paths']) 326 partial_interface_files[interface_name]['include_paths'])
499 if interface_name in partial_interface_files else ([], [])) 327 if interface_name in partial_interface_files else ([], []))
500 328
501 implemented_interfaces = interface_info['implements_interfaces'] 329 implemented_interfaces = interface_info['implements_interfaces']
502 try: 330 try:
503 implemented_interfaces_full_paths = [ 331 implemented_interfaces_full_paths = [
504 interfaces_info[interface]['full_path'] 332 interfaces_info[interface]['full_path']
505 for interface in implemented_interfaces] 333 for interface in implemented_interfaces]
506 implemented_interfaces_include_paths = [ 334 implemented_interfaces_include_paths = [
507 interfaces_info[interface]['include_path'] 335 interfaces_info[interface]['include_path']
508 for interface in implemented_interfaces 336 for interface in implemented_interfaces
509 if 'include_path' in interfaces_info[interface]] 337 if 'include_path' in interfaces_info[interface]]
510 except KeyError as key_name: 338 except KeyError as key_name:
511 raise IdlInterfaceFileNotFoundError('Could not find the IDL file whe re the following implemented interface is defined: %s' % key_name) 339 raise IdlInterfaceFileNotFoundError('Could not find the IDL file whe re the following implemented interface is defined: %s' % key_name)
512 340
513 interface_info['dependencies_full_paths'] = ( 341 interface_info['dependencies_full_paths'] = (
514 partial_interfaces_full_paths + 342 partial_interfaces_full_paths +
515 implemented_interfaces_full_paths) 343 implemented_interfaces_full_paths)
516 interface_info['dependencies_include_paths'] = ( 344 interface_info['dependencies_include_paths'] = (
517 partial_interfaces_include_paths + 345 partial_interfaces_include_paths +
518 implemented_interfaces_include_paths) 346 implemented_interfaces_include_paths)
519 347
520 return global_constructors
521
522 348
523 ################################################################################ 349 ################################################################################
524 350
525 def main(): 351 def main():
526 options, args = parse_options() 352 options, args = parse_options()
527 353
528 # Static IDL files are passed in a file (generated at GYP time), due to OS 354 # Static IDL files are passed in a file (generated at GYP time), due to OS
529 # command line length limits 355 # command line length limits
530 with open(options.idl_files_list) as idl_files_list: 356 with open(options.idl_files_list) as idl_files_list:
531 idl_files = [line.rstrip('\n') for line in idl_files_list] 357 idl_files = [line.rstrip('\n') for line in idl_files_list]
532 # Generated IDL files are passed at the command line, since these are in the 358 # Generated IDL files are passed at the command line, since these are in the
533 # build directory, which is determined at build time, not GYP time, so these 359 # build directory, which is determined at build time, not GYP time, so these
534 # cannot be included in the file listing static files 360 # cannot be included in the file listing static files
535 idl_files.extend(args) 361 idl_files.extend(args)
536 362
537 only_if_changed = options.write_file_only_if_changed 363 only_if_changed = options.write_file_only_if_changed
538 global_constructors_filenames = { 364 compute_interfaces_info(idl_files)
539 'Window': options.window_constructors_file, 365 write_pickle_file(options.interfaces_info_file, interfaces_info, only_if_cha nged)
540 'WorkerGlobalScope': options.workerglobalscope_constructors_file,
541 'SharedWorkerGlobalScope': options.sharedworkerglobalscope_constructors_ file,
542 'DedicatedWorkerGlobalScope': options.dedicatedworkerglobalscope_constru ctors_file,
543 'ServiceWorkerGlobalScope': options.serviceworkerglobalscope_constructor s_file,
544 }
545
546 global_constructors = parse_idl_files(idl_files, global_constructors_filenam es)
547
548 write_dependencies_file(options.interface_dependencies_file, only_if_changed ) 366 write_dependencies_file(options.interface_dependencies_file, only_if_changed )
549 write_pickle_file(options.interfaces_info_file, interfaces_info, only_if_cha nged)
550 for interface_name, filename in global_constructors_filenames.iteritems():
551 if interface_name in interfaces_info:
552 write_global_constructors_partial_interface(interface_name, filename , global_constructors[interface_name], only_if_changed)
553 write_event_names_file(options.event_names_file, only_if_changed) 367 write_event_names_file(options.event_names_file, only_if_changed)
554 368
555 369
556 if __name__ == '__main__': 370 if __name__ == '__main__':
557 main() 371 sys.exit(main())
OLDNEW
« no previous file with comments | « Source/bindings/scripts/compute_dependencies.py ('k') | Source/bindings/scripts/generate_global_constructors.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698