diff --git a/HISTORY.rst b/HISTORY.rst index 91973810..c36dcbb1 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -31,6 +31,8 @@ Release History * Fixed compiling error if space is in user's folder (`issue #56 `_) * Fixed `AttributeError: 'module' object has no attribute 'disable_warnings'` when a version of `requests` package is less then 2.4.0 +* Fixed bug with invalid process's "return code" when PlatformIO has internal + error (`issue #81 `_) 0.10.2 (2015-01-06) diff --git a/platformio/__main__.py b/platformio/__main__.py index 844ead39..ed6aeee2 100644 --- a/platformio/__main__.py +++ b/platformio/__main__.py @@ -8,8 +8,7 @@ from traceback import format_exc import click -from platformio import __version__, maintenance -from platformio.exception import PlatformioException, UnknownCLICommand +from platformio import __version__, exception, maintenance from platformio.util import get_source_dir @@ -31,7 +30,7 @@ class PlatformioCLI(click.MultiCommand): # pylint: disable=R0904 mod = __import__("platformio.commands." + name, None, None, ["cli"]) except ImportError: - raise UnknownCLICommand(name) + raise exception.UnknownCLICommand(name) return mod.cli @@ -52,12 +51,13 @@ def main(): try: cli(None) except Exception as e: # pylint: disable=W0703 - maintenance.on_platformio_exception(e) - if isinstance(e, PlatformioException): - click.echo("Error: " + str(e), err=True) - sys_exit(1) - else: - print format_exc() + if not isinstance(e, exception.ReturnErrorCode): + maintenance.on_platformio_exception(e) + if isinstance(e, exception.PlatformioException): + click.echo("Error: " + str(e), err=True) + else: + click.echo(format_exc(), err=True) + sys_exit(1) if __name__ == "__main__": diff --git a/platformio/commands/run.py b/platformio/commands/run.py index 726d39bb..d4c5bd8a 100644 --- a/platformio/commands/run.py +++ b/platformio/commands/run.py @@ -36,6 +36,7 @@ def cli(ctx, environment, target, upload_port): getmtime(_pioenvs_dir)): rmtree(_pioenvs_dir) + found_error = False _first_done = False for section in config.sections(): # skip main configuration section @@ -56,9 +57,13 @@ def cli(ctx, environment, target, upload_port): if _first_done: click.echo() - process_environment(ctx, envname, options, target, upload_port) + if not process_environment(ctx, envname, options, target, upload_port): + found_error = True _first_done = True + if found_error: + raise exception.ReturnErrorCode() + def process_environment(ctx, name, options, targets, upload_port): terminal_width, _ = click.get_terminal_size() @@ -72,8 +77,8 @@ def process_environment(ctx, name, options, targets, upload_port): click.secho("-" * terminal_width, bold=True) result = _run_environment(ctx, name, options, targets, upload_port) - is_error = "error" in result['err'].lower() + is_error = result['returncode'] != 0 summary_text = " Took %.2f seconds " % (time() - start_time) half_line = "=" * ((terminal_width - len(summary_text) - 10) / 2) click.echo("%s [%s]%s%s" % ( @@ -84,6 +89,8 @@ def process_environment(ctx, name, options, targets, upload_port): half_line ), err=is_error) + return not is_error + def _run_environment(ctx, name, options, targets, upload_port): variables = ["PIOENV=" + name] diff --git a/platformio/exception.py b/platformio/exception.py index 05f9c0ea..5646befe 100644 --- a/platformio/exception.py +++ b/platformio/exception.py @@ -13,6 +13,10 @@ class PlatformioException(Exception): return Exception.__str__(self) +class ReturnErrorCode(PlatformioException): + pass + + class AbortedByUser(PlatformioException): MESSAGE = "Aborted by user" diff --git a/platformio/platforms/base.py b/platformio/platforms/base.py index ad950318..dcfef9d9 100644 --- a/platformio/platforms/base.py +++ b/platformio/platforms/base.py @@ -1,6 +1,7 @@ # Copyright (C) Ivan Kravets # See LICENSE for details. +import re from imp import load_source from os import listdir from os.path import isdir, isfile, join @@ -76,6 +77,10 @@ class PlatformFactory(object): class BasePlatform(object): PACKAGES = {} + LINE_ERROR_RE = re.compile(r"(\s+error|error[:\s]+)", re.I) + + def __init__(self): + self._found_error = False def get_name(self): return self.__class__.__name__[:-8].lower() @@ -214,6 +219,7 @@ class BasePlatform(object): variables.append( "PIOPACKAGE_%s=%s" % (options['alias'].upper(), name)) + self._found_error = False try: result = util.exec_command( [ @@ -227,9 +233,10 @@ class BasePlatform(object): except OSError: raise exception.SConsNotInstalled() - return self.after_run(result) + assert "returncode" in result + if self._found_error: + result['returncode'] = 1 - def after_run(self, result): # pylint: disable=R0201 return result def on_run_out(self, line): # pylint: disable=R0201 @@ -239,5 +246,7 @@ class BasePlatform(object): click.secho(line, fg=fg) def on_run_err(self, line): # pylint: disable=R0201 - click.secho(line, err=True, - fg="red" if "error" in line.lower() else "yellow") + is_error = self.LINE_ERROR_RE.search(line) is not None + if is_error: + self._found_error = True + click.secho(line, err=True, fg="red" if is_error else "yellow") diff --git a/platformio/util.py b/platformio/util.py index ca9f6aa6..0e410ba3 100644 --- a/platformio/util.py +++ b/platformio/util.py @@ -117,7 +117,12 @@ def change_filemtime(path, time): def exec_command(*args, **kwargs): - result = {"out": None, "err": None} + result = { + "out": None, + "err": None, + "returncode": None + } + default = dict( stdout=subprocess.PIPE, stderr=subprocess.PIPE, @@ -129,6 +134,7 @@ def exec_command(*args, **kwargs): p = subprocess.Popen(*args, **kwargs) try: result['out'], result['err'] = p.communicate() + result['returncode'] = p.returncode except KeyboardInterrupt: for s in ("stdout", "stderr"): if isinstance(kwargs[s], AsyncPipe): @@ -141,9 +147,8 @@ def exec_command(*args, **kwargs): result[s[3:]] = "\n".join(kwargs[s].get_buffer()) for k, v in result.iteritems(): - if not v: - continue - result[k].strip() + if v and isinstance(v, basestring): + result[k].strip() return result diff --git a/tests/test_examples.py b/tests/test_examples.py index 89c5e500..df29b20c 100644 --- a/tests/test_examples.py +++ b/tests/test_examples.py @@ -30,9 +30,8 @@ def test_run(platformio_setup, pioproject_dir): ["platformio", "run"], cwd=pioproject_dir ) - output = "%s\n%s" % (result['out'], result['err']) - if "error" in output.lower(): - pytest.fail(output) + if result['returncode'] != 0: + pytest.fail(result) # check .elf file pioenvs_dir = join(pioproject_dir, ".pioenvs")