Index: third_party/WebKit/Source/bindings/scripts/code_generator.py |
diff --git a/third_party/WebKit/Source/bindings/scripts/code_generator.py b/third_party/WebKit/Source/bindings/scripts/code_generator.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..02448dd131ea5b95fad46c1fb50aa766d00e02ac |
--- /dev/null |
+++ b/third_party/WebKit/Source/bindings/scripts/code_generator.py |
@@ -0,0 +1,184 @@ |
+# Copyright 2016 The Chromium Authors. All rights reserved. |
+# Use of this source code is governed by a BSD-style license that can be |
+# found in the LICENSE file. |
+ |
+# pylint: disable=import-error,print-statement,relative-import |
+ |
+"""Plumbing for a Jinja-based code generator, including CodeGeneratorBase, a base class for all generators.""" |
+ |
+import os |
+import posixpath |
+import re |
+import sys |
+ |
+from idl_types import set_ancestors, IdlType |
+from v8_attributes import attribute_filters |
+from v8_globals import includes |
+from v8_interface import constant_filters |
+from v8_types import set_component_dirs |
+from v8_methods import method_filters |
+from v8_utilities import capitalize, for_origin_trial_feature, unique_by |
+from utilities import (idl_filename_to_component, is_valid_component_dependency, |
+ format_remove_duplicates, format_blink_cpp_source_code) |
+ |
+# Path handling for libraries and templates |
+# Paths have to be normalized because Jinja uses the exact template path to |
+# determine the hash used in the cache filename, and we need a pre-caching step |
+# to be concurrency-safe. Use absolute path because __file__ is absolute if |
+# module is imported, and relative if executed directly. |
+# If paths differ between pre-caching and individual file compilation, the cache |
+# is regenerated, which causes a race condition and breaks concurrent build, |
+# since some compile processes will try to read the partially written cache. |
+MODULE_PATH, _ = os.path.split(os.path.realpath(__file__)) |
+THIRD_PARTY_DIR = os.path.normpath(os.path.join( |
+ MODULE_PATH, os.pardir, os.pardir, os.pardir, os.pardir)) |
+TEMPLATES_DIR = os.path.normpath(os.path.join( |
+ MODULE_PATH, os.pardir, 'templates')) |
+ |
+# jinja2 is in chromium's third_party directory. |
+# Insert at 1 so at front to override system libraries, and |
+# after path[0] == invoking script dir |
+sys.path.insert(1, THIRD_PARTY_DIR) |
+import jinja2 |
+ |
+ |
+def generate_indented_conditional(code, conditional): |
+ # Indent if statement to level of original code |
+ indent = re.match(' *', code).group(0) |
+ return ('%sif (%s) {\n' % (indent, conditional) + |
+ ' %s\n' % '\n '.join(code.splitlines()) + |
+ '%s}\n' % indent) |
+ |
+ |
+# [Exposed] |
+def exposed_if(code, exposed_test): |
+ if not exposed_test: |
+ return code |
+ return generate_indented_conditional(code, 'executionContext && (%s)' % exposed_test) |
+ |
+ |
+# [SecureContext] |
+def secure_context_if(code, secure_context_test): |
+ if not secure_context_test: |
+ return code |
+ return generate_indented_conditional(code, 'executionContext && (%s)' % secure_context_test) |
+ |
+ |
+# [RuntimeEnabled] |
+def runtime_enabled_if(code, runtime_enabled_function_name): |
+ if not runtime_enabled_function_name: |
+ return code |
+ return generate_indented_conditional(code, '%s()' % runtime_enabled_function_name) |
+ |
+ |
+def initialize_jinja_env(cache_dir): |
+ jinja_env = jinja2.Environment( |
+ loader=jinja2.FileSystemLoader(TEMPLATES_DIR), |
+ # Bytecode cache is not concurrency-safe unless pre-cached: |
+ # if pre-cached this is read-only, but writing creates a race condition. |
+ bytecode_cache=jinja2.FileSystemBytecodeCache(cache_dir), |
+ keep_trailing_newline=True, # newline-terminate generated files |
+ lstrip_blocks=True, # so can indent control flow tags |
+ trim_blocks=True) |
+ jinja_env.filters.update({ |
+ 'blink_capitalize': capitalize, |
+ 'exposed': exposed_if, |
+ 'for_origin_trial_feature': for_origin_trial_feature, |
+ 'format_blink_cpp_source_code': format_blink_cpp_source_code, |
+ 'format_remove_duplicates': format_remove_duplicates, |
+ 'runtime_enabled': runtime_enabled_if, |
+ 'secure_context': secure_context_if, |
+ 'unique_by': unique_by}) |
+ jinja_env.filters.update(attribute_filters()) |
+ jinja_env.filters.update(constant_filters()) |
+ jinja_env.filters.update(method_filters()) |
+ return jinja_env |
+ |
+ |
+def normalize_and_sort_includes(include_paths): |
+ normalized_include_paths = [] |
+ for include_path in include_paths: |
+ match = re.search(r'/gen/blink/(.*)$', posixpath.abspath(include_path)) |
+ if match: |
+ include_path = match.group(1) |
+ normalized_include_paths.append(include_path) |
+ return sorted(normalized_include_paths) |
+ |
+ |
+class CodeGeneratorBase(object): |
+ """Base class for jinja-powered jinja template generation. |
+ """ |
+ def __init__(self, generator_name, info_provider, cache_dir, output_dir): |
+ self.generator_name = generator_name |
+ self.info_provider = info_provider |
+ self.jinja_env = initialize_jinja_env(cache_dir) |
+ self.output_dir = output_dir |
+ self.set_global_type_info() |
+ |
+ def should_generate_code(self, definitions): |
+ return definitions.interfaces or definitions.dictionaries |
+ |
+ def set_global_type_info(self): |
+ interfaces_info = self.info_provider.interfaces_info |
+ set_ancestors(interfaces_info['ancestors']) |
+ IdlType.set_callback_interfaces(interfaces_info['callback_interfaces']) |
+ IdlType.set_dictionaries(interfaces_info['dictionaries']) |
+ IdlType.set_enums(self.info_provider.enumerations) |
+ IdlType.set_implemented_as_interfaces(interfaces_info['implemented_as_interfaces']) |
+ IdlType.set_garbage_collected_types(interfaces_info['garbage_collected_interfaces']) |
+ set_component_dirs(interfaces_info['component_dirs']) |
+ |
+ def render_template(self, include_paths, header_template, cpp_template, |
+ template_context, component=None): |
+ template_context['code_generator'] = self.generator_name |
+ |
+ # Add includes for any dependencies |
+ template_context['header_includes'] = normalize_and_sort_includes( |
+ template_context['header_includes']) |
+ |
+ for include_path in include_paths: |
+ if component: |
+ dependency = idl_filename_to_component(include_path) |
+ assert is_valid_component_dependency(component, dependency) |
+ includes.add(include_path) |
+ |
+ template_context['cpp_includes'] = normalize_and_sort_includes(includes) |
+ |
+ header_text = header_template.render(template_context) |
+ cpp_text = cpp_template.render(template_context) |
+ return header_text, cpp_text |
+ |
+ def generate_code(self, definitions, definition_name): |
+ """Invokes code generation. The [definitions] argument is a list of definitions, |
+ and the [definition_name] is the name of the definition |
+ """ |
+ # This should be implemented in subclasses. |
+ raise NotImplementedError() |
+ |
+ |
+def main(argv): |
+ # If file itself executed, cache templates |
+ try: |
+ cache_dir = argv[1] |
+ dummy_filename = argv[2] |
+ except IndexError: |
+ print 'Usage: %s CACHE_DIR DUMMY_FILENAME' % argv[0] |
+ return 1 |
+ |
+ # Cache templates |
+ jinja_env = initialize_jinja_env(cache_dir) |
+ template_filenames = [filename for filename in os.listdir(TEMPLATES_DIR) |
+ # Skip .svn, directories, etc. |
+ if filename.endswith(('.cpp', '.h'))] |
+ for template_filename in template_filenames: |
+ jinja_env.get_template(template_filename) |
+ |
+ # Create a dummy file as output for the build system, |
+ # since filenames of individual cache files are unpredictable and opaque |
+ # (they are hashes of the template path, which varies based on environment) |
+ with open(dummy_filename, 'w') as dummy_file: |
+ pass # |open| creates or touches the file |
+ |
+ |
+if __name__ == '__main__': |
+ sys.exit(main(sys.argv)) |