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

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

Issue 481433003: Makes the analyzer output the set of targets needing a build (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/gyptest-analyzer.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 # 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.
11 ignore_targets: list of targets not to include in effected_targets. If a target
scottmg 2014/08/15 19:44:45 All the 'effected/Effected's should be 'affected/A
sky 2014/08/15 19:58:12 I *knew* I would get that wrong;)
12 that any of these targets depend on is modified the target won't be output.
13 These targets will be output if they contained one of the supplied file names.
scottmg 2014/08/15 21:30:24 so, if there's a target in ignored_targets, and so
sky 2014/08/15 22:39:37 Ya, sorry, this is all rather complex. Consider th
11 14
12 The following is output: 15 The following is output:
13 error: only supplied if there is an error. 16 error: only supplied if there is an error.
14 warning: only supplied if there is a warning. 17 warning: only supplied if there is a warning.
15 targets: the set of targets passed in via targets that either directly or 18 targets: the set of targets passed in via targets that either directly or
16 indirectly depend upon the set of paths supplied in files. 19 indirectly depend upon the set of paths supplied in files. This is not output
17 status: indicates if any of the supplied files matched at least one target. 20 effected_targets: minimal set of targets that directly depend on the changed
21 files. The output from this could be passed into a compile step to build the
22 minimal set of targets that are effected by the set of changed files.
23 status: outputs one of three values: none of the supplied files were found,
24 one of the include files changed so that it should be assumed everything
25 changed (in this case targets and effected_targets are not output) or at
26 least one file was found.
18 27
19 If the generator flag analyzer_output_path is specified, output is written 28 If the generator flag analyzer_output_path is specified, output is written
20 there. Otherwise output is written to stdout. 29 there. Otherwise output is written to stdout.
21 """ 30 """
22 31
23 import gyp.common 32 import gyp.common
24 import gyp.ninja_syntax as ninja_syntax 33 import gyp.ninja_syntax as ninja_syntax
25 import json 34 import json
26 import os 35 import os
27 import posixpath 36 import posixpath
28 import sys 37 import sys
29 38
30 debug = False 39 debug = False
31 40
32 found_dependency_string = 'Found dependency' 41 found_dependency_string = 'Found dependency'
33 no_dependency_string = 'No dependencies' 42 no_dependency_string = 'No dependencies'
43 # Status when it should be assumed that everything has changed.
44 all_changed_string = 'Found dependency (all)'
34 45
35 # MatchStatus is used indicate if and how a target depends upon the supplied 46 # MatchStatus is used indicate if and how a target depends upon the supplied
36 # sources. 47 # sources.
37 # The target's sources contain one of the supplied paths.
38 MATCH_STATUS_MATCHES = 1 48 MATCH_STATUS_MATCHES = 1
39 # The target has a dependency on another target that contains one of the 49 # The target has a dependency on another target that contains one of the
40 # supplied paths. 50 # supplied paths.
41 MATCH_STATUS_MATCHES_BY_DEPENDENCY = 2 51 MATCH_STATUS_MATCHES_BY_DEPENDENCY = 2
42 # The target's sources weren't in the supplied paths and none of the target's 52 # The target's sources weren't in the supplied paths and none of the target's
43 # dependencies depend upon a target that matched. 53 # dependencies depend upon a target that matched.
44 MATCH_STATUS_DOESNT_MATCH = 3 54 MATCH_STATUS_DOESNT_MATCH = 3
45 # The target doesn't contain the source, but the dependent targets have not yet 55 # The target doesn't contain the source, but the dependent targets have not yet
46 # been visited to determine a more specific status yet. 56 # been visited to determine a more specific status yet.
47 MATCH_STATUS_TBD = 4 57 MATCH_STATUS_TBD = 4
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after
151 results) 161 results)
152 if 'rules' in target_dict: 162 if 'rules' in target_dict:
153 for rule in target_dict['rules']: 163 for rule in target_dict['rules']:
154 _ExtractSourcesFromAction(rule, base_path, base_path_components, results) 164 _ExtractSourcesFromAction(rule, base_path, base_path_components, results)
155 165
156 return results 166 return results
157 167
158 168
159 class Target(object): 169 class Target(object):
160 """Holds information about a particular target: 170 """Holds information about a particular target:
161 deps: set of the names of direct dependent targets. 171 deps: set of Targets this Target depends upon. This is not recursive, only the
162 match_staus: one of the MatchStatus values""" 172 direct dependent Targets.
163 def __init__(self): 173 match_staus: one of the MatchStatus values.
174 back_deps: set of Targets that have a dependency on this Target.
175 visited: used during iteration to indicate whether we've visited this target.
176 This is used for two iterations, once in building the set of Targets and
177 again in _GetEffectedTargets().
178 name: fully qualified name of the target."""
179 def __init__(self, name):
164 self.deps = set() 180 self.deps = set()
165 self.match_status = MATCH_STATUS_TBD 181 self.match_status = MATCH_STATUS_TBD
182 self.back_deps = set()
183 self.visited = False
184 self.name = name
166 185
167 186
168 class Config(object): 187 class Config(object):
169 """Details what we're looking for 188 """Details what we're looking for
170 files: set of files to search for 189 files: set of files to search for
171 targets: see file description for details""" 190 targets: see file description for details.
191 ignore_targets: see file description for details"""
172 def __init__(self): 192 def __init__(self):
173 self.files = [] 193 self.files = []
174 self.targets = [] 194 self.targets = set()
195 self.ignore_targets = set()
175 196
176 def Init(self, params): 197 def Init(self, params):
177 """Initializes Config. This is a separate method as it raises an exception 198 """Initializes Config. This is a separate method as it raises an exception
178 if there is a parse error.""" 199 if there is a parse error."""
179 generator_flags = params.get('generator_flags', {}) 200 generator_flags = params.get('generator_flags', {})
180 config_path = generator_flags.get('config_path', None) 201 config_path = generator_flags.get('config_path', None)
181 if not config_path: 202 if not config_path:
182 return 203 return
183 try: 204 try:
184 f = open(config_path, 'r') 205 f = open(config_path, 'r')
185 config = json.load(f) 206 config = json.load(f)
186 f.close() 207 f.close()
187 except IOError: 208 except IOError:
188 raise Exception('Unable to open file ' + config_path) 209 raise Exception('Unable to open file ' + config_path)
189 except ValueError as e: 210 except ValueError as e:
190 raise Exception('Unable to parse config file ' + config_path + str(e)) 211 raise Exception('Unable to parse config file ' + config_path + str(e))
191 if not isinstance(config, dict): 212 if not isinstance(config, dict):
192 raise Exception('config_path must be a JSON file containing a dictionary') 213 raise Exception('config_path must be a JSON file containing a dictionary')
193 self.files = config.get('files', []) 214 self.files = config.get('files', [])
194 # Coalesce duplicates 215 # Convert to sets so that we remove duplicates.
195 self.targets = list(set(config.get('targets', []))) 216 self.targets = set(config.get('targets', []))
217 self.ignore_targets = set(config.get('ignore_targets', []))
196 218
197 219
198 def _WasBuildFileModified(build_file, data, files): 220 def _WasBuildFileModified(build_file, data, files):
199 """Returns true if the build file |build_file| is either in |files| or 221 """Returns true if the build file |build_file| is either in |files| or
200 one of the files included by |build_file| is in |files|.""" 222 one of the files included by |build_file| is in |files|."""
201 if _ToGypPath(build_file) in files: 223 if _ToGypPath(build_file) in files:
202 if debug: 224 if debug:
203 print 'gyp file modified', build_file 225 print 'gyp file modified', build_file
204 return True 226 return True
205 227
206 # First element of included_files is the file itself. 228 # First element of included_files is the file itself.
207 if len(data[build_file]['included_files']) <= 1: 229 if len(data[build_file]['included_files']) <= 1:
208 return False 230 return False
209 231
210 for include_file in data[build_file]['included_files'][1:]: 232 for include_file in data[build_file]['included_files'][1:]:
211 # |included_files| are relative to the directory of the |build_file|. 233 # |included_files| are relative to the directory of the |build_file|.
212 rel_include_file = \ 234 rel_include_file = \
213 _ToGypPath(gyp.common.UnrelativePath(include_file, build_file)) 235 _ToGypPath(gyp.common.UnrelativePath(include_file, build_file))
214 if rel_include_file in files: 236 if rel_include_file in files:
215 if debug: 237 if debug:
216 print 'included gyp file modified, gyp_file=', build_file, \ 238 print 'included gyp file modified, gyp_file=', build_file, \
217 'included file=', rel_include_file 239 'included file=', rel_include_file
218 return True 240 return True
219 return False 241 return False
220 242
221 243
222 def _GenerateTargets(data, target_list, target_dicts, toplevel_dir, files): 244 def _GenerateTargets(data, target_list, target_dicts, toplevel_dir, files,
223 """Generates a dictionary with the key the name of a target and the value a 245 build_files):
224 Target. |toplevel_dir| is the root of the source tree. If the sources of 246 """Returns a tuple of the following:
225 a target match that of |files|, then |target.matched| is set to True. 247 . A dictionary mapping from fully qualified name to Target.
226 This returns a tuple of the dictionary and whether at least one target's 248 . A list of the targets that have a source file in |files|.
227 sources listed one of the paths in |files|.""" 249 . Set of root Targets reachable from the the files |build_files|.
250 This sets the |match_status| of the targets that contain any of the source
251 files in |files| to MATCH_STATUS_MATCHES.
252 |toplevel_dir| is the root of the source tree."""
253 # Maps from target name to Target.
228 targets = {} 254 targets = {}
229 255
256 # Targets that matched.
257 matching_targets = []
258
230 # Queue of targets to visit. 259 # Queue of targets to visit.
231 targets_to_visit = target_list[:] 260 targets_to_visit = target_list[:]
232 261
233 matched = False
234
235 # Maps from build file to a boolean indicating whether the build file is in 262 # Maps from build file to a boolean indicating whether the build file is in
236 # |files|. 263 # |files|.
237 build_file_in_files = {} 264 build_file_in_files = {}
238 265
266 # Root targets across all files.
267 roots = set()
268
269 # Set of Targets in |build_files|.
270 build_file_targets = set()
271
239 while len(targets_to_visit) > 0: 272 while len(targets_to_visit) > 0:
240 target_name = targets_to_visit.pop() 273 target_name = targets_to_visit.pop()
241 if target_name in targets: 274 if not target_name in targets:
275 target = Target(target_name)
276 targets[target_name] = target
277 roots.add(target)
278 elif targets[target_name].visited:
242 continue 279 continue
280 else:
281 target = targets[target_name]
243 282
244 target = Target() 283 target.visited = True
245 targets[target_name] = target
246 284
247 build_file = gyp.common.ParseQualifiedTarget(target_name)[0] 285 build_file = gyp.common.ParseQualifiedTarget(target_name)[0]
248 if not build_file in build_file_in_files: 286 if not build_file in build_file_in_files:
249 build_file_in_files[build_file] = \ 287 build_file_in_files[build_file] = \
250 _WasBuildFileModified(build_file, data, files) 288 _WasBuildFileModified(build_file, data, files)
251 289
290 if build_file in build_files:
291 build_file_targets.add(target)
292
252 # If a build file (or any of its included files) is modified we assume all 293 # If a build file (or any of its included files) is modified we assume all
253 # targets in the file are modified. 294 # targets in the file are modified.
254 if build_file_in_files[build_file]: 295 if build_file_in_files[build_file]:
296 print 'matching target from modified build file', target_name
255 target.match_status = MATCH_STATUS_MATCHES 297 target.match_status = MATCH_STATUS_MATCHES
256 matched = True 298 matching_targets.append(target)
257 else: 299 else:
258 sources = _ExtractSources(target_name, target_dicts[target_name], 300 sources = _ExtractSources(target_name, target_dicts[target_name],
259 toplevel_dir) 301 toplevel_dir)
260 for source in sources: 302 for source in sources:
261 if source in files: 303 if source in files:
304 print 'target', target_name, 'matches', source
262 target.match_status = MATCH_STATUS_MATCHES 305 target.match_status = MATCH_STATUS_MATCHES
263 matched = True 306 matching_targets.append(target)
264 break 307 break
265 308
309 # Add dependencies to visit as well as updating back pointers for deps.
266 for dep in target_dicts[target_name].get('dependencies', []): 310 for dep in target_dicts[target_name].get('dependencies', []):
267 targets[target_name].deps.add(dep)
268 targets_to_visit.append(dep) 311 targets_to_visit.append(dep)
269 312
270 return targets, matched 313 if not dep in targets:
314 dep_target = Target(dep)
315 targets[dep] = dep_target
316 else:
317 dep_target = targets[dep]
318 roots.discard(dep_target)
319
320 target.deps.add(dep_target)
321 dep_target.back_deps.add(target)
322
323 return targets, matching_targets, roots & build_file_targets
271 324
272 325
273 def _GetUnqualifiedToQualifiedMapping(all_targets, to_find): 326 def _GetUnqualifiedToTargetMapping(all_targets, to_find):
274 """Returns a mapping (dictionary) from unqualified name to qualified name for 327 """Returns a mapping (dictionary) from unqualified name to Target for all the
275 all the targets in |to_find|.""" 328 Targets in |to_find|."""
276 result = {} 329 result = {}
277 if not to_find: 330 if not to_find:
278 return result 331 return result
279 to_find = set(to_find) 332 to_find = set(to_find)
280 for target_name in all_targets.keys(): 333 for target_name in all_targets.keys():
281 extracted = gyp.common.ParseQualifiedTarget(target_name) 334 extracted = gyp.common.ParseQualifiedTarget(target_name)
282 if len(extracted) > 1 and extracted[1] in to_find: 335 if len(extracted) > 1 and extracted[1] in to_find:
283 to_find.remove(extracted[1]) 336 to_find.remove(extracted[1])
284 result[extracted[1]] = target_name 337 result[extracted[1]] = all_targets[target_name]
285 if not to_find: 338 if not to_find:
286 return result 339 return result
287 return result 340 return result
288 341
289 342
290 def _DoesTargetDependOn(target, all_targets): 343 def _DoesTargetDependOn(target):
291 """Returns true if |target| or any of its dependencies matches the supplied 344 """Returns true if |target| or any of its dependencies matches the supplied
292 set of paths. This updates |matches| of the Targets as it recurses. 345 set of paths. This updates |matches| of the Targets as it recurses.
293 target: the Target to look for. 346 target: the Target to look for."""
294 all_targets: mapping from target name to Target.
295 matching_targets: set of targets looking for."""
296 if target.match_status == MATCH_STATUS_DOESNT_MATCH: 347 if target.match_status == MATCH_STATUS_DOESNT_MATCH:
297 return False 348 return False
298 if target.match_status == MATCH_STATUS_MATCHES or \ 349 if target.match_status == MATCH_STATUS_MATCHES or \
299 target.match_status == MATCH_STATUS_MATCHES_BY_DEPENDENCY: 350 target.match_status == MATCH_STATUS_MATCHES_BY_DEPENDENCY:
300 return True 351 return True
301 for dep_name in target.deps: 352 for dep in target.deps:
302 dep_target = all_targets[dep_name] 353 if _DoesTargetDependOn(dep):
303 if _DoesTargetDependOn(dep_target, all_targets): 354 target.match_status = MATCH_STATUS_MATCHES_BY_DEPENDENCY
304 dep_target.match_status = MATCH_STATUS_MATCHES_BY_DEPENDENCY
305 return True 355 return True
306 dep_target.match_status = MATCH_STATUS_DOESNT_MATCH 356 target.match_status = MATCH_STATUS_DOESNT_MATCH
307 return False 357 return False
308 358
309 359
310 def _GetTargetsDependingOn(all_targets, possible_targets): 360 def _GetTargetsDependingOn(possible_targets):
311 """Returns the list of targets in |possible_targets| that depend (either 361 """Returns the list of Targets in |possible_targets| that depend (either
312 directly on indirectly) on the matched files. 362 directly on indirectly) on the matched targets.
313 all_targets: mapping from target name to Target.
314 possible_targets: targets to search from.""" 363 possible_targets: targets to search from."""
315 found = [] 364 found = []
316 for target in possible_targets: 365 for target in possible_targets:
317 if _DoesTargetDependOn(all_targets[target], all_targets): 366 if _DoesTargetDependOn(target):
318 # possible_targets was initially unqualified, keep it unqualified. 367 found.append(target)
319 found.append(gyp.common.ParseQualifiedTarget(target)[1])
320 return found 368 return found
321 369
322 370
371 def _RemoveTargetsThatDependOn(target, targets):
372 """Removes |target| from |targets|. If |targets| is not empty recurses through
373 descendants."""
374 if target in targets:
375 targets.remove(target)
376 return len(targets) == 0
377
378 if target.visited or target.match_status == MATCH_STATUS_DOESNT_MATCH:
379 return False
380
381 for child_target in target.deps:
382 if _RemoveTargetsThatDependOn(child_target, targets):
383 return True
384
385 return False
386
387
388 def _AddFirstModified(target, result):
389 """Adds |target| to |result| if it matches, otherwise recursively descends
390 through children doing the same.
391 |result|: targets that match are added here. Once a target matches none of its
392 dependencies are considered."""
393 if target.visited:
394 return
395
396 if target.match_status == MATCH_STATUS_MATCHES or \
397 target.match_status == MATCH_STATUS_MATCHES_BY_DEPENDENCY:
398 # Because of iteration order it is possible for |target| to depend on other
399 # targets in |result|. Prune them.
400 if result:
401 _RemoveTargetsThatDependOn(target, result)
402 result.add(target)
403 target.visited = True
404 return
405
406 target.visited = True
407
408 if target.match_status == MATCH_STATUS_DOESNT_MATCH:
409 return
410
411 for child_target in target.deps:
412 _AddFirstModified(child_target, result)
413
414
415 def _MarkVisitedAndMatches(target):
416 """Marks |target| as visited and matches (assuming it isn't already), and
417 recurses through dependencies. Does nothing if already |target| has already
418 been visited."""
419 if target.visited:
420 return
421
422 target.visited = True
423
424 if target.match_status != MATCH_STATUS_MATCHES:
425 target.match_status = MATCH_STATUS_MATCHES_BY_DEPENDENCY
426
427 for child_target in target.deps:
428 _MarkVisitedAndMatches(child_target)
429
430
431 def _MarkDepsVisitedAndMatches(target, ignore):
432 """Marks all the |deps| of |target| that are not in |ignore| as visited and
433 matches. See _MarkVisitedAndMatches() for details."""
434 for child_target in target.deps:
435 if child_target not in ignore:
436 _MarkVisitedAndMatches(child_target)
437
438
439 def _GetEffectedTargets(matching_targets, matched_search_targets, roots,
440 ignore_targets):
441 """Returns the set of Targets that directly depend on |matching_targets| and
442 have not yet matched one of the supplied targets.
443 matched_search_targets: set of targets passed in (by way of config) that
444 matched a dependency.
445 roots: set of root targets in the build files to search from.
446 ignore_targets: see file description for details."""
447 # This function strives to minimize the set of targets it returns. For example
448 # if A and B are in matching_targets, but B depends upon A then we only need
449 # return A for both to be compiled.
450 #
451 # When we get here all the Targets have their |visited| state set to False.
452 # As not all Targets may have been visited the |match_status| may still be
453 # TBD for many. For example, if B depends on A and A matched, then B's state
454 # may still be TBD. We need to ensure the match_status is set correctly so
455 # then when we resurse from the root targets we don't attempt to include
456 # a Target that would be included by way of another Targets depedendencies.
457
458 # First step, visit the search targets, recursively marking all of their
459 # descendants as visited and matching.
460 for target in matched_search_targets:
461 _MarkDepsVisitedAndMatches(target, set())
462
463 # For all the targets that contained matching files we need to mark any of
464 # their dependencies as visited and matching. Similarly any targets that
465 # depend on |matching_targets| needs to be marked as matching as well. We have
466 # to take care not to mark the |matching_targets| (or the |ignore_targets|)
467 # visited, otherwise we won't add it to the result later on.
468 for target in matching_targets:
469 _MarkDepsVisitedAndMatches(target, set())
470 ignore_plus_target = set(ignore_targets)
471 ignore_plus_target.add(target)
472 for back_dep_target in target.back_deps:
473 if back_dep_target not in ignore_targets:
474 back_dep_target.match_status = MATCH_STATUS_MATCHES_BY_DEPENDENCY
475 _MarkDepsVisitedAndMatches(back_dep_target, ignore_plus_target)
476
477 # Reset the status of any in |ignore_targets| that match by way of a
478 # dependency. This way we won't incorrectly pull them in again (they may have
479 # matched by dependency). We don't reset those that matched explicitly as they
480 # have to be compiled.
481 for target in ignore_targets:
482 if target.match_status == MATCH_STATUS_MATCHES_BY_DEPENDENCY:
483 target.match_status = MATCH_STATUS_TBD
484
485 # Finally we can search down from the root Targets adding the first Targets
486 # that match.
487 result = set()
488 for root in roots:
489 _AddFirstModified(root, result)
490
491 return result
492
493
323 def _WriteOutput(params, **values): 494 def _WriteOutput(params, **values):
324 """Writes the output, either to stdout or a file is specified.""" 495 """Writes the output, either to stdout or a file is specified."""
325 output_path = params.get('generator_flags', {}).get( 496 output_path = params.get('generator_flags', {}).get(
326 'analyzer_output_path', None) 497 'analyzer_output_path', None)
327 if not output_path: 498 if not output_path:
328 print json.dumps(values) 499 print json.dumps(values)
329 return 500 return
330 try: 501 try:
331 f = open(output_path, 'w') 502 f = open(output_path, 'w')
332 f.write(json.dumps(values) + '\n') 503 f.write(json.dumps(values) + '\n')
333 f.close() 504 f.close()
334 except IOError as e: 505 except IOError as e:
335 print 'Error writing to output file', output_path, str(e) 506 print 'Error writing to output file', output_path, str(e)
336 507
337 508
509 def _WasIncludeFileModified(params, files):
510 """Returns true if one of the files in |files| is in the set of included
511 files."""
512 if params['options'].includes:
513 for include in params['options'].includes:
514 if _ToGypPath(include) in files:
515 print 'Include file modified, assuming all changed', include
516 return True
517 return False
518
519
520 def _NamesNotIn(names, mapping):
521 """Returns a list of the values in |names| that are not in |mapping|."""
522 return [name for name in names if name not in mapping]
523
524
525 def _LookupTargets(names, mapping):
526 """Returns a list of the mapping[name] for each value in |names| that is in
527 |mapping|."""
528 return [mapping[name] for name in names if name in mapping]
529
530
338 def CalculateVariables(default_variables, params): 531 def CalculateVariables(default_variables, params):
339 """Calculate additional variables for use in the build (called by gyp).""" 532 """Calculate additional variables for use in the build (called by gyp)."""
340 flavor = gyp.common.GetFlavor(params) 533 flavor = gyp.common.GetFlavor(params)
341 if flavor == 'mac': 534 if flavor == 'mac':
342 default_variables.setdefault('OS', 'mac') 535 default_variables.setdefault('OS', 'mac')
343 elif flavor == 'win': 536 elif flavor == 'win':
344 default_variables.setdefault('OS', 'win') 537 default_variables.setdefault('OS', 'win')
345 # Copy additional generator configuration data from VS, which is shared 538 # Copy additional generator configuration data from VS, which is shared
346 # by the Windows Ninja generator. 539 # by the Windows Ninja generator.
347 import gyp.generator.msvs as msvs_generator 540 import gyp.generator.msvs as msvs_generator
(...skipping 16 matching lines...) Expand all
364 try: 557 try:
365 config.Init(params) 558 config.Init(params)
366 if not config.files: 559 if not config.files:
367 raise Exception('Must specify files to analyze via config_path generator ' 560 raise Exception('Must specify files to analyze via config_path generator '
368 'flag') 561 'flag')
369 562
370 toplevel_dir = _ToGypPath(os.path.abspath(params['options'].toplevel_dir)) 563 toplevel_dir = _ToGypPath(os.path.abspath(params['options'].toplevel_dir))
371 if debug: 564 if debug:
372 print 'toplevel_dir', toplevel_dir 565 print 'toplevel_dir', toplevel_dir
373 566
374 matched = False 567 if _WasIncludeFileModified(params, config.files):
375 matched_include = False 568 result_dict = { 'status': all_changed_string }
569 _WriteOutput(params, **result_dict)
570 return
376 571
377 # If one of the modified files is an include file then everything is 572 all_targets, matching_targets, roots = _GenerateTargets(
378 # affected. 573 data, target_list, target_dicts, toplevel_dir, frozenset(config.files),
379 if params['options'].includes: 574 params['build_files'])
380 for include in params['options'].includes:
381 if _ToGypPath(include) in config.files:
382 if debug:
383 print 'include path modified', include
384 matched_include = True
385 matched = True
386 break
387
388 if not matched:
389 all_targets, matched = _GenerateTargets(data, target_list, target_dicts,
390 toplevel_dir,
391 frozenset(config.files))
392 575
393 warning = None 576 warning = None
394 if matched_include: 577 combined_targets = config.targets | config.ignore_targets
395 output_targets = config.targets 578 unqualified_mapping = _GetUnqualifiedToTargetMapping(all_targets,
396 elif matched: 579 combined_targets)
397 unqualified_mapping = _GetUnqualifiedToQualifiedMapping( 580 if len(unqualified_mapping) != len(combined_targets):
398 all_targets, config.targets) 581 not_found = _NamesNotIn(combined_targets, unqualified_mapping)
399 if len(unqualified_mapping) != len(config.targets): 582 warning = 'Unable to find all targets: ' + str(not_found)
400 not_found = [] 583
401 for target in config.targets: 584 if matching_targets:
402 if not target in unqualified_mapping: 585 search_targets = _LookupTargets(config.targets, unqualified_mapping)
403 not_found.append(target) 586 ignore_targets = _LookupTargets(config.ignore_targets,
404 warning = 'Unable to find all targets: ' + str(not_found) 587 unqualified_mapping)
405 qualified_targets = [] 588
406 for target in config.targets: 589 matched_search_targets = _GetTargetsDependingOn(search_targets)
407 if target in unqualified_mapping: 590 # Reset the visited status for _GetEffectedTargets.
408 qualified_targets.append(unqualified_mapping[target]) 591 for target in all_targets.itervalues():
409 output_targets = _GetTargetsDependingOn(all_targets, qualified_targets) 592 target.visited = False
593 effected_targets = _GetEffectedTargets(matching_targets,
594 matched_search_targets, roots,
595 ignore_targets)
596 matched_search_targets = [gyp.common.ParseQualifiedTarget(target.name)[1]
597 for target in matched_search_targets]
598 effected_targets = [gyp.common.ParseQualifiedTarget(target.name)[1]
599 for target in effected_targets]
410 else: 600 else:
411 output_targets = [] 601 matched_search_targets = []
602 effected_targets = []
412 603
413 result_dict = { 'targets': output_targets, 604 result_dict = { 'targets': matched_search_targets,
414 'status': found_dependency_string if matched else 605 'status': found_dependency_string if matching_targets else
415 no_dependency_string } 606 no_dependency_string,
607 'effected_targets': effected_targets}
416 if warning: 608 if warning:
417 result_dict['warning'] = warning 609 result_dict['warning'] = warning
418 _WriteOutput(params, **result_dict) 610 _WriteOutput(params, **result_dict)
419 611
420 except Exception as e: 612 except Exception as e:
421 _WriteOutput(params, error=str(e)) 613 _WriteOutput(params, error=str(e))
OLDNEW
« no previous file with comments | « no previous file | test/analyzer/gyptest-analyzer.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698