Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 # Copyright (c) 2012 Google Inc. All rights reserved. | 1 # Copyright (c) 2012 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 from compiler.ast import Const | 5 from compiler.ast import Const |
| 6 from compiler.ast import Dict | 6 from compiler.ast import Dict |
| 7 from compiler.ast import Discard | 7 from compiler.ast import Discard |
| 8 from compiler.ast import List | 8 from compiler.ast import List |
| 9 from compiler.ast import Module | 9 from compiler.ast import Module |
| 10 from compiler.ast import Node | 10 from compiler.ast import Node |
| (...skipping 344 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 355 if depth: | 355 if depth: |
| 356 # TODO(dglazkov) The backslash/forward-slash replacement at the end is a | 356 # TODO(dglazkov) The backslash/forward-slash replacement at the end is a |
| 357 # temporary measure. This should really be addressed by keeping all paths | 357 # temporary measure. This should really be addressed by keeping all paths |
| 358 # in POSIX until actual project generation. | 358 # in POSIX until actual project generation. |
| 359 d = gyp.common.RelativePath(depth, os.path.dirname(build_file_path)) | 359 d = gyp.common.RelativePath(depth, os.path.dirname(build_file_path)) |
| 360 if d == '': | 360 if d == '': |
| 361 variables['DEPTH'] = '.' | 361 variables['DEPTH'] = '.' |
| 362 else: | 362 else: |
| 363 variables['DEPTH'] = d.replace('\\', '/') | 363 variables['DEPTH'] = d.replace('\\', '/') |
| 364 | 364 |
| 365 if build_file_path in data['target_build_files']: | 365 # The 'target_build_files' key is only set when loading target build files in |
| 366 # Already loaded. | 366 # the non-parallel code path, where LoadTargetBuildFile is called |
| 367 return False | 367 # recursively. In the parallel code path, we don't need to check whether the |
| 368 data['target_build_files'].add(build_file_path) | 368 # |build_file_path| has already been loaded, because the 'scheduled' set in |
| 369 # ParallelState guarantees that we never load the same |build_file_path| | |
| 370 # twice. | |
| 371 if 'target_build_files' in data: | |
| 372 if build_file_path in data['target_build_files']: | |
| 373 # Already loaded. | |
| 374 return False | |
| 375 data['target_build_files'].add(build_file_path) | |
| 369 | 376 |
| 370 gyp.DebugOutput(gyp.DEBUG_INCLUDES, | 377 gyp.DebugOutput(gyp.DEBUG_INCLUDES, |
| 371 "Loading Target Build File '%s'", build_file_path) | 378 "Loading Target Build File '%s'", build_file_path) |
| 372 | 379 |
| 373 build_file_data = LoadOneBuildFile(build_file_path, data, aux_data, | 380 build_file_data = LoadOneBuildFile(build_file_path, data, aux_data, |
| 374 includes, True, check) | 381 includes, True, check) |
| 375 | 382 |
| 376 # Store DEPTH for later use in generators. | 383 # Store DEPTH for later use in generators. |
| 377 build_file_data['_DEPTH'] = depth | 384 build_file_data['_DEPTH'] = depth |
| 378 | 385 |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 449 try: | 456 try: |
| 450 LoadTargetBuildFile(dependency, data, aux_data, variables, | 457 LoadTargetBuildFile(dependency, data, aux_data, variables, |
| 451 includes, depth, check, load_dependencies) | 458 includes, depth, check, load_dependencies) |
| 452 except Exception, e: | 459 except Exception, e: |
| 453 gyp.common.ExceptionAppend( | 460 gyp.common.ExceptionAppend( |
| 454 e, 'while loading dependencies of %s' % build_file_path) | 461 e, 'while loading dependencies of %s' % build_file_path) |
| 455 raise | 462 raise |
| 456 else: | 463 else: |
| 457 return (build_file_path, dependencies) | 464 return (build_file_path, dependencies) |
| 458 | 465 |
| 459 | 466 per_process_data = {} |
|
scottmg
2014/12/02 22:50:52
please move these up to the top
| |
| 467 per_process_aux_data = {} | |
| 460 def CallLoadTargetBuildFile(global_flags, | 468 def CallLoadTargetBuildFile(global_flags, |
| 461 build_file_path, data, | 469 build_file_path, variables, |
| 462 aux_data, variables, | |
| 463 includes, depth, check, | 470 includes, depth, check, |
| 464 generator_input_info): | 471 generator_input_info): |
| 465 """Wrapper around LoadTargetBuildFile for parallel processing. | 472 """Wrapper around LoadTargetBuildFile for parallel processing. |
| 466 | 473 |
| 467 This wrapper is used when LoadTargetBuildFile is executed in | 474 This wrapper is used when LoadTargetBuildFile is executed in |
| 468 a worker process. | 475 a worker process. |
| 469 """ | 476 """ |
| 470 | 477 |
|
scottmg
2014/12/02 22:50:52
can you add
global per_process_data
global pe
| |
| 471 try: | 478 try: |
| 472 signal.signal(signal.SIGINT, signal.SIG_IGN) | 479 signal.signal(signal.SIGINT, signal.SIG_IGN) |
| 473 | 480 |
| 474 # Apply globals so that the worker process behaves the same. | 481 # Apply globals so that the worker process behaves the same. |
| 475 for key, value in global_flags.iteritems(): | 482 for key, value in global_flags.iteritems(): |
| 476 globals()[key] = value | 483 globals()[key] = value |
| 477 | 484 |
| 478 # Save the keys so we can return data that changed. | |
| 479 data_keys = set(data) | |
| 480 aux_data_keys = set(aux_data) | |
| 481 | |
| 482 SetGeneratorGlobals(generator_input_info) | 485 SetGeneratorGlobals(generator_input_info) |
| 483 result = LoadTargetBuildFile(build_file_path, data, | 486 result = LoadTargetBuildFile(build_file_path, per_process_data, |
| 484 aux_data, variables, | 487 per_process_aux_data, variables, |
| 485 includes, depth, check, False) | 488 includes, depth, check, False) |
| 486 if not result: | 489 if not result: |
| 487 return result | 490 return result |
| 488 | 491 |
| 489 (build_file_path, dependencies) = result | 492 (build_file_path, dependencies) = result |
| 490 | 493 |
| 491 data_out = {} | 494 # We can safely delete the build_file_data from per_process_data because it |
| 492 for key in data: | 495 # will never be referenced by this process again, so we don't need to keep |
| 493 if key == 'target_build_files': | 496 # it in the cache. |
| 494 continue | 497 build_file_data = per_process_data[build_file_path] |
|
scottmg
2014/12/02 22:50:52
build_file_data = per_process_data.pop(build_file_
| |
| 495 if key not in data_keys: | 498 del per_process_data[build_file_path] |
| 496 data_out[key] = data[key] | |
| 497 aux_data_out = {} | |
| 498 for key in aux_data: | |
| 499 if key not in aux_data_keys: | |
| 500 aux_data_out[key] = aux_data[key] | |
| 501 | 499 |
| 502 # This gets serialized and sent back to the main process via a pipe. | 500 # This gets serialized and sent back to the main process via a pipe. |
| 503 # It's handled in LoadTargetBuildFileCallback. | 501 # It's handled in LoadTargetBuildFileCallback. |
| 504 return (build_file_path, | 502 return (build_file_path, |
| 505 data_out, | 503 build_file_data, |
| 506 aux_data_out, | |
| 507 dependencies) | 504 dependencies) |
| 508 except GypError, e: | 505 except GypError, e: |
| 509 sys.stderr.write("gyp: %s\n" % e) | 506 sys.stderr.write("gyp: %s\n" % e) |
| 510 return None | 507 return None |
| 511 except Exception, e: | 508 except Exception, e: |
| 512 print >>sys.stderr, 'Exception:', e | 509 print >>sys.stderr, 'Exception:', e |
| 513 print >>sys.stderr, traceback.format_exc() | 510 print >>sys.stderr, traceback.format_exc() |
| 514 return None | 511 return None |
| 515 | 512 |
| 516 | 513 |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 527 """ | 524 """ |
| 528 | 525 |
| 529 def __init__(self): | 526 def __init__(self): |
| 530 # The multiprocessing pool. | 527 # The multiprocessing pool. |
| 531 self.pool = None | 528 self.pool = None |
| 532 # The condition variable used to protect this object and notify | 529 # The condition variable used to protect this object and notify |
| 533 # the main loop when there might be more data to process. | 530 # the main loop when there might be more data to process. |
| 534 self.condition = None | 531 self.condition = None |
| 535 # The "data" dict that was passed to LoadTargetBuildFileParallel | 532 # The "data" dict that was passed to LoadTargetBuildFileParallel |
| 536 self.data = None | 533 self.data = None |
| 537 # The "aux_data" dict that was passed to LoadTargetBuildFileParallel | |
| 538 self.aux_data = None | |
| 539 # The number of parallel calls outstanding; decremented when a response | 534 # The number of parallel calls outstanding; decremented when a response |
| 540 # was received. | 535 # was received. |
| 541 self.pending = 0 | 536 self.pending = 0 |
| 542 # The set of all build files that have been scheduled, so we don't | 537 # The set of all build files that have been scheduled, so we don't |
| 543 # schedule the same one twice. | 538 # schedule the same one twice. |
| 544 self.scheduled = set() | 539 self.scheduled = set() |
| 545 # A list of dependency build file paths that haven't been scheduled yet. | 540 # A list of dependency build file paths that haven't been scheduled yet. |
| 546 self.dependencies = [] | 541 self.dependencies = [] |
| 547 # Flag to indicate if there was an error in a child process. | 542 # Flag to indicate if there was an error in a child process. |
| 548 self.error = False | 543 self.error = False |
| 549 | 544 |
| 550 def LoadTargetBuildFileCallback(self, result): | 545 def LoadTargetBuildFileCallback(self, result): |
| 551 """Handle the results of running LoadTargetBuildFile in another process. | 546 """Handle the results of running LoadTargetBuildFile in another process. |
| 552 """ | 547 """ |
| 553 self.condition.acquire() | 548 self.condition.acquire() |
| 554 if not result: | 549 if not result: |
| 555 self.error = True | 550 self.error = True |
| 556 self.condition.notify() | 551 self.condition.notify() |
| 557 self.condition.release() | 552 self.condition.release() |
| 558 return | 553 return |
| 559 (build_file_path0, data0, aux_data0, dependencies0) = result | 554 (build_file_path0, build_file_data0, dependencies0) = result |
| 555 self.data[build_file_path0] = build_file_data0 | |
| 560 self.data['target_build_files'].add(build_file_path0) | 556 self.data['target_build_files'].add(build_file_path0) |
| 561 for key in data0: | |
| 562 self.data[key] = data0[key] | |
| 563 for key in aux_data0: | |
| 564 self.aux_data[key] = aux_data0[key] | |
| 565 for new_dependency in dependencies0: | 557 for new_dependency in dependencies0: |
| 566 if new_dependency not in self.scheduled: | 558 if new_dependency not in self.scheduled: |
| 567 self.scheduled.add(new_dependency) | 559 self.scheduled.add(new_dependency) |
| 568 self.dependencies.append(new_dependency) | 560 self.dependencies.append(new_dependency) |
| 569 self.pending -= 1 | 561 self.pending -= 1 |
| 570 self.condition.notify() | 562 self.condition.notify() |
| 571 self.condition.release() | 563 self.condition.release() |
| 572 | 564 |
| 573 | 565 |
| 574 def LoadTargetBuildFilesParallel(build_files, data, aux_data, | 566 def LoadTargetBuildFilesParallel(build_files, data, aux_data, |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 588 while parallel_state.dependencies or parallel_state.pending: | 580 while parallel_state.dependencies or parallel_state.pending: |
| 589 if parallel_state.error: | 581 if parallel_state.error: |
| 590 break | 582 break |
| 591 if not parallel_state.dependencies: | 583 if not parallel_state.dependencies: |
| 592 parallel_state.condition.wait() | 584 parallel_state.condition.wait() |
| 593 continue | 585 continue |
| 594 | 586 |
| 595 dependency = parallel_state.dependencies.pop() | 587 dependency = parallel_state.dependencies.pop() |
| 596 | 588 |
| 597 parallel_state.pending += 1 | 589 parallel_state.pending += 1 |
| 598 data_in = {} | |
| 599 data_in['target_build_files'] = data['target_build_files'] | |
| 600 aux_data_in = {} | |
| 601 global_flags = { | 590 global_flags = { |
| 602 'path_sections': globals()['path_sections'], | 591 'path_sections': globals()['path_sections'], |
| 603 'non_configuration_keys': globals()['non_configuration_keys'], | 592 'non_configuration_keys': globals()['non_configuration_keys'], |
| 604 'multiple_toolsets': globals()['multiple_toolsets']} | 593 'multiple_toolsets': globals()['multiple_toolsets']} |
| 605 | 594 |
| 606 if not parallel_state.pool: | 595 if not parallel_state.pool: |
| 607 parallel_state.pool = multiprocessing.Pool(multiprocessing.cpu_count()) | 596 parallel_state.pool = multiprocessing.Pool(multiprocessing.cpu_count()) |
| 608 parallel_state.pool.apply_async( | 597 parallel_state.pool.apply_async( |
| 609 CallLoadTargetBuildFile, | 598 CallLoadTargetBuildFile, |
| 610 args = (global_flags, dependency, | 599 args = (global_flags, dependency, |
| 611 data_in, aux_data_in, | |
| 612 variables, includes, depth, check, generator_input_info), | 600 variables, includes, depth, check, generator_input_info), |
| 613 callback = parallel_state.LoadTargetBuildFileCallback) | 601 callback = parallel_state.LoadTargetBuildFileCallback) |
| 614 except KeyboardInterrupt, e: | 602 except KeyboardInterrupt, e: |
| 615 parallel_state.pool.terminate() | 603 parallel_state.pool.terminate() |
| 616 raise e | 604 raise e |
| 617 | 605 |
| 618 parallel_state.condition.release() | 606 parallel_state.condition.release() |
| 619 | 607 |
| 620 parallel_state.pool.close() | 608 parallel_state.pool.close() |
| 621 parallel_state.pool.join() | 609 parallel_state.pool.join() |
| (...skipping 2235 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2857 ValidateRunAsInTarget(target, target_dict, build_file) | 2845 ValidateRunAsInTarget(target, target_dict, build_file) |
| 2858 ValidateActionsInTarget(target, target_dict, build_file) | 2846 ValidateActionsInTarget(target, target_dict, build_file) |
| 2859 | 2847 |
| 2860 # Generators might not expect ints. Turn them into strs. | 2848 # Generators might not expect ints. Turn them into strs. |
| 2861 TurnIntIntoStrInDict(data) | 2849 TurnIntIntoStrInDict(data) |
| 2862 | 2850 |
| 2863 # TODO(mark): Return |data| for now because the generator needs a list of | 2851 # TODO(mark): Return |data| for now because the generator needs a list of |
| 2864 # build files that came in. In the future, maybe it should just accept | 2852 # build files that came in. In the future, maybe it should just accept |
| 2865 # a list, and not the whole data dict. | 2853 # a list, and not the whole data dict. |
| 2866 return [flat_list, targets, data] | 2854 return [flat_list, targets, data] |
| OLD | NEW |