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

Side by Side Diff: pylib/gyp/generator/analyzer.py

Issue 445113002: Style changes for analyzer (Closed) Base URL: http://gyp.googlecode.com/svn/trunk/
Patch Set: Created 6 years, 4 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 | Annotate | Revision Log
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright (c) 2014 Google Inc. All rights reserved. 1 # Copyright (c) 2014 Google Inc. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 """ 5 """
6 This script is intended for use as a GYP_GENERATOR. It takes as input (by way of 6 This script is intended for use as a GYP_GENERATOR. It takes as input (by way of
7 the generator flag config_path) the path of a json file that dictates the files 7 the generator flag config_path) the path of a json file that dictates the files
8 and targets to search for. The following keys are supported: 8 and targets to search for. The following keys are supported:
9 files: list of paths (relative) of the files to search for. 9 files: list of paths (relative) of the files to search for.
10 targets: list of targets to search for. The target names are unqualified. 10 targets: list of targets to search for. The target names are unqualified.
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
57 generator_default_variables[dirname] = '!!!' 57 generator_default_variables[dirname] = '!!!'
58 58
59 for unused in ['RULE_INPUT_PATH', 'RULE_INPUT_ROOT', 'RULE_INPUT_NAME', 59 for unused in ['RULE_INPUT_PATH', 'RULE_INPUT_ROOT', 'RULE_INPUT_NAME',
60 'RULE_INPUT_DIRNAME', 'RULE_INPUT_EXT', 60 'RULE_INPUT_DIRNAME', 'RULE_INPUT_EXT',
61 'EXECUTABLE_PREFIX', 'EXECUTABLE_SUFFIX', 61 'EXECUTABLE_PREFIX', 'EXECUTABLE_SUFFIX',
62 'STATIC_LIB_PREFIX', 'STATIC_LIB_SUFFIX', 62 'STATIC_LIB_PREFIX', 'STATIC_LIB_SUFFIX',
63 'SHARED_LIB_PREFIX', 'SHARED_LIB_SUFFIX', 63 'SHARED_LIB_PREFIX', 'SHARED_LIB_SUFFIX',
64 'CONFIGURATION_NAME']: 64 'CONFIGURATION_NAME']:
65 generator_default_variables[unused] = '' 65 generator_default_variables[unused] = ''
66 66
67
67 def _ToGypPath(path): 68 def _ToGypPath(path):
68 """Converts a path to the format used by gyp.""" 69 """Converts a path to the format used by gyp."""
69 if os.sep == '\\' and os.altsep == '/': 70 if os.sep == '\\' and os.altsep == '/':
70 return path.replace('\\', '/') 71 return path.replace('\\', '/')
71 return path 72 return path
72 73
73 def __ExtractBasePath(target):
74 """Extracts the path components of the specified gyp target path."""
75 last_index = target.rfind('/')
76 if last_index == -1:
77 return ''
78 return target[0:(last_index + 1)]
79 74
80 def __ResolveParent(path, base_path_components): 75 def _ResolveParent(path, base_path_components):
81 """Resolves |path|, which starts with at least one '../'. Returns an empty 76 """Resolves |path|, which starts with at least one '../'. Returns an empty
82 string if the path shouldn't be considered. See __AddSources() for a 77 string if the path shouldn't be considered. See _AddSources() for a
83 description of |base_path_components|.""" 78 description of |base_path_components|."""
84 depth = 0 79 depth = 0
85 while path.startswith('../'): 80 while path.startswith('../'):
86 depth += 1 81 depth += 1
87 path = path[3:] 82 path = path[3:]
88 # Relative includes may go outside the source tree. For example, an action may 83 # Relative includes may go outside the source tree. For example, an action may
89 # have inputs in /usr/include, which are not in the source tree. 84 # have inputs in /usr/include, which are not in the source tree.
90 if depth > len(base_path_components): 85 if depth > len(base_path_components):
91 return '' 86 return ''
92 if depth == len(base_path_components): 87 if depth == len(base_path_components):
93 return path 88 return path
94 return '/'.join(base_path_components[0:len(base_path_components) - depth]) + \ 89 return '/'.join(base_path_components[0:len(base_path_components) - depth]) + \
95 '/' + path 90 '/' + path
96 91
97 def __AddSources(sources, base_path, base_path_components, result): 92
93 def _AddSources(sources, base_path, base_path_components, result):
98 """Extracts valid sources from |sources| and adds them to |result|. Each 94 """Extracts valid sources from |sources| and adds them to |result|. Each
99 source file is relative to |base_path|, but may contain '..'. To make 95 source file is relative to |base_path|, but may contain '..'. To make
100 resolving '..' easier |base_path_components| contains each of the 96 resolving '..' easier |base_path_components| contains each of the
101 directories in |base_path|. Additionally each source may contain variables. 97 directories in |base_path|. Additionally each source may contain variables.
102 Such sources are ignored as it is assumed dependencies on them are expressed 98 Such sources are ignored as it is assumed dependencies on them are expressed
103 and tracked in some other means.""" 99 and tracked in some other means."""
104 # NOTE: gyp paths are always posix style. 100 # NOTE: gyp paths are always posix style.
105 for source in sources: 101 for source in sources:
106 if not len(source) or source.startswith('!!!') or source.startswith('$'): 102 if not len(source) or source.startswith('!!!') or source.startswith('$'):
107 continue 103 continue
108 # variable expansion may lead to //. 104 # variable expansion may lead to //.
109 org_source = source 105 org_source = source
110 source = source[0] + source[1:].replace('//', '/') 106 source = source[0] + source[1:].replace('//', '/')
111 if source.startswith('../'): 107 if source.startswith('../'):
112 source = __ResolveParent(source, base_path_components) 108 source = _ResolveParent(source, base_path_components)
113 if len(source): 109 if len(source):
114 result.append(source) 110 result.append(source)
115 continue 111 continue
116 result.append(base_path + source) 112 result.append(base_path + source)
117 if debug: 113 if debug:
118 print 'AddSource', org_source, result[len(result) - 1] 114 print 'AddSource', org_source, result[len(result) - 1]
119 115
120 def __ExtractSourcesFromAction(action, base_path, base_path_components, 116
121 results): 117 def _ExtractSourcesFromAction(action, base_path, base_path_components,
118 results):
122 if 'inputs' in action: 119 if 'inputs' in action:
123 __AddSources(action['inputs'], base_path, base_path_components, results) 120 _AddSources(action['inputs'], base_path, base_path_components, results)
124 121
125 def __ExtractSources(target, target_dict, toplevel_dir): 122
123 def _ExtractSources(target, target_dict, toplevel_dir):
126 # |target| is either absolute or relative and in the format of the OS. Gyp 124 # |target| is either absolute or relative and in the format of the OS. Gyp
127 # source paths are always posix. Convert |target| to a posix path relative to 125 # source paths are always posix. Convert |target| to a posix path relative to
128 # |toplevel_dir_|. This is done to make it easy to build source paths. 126 # |toplevel_dir_|. This is done to make it easy to build source paths.
129 base_path = _ToGypPath(target) 127 base_path = _ToGypPath(target)
130 if base_path == toplevel_dir: 128 if base_path == toplevel_dir:
131 base_path = '' 129 base_path = ''
132 elif base_path.startswith(toplevel_dir + '/'): 130 elif base_path.startswith(toplevel_dir + '/'):
133 base_path = base_path[len(toplevel_dir) + len('/'):] 131 base_path = base_path[len(toplevel_dir) + len('/'):]
134 base_path = posixpath.dirname(base_path) 132 base_path = posixpath.dirname(base_path)
135 base_path_components = base_path.split('/') 133 base_path_components = base_path.split('/')
136 134
137 # Add a trailing '/' so that __AddSources() can easily build paths. 135 # Add a trailing '/' so that _AddSources() can easily build paths.
138 if len(base_path): 136 if len(base_path):
139 base_path += '/' 137 base_path += '/'
140 138
141 if debug: 139 if debug:
142 print 'ExtractSources', target, base_path 140 print 'ExtractSources', target, base_path
143 141
144 results = [] 142 results = []
145 if 'sources' in target_dict: 143 if 'sources' in target_dict:
146 __AddSources(target_dict['sources'], base_path, base_path_components, 144 _AddSources(target_dict['sources'], base_path, base_path_components,
147 results) 145 results)
148 # Include the inputs from any actions. Any changes to these effect the 146 # Include the inputs from any actions. Any changes to these effect the
149 # resulting output. 147 # resulting output.
150 if 'actions' in target_dict: 148 if 'actions' in target_dict:
151 for action in target_dict['actions']: 149 for action in target_dict['actions']:
152 __ExtractSourcesFromAction(action, base_path, base_path_components, 150 _ExtractSourcesFromAction(action, base_path, base_path_components,
153 results) 151 results)
154 if 'rules' in target_dict: 152 if 'rules' in target_dict:
155 for rule in target_dict['rules']: 153 for rule in target_dict['rules']:
156 __ExtractSourcesFromAction(rule, base_path, base_path_components, results) 154 _ExtractSourcesFromAction(rule, base_path, base_path_components, results)
157 155
158 return results 156 return results
159 157
158
160 class Target(object): 159 class Target(object):
161 """Holds information about a particular target: 160 """Holds information about a particular target:
162 deps: set of the names of direct dependent targets. 161 deps: set of the names of direct dependent targets.
163 match_staus: one of the MatchStatus values""" 162 match_staus: one of the MatchStatus values"""
164 def __init__(self): 163 def __init__(self):
165 self.deps = set() 164 self.deps = set()
166 self.match_status = MATCH_STATUS_TBD 165 self.match_status = MATCH_STATUS_TBD
167 166
167
168 class Config(object): 168 class Config(object):
169 """Details what we're looking for 169 """Details what we're looking for
170 look_for_dependency_only: if true only search for a target listing any of 170 look_for_dependency_only: if true only search for a target listing any of
171 the files in files. 171 the files in files.
172 files: set of files to search for 172 files: set of files to search for
173 targets: see file description for details""" 173 targets: see file description for details"""
174 def __init__(self): 174 def __init__(self):
175 self.look_for_dependency_only = True 175 self.look_for_dependency_only = True
176 self.files = [] 176 self.files = []
177 self.targets = [] 177 self.targets = []
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
212 f = open(file_path, 'r') 212 f = open(file_path, 'r')
213 for file_name in f: 213 for file_name in f:
214 if file_name.endswith('\n'): 214 if file_name.endswith('\n'):
215 file_name = file_name[0:len(file_name) - 1] 215 file_name = file_name[0:len(file_name) - 1]
216 if len(file_name): 216 if len(file_name):
217 self.files.append(file_name) 217 self.files.append(file_name)
218 f.close() 218 f.close()
219 except IOError: 219 except IOError:
220 raise Exception('Unable to open file', file_path) 220 raise Exception('Unable to open file', file_path)
221 221
222
222 def _WasBuildFileModified(build_file, data, files): 223 def _WasBuildFileModified(build_file, data, files):
223 """Returns true if the build file |build_file| is either in |files| or 224 """Returns true if the build file |build_file| is either in |files| or
224 one of the files included by |build_file| is in |files|.""" 225 one of the files included by |build_file| is in |files|."""
225 if _ToGypPath(build_file) in files: 226 if _ToGypPath(build_file) in files:
226 if debug: 227 if debug:
227 print 'gyp file modified', build_file 228 print 'gyp file modified', build_file
228 return True 229 return True
229 230
230 # First element of included_files is the file itself. 231 # First element of included_files is the file itself.
231 if len(data[build_file]['included_files']) <= 1: 232 if len(data[build_file]['included_files']) <= 1:
232 return False 233 return False
233 234
234 for include_file in data[build_file]['included_files'][1:]: 235 for include_file in data[build_file]['included_files'][1:]:
235 # |included_files| are relative to the directory of the |build_file|. 236 # |included_files| are relative to the directory of the |build_file|.
236 rel_include_file = \ 237 rel_include_file = \
237 _ToGypPath(gyp.common.UnrelativePath(include_file, build_file)) 238 _ToGypPath(gyp.common.UnrelativePath(include_file, build_file))
238 if rel_include_file in files: 239 if rel_include_file in files:
239 if debug: 240 if debug:
240 print 'included gyp file modified, gyp_file=', build_file, \ 241 print 'included gyp file modified, gyp_file=', build_file, \
241 'included file=', rel_include_file 242 'included file=', rel_include_file
242 return True 243 return True
243 return False 244 return False
244 245
245 def __GenerateTargets(data, target_list, target_dicts, toplevel_dir, files): 246
247 def _GenerateTargets(data, target_list, target_dicts, toplevel_dir, files):
246 """Generates a dictionary with the key the name of a target and the value a 248 """Generates a dictionary with the key the name of a target and the value a
247 Target. |toplevel_dir| is the root of the source tree. If the sources of 249 Target. |toplevel_dir| is the root of the source tree. If the sources of
248 a target match that of |files|, then |target.matched| is set to True. 250 a target match that of |files|, then |target.matched| is set to True.
249 This returns a tuple of the dictionary and whether at least one target's 251 This returns a tuple of the dictionary and whether at least one target's
250 sources listed one of the paths in |files|.""" 252 sources listed one of the paths in |files|."""
251 targets = {} 253 targets = {}
252 254
253 # Queue of targets to visit. 255 # Queue of targets to visit.
254 targets_to_visit = target_list[:] 256 targets_to_visit = target_list[:]
255 257
(...skipping 15 matching lines...) Expand all
271 if not build_file in build_file_in_files: 273 if not build_file in build_file_in_files:
272 build_file_in_files[build_file] = \ 274 build_file_in_files[build_file] = \
273 _WasBuildFileModified(build_file, data, files) 275 _WasBuildFileModified(build_file, data, files)
274 276
275 # If a build file (or any of its included files) is modified we assume all 277 # If a build file (or any of its included files) is modified we assume all
276 # targets in the file are modified. 278 # targets in the file are modified.
277 if build_file_in_files[build_file]: 279 if build_file_in_files[build_file]:
278 target.match_status = MATCH_STATUS_MATCHES 280 target.match_status = MATCH_STATUS_MATCHES
279 matched = True 281 matched = True
280 else: 282 else:
281 sources = __ExtractSources(target_name, target_dicts[target_name], 283 sources = _ExtractSources(target_name, target_dicts[target_name],
282 toplevel_dir) 284 toplevel_dir)
283 for source in sources: 285 for source in sources:
284 if source in files: 286 if source in files:
285 target.match_status = MATCH_STATUS_MATCHES 287 target.match_status = MATCH_STATUS_MATCHES
286 matched = True 288 matched = True
287 break 289 break
288 290
289 for dep in target_dicts[target_name].get('dependencies', []): 291 for dep in target_dicts[target_name].get('dependencies', []):
290 targets[target_name].deps.add(dep) 292 targets[target_name].deps.add(dep)
291 targets_to_visit.append(dep) 293 targets_to_visit.append(dep)
292 294
293 return targets, matched 295 return targets, matched
294 296
297
295 def _GetUnqualifiedToQualifiedMapping(all_targets, to_find): 298 def _GetUnqualifiedToQualifiedMapping(all_targets, to_find):
296 """Returns a mapping (dictionary) from unqualified name to qualified name for 299 """Returns a mapping (dictionary) from unqualified name to qualified name for
297 all the targets in |to_find|.""" 300 all the targets in |to_find|."""
298 result = {} 301 result = {}
299 if not to_find: 302 if not to_find:
300 return result 303 return result
301 to_find = set(to_find) 304 to_find = set(to_find)
302 for target_name in all_targets.keys(): 305 for target_name in all_targets.keys():
303 extracted = gyp.common.ParseQualifiedTarget(target_name) 306 extracted = gyp.common.ParseQualifiedTarget(target_name)
304 if len(extracted) > 1 and extracted[1] in to_find: 307 if len(extracted) > 1 and extracted[1] in to_find:
305 to_find.remove(extracted[1]) 308 to_find.remove(extracted[1])
306 result[extracted[1]] = target_name 309 result[extracted[1]] = target_name
307 if not to_find: 310 if not to_find:
308 return result 311 return result
309 return result 312 return result
310 313
314
311 def _DoesTargetDependOn(target, all_targets): 315 def _DoesTargetDependOn(target, all_targets):
312 """Returns true if |target| or any of its dependencies matches the supplied 316 """Returns true if |target| or any of its dependencies matches the supplied
313 set of paths. This updates |matches| of the Targets as it recurses. 317 set of paths. This updates |matches| of the Targets as it recurses.
314 target: the Target to look for. 318 target: the Target to look for.
315 all_targets: mapping from target name to Target. 319 all_targets: mapping from target name to Target.
316 matching_targets: set of targets looking for.""" 320 matching_targets: set of targets looking for."""
317 if target.match_status == MATCH_STATUS_DOESNT_MATCH: 321 if target.match_status == MATCH_STATUS_DOESNT_MATCH:
318 return False 322 return False
319 if target.match_status == MATCH_STATUS_MATCHES or \ 323 if target.match_status == MATCH_STATUS_MATCHES or \
320 target.match_status == MATCH_STATUS_MATCHES_BY_DEPENDENCY: 324 target.match_status == MATCH_STATUS_MATCHES_BY_DEPENDENCY:
321 return True 325 return True
322 for dep_name in target.deps: 326 for dep_name in target.deps:
323 dep_target = all_targets[dep_name] 327 dep_target = all_targets[dep_name]
324 if _DoesTargetDependOn(dep_target, all_targets): 328 if _DoesTargetDependOn(dep_target, all_targets):
325 dep_target.match_status = MATCH_STATUS_MATCHES_BY_DEPENDENCY 329 dep_target.match_status = MATCH_STATUS_MATCHES_BY_DEPENDENCY
326 return True 330 return True
327 dep_target.match_status = MATCH_STATUS_DOESNT_MATCH 331 dep_target.match_status = MATCH_STATUS_DOESNT_MATCH
328 return False 332 return False
329 333
334
330 def _GetTargetsDependingOn(all_targets, possible_targets): 335 def _GetTargetsDependingOn(all_targets, possible_targets):
331 """Returns the list of targets in |possible_targets| that depend (either 336 """Returns the list of targets in |possible_targets| that depend (either
332 directly on indirectly) on the matched files. 337 directly on indirectly) on the matched files.
333 all_targets: mapping from target name to Target. 338 all_targets: mapping from target name to Target.
334 possible_targets: targets to search from.""" 339 possible_targets: targets to search from."""
335 found = [] 340 found = []
336 for target in possible_targets: 341 for target in possible_targets:
337 if _DoesTargetDependOn(all_targets[target], all_targets): 342 if _DoesTargetDependOn(all_targets[target], all_targets):
338 # possible_targets was initially unqualified, keep it unqualified. 343 # possible_targets was initially unqualified, keep it unqualified.
339 found.append(gyp.common.ParseQualifiedTarget(target)[1]) 344 found.append(gyp.common.ParseQualifiedTarget(target)[1])
340 return found 345 return found
341 346
347
342 def _WriteOutput(params, **values): 348 def _WriteOutput(params, **values):
343 """Writes the output, either to stdout or a file is specified.""" 349 """Writes the output, either to stdout or a file is specified."""
344 output_path = params.get('generator_flags', {}).get( 350 output_path = params.get('generator_flags', {}).get(
345 'analyzer_output_path', None) 351 'analyzer_output_path', None)
346 if not output_path: 352 if not output_path:
347 print json.dumps(values) 353 print json.dumps(values)
348 return 354 return
349 try: 355 try:
350 f = open(output_path, 'w') 356 f = open(output_path, 'w')
351 f.write(json.dumps(values) + '\n') 357 f.write(json.dumps(values) + '\n')
352 f.close() 358 f.close()
353 except IOError as e: 359 except IOError as e:
354 print 'Error writing to output file', output_path, str(e) 360 print 'Error writing to output file', output_path, str(e)
355 361
362
356 def CalculateVariables(default_variables, params): 363 def CalculateVariables(default_variables, params):
357 """Calculate additional variables for use in the build (called by gyp).""" 364 """Calculate additional variables for use in the build (called by gyp)."""
358 flavor = gyp.common.GetFlavor(params) 365 flavor = gyp.common.GetFlavor(params)
359 if flavor == 'mac': 366 if flavor == 'mac':
360 default_variables.setdefault('OS', 'mac') 367 default_variables.setdefault('OS', 'mac')
361 elif flavor == 'win': 368 elif flavor == 'win':
362 default_variables.setdefault('OS', 'win') 369 default_variables.setdefault('OS', 'win')
363 # Copy additional generator configuration data from VS, which is shared 370 # Copy additional generator configuration data from VS, which is shared
364 # by the Windows Ninja generator. 371 # by the Windows Ninja generator.
365 import gyp.generator.msvs as msvs_generator 372 import gyp.generator.msvs as msvs_generator
366 generator_additional_non_configuration_keys = getattr(msvs_generator, 373 generator_additional_non_configuration_keys = getattr(msvs_generator,
367 'generator_additional_non_configuration_keys', []) 374 'generator_additional_non_configuration_keys', [])
368 generator_additional_path_sections = getattr(msvs_generator, 375 generator_additional_path_sections = getattr(msvs_generator,
369 'generator_additional_path_sections', []) 376 'generator_additional_path_sections', [])
370 377
371 gyp.msvs_emulation.CalculateCommonVariables(default_variables, params) 378 gyp.msvs_emulation.CalculateCommonVariables(default_variables, params)
372 else: 379 else:
373 operating_system = flavor 380 operating_system = flavor
374 if flavor == 'android': 381 if flavor == 'android':
375 operating_system = 'linux' # Keep this legacy behavior for now. 382 operating_system = 'linux' # Keep this legacy behavior for now.
376 default_variables.setdefault('OS', operating_system) 383 default_variables.setdefault('OS', operating_system)
377 384
385
378 def GenerateOutput(target_list, target_dicts, data, params): 386 def GenerateOutput(target_list, target_dicts, data, params):
379 """Called by gyp as the final stage. Outputs results.""" 387 """Called by gyp as the final stage. Outputs results."""
380 config = Config() 388 config = Config()
381 try: 389 try:
382 config.Init(params) 390 config.Init(params)
383 if not config.files: 391 if not config.files:
384 if config.look_for_dependency_only: 392 if config.look_for_dependency_only:
385 print 'Must specify files to analyze via file_path generator flag' 393 print 'Must specify files to analyze via file_path generator flag'
386 return 394 return
387 raise Exception('Must specify files to analyze via config_path generator ' 395 raise Exception('Must specify files to analyze via config_path generator '
(...skipping 11 matching lines...) Expand all
399 if params['options'].includes: 407 if params['options'].includes:
400 for include in params['options'].includes: 408 for include in params['options'].includes:
401 if _ToGypPath(include) in config.files: 409 if _ToGypPath(include) in config.files:
402 if debug: 410 if debug:
403 print 'include path modified', include 411 print 'include path modified', include
404 matched_include = True 412 matched_include = True
405 matched = True 413 matched = True
406 break 414 break
407 415
408 if not matched: 416 if not matched:
409 all_targets, matched = __GenerateTargets(data, target_list, target_dicts, 417 all_targets, matched = _GenerateTargets(data, target_list, target_dicts,
410 toplevel_dir, 418 toplevel_dir,
411 frozenset(config.files)) 419 frozenset(config.files))
412 420
413 # Set of targets that refer to one of the files. 421 # Set of targets that refer to one of the files.
414 if config.look_for_dependency_only: 422 if config.look_for_dependency_only:
415 print found_dependency_string if matched else no_dependency_string 423 print found_dependency_string if matched else no_dependency_string
416 return 424 return
417 425
418 warning = None 426 warning = None
419 if matched_include: 427 if matched_include:
420 output_targets = config.targets 428 output_targets = config.targets
421 elif matched: 429 elif matched:
(...skipping 15 matching lines...) Expand all
437 445
438 result_dict = { 'targets': output_targets, 446 result_dict = { 'targets': output_targets,
439 'status': found_dependency_string if matched else 447 'status': found_dependency_string if matched else
440 no_dependency_string } 448 no_dependency_string }
441 if warning: 449 if warning:
442 result_dict['warning'] = warning 450 result_dict['warning'] = warning
443 _WriteOutput(params, **result_dict) 451 _WriteOutput(params, **result_dict)
444 452
445 except Exception as e: 453 except Exception as e:
446 _WriteOutput(params, error=str(e)) 454 _WriteOutput(params, error=str(e))
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698