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

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: minor change Created 4 years, 9 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
« no previous file with comments | « no previous file | build/android/gyp/java_cpp_string_tests.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 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
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:])
OLDNEW
« no previous file with comments | « no previous file | build/android/gyp/java_cpp_string_tests.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698