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

Side by Side Diff: scripts/slave/annotated_run.py

Issue 1071733003: Revert of Make allow_subannotations more robust. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/build
Patch Set: Created 5 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « scripts/common/annotator.py ('k') | scripts/slave/recipe_modules/step/api.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright (c) 2013 The Chromium Authors. All rights reserved. 2 # Copyright (c) 2013 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 """Entry point for fully-annotated builds. 6 """Entry point for fully-annotated builds.
7 7
8 This script is part of the effort to move all builds to annotator-based 8 This script is part of the effort to move all builds to annotator-based
9 systems. Any builder configured to use the AnnotatorFactory.BaseFactory() 9 systems. Any builder configured to use the AnnotatorFactory.BaseFactory()
10 found in scripts/master/factory/annotator_factory.py executes a single 10 found in scripts/master/factory/annotator_factory.py executes a single
(...skipping 187 matching lines...) Expand 10 before | Expand all | Expand 10 after
198 self.abort_reason = None 198 self.abort_reason = None
199 199
200 @property 200 @property
201 def step(self): 201 def step(self):
202 return copy.deepcopy(self._step) 202 return copy.deepcopy(self._step)
203 203
204 @property 204 @property
205 def retcode(self): 205 def retcode(self):
206 return self._retcode 206 return self._retcode
207 207
208 @retcode.setter
209 def retcode(self, val):
210 assert self._retcode is None, 'Can\'t override already-defined retcode'
211 self._retcode = val
212
213 @property 208 @property
214 def presentation(self): 209 def presentation(self):
215 return self._presentation 210 return self._presentation
216 211
217 # TODO(martiniss) update comment 212 # TODO(martiniss) update comment
218 # Result of 'render_step', fed into 'step_callback'. 213 # Result of 'render_step', fed into 'step_callback'.
219 Placeholders = collections.namedtuple( 214 Placeholders = collections.namedtuple(
220 'Placeholders', ['cmd', 'stdout', 'stderr', 'stdin']) 215 'Placeholders', ['cmd', 'stdout', 'stderr', 'stdin'])
221 216
222 217
(...skipping 222 matching lines...) Expand 10 before | Expand all | Expand 10 after
445 return engine.run(steps, api) 440 return engine.run(steps, api)
446 441
447 442
448 class RecipeEngine(object): 443 class RecipeEngine(object):
449 """Knows how to execute steps emitted by a recipe, holds global state such as 444 """Knows how to execute steps emitted by a recipe, holds global state such as
450 step history and build properties. Each recipe module API has a reference to 445 step history and build properties. Each recipe module API has a reference to
451 this object. 446 this object.
452 447
453 Recipe modules that are aware of the engine: 448 Recipe modules that are aware of the engine:
454 * properties - uses engine.properties. 449 * properties - uses engine.properties.
450 * step_history - uses engine.step_history.
455 * step - uses engine.create_step(...). 451 * step - uses engine.create_step(...).
456 452
457 This class acts mostly as a documentation of expected public engine interface. 453 This class acts mostly as a documentation of expected public engine interface.
458 """ 454 """
459 455
460 @staticmethod 456 @staticmethod
461 def create(stream, properties, test_data): 457 def create(stream, properties, test_data):
462 """Create a new instance of RecipeEngine based on 'engine' property.""" 458 """Create a new instance of RecipeEngine based on 'engine' property."""
463 engine_cls_name = properties.get('engine', 'SequentialRecipeEngine') 459 engine_cls_name = properties.get('engine', 'SequentialRecipeEngine')
464 for cls in RecipeEngine.__subclasses__(): 460 for cls in RecipeEngine.__subclasses__():
(...skipping 27 matching lines...) Expand all
492 Args: 488 Args:
493 step: ConfigGroup object with information about the step, see 489 step: ConfigGroup object with information about the step, see
494 recipe_modules/step/config.py. 490 recipe_modules/step/config.py.
495 491
496 Returns: 492 Returns:
497 Opaque engine specific object that is understood by 'run_steps' method. 493 Opaque engine specific object that is understood by 'run_steps' method.
498 """ 494 """
499 raise NotImplementedError 495 raise NotImplementedError
500 496
501 497
502 def _merge(*dicts):
503 result = {}
504 for d in dicts:
505 result.update(d)
506 return result
507
508
509 class SequentialRecipeEngine(RecipeEngine): 498 class SequentialRecipeEngine(RecipeEngine):
510 """Always runs step sequentially. Currently the engine used by default.""" 499 """Always runs step sequentially. Currently the engine used by default."""
511 def __init__(self, stream, properties, test_data): 500 def __init__(self, stream, properties, test_data):
512 super(SequentialRecipeEngine, self).__init__() 501 super(SequentialRecipeEngine, self).__init__()
513 self._stream = stream 502 self._stream = stream
514 self._properties = properties 503 self._properties = properties
515 self._test_data = test_data 504 self._test_data = test_data
516 self._step_results = collections.OrderedDict() 505 self._step_history = collections.OrderedDict()
517 self._step_disambiguation_index = {}
518 506
519 self._annotation = None 507 self._previous_step_annotation = None
520 self._step_result = None 508 self._previous_step_result = None
521 self._api = None 509 self._api = None
522 510
523 @property 511 @property
524 def properties(self): 512 def properties(self):
525 return self._properties 513 return self._properties
526 514
527 @property 515 @property
528 def previous_step_result(self): 516 def previous_step_result(self):
529 """Allows api.step to get the active result from any context.""" 517 """Allows api.step to get the active result from any context."""
530 return self._step_result 518 return self._previous_step_result
531 519
532 def _emit_results(self): 520 def _emit_results(self):
533 annotation = self._annotation 521 annotation = self._previous_step_annotation
534 step_result = self._step_result 522 step_result = self._previous_step_result
535 523
536 self._annotation = None 524 self._previous_step_annotation = None
537 self._step_result = None 525 self._previous_step_result = None
538 526
539 if not annotation or not step_result: 527 if not annotation or not step_result:
540 return 528 return
541 529
542 step_result.presentation.finalize(annotation) 530 step_result.presentation.finalize(annotation)
543 if self._test_data.enabled: 531 if self._test_data.enabled:
544 val = annotation.stream.getvalue() 532 val = annotation.stream.getvalue()
545 lines = filter(None, val.splitlines()) 533 lines = filter(None, val.splitlines())
546 if lines: 534 if lines:
547 # note that '~' sorts after 'z' so that this will be last on each 535 # note that '~' sorts after 'z' so that this will be last on each
548 # step. also use _step to get access to the mutable step 536 # step. also use _step to get access to the mutable step
549 # dictionary. 537 # dictionary.
550 # pylint: disable=w0212 538 # pylint: disable=w0212
551 step_result._step['~followup_annotations'] = lines 539 step_result._step['~followup_annotations'] = lines
552 annotation.step_ended() 540 annotation.step_ended()
553 541
554 def _disambiguate_name(self, step_name): 542 def run_step(self, step):
555 if step_name in self._step_disambiguation_index: 543 ok_ret = step.pop('ok_ret')
556 self._step_disambiguation_index[step_name] += 1 544 infra_step = step.pop('infra_step')
557 step_name += ' (%s)' % self._step_disambiguation_index[step_name] 545
546 test_data_fn = step.pop('step_test_data', recipe_test_api.StepTestData)
547 step_test = self._test_data.pop_step_test_data(step['name'],
548 test_data_fn)
549 placeholders = render_step(step, step_test)
550
551 self._step_history[step['name']] = step
552 self._emit_results()
553
554 step_result = None
555
556 if not self._test_data.enabled:
557 self._previous_step_annotation, retcode = annotator.run_step(
558 self._stream, **step)
559
560 step_result = StepData(step, retcode)
561 self._previous_step_annotation.annotation_stream.step_cursor(step['name'])
558 else: 562 else:
559 self._step_disambiguation_index[step_name] = 1 563 self._previous_step_annotation = annotation = self._stream.step(
560 return step_name 564 step['name'])
565 annotation.step_started()
566 try:
567 annotation.stream = cStringIO.StringIO()
561 568
562 def _disambiguate_step(self, step): 569 step_result = StepData(step, step_test.retcode)
563 """Disambiguates step (destructively) by adding an index afterward. E.g.
564
565 gclient sync
566 gclient sync (2)
567 ...
568 """
569 step['name'] = self._disambiguate_name(step['name'])
570
571 def _subannotator(self):
572 class Subannotator(object):
573 # We use ann as the self argument because we are closing over the
574 # SequentialRecipeEngine self.
575 # pylint: disable=e0213
576 def BUILD_STEP(ann, name):
577 self._open_step({'name': self._disambiguate_name(name)})
578
579 def STEP_WARNINGS(ann):
580 self._step_result.presentation.status = 'WARNING'
581 def STEP_FAILURE(ann):
582 self._step_result.presentation.status = 'FAILURE'
583 def STEP_EXCEPTION(ann):
584 self._step_result.presentation.status = 'EXCEPTION'
585
586 def STEP_TEXT(ann, msg):
587 self._step_result.presentation.step_text = msg
588
589 def STEP_LINK(ann, link_label, link_url):
590 self._step_result.presentation.links[link_label] = link_url
591
592 def STEP_LOG_LINE(ann, log_label, log_line):
593 self._step_result.presentation.logs[log_label] += log_line
594 def STEP_LOG_END(ann, log_label):
595 # We do step finalization all at once.
596 pass
597
598 def SET_BUILD_PROPERTY(ann, name, value):
599 self._step_result.presentation.properties[name] = value
600
601 def STEP_SUMMARY_TEXT(ann, msg):
602 self._step_result.presentation.step_summary_text = msg
603
604 return Subannotator()
605
606 def _open_step(self, step):
607 self._emit_results()
608 step_result = StepData(step, None)
609 self._step_results[step['name']] = step_result
610 self._step_result = step_result
611 self._annotation = self._stream.step(step['name'])
612 self._annotation.step_started()
613 if self._test_data.enabled:
614 self._annotation.stream = cStringIO.StringIO()
615
616 def _step_kernel(self, step, step_test, subannotator=None):
617 if not self._test_data.enabled:
618 # Warning: run_step can change the current self._annotation and
619 # self._step_result if it uses a subannotator.
620 retcode = annotator.run_step(
621 self._stream,
622 step_annotation=self._annotation,
623 subannotator=subannotator,
624 **step)
625 self._step_result.retcode = retcode
626 # TODO(luqui): What is the purpose of this line?
627 self._annotation.annotation_stream.step_cursor(
628 self._step_result.step['name'])
629 else:
630 try:
631 self._step_result.retcode = step_test.retcode
632 except OSError: 570 except OSError:
633 exc_type, exc_value, exc_tb = sys.exc_info() 571 exc_type, exc_value, exc_tb = sys.exc_info()
634 trace = traceback.format_exception(exc_type, exc_value, exc_tb) 572 trace = traceback.format_exception(exc_type, exc_value, exc_tb)
635 trace_lines = ''.join(trace).split('\n') 573 trace_lines = ''.join(trace).split('\n')
636 self._annotation.write_log_lines( 574 annotation.write_log_lines('exception', filter(None, trace_lines))
637 'exception', filter(None, trace_lines)) 575 annotation.step_exception()
638 self._annotation.step_exception()
639 576
640 return self._step_result.retcode 577 get_placeholder_results(step_result, placeholders)
578 self._previous_step_result = step_result
641 579
642 def run_step(self, step): 580 if step_result.retcode in ok_ret:
643 self._disambiguate_step(step) 581 step_result.presentation.status = 'SUCCESS'
644 ok_ret = step.pop('ok_ret') 582 return step_result
645 infra_step = step.pop('infra_step')
646 allow_subannotations = step.get('allow_subannotations', False)
647
648 test_data_fn = step.pop('step_test_data', recipe_test_api.StepTestData)
649 step_test = self._test_data.pop_step_test_data(step['name'], test_data_fn)
650 placeholders = render_step(step, step_test)
651
652 if allow_subannotations:
653 # TODO(luqui) Make this hierarchical.
654 self._open_step(step)
655 start_annotation = self._annotation
656 retcode = self._step_kernel(step, step_test,
657 subannotator=self._subannotator())
658
659 # Open a closing step for presentation modifications.
660 if self._annotation != start_annotation:
661 self._open_step({ 'name': step['name'] + ' (end)' })
662 self._step_result.retcode = retcode
663 else:
664 self._open_step(step)
665 self._step_kernel(step, step_test)
666
667 get_placeholder_results(self._step_result, placeholders)
668
669 if self._step_result.retcode in ok_ret:
670 self._step_result.presentation.status = 'SUCCESS'
671 return self._step_result
672 else: 583 else:
673 if not infra_step: 584 if not infra_step:
674 state = 'FAILURE' 585 state = 'FAILURE'
675 exc = recipe_api.StepFailure 586 exc = recipe_api.StepFailure
676 else: 587 else:
677 state = 'EXCEPTION' 588 state = 'EXCEPTION'
678 exc = recipe_api.InfraFailure 589 exc = recipe_api.InfraFailure
679 590
680 self._step_result.presentation.status = state 591 step_result.presentation.status = state
681 if step_test.enabled: 592 if step_test.enabled:
682 # To avoid cluttering the expectations, don't emit this in testmode. 593 # To avoid cluttering the expectations, don't emit this in testmode.
683 self._annotation.emit( 594 self._previous_step_annotation.emit(
684 'step returned non-zero exit code: %d' % self._step_result.retcode) 595 'step returned non-zero exit code: %d' % step_result.retcode)
685 596
686 raise exc(step['name'], self._step_result) 597 raise exc(step['name'], step_result)
687 598
688 599
689 def run(self, steps_function, api): 600 def run(self, steps_function, api):
690 self._api = api 601 self._api = api
691 retcode = None 602 retcode = None
692 final_result = None 603 final_result = None
693 604
694 try: 605 try:
695 try: 606 try:
696 retcode = steps_function(api) 607 retcode = steps_function(api)
697 assert retcode is None, ( 608 assert retcode is None, (
698 'Non-None return from GenSteps is not supported yet') 609 "Non-None return from GenSteps is not supported yet")
699 610
700 assert not self._test_data.enabled or not self._test_data.step_data, ( 611 assert not self._test_data.enabled or not self._test_data.step_data, (
701 'Unconsumed test data! %s' % (self._test_data.step_data,)) 612 "Unconsumed test data! %s" % (self._test_data.step_data,))
702 finally: 613 finally:
703 self._emit_results() 614 self._emit_results()
704 except recipe_api.StepFailure as f: 615 except recipe_api.StepFailure as f:
705 retcode = f.retcode or 1 616 retcode = f.retcode or 1
706 final_result = { 617 final_result = {
707 'name': '$final_result', 618 "name": "$final_result",
708 'reason': f.reason, 619 "reason": f.reason,
709 'status_code': retcode 620 "status_code": retcode
710 } 621 }
711 622
712 except Exception as ex: 623 except Exception as ex:
713 unexpected_exception = self._test_data.is_unexpected_exception(ex) 624 unexpected_exception = self._test_data.is_unexpected_exception(ex)
714 625
715 retcode = -1 626 retcode = -1
716 final_result = { 627 final_result = {
717 'name': '$final_result', 628 "name": "$final_result",
718 'reason': 'Uncaught Exception: %r' % ex, 629 "reason": "Uncaught Exception: %r" % ex,
719 'status_code': retcode 630 "status_code": retcode
720 } 631 }
721 632
722 with self._stream.step('Uncaught Exception') as s: 633 with self._stream.step('Uncaught Exception') as s:
723 s.step_exception() 634 s.step_exception()
724 s.write_log_lines('exception', traceback.format_exc().splitlines()) 635 s.write_log_lines('exception', traceback.format_exc().splitlines())
725 636
726 if unexpected_exception: 637 if unexpected_exception:
727 raise 638 raise
728 639
729 if final_result is not None: 640 if final_result is not None:
730 self._step_results[final_result['name']] = ( 641 self._step_history[final_result['name']] = final_result
731 StepData(final_result, final_result['status_code']))
732 642
733 return RecipeExecutionResult(retcode, self._step_results) 643 return RecipeExecutionResult(retcode, self._step_history)
734 644
735 def create_step(self, step): # pylint: disable=R0201 645 def create_step(self, step): # pylint: disable=R0201
736 # This version of engine doesn't do anything, just converts step to dict 646 # This version of engine doesn't do anything, just converts step to dict
737 # (that is consumed by annotator engine). 647 # (that is consumed by annotator engine).
738 return step.as_jsonish() 648 return step.as_jsonish()
739 649
740 650
741 class ParallelRecipeEngine(RecipeEngine): 651 class ParallelRecipeEngine(RecipeEngine):
742 """New engine that knows how to run steps in parallel. 652 """New engine that knows how to run steps in parallel.
743 653
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
795 705
796 def shell_main(argv): 706 def shell_main(argv):
797 if update_scripts(): 707 if update_scripts():
798 return subprocess.call([sys.executable] + argv) 708 return subprocess.call([sys.executable] + argv)
799 else: 709 else:
800 return main(argv) 710 return main(argv)
801 711
802 712
803 if __name__ == '__main__': 713 if __name__ == '__main__':
804 sys.exit(shell_main(sys.argv)) 714 sys.exit(shell_main(sys.argv))
OLDNEW
« no previous file with comments | « scripts/common/annotator.py ('k') | scripts/slave/recipe_modules/step/api.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698