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

Side by Side Diff: Source/bindings/scripts/compute_dependencies.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
(Empty)
1 #!/usr/bin/python
2 #
3 # Copyright (C) 2013 Google Inc. All rights reserved.
4 #
5 # Redistribution and use in source and binary forms, with or without
6 # modification, are permitted provided that the following conditions are
7 # met:
8 #
9 # * Redistributions of source code must retain the above copyright
10 # notice, this list of conditions and the following disclaimer.
11 # * Redistributions in binary form must reproduce the above
12 # copyright notice, this list of conditions and the following disclaimer
13 # in the documentation and/or other materials provided with the
14 # distribution.
15 # * Neither the name of Google Inc. nor the names of its
16 # contributors may be used to endorse or promote products derived from
17 # this software without specific prior written permission.
18 #
19 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
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.
30
31 import optparse
32 import os
33 import cPickle as pickle
34 import posixpath
35 import re
36 import string
37
38 module_path = os.path.dirname(__file__)
39 source_path = os.path.normpath(os.path.join(module_path, os.pardir, os.pardir))
40
41 INHERITED_EXTENDED_ATTRIBUTES = set([
42 'ActiveDOMObject',
43 'DependentLifetime',
44 'WillBeGarbageCollected',
45 ])
46
47
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 = {}
55
56 # Auxiliary variables (not visible to future build steps)
57 partial_interface_files = {}
58 parent_interfaces = {}
59 extended_attributes_by_interface = {} # interface name -> extended attributes
60
61
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):
68 """Raised if the IDL file implementing an interface cannot be found."""
69 pass
70
71
72 def parse_options():
73 usage = 'Usage: %prog [options] [generated1.idl]...'
74 parser = optparse.OptionParser(usage=usage)
75 parser.add_option('--event-names-file', help='output file')
76 parser.add_option('--idl-files-list', help='file listing IDL files')
77 parser.add_option('--interface-dependencies-file', help='output file')
78 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')
85 options, args = parser.parse_args()
86 if options.event_names_file is None:
87 parser.error('Must specify an output file using --event-names-file.')
88 if options.interface_dependencies_file is None:
89 parser.error('Must specify an output file using --interface-dependencies -file.')
90 if options.interfaces_info_file is None:
91 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:
103 parser.error('Must specify a file listing IDL files using --idl-files-li st.')
104 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.')
106 options.write_file_only_if_changed = bool(options.write_file_only_if_changed )
107 return options, args
108
109
110 ################################################################################
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
227 ################################################################################
228
229 def write_dependencies_file(dependencies_filename, only_if_changed):
230 """Write the interface dependencies file.
231
232 The format is as follows:
233
234 Document.idl P.idl
235 Event.idl
236 Window.idl Q.idl R.idl S.idl
237 ...
238
239 The above indicates that:
240 Document.idl depends on P.idl,
241 Event.idl depends on no other IDL files, and
242 Window.idl depends on Q.idl, R.idl, and S.idl.
243
244 An IDL that is a dependency of another IDL (e.g. P.idl) does not have its
245 own line in the dependency file.
246 """
247 # FIXME: remove text format once Perl gone (Python uses pickle)
248 dependencies_list = sorted(
249 (interface_info['full_path'], sorted(interface_info['dependencies_full_p aths']))
250 for interface_info in interfaces_info.values())
251 lines = ['%s %s\n' % (idl_file, ' '.join(dependency_files))
252 for idl_file, dependency_files in dependencies_list]
253 write_file(lines, dependencies_filename, only_if_changed)
254
255
256 def write_event_names_file(destination_filename, only_if_changed):
257 # Generate event names for all interfaces that inherit from Event,
258 # including Event itself.
259 event_names = set(
260 interface_name
261 for interface_name, interface_info in interfaces_info.iteritems()
262 if (interface_name == 'Event' or
263 ('ancestors' in interface_info and
264 interface_info['ancestors'][-1] == 'Event')))
265
266 def extended_attribute_string(name):
267 value = extended_attributes[name]
268 if name == 'RuntimeEnabled':
269 value += 'Enabled'
270 return name + '=' + value
271
272 source_dir, _ = os.path.split(os.getcwd())
273 lines = []
274 lines.append('namespace="Event"\n')
275 lines.append('\n')
276 for filename, extended_attributes in sorted(
277 (interface_info['full_path'],
278 extended_attributes_by_interface[interface_name])
279 for interface_name, interface_info in interfaces_info.iteritems()
280 if interface_name in event_names):
281 refined_filename, _ = os.path.splitext(os.path.relpath(filename, source_ dir))
282 refined_filename = refined_filename.replace(os.sep, posixpath.sep)
283 extended_attributes_list = [
284 extended_attribute_string(name)
285 for name in 'Conditional', 'ImplementedAs', 'RuntimeEnabled'
286 if name in extended_attributes]
287 lines.append('%s %s\n' % (refined_filename, ', '.join(extended_attribute s_list)))
288 write_file(lines, destination_filename, only_if_changed)
289
290
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 ################################################################################
300 # Dependency resolution
301 ################################################################################
302
303 def include_path(idl_filename, implemented_as=None):
304 """Returns relative path to header file in POSIX format; used in includes.
305
306 POSIX format is used for consistency of output, so reference tests are
307 platform-independent.
308 """
309 relative_path_local = os.path.relpath(idl_filename, source_path)
310 relative_dir_local = os.path.dirname(relative_path_local)
311 relative_dir_posix = relative_dir_local.replace(os.path.sep, posixpath.sep)
312
313 idl_file_basename, _ = os.path.splitext(os.path.basename(idl_filename))
314 cpp_class_name = implemented_as or idl_file_basename
315
316 return posixpath.join(relative_dir_posix, cpp_class_name + '.h')
317
318
319 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,
321 {'full_paths': [],
322 'include_paths': []})
323 paths_dict['full_paths'].append(full_path)
324 if this_include_path:
325 paths_dict['include_paths'].append(this_include_path)
326
327
328 def generate_dependencies(idl_filename):
329 """Compute dependencies for IDL file, returning True if main (non-partial) i nterface"""
330 full_path = os.path.realpath(idl_filename)
331 idl_file_contents = get_file_contents(full_path)
332
333 extended_attributes = get_interface_extended_attributes_from_idl(idl_file_co ntents)
334 implemented_as = extended_attributes.get('ImplementedAs')
335 # FIXME: remove [NoHeader] once switch to Python
336 this_include_path = (include_path(idl_filename, implemented_as)
337 if 'NoHeader' not in extended_attributes else None)
338
339 # Handle partial interfaces
340 partial_interface_name = get_partial_interface_name_from_idl(idl_file_conten ts)
341 if partial_interface_name:
342 add_paths_to_partials_dict(partial_interface_name, full_path, this_inclu de_path)
343 return False
344
345 # If not a partial interface, the basename is the interface name
346 interface_name, _ = os.path.splitext(os.path.basename(idl_filename))
347
348 interfaces_info[interface_name] = {
349 'full_path': full_path,
350 '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),
352 # Interfaces that are referenced (used as types) and that we introspect
353 # during code generation (beyond interface-level data ([ImplementedAs],
354 # is_callback_interface, ancestors, and inherited extended attributes):
355 # deep dependencies.
356 # These cause rebuilds of referrers, due to the dependency, so these
357 # should be minimized; currently only targets of [PutForwards].
358 'referenced_interfaces': get_put_forward_interfaces_from_idl(idl_file_co ntents),
359 }
360 if this_include_path:
361 interfaces_info[interface_name]['include_path'] = this_include_path
362 if implemented_as:
363 interfaces_info[interface_name]['implemented_as'] = implemented_as
364
365 return True
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
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)
414 if parent:
415 parent_interfaces[interface_name] = parent
416
417
418 def record_extended_attributes(idl_filename):
419 interface_name, _ = os.path.splitext(os.path.basename(idl_filename))
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]
428 interface_extended_attributes = extended_attributes_by_interface[interface_n ame]
429 inherited_extended_attributes = dict(
430 (key, value)
431 for key, value in interface_extended_attributes.iteritems()
432 if key in INHERITED_EXTENDED_ATTRIBUTES)
433
434 def generate_ancestors(interface_name):
435 while interface_name in parent_interfaces:
436 interface_name = parent_interfaces[interface_name]
437 yield interface_name
438
439 ancestors = list(generate_ancestors(interface_name))
440 if not ancestors:
441 if inherited_extended_attributes:
442 interface_info['inherited_extended_attributes'] = inherited_extended _attributes
443 return
444
445 interface_info['ancestors'] = ancestors
446 for ancestor in ancestors:
447 # Extended attributes are missing if an ancestor is an interface that
448 # we're not processing, notably real IDL files if only processing test
449 # IDL files, or generated support files.
450 ancestor_extended_attributes = extended_attributes_by_interface.get(ance stor, {})
451 inherited_extended_attributes.update(dict(
452 (key, value)
453 for key, value in ancestor_extended_attributes.iteritems()
454 if key in INHERITED_EXTENDED_ATTRIBUTES))
455 if inherited_extended_attributes:
456 interface_info['inherited_extended_attributes'] = inherited_extended_att ributes
457
458
459 def parse_idl_files(idl_files, global_constructors_filenames):
460 """Compute dependencies between IDL files, and return constructors on global objects.
461
462 Primary effect is computing info about main interfaces, stored in global
463 interfaces_info.
464 The keys are the interfaces for which bindings are generated;
465 this does not include interfaces implemented by another interface.
466
467 Returns:
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:
483 generate_ancestors_and_inherited_extended_attributes(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
493 # An IDL file's dependencies are partial interface files that extend it,
494 # and files for other interfaces that this interfaces implements.
495 for interface_name, interface_info in interfaces_info.iteritems():
496 partial_interfaces_full_paths, partial_interfaces_include_paths = (
497 (partial_interface_files[interface_name]['full_paths'],
498 partial_interface_files[interface_name]['include_paths'])
499 if interface_name in partial_interface_files else ([], []))
500
501 implemented_interfaces = interface_info['implements_interfaces']
502 try:
503 implemented_interfaces_full_paths = [
504 interfaces_info[interface]['full_path']
505 for interface in implemented_interfaces]
506 implemented_interfaces_include_paths = [
507 interfaces_info[interface]['include_path']
508 for interface in implemented_interfaces
509 if 'include_path' in interfaces_info[interface]]
510 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)
512
513 interface_info['dependencies_full_paths'] = (
514 partial_interfaces_full_paths +
515 implemented_interfaces_full_paths)
516 interface_info['dependencies_include_paths'] = (
517 partial_interfaces_include_paths +
518 implemented_interfaces_include_paths)
519
520 return global_constructors
521
522
523 ################################################################################
524
525 def main():
526 options, args = parse_options()
527
528 # Static IDL files are passed in a file (generated at GYP time), due to OS
529 # command line length limits
530 with open(options.idl_files_list) as idl_files_list:
531 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
533 # build directory, which is determined at build time, not GYP time, so these
534 # cannot be included in the file listing static files
535 idl_files.extend(args)
536
537 only_if_changed = options.write_file_only_if_changed
538 global_constructors_filenames = {
539 'Window': options.window_constructors_file,
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 )
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)
554
555
556 if __name__ == '__main__':
557 main()
OLDNEW
« no previous file with comments | « Source/bindings/generated_bindings.gyp ('k') | Source/bindings/scripts/compute_interfaces_info.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698