OLD | NEW |
---|---|
1 #!/usr/bin/python | 1 #!/usr/bin/python |
2 # Copyright 2014 The Chromium Authors. All rights reserved. | 2 # Copyright 2014 The Chromium Authors. All rights reserved. |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 | 5 |
6 import argparse | |
6 import collections | 7 import collections |
7 import hashlib | 8 import hashlib |
8 import operator | 9 import operator |
9 import os | 10 import os |
10 import re | 11 import re |
11 import sys | 12 import sys |
12 | 13 |
13 | 14 SCRIPT_NAME = "generate_ui_string_overrider.py" |
14 RESOURCE_EXTRACT_REGEX = re.compile('^#define (\S*) (\d*)$', re.MULTILINE) | 15 RESOURCE_EXTRACT_REGEX = re.compile('^#define (\S*) (\d*)$', re.MULTILINE) |
15 | 16 |
16 class Error(Exception): | 17 class Error(Exception): |
17 """Base error class for all exceptions in generated_resources_map.""" | 18 """Base error class for all exceptions in generated_resources_map.""" |
18 | 19 |
19 | 20 |
20 class HashCollisionError(Error): | 21 class HashCollisionError(Error): |
21 """Multiple resource names hash to the same value.""" | 22 """Multiple resource names hash to the same value.""" |
22 | 23 |
23 | 24 |
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
116 lines = [entry_pattern % (data_getter(r), r.name) for r in resources] | 117 lines = [entry_pattern % (data_getter(r), r.name) for r in resources] |
117 pattern = """const %(type)s %(name)s[] = { | 118 pattern = """const %(type)s %(name)s[] = { |
118 %(content)s | 119 %(content)s |
119 }; | 120 }; |
120 """ | 121 """ |
121 return pattern % {'type': array_type, | 122 return pattern % {'type': array_type, |
122 'name': array_name, | 123 'name': array_name, |
123 'content': '\n'.join(lines)} | 124 'content': '\n'.join(lines)} |
124 | 125 |
125 | 126 |
126 def _GenerateFileContent(resources_content): | 127 def _GenerateSourceFileContent(resources_content, namespace, header_filename): |
127 """Generates the .cc content from the given generated_resources.h content. | 128 """Generates the .cc content from the given generated grit headers content. |
128 | 129 |
129 Args: | 130 Args: |
130 resources_content: The input string to process, contains lines of the form | 131 resources_content: The input string to process, contains lines of the form |
131 "#define NAME INDEX". | 132 "#define NAME INDEX". |
132 | 133 |
134 namespace: The namespace in which the generated code should be scoped. If | |
135 not defined, then the code will be in the global namespace. | |
136 | |
137 header_filename: Path to the corresponding .h. | |
138 | |
133 Returns: | 139 Returns: |
134 .cc file content defining the kResourceHashes and kResourceIndices arrays. | 140 .cc file content implementing the CreateUIStringOverrider() factory. |
135 """ | 141 """ |
136 hashed_tuples = _GetResourceListFromString(resources_content) | 142 hashed_tuples = _GetResourceListFromString(resources_content) |
137 | 143 |
138 collisions = _CheckForHashCollisions(hashed_tuples) | 144 collisions = _CheckForHashCollisions(hashed_tuples) |
139 if collisions: | 145 if collisions: |
140 error_message = "\n".join( | 146 error_message = "\n".join( |
141 ["hash: %i, name: %s" % (i[0], i[1]) for i in sorted(collisions)]) | 147 ["hash: %i, name: %s" % (i[0], i[1]) for i in sorted(collisions)]) |
142 error_message = ("\nThe following names had hash collisions " | 148 error_message = ("\nThe following names had hash collisions " |
143 "(sorted by the hash value):\n%s\n" %(error_message)) | 149 "(sorted by the hash value):\n%s\n" %(error_message)) |
144 raise HashCollisionError(error_message) | 150 raise HashCollisionError(error_message) |
145 | 151 |
146 hashes_array = _GenDataArray( | 152 hashes_array = _GenDataArray( |
147 hashed_tuples, " %iU, // %s", 'kResourceHashes', 'uint32_t', | 153 hashed_tuples, " %iU, // %s", 'kResourceHashes', 'uint32_t', |
148 operator.attrgetter('hash')) | 154 operator.attrgetter('hash')) |
149 indices_array = _GenDataArray( | 155 indices_array = _GenDataArray( |
150 hashed_tuples, " %s, // %s", 'kResourceIndices', 'int', | 156 hashed_tuples, " %s, // %s", 'kResourceIndices', 'int', |
151 operator.attrgetter('index')) | 157 operator.attrgetter('index')) |
152 | 158 |
159 namespace_prefix = "" | |
160 namespace_suffix = "" | |
161 if namespace: | |
162 namespace_prefix = "namespace %s {\n\n" % namespace | |
163 namespace_suffix = "\n} // namespace %s\n" % namespace | |
164 | |
153 return ( | 165 return ( |
154 "// This file was generated by generate_resources_map.py. Do not edit.\n" | 166 "// This file was generated by %s. Do not edit.\n" |
155 "\n\n" | 167 "\n" |
156 "#include " | 168 "#include \"%s\"\n\n" |
157 "\"chrome/browser/metrics/variations/generated_resources_map.h\"\n\n" | 169 "%s" |
158 "namespace chrome_variations {\n\n" | 170 "namespace {\n\n" |
159 "const size_t kNumResources = %i;\n\n" | 171 "const size_t kNumResources = %i;\n\n" |
160 "%s" | 172 "%s" |
161 "\n" | 173 "\n" |
162 "%s" | 174 "%s" |
163 "\n" | 175 "\n" |
164 "} // namespace chrome_variations\n") % ( | 176 "} // namespace\n" |
165 len(hashed_tuples), hashes_array, indices_array) | 177 "\n" |
178 "variations::UIStringOverrider CreateUIStringOverrider() {\n" | |
179 " return variations::UIStringOverrider(\n" | |
180 " kResourceHashes, kResourceIndices, kNumResources);\n" | |
181 "}\n" | |
182 "%s") % ( | |
jwd
2015/09/29 15:02:54
Can you use dictionary based string interpolation
sdefresne
2015/09/30 09:38:43
Done.
| |
183 SCRIPT_NAME, header_filename, namespace_prefix, len(hashed_tuples), | |
184 hashes_array, indices_array, namespace_suffix) | |
166 | 185 |
167 | 186 |
168 def main(resources_file, map_file): | 187 def _GenerateHeaderFileContent(namespace, header_filename): |
188 """Generates the .h for to the .cc generated by _GenerateSourceFileContent. | |
189 | |
190 Args: | |
191 namespace: The namespace in which the generated code should be scoped. If | |
192 not defined, then the code will be in the global namespace. | |
193 | |
194 header_filename: Path to the corresponding .h. Used to generate the include | |
195 guards. | |
196 | |
197 Returns: | |
198 .cc file content implementing the CreateUIStringOverrider() factory. | |
199 """ | |
200 | |
201 include_guard = re.sub('[^A-Z]', '_', header_filename.upper()) + '_' | |
202 | |
203 namespace_prefix = "" | |
204 namespace_suffix = "" | |
205 if namespace: | |
206 namespace_prefix = "namespace %s {\n\n" % namespace | |
207 namespace_suffix = "\n} // namespace %s\n" % namespace | |
208 | |
209 return ( | |
210 "// This file was generated by %s. Do not edit.\n" | |
211 "\n" | |
212 "#ifndef %s\n" | |
213 "#define %s\n" | |
214 "\n" | |
215 "#include \"components/variations/service/ui_string_overrider.h\"\n\n" | |
216 "%s" | |
217 "// Returns an initialized UIStringOverrider.\n" | |
218 "variations::UIStringOverrider CreateUIStringOverrider();\n" | |
219 "%s" | |
220 "\n" | |
221 "#endif // %s\n" | |
jwd
2015/09/29 15:02:54
optional nit: use dictionary string interpolation
sdefresne
2015/09/30 09:38:43
Done.
| |
222 ) % ( | |
223 SCRIPT_NAME, include_guard, include_guard, namespace_prefix, | |
224 namespace_suffix, include_guard) | |
225 | |
226 | |
227 def main(): | |
228 arg_parser = argparse.ArgumentParser( | |
229 description="Generate UIStringOverrider factory from resources headers " | |
230 "generated by grit.") | |
231 arg_parser.add_argument( | |
232 "--output_dir", "-o", required=True, | |
233 help="Base directory to for generated files.") | |
234 arg_parser.add_argument( | |
235 "--source_filename", "-S", required=True, | |
236 help="File name of the generated source file.") | |
237 arg_parser.add_argument( | |
238 "--header_filename", "-H", required=True, | |
239 help="File name of the generated header file.") | |
240 arg_parser.add_argument( | |
241 "--namespace", "-N", default="", | |
242 help="Namespace of the generated factory function (code will be in " | |
243 "the global namespace if this is omitted).") | |
244 arg_parser.add_argument( | |
245 "--test_support", "-t", action="store_true", default=False, | |
246 help="Make internal variables accessible for testing.") | |
247 arg_parser.add_argument( | |
248 "inputs", metavar="FILENAME", nargs="+", | |
249 help="Path to resources header file generated by grit.") | |
250 arguments = arg_parser.parse_args() | |
251 | |
169 generated_resources_h = "" | 252 generated_resources_h = "" |
170 with open(resources_file, "r") as resources: | 253 for resources_file in arguments.inputs: |
171 generated_resources_h = resources.read() | 254 with open(resources_file, "r") as resources: |
255 generated_resources_h += resources.read() | |
172 | 256 |
173 if len(generated_resources_h) == 0: | 257 if len(generated_resources_h) == 0: |
174 raise Error("No content loaded for %s." % (resources_file)) | 258 raise Error("No content loaded for %s." % (resources_file)) |
175 | 259 |
176 file_content = _GenerateFileContent(generated_resources_h) | 260 source_file_content = _GenerateSourceFileContent( |
261 generated_resources_h, arguments.namespace, arguments.header_filename) | |
262 header_file_content = _GenerateHeaderFileContent( | |
263 arguments.namespace, arguments.header_filename) | |
177 | 264 |
178 with open(map_file, "w") as generated_file: | 265 with open(os.path.join( |
179 generated_file.write(file_content) | 266 arguments.output_dir, arguments.source_filename), "w") as generated_file: |
267 generated_file.write(source_file_content) | |
268 with open(os.path.join( | |
269 arguments.output_dir, arguments.header_filename), "w") as generated_file: | |
270 generated_file.write(header_file_content) | |
180 | 271 |
181 | 272 |
182 if __name__ == '__main__': | 273 if __name__ == '__main__': |
183 sys.exit(main(sys.argv[1], sys.argv[2])) | 274 sys.exit(main()) |
OLD | NEW |