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" cosists of "a1" and "a2". Notice | |
Dirk Pranke
2015/11/12 00:37:20
nit: s/cosists/consists/
sky
2015/11/12 00:47:22
Done.
| |
59 that "b1" and "b2" are not in the "all" target as "b.gyp" was not directly | |
60 supplied to gyp. OTOH if both "a.gyp" and "b.gyp" are supplied to gyp then | |
61 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|. |
Dirk Pranke
2015/11/12 00:37:20
this is actually the set of all targets in build_f
sky
2015/11/12 00:47:23
No, just the set of targets in the build files. Th
Dirk Pranke
2015/11/12 00:48:41
Acknowledged.
| |
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) } | |
539 _WriteOutput(params, **result_dict) | 811 _WriteOutput(params, **result_dict) |
540 return | 812 return |
541 | 813 |
542 all_targets, matching_targets, _ = _GenerateTargets( | 814 calculator = TargetCalculator(config.files, |
543 data, target_list, target_dicts, toplevel_dir, frozenset(config.files), | 815 config.additional_compile_target_names, |
544 params['build_files']) | 816 config.test_target_names, data, |
817 target_list, target_dicts, toplevel_dir, | |
818 params['build_files']) | |
819 if not calculator.is_build_impacted(): | |
820 _WriteOutput(params, test_targets=[], compile_targets=[], | |
821 status=no_dependency_string, | |
822 invalid_targets=calculator.invalid_targets) | |
823 return | |
545 | 824 |
546 unqualified_mapping = _GetUnqualifiedToTargetMapping(all_targets, | 825 test_target_names = calculator.find_matching_test_target_names() |
547 config.targets) | 826 compile_target_names = calculator.find_matching_compile_target_names() |
548 invalid_targets = None | 827 found_at_least_one_target = compile_target_names or test_target_names |
549 if len(unqualified_mapping) != len(config.targets): | 828 result_dict = { 'test_targets': test_target_names, |
550 invalid_targets = _NamesNotIn(config.targets, unqualified_mapping) | 829 'status': found_dependency_string if |
551 | 830 found_at_least_one_target else no_dependency_string, |
552 if matching_targets: | 831 'compile_targets': compile_target_names} |
553 search_targets = _LookupTargets(config.targets, unqualified_mapping) | 832 if calculator.invalid_targets: |
554 print 'supplied targets' | 833 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) | 834 _WriteOutput(params, **result_dict) |
578 | 835 |
579 except Exception as e: | 836 except Exception as e: |
580 _WriteOutput(params, error=str(e)) | 837 _WriteOutput(params, error=str(e)) |
OLD | NEW |