From 74da24cb61ef4478ca499c029a732cdba13ee668 Mon Sep 17 00:00:00 2001 From: Chris Johns Date: Thu, 8 May 2014 02:58:14 +0000 Subject: [PATCH] sb: Generate an error report on an error. Generate an error report users can send to the mailing list with error details. --- source-builder/sb/build.py | 34 ++++++++++++++------- source-builder/sb/ereport.py | 53 +++++++++++++++++++++++++++++++++ source-builder/sb/freebsd.py | 2 +- source-builder/sb/git.py | 4 +-- source-builder/sb/log.py | 9 +++++- source-builder/sb/options.py | 14 +++++++-- source-builder/sb/setbuilder.py | 26 ++++++++++++---- 7 files changed, 120 insertions(+), 22 deletions(-) create mode 100755 source-builder/sb/ereport.py diff --git a/source-builder/sb/build.py b/source-builder/sb/build.py index c9ff07a..eb39349 100644 --- a/source-builder/sb/build.py +++ b/source-builder/sb/build.py @@ -36,6 +36,7 @@ try: import config import download import error + import ereport import execute import log import options @@ -262,7 +263,8 @@ class build: log.output('run: ' + cmd) exit_code, proc, output = e.shell(cmd, cwd = path.host(cwd)) if exit_code != 0: - raise error.general('shell cmd failed: %s' % (cmd)) + log.output('shell cmd failed: %s' % (cmd)) + raise error.general('building %s' % (self.macros['buildname'])) def builddir(self): builddir = self.config.abspath('_builddir') @@ -431,6 +433,10 @@ def find_config(config, configs): return None def run(args): + ec = 0 + opts = None + b = None + erheader = None try: optargs = { '--list-configs': 'List available configurations' } opts = options.load(args, optargs) @@ -451,21 +457,29 @@ def run(args): for config_file in opts.config_files(): b = build(config_file, True, opts) b.make() - del b + b = None except error.general, gerr: - print gerr - print >> sys.stderr, 'Build FAILED' - sys.exit(1) + erheader = 'Build: %s' % (gerr) + log.notice(str(gerr)) + log.stderr('Build FAILED') + ec = 1 except error.internal, ierr: - print ierr - print >> sys.stderr, 'Build FAILED' - sys.exit(1) + erheader = 'Build: %s' % (ierr) + log.notice(str(ierr)) + log.stderr('Internal Build FAILED') + ec = 1 except error.exit, eerr: pass except KeyboardInterrupt: log.notice('abort: user terminated') - sys.exit(1) - sys.exit(0) + ec = 1 + if (ec != 0 and erheader and opts and b) or (opts and opts.dry_run()): + if opts.dry_run(): + bname = 'dry-run' + else: + bname = b.name() + ereport.generate('rsb-report-%s.txt' % (bname), opts, erheader) + sys.exit(ec) if __name__ == "__main__": run(sys.argv) diff --git a/source-builder/sb/ereport.py b/source-builder/sb/ereport.py new file mode 100755 index 0000000..897b353 --- /dev/null +++ b/source-builder/sb/ereport.py @@ -0,0 +1,53 @@ +# +# RTEMS Tools Project (http://www.rtems.org/) +# Copyright 2010-2014 Chris Johns (chrisj@rtems.org) +# All rights reserved. +# +# This file is part of the RTEMS Tools package in 'rtems-testing'. +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +# +# Create an error log. +# + +import os + +import error +import log + +def generate(name, opts, header = None, footer = None): + r = ['RTEMS Tools Project - Source Builder Error Report'] + [] + if header: + r += [' %s' % (header)] + r += [opts.info()] + if opts.defaults.get_value('%{_sbgit_valid}') == '1': + r += [' %s/%s' % (opts.defaults.get_value('%{_sbgit_remotes}'), + opts.defaults.get_value('%{_sbgit_id}'))] + else: + r += [' RSB: not a valid repo'] + r += [' %s' % (' '.join(os.uname()))] + r += [] + r += ['Tail of the build log:'] + r += log.tail() + if footer: + r += [footer] + try: + name = name.replace('/', '-') + l = open(name, 'w') + l.write(os.linesep.join(r)) + l.close() + log.notice(' See error report: %s' % (name)) + except: + log.stderr('error: failure to create error report') + raise diff --git a/source-builder/sb/freebsd.py b/source-builder/sb/freebsd.py index bc01be8..13589b7 100644 --- a/source-builder/sb/freebsd.py +++ b/source-builder/sb/freebsd.py @@ -83,7 +83,7 @@ def load(): cxx = '/usr/bin/c++' if check.check_exe(cxx, cxx): defines['__cxx'] = cxx - defines['optflags_build'] = '-O2 -pipe -fbracket-depth=1024' + #defines['optflags_build'] = '-O2 -pipe -fbracket-depth=1024' cvs = 'cvs' if check.check_exe(cvs, cvs): defines['__cvs'] = cvs diff --git a/source-builder/sb/git.py b/source-builder/sb/git.py index 3c0fcd7..890889d 100644 --- a/source-builder/sb/git.py +++ b/source-builder/sb/git.py @@ -50,10 +50,10 @@ class repo: self._git_exit_code(exit_code) return exit_code, output - def __init__(self, _path, opts, macros = None): + def __init__(self, _path, opts = None, macros = None): self.path = _path self.opts = opts - if macros is None: + if macros is None and opts is not None: self.macros = opts.defaults else: self.macros = macros diff --git a/source-builder/sb/log.py b/source-builder/sb/log.py index fc61d68..c58daa8 100755 --- a/source-builder/sb/log.py +++ b/source-builder/sb/log.py @@ -86,9 +86,16 @@ def flush(log = None): elif default is not None: default.flush() +def tail(log = None): + if log is not None: + return log.tail + if default is not None: + return default.tail + return 'No log output' + class log: """Log output to stdout or a file.""" - def __init__(self, streams = None, tail_size = 100): + def __init__(self, streams = None, tail_size = 200): self.tail = [] self.tail_size = tail_size self.fhs = [None, None] diff --git a/source-builder/sb/options.py b/source-builder/sb/options.py index 2d5c04d..af64080 100644 --- a/source-builder/sb/options.py +++ b/source-builder/sb/options.py @@ -303,6 +303,10 @@ class command_line: repo_valid = '1' repo_head = repo.head() repo_clean = not repo.dirty() + repo_remotes = '%{nil}' + remotes = repo.remotes() + if 'origin' in remotes: + repo_remotes = '%s/origin' % (remotes['origin']['url']) repo_id = repo_head if not repo_clean: repo_id += '-modified' @@ -311,11 +315,13 @@ class command_line: repo_valid = '0' repo_head = '%{nil}' repo_clean = '%{nil}' + repo_remotes = '%{nil}' repo_id = 'no-repo' repo_mail = None self.defaults['_sbgit_valid'] = repo_valid self.defaults['_sbgit_head'] = repo_head self.defaults['_sbgit_clean'] = str(repo_clean) + self.defaults['_sbgit_remotes'] = str(repo_remotes) self.defaults['_sbgit_id'] = repo_id if repo_mail is not None: self.defaults['_sbgit_mail'] = repo_mail @@ -462,9 +468,13 @@ class command_line: def download_disabled(self): return self.opts['no-download'] != '0' + def info(self): + s = ' Command Line: %s%s' % (' '.join(self.argv), os.linesep) + s += ' Python: %s' % (sys.version.replace('\n', '')) + return s + def log_info(self): - log.output(' Command Line: %s' % (' '.join(self.argv))) - log.output(' Python: %s' % (sys.version.replace('\n', ''))) + log.output(self.info()) def load(args, optargs = None, defaults = '%{_sbdir}/defaults.mc'): """ diff --git a/source-builder/sb/setbuilder.py b/source-builder/sb/setbuilder.py index 1491e9b..690c37b 100644 --- a/source-builder/sb/setbuilder.py +++ b/source-builder/sb/setbuilder.py @@ -33,6 +33,7 @@ try: import build import check import error + import ereport import log import mailer import options @@ -423,6 +424,10 @@ def list_bset_cfg_files(opts, configs): def run(): import sys + ec = 0 + opts = None + b = None + erheader = None try: optargs = { '--list-configs': 'List available configurations', '--list-bsets': 'List available build sets', @@ -450,25 +455,34 @@ def run(): for bset in opts.params(): b = buildset(bset, configs, opts) b.build(deps) - del b + b = None if deps is not None: c = 0 for d in sorted(set(deps)): c += 1 print 'dep[%d]: %s' % (c, d) except error.general, gerr: + erheader = 'Build: %s' % (gerr) log.notice(str(gerr)) - print >> sys.stderr, 'Build FAILED' - sys.exit(1) + log.stderr('Build FAILED') + ec = 1 except error.internal, ierr: + erheader = 'Build: %s' % (ierr) log.notice(str(ierr)) - sys.exit(1) + log.stderr('Internal Build FAILED') + ec = 1 except error.exit, eerr: pass except KeyboardInterrupt: log.notice('abort: user terminated') - sys.exit(1) - sys.exit(0) + ec = 1 + if (ec != 0 and erheader and opts and b) or (opts and opts.dry_run()): + if opts.dry_run(): + bset = 'dry-run' + else: + bset = b.bset + ereport.generate('rsb-report-%s.txt' % (bset), opts, erheader) + sys.exit(ec) if __name__ == "__main__": run()