sb: Generate an error report on an error.

Generate an error report users can send to the mailing list with
error details.
This commit is contained in:
Chris Johns
2014-05-08 02:58:14 +00:00
parent 2a6acc7267
commit 74da24cb61
7 changed files with 120 additions and 22 deletions

View File

@@ -36,6 +36,7 @@ try:
import config import config
import download import download
import error import error
import ereport
import execute import execute
import log import log
import options import options
@@ -262,7 +263,8 @@ class build:
log.output('run: ' + cmd) log.output('run: ' + cmd)
exit_code, proc, output = e.shell(cmd, cwd = path.host(cwd)) exit_code, proc, output = e.shell(cmd, cwd = path.host(cwd))
if exit_code != 0: 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): def builddir(self):
builddir = self.config.abspath('_builddir') builddir = self.config.abspath('_builddir')
@@ -431,6 +433,10 @@ def find_config(config, configs):
return None return None
def run(args): def run(args):
ec = 0
opts = None
b = None
erheader = None
try: try:
optargs = { '--list-configs': 'List available configurations' } optargs = { '--list-configs': 'List available configurations' }
opts = options.load(args, optargs) opts = options.load(args, optargs)
@@ -451,21 +457,29 @@ def run(args):
for config_file in opts.config_files(): for config_file in opts.config_files():
b = build(config_file, True, opts) b = build(config_file, True, opts)
b.make() b.make()
del b b = None
except error.general, gerr: except error.general, gerr:
print gerr erheader = 'Build: %s' % (gerr)
print >> sys.stderr, 'Build FAILED' log.notice(str(gerr))
sys.exit(1) log.stderr('Build FAILED')
ec = 1
except error.internal, ierr: except error.internal, ierr:
print ierr erheader = 'Build: %s' % (ierr)
print >> sys.stderr, 'Build FAILED' log.notice(str(ierr))
sys.exit(1) log.stderr('Internal Build FAILED')
ec = 1
except error.exit, eerr: except error.exit, eerr:
pass pass
except KeyboardInterrupt: except KeyboardInterrupt:
log.notice('abort: user terminated') log.notice('abort: user terminated')
sys.exit(1) ec = 1
sys.exit(0) 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__": if __name__ == "__main__":
run(sys.argv) run(sys.argv)

53
source-builder/sb/ereport.py Executable file
View File

@@ -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

View File

@@ -83,7 +83,7 @@ def load():
cxx = '/usr/bin/c++' cxx = '/usr/bin/c++'
if check.check_exe(cxx, cxx): if check.check_exe(cxx, cxx):
defines['__cxx'] = cxx defines['__cxx'] = cxx
defines['optflags_build'] = '-O2 -pipe -fbracket-depth=1024' #defines['optflags_build'] = '-O2 -pipe -fbracket-depth=1024'
cvs = 'cvs' cvs = 'cvs'
if check.check_exe(cvs, cvs): if check.check_exe(cvs, cvs):
defines['__cvs'] = cvs defines['__cvs'] = cvs

View File

@@ -50,10 +50,10 @@ class repo:
self._git_exit_code(exit_code) self._git_exit_code(exit_code)
return exit_code, output return exit_code, output
def __init__(self, _path, opts, macros = None): def __init__(self, _path, opts = None, macros = None):
self.path = _path self.path = _path
self.opts = opts self.opts = opts
if macros is None: if macros is None and opts is not None:
self.macros = opts.defaults self.macros = opts.defaults
else: else:
self.macros = macros self.macros = macros

View File

@@ -86,9 +86,16 @@ def flush(log = None):
elif default is not None: elif default is not None:
default.flush() 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: class log:
"""Log output to stdout or a file.""" """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 = []
self.tail_size = tail_size self.tail_size = tail_size
self.fhs = [None, None] self.fhs = [None, None]

View File

@@ -303,6 +303,10 @@ class command_line:
repo_valid = '1' repo_valid = '1'
repo_head = repo.head() repo_head = repo.head()
repo_clean = not repo.dirty() 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 repo_id = repo_head
if not repo_clean: if not repo_clean:
repo_id += '-modified' repo_id += '-modified'
@@ -311,11 +315,13 @@ class command_line:
repo_valid = '0' repo_valid = '0'
repo_head = '%{nil}' repo_head = '%{nil}'
repo_clean = '%{nil}' repo_clean = '%{nil}'
repo_remotes = '%{nil}'
repo_id = 'no-repo' repo_id = 'no-repo'
repo_mail = None repo_mail = None
self.defaults['_sbgit_valid'] = repo_valid self.defaults['_sbgit_valid'] = repo_valid
self.defaults['_sbgit_head'] = repo_head self.defaults['_sbgit_head'] = repo_head
self.defaults['_sbgit_clean'] = str(repo_clean) self.defaults['_sbgit_clean'] = str(repo_clean)
self.defaults['_sbgit_remotes'] = str(repo_remotes)
self.defaults['_sbgit_id'] = repo_id self.defaults['_sbgit_id'] = repo_id
if repo_mail is not None: if repo_mail is not None:
self.defaults['_sbgit_mail'] = repo_mail self.defaults['_sbgit_mail'] = repo_mail
@@ -462,9 +468,13 @@ class command_line:
def download_disabled(self): def download_disabled(self):
return self.opts['no-download'] != '0' 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): def log_info(self):
log.output(' Command Line: %s' % (' '.join(self.argv))) log.output(self.info())
log.output(' Python: %s' % (sys.version.replace('\n', '')))
def load(args, optargs = None, defaults = '%{_sbdir}/defaults.mc'): def load(args, optargs = None, defaults = '%{_sbdir}/defaults.mc'):
""" """

View File

@@ -33,6 +33,7 @@ try:
import build import build
import check import check
import error import error
import ereport
import log import log
import mailer import mailer
import options import options
@@ -423,6 +424,10 @@ def list_bset_cfg_files(opts, configs):
def run(): def run():
import sys import sys
ec = 0
opts = None
b = None
erheader = None
try: try:
optargs = { '--list-configs': 'List available configurations', optargs = { '--list-configs': 'List available configurations',
'--list-bsets': 'List available build sets', '--list-bsets': 'List available build sets',
@@ -450,25 +455,34 @@ def run():
for bset in opts.params(): for bset in opts.params():
b = buildset(bset, configs, opts) b = buildset(bset, configs, opts)
b.build(deps) b.build(deps)
del b b = None
if deps is not None: if deps is not None:
c = 0 c = 0
for d in sorted(set(deps)): for d in sorted(set(deps)):
c += 1 c += 1
print 'dep[%d]: %s' % (c, d) print 'dep[%d]: %s' % (c, d)
except error.general, gerr: except error.general, gerr:
erheader = 'Build: %s' % (gerr)
log.notice(str(gerr)) log.notice(str(gerr))
print >> sys.stderr, 'Build FAILED' log.stderr('Build FAILED')
sys.exit(1) ec = 1
except error.internal, ierr: except error.internal, ierr:
erheader = 'Build: %s' % (ierr)
log.notice(str(ierr)) log.notice(str(ierr))
sys.exit(1) log.stderr('Internal Build FAILED')
ec = 1
except error.exit, eerr: except error.exit, eerr:
pass pass
except KeyboardInterrupt: except KeyboardInterrupt:
log.notice('abort: user terminated') log.notice('abort: user terminated')
sys.exit(1) ec = 1
sys.exit(0) 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__": if __name__ == "__main__":
run() run()