OLD | NEW |
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 test_targets: unqualified target names to search for. Any target in this list |
| 11 that depends upon a file in |files| is output regardless of the type of target |
| 12 or chain of dependencies. |
| 13 additional_compile_targets: Unqualified targets to search for in addition to |
| 14 test_targets. Targets in the combined list that depend upon a file in |files| |
| 15 are not necessarily output. For example, if the target is of type none then the |
| 16 target is not output (but one of the descendants of the target will be). |
11 | 17 |
12 The following is output: | 18 The following is output: |
13 error: only supplied if there is an error. | 19 error: only supplied if there is an error. |
14 build_targets: minimal set of targets that directly depend on the changed | 20 compile_targets: minimal set of targets that directly or indirectly (for |
15 files and need to be built. The expectation is this set of targets is passed | 21 targets of type none) depend on the files in |files| and is one of the |
16 into a build step. The returned values are either values in the supplied | 22 supplied targets or a target that one of the supplied targets depends on. |
17 targets, or have a dependency on one of the supplied targets. | 23 The expectation is this set of targets is passed into a build step. |
| 24 test_targets: set of targets from the supplied |test_targets| that either |
| 25 directly or indirectly depend upon a file in |files|. This list if useful |
| 26 if additional processing needs to be done for certain targets after the |
| 27 build, such as running tests. |
18 status: outputs one of three values: none of the supplied files were found, | 28 status: outputs one of three values: none of the supplied files were found, |
19 one of the include files changed so that it should be assumed everything | 29 one of the include files changed so that it should be assumed everything |
20 changed (in this case targets and build_targets are not output) or at | 30 changed (in this case test_targets and compile_targets are not output) or at |
21 least one file was found. | 31 least one file was found. |
22 invalid_targets: list of supplied targets thare were not found. | 32 invalid_targets: list of supplied targets that were not found. |
23 | 33 |
24 Example: | 34 Example: |
25 Consider a graph like the following: | 35 Consider a graph like the following: |
26 A D | 36 A D |
27 / \ | 37 / \ |
28 B C | 38 B C |
29 A depends upon both B and C, A is of type none and B and C are executables. | 39 A depends upon both B and C, A is of type none and B and C are executables. |
30 D is an executable, has no dependencies and nothing depends on it. | 40 D is an executable, has no dependencies and nothing depends on it. |
31 If |targets| = ["A"] and files = ["b.cc", "d.cc"] (B depends upon b.cc and D | 41 If |additional_compile_targets| = ["A"], |test_targets| = ["B", "C"] and |
32 depends upon d.cc), then the following is output: | 42 files = ["b.cc", "d.cc"] (B depends upon b.cc and D depends upon d.cc), then |
33 |build_targets| = ["B"] B must built as it depends upon the changed file b.cc | 43 the following is output: |
| 44 |compile_targets| = ["B"] B must built as it depends upon the changed file b.cc |
34 and the supplied target A depends upon it. A is not output as a build_target | 45 and the supplied target A depends upon it. A is not output as a build_target |
35 as it is of type none with no rules and actions. | 46 as it is of type none with no rules and actions. |
| 47 |test_targets| = ["B"] B directly depends upon the change file b.cc. |
36 | 48 |
37 Even though the file d.cc, which D depends upon, has changed D is not output | 49 Even though the file d.cc, which D depends upon, has changed D is not output |
38 as none of the supplied targets (A) depend upon D. | 50 as it was not supplied by way of |additional_compile_targets| or |test_targets|. |
39 | 51 |
40 If the generator flag analyzer_output_path is specified, output is written | 52 If the generator flag analyzer_output_path is specified, output is written |
41 there. Otherwise output is written to stdout. | 53 there. Otherwise output is written to stdout. |
| 54 |
| 55 In Gyp the "all" target is shorthand for the root targets in the files passed |
| 56 to gyp. For example, if file "a.gyp" contains targets "a1" and |
| 57 "a2", and file "b.gyp" contains targets "b1" and "b2" and "a2" has a dependency |
| 58 on "b2" and gyp is supplied "a.gyp" then "all" consists of "a1" and "a2". |
| 59 Notice that "b1" and "b2" are not in the "all" target as "b.gyp" was not |
| 60 directly supplied to gyp. OTOH if both "a.gyp" and "b.gyp" are supplied to gyp |
| 61 then the "all" target includes "b1" and "b2". |
42 """ | 62 """ |
43 | 63 |
44 import gyp.common | 64 import gyp.common |
45 import gyp.ninja_syntax as ninja_syntax | 65 import gyp.ninja_syntax as ninja_syntax |
46 import json | 66 import json |
47 import os | 67 import os |
48 import posixpath | 68 import posixpath |
49 import sys | 69 import sys |
50 | 70 |
51 debug = False | 71 debug = False |
(...skipping 166 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
218 self.is_or_has_linked_ancestor = False | 238 self.is_or_has_linked_ancestor = False |
219 | 239 |
220 | 240 |
221 class Config(object): | 241 class Config(object): |
222 """Details what we're looking for | 242 """Details what we're looking for |
223 files: set of files to search for | 243 files: set of files to search for |
224 targets: see file description for details.""" | 244 targets: see file description for details.""" |
225 def __init__(self): | 245 def __init__(self): |
226 self.files = [] | 246 self.files = [] |
227 self.targets = set() | 247 self.targets = set() |
| 248 self.additional_compile_target_names = set() |
| 249 self.test_target_names = set() |
| 250 # Needed until recipes are updated. |
| 251 self.deprecated_mode = False |
228 | 252 |
229 def Init(self, params): | 253 def Init(self, params): |
230 """Initializes Config. This is a separate method as it raises an exception | 254 """Initializes Config. This is a separate method as it raises an exception |
231 if there is a parse error.""" | 255 if there is a parse error.""" |
232 generator_flags = params.get('generator_flags', {}) | 256 generator_flags = params.get('generator_flags', {}) |
233 config_path = generator_flags.get('config_path', None) | 257 config_path = generator_flags.get('config_path', None) |
234 if not config_path: | 258 if not config_path: |
235 return | 259 return |
236 try: | 260 try: |
237 f = open(config_path, 'r') | 261 f = open(config_path, 'r') |
238 config = json.load(f) | 262 config = json.load(f) |
239 f.close() | 263 f.close() |
240 except IOError: | 264 except IOError: |
241 raise Exception('Unable to open file ' + config_path) | 265 raise Exception('Unable to open file ' + config_path) |
242 except ValueError as e: | 266 except ValueError as e: |
243 raise Exception('Unable to parse config file ' + config_path + str(e)) | 267 raise Exception('Unable to parse config file ' + config_path + str(e)) |
244 if not isinstance(config, dict): | 268 if not isinstance(config, dict): |
245 raise Exception('config_path must be a JSON file containing a dictionary') | 269 raise Exception('config_path must be a JSON file containing a dictionary') |
246 self.files = config.get('files', []) | 270 self.files = config.get('files', []) |
247 self.targets = set(config.get('targets', [])) | 271 if config.get('targets'): |
| 272 self.targets = set(config.get('targets')) |
| 273 self.deprecated_mode = True |
| 274 else: |
| 275 self.additional_compile_target_names = set( |
| 276 config.get('additional_compile_targets', [])) |
| 277 self.test_target_names = set(config.get('test_targets', [])) |
248 | 278 |
249 | 279 |
250 def _WasBuildFileModified(build_file, data, files, toplevel_dir): | 280 def _WasBuildFileModified(build_file, data, files, toplevel_dir): |
251 """Returns true if the build file |build_file| is either in |files| or | 281 """Returns true if the build file |build_file| is either in |files| or |
252 one of the files included by |build_file| is in |files|. |toplevel_dir| is | 282 one of the files included by |build_file| is in |files|. |toplevel_dir| is |
253 the root of the source tree.""" | 283 the root of the source tree.""" |
254 if _ToLocalPath(toplevel_dir, _ToGypPath(build_file)) in files: | 284 if _ToLocalPath(toplevel_dir, _ToGypPath(build_file)) in files: |
255 if debug: | 285 if debug: |
256 print 'gyp file modified', build_file | 286 print 'gyp file modified', build_file |
257 return True | 287 return True |
(...skipping 30 matching lines...) Expand all Loading... |
288 # If a 'none' target has rules or actions we assume it requires a build. | 318 # If a 'none' target has rules or actions we assume it requires a build. |
289 return bool(target_dict['type'] != 'none' or | 319 return bool(target_dict['type'] != 'none' or |
290 target_dict.get('actions') or target_dict.get('rules')) | 320 target_dict.get('actions') or target_dict.get('rules')) |
291 | 321 |
292 | 322 |
293 def _GenerateTargets(data, target_list, target_dicts, toplevel_dir, files, | 323 def _GenerateTargets(data, target_list, target_dicts, toplevel_dir, files, |
294 build_files): | 324 build_files): |
295 """Returns a tuple of the following: | 325 """Returns a tuple of the following: |
296 . A dictionary mapping from fully qualified name to Target. | 326 . A dictionary mapping from fully qualified name to Target. |
297 . A list of the targets that have a source file in |files|. | 327 . A list of the targets that have a source file in |files|. |
298 . Set of root Targets reachable from the the files |build_files|. This | 328 . Targets that constitute the 'all' target. See description at top of file |
299 is the set of targets built by the 'all' target. | 329 for details on the 'all' target. |
300 This sets the |match_status| of the targets that contain any of the source | 330 This sets the |match_status| of the targets that contain any of the source |
301 files in |files| to MATCH_STATUS_MATCHES. | 331 files in |files| to MATCH_STATUS_MATCHES. |
302 |toplevel_dir| is the root of the source tree.""" | 332 |toplevel_dir| is the root of the source tree.""" |
303 # Maps from target name to Target. | 333 # Maps from target name to Target. |
304 targets = {} | 334 name_to_target = {} |
305 | 335 |
306 # Targets that matched. | 336 # Targets that matched. |
307 matching_targets = [] | 337 matching_targets = [] |
308 | 338 |
309 # Queue of targets to visit. | 339 # Queue of targets to visit. |
310 targets_to_visit = target_list[:] | 340 targets_to_visit = target_list[:] |
311 | 341 |
312 # Maps from build file to a boolean indicating whether the build file is in | 342 # Maps from build file to a boolean indicating whether the build file is in |
313 # |files|. | 343 # |files|. |
314 build_file_in_files = {} | 344 build_file_in_files = {} |
315 | 345 |
316 # Root targets across all files. | 346 # Root targets across all files. |
317 roots = set() | 347 roots = set() |
318 | 348 |
319 # Set of Targets in |build_files|. | 349 # Set of Targets in |build_files|. |
320 build_file_targets = set() | 350 build_file_targets = set() |
321 | 351 |
322 while len(targets_to_visit) > 0: | 352 while len(targets_to_visit) > 0: |
323 target_name = targets_to_visit.pop() | 353 target_name = targets_to_visit.pop() |
324 created_target, target = _GetOrCreateTargetByName(targets, target_name) | 354 created_target, target = _GetOrCreateTargetByName(name_to_target, |
| 355 target_name) |
325 if created_target: | 356 if created_target: |
326 roots.add(target) | 357 roots.add(target) |
327 elif target.visited: | 358 elif target.visited: |
328 continue | 359 continue |
329 | 360 |
330 target.visited = True | 361 target.visited = True |
331 target.requires_build = _DoesTargetTypeRequireBuild( | 362 target.requires_build = _DoesTargetTypeRequireBuild( |
332 target_dicts[target_name]) | 363 target_dicts[target_name]) |
333 target_type = target_dicts[target_name]['type'] | 364 target_type = target_dicts[target_name]['type'] |
334 target.is_executable = target_type == 'executable' | 365 target.is_executable = target_type == 'executable' |
(...skipping 22 matching lines...) Expand all Loading... |
357 if _ToGypPath(os.path.normpath(source)) in files: | 388 if _ToGypPath(os.path.normpath(source)) in files: |
358 print 'target', target_name, 'matches', source | 389 print 'target', target_name, 'matches', source |
359 target.match_status = MATCH_STATUS_MATCHES | 390 target.match_status = MATCH_STATUS_MATCHES |
360 matching_targets.append(target) | 391 matching_targets.append(target) |
361 break | 392 break |
362 | 393 |
363 # Add dependencies to visit as well as updating back pointers for deps. | 394 # Add dependencies to visit as well as updating back pointers for deps. |
364 for dep in target_dicts[target_name].get('dependencies', []): | 395 for dep in target_dicts[target_name].get('dependencies', []): |
365 targets_to_visit.append(dep) | 396 targets_to_visit.append(dep) |
366 | 397 |
367 created_dep_target, dep_target = _GetOrCreateTargetByName(targets, dep) | 398 created_dep_target, dep_target = _GetOrCreateTargetByName(name_to_target, |
| 399 dep) |
368 if not created_dep_target: | 400 if not created_dep_target: |
369 roots.discard(dep_target) | 401 roots.discard(dep_target) |
370 | 402 |
371 target.deps.add(dep_target) | 403 target.deps.add(dep_target) |
372 dep_target.back_deps.add(target) | 404 dep_target.back_deps.add(target) |
373 | 405 |
374 return targets, matching_targets, roots & build_file_targets | 406 return name_to_target, matching_targets, roots & build_file_targets |
375 | 407 |
376 | 408 |
377 def _GetUnqualifiedToTargetMapping(all_targets, to_find): | 409 def _GetUnqualifiedToTargetMapping(all_targets, to_find): |
378 """Returns a mapping (dictionary) from unqualified name to Target for all the | 410 """Returns a tuple of the following: |
379 Targets in |to_find|.""" | 411 . mapping (dictionary) from unqualified name to Target for all the |
| 412 Targets in |to_find|. |
| 413 . any target names not found. If this is empty all targets were found.""" |
380 result = {} | 414 result = {} |
381 if not to_find: | 415 if not to_find: |
382 return result | 416 return {}, [] |
383 to_find = set(to_find) | 417 to_find = set(to_find) |
384 for target_name in all_targets.keys(): | 418 for target_name in all_targets.keys(): |
385 extracted = gyp.common.ParseQualifiedTarget(target_name) | 419 extracted = gyp.common.ParseQualifiedTarget(target_name) |
386 if len(extracted) > 1 and extracted[1] in to_find: | 420 if len(extracted) > 1 and extracted[1] in to_find: |
387 to_find.remove(extracted[1]) | 421 to_find.remove(extracted[1]) |
388 result[extracted[1]] = all_targets[target_name] | 422 result[extracted[1]] = all_targets[target_name] |
389 if not to_find: | 423 if not to_find: |
390 return result | 424 return result, [] |
391 return result | 425 return result, [x for x in to_find] |
392 | 426 |
393 | 427 |
394 def _AddBuildTargets(target, roots, result): | 428 def _AddBuildTargetsDeprecated(target, roots, result): |
395 """Recurses through all targets that depend on |target|, adding all targets | 429 """Recurses through all targets that depend on |target|, adding all targets |
396 that need to be built (and are in |roots|) to |result|. | 430 that need to be built (and are in |roots|) to |result|. |
397 roots: set of root targets. | 431 roots: set of root targets. |
398 result: targets that need to be built are added here.""" | 432 result: targets that need to be built are added here.""" |
399 if target.visited: | 433 if target.visited: |
400 return | 434 return |
401 | 435 |
402 target.visited = True | 436 target.visited = True |
403 target.in_roots = target in roots | 437 target.in_roots = target in roots |
404 | 438 |
405 for back_dep_target in target.back_deps: | 439 for back_dep_target in target.back_deps: |
406 _AddBuildTargets(back_dep_target, roots, result) | 440 _AddBuildTargetsDeprecated(back_dep_target, roots, result) |
407 target.added_to_compile_targets |= back_dep_target.added_to_compile_targets | 441 target.added_to_compile_targets |= back_dep_target.added_to_compile_targets |
408 target.in_roots |= back_dep_target.in_roots | 442 target.in_roots |= back_dep_target.in_roots |
409 target.is_or_has_linked_ancestor |= ( | 443 target.is_or_has_linked_ancestor |= ( |
410 back_dep_target.is_or_has_linked_ancestor) | 444 back_dep_target.is_or_has_linked_ancestor) |
411 | 445 |
412 # Always add 'executable' targets. Even though they may be built by other | 446 # Always add 'executable' targets. Even though they may be built by other |
413 # targets that depend upon them it makes detection of what is going to be | 447 # targets that depend upon them it makes detection of what is going to be |
414 # built easier. | 448 # built easier. |
415 # And always add static_libraries that have no dependencies on them from | 449 # And always add static_libraries that have no dependencies on them from |
416 # linkables. This is necessary as the other dependencies on them may be | 450 # linkables. This is necessary as the other dependencies on them may be |
417 # static libraries themselves, which are not compile time dependencies. | 451 # static libraries themselves, which are not compile time dependencies. |
418 if target.in_roots and \ | 452 if target.in_roots and \ |
419 (target.is_executable or | 453 (target.is_executable or |
420 (not target.added_to_compile_targets and target.requires_build) or | 454 (not target.added_to_compile_targets and target.requires_build) or |
421 (target.is_static_library and not target.is_or_has_linked_ancestor)): | 455 (target.is_static_library and not target.is_or_has_linked_ancestor)): |
422 print '\t\tadding to build targets', target.name, 'executable', \ | 456 print '\t\tadding to build targets', target.name, 'executable', \ |
423 target.is_executable, 'added_to_compile_targets', \ | 457 target.is_executable, 'added_to_compile_targets', \ |
424 target.added_to_compile_targets, 'requires_build', \ | 458 target.added_to_compile_targets, 'requires_build', \ |
425 target.requires_build, 'is_static_library', \ | 459 target.requires_build, 'is_static_library', \ |
426 target.is_static_library, 'is_or_has_linked_ancestor', \ | 460 target.is_static_library, 'is_or_has_linked_ancestor', \ |
427 target.is_or_has_linked_ancestor | 461 target.is_or_has_linked_ancestor |
428 result.add(target) | 462 result.add(target) |
429 target.added_to_compile_targets = True | 463 target.added_to_compile_targets = True |
430 | 464 |
431 | 465 |
432 def _GetBuildTargets(matching_targets, roots): | 466 def _GetBuildTargetsDeprecated(matching_targets, roots): |
433 """Returns the set of Targets that require a build. | 467 """Returns the set of Targets that require a build. |
434 matching_targets: targets that changed and need to be built. | 468 matching_targets: targets that changed and need to be built. |
435 roots: set of root targets in the build files to search from.""" | 469 roots: set of root targets in the build files to search from.""" |
436 result = set() | 470 result = set() |
437 for target in matching_targets: | 471 for target in matching_targets: |
438 print '\tfinding build targets for match', target.name | 472 print '\tfinding build targets for match', target.name |
439 _AddBuildTargets(target, roots, result) | 473 _AddBuildTargetsDeprecated(target, roots, result) |
440 return result | 474 return result |
441 | 475 |
442 | 476 |
| 477 def _DoesTargetDependOnMatchingTargets(target): |
| 478 """Returns true if |target| or any of its dependencies is one of the |
| 479 targets containing the files supplied as input to analyzer. This updates |
| 480 |matches| of the Targets as it recurses. |
| 481 target: the Target to look for.""" |
| 482 if target.match_status == MATCH_STATUS_DOESNT_MATCH: |
| 483 return False |
| 484 if target.match_status == MATCH_STATUS_MATCHES or \ |
| 485 target.match_status == MATCH_STATUS_MATCHES_BY_DEPENDENCY: |
| 486 return True |
| 487 for dep in target.deps: |
| 488 if _DoesTargetDependOnMatchingTargets(dep): |
| 489 target.match_status = MATCH_STATUS_MATCHES_BY_DEPENDENCY |
| 490 print '\t', target.name, 'matches by dep', dep.name |
| 491 return True |
| 492 target.match_status = MATCH_STATUS_DOESNT_MATCH |
| 493 return False |
| 494 |
| 495 |
| 496 def _GetTargetsDependingOnMatchingTargets(possible_targets): |
| 497 """Returns the list of Targets in |possible_targets| that depend (either |
| 498 directly on indirectly) on at least one of the targets containing the files |
| 499 supplied as input to analyzer. |
| 500 possible_targets: targets to search from.""" |
| 501 found = [] |
| 502 print 'Targets that matched by dependency:' |
| 503 for target in possible_targets: |
| 504 if _DoesTargetDependOnMatchingTargets(target): |
| 505 found.append(target) |
| 506 return found |
| 507 |
| 508 |
| 509 def _AddCompileTargets(target, roots, add_if_no_ancestor, result): |
| 510 """Recurses through all targets that depend on |target|, adding all targets |
| 511 that need to be built (and are in |roots|) to |result|. |
| 512 roots: set of root targets. |
| 513 add_if_no_ancestor: If true and there are no ancestors of |target| then add |
| 514 |target| to |result|. |target| must still be in |roots|. |
| 515 result: targets that need to be built are added here.""" |
| 516 if target.visited: |
| 517 return |
| 518 |
| 519 target.visited = True |
| 520 target.in_roots = target in roots |
| 521 |
| 522 for back_dep_target in target.back_deps: |
| 523 _AddCompileTargets(back_dep_target, roots, False, result) |
| 524 target.added_to_compile_targets |= back_dep_target.added_to_compile_targets |
| 525 target.in_roots |= back_dep_target.in_roots |
| 526 target.is_or_has_linked_ancestor |= ( |
| 527 back_dep_target.is_or_has_linked_ancestor) |
| 528 |
| 529 # Always add 'executable' targets. Even though they may be built by other |
| 530 # targets that depend upon them it makes detection of what is going to be |
| 531 # built easier. |
| 532 # And always add static_libraries that have no dependencies on them from |
| 533 # linkables. This is necessary as the other dependencies on them may be |
| 534 # static libraries themselves, which are not compile time dependencies. |
| 535 if target.in_roots and \ |
| 536 (target.is_executable or |
| 537 (not target.added_to_compile_targets and |
| 538 (add_if_no_ancestor or target.requires_build)) or |
| 539 (target.is_static_library and add_if_no_ancestor and |
| 540 not target.is_or_has_linked_ancestor)): |
| 541 print '\t\tadding to compile targets', target.name, 'executable', \ |
| 542 target.is_executable, 'added_to_compile_targets', \ |
| 543 target.added_to_compile_targets, 'add_if_no_ancestor', \ |
| 544 add_if_no_ancestor, 'requires_build', target.requires_build, \ |
| 545 'is_static_library', target.is_static_library, \ |
| 546 'is_or_has_linked_ancestor', target.is_or_has_linked_ancestor |
| 547 result.add(target) |
| 548 target.added_to_compile_targets = True |
| 549 |
| 550 |
| 551 def _GetCompileTargets(matching_targets, supplied_targets): |
| 552 """Returns the set of Targets that require a build. |
| 553 matching_targets: targets that changed and need to be built. |
| 554 supplied_targets: set of targets supplied to analyzer to search from.""" |
| 555 result = set() |
| 556 for target in matching_targets: |
| 557 print 'finding compile targets for match', target.name |
| 558 _AddCompileTargets(target, supplied_targets, True, result) |
| 559 return result |
| 560 |
| 561 |
443 def _WriteOutput(params, **values): | 562 def _WriteOutput(params, **values): |
444 """Writes the output, either to stdout or a file is specified.""" | 563 """Writes the output, either to stdout or a file is specified.""" |
445 if 'error' in values: | 564 if 'error' in values: |
446 print 'Error:', values['error'] | 565 print 'Error:', values['error'] |
447 if 'status' in values: | 566 if 'status' in values: |
448 print values['status'] | 567 print values['status'] |
449 if 'targets' in values: | 568 if 'targets' in values: |
450 values['targets'].sort() | 569 values['targets'].sort() |
451 print 'Supplied targets that depend on changed files:' | 570 print 'Supplied targets that depend on changed files:' |
452 for target in values['targets']: | 571 for target in values['targets']: |
453 print '\t', target | 572 print '\t', target |
454 if 'invalid_targets' in values: | 573 if 'invalid_targets' in values: |
455 values['invalid_targets'].sort() | 574 values['invalid_targets'].sort() |
456 print 'The following targets were not found:' | 575 print 'The following targets were not found:' |
457 for target in values['invalid_targets']: | 576 for target in values['invalid_targets']: |
458 print '\t', target | 577 print '\t', target |
459 if 'build_targets' in values: | 578 if 'build_targets' in values: |
460 values['build_targets'].sort() | 579 values['build_targets'].sort() |
461 print 'Targets that require a build:' | 580 print 'Targets that require a build:' |
462 for target in values['build_targets']: | 581 for target in values['build_targets']: |
463 print '\t', target | 582 print '\t', target |
| 583 if 'compile_targets' in values: |
| 584 values['compile_targets'].sort() |
| 585 print 'Targets that need to be built:' |
| 586 for target in values['compile_targets']: |
| 587 print '\t', target |
| 588 if 'test_targets' in values: |
| 589 values['test_targets'].sort() |
| 590 print 'Test targets:' |
| 591 for target in values['test_targets']: |
| 592 print '\t', target |
464 | 593 |
465 output_path = params.get('generator_flags', {}).get( | 594 output_path = params.get('generator_flags', {}).get( |
466 'analyzer_output_path', None) | 595 'analyzer_output_path', None) |
467 if not output_path: | 596 if not output_path: |
468 print json.dumps(values) | 597 print json.dumps(values) |
469 return | 598 return |
470 try: | 599 try: |
471 f = open(output_path, 'w') | 600 f = open(output_path, 'w') |
472 f.write(json.dumps(values) + '\n') | 601 f.write(json.dumps(values) + '\n') |
473 f.close() | 602 f.close() |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
513 'generator_additional_path_sections', []) | 642 'generator_additional_path_sections', []) |
514 | 643 |
515 gyp.msvs_emulation.CalculateCommonVariables(default_variables, params) | 644 gyp.msvs_emulation.CalculateCommonVariables(default_variables, params) |
516 else: | 645 else: |
517 operating_system = flavor | 646 operating_system = flavor |
518 if flavor == 'android': | 647 if flavor == 'android': |
519 operating_system = 'linux' # Keep this legacy behavior for now. | 648 operating_system = 'linux' # Keep this legacy behavior for now. |
520 default_variables.setdefault('OS', operating_system) | 649 default_variables.setdefault('OS', operating_system) |
521 | 650 |
522 | 651 |
| 652 def _GenerateOutputDeprecated(target_list, target_dicts, data, params, config): |
| 653 """Old deprecated behavior, will be nuked shortly.""" |
| 654 toplevel_dir = _ToGypPath(os.path.abspath(params['options'].toplevel_dir)) |
| 655 |
| 656 if _WasGypIncludeFileModified(params, config.files): |
| 657 result_dict = { 'status': all_changed_string, |
| 658 'targets': list(config.targets) } |
| 659 _WriteOutput(params, **result_dict) |
| 660 return |
| 661 |
| 662 all_targets, matching_targets, root_targets = _GenerateTargets( |
| 663 data, target_list, target_dicts, toplevel_dir, frozenset(config.files), |
| 664 params['build_files']) |
| 665 |
| 666 unqualified_mapping, invalid_targets = _GetUnqualifiedToTargetMapping( |
| 667 all_targets, config.targets) |
| 668 |
| 669 if matching_targets: |
| 670 search_targets = _LookupTargets(config.targets, unqualified_mapping) |
| 671 print 'supplied targets' |
| 672 for target in config.targets: |
| 673 print '\t', target |
| 674 print 'expanded supplied targets' |
| 675 for target in search_targets: |
| 676 print '\t', target.name |
| 677 # Reset the visited status for _GetBuildTargets. |
| 678 for target in all_targets.itervalues(): |
| 679 target.visited = False |
| 680 build_targets = _GetBuildTargetsDeprecated(matching_targets, search_targets) |
| 681 build_targets = [gyp.common.ParseQualifiedTarget(target.name)[1] |
| 682 for target in build_targets] |
| 683 else: |
| 684 build_targets = [] |
| 685 |
| 686 result_dict = { 'targets': build_targets, |
| 687 'status': found_dependency_string if matching_targets else |
| 688 no_dependency_string, |
| 689 'build_targets': build_targets} |
| 690 if invalid_targets: |
| 691 result_dict['invalid_targets'] = invalid_targets |
| 692 _WriteOutput(params, **result_dict) |
| 693 |
| 694 |
| 695 class TargetCalculator(object): |
| 696 """Calculates the matching test_targets and matching compile_targets.""" |
| 697 def __init__(self, files, additional_compile_target_names, test_target_names, |
| 698 data, target_list, target_dicts, toplevel_dir, build_files): |
| 699 self._additional_compile_target_names = set(additional_compile_target_names) |
| 700 self._test_target_names = set(test_target_names) |
| 701 self._name_to_target, self._changed_targets, self._root_targets = ( |
| 702 _GenerateTargets(data, target_list, target_dicts, toplevel_dir, |
| 703 frozenset(files), build_files)) |
| 704 self._unqualified_mapping, self.invalid_targets = ( |
| 705 _GetUnqualifiedToTargetMapping(self._name_to_target, |
| 706 self._supplied_target_names_no_all())) |
| 707 |
| 708 def _supplied_target_names(self): |
| 709 return self._additional_compile_target_names | self._test_target_names |
| 710 |
| 711 def _supplied_target_names_no_all(self): |
| 712 """Returns the supplied test targets without 'all'.""" |
| 713 result = self._supplied_target_names(); |
| 714 result.discard('all') |
| 715 return result |
| 716 |
| 717 def is_build_impacted(self): |
| 718 """Returns true if the supplied files impact the build at all.""" |
| 719 return self._changed_targets |
| 720 |
| 721 def find_matching_test_target_names(self): |
| 722 """Returns the set of output test targets.""" |
| 723 assert self.is_build_impacted() |
| 724 # Find the test targets first. 'all' is special cased to mean all the |
| 725 # root targets. To deal with all the supplied |test_targets| are expanded |
| 726 # to include the root targets during lookup. If any of the root targets |
| 727 # match, we remove it and replace it with 'all'. |
| 728 test_target_names_no_all = set(self._test_target_names) |
| 729 test_target_names_no_all.discard('all') |
| 730 test_targets_no_all = _LookupTargets(test_target_names_no_all, |
| 731 self._unqualified_mapping) |
| 732 test_target_names_contains_all = 'all' in self._test_target_names |
| 733 if test_target_names_contains_all: |
| 734 test_targets = [x for x in (set(test_targets_no_all) | |
| 735 set(self._root_targets))] |
| 736 else: |
| 737 test_targets = [x for x in test_targets_no_all] |
| 738 print 'supplied test_targets' |
| 739 for target_name in self._test_target_names: |
| 740 print '\t', target_name |
| 741 print 'found test_targets' |
| 742 for target in test_targets: |
| 743 print '\t', target.name |
| 744 print 'searching for matching test targets' |
| 745 matching_test_targets = _GetTargetsDependingOnMatchingTargets(test_targets) |
| 746 matching_test_targets_contains_all = (test_target_names_contains_all and |
| 747 set(matching_test_targets) & |
| 748 set(self._root_targets)) |
| 749 if matching_test_targets_contains_all: |
| 750 # Remove any of the targets for all that were not explicitly supplied, |
| 751 # 'all' is subsequentely added to the matching names below. |
| 752 matching_test_targets = [x for x in (set(matching_test_targets) & |
| 753 set(test_targets_no_all))] |
| 754 print 'matched test_targets' |
| 755 for target in matching_test_targets: |
| 756 print '\t', target.name |
| 757 matching_target_names = [gyp.common.ParseQualifiedTarget(target.name)[1] |
| 758 for target in matching_test_targets] |
| 759 if matching_test_targets_contains_all: |
| 760 matching_target_names.append('all') |
| 761 print '\tall' |
| 762 return matching_target_names |
| 763 |
| 764 def find_matching_compile_target_names(self): |
| 765 """Returns the set of output compile targets.""" |
| 766 assert self.is_build_impacted(); |
| 767 # Compile targets are found by searching up from changed targets. |
| 768 # Reset the visited status for _GetBuildTargets. |
| 769 for target in self._name_to_target.itervalues(): |
| 770 target.visited = False |
| 771 |
| 772 supplied_targets = _LookupTargets(self._supplied_target_names_no_all(), |
| 773 self._unqualified_mapping) |
| 774 if 'all' in self._supplied_target_names(): |
| 775 supplied_targets = [x for x in (set(supplied_targets) | |
| 776 set(self._root_targets))] |
| 777 print 'Supplied test_targets & compile_targets' |
| 778 for target in supplied_targets: |
| 779 print '\t', target.name |
| 780 print 'Finding compile targets' |
| 781 compile_targets = _GetCompileTargets(self._changed_targets, |
| 782 supplied_targets) |
| 783 return [gyp.common.ParseQualifiedTarget(target.name)[1] |
| 784 for target in compile_targets] |
| 785 |
| 786 |
523 def GenerateOutput(target_list, target_dicts, data, params): | 787 def GenerateOutput(target_list, target_dicts, data, params): |
524 """Called by gyp as the final stage. Outputs results.""" | 788 """Called by gyp as the final stage. Outputs results.""" |
525 config = Config() | 789 config = Config() |
526 try: | 790 try: |
527 config.Init(params) | 791 config.Init(params) |
| 792 |
528 if not config.files: | 793 if not config.files: |
529 raise Exception('Must specify files to analyze via config_path generator ' | 794 raise Exception('Must specify files to analyze via config_path generator ' |
530 'flag') | 795 'flag') |
531 | 796 |
| 797 if config.deprecated_mode: |
| 798 _GenerateOutputDeprecated(target_list, target_dicts, data, params, |
| 799 config) |
| 800 return |
| 801 |
532 toplevel_dir = _ToGypPath(os.path.abspath(params['options'].toplevel_dir)) | 802 toplevel_dir = _ToGypPath(os.path.abspath(params['options'].toplevel_dir)) |
533 if debug: | 803 if debug: |
534 print 'toplevel_dir', toplevel_dir | 804 print 'toplevel_dir', toplevel_dir |
535 | 805 |
536 if _WasGypIncludeFileModified(params, config.files): | 806 if _WasGypIncludeFileModified(params, config.files): |
537 result_dict = { 'status': all_changed_string, | 807 result_dict = { 'status': all_changed_string, |
538 'targets': list(config.targets) } | 808 'test_targets': list(config.test_target_names), |
| 809 'compile_targets': list( |
| 810 config.additional_compile_target_names | |
| 811 config.test_target_names) } |
539 _WriteOutput(params, **result_dict) | 812 _WriteOutput(params, **result_dict) |
540 return | 813 return |
541 | 814 |
542 all_targets, matching_targets, _ = _GenerateTargets( | 815 calculator = TargetCalculator(config.files, |
543 data, target_list, target_dicts, toplevel_dir, frozenset(config.files), | 816 config.additional_compile_target_names, |
544 params['build_files']) | 817 config.test_target_names, data, |
| 818 target_list, target_dicts, toplevel_dir, |
| 819 params['build_files']) |
| 820 if not calculator.is_build_impacted(): |
| 821 _WriteOutput(params, test_targets=[], compile_targets=[], |
| 822 status=no_dependency_string, |
| 823 invalid_targets=calculator.invalid_targets) |
| 824 return |
545 | 825 |
546 unqualified_mapping = _GetUnqualifiedToTargetMapping(all_targets, | 826 test_target_names = calculator.find_matching_test_target_names() |
547 config.targets) | 827 compile_target_names = calculator.find_matching_compile_target_names() |
548 invalid_targets = None | 828 found_at_least_one_target = compile_target_names or test_target_names |
549 if len(unqualified_mapping) != len(config.targets): | 829 result_dict = { 'test_targets': test_target_names, |
550 invalid_targets = _NamesNotIn(config.targets, unqualified_mapping) | 830 'status': found_dependency_string if |
551 | 831 found_at_least_one_target else no_dependency_string, |
552 if matching_targets: | 832 'compile_targets': compile_target_names} |
553 search_targets = _LookupTargets(config.targets, unqualified_mapping) | 833 if calculator.invalid_targets: |
554 print 'supplied targets' | 834 result_dict['invalid_targets'] = calculator.invalid_targets |
555 for target in config.targets: | |
556 print '\t', target | |
557 print 'expanded supplied targets' | |
558 for target in search_targets: | |
559 print '\t', target.name | |
560 # Reset the visited status for _GetBuildTargets. | |
561 for target in all_targets.itervalues(): | |
562 target.visited = False | |
563 print 'Finding build targets' | |
564 build_targets = _GetBuildTargets(matching_targets, search_targets) | |
565 build_targets = [gyp.common.ParseQualifiedTarget(target.name)[1] | |
566 for target in build_targets] | |
567 else: | |
568 build_targets = [] | |
569 | |
570 # TODO(sky): nuke 'targets'. | |
571 result_dict = { 'targets': build_targets, | |
572 'status': found_dependency_string if matching_targets else | |
573 no_dependency_string, | |
574 'build_targets': build_targets} | |
575 if invalid_targets: | |
576 result_dict['invalid_targets'] = invalid_targets | |
577 _WriteOutput(params, **result_dict) | 835 _WriteOutput(params, **result_dict) |
578 | 836 |
579 except Exception as e: | 837 except Exception as e: |
580 _WriteOutput(params, error=str(e)) | 838 _WriteOutput(params, error=str(e)) |
OLD | NEW |