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 StringDefinition(object): |
raymes
2016/02/16 00:58:21
I guess this is actually a list of strings. So per
lshang
2016/02/16 23:00:19
Done.
| |
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 HeaderParser(object): |
raymes
2016/02/16 00:58:20
This is no longer parsing a header file, so perhap
lshang
2016/02/16 23:00:20
Done.
| |
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 multi_line_comment_end_re = re.compile(r'\s*\*/') |
129 enum_end_re = re.compile(r'^\s*}\s*;\.*$') | 67 single_string_line_re = re.compile( |
68 r'^\s*const\s*char\s*(\w+)\[\]\s*\=\s*([^,\n]+)?"([^"]*)";') | |
raymes
2016/02/16 00:58:21
We may not need ([^,\n]+)?
lshang
2016/02/16 23:00:19
Done.
| |
69 multi_string_line_start_re = re.compile( | |
70 r'^\s*const\s*char\s*(\w+)\[\]\s*\=?\s*$') | |
71 multi_string_line_end_re = re.compile( | |
72 r'^\s*\=?\s*"([^"]*)";') | |
130 generator_directive_re = re.compile( | 73 generator_directive_re = re.compile( |
131 r'^\s*//\s+GENERATED_JAVA_(\w+)\s*:\s*([\.\w]+)$') | 74 r'^\s*//\s+GENERATED_JAVA_(\w+)\s*:\s*([\.\w]+)$') |
132 multi_line_generator_directive_start_re = re.compile( | 75 multi_line_generator_directive_start_re = re.compile( |
133 r'^\s*//\s+GENERATED_JAVA_(\w+)\s*:\s*\(([\.\w]*)$') | 76 r'^\s*//\s+GENERATED_JAVA_(\w+)\s*:\s*\(([\.\w]*)$') |
134 multi_line_directive_continuation_re = re.compile( | 77 multi_line_directive_continuation_re = re.compile( |
135 r'^\s*//\s+([\.\w]+)$') | 78 r'^\s*//\s+([\.\w]+)$') |
136 multi_line_directive_end_re = re.compile( | 79 multi_line_directive_end_re = re.compile( |
137 r'^\s*//\s+([\.\w]*)\)$') | 80 r'^\s*//\s+([\.\w]*)\)$') |
138 | 81 |
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 | 82 |
146 def __init__(self, lines, path=None): | 83 def __init__(self, lines, path=None): |
147 self._lines = lines | 84 self._lines = lines |
148 self._path = path | 85 self._path = path |
149 self._enum_definitions = [] | 86 self._string_definitions = [] |
150 self._in_enum = False | |
151 self._current_definition = None | 87 self._current_definition = None |
152 self._generator_directives = DirectiveSet() | 88 self._generator_directives = DirectiveSet() |
153 self._multi_line_generator_directive = None | 89 self._multi_line_generator_directive = None |
90 self._current_key = None | |
raymes
2016/02/16 00:58:21
maybe call this:
_current_multiline_string_key
lshang
2016/02/16 23:00:20
Done.
| |
91 self._in_comment = False | |
154 | 92 |
155 def _ApplyGeneratorDirectives(self): | 93 def _ApplyGeneratorDirectives(self): |
156 self._generator_directives.UpdateDefinition(self._current_definition) | 94 self._generator_directives.UpdateDefinition(self._current_definition) |
157 self._generator_directives = DirectiveSet() | 95 self._generator_directives = DirectiveSet() |
158 | 96 |
159 def ParseDefinitions(self): | 97 def ParseDefinitions(self): |
98 self._current_definition = StringDefinition() | |
160 for line in self._lines: | 99 for line in self._lines: |
161 self._ParseLine(line) | 100 self._ParseLine(line) |
162 return self._enum_definitions | 101 self._ApplyGeneratorDirectives() |
102 self._current_definition.Finalize() | |
103 return self._current_definition | |
163 | 104 |
164 def _ParseLine(self, line): | 105 def _ParseLine(self, line): |
165 if self._multi_line_generator_directive: | 106 if self._multi_line_generator_directive: |
166 self._ParseMultiLineDirectiveLine(line) | 107 self._ParseMultiLineDirectiveLine(line) |
167 elif not self._in_enum: | 108 elif self._in_comment: |
168 self._ParseRegularLine(line) | 109 self._ParseCommentLine(line) |
169 else: | 110 else: |
170 self._ParseEnumLine(line) | 111 self._ParseStringLine(line) |
171 | 112 |
172 def _ParseEnumLine(self, line): | 113 def _ParseStringLine(self, line): |
114 generator_directive = HeaderParser.generator_directive_re.match(line) | |
115 multi_line_generator_directive_start = ( | |
116 HeaderParser.multi_line_generator_directive_start_re.match(line)) | |
117 if generator_directive: | |
118 directive_name = generator_directive.groups()[0] | |
119 directive_value = generator_directive.groups()[1] | |
120 self._generator_directives.Update(directive_name, directive_value) | |
121 return | |
122 elif multi_line_generator_directive_start: | |
123 directive_name = multi_line_generator_directive_start.groups()[0] | |
124 directive_value = multi_line_generator_directive_start.groups()[1] | |
125 self._multi_line_generator_directive = (directive_name, [directive_value]) | |
126 return | |
127 | |
173 if HeaderParser.single_line_comment_re.match(line): | 128 if HeaderParser.single_line_comment_re.match(line): |
174 return | 129 return |
175 if HeaderParser.multi_line_comment_start_re.match(line): | 130 if HeaderParser.multi_line_comment_start_re.match(line): |
176 raise Exception('Multi-line comments in enums are not supported in ' + | 131 self._in_comment = True |
raymes
2016/02/16 00:58:21
Could we just ignore multi line comments, as in th
lshang
2016/02/16 23:00:19
Done.
| |
177 self._path) | 132 return |
178 enum_end = HeaderParser.enum_end_re.match(line) | 133 |
179 enum_entry = HeaderParser.enum_line_re.match(line) | 134 string_multi_line_start = ( |
180 if enum_end: | 135 HeaderParser.multi_string_line_start_re.match(line)) |
181 self._ApplyGeneratorDirectives() | 136 single_string_line = HeaderParser.single_string_line_re.match(line) |
182 self._current_definition.Finalize() | 137 if string_multi_line_start or single_string_line: |
183 self._enum_definitions.append(self._current_definition) | 138 if self._generator_directives.empty: |
184 self._in_enum = False | 139 return |
185 elif enum_entry: | 140 if string_multi_line_start: |
186 enum_key = enum_entry.groups()[0] | 141 self._current_key = string_multi_line_start.groups()[0] |
187 enum_value = enum_entry.groups()[2] | 142 return |
188 self._current_definition.AppendEntry(enum_key, enum_value) | 143 elif single_string_line: |
144 string_key = single_string_line.groups()[0] | |
145 string_value = single_string_line.groups()[2] | |
146 self._current_definition.AppendEntry(string_key, string_value) | |
147 return | |
148 | |
149 string_multi_line_end = HeaderParser.multi_string_line_end_re.match(line) | |
150 if string_multi_line_end: | |
151 string_key = self._current_key | |
raymes
2016/02/16 00:58:21
Maybe a slightly different approach would be safer
lshang
2016/02/16 23:00:20
Done.
| |
152 string_value = string_multi_line_end.groups()[0] | |
153 self._current_definition.AppendEntry(string_key, string_value) | |
154 return | |
155 | |
189 | 156 |
190 def _ParseMultiLineDirectiveLine(self, line): | 157 def _ParseMultiLineDirectiveLine(self, line): |
191 multi_line_directive_continuation = ( | 158 multi_line_directive_continuation = ( |
192 HeaderParser.multi_line_directive_continuation_re.match(line)) | 159 HeaderParser.multi_line_directive_continuation_re.match(line)) |
193 multi_line_directive_end = ( | 160 multi_line_directive_end = ( |
194 HeaderParser.multi_line_directive_end_re.match(line)) | 161 HeaderParser.multi_line_directive_end_re.match(line)) |
195 | 162 |
196 if multi_line_directive_continuation: | 163 if multi_line_directive_continuation: |
197 value_cont = multi_line_directive_continuation.groups()[0] | 164 value_cont = multi_line_directive_continuation.groups()[0] |
198 self._multi_line_generator_directive[1].append(value_cont) | 165 self._multi_line_generator_directive[1].append(value_cont) |
199 elif multi_line_directive_end: | 166 elif multi_line_directive_end: |
200 directive_name = self._multi_line_generator_directive[0] | 167 directive_name = self._multi_line_generator_directive[0] |
201 directive_value = "".join(self._multi_line_generator_directive[1]) | 168 directive_value = "".join(self._multi_line_generator_directive[1]) |
202 directive_value += multi_line_directive_end.groups()[0] | 169 directive_value += multi_line_directive_end.groups()[0] |
203 self._multi_line_generator_directive = None | 170 self._multi_line_generator_directive = None |
204 self._generator_directives.Update(directive_name, directive_value) | 171 self._generator_directives.Update(directive_name, directive_value) |
205 else: | 172 else: |
206 raise Exception('Malformed multi-line directive declaration in ' + | 173 raise Exception('Malformed multi-line directive declaration in ' + |
207 self._path) | 174 self._path) |
208 | 175 |
209 def _ParseRegularLine(self, line): | 176 def _ParseCommentLine(self, line): |
210 enum_start = HeaderParser.enum_start_re.match(line) | 177 multi_line_comment_end = HeaderParser.multi_line_comment_end_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 | 178 |
215 if generator_directive: | 179 if multi_line_comment_end: |
216 directive_name = generator_directive.groups()[0] | 180 self._in_comment = False |
217 directive_value = generator_directive.groups()[1] | 181 return |
218 self._generator_directives.Update(directive_name, directive_value) | 182 |
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 | 183 |
231 def GetScriptName(): | 184 def GetScriptName(): |
232 return os.path.basename(os.path.abspath(sys.argv[0])) | 185 script_components = os.path.abspath(sys.argv[0]).split(os.path.sep) |
186 build_index = script_components.index('build') | |
187 return os.sep.join(script_components[build_index:]) | |
raymes
2016/02/16 00:58:21
Hmm I'm not sure I understand why this changed?
lshang
2016/02/16 23:00:19
Done.
java_cpp_enum.py changed this on 2016-01-12,
| |
233 | 188 |
234 def DoGenerate(source_paths): | 189 def DoGenerate(source_paths): |
235 for source_path in source_paths: | 190 for source_path in source_paths: |
236 enum_definitions = DoParseHeaderFile(source_path) | 191 string_definition = DoParseHeaderFile(source_path) |
237 if not enum_definitions: | 192 if not string_definition: |
238 raise Exception('No enums found in %s\n' | 193 raise Exception('No strings found in %s\n' |
239 'Did you forget prefixing enums with ' | 194 'Did you forget prefixing strings with ' |
240 '"// GENERATED_JAVA_ENUM_PACKAGE: foo"?' % | 195 '"// GENERATED_JAVA_STRING_PACKAGE: foo"?' % |
241 source_path) | 196 source_path) |
242 for enum_definition in enum_definitions: | 197 #for string_definition in string_definitions: |
243 package_path = enum_definition.enum_package.replace('.', os.path.sep) | 198 package_path = string_definition.string_package.replace('.', os.path.sep) |
244 file_name = enum_definition.class_name + '.java' | 199 file_name = string_definition.class_name + '.java' |
245 output_path = os.path.join(package_path, file_name) | 200 output_path = os.path.join(package_path, file_name) |
246 output = GenerateOutput(source_path, enum_definition) | 201 output = GenerateOutput(source_path, string_definition) |
247 yield output_path, output | 202 yield output_path, output |
248 | |
249 | 203 |
250 def DoParseHeaderFile(path): | 204 def DoParseHeaderFile(path): |
251 with open(path) as f: | 205 with open(path) as f: |
252 return HeaderParser(f.readlines(), path).ParseDefinitions() | 206 return HeaderParser(f.readlines(), path).ParseDefinitions() |
253 | 207 |
254 | 208 |
255 def GenerateOutput(source_path, enum_definition): | 209 def GenerateOutput(source_path, string_definition): |
256 template = Template(""" | 210 template = Template(""" |
257 // Copyright ${YEAR} The Chromium Authors. All rights reserved. | 211 // Copyright ${YEAR} The Chromium Authors. All rights reserved. |
258 // Use of this source code is governed by a BSD-style license that can be | 212 // Use of this source code is governed by a BSD-style license that can be |
259 // found in the LICENSE file. | 213 // found in the LICENSE file. |
260 | 214 |
261 // This file is autogenerated by | 215 // This file is autogenerated by |
262 // ${SCRIPT_NAME} | 216 // ${SCRIPT_NAME} |
263 // From | 217 // From |
264 // ${SOURCE_PATH} | 218 // ${SOURCE_PATH} |
265 | 219 |
266 package ${PACKAGE}; | 220 package ${PACKAGE}; |
267 | 221 |
268 public class ${CLASS_NAME} { | 222 public class ${CLASS_NAME} { |
269 ${ENUM_ENTRIES} | 223 ${STRING_ENTRIES} |
270 } | 224 } |
271 """) | 225 """) |
272 | 226 |
273 enum_template = Template(' public static final int ${NAME} = ${VALUE};') | 227 string_template = ( |
274 enum_entries_string = [] | 228 Template(' public static final String ${NAME} = "${VALUE}";')) |
275 for enum_name, enum_value in enum_definition.entries.iteritems(): | 229 string_entries_string = [] |
230 for string_name, string_value in string_definition.entries.iteritems(): | |
276 values = { | 231 values = { |
277 'NAME': enum_name, | 232 'NAME': string_name, |
278 'VALUE': enum_value, | 233 'VALUE': string_value, |
279 } | 234 } |
280 enum_entries_string.append(enum_template.substitute(values)) | 235 string_entries_string.append(string_template.substitute(values)) |
281 enum_entries_string = '\n'.join(enum_entries_string) | 236 string_entries_string = '\n'.join(string_entries_string) |
282 | 237 |
283 values = { | 238 values = { |
284 'CLASS_NAME': enum_definition.class_name, | 239 'CLASS_NAME': string_definition.class_name, |
285 'ENUM_ENTRIES': enum_entries_string, | 240 'STRING_ENTRIES': string_entries_string, |
286 'PACKAGE': enum_definition.enum_package, | 241 'PACKAGE': string_definition.string_package, |
287 'SCRIPT_NAME': GetScriptName(), | 242 'SCRIPT_NAME': GetScriptName(), |
288 'SOURCE_PATH': source_path, | 243 'SOURCE_PATH': source_path, |
289 'YEAR': str(date.today().year) | 244 'YEAR': str(date.today().year) |
290 } | 245 } |
291 return template.substitute(values) | 246 return template.substitute(values) |
292 | 247 |
293 | 248 |
294 def AssertFilesList(output_paths, assert_files_list): | 249 def AssertFilesList(output_paths, assert_files_list): |
295 actual = set(output_paths) | 250 actual = set(output_paths) |
296 expected = set(assert_files_list) | 251 expected = set(assert_files_list) |
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
357 if options.assert_files_list: | 312 if options.assert_files_list: |
358 AssertFilesList(output_paths, options.assert_files_list) | 313 AssertFilesList(output_paths, options.assert_files_list) |
359 | 314 |
360 if options.verbose: | 315 if options.verbose: |
361 print 'Output paths:' | 316 print 'Output paths:' |
362 print '\n'.join(output_paths) | 317 print '\n'.join(output_paths) |
363 | 318 |
364 # Used by GYP. | 319 # Used by GYP. |
365 return ' '.join(output_paths) | 320 return ' '.join(output_paths) |
366 | 321 |
367 | |
368 if __name__ == '__main__': | 322 if __name__ == '__main__': |
369 DoMain(sys.argv[1:]) | 323 DoMain(sys.argv[1:]) |
OLD | NEW |