Chromium Code Reviews| 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 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 Loading... | |
| 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:]) |
| OLD | NEW |