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

Side by Side Diff: scripts/common/annotator.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 | « no previous file | scripts/slave/annotated_run.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 """Contains generating and parsing systems of the Chromium Buildbot Annotator. 6 """Contains generating and parsing systems of the Chromium Buildbot Annotator.
7 7
8 When executed as a script, this reads step name / command pairs from a file and 8 When executed as a script, this reads step name / command pairs from a file and
9 executes those lines while annotating the output. The input is json: 9 executes those lines while annotating the output. The input is json:
10 10
(...skipping 446 matching lines...) Expand 10 before | Expand all | Expand 10 after
457 457
458 step.step_trigger(json.dumps({ 458 step.step_trigger(json.dumps({
459 'builderNames': [builder_name], 459 'builderNames': [builder_name],
460 'bucket': trig.get('bucket'), 460 'bucket': trig.get('bucket'),
461 'changes': changes, 461 'changes': changes,
462 'properties': trig.get('properties'), 462 'properties': trig.get('properties'),
463 }, sort_keys=True)) 463 }, sort_keys=True))
464 464
465 465
466 def run_step(stream, name, cmd, 466 def run_step(stream, name, cmd,
467 step_annotation,
468 cwd=None, env=None, 467 cwd=None, env=None,
469 subannotator=None, 468 allow_subannotations=False,
470 trigger_specs=None, 469 trigger_specs=None,
471 **kwargs): 470 **kwargs):
472 """Runs a single step. 471 """Runs a single step.
473 472
474 Context: 473 Context:
475 stream: StructuredAnnotationStream to use to emit step 474 stream: StructuredAnnotationStream to use to emit step
476 step_annotation: optional StructuredAnnotationStep to use instead
477 of creating one
478 475
479 Step parameters: 476 Step parameters:
480 name: name of the step, will appear in buildbots waterfall 477 name: name of the step, will appear in buildbots waterfall
481 cmd: command to run, list of one or more strings 478 cmd: command to run, list of one or more strings
482 cwd: absolute path to working directory for the command 479 cwd: absolute path to working directory for the command
483 env: dict with overrides for environment variables 480 env: dict with overrides for environment variables
484 subannotator: a callback_implementor class used to parse subannotations 481 allow_subannotations: if True, lets the step emit its own annotations
485 that the command emits; if None, subannotations will be suppressed.
486 trigger_specs: a list of trigger specifications, which are dict with keys: 482 trigger_specs: a list of trigger specifications, which are dict with keys:
487 properties: a dict of properties. 483 properties: a dict of properties.
488 Buildbot requires buildername property. 484 Buildbot requires buildername property.
489 485
490 Known kwargs: 486 Known kwargs:
491 stdout: Path to a file to put step stdout into. If used, stdout won't appear 487 stdout: Path to a file to put step stdout into. If used, stdout won't appear
492 in annotator's stdout (and |allow_subannotations| is ignored). 488 in annotator's stdout (and |allow_subannotations| is ignored).
493 stderr: Path to a file to put step stderr into. If used, stderr won't appear 489 stderr: Path to a file to put step stderr into. If used, stderr won't appear
494 in annotator's stderr. 490 in annotator's stderr.
495 stdin: Path to a file to read step stdin from. 491 stdin: Path to a file to read step stdin from.
496 492
497 Returns the returncode of the step. 493 Returns the returncode of the step.
498 """ 494 """
499 if isinstance(cmd, basestring): 495 if isinstance(cmd, basestring):
500 cmd = (cmd,) 496 cmd = (cmd,)
501 cmd = map(str, cmd) 497 cmd = map(str, cmd)
502 498
503 # For error reporting. 499 # For error reporting.
504 step_dict = kwargs.copy() 500 step_dict = kwargs.copy()
505 step_dict.update({ 501 step_dict.update({
506 'name': name, 502 'name': name,
507 'cmd': cmd, 503 'cmd': cmd,
508 'cwd': cwd, 504 'cwd': cwd,
509 'env': env, 505 'env': env,
510 'allow_subannotations': subannotator is not None, 506 'allow_subannotations': allow_subannotations,
511 }) 507 })
512 step_env = _merge_envs(os.environ, env) 508 step_env = _merge_envs(os.environ, env)
513 509
510 step_annotation = stream.step(name)
511 step_annotation.step_started()
512
514 print_step(step_dict, step_env, stream) 513 print_step(step_dict, step_env, stream)
515 returncode = 0 514 returncode = 0
516 if cmd: 515 if cmd:
517 try: 516 try:
518 # Open file handles for IO redirection based on file names in step_dict. 517 # Open file handles for IO redirection based on file names in step_dict.
519 fhandles = { 518 fhandles = {
520 'stdout': subprocess.PIPE, 519 'stdout': subprocess.PIPE,
521 'stderr': subprocess.PIPE, 520 'stderr': subprocess.PIPE,
522 'stdin': None, 521 'stdin': None,
523 } 522 }
(...skipping 27 matching lines...) Expand all
551 universal_newlines=True, 550 universal_newlines=True,
552 creationflags=creationflags, 551 creationflags=creationflags,
553 **fhandles) 552 **fhandles)
554 553
555 # Safe to close file handles now that subprocess has inherited them. 554 # Safe to close file handles now that subprocess has inherited them.
556 for handle in fhandles.itervalues(): 555 for handle in fhandles.itervalues():
557 if isinstance(handle, file): 556 if isinstance(handle, file):
558 handle.close() 557 handle.close()
559 558
560 outlock = threading.Lock() 559 outlock = threading.Lock()
561 def filter_lines(inhandle, outhandle): 560 def filter_lines(lock, allow_subannotations, inhandle, outhandle):
562 while True: 561 while True:
563 line = inhandle.readline() 562 line = inhandle.readline()
564 if not line: 563 if not line:
565 break 564 break
566 with outlock: 565 lock.acquire()
567 if line.startswith('@@@'): 566 try:
568 if subannotator: 567 if not allow_subannotations and line.startswith('@@@'):
569 # The subannotator might write to the handle, thus the lock. 568 outhandle.write('!')
570 MatchAnnotation(line.strip(), subannotator) 569 outhandle.write(line)
571 else:
572 outhandle.write('!')
573 outhandle.write(line)
574 else:
575 outhandle.write(line)
576 outhandle.flush() 570 outhandle.flush()
571 finally:
572 lock.release()
577 573
578 # Pump piped stdio through filter_lines. IO going to files on disk is 574 # Pump piped stdio through filter_lines. IO going to files on disk is
579 # not filtered. 575 # not filtered.
580 threads = [] 576 threads = []
581 for key in ('stdout', 'stderr'): 577 for key in ('stdout', 'stderr'):
582 if fhandles[key] == subprocess.PIPE: 578 if fhandles[key] == subprocess.PIPE:
583 inhandle = getattr(proc, key) 579 inhandle = getattr(proc, key)
584 outhandle = getattr(sys, key) 580 outhandle = getattr(sys, key)
585 threads.append(threading.Thread( 581 threads.append(threading.Thread(
586 target=filter_lines, 582 target=filter_lines,
587 args=(inhandle, outhandle))) 583 args=(outlock, allow_subannotations, inhandle, outhandle)))
588 584
589 for th in threads: 585 for th in threads:
590 th.start() 586 th.start()
591 proc.wait() 587 proc.wait()
592 for th in threads: 588 for th in threads:
593 th.join() 589 th.join()
594 returncode = proc.returncode 590 returncode = proc.returncode
595 except OSError: 591 except OSError:
596 # File wasn't found, error will be reported to stream when the exception 592 # File wasn't found, error will be reported to stream when the exception
597 # crosses the context manager. 593 # crosses the context manager.
598 step_annotation.step_exception_occured(*sys.exc_info()) 594 step_annotation.step_exception_occured(*sys.exc_info())
599 raise 595 raise
600 596
601 # TODO(martiniss) move logic into own module? 597 # TODO(martiniss) move logic into own module?
602 if trigger_specs: 598 if trigger_specs:
603 triggerBuilds(step_annotation, trigger_specs) 599 triggerBuilds(step_annotation, trigger_specs)
604 600
605 return returncode 601 return step_annotation, returncode
606 602
607 def update_build_failure(failure, retcode, **_kwargs): 603 def update_build_failure(failure, retcode, **_kwargs):
608 """Potentially moves failure from False to True, depending on returncode of 604 """Potentially moves failure from False to True, depending on returncode of
609 the run step and the step's configuration. 605 the run step and the step's configuration.
610 606
611 can_fail_build: A boolean indicating that a bad retcode for this step should 607 can_fail_build: A boolean indicating that a bad retcode for this step should
612 be intepreted as a build failure. 608 be intepreted as a build failure.
613 609
614 Returns new value for failure. 610 Returns new value for failure.
615 611
(...skipping 10 matching lines...) Expand all
626 sys.exit(1) 622 sys.exit(1)
627 623
628 stream = StructuredAnnotationStream() 624 stream = StructuredAnnotationStream()
629 ret_codes = [] 625 ret_codes = []
630 build_failure = False 626 build_failure = False
631 prev_annotation = None 627 prev_annotation = None
632 for step in steps: 628 for step in steps:
633 if build_failure and not step.get('always_run', False): 629 if build_failure and not step.get('always_run', False):
634 ret = None 630 ret = None
635 else: 631 else:
636 prev_annotation = stream.step(step['name']) 632 prev_annotation, ret = run_step(stream, **step)
637 prev_annotation.step_started()
638 ret = run_step(stream, step_annotation=prev_annotation, **step)
639 stream = prev_annotation.annotation_stream 633 stream = prev_annotation.annotation_stream
640 if ret > 0: 634 if ret > 0:
641 stream.step_cursor(stream.current_step) 635 stream.step_cursor(stream.current_step)
642 stream.emit('step returned non-zero exit code: %d' % ret) 636 stream.emit('step returned non-zero exit code: %d' % ret)
643 prev_annotation.step_failure() 637 prev_annotation.step_failure()
644 638
645 prev_annotation.step_ended() 639 prev_annotation.step_ended()
646 build_failure = update_build_failure(build_failure, ret) 640 build_failure = update_build_failure(build_failure, ret)
647 ret_codes.append(ret) 641 ret_codes.append(ret)
648 if prev_annotation: 642 if prev_annotation:
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
689 steps.extend(json.load(sys.stdin, object_hook=force_dict_strs)) 683 steps.extend(json.load(sys.stdin, object_hook=force_dict_strs))
690 else: 684 else:
691 with open(args[0], 'rb') as f: 685 with open(args[0], 'rb') as f:
692 steps.extend(json.load(f, object_hook=force_dict_strs)) 686 steps.extend(json.load(f, object_hook=force_dict_strs))
693 687
694 return 1 if run_steps(steps, False)[0] else 0 688 return 1 if run_steps(steps, False)[0] else 0
695 689
696 690
697 if __name__ == '__main__': 691 if __name__ == '__main__':
698 sys.exit(main()) 692 sys.exit(main())
OLDNEW
« no previous file with comments | « no previous file | scripts/slave/annotated_run.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698