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

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

Issue 442083004: Adds support for detecting modified gyp* files to 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 | test/analyzer/common.gypi » ('j') | test/analyzer/gyptest-analyzer.new.py » ('J')
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 def _ToGypPath(path):
scottmg 2014/08/05 23:02:43 I don't understand why some functions are _ and so
sky 2014/08/06 00:26:07 Indeed. Python sites recommend __, which is why I
68 """Converts a path to the format used by gyp."""
69 if os.sep == '\\' and os.altsep == '/':
70 return path.replace('\\', '/')
71 return path
72
67 def __ExtractBasePath(target): 73 def __ExtractBasePath(target):
68 """Extracts the path components of the specified gyp target path.""" 74 """Extracts the path components of the specified gyp target path."""
69 last_index = target.rfind('/') 75 last_index = target.rfind('/')
70 if last_index == -1: 76 if last_index == -1:
71 return '' 77 return ''
72 return target[0:(last_index + 1)] 78 return target[0:(last_index + 1)]
73 79
74 def __ResolveParent(path, base_path_components): 80 def __ResolveParent(path, base_path_components):
75 """Resolves |path|, which starts with at least one '../'. Returns an empty 81 """Resolves |path|, which starts with at least one '../'. Returns an empty
76 string if the path shouldn't be considered. See __AddSources() for a 82 string if the path shouldn't be considered. See __AddSources() for a
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
113 119
114 def __ExtractSourcesFromAction(action, base_path, base_path_components, 120 def __ExtractSourcesFromAction(action, base_path, base_path_components,
115 results): 121 results):
116 if 'inputs' in action: 122 if 'inputs' in action:
117 __AddSources(action['inputs'], base_path, base_path_components, results) 123 __AddSources(action['inputs'], base_path, base_path_components, results)
118 124
119 def __ExtractSources(target, target_dict, toplevel_dir): 125 def __ExtractSources(target, target_dict, toplevel_dir):
120 # |target| is either absolute or relative and in the format of the OS. Gyp 126 # |target| is either absolute or relative and in the format of the OS. Gyp
121 # source paths are always posix. Convert |target| to a posix path relative to 127 # source paths are always posix. Convert |target| to a posix path relative to
122 # |toplevel_dir_|. This is done to make it easy to build source paths. 128 # |toplevel_dir_|. This is done to make it easy to build source paths.
123 if os.sep == '\\' and os.altsep == '/': 129 base_path = _ToGypPath(target)
124 base_path = target.replace('\\', '/')
125 else:
126 base_path = target
127 if base_path == toplevel_dir: 130 if base_path == toplevel_dir:
128 base_path = '' 131 base_path = ''
129 elif base_path.startswith(toplevel_dir + '/'): 132 elif base_path.startswith(toplevel_dir + '/'):
130 base_path = base_path[len(toplevel_dir) + len('/'):] 133 base_path = base_path[len(toplevel_dir) + len('/'):]
131 base_path = posixpath.dirname(base_path) 134 base_path = posixpath.dirname(base_path)
132 base_path_components = base_path.split('/') 135 base_path_components = base_path.split('/')
133 136
134 # Add a trailing '/' so that __AddSources() can easily build paths. 137 # Add a trailing '/' so that __AddSources() can easily build paths.
135 if len(base_path): 138 if len(base_path):
136 base_path += '/' 139 base_path += '/'
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after
209 f = open(file_path, 'r') 212 f = open(file_path, 'r')
210 for file_name in f: 213 for file_name in f:
211 if file_name.endswith('\n'): 214 if file_name.endswith('\n'):
212 file_name = file_name[0:len(file_name) - 1] 215 file_name = file_name[0:len(file_name) - 1]
213 if len(file_name): 216 if len(file_name):
214 self.files.append(file_name) 217 self.files.append(file_name)
215 f.close() 218 f.close()
216 except IOError: 219 except IOError:
217 raise Exception('Unable to open file', file_path) 220 raise Exception('Unable to open file', file_path)
218 221
219 def __GenerateTargets(target_list, target_dicts, toplevel_dir, files): 222 def _WasBuildFileModified(build_file, data, files):
223 """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 if _ToGypPath(build_file) in files:
226 if debug:
227 print 'gyp file modified', build_file
228 return True
229
230 # First element of included_files is the file itself.
231 if len(data[build_file]['included_files']) <= 1:
232 return False
233
234 for include_file in data[build_file]['included_files'][1:]:
235 # |included_files| are relative to the directory of the |build_file|.
236 rel_include_file = \
237 _ToGypPath(gyp.common.UnrelativePath(include_file, build_file))
238 if rel_include_file in files:
239 if debug:
240 print 'included gyp file modified, gyp_file=', build_file, \
241 'included file=', rel_include_file
242 return True
243 return False
244
245 def __GenerateTargets(data, target_list, target_dicts, toplevel_dir, files):
220 """Generates a dictionary with the key the name of a target and the value a 246 """Generates a dictionary with the key the name of a target and the value a
221 Target. |toplevel_dir| is the root of the source tree. If the sources of 247 Target. |toplevel_dir| is the root of the source tree. If the sources of
222 a target match that of |files|, then |target.matched| is set to True. 248 a target match that of |files|, then |target.matched| is set to True.
223 This returns a tuple of the dictionary and whether at least one target's 249 This returns a tuple of the dictionary and whether at least one target's
224 sources listed one of the paths in |files|.""" 250 sources listed one of the paths in |files|."""
225 targets = {} 251 targets = {}
226 252
227 # Queue of targets to visit. 253 # Queue of targets to visit.
228 targets_to_visit = target_list[:] 254 targets_to_visit = target_list[:]
229 255
230 matched = False 256 matched = False
231 257
258 # Maps from build file to a boolean indicating whether the build file is in
259 # |files|.
260 build_file_in_files = {}
261
232 while len(targets_to_visit) > 0: 262 while len(targets_to_visit) > 0:
233 target_name = targets_to_visit.pop() 263 target_name = targets_to_visit.pop()
234 if target_name in targets: 264 if target_name in targets:
235 continue 265 continue
236 266
237 target = Target() 267 target = Target()
238 targets[target_name] = target 268 targets[target_name] = target
239 sources = __ExtractSources(target_name, target_dicts[target_name], 269
240 toplevel_dir) 270 build_file = gyp.common.ParseQualifiedTarget(target_name)[0]
241 for source in sources: 271 if not build_file in build_file_in_files:
242 if source in files: 272 build_file_in_files[build_file] = \
243 target.match_status = MATCH_STATUS_MATCHES 273 _WasBuildFileModified(build_file, data, files)
244 matched = True 274
245 break 275 # If a build file (or any of its included files) is modified we assume all
276 # targets in the file are modified.
277 if build_file_in_files[build_file]:
278 target.match_status = MATCH_STATUS_MATCHES
279 matched = True
280 else:
281 sources = __ExtractSources(target_name, target_dicts[target_name],
282 toplevel_dir)
283 for source in sources:
284 if source in files:
285 target.match_status = MATCH_STATUS_MATCHES
286 matched = True
287 break
246 288
247 for dep in target_dicts[target_name].get('dependencies', []): 289 for dep in target_dicts[target_name].get('dependencies', []):
248 targets[target_name].deps.add(dep) 290 targets[target_name].deps.add(dep)
249 targets_to_visit.append(dep) 291 targets_to_visit.append(dep)
250 292
251 return targets, matched 293 return targets, matched
252 294
253 def _GetUnqualifiedToQualifiedMapping(all_targets, to_find): 295 def _GetUnqualifiedToQualifiedMapping(all_targets, to_find):
254 """Returns a mapping (dictionary) from unqualified name to qualified name for 296 """Returns a mapping (dictionary) from unqualified name to qualified name for
255 all the targets in |to_find|.""" 297 all the targets in |to_find|."""
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after
338 config = Config() 380 config = Config()
339 try: 381 try:
340 config.Init(params) 382 config.Init(params)
341 if not config.files: 383 if not config.files:
342 if config.look_for_dependency_only: 384 if config.look_for_dependency_only:
343 print 'Must specify files to analyze via file_path generator flag' 385 print 'Must specify files to analyze via file_path generator flag'
344 return 386 return
345 raise Exception('Must specify files to analyze via config_path generator ' 387 raise Exception('Must specify files to analyze via config_path generator '
346 'flag') 388 'flag')
347 389
348 toplevel_dir = os.path.abspath(params['options'].toplevel_dir) 390 toplevel_dir = _ToGypPath(os.path.abspath(params['options'].toplevel_dir))
349 if os.sep == '\\' and os.altsep == '/':
350 toplevel_dir = toplevel_dir.replace('\\', '/')
351 if debug: 391 if debug:
352 print 'toplevel_dir', toplevel_dir 392 print 'toplevel_dir', toplevel_dir
353 393
354 all_targets, matched = __GenerateTargets(target_list, target_dicts, 394 matched = False
355 toplevel_dir, 395 matched_include = False
356 frozenset(config.files)) 396
397 # If one of the modified files is an include file then everything is
398 # effected.
scottmg 2014/08/05 23:02:43 nit; affected.
sky 2014/08/06 00:26:07 I ALWAYS get that wrong.
399 if params['options'].includes:
400 for include in params['options'].includes:
401 if _ToGypPath(include) in config.files:
402 if debug:
403 print 'include path modified', include
404 matched_include = True
405 matched = True
406 break
407
408 if not matched:
409 all_targets, matched = __GenerateTargets(data, target_list, target_dicts,
410 toplevel_dir,
411 frozenset(config.files))
357 412
358 # Set of targets that refer to one of the files. 413 # Set of targets that refer to one of the files.
359 if config.look_for_dependency_only: 414 if config.look_for_dependency_only:
360 print found_dependency_string if matched else no_dependency_string 415 print found_dependency_string if matched else no_dependency_string
361 return 416 return
362 417
363 warning = None 418 warning = None
364 if matched: 419 if matched_include:
420 output_targets = config.targets
421 elif matched:
365 unqualified_mapping = _GetUnqualifiedToQualifiedMapping( 422 unqualified_mapping = _GetUnqualifiedToQualifiedMapping(
366 all_targets, config.targets) 423 all_targets, config.targets)
367 if len(unqualified_mapping) != len(config.targets): 424 if len(unqualified_mapping) != len(config.targets):
368 not_found = [] 425 not_found = []
369 for target in config.targets: 426 for target in config.targets:
370 if not target in unqualified_mapping: 427 if not target in unqualified_mapping:
371 not_found.append(target) 428 not_found.append(target)
372 warning = 'Unable to find all targets: ' + str(not_found) 429 warning = 'Unable to find all targets: ' + str(not_found)
373 qualified_targets = [] 430 qualified_targets = []
374 for target in config.targets: 431 for target in config.targets:
375 if target in unqualified_mapping: 432 if target in unqualified_mapping:
376 qualified_targets.append(unqualified_mapping[target]) 433 qualified_targets.append(unqualified_mapping[target])
377 output_targets = _GetTargetsDependingOn(all_targets, qualified_targets) 434 output_targets = _GetTargetsDependingOn(all_targets, qualified_targets)
378 else: 435 else:
379 output_targets = [] 436 output_targets = []
380 437
381 result_dict = { 'targets': output_targets, 438 result_dict = { 'targets': output_targets,
382 'status': found_dependency_string if matched else 439 'status': found_dependency_string if matched else
383 no_dependency_string } 440 no_dependency_string }
384 if warning: 441 if warning:
385 result_dict['warning'] = warning 442 result_dict['warning'] = warning
386 _WriteOutput(params, **result_dict) 443 _WriteOutput(params, **result_dict)
387 444
388 except Exception as e: 445 except Exception as e:
389 _WriteOutput(params, error=str(e)) 446 _WriteOutput(params, error=str(e))
OLDNEW
« no previous file with comments | « no previous file | test/analyzer/common.gypi » ('j') | test/analyzer/gyptest-analyzer.new.py » ('J')

Powered by Google App Engine
This is Rietveld 408576698