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

Unified Diff: build/android/gyp/java_cpp_enum.py

Issue 484603004: New C++ -> Java enum build rule + parser/generator. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 6 years, 3 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « android_webview/java_library_common.mk ('k') | build/android/gyp/java_cpp_enum_tests.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: build/android/gyp/java_cpp_enum.py
diff --git a/build/android/gyp/java_cpp_enum.py b/build/android/gyp/java_cpp_enum.py
new file mode 100755
index 0000000000000000000000000000000000000000..ad0974232a07da5cd3d3aad28ef546853d095fae
--- /dev/null
+++ b/build/android/gyp/java_cpp_enum.py
@@ -0,0 +1,237 @@
+#!/usr/bin/env python
+#
+# Copyright 2014 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.
+
+import collections
+import re
+import optparse
+import os
+from string import Template
+import sys
+
+from util import build_utils
+
+class EnumDefinition(object):
+ def __init__(self, class_name=None, class_package=None, entries=None):
+ self.class_name = class_name
+ self.class_package = class_package
+ self.entries = collections.OrderedDict(entries or [])
+ self.prefix_to_strip = ''
+
+ def AppendEntry(self, key, value):
+ if key in self.entries:
+ raise Exception('Multiple definitions of key %s found.' % key)
+ self.entries[key] = value
+
+ def Finalize(self):
+ self._Validate()
+ self._AssignEntryIndices()
+ self._StripPrefix()
+
+ def _Validate(self):
+ assert self.class_name
+ assert self.class_package
+ assert self.entries
+
+ def _AssignEntryIndices(self):
+ # Supporting the same set enum value assignments the compiler does is rather
+ # complicated, so we limit ourselves to these cases:
+ # - all the enum constants have values assigned,
+ # - enum constants reference other enum constants or have no value assigned.
+
+ if not all(self.entries.values()):
+ index = 0
+ for key, value in self.entries.iteritems():
+ if not value:
+ self.entries[key] = index
+ index = index + 1
+ elif value in self.entries:
+ self.entries[key] = self.entries[value]
+ else:
+ raise Exception('You can only reference other enum constants unless '
+ 'you assign values to all of the constants.')
+
+ def _StripPrefix(self):
+ if not self.prefix_to_strip:
+ prefix_to_strip = re.sub('(?!^)([A-Z]+)', r'_\1', self.class_name).upper()
+ prefix_to_strip += '_'
+ if not all([w.startswith(prefix_to_strip) for w in self.entries.keys()]):
+ prefix_to_strip = ''
+ else:
+ prefix_to_strip = self.prefix_to_strip
+ entries = ((k.replace(prefix_to_strip, '', 1), v) for (k, v) in
+ self.entries.iteritems())
+ self.entries = collections.OrderedDict(entries)
+
+class HeaderParser(object):
+ single_line_comment_re = re.compile(r'\s*//')
+ multi_line_comment_start_re = re.compile(r'\s*/\*')
+ enum_start_re = re.compile(r'^\s*enum\s+(\w+)\s+{\s*$')
+ enum_line_re = re.compile(r'^\s*(\w+)(\s*\=\s*([^,\n]+))?,?\s*$')
+ enum_end_re = re.compile(r'^\s*}\s*;\s*$')
+ generator_directive_re = re.compile(
+ r'^\s*//\s+GENERATED_JAVA_(\w+)\s*:\s*([\.\w]+)$')
+
+ def __init__(self, lines):
+ self._lines = lines
+ self._enum_definitions = []
+ self._in_enum = False
+ self._current_definition = None
+ self._generator_directives = {}
+
+ def ParseDefinitions(self):
+ for line in self._lines:
+ self._ParseLine(line)
+ return self._enum_definitions
+
+ def _ParseLine(self, line):
+ if not self._in_enum:
+ self._ParseRegularLine(line)
+ else:
+ self._ParseEnumLine(line)
+
+ def _ParseEnumLine(self, line):
+ if HeaderParser.single_line_comment_re.match(line):
+ return
+ if HeaderParser.multi_line_comment_start_re.match(line):
+ raise Exception('Multi-line comments in enums are not supported.')
+ enum_end = HeaderParser.enum_end_re.match(line)
+ enum_entry = HeaderParser.enum_line_re.match(line)
+ if enum_end:
+ self._ApplyGeneratorDirectives()
+ self._current_definition.Finalize()
+ self._enum_definitions.append(self._current_definition)
+ self._in_enum = False
+ elif enum_entry:
+ enum_key = enum_entry.groups()[0]
+ enum_value = enum_entry.groups()[2]
+ self._current_definition.AppendEntry(enum_key, enum_value)
+
+ def _GetCurrentEnumPackageName(self):
+ return self._generator_directives.get('ENUM_PACKAGE')
+
+ def _GetCurrentEnumPrefixToStrip(self):
+ return self._generator_directives.get('PREFIX_TO_STRIP', '')
+
+ def _ApplyGeneratorDirectives(self):
+ current_definition = self._current_definition
+ current_definition.class_package = self._GetCurrentEnumPackageName()
+ current_definition.prefix_to_strip = self._GetCurrentEnumPrefixToStrip()
+ self._generator_directives = {}
+
+ def _ParseRegularLine(self, line):
+ enum_start = HeaderParser.enum_start_re.match(line)
+ generator_directive = HeaderParser.generator_directive_re.match(line)
+ if enum_start:
+ if not self._GetCurrentEnumPackageName():
+ return
+ self._current_definition = EnumDefinition()
+ self._current_definition.class_name = enum_start.groups()[0]
+ self._in_enum = True
+ elif generator_directive:
+ directive_name = generator_directive.groups()[0]
+ directive_value = generator_directive.groups()[1]
+ self._generator_directives[directive_name] = directive_value
+
+
+def GetScriptName():
+ script_components = os.path.abspath(sys.argv[0]).split(os.path.sep)
+ build_index = script_components.index('build')
+ return os.sep.join(script_components[build_index:])
+
+
+def DoGenerate(options, source_paths):
+ output_paths = []
+ for source_path in source_paths:
+ enum_definitions = DoParseHeaderFile(source_path)
+ for enum_definition in enum_definitions:
+ package_path = enum_definition.class_package.replace('.', os.path.sep)
+ file_name = enum_definition.class_name + '.java'
+ output_path = os.path.join(options.output_dir, package_path, file_name)
+ output_paths.append(output_path)
+ if not options.print_output_only:
+ build_utils.MakeDirectory(os.path.dirname(output_path))
+ DoWriteOutput(source_path, output_path, enum_definition)
+ return output_paths
+
+
+def DoParseHeaderFile(path):
+ with open(path) as f:
+ return HeaderParser(f.readlines()).ParseDefinitions()
+
+
+def GenerateOutput(source_path, enum_definition):
+ template = Template("""
+// Copyright 2014 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.
+
+// This file is autogenerated by
+// ${SCRIPT_NAME}
+// From
+// ${SOURCE_PATH}
+
+package ${PACKAGE};
+
+public class ${CLASS_NAME} {
+${ENUM_ENTRIES}
+}
+""")
+
+ enum_template = Template(' public static final int ${NAME} = ${VALUE};')
+ enum_entries_string = []
+ for enum_name, enum_value in enum_definition.entries.iteritems():
+ values = {
+ 'NAME': enum_name,
+ 'VALUE': enum_value,
+ }
+ enum_entries_string.append(enum_template.substitute(values))
+ enum_entries_string = '\n'.join(enum_entries_string)
+
+ values = {
+ 'CLASS_NAME': enum_definition.class_name,
+ 'ENUM_ENTRIES': enum_entries_string,
+ 'PACKAGE': enum_definition.class_package,
+ 'SCRIPT_NAME': GetScriptName(),
+ 'SOURCE_PATH': source_path,
+ }
+ return template.substitute(values)
+
+
+def DoWriteOutput(source_path, output_path, enum_definition):
+ with open(output_path, 'w') as out_file:
+ out_file.write(GenerateOutput(source_path, enum_definition))
+
+def AssertFilesList(output_paths, assert_files_list):
+ actual = set(output_paths)
+ expected = set(assert_files_list)
+ if not actual == expected:
+ need_to_add = list(actual - expected)
+ need_to_remove = list(expected - actual)
+ raise Exception('Output files list does not match expectations. Please '
+ 'add %s and remove %s.' % (need_to_add, need_to_remove))
+
+def DoMain(argv):
+ parser = optparse.OptionParser()
+
+ parser.add_option('--assert_file', action="append", default=[],
+ dest="assert_files_list", help='Assert that the given '
+ 'file is an output. There can be multiple occurrences of '
+ 'this flag.')
+ parser.add_option('--output_dir', help='Base path for generated files.')
+ parser.add_option('--print_output_only', help='Only print output paths.',
+ action='store_true')
+
+ options, args = parser.parse_args(argv)
+
+ output_paths = DoGenerate(options, args)
+
+ if options.assert_files_list:
+ AssertFilesList(output_paths, options.assert_files_list)
+
+ return " ".join(output_paths)
+
+if __name__ == '__main__':
+ DoMain(sys.argv[1:])
« no previous file with comments | « android_webview/java_library_common.mk ('k') | build/android/gyp/java_cpp_enum_tests.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698