OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # | 2 # |
3 # Copyright 2014 The Chromium Authors. All rights reserved. | 3 # Copyright 2016 The Chromium Authors. All rights reserved. |
4 # Use of this source code is governed by a BSD-style license that can be | 4 # Use of this source code is governed by a BSD-style license that can be |
5 # found in the LICENSE file. | 5 # found in the LICENSE file. |
6 | 6 |
7 import collections | 7 import collections |
8 from datetime import date | 8 from datetime import date |
9 import re | 9 import re |
10 import optparse | 10 import optparse |
11 import os | 11 import os |
12 from string import Template | 12 from string import Template |
13 import sys | 13 import sys |
14 import zipfile | 14 import zipfile |
15 | 15 |
16 from util import build_utils | 16 from util import build_utils |
17 | 17 |
18 # List of C++ types that are compatible with the Java code generated by this | 18 class StringListDefinition(object): |
19 # script. | 19 def __init__(self, class_name=None, string_package=None, entries=None): |
20 # | 20 self.class_name = class_name |
21 # This script can parse .idl files however, at present it ignores special | 21 self.string_package = string_package |
22 # rules such as [cpp_enum_prefix_override="ax_attr"]. | |
23 ENUM_FIXED_TYPE_WHITELIST = ['char', 'unsigned char', | |
24 'short', 'unsigned short', | |
25 'int', 'int8_t', 'int16_t', 'int32_t', 'uint8_t', 'uint16_t'] | |
26 | |
27 class EnumDefinition(object): | |
28 def __init__(self, original_enum_name=None, class_name_override=None, | |
29 enum_package=None, entries=None, fixed_type=None): | |
30 self.original_enum_name = original_enum_name | |
31 self.class_name_override = class_name_override | |
32 self.enum_package = enum_package | |
33 self.entries = collections.OrderedDict(entries or []) | 22 self.entries = collections.OrderedDict(entries or []) |
34 self.prefix_to_strip = None | |
35 self.fixed_type = fixed_type | |
36 | 23 |
37 def AppendEntry(self, key, value): | 24 def AppendEntry(self, key, value): |
38 if key in self.entries: | 25 if key in self.entries: |
39 raise Exception('Multiple definitions of key %s found.' % key) | 26 raise Exception('Multiple definitions of key %s found.' % key) |
40 self.entries[key] = value | 27 self.entries[key] = value |
41 | 28 |
42 @property | |
43 def class_name(self): | |
44 return self.class_name_override or self.original_enum_name | |
45 | |
46 def Finalize(self): | 29 def Finalize(self): |
47 self._Validate() | 30 self._Validate() |
48 self._AssignEntryIndices() | |
49 self._StripPrefix() | |
50 | 31 |
51 def _Validate(self): | 32 def _Validate(self): |
52 assert self.class_name | 33 assert self.class_name |
53 assert self.enum_package | 34 assert self.string_package |
54 assert self.entries | 35 assert self.entries |
55 if self.fixed_type and self.fixed_type not in ENUM_FIXED_TYPE_WHITELIST: | |
56 raise Exception('Fixed type %s for enum %s not whitelisted.' % | |
57 (self.fixed_type, self.class_name)) | |
58 | |
59 def _AssignEntryIndices(self): | |
60 # Enums, if given no value, are given the value of the previous enum + 1. | |
61 if not all(self.entries.values()): | |
62 prev_enum_value = -1 | |
63 for key, value in self.entries.iteritems(): | |
64 if not value: | |
65 self.entries[key] = prev_enum_value + 1 | |
66 elif value in self.entries: | |
67 self.entries[key] = self.entries[value] | |
68 else: | |
69 try: | |
70 self.entries[key] = int(value) | |
71 except ValueError: | |
72 raise Exception('Could not interpret integer from enum value "%s" ' | |
73 'for key %s.' % (value, key)) | |
74 prev_enum_value = self.entries[key] | |
75 | 36 |
76 | 37 |
77 def _StripPrefix(self): | 38 class DirectiveSet(object): |
78 prefix_to_strip = self.prefix_to_strip | 39 class_name_key = 'CLASS_NAME' |
79 if not prefix_to_strip: | 40 string_package_key = 'STRING_PACKAGE' |
80 prefix_to_strip = self.original_enum_name | |
81 prefix_to_strip = re.sub('(?!^)([A-Z]+)', r'_\1', prefix_to_strip).upper() | |
82 prefix_to_strip += '_' | |
83 if not all([w.startswith(prefix_to_strip) for w in self.entries.keys()]): | |
84 prefix_to_strip = '' | |
85 | 41 |
86 entries = collections.OrderedDict() | 42 known_keys = [class_name_key, string_package_key] |
87 for (k, v) in self.entries.iteritems(): | |
88 stripped_key = k.replace(prefix_to_strip, '', 1) | |
89 if isinstance(v, basestring): | |
90 stripped_value = v.replace(prefix_to_strip, '', 1) | |
91 else: | |
92 stripped_value = v | |
93 entries[stripped_key] = stripped_value | |
94 | |
95 self.entries = entries | |
96 | |
97 class DirectiveSet(object): | |
98 class_name_override_key = 'CLASS_NAME_OVERRIDE' | |
99 enum_package_key = 'ENUM_PACKAGE' | |
100 prefix_to_strip_key = 'PREFIX_TO_STRIP' | |
101 | |
102 known_keys = [class_name_override_key, enum_package_key, prefix_to_strip_key] | |
103 | 43 |
104 def __init__(self): | 44 def __init__(self): |
105 self._directives = {} | 45 self._directives = {} |
106 | 46 |
107 def Update(self, key, value): | 47 def Update(self, key, value): |
108 if key not in DirectiveSet.known_keys: | 48 if key not in DirectiveSet.known_keys: |
109 raise Exception("Unknown directive: " + key) | 49 raise Exception("Unknown directive: " + key) |
110 self._directives[key] = value | 50 self._directives[key] = value |
111 | 51 |
112 @property | 52 @property |
113 def empty(self): | 53 def empty(self): |
114 return len(self._directives) == 0 | 54 return len(self._directives) == 0 |
115 | 55 |
116 def UpdateDefinition(self, definition): | 56 def UpdateDefinition(self, definition): |
117 definition.class_name_override = self._directives.get( | 57 definition.class_name = self._directives.get( |
118 DirectiveSet.class_name_override_key, '') | 58 DirectiveSet.class_name_key) |
119 definition.enum_package = self._directives.get( | 59 definition.string_package = self._directives.get( |
120 DirectiveSet.enum_package_key) | 60 DirectiveSet.string_package_key) |
121 definition.prefix_to_strip = self._directives.get( | |
122 DirectiveSet.prefix_to_strip_key) | |
123 | 61 |
124 | 62 |
125 class HeaderParser(object): | 63 class CppStringFileParser(object): |
126 single_line_comment_re = re.compile(r'\s*//') | 64 single_line_comment_re = re.compile(r'\s*//') |
127 multi_line_comment_start_re = re.compile(r'\s*/\*') | 65 multi_line_comment_start_re = re.compile(r'\s*/\*') |
128 enum_line_re = re.compile(r'^\s*(\w+)(\s*\=\s*([^,\n]+))?,?') | 66 single_string_line_re = re.compile( |
129 enum_end_re = re.compile(r'^\s*}\s*;\.*$') | 67 r'^\s*const\s*char\s*(\w+)\[\]\s*\=\s*"([^"]*)";') |
| 68 multi_string_line_start_re = re.compile( |
| 69 r'^\s*const\s*char\s*(\w+)\[\]\s*\=?\s*$') |
| 70 multi_string_line_end_re = re.compile( |
| 71 r'^\s*\=?\s*"([^"]*)";') |
130 generator_directive_re = re.compile( | 72 generator_directive_re = re.compile( |
131 r'^\s*//\s+GENERATED_JAVA_(\w+)\s*:\s*([\.\w]+)$') | 73 r'^\s*//\s+GENERATED_JAVA_(\w+)\s*:\s*([\.\w]+)$') |
132 multi_line_generator_directive_start_re = re.compile( | 74 multi_line_generator_directive_start_re = re.compile( |
133 r'^\s*//\s+GENERATED_JAVA_(\w+)\s*:\s*\(([\.\w]*)$') | 75 r'^\s*//\s+GENERATED_JAVA_(\w+)\s*:\s*\(([\.\w]*)$') |
134 multi_line_directive_continuation_re = re.compile( | 76 multi_line_directive_continuation_re = re.compile( |
135 r'^\s*//\s+([\.\w]+)$') | 77 r'^\s*//\s+([\.\w]+)$') |
136 multi_line_directive_end_re = re.compile( | 78 multi_line_directive_end_re = re.compile( |
137 r'^\s*//\s+([\.\w]*)\)$') | 79 r'^\s*//\s+([\.\w]*)\)$') |
138 | 80 |
139 optional_class_or_struct_re = r'(class|struct)?' | |
140 enum_name_re = r'(\w+)' | |
141 optional_fixed_type_re = r'(\:\s*(\w+\s*\w+?))?' | |
142 enum_start_re = re.compile(r'^\s*(?:\[cpp.*\])?\s*enum\s+' + | |
143 optional_class_or_struct_re + '\s*' + enum_name_re + '\s*' + | |
144 optional_fixed_type_re + '\s*{\s*$') | |
145 | 81 |
146 def __init__(self, lines, path=None): | 82 def __init__(self, lines, path=None): |
147 self._lines = lines | 83 self._lines = lines |
148 self._path = path | 84 self._path = path |
149 self._enum_definitions = [] | 85 self._string_definitions = [] |
150 self._in_enum = False | |
151 self._current_definition = None | 86 self._current_definition = None |
152 self._generator_directives = DirectiveSet() | 87 self._generator_directives = DirectiveSet() |
153 self._multi_line_generator_directive = None | 88 self._multi_line_generator_directive = None |
| 89 self._current_multiline_string_key = None |
154 | 90 |
155 def _ApplyGeneratorDirectives(self): | 91 def _ApplyGeneratorDirectives(self): |
156 self._generator_directives.UpdateDefinition(self._current_definition) | 92 self._generator_directives.UpdateDefinition(self._current_definition) |
157 self._generator_directives = DirectiveSet() | 93 self._generator_directives = DirectiveSet() |
158 | 94 |
159 def ParseDefinitions(self): | 95 def ParseDefinitions(self): |
| 96 self._current_definition = StringListDefinition() |
160 for line in self._lines: | 97 for line in self._lines: |
161 self._ParseLine(line) | 98 self._ParseLine(line) |
162 return self._enum_definitions | 99 self._ApplyGeneratorDirectives() |
| 100 self._current_definition.Finalize() |
| 101 return self._current_definition |
163 | 102 |
164 def _ParseLine(self, line): | 103 def _ParseLine(self, line): |
165 if self._multi_line_generator_directive: | 104 if self._multi_line_generator_directive: |
166 self._ParseMultiLineDirectiveLine(line) | 105 self._ParseMultiLineDirectiveLine(line) |
167 elif not self._in_enum: | |
168 self._ParseRegularLine(line) | |
169 else: | 106 else: |
170 self._ParseEnumLine(line) | 107 self._ParseStringLine(line) |
171 | 108 |
172 def _ParseEnumLine(self, line): | 109 def _ParseStringLine(self, line): |
173 if HeaderParser.single_line_comment_re.match(line): | 110 # If we're continuing the start of a string definition, we need to match |
| 111 # the end part of the string defintiion. |
| 112 if self._current_multiline_string_key: |
| 113 string_multi_line_end = ( |
| 114 CppStringFileParser.multi_string_line_end_re.match(line)) |
| 115 if not string_multi_line_end: |
| 116 raise Exception('Incomplete multi-line string definition ' + |
| 117 self._path) |
| 118 |
| 119 string_key = self._current_multiline_string_key |
| 120 string_value = string_multi_line_end.groups()[0] |
| 121 self._current_definition.AppendEntry(string_key, string_value) |
| 122 self._current_multiline_string_key = None |
174 return | 123 return |
175 if HeaderParser.multi_line_comment_start_re.match(line): | 124 |
176 raise Exception('Multi-line comments in enums are not supported in ' + | 125 generator_directive = CppStringFileParser.generator_directive_re.match(line) |
| 126 multi_line_generator_directive_start = ( |
| 127 CppStringFileParser.multi_line_generator_directive_start_re.match(line)) |
| 128 if generator_directive: |
| 129 directive_name = generator_directive.groups()[0] |
| 130 directive_value = generator_directive.groups()[1] |
| 131 self._generator_directives.Update(directive_name, directive_value) |
| 132 return |
| 133 if multi_line_generator_directive_start: |
| 134 directive_name = multi_line_generator_directive_start.groups()[0] |
| 135 directive_value = multi_line_generator_directive_start.groups()[1] |
| 136 self._multi_line_generator_directive = (directive_name, [directive_value]) |
| 137 return |
| 138 |
| 139 if CppStringFileParser.single_line_comment_re.match(line): |
| 140 return |
| 141 if CppStringFileParser.multi_line_comment_start_re.match(line): |
| 142 raise Exception('Multi-line comments in strings are not supported in ' + |
177 self._path) | 143 self._path) |
178 enum_end = HeaderParser.enum_end_re.match(line) | 144 |
179 enum_entry = HeaderParser.enum_line_re.match(line) | 145 string_multi_line_start = ( |
180 if enum_end: | 146 CppStringFileParser.multi_string_line_start_re.match(line)) |
181 self._ApplyGeneratorDirectives() | 147 single_string_line = CppStringFileParser.single_string_line_re.match(line) |
182 self._current_definition.Finalize() | 148 if string_multi_line_start or single_string_line: |
183 self._enum_definitions.append(self._current_definition) | 149 if self._generator_directives.empty: |
184 self._in_enum = False | 150 raise Exception('Missing directives before string definition in ' + |
185 elif enum_entry: | 151 self._path) |
186 enum_key = enum_entry.groups()[0] | 152 if string_multi_line_start: |
187 enum_value = enum_entry.groups()[2] | 153 self._current_multiline_string_key = string_multi_line_start.groups()[0] |
188 self._current_definition.AppendEntry(enum_key, enum_value) | 154 return |
| 155 if single_string_line: |
| 156 string_key = single_string_line.groups()[0] |
| 157 string_value = single_string_line.groups()[1] |
| 158 self._current_definition.AppendEntry(string_key, string_value) |
| 159 return |
| 160 |
189 | 161 |
190 def _ParseMultiLineDirectiveLine(self, line): | 162 def _ParseMultiLineDirectiveLine(self, line): |
191 multi_line_directive_continuation = ( | 163 multi_line_directive_continuation = ( |
192 HeaderParser.multi_line_directive_continuation_re.match(line)) | 164 CppStringFileParser.multi_line_directive_continuation_re.match(line)) |
193 multi_line_directive_end = ( | 165 multi_line_directive_end = ( |
194 HeaderParser.multi_line_directive_end_re.match(line)) | 166 CppStringFileParser.multi_line_directive_end_re.match(line)) |
195 | 167 |
196 if multi_line_directive_continuation: | 168 if multi_line_directive_continuation: |
197 value_cont = multi_line_directive_continuation.groups()[0] | 169 value_cont = multi_line_directive_continuation.groups()[0] |
198 self._multi_line_generator_directive[1].append(value_cont) | 170 self._multi_line_generator_directive[1].append(value_cont) |
199 elif multi_line_directive_end: | 171 elif multi_line_directive_end: |
200 directive_name = self._multi_line_generator_directive[0] | 172 directive_name = self._multi_line_generator_directive[0] |
201 directive_value = "".join(self._multi_line_generator_directive[1]) | 173 directive_value = "".join(self._multi_line_generator_directive[1]) |
202 directive_value += multi_line_directive_end.groups()[0] | 174 directive_value += multi_line_directive_end.groups()[0] |
203 self._multi_line_generator_directive = None | 175 self._multi_line_generator_directive = None |
204 self._generator_directives.Update(directive_name, directive_value) | 176 self._generator_directives.Update(directive_name, directive_value) |
205 else: | 177 else: |
206 raise Exception('Malformed multi-line directive declaration in ' + | 178 raise Exception('Malformed multi-line directive declaration in ' + |
207 self._path) | 179 self._path) |
208 | 180 |
209 def _ParseRegularLine(self, line): | |
210 enum_start = HeaderParser.enum_start_re.match(line) | |
211 generator_directive = HeaderParser.generator_directive_re.match(line) | |
212 multi_line_generator_directive_start = ( | |
213 HeaderParser.multi_line_generator_directive_start_re.match(line)) | |
214 | |
215 if generator_directive: | |
216 directive_name = generator_directive.groups()[0] | |
217 directive_value = generator_directive.groups()[1] | |
218 self._generator_directives.Update(directive_name, directive_value) | |
219 elif multi_line_generator_directive_start: | |
220 directive_name = multi_line_generator_directive_start.groups()[0] | |
221 directive_value = multi_line_generator_directive_start.groups()[1] | |
222 self._multi_line_generator_directive = (directive_name, [directive_value]) | |
223 elif enum_start: | |
224 if self._generator_directives.empty: | |
225 return | |
226 self._current_definition = EnumDefinition( | |
227 original_enum_name=enum_start.groups()[1], | |
228 fixed_type=enum_start.groups()[3]) | |
229 self._in_enum = True | |
230 | 181 |
231 def GetScriptName(): | 182 def GetScriptName(): |
232 return os.path.basename(os.path.abspath(sys.argv[0])) | 183 return os.path.basename(os.path.abspath(sys.argv[0])) |
233 | 184 |
234 def DoGenerate(source_paths): | 185 def DoGenerate(source_paths): |
235 for source_path in source_paths: | 186 for source_path in source_paths: |
236 enum_definitions = DoParseHeaderFile(source_path) | 187 string_definition = DoParseHeaderFile(source_path) |
237 if not enum_definitions: | 188 if not string_definition: |
238 raise Exception('No enums found in %s\n' | 189 raise Exception('No strings found in %s\n' |
239 'Did you forget prefixing enums with ' | 190 'Did you forget prefixing strings with ' |
240 '"// GENERATED_JAVA_ENUM_PACKAGE: foo"?' % | 191 '"// GENERATED_JAVA_STRING_PACKAGE: foo"?' % |
241 source_path) | 192 source_path) |
242 for enum_definition in enum_definitions: | 193 package_path = string_definition.string_package.replace('.', os.path.sep) |
243 package_path = enum_definition.enum_package.replace('.', os.path.sep) | 194 file_name = string_definition.class_name + '.java' |
244 file_name = enum_definition.class_name + '.java' | 195 output_path = os.path.join(package_path, file_name) |
245 output_path = os.path.join(package_path, file_name) | 196 output = GenerateOutput(source_path, string_definition) |
246 output = GenerateOutput(source_path, enum_definition) | 197 yield output_path, output |
247 yield output_path, output | |
248 | |
249 | 198 |
250 def DoParseHeaderFile(path): | 199 def DoParseHeaderFile(path): |
251 with open(path) as f: | 200 with open(path) as f: |
252 return HeaderParser(f.readlines(), path).ParseDefinitions() | 201 return CppStringFileParser(f.readlines(), path).ParseDefinitions() |
253 | 202 |
254 | 203 |
255 def GenerateOutput(source_path, enum_definition): | 204 def GenerateOutput(source_path, string_definition): |
256 template = Template(""" | 205 template = Template(""" |
257 // Copyright ${YEAR} The Chromium Authors. All rights reserved. | 206 // Copyright ${YEAR} The Chromium Authors. All rights reserved. |
258 // Use of this source code is governed by a BSD-style license that can be | 207 // Use of this source code is governed by a BSD-style license that can be |
259 // found in the LICENSE file. | 208 // found in the LICENSE file. |
260 | 209 |
261 // This file is autogenerated by | 210 // This file is autogenerated by |
262 // ${SCRIPT_NAME} | 211 // ${SCRIPT_NAME} |
263 // From | 212 // From |
264 // ${SOURCE_PATH} | 213 // ${SOURCE_PATH} |
265 | 214 |
266 package ${PACKAGE}; | 215 package ${PACKAGE}; |
267 | 216 |
268 public class ${CLASS_NAME} { | 217 public class ${CLASS_NAME} { |
269 ${ENUM_ENTRIES} | 218 ${STRING_ENTRIES} |
270 } | 219 } |
271 """) | 220 """) |
272 | 221 |
273 enum_template = Template(' public static final int ${NAME} = ${VALUE};') | 222 string_template = ( |
274 enum_entries_string = [] | 223 Template(' public static final String ${NAME} = "${VALUE}";')) |
275 for enum_name, enum_value in enum_definition.entries.iteritems(): | 224 string_entries_string = [] |
| 225 for string_name, string_value in string_definition.entries.iteritems(): |
276 values = { | 226 values = { |
277 'NAME': enum_name, | 227 'NAME': string_name, |
278 'VALUE': enum_value, | 228 'VALUE': string_value, |
279 } | 229 } |
280 enum_entries_string.append(enum_template.substitute(values)) | 230 string_entries_string.append(string_template.substitute(values)) |
281 enum_entries_string = '\n'.join(enum_entries_string) | 231 string_entries_string = '\n'.join(string_entries_string) |
282 | 232 |
283 values = { | 233 values = { |
284 'CLASS_NAME': enum_definition.class_name, | 234 'CLASS_NAME': string_definition.class_name, |
285 'ENUM_ENTRIES': enum_entries_string, | 235 'STRING_ENTRIES': string_entries_string, |
286 'PACKAGE': enum_definition.enum_package, | 236 'PACKAGE': string_definition.string_package, |
287 'SCRIPT_NAME': GetScriptName(), | 237 'SCRIPT_NAME': GetScriptName(), |
288 'SOURCE_PATH': source_path, | 238 'SOURCE_PATH': source_path, |
289 'YEAR': str(date.today().year) | 239 'YEAR': str(date.today().year) |
290 } | 240 } |
291 return template.substitute(values) | 241 return template.substitute(values) |
292 | 242 |
293 | 243 |
294 def AssertFilesList(output_paths, assert_files_list): | 244 def AssertFilesList(output_paths, assert_files_list): |
295 actual = set(output_paths) | 245 actual = set(output_paths) |
296 expected = set(assert_files_list) | 246 expected = set(assert_files_list) |
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
357 if options.assert_files_list: | 307 if options.assert_files_list: |
358 AssertFilesList(output_paths, options.assert_files_list) | 308 AssertFilesList(output_paths, options.assert_files_list) |
359 | 309 |
360 if options.verbose: | 310 if options.verbose: |
361 print 'Output paths:' | 311 print 'Output paths:' |
362 print '\n'.join(output_paths) | 312 print '\n'.join(output_paths) |
363 | 313 |
364 # Used by GYP. | 314 # Used by GYP. |
365 return ' '.join(output_paths) | 315 return ' '.join(output_paths) |
366 | 316 |
367 | |
368 if __name__ == '__main__': | 317 if __name__ == '__main__': |
369 DoMain(sys.argv[1:]) | 318 DoMain(sys.argv[1:]) |
OLD | NEW |