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

Side by Side Diff: build/android/gyp/java_cpp_string.py

Issue 1664113002: C++->Java string constants auto-generator (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@contentsettingstype_enum_2_string
Patch Set: set upstream back to ContentSettingsType-enum-to-string Created 4 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
OLDNEW
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 self._current_multiline_string_key:
raymes 2016/02/18 02:22:32 Perhaps make a comment here about what's going on:
lshang 2016/02/29 07:45:03 Done.
111 string_multi_line_end = (
112 CppStringFileParser.multi_string_line_end_re.match(line))
113 if not string_multi_line_end:
114 raise Exception('Incomplete multi-line string definition ' +
115 self._path)
raymes 2016/02/18 02:22:32 nit: indentation
lshang 2016/02/29 07:45:03 Done.
116 else:
raymes 2016/02/18 02:22:32 nit: the else isn't needed here
lshang 2016/02/29 07:45:03 Done.
117 string_key = self._current_multiline_string_key
118 string_value = string_multi_line_end.groups()[0]
119 self._current_definition.AppendEntry(string_key, string_value)
120 self._current_multiline_string_key = None
121 return
122
123 generator_directive = CppStringFileParser.generator_directive_re.match(line)
124 multi_line_generator_directive_start = (
125 CppStringFileParser.multi_line_generator_directive_start_re.match(line))
126 if generator_directive:
127 directive_name = generator_directive.groups()[0]
128 directive_value = generator_directive.groups()[1]
129 self._generator_directives.Update(directive_name, directive_value)
174 return 130 return
175 if HeaderParser.multi_line_comment_start_re.match(line): 131 elif multi_line_generator_directive_start:
raymes 2016/02/18 02:22:32 nit: elif->if
lshang 2016/02/29 07:45:03 Done.
176 raise Exception('Multi-line comments in enums are not supported in ' + 132 directive_name = multi_line_generator_directive_start.groups()[0]
133 directive_value = multi_line_generator_directive_start.groups()[1]
134 self._multi_line_generator_directive = (directive_name, [directive_value])
135 return
136
137 if CppStringFileParser.single_line_comment_re.match(line):
138 return
139 if CppStringFileParser.multi_line_comment_start_re.match(line):
140 raise Exception('Multi-line comments in strings are not supported in ' +
177 self._path) 141 self._path)
178 enum_end = HeaderParser.enum_end_re.match(line) 142
179 enum_entry = HeaderParser.enum_line_re.match(line) 143 string_multi_line_start = (
180 if enum_end: 144 CppStringFileParser.multi_string_line_start_re.match(line))
181 self._ApplyGeneratorDirectives() 145 single_string_line = CppStringFileParser.single_string_line_re.match(line)
182 self._current_definition.Finalize() 146 if string_multi_line_start or single_string_line:
183 self._enum_definitions.append(self._current_definition) 147 if self._generator_directives.empty:
184 self._in_enum = False 148 return
185 elif enum_entry: 149 if string_multi_line_start:
186 enum_key = enum_entry.groups()[0] 150 self._current_multiline_string_key = string_multi_line_start.groups()[0]
187 enum_value = enum_entry.groups()[2] 151 return
188 self._current_definition.AppendEntry(enum_key, enum_value) 152 elif single_string_line:
raymes 2016/02/18 02:22:31 nit: elif->if
lshang 2016/02/29 07:45:02 Done.
153 string_key = single_string_line.groups()[0]
154 string_value = single_string_line.groups()[1]
155 self._current_definition.AppendEntry(string_key, string_value)
156 return
157
189 158
190 def _ParseMultiLineDirectiveLine(self, line): 159 def _ParseMultiLineDirectiveLine(self, line):
191 multi_line_directive_continuation = ( 160 multi_line_directive_continuation = (
192 HeaderParser.multi_line_directive_continuation_re.match(line)) 161 CppStringFileParser.multi_line_directive_continuation_re.match(line))
193 multi_line_directive_end = ( 162 multi_line_directive_end = (
194 HeaderParser.multi_line_directive_end_re.match(line)) 163 CppStringFileParser.multi_line_directive_end_re.match(line))
195 164
196 if multi_line_directive_continuation: 165 if multi_line_directive_continuation:
197 value_cont = multi_line_directive_continuation.groups()[0] 166 value_cont = multi_line_directive_continuation.groups()[0]
198 self._multi_line_generator_directive[1].append(value_cont) 167 self._multi_line_generator_directive[1].append(value_cont)
199 elif multi_line_directive_end: 168 elif multi_line_directive_end:
200 directive_name = self._multi_line_generator_directive[0] 169 directive_name = self._multi_line_generator_directive[0]
201 directive_value = "".join(self._multi_line_generator_directive[1]) 170 directive_value = "".join(self._multi_line_generator_directive[1])
202 directive_value += multi_line_directive_end.groups()[0] 171 directive_value += multi_line_directive_end.groups()[0]
203 self._multi_line_generator_directive = None 172 self._multi_line_generator_directive = None
204 self._generator_directives.Update(directive_name, directive_value) 173 self._generator_directives.Update(directive_name, directive_value)
205 else: 174 else:
206 raise Exception('Malformed multi-line directive declaration in ' + 175 raise Exception('Malformed multi-line directive declaration in ' +
207 self._path) 176 self._path)
208 177
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 178
231 def GetScriptName(): 179 def GetScriptName():
232 return os.path.basename(os.path.abspath(sys.argv[0])) 180 return os.path.basename(os.path.abspath(sys.argv[0]))
233 181
234 def DoGenerate(source_paths): 182 def DoGenerate(source_paths):
235 for source_path in source_paths: 183 for source_path in source_paths:
236 enum_definitions = DoParseHeaderFile(source_path) 184 string_definition = DoParseHeaderFile(source_path)
237 if not enum_definitions: 185 if not string_definition:
238 raise Exception('No enums found in %s\n' 186 raise Exception('No strings found in %s\n'
239 'Did you forget prefixing enums with ' 187 'Did you forget prefixing strings with '
240 '"// GENERATED_JAVA_ENUM_PACKAGE: foo"?' % 188 '"// GENERATED_JAVA_STRING_PACKAGE: foo"?' %
241 source_path) 189 source_path)
242 for enum_definition in enum_definitions: 190 #for string_definition in string_definitions:
243 package_path = enum_definition.enum_package.replace('.', os.path.sep) 191 package_path = string_definition.string_package.replace('.', os.path.sep)
244 file_name = enum_definition.class_name + '.java' 192 file_name = string_definition.class_name + '.java'
245 output_path = os.path.join(package_path, file_name) 193 output_path = os.path.join(package_path, file_name)
246 output = GenerateOutput(source_path, enum_definition) 194 output = GenerateOutput(source_path, string_definition)
247 yield output_path, output 195 yield output_path, output
248
249 196
250 def DoParseHeaderFile(path): 197 def DoParseHeaderFile(path):
251 with open(path) as f: 198 with open(path) as f:
252 return HeaderParser(f.readlines(), path).ParseDefinitions() 199 return CppStringFileParser(f.readlines(), path).ParseDefinitions()
253 200
254 201
255 def GenerateOutput(source_path, enum_definition): 202 def GenerateOutput(source_path, string_definition):
256 template = Template(""" 203 template = Template("""
257 // Copyright ${YEAR} The Chromium Authors. All rights reserved. 204 // Copyright ${YEAR} The Chromium Authors. All rights reserved.
258 // Use of this source code is governed by a BSD-style license that can be 205 // Use of this source code is governed by a BSD-style license that can be
259 // found in the LICENSE file. 206 // found in the LICENSE file.
260 207
261 // This file is autogenerated by 208 // This file is autogenerated by
262 // ${SCRIPT_NAME} 209 // ${SCRIPT_NAME}
263 // From 210 // From
264 // ${SOURCE_PATH} 211 // ${SOURCE_PATH}
265 212
266 package ${PACKAGE}; 213 package ${PACKAGE};
267 214
268 public class ${CLASS_NAME} { 215 public class ${CLASS_NAME} {
269 ${ENUM_ENTRIES} 216 ${STRING_ENTRIES}
270 } 217 }
271 """) 218 """)
272 219
273 enum_template = Template(' public static final int ${NAME} = ${VALUE};') 220 string_template = (
274 enum_entries_string = [] 221 Template(' public static final String ${NAME} = "${VALUE}";'))
275 for enum_name, enum_value in enum_definition.entries.iteritems(): 222 string_entries_string = []
223 for string_name, string_value in string_definition.entries.iteritems():
276 values = { 224 values = {
277 'NAME': enum_name, 225 'NAME': string_name,
278 'VALUE': enum_value, 226 'VALUE': string_value,
279 } 227 }
280 enum_entries_string.append(enum_template.substitute(values)) 228 string_entries_string.append(string_template.substitute(values))
281 enum_entries_string = '\n'.join(enum_entries_string) 229 string_entries_string = '\n'.join(string_entries_string)
282 230
283 values = { 231 values = {
284 'CLASS_NAME': enum_definition.class_name, 232 'CLASS_NAME': string_definition.class_name,
285 'ENUM_ENTRIES': enum_entries_string, 233 'STRING_ENTRIES': string_entries_string,
286 'PACKAGE': enum_definition.enum_package, 234 'PACKAGE': string_definition.string_package,
287 'SCRIPT_NAME': GetScriptName(), 235 'SCRIPT_NAME': GetScriptName(),
288 'SOURCE_PATH': source_path, 236 'SOURCE_PATH': source_path,
289 'YEAR': str(date.today().year) 237 'YEAR': str(date.today().year)
290 } 238 }
291 return template.substitute(values) 239 return template.substitute(values)
292 240
293 241
294 def AssertFilesList(output_paths, assert_files_list): 242 def AssertFilesList(output_paths, assert_files_list):
295 actual = set(output_paths) 243 actual = set(output_paths)
296 expected = set(assert_files_list) 244 expected = set(assert_files_list)
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
357 if options.assert_files_list: 305 if options.assert_files_list:
358 AssertFilesList(output_paths, options.assert_files_list) 306 AssertFilesList(output_paths, options.assert_files_list)
359 307
360 if options.verbose: 308 if options.verbose:
361 print 'Output paths:' 309 print 'Output paths:'
362 print '\n'.join(output_paths) 310 print '\n'.join(output_paths)
363 311
364 # Used by GYP. 312 # Used by GYP.
365 return ' '.join(output_paths) 313 return ' '.join(output_paths)
366 314
367
368 if __name__ == '__main__': 315 if __name__ == '__main__':
369 DoMain(sys.argv[1:]) 316 DoMain(sys.argv[1:])
OLDNEW
« no previous file with comments | « no previous file | build/android/gyp/java_cpp_string_tests.py » ('j') | build/android/gyp/java_cpp_string_tests.py » ('J')

Powered by Google App Engine
This is Rietveld 408576698