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

Side by Side Diff: scripts/common/annotator.py

Issue 1001183002: Make allow_subannotations more robust. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/build
Patch Set: Implementation of subannotations (with engine refactor) Created 5 years, 9 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') | scripts/slave/annotated_run.py » ('J')
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 445 matching lines...) Expand 10 before | Expand all | Expand 10 after
456 changes = map(normalizeChange, changes) 456 changes = map(normalizeChange, changes)
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,
luqui 2015/03/23 23:26:32 This function has no business being in this file.
luqui 2015/03/27 22:46:58 Not actually true, it's called by run_steps, which
467 step_annotation,
467 cwd=None, env=None, 468 cwd=None, env=None,
468 allow_subannotations=False, 469 subannotator=None,
469 trigger_specs=None, 470 trigger_specs=None,
470 **kwargs): 471 **kwargs):
471 """Runs a single step. 472 """Runs a single step.
472 473
473 Context: 474 Context:
474 stream: StructuredAnnotationStream to use to emit step 475 stream: StructuredAnnotationStream to use to emit step
476 step_annotation: optional StructuredAnnotationStep to use instead
477 of creating one
475 478
476 Step parameters: 479 Step parameters:
477 name: name of the step, will appear in buildbots waterfall 480 name: name of the step, will appear in buildbots waterfall
478 cmd: command to run, list of one or more strings 481 cmd: command to run, list of one or more strings
479 cwd: absolute path to working directory for the command 482 cwd: absolute path to working directory for the command
480 env: dict with overrides for environment variables 483 env: dict with overrides for environment variables
481 allow_subannotations: if True, lets the step emit its own annotations 484 subannotator: a callback_implementor class used to parse subannotations
485 that the command emits; if None, subannotations will be suppressed.
482 trigger_specs: a list of trigger specifications, which are dict with keys: 486 trigger_specs: a list of trigger specifications, which are dict with keys:
483 properties: a dict of properties. 487 properties: a dict of properties.
484 Buildbot requires buildername property. 488 Buildbot requires buildername property.
485 489
486 Known kwargs: 490 Known kwargs:
487 stdout: Path to a file to put step stdout into. If used, stdout won't appear 491 stdout: Path to a file to put step stdout into. If used, stdout won't appear
488 in annotator's stdout (and |allow_subannotations| is ignored). 492 in annotator's stdout (and |allow_subannotations| is ignored).
489 stderr: Path to a file to put step stderr into. If used, stderr won't appear 493 stderr: Path to a file to put step stderr into. If used, stderr won't appear
490 in annotator's stderr. 494 in annotator's stderr.
491 stdin: Path to a file to read step stdin from. 495 stdin: Path to a file to read step stdin from.
492 496
493 Returns the returncode of the step. 497 Returns the returncode of the step.
494 """ 498 """
495 if isinstance(cmd, basestring): 499 if isinstance(cmd, basestring):
496 cmd = (cmd,) 500 cmd = (cmd,)
497 cmd = map(str, cmd) 501 cmd = map(str, cmd)
498 502
499 # For error reporting. 503 # For error reporting.
500 step_dict = kwargs.copy() 504 step_dict = kwargs.copy()
501 step_dict.update({ 505 step_dict.update({
502 'name': name, 506 'name': name,
503 'cmd': cmd, 507 'cmd': cmd,
504 'cwd': cwd, 508 'cwd': cwd,
505 'env': env, 509 'env': env,
506 'allow_subannotations': allow_subannotations, 510 'allow_subannotations': subannotator is not None,
507 }) 511 })
508 step_env = _merge_envs(os.environ, env) 512 step_env = _merge_envs(os.environ, env)
509 513
510 step_annotation = stream.step(name)
511 step_annotation.step_started()
512
513 print_step(step_dict, step_env, stream) 514 print_step(step_dict, step_env, stream)
514 returncode = 0 515 returncode = 0
515 if cmd: 516 if cmd:
516 try: 517 try:
517 # Open file handles for IO redirection based on file names in step_dict. 518 # Open file handles for IO redirection based on file names in step_dict.
518 fhandles = { 519 fhandles = {
519 'stdout': subprocess.PIPE, 520 'stdout': subprocess.PIPE,
520 'stderr': subprocess.PIPE, 521 'stderr': subprocess.PIPE,
521 'stdin': None, 522 'stdin': None,
522 } 523 }
523 for key in fhandles: 524 for key in fhandles:
524 if key in step_dict: 525 if key in step_dict:
525 fhandles[key] = open(step_dict[key], 526 fhandles[key] = open(step_dict[key],
526 'rb' if key == 'stdin' else 'wb') 527 'rb' if key == 'stdin' else 'wb')
527 528
528 with modify_lookup_path(step_env.get('PATH')): 529 with modify_lookup_path(step_env.get('PATH')):
529 proc = subprocess.Popen( 530 proc = subprocess.Popen(
530 cmd, 531 cmd,
531 env=step_env, 532 env=step_env,
532 cwd=cwd, 533 cwd=cwd,
533 universal_newlines=True, 534 universal_newlines=True,
534 **fhandles) 535 **fhandles)
535 536
536 # Safe to close file handles now that subprocess has inherited them. 537 # Safe to close file handles now that subprocess has inherited them.
537 for handle in fhandles.itervalues(): 538 for handle in fhandles.itervalues():
538 if isinstance(handle, file): 539 if isinstance(handle, file):
539 handle.close() 540 handle.close()
540 541
541 outlock = threading.Lock() 542 outlock = threading.Lock()
542 def filter_lines(lock, allow_subannotations, inhandle, outhandle): 543 def filter_lines(inhandle, outhandle):
543 while True: 544 while True:
544 line = inhandle.readline() 545 line = inhandle.readline()
545 if not line: 546 if not line:
546 break 547 break
547 lock.acquire() 548 with outlock:
548 try: 549 if line.startswith('@@@'):
549 if not allow_subannotations and line.startswith('@@@'): 550 if subannotator:
550 outhandle.write('!') 551 # The subannotator might write to the handle, thus the lock.
551 outhandle.write(line) 552 MatchAnnotation(line.strip(), subannotator)
553 else:
554 outhandle.write('!')
555 outhandle.write(line)
556 else:
557 outhandle.write(line)
552 outhandle.flush() 558 outhandle.flush()
553 finally:
554 lock.release()
555 559
556 # Pump piped stdio through filter_lines. IO going to files on disk is 560 # Pump piped stdio through filter_lines. IO going to files on disk is
557 # not filtered. 561 # not filtered.
558 threads = [] 562 threads = []
559 for key in ('stdout', 'stderr'): 563 for key in ('stdout', 'stderr'):
560 if fhandles[key] == subprocess.PIPE: 564 if fhandles[key] == subprocess.PIPE:
561 inhandle = getattr(proc, key) 565 inhandle = getattr(proc, key)
562 outhandle = getattr(sys, key) 566 outhandle = getattr(sys, key)
563 threads.append(threading.Thread( 567 threads.append(threading.Thread(
564 target=filter_lines, 568 target=filter_lines,
565 args=(outlock, allow_subannotations, inhandle, outhandle))) 569 args=(inhandle, outhandle)))
566 570
567 for th in threads: 571 for th in threads:
568 th.start() 572 th.start()
569 proc.wait() 573 proc.wait()
570 for th in threads: 574 for th in threads:
571 th.join() 575 th.join()
572 returncode = proc.returncode 576 returncode = proc.returncode
573 except OSError: 577 except OSError:
574 # File wasn't found, error will be reported to stream when the exception 578 # File wasn't found, error will be reported to stream when the exception
575 # crosses the context manager. 579 # crosses the context manager.
576 step_annotation.step_exception_occured(*sys.exc_info()) 580 step_annotation.step_exception_occured(*sys.exc_info())
577 raise 581 raise
578 582
579 # TODO(martiniss) move logic into own module? 583 # TODO(martiniss) move logic into own module?
580 if trigger_specs: 584 if trigger_specs:
581 triggerBuilds(step_annotation, trigger_specs) 585 triggerBuilds(step_annotation, trigger_specs)
582 586
583 return step_annotation, returncode 587 return returncode
584 588
585 def update_build_failure(failure, retcode, **_kwargs): 589 def update_build_failure(failure, retcode, **_kwargs):
586 """Potentially moves failure from False to True, depending on returncode of 590 """Potentially moves failure from False to True, depending on returncode of
587 the run step and the step's configuration. 591 the run step and the step's configuration.
588 592
589 can_fail_build: A boolean indicating that a bad retcode for this step should 593 can_fail_build: A boolean indicating that a bad retcode for this step should
590 be intepreted as a build failure. 594 be intepreted as a build failure.
591 595
592 Returns new value for failure. 596 Returns new value for failure.
593 597
(...skipping 10 matching lines...) Expand all
604 sys.exit(1) 608 sys.exit(1)
605 609
606 stream = StructuredAnnotationStream() 610 stream = StructuredAnnotationStream()
607 ret_codes = [] 611 ret_codes = []
608 build_failure = False 612 build_failure = False
609 prev_annotation = None 613 prev_annotation = None
610 for step in steps: 614 for step in steps:
611 if build_failure and not step.get('always_run', False): 615 if build_failure and not step.get('always_run', False):
612 ret = None 616 ret = None
613 else: 617 else:
614 prev_annotation, ret = run_step(stream, **step) 618 prev_annotation = stream.step(step['name'])
619 ret = run_step(stream, **step)
615 stream = prev_annotation.annotation_stream 620 stream = prev_annotation.annotation_stream
616 if ret > 0: 621 if ret > 0:
617 stream.step_cursor(stream.current_step) 622 stream.step_cursor(stream.current_step)
618 stream.emit('step returned non-zero exit code: %d' % ret) 623 stream.emit('step returned non-zero exit code: %d' % ret)
619 prev_annotation.step_failure() 624 prev_annotation.step_failure()
620 625
621 prev_annotation.step_ended() 626 prev_annotation.step_ended()
622 build_failure = update_build_failure(build_failure, ret) 627 build_failure = update_build_failure(build_failure, ret)
623 ret_codes.append(ret) 628 ret_codes.append(ret)
624 if prev_annotation: 629 if prev_annotation:
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
665 steps.extend(json.load(sys.stdin, object_hook=force_dict_strs)) 670 steps.extend(json.load(sys.stdin, object_hook=force_dict_strs))
666 else: 671 else:
667 with open(args[0], 'rb') as f: 672 with open(args[0], 'rb') as f:
668 steps.extend(json.load(f, object_hook=force_dict_strs)) 673 steps.extend(json.load(f, object_hook=force_dict_strs))
669 674
670 return 1 if run_steps(steps, False)[0] else 0 675 return 1 if run_steps(steps, False)[0] else 0
671 676
672 677
673 if __name__ == '__main__': 678 if __name__ == '__main__':
674 sys.exit(main()) 679 sys.exit(main())
OLDNEW
« no previous file with comments | « no previous file | scripts/slave/annotated_run.py » ('j') | scripts/slave/annotated_run.py » ('J')

Powered by Google App Engine
This is Rietveld 408576698