OLD | NEW |
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 Loading... |
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 Loading... |
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()) |
OLD | NEW |