diff --git a/HISTORY.rst b/HISTORY.rst index ef32bb7c..2372d1a5 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -11,26 +11,27 @@ Release History (`issue #108 `_) * Created `PlatformIO gitter.im `_ room (`issue #174 `_) -* Added global ``-f, --force`` option which will force to accept any +* Global ``-f, --force`` option which will force to accept any confirmation prompts (`issue #152 `_) -* Allowed to run project with `platformio run --project-dir `_ option without changing the current working +* Run project with `platformio run --project-dir `_ option without changing the current working directory (`issue #192 `_) -* Allowed to add library dependencies for build environment using +* Control verbosity of `platformio run `_ command via ``-v/--verbose`` option +* Add library dependencies for build environment using `install_libs `_ option in ``platformio.ini`` (`issue #134 `_) -* Allowed to specify libraries which are compatible with build environment using +* Specify libraries which are compatible with build environment using `use_libs `_ option in ``platformio.ini`` (`issue #148 `_) -* Allowed to add more boards to PlatformIO project with +* Add more boards to PlatformIO project with `platformio init --board `__ command (`issue #167 `_) -* Allowed to choose which library to update +* Choose which library to update (`issue #168 `_) -* Allowed to specify `platformio init --env-prefix `__ when initialise/update project +* Specify `platformio init --env-prefix `__ when initialise/update project (`issue #182 `_) * Disabled automatic updates by default for platforms, packages and libraries (`issue #171 `_) diff --git a/docs/userguide/cmd_ci.rst b/docs/userguide/cmd_ci.rst index f5743bff..3c2842d4 100644 --- a/docs/userguide/cmd_ci.rst +++ b/docs/userguide/cmd_ci.rst @@ -106,6 +106,14 @@ Don't remove :option:`platformio ci --build-dir` after build process. Buid project using pre-configured :ref:`projectconf`. +.. option:: + -v, --verbose + +Shows details about the results of processing environments. More details +:option:`platformio run --verbose` + +By default, verbosity level is set to 1 (only errors will be printed). + Examples -------- diff --git a/docs/userguide/cmd_run.rst b/docs/userguide/cmd_run.rst index 9daa9dda..468f22cd 100644 --- a/docs/userguide/cmd_run.rst +++ b/docs/userguide/cmd_run.rst @@ -47,6 +47,21 @@ Upload port of embedded board. To print all available ports use Specify the path to project directory. By default, ``--build-dir`` is equal to current working directory (``CWD``). +.. option:: + -v, --verbose + +Shows details about the results of processing environments. Each instance of +``--verbose`` on the command line increases the verbosity level by one, so if +you need more details on the output, specify it twice. + +There 3 levels of verbosity: + +1. ``-v`` - output errors only +2. ``-vv`` - output errors and warnings +3. ``-vvv`` - output errors, warnings and additional information + +By default, verbosity level is set to 3 (maximum information). + Examples -------- diff --git a/platformio/commands/ci.py b/platformio/commands/ci.py index 2218eb76..a9f24be5 100644 --- a/platformio/commands/ci.py +++ b/platformio/commands/ci.py @@ -52,9 +52,10 @@ def validate_boards(ctx, param, value): # pylint: disable=W0613 @click.option("--project-conf", type=click.Path(exists=True, file_okay=True, dir_okay=False, readable=True, resolve_path=True)) +@click.option("--verbose", "-v", count=True, default=1) @click.pass_context def cli(ctx, src, lib, exclude, board, # pylint: disable=R0913 - build_dir, keep_build_dir, project_conf): + build_dir, keep_build_dir, project_conf, verbose): if not src: src = environ.get("PLATFORMIO_CI_SRC", "").split(":") @@ -86,7 +87,7 @@ def cli(ctx, src, lib, exclude, board, # pylint: disable=R0913 disable_auto_uploading=True) # process project - ctx.invoke(cmd_run, project_dir=build_dir) + ctx.invoke(cmd_run, project_dir=build_dir, verbose=verbose) finally: if not keep_build_dir: rmtree(build_dir) diff --git a/platformio/commands/run.py b/platformio/commands/run.py index 5cbd650f..b61544ba 100644 --- a/platformio/commands/run.py +++ b/platformio/commands/run.py @@ -24,8 +24,10 @@ from platformio.platforms.base import PlatformFactory @click.option("--project-dir", default=getcwd, type=click.Path(exists=True, file_okay=False, dir_okay=True, writable=True, resolve_path=True)) +@click.option("--verbose", "-v", count=True, default=3) @click.pass_context -def cli(ctx, environment, target, upload_port, project_dir): +def cli(ctx, environment, target, upload_port, # pylint: disable=R0913,R0914 + project_dir, verbose): initial_cwd = getcwd() chdir(project_dir) try: @@ -47,98 +49,111 @@ def cli(ctx, environment, target, upload_port, project_dir): results = [] for section in config.sections(): - if results and results[-1] is not None: + # skip main configuration section + if section == "platformio": + continue + + if not section.startswith("env:"): + raise exception.InvalidEnvName(section) + + envname = section[4:] + if environment and envname not in environment: + # echo("Skipped %s environment" % style(envname, fg="yellow")) + continue + + if results: click.echo() - results.append(_process_conf_section( - ctx, config, section, environment, target, upload_port)) + options = {} + for k, v in config.items(section): + options[k] = v - if not all([r for r in results if r is not None]): + ep = EnvironmentProcessor( + ctx, envname, options, target, upload_port, verbose) + results.append(ep.process()) + + if not all(results): raise exception.ReturnErrorCode() finally: chdir(initial_cwd) -def _process_conf_section(ctx, config, section, # pylint: disable=R0913 - environment, target, upload_port): - # skip main configuration section - if section == "platformio": - return None +class EnvironmentProcessor(object): - if section[:4] != "env:": - raise exception.InvalidEnvName(section) + def __init__(self, cmd_ctx, name, options, # pylint: disable=R0913 + targets, upload_port, verbose): + self.cmd_ctx = cmd_ctx + self.name = name + self.options = options + self.targets = targets + self.upload_port = upload_port + self.verbose_level = int(verbose) - envname = section[4:] - if environment and envname not in environment: - # echo("Skipped %s environment" % style(envname, fg="yellow")) - return None + def process(self): + terminal_width, _ = click.get_terminal_size() + start_time = time() - options = {} - for k, v in config.items(section): - options[k] = v + click.echo("[%s] Processing %s (%s)" % ( + datetime.now().strftime("%c"), + click.style(self.name, fg="cyan", bold=True), + ", ".join(["%s: %s" % (k, v) for k, v in self.options.iteritems()]) + )) + click.secho("-" * terminal_width, bold=True) - return _process_environment(ctx, envname, options, target, upload_port) + result = self._run() + + 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" % ( + half_line, + (click.style(" ERROR ", fg="red", bold=True) + if is_error else click.style("SUCCESS", fg="green", bold=True)), + summary_text, + half_line + ), err=is_error) + + return not is_error + + def _get_build_variables(self): + variables = ["PIOENV=" + self.name] + if self.upload_port: + variables.append("UPLOAD_PORT=%s" % self.upload_port) + for k, v in self.options.items(): + k = k.upper() + if k == "TARGETS" or (k == "UPLOAD_PORT" and self.upload_port): + continue + variables.append("%s=%s" % (k.upper(), v)) + return variables + + def _get_build_targets(self): + targets = [] + if self.targets: + targets = [t for t in self.targets] + elif "targets" in self.options: + targets = self.options['targets'].split() + return targets + + def _run(self): + if "platform" not in self.options: + raise exception.UndefinedEnvPlatform(self.name) + + platform = self.options['platform'] + build_vars = self._get_build_variables() + build_targets = self._get_build_targets() + + telemetry.on_run_environment(self.options, build_targets) + + # install platform and libs dependencies + _autoinstall_platform(self.cmd_ctx, platform) + if "install_libs" in self.options: + _autoinstall_libs(self.cmd_ctx, self.options['install_libs']) + + p = PlatformFactory.newPlatform(platform) + return p.run(build_vars, build_targets, self.verbose_level) -def _process_environment(ctx, name, options, targets, upload_port): - terminal_width, _ = click.get_terminal_size() - start_time = time() - - click.echo("[%s] Processing %s (%s)" % ( - datetime.now().strftime("%c"), - click.style(name, fg="cyan", bold=True), - ", ".join(["%s: %s" % (k, v) for k, v in options.iteritems()]) - )) - click.secho("-" * terminal_width, bold=True) - - result = _run_environment(ctx, name, options, targets, upload_port) - - 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" % ( - half_line, - (click.style(" ERROR ", fg="red", bold=True) - if is_error else click.style("SUCCESS", fg="green", bold=True)), - summary_text, - half_line - ), err=is_error) - - return not is_error - - -def _run_environment(ctx, name, options, targets, upload_port): - variables = ["PIOENV=" + name] - if upload_port: - variables.append("UPLOAD_PORT=%s" % upload_port) - for k, v in options.items(): - k = k.upper() - if k == "TARGETS" or (k == "UPLOAD_PORT" and upload_port): - continue - variables.append("%s=%s" % (k.upper(), v)) - - envtargets = [] - if targets: - envtargets = [t for t in targets] - elif "targets" in options: - envtargets = options['targets'].split() - - if "platform" not in options: - raise exception.UndefinedEnvPlatform(name) - platform = options['platform'] - - telemetry.on_run_environment(options, envtargets) - - # install platform and libs dependencies - _autoinstall_env_platform(ctx, platform) - if "install_libs" in options: - _autoinstall_env_libs(ctx, options['install_libs']) - - p = PlatformFactory.newPlatform(platform) - return p.run(variables, envtargets) - - -def _autoinstall_env_platform(ctx, platform): +def _autoinstall_platform(ctx, platform): installed_platforms = PlatformFactory.get_platforms( installed=True).keys() if (platform not in installed_platforms and ( @@ -148,7 +163,7 @@ def _autoinstall_env_platform(ctx, platform): ctx.invoke(cmd_platforms_install, platforms=[platform]) -def _autoinstall_env_libs(ctx, libids_list): +def _autoinstall_libs(ctx, libids_list): require_libs = [int(l.strip()) for l in libids_list.split(",")] installed_libs = [ l['id'] for l in LibraryManager().get_installed().values() diff --git a/platformio/platforms/atmelavr.py b/platformio/platforms/atmelavr.py index 9c7bc243..23486589 100644 --- a/platformio/platforms/atmelavr.py +++ b/platformio/platforms/atmelavr.py @@ -47,7 +47,7 @@ class AtmelavrPlatform(BasePlatform): else: BasePlatform.on_run_err(self, line) - def run(self, variables, targets): + def run(self, variables, targets, verbose): for v in variables: if "BOARD=" not in v: continue @@ -58,4 +58,4 @@ class AtmelavrPlatform(BasePlatform): tuploader = "tool-micronucleus" self.PACKAGES[tuploader]['alias'] = "uploader" break - return BasePlatform.run(self, variables, targets) + return BasePlatform.run(self, variables, targets, verbose) diff --git a/platformio/platforms/base.py b/platformio/platforms/base.py index d91118ec..b1305c3f 100644 --- a/platformio/platforms/base.py +++ b/platformio/platforms/base.py @@ -190,6 +190,12 @@ class BasePlatform(object): def __init__(self): self._found_error = False + self._last_echo_line = None + + # 1 = errors + # 2 = 1 + warnings + # 3 = 2 + others + self._verbose_level = 3 def get_type(self): return self.__class__.__name__[:-8].lower() @@ -306,10 +312,12 @@ class BasePlatform(object): obsolated = pm.get_outdated() return not set(self.get_packages().keys()).isdisjoint(set(obsolated)) - def run(self, variables, targets): + def run(self, variables, targets, verbose): assert isinstance(variables, list) assert isinstance(targets, list) + self._verbose_level = int(verbose) + installed_platforms = PlatformFactory.get_platforms( installed=True).keys() installed_packages = PackageManager.get_installed() @@ -356,16 +364,34 @@ class BasePlatform(object): if self._found_error: result['returncode'] = 1 + if self._last_echo_line == ".": + click.echo("") + return result - def on_run_out(self, line): # pylint: disable=R0201 - fg = None - if "is up to date" in line: - fg = "green" - click.secho(line, fg=fg) + def on_run_out(self, line): + self._echo_line(line, level=3) - def on_run_err(self, line): # pylint: disable=R0201 + def on_run_err(self, line): 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") + self._echo_line(line, level=1 if is_error else 2) + + def _echo_line(self, line, level): + assert 1 <= level <= 3 + + fg = ("red", "yellow", None)[level - 1] + if level == 3 and "is up to date" in line: + fg = "green" + + if level > self._verbose_level: + click.secho(".", fg=fg, err=level < 3, nl=False) + self._last_echo_line = "." + return + + if self._last_echo_line == ".": + click.echo("") + self._last_echo_line = line + + click.secho(line, fg=fg, err=level < 3) diff --git a/platformio/platforms/teensy.py b/platformio/platforms/teensy.py index cc79c3ee..07316d15 100644 --- a/platformio/platforms/teensy.py +++ b/platformio/platforms/teensy.py @@ -44,7 +44,7 @@ class TeensyPlatform(BasePlatform): def get_name(self): return "Teensy" - def run(self, variables, targets): + def run(self, variables, targets, verbose): for v in variables: if "BOARD=" not in v: continue @@ -56,4 +56,4 @@ class TeensyPlatform(BasePlatform): tpackage = "toolchain-gccarmnoneeabi" self.PACKAGES[tpackage]['alias'] = "toolchain" break - return BasePlatform.run(self, variables, targets) + return BasePlatform.run(self, variables, targets, verbose)