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 posixpath | 84 import posixpath |
| 85 import sys |
34 | 86 |
35 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 | 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 |
36 | 88 |
37 module_path = os.path.dirname(__file__) | 89 module_path = os.path.dirname(__file__) |
38 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)) |
39 | 91 |
40 INHERITED_EXTENDED_ATTRIBUTES = set([ | 92 INHERITED_EXTENDED_ATTRIBUTES = set([ |
41 'ActiveDOMObject', | 93 'ActiveDOMObject', |
42 'DependentLifetime', | 94 'DependentLifetime', |
43 'WillBeGarbageCollected', | 95 'WillBeGarbageCollected', |
44 ]) | 96 ]) |
45 | 97 |
46 | 98 # Main variable (filled in and exported) |
47 # interfaces_info is *exported* (in a pickle), and should only contain data | |
48 # about an interface that contains paths or is needed by *other* interfaces, | |
49 # i.e., file layout data (to abstract the compiler from file paths) or | |
50 # public data (to avoid having to read other interfaces unnecessarily). | |
51 # It should *not* contain full information about an interface (e.g., all | |
52 # extended attributes), as this would cause unnecessary rebuilds. | |
53 interfaces_info = {} | 99 interfaces_info = {} |
54 | 100 |
55 # Auxiliary variables (not visible to future build steps) | 101 # Auxiliary variables (not visible to future build steps) |
56 partial_interface_files = {} | 102 partial_interface_files = {} |
57 parent_interfaces = {} | 103 parent_interfaces = {} |
58 extended_attributes_by_interface = {} # interface name -> extended attributes | 104 extended_attributes_by_interface = {} # interface name -> extended attributes |
59 | 105 |
60 | 106 |
61 class IdlInterfaceFileNotFoundError(Exception): | 107 class IdlInterfaceFileNotFoundError(Exception): |
62 """Raised if the IDL file implementing an interface cannot be found.""" | 108 """Raised if the IDL file implementing an interface cannot be found.""" |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
101 ... | 147 ... |
102 | 148 |
103 The above indicates that: | 149 The above indicates that: |
104 Document.idl depends on P.idl, | 150 Document.idl depends on P.idl, |
105 Event.idl depends on no other IDL files, and | 151 Event.idl depends on no other IDL files, and |
106 Window.idl depends on Q.idl, R.idl, and S.idl. | 152 Window.idl depends on Q.idl, R.idl, and S.idl. |
107 | 153 |
108 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 |
109 own line in the dependency file. | 155 own line in the dependency file. |
110 """ | 156 """ |
111 # FIXME: remove text format once Perl gone (Python uses pickle) | 157 # FIXME: remove this file once Perl is gone http://crbug.com/239771 |
112 dependencies_list = sorted( | 158 dependencies_list = sorted( |
113 (interface_info['full_path'], sorted(interface_info['dependencies_full_p
aths'])) | 159 (interface_info['full_path'], sorted(interface_info['dependencies_full_p
aths'])) |
114 for interface_info in interfaces_info.values()) | 160 for interface_info in interfaces_info.values()) |
115 lines = ['%s %s\n' % (idl_file, ' '.join(dependency_files)) | 161 lines = ['%s %s\n' % (idl_file, ' '.join(dependency_files)) |
116 for idl_file, dependency_files in dependencies_list] | 162 for idl_file, dependency_files in dependencies_list] |
117 write_file(lines, dependencies_filename, only_if_changed) | 163 write_file(lines, dependencies_filename, only_if_changed) |
118 | 164 |
119 | 165 |
120 def write_event_names_file(destination_filename, only_if_changed): | 166 def write_event_names_file(destination_filename, only_if_changed): |
121 # Generate event names for all interfaces that inherit from Event, | 167 # Generate event names for all interfaces that inherit from Event, |
122 # including Event itself. | 168 # including Event itself. |
| 169 # FIXME: factor out. http://crbug.com/341748 |
123 event_names = set( | 170 event_names = set( |
124 interface_name | 171 interface_name |
125 for interface_name, interface_info in interfaces_info.iteritems() | 172 for interface_name, interface_info in interfaces_info.iteritems() |
126 if (interface_name == 'Event' or | 173 if (interface_name == 'Event' or |
127 ('ancestors' in interface_info and | 174 ('ancestors' in interface_info and |
128 interface_info['ancestors'][-1] == 'Event'))) | 175 interface_info['ancestors'][-1] == 'Event'))) |
129 | 176 |
130 def extended_attribute_string(name): | 177 def extended_attribute_string(name): |
131 value = extended_attributes[name] | 178 value = extended_attributes[name] |
132 if name == 'RuntimeEnabled': | 179 if name == 'RuntimeEnabled': |
(...skipping 13 matching lines...) Expand all Loading... |
146 refined_filename = refined_filename.replace(os.sep, posixpath.sep) | 193 refined_filename = refined_filename.replace(os.sep, posixpath.sep) |
147 extended_attributes_list = [ | 194 extended_attributes_list = [ |
148 extended_attribute_string(name) | 195 extended_attribute_string(name) |
149 for name in 'Conditional', 'ImplementedAs', 'RuntimeEnabled' | 196 for name in 'Conditional', 'ImplementedAs', 'RuntimeEnabled' |
150 if name in extended_attributes] | 197 if name in extended_attributes] |
151 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))) |
152 write_file(lines, destination_filename, only_if_changed) | 199 write_file(lines, destination_filename, only_if_changed) |
153 | 200 |
154 | 201 |
155 ################################################################################ | 202 ################################################################################ |
156 # Dependency resolution | 203 # Computations |
157 ################################################################################ | 204 ################################################################################ |
158 | 205 |
159 def include_path(idl_filename, implemented_as=None): | 206 def include_path(idl_filename, implemented_as=None): |
160 """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. |
161 | 208 |
162 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 |
163 platform-independent. | 210 platform-independent. |
164 """ | 211 """ |
165 relative_path_local = os.path.relpath(idl_filename, source_path) | 212 relative_path_local = os.path.relpath(idl_filename, source_path) |
166 relative_dir_local = os.path.dirname(relative_path_local) | 213 relative_dir_local = os.path.dirname(relative_path_local) |
167 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) |
168 | 215 |
169 idl_file_basename, _ = os.path.splitext(os.path.basename(idl_filename)) | 216 idl_file_basename, _ = os.path.splitext(os.path.basename(idl_filename)) |
170 cpp_class_name = implemented_as or idl_file_basename | 217 cpp_class_name = implemented_as or idl_file_basename |
171 | 218 |
172 return posixpath.join(relative_dir_posix, cpp_class_name + '.h') | 219 return posixpath.join(relative_dir_posix, cpp_class_name + '.h') |
173 | 220 |
174 | 221 |
175 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): |
176 paths_dict = partial_interface_files.setdefault(partial_interface_name, | 223 paths_dict = partial_interface_files.setdefault(partial_interface_name, |
177 {'full_paths': [], | 224 {'full_paths': [], |
178 'include_paths': []}) | 225 'include_paths': []}) |
179 paths_dict['full_paths'].append(full_path) | 226 paths_dict['full_paths'].append(full_path) |
180 if this_include_path: | 227 if this_include_path: |
181 paths_dict['include_paths'].append(this_include_path) | 228 paths_dict['include_paths'].append(this_include_path) |
182 | 229 |
183 | 230 |
184 def generate_dependencies(idl_filename): | 231 def compute_individual_info(idl_filename): |
185 """Compute dependencies for IDL file, returning True if main (non-partial) i
nterface""" | |
186 full_path = os.path.realpath(idl_filename) | 232 full_path = os.path.realpath(idl_filename) |
187 idl_file_contents = get_file_contents(full_path) | 233 idl_file_contents = get_file_contents(full_path) |
188 | 234 |
189 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) |
190 implemented_as = extended_attributes.get('ImplementedAs') | 236 implemented_as = extended_attributes.get('ImplementedAs') |
191 # FIXME: remove [NoHeader] once switch to Python | 237 # FIXME: remove [NoHeader] once switch to Python |
192 this_include_path = (include_path(idl_filename, implemented_as) | 238 this_include_path = (include_path(idl_filename, implemented_as) |
193 if 'NoHeader' not in extended_attributes else None) | 239 if 'NoHeader' not in extended_attributes else None) |
194 | 240 |
195 # Handle partial interfaces | 241 # Handle partial interfaces |
(...skipping 15 matching lines...) Expand all Loading... |
211 # deep dependencies. | 257 # deep dependencies. |
212 # These cause rebuilds of referrers, due to the dependency, so these | 258 # These cause rebuilds of referrers, due to the dependency, so these |
213 # should be minimized; currently only targets of [PutForwards]. | 259 # should be minimized; currently only targets of [PutForwards]. |
214 '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), |
215 } | 261 } |
216 if this_include_path: | 262 if this_include_path: |
217 interfaces_info[interface_name]['include_path'] = this_include_path | 263 interfaces_info[interface_name]['include_path'] = this_include_path |
218 if implemented_as: | 264 if implemented_as: |
219 interfaces_info[interface_name]['implemented_as'] = implemented_as | 265 interfaces_info[interface_name]['implemented_as'] = implemented_as |
220 | 266 |
221 # Record extended attributes | 267 # Record auxiliary information |
222 extended_attributes_by_interface[interface_name] = extended_attributes | 268 extended_attributes_by_interface[interface_name] = extended_attributes |
223 | |
224 # Record parents | |
225 parent = get_parent_interface(idl_file_contents) | 269 parent = get_parent_interface(idl_file_contents) |
226 if parent: | 270 if parent: |
227 parent_interfaces[interface_name] = parent | 271 parent_interfaces[interface_name] = parent |
228 | 272 |
229 | 273 |
230 def generate_ancestors_and_inherited_extended_attributes(interface_name): | 274 def compute_inheritance_info(interface_name): |
| 275 """Computes inheritance information, namely ancestors and inherited extended
attributes.""" |
231 interface_info = interfaces_info[interface_name] | 276 interface_info = interfaces_info[interface_name] |
232 interface_extended_attributes = extended_attributes_by_interface[interface_n
ame] | 277 interface_extended_attributes = extended_attributes_by_interface[interface_n
ame] |
233 inherited_extended_attributes = dict( | 278 inherited_extended_attributes = dict( |
234 (key, value) | 279 (key, value) |
235 for key, value in interface_extended_attributes.iteritems() | 280 for key, value in interface_extended_attributes.iteritems() |
236 if key in INHERITED_EXTENDED_ATTRIBUTES) | 281 if key in INHERITED_EXTENDED_ATTRIBUTES) |
237 | 282 |
238 def generate_ancestors(interface_name): | 283 def generate_ancestors(interface_name): |
239 while interface_name in parent_interfaces: | 284 while interface_name in parent_interfaces: |
240 interface_name = parent_interfaces[interface_name] | 285 interface_name = parent_interfaces[interface_name] |
241 yield interface_name | 286 yield interface_name |
242 | 287 |
243 ancestors = list(generate_ancestors(interface_name)) | 288 ancestors = list(generate_ancestors(interface_name)) |
244 if not ancestors: | 289 if not ancestors: |
245 if inherited_extended_attributes: | 290 if inherited_extended_attributes: |
246 interface_info['inherited_extended_attributes'] = inherited_extended
_attributes | 291 interface_info['inherited_extended_attributes'] = inherited_extended
_attributes |
247 return | 292 return |
248 | 293 |
249 interface_info['ancestors'] = ancestors | 294 interface_info['ancestors'] = ancestors |
250 for ancestor in ancestors: | 295 for ancestor in ancestors: |
251 # Extended attributes are missing if an ancestor is an interface that | 296 # Extended attributes are missing if an ancestor is an interface that |
252 # 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 |
253 # IDL files, or generated support files. | 298 # IDL files. |
254 ancestor_extended_attributes = extended_attributes_by_interface.get(ance
stor, {}) | 299 ancestor_extended_attributes = extended_attributes_by_interface.get(ance
stor, {}) |
255 inherited_extended_attributes.update(dict( | 300 inherited_extended_attributes.update(dict( |
256 (key, value) | 301 (key, value) |
257 for key, value in ancestor_extended_attributes.iteritems() | 302 for key, value in ancestor_extended_attributes.iteritems() |
258 if key in INHERITED_EXTENDED_ATTRIBUTES)) | 303 if key in INHERITED_EXTENDED_ATTRIBUTES)) |
259 if inherited_extended_attributes: | 304 if inherited_extended_attributes: |
260 interface_info['inherited_extended_attributes'] = inherited_extended_att
ributes | 305 interface_info['inherited_extended_attributes'] = inherited_extended_att
ributes |
261 | 306 |
262 | 307 |
263 def parse_idl_files(idl_files): | 308 def compute_interfaces_info(idl_files): |
264 """Compute information about IDL files. | 309 """Compute information about IDL files. |
265 | 310 |
266 Primary effect is computing info about main interfaces, stored in global | 311 Information is stored in global interfaces_info. |
267 interfaces_info. | |
268 """ | 312 """ |
269 # Generate dependencies. | 313 # Compute information for individual files |
270 for idl_filename in idl_files: | 314 for idl_filename in idl_files: |
271 generate_dependencies(idl_filename) | 315 compute_individual_info(idl_filename) |
272 | 316 |
| 317 # Once all individual files handled, can compute inheritance information |
273 for interface_name in interfaces_info: | 318 for interface_name in interfaces_info: |
274 generate_ancestors_and_inherited_extended_attributes(interface_name) | 319 compute_inheritance_info(interface_name) |
275 | 320 |
276 # 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, |
277 # and files for other interfaces that this interfaces implements. | 322 # and files for other interfaces that this interfaces implements. |
278 for interface_name, interface_info in interfaces_info.iteritems(): | 323 for interface_name, interface_info in interfaces_info.iteritems(): |
279 partial_interfaces_full_paths, partial_interfaces_include_paths = ( | 324 partial_interfaces_full_paths, partial_interfaces_include_paths = ( |
280 (partial_interface_files[interface_name]['full_paths'], | 325 (partial_interface_files[interface_name]['full_paths'], |
281 partial_interface_files[interface_name]['include_paths']) | 326 partial_interface_files[interface_name]['include_paths']) |
282 if interface_name in partial_interface_files else ([], [])) | 327 if interface_name in partial_interface_files else ([], [])) |
283 | 328 |
284 implemented_interfaces = interface_info['implements_interfaces'] | 329 implemented_interfaces = interface_info['implements_interfaces'] |
(...skipping 24 matching lines...) Expand all Loading... |
309 # 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 |
310 # command line length limits | 355 # command line length limits |
311 with open(options.idl_files_list) as idl_files_list: | 356 with open(options.idl_files_list) as idl_files_list: |
312 idl_files = [line.rstrip('\n') for line in idl_files_list] | 357 idl_files = [line.rstrip('\n') for line in idl_files_list] |
313 # 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 |
314 # 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 |
315 # cannot be included in the file listing static files | 360 # cannot be included in the file listing static files |
316 idl_files.extend(args) | 361 idl_files.extend(args) |
317 | 362 |
318 only_if_changed = options.write_file_only_if_changed | 363 only_if_changed = options.write_file_only_if_changed |
319 | 364 compute_interfaces_info(idl_files) |
320 parse_idl_files(idl_files) | |
321 write_pickle_file(options.interfaces_info_file, interfaces_info, only_if_cha
nged) | 365 write_pickle_file(options.interfaces_info_file, interfaces_info, only_if_cha
nged) |
322 write_dependencies_file(options.interface_dependencies_file, only_if_changed
) | 366 write_dependencies_file(options.interface_dependencies_file, only_if_changed
) |
323 write_event_names_file(options.event_names_file, only_if_changed) | 367 write_event_names_file(options.event_names_file, only_if_changed) |
324 | 368 |
325 | 369 |
326 if __name__ == '__main__': | 370 if __name__ == '__main__': |
327 main() | 371 sys.exit(main()) |
OLD | NEW |