# HG changeset patch # User cmlenz # Date 1123873876 0 # Node ID 3ed8f568f60a2f85acd28a541e7e685f37ff9549 # Parent 091ead7d2876e417582d4bff30ab94816ae64854 Fix error handling so that reports are still generated even if a command has failed. diff --git a/bitten/build/ctools.py b/bitten/build/ctools.py --- a/bitten/build/ctools.py +++ b/bitten/build/ctools.py @@ -49,4 +49,4 @@ xmlio.SubElement(log_elem, 'message', level='error')[err] ctxt.log(log_elem) if cmdline.returncode != 0: - raise BuildError, 'make failed (%s)' % cmdline.returncode + ctxt.error('make failed (%s)' % cmdline.returncode) diff --git a/bitten/build/pythontools.py b/bitten/build/pythontools.py --- a/bitten/build/pythontools.py +++ b/bitten/build/pythontools.py @@ -47,7 +47,7 @@ xmlio.SubElement(log_elem, 'message', level=level)[err] ctxt.log(log_elem) if cmdline.returncode != 0: - raise BuildError, 'distutils failed (%s)' % cmdline.returncode + ctxt.error('distutils failed (%s)' % cmdline.returncode) def pylint(ctxt, file=None): """Extract data from a `pylint` run written to a file.""" @@ -78,7 +78,7 @@ finally: fd.close() except IOError, e: - raise BuildError, 'Error opening pylint results file (%s)' % e + log.warning('Error opening pylint results file (%s)', e) def trace(ctxt, summary=None, coverdir=None, include=None, exclude=None): """Extract data from a `trace.py` run.""" @@ -125,7 +125,7 @@ finally: summary_file.close() except IOError, e: - raise BuildError, 'Error opening unittest results file (%s)' % e + log.warning('Error opening unittest results file (%s)', e) def unittest(ctxt, file=None): @@ -147,4 +147,4 @@ finally: fd.close() except IOError, e: - raise BuildError, 'Error opening unittest results file (%s)' % e + log.warning('Error opening unittest results file (%s)', e) diff --git a/bitten/recipe.py b/bitten/recipe.py --- a/bitten/recipe.py +++ b/bitten/recipe.py @@ -44,6 +44,9 @@ self.basedir = os.path.realpath(basedir) self.output = [] + def error(self, message): + self.output.append((Recipe.ERROR, self.current_function, message)) + def log(self, xml_elem): self.output.append((Recipe.LOG, self.current_function, xml_elem)) @@ -80,19 +83,22 @@ def execute(self, ctxt): ctxt.current_step = self try: - try: - for function, args in self: - ctxt.current_function = function.__name__ - function(ctxt, **args) - ctxt.current_function = None - except BuildError, e: - if self.onerror == 'fail': - raise BuildError, e - log.warning('Ignoring error in step %s (%s)', self.id, e) + for function, args in self: + ctxt.current_function = function.__name__ + function(ctxt, **args) + ctxt.current_function = None finally: ctxt.current_step = None + errors = [] while ctxt.output: - yield ctxt.output.pop() + type, function, output = ctxt.output.pop() + yield type, function, output + if type == Recipe.ERROR: + errors.append((function, output)) + if errors: + if self.onerror == 'fail': + raise BuildError, 'Build step %s failed' % self.id + log.warning('Ignoring error in step %s (%s)', self.id, e) def _args(self, elem): return dict([(name.replace('-', '_'), value) for name, value @@ -116,8 +122,10 @@ """A build recipe. Iterate over this object to get the individual build steps in the order they - have been defined in the recipe file.""" + have been defined in the recipe file. + """ + ERROR = 'error' LOG = 'log' REPORT = 'report' diff --git a/bitten/slave.py b/bitten/slave.py --- a/bitten/slave.py +++ b/bitten/slave.py @@ -174,15 +174,28 @@ log.info('Executing build step "%s"', step.id) started = datetime.utcnow() try: - xml = xmlio.Element('step', id=step.id, result='success', + xml = xmlio.Element('step', id=step.id, description=step.description, time=started.isoformat()) - for type, function, output in step.execute(recipe.ctxt): - xmlio.SubElement(xml, type, type=function)[output] + step_failed = False + try: + for type, function, output in step.execute(recipe.ctxt): + if type == Recipe.ERROR: + step_failed = True + xmlio.SubElement(xml, type, type=function)[output] + except BuildError, e: + log.error('Build step %s failed', step.id) + failed = True xml.attr['duration'] = (datetime.utcnow() - started).seconds - log.info('Build step %s completed successfully', step.id) + if step_failed: + xml.attr['result'] = 'failure' + log.warning('Build step %s failed', step.id) + else: + xml.attr['result'] = 'success' + log.info('Build step %s completed successfully', + step.id) self.channel.send_ans(msgno, beep.Payload(xml)) - except (BuildError, InvalidRecipeError), e: + except InvalidRecipeError, e: log.warning('Build step %s failed: %s', step.id, e) duration = datetime.utcnow() - started failed = True diff --git a/bitten/util/xmlio.py b/bitten/util/xmlio.py --- a/bitten/util/xmlio.py +++ b/bitten/util/xmlio.py @@ -205,7 +205,7 @@ return self.children() def gettext(self): - return ''.join([c.nodeValue for c in self._node.childNodes]) + return ''.join([c.nodeValue or '' for c in self._node.childNodes]) def write(self, out, newlines=False): """Serializes the element and writes the XML to the given output diff --git a/scripts/build.py b/scripts/build.py --- a/scripts/build.py +++ b/scripts/build.py @@ -54,7 +54,8 @@ print print '-->', step.description or step.id for type, function, output in step.execute(recipe.ctxt): - pass + if type == Recipe.ERROR: + log.error('Failure in step "%s": %s', step.id, output) if step.id in steps_to_run: steps_to_run[step.id] = True