From d09964a8979c376032be68d97592d711188ff8e4 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Mon, 3 Jun 2019 19:20:10 +0300 Subject: [PATCH] Use common IDE data loading for IDE and DEBUG --- platformio/commands/debug/command.py | 7 +- platformio/commands/debug/helpers.py | 61 +++++--------- platformio/commands/init.py | 2 +- platformio/commands/run/command.py | 8 +- platformio/commands/run/helpers.py | 6 +- platformio/commands/run/processor.py | 4 +- platformio/ide/projectgenerator.py | 115 +++++++++------------------ platformio/project/helpers.py | 24 +++++- 8 files changed, 97 insertions(+), 130 deletions(-) diff --git a/platformio/commands/debug/command.py b/platformio/commands/debug/command.py index 891363f1..70111e6f 100644 --- a/platformio/commands/debug/command.py +++ b/platformio/commands/debug/command.py @@ -24,7 +24,8 @@ from platformio import exception, util from platformio.commands.debug import helpers from platformio.managers.core import inject_contrib_pysite from platformio.project.config import ProjectConfig -from platformio.project.helpers import is_platformio_project +from platformio.project.helpers import (is_platformio_project, + load_project_ide_data) @click.command("debug", @@ -81,7 +82,7 @@ def cli(ctx, project_dir, project_conf, environment, verbose, interface, return helpers.predebug_project(ctx, project_dir, env_name, False, verbose) - configuration = helpers.load_configuration(ctx, project_dir, env_name) + configuration = load_project_ide_data(project_dir, env_name) if not configuration: raise exception.DebugInvalidOptions( "Could not load debug configuration") @@ -116,8 +117,8 @@ def cli(ctx, project_dir, project_conf, environment, verbose, interface, if rebuild_prog: if helpers.is_mi_mode(__unprocessed): - output = helpers.GDBBytesIO() click.echo('~"Preparing firmware for debugging...\\n"') + output = helpers.GDBBytesIO() with helpers.capture_std_streams(output): helpers.predebug_project(ctx, project_dir, env_name, preload, verbose) diff --git a/platformio/commands/debug/helpers.py b/platformio/commands/debug/helpers.py index 6c215512..c9b36897 100644 --- a/platformio/commands/debug/helpers.py +++ b/platformio/commands/debug/helpers.py @@ -12,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -import json import sys import time from contextlib import contextmanager @@ -39,6 +38,17 @@ class GDBBytesIO(BytesIO): # pylint: disable=too-few-public-methods self.STDOUT.flush() +@contextmanager +def capture_std_streams(stdout, stderr=None): + _stdout = sys.stdout + _stderr = sys.stderr + sys.stdout = stdout + sys.stderr = stderr or stdout + yield + sys.stdout = _stdout + sys.stderr = _stderr + + def is_mi_mode(args): return "--interpreter" in " ".join(args) @@ -59,6 +69,16 @@ def get_default_debug_env(config): return default_envs[0] if default_envs else all_envs[0] +def predebug_project(ctx, project_dir, env_name, preload, verbose): + ctx.invoke(cmd_run, + project_dir=project_dir, + environment=[env_name], + target=["debug"] + (["upload"] if preload else []), + verbose=verbose) + if preload: + time.sleep(5) + + def validate_debug_options(cmd_ctx, env_options): def _cleanup_cmds(items): @@ -144,45 +164,6 @@ def validate_debug_options(cmd_ctx, env_options): return result -def predebug_project(ctx, project_dir, env_name, preload, verbose): - ctx.invoke(cmd_run, - project_dir=project_dir, - environment=[env_name], - target=["debug"] + (["upload"] if preload else []), - verbose=verbose) - if preload: - time.sleep(5) - - -@contextmanager -def capture_std_streams(stdout, stderr=None): - _stdout = sys.stdout - _stderr = sys.stderr - sys.stdout = stdout - sys.stderr = stderr or stdout - yield - sys.stdout = _stdout - sys.stderr = _stderr - - -def load_configuration(ctx, project_dir, env_name): - output = BytesIO() - with capture_std_streams(output): - ctx.invoke(cmd_run, - project_dir=project_dir, - environment=[env_name], - target=["idedata"]) - result = output.getvalue().decode() - output.close() - if '"includes":' not in result: - return None - for line in result.split("\n"): - line = line.strip() - if line.startswith('{"') and "cxx_path" in line: - return json.loads(line[:line.rindex("}") + 1]) - return None - - def configure_esp32_load_cmds(debug_options, configuration): ignore_conds = [ debug_options['load_cmds'] != ["load"], diff --git a/platformio/commands/init.py b/platformio/commands/init.py index d3d8f52f..09eddacf 100644 --- a/platformio/commands/init.py +++ b/platformio/commands/init.py @@ -132,7 +132,7 @@ def cli( def get_best_envname(project_dir, boards=None): - config = ProjectConfig(join(project_dir, "platformio.ini")) + config = ProjectConfig.get_instance(join(project_dir, "platformio.ini")) config.validate() envname = None diff --git a/platformio/commands/run/command.py b/platformio/commands/run/command.py index 23ca09d4..c738749e 100644 --- a/platformio/commands/run/command.py +++ b/platformio/commands/run/command.py @@ -20,8 +20,8 @@ import click from platformio import exception, util from platformio.commands.device import device_monitor as cmd_device_monitor -from platformio.commands.run.helpers import (_clean_build_dir, - _handle_legacy_libdeps, +from platformio.commands.run.helpers import (clean_build_dir, + handle_legacy_libdeps, print_summary) from platformio.commands.run.processor import EnvironmentProcessor from platformio.project.config import ProjectConfig @@ -64,7 +64,7 @@ def cli(ctx, environment, target, upload_port, project_dir, project_conf, # clean obsolete build dir if not disable_auto_clean: try: - _clean_build_dir(get_project_build_dir()) + clean_build_dir(get_project_build_dir()) except: # pylint: disable=bare-except click.secho( "Can not remove temporary directory `%s`. Please remove " @@ -76,7 +76,7 @@ def cli(ctx, environment, target, upload_port, project_dir, project_conf, project_conf or join(project_dir, "platformio.ini")) config.validate(environment) - _handle_legacy_libdeps(project_dir, config) + handle_legacy_libdeps(project_dir, config) results = [] start_time = time() diff --git a/platformio/commands/run/helpers.py b/platformio/commands/run/helpers.py index d1e6b59f..ddb1fb9d 100644 --- a/platformio/commands/run/helpers.py +++ b/platformio/commands/run/helpers.py @@ -27,7 +27,7 @@ from platformio.project.helpers import (calculate_project_hash, get_project_libdeps_dir) -def _handle_legacy_libdeps(project_dir, config): +def handle_legacy_libdeps(project_dir, config): legacy_libdeps_dir = join(project_dir, ".piolibdeps") if (not isdir(legacy_libdeps_dir) or legacy_libdeps_dir == get_project_libdeps_dir()): @@ -46,7 +46,7 @@ def _handle_legacy_libdeps(project_dir, config): fg="yellow") -def _autoinstall_libdeps(ctx, envname, libraries, verbose=False): +def autoinstall_libdeps(ctx, envname, libraries, verbose=False): if not libraries: return libdeps_dir = join(get_project_libdeps_dir(), envname) @@ -62,7 +62,7 @@ def _autoinstall_libdeps(ctx, envname, libraries, verbose=False): click.secho(str(e), fg="yellow") -def _clean_build_dir(build_dir): +def clean_build_dir(build_dir): # remove legacy ".pioenvs" folder legacy_build_dir = join(get_project_dir(), ".pioenvs") if isdir(legacy_build_dir) and legacy_build_dir != build_dir: diff --git a/platformio/commands/run/processor.py b/platformio/commands/run/processor.py index 66e65637..66c07296 100644 --- a/platformio/commands/run/processor.py +++ b/platformio/commands/run/processor.py @@ -19,7 +19,7 @@ import click from platformio import exception, telemetry from platformio.commands.platform import \ platform_install as cmd_platform_install -from platformio.commands.run.helpers import _autoinstall_libdeps, print_header +from platformio.commands.run.helpers import autoinstall_libdeps, print_header from platformio.commands.test.processor import (CTX_META_TEST_IS_RUNNING, CTX_META_TEST_RUNNING_NAME) from platformio.managers.platform import PlatformFactory @@ -104,7 +104,7 @@ class EnvironmentProcessor(object): if "monitor" in build_targets: build_targets.remove("monitor") if "nobuild" not in build_targets and "lib_deps" in self.options: - _autoinstall_libdeps( + autoinstall_libdeps( self.cmd_ctx, self.name, self.config.get("env:" + self.name, "lib_deps"), self.verbose) diff --git a/platformio/ide/projectgenerator.py b/platformio/ide/projectgenerator.py index eb3ce6ec..a83f7e6e 100644 --- a/platformio/ide/projectgenerator.py +++ b/platformio/ide/projectgenerator.py @@ -13,23 +13,21 @@ # limitations under the License. import codecs -import json import os import re import sys from os.path import abspath, basename, expanduser, isdir, isfile, join, relpath import bottle -from click.testing import CliRunner -from platformio import exception, util -from platformio.commands.run import cli as cmd_run +from platformio import util from platformio.compat import WINDOWS, get_file_contents from platformio.proc import where_is_program from platformio.project.config import ProjectConfig from platformio.project.helpers import (get_project_lib_dir, get_project_libdeps_dir, - get_project_src_dir) + get_project_src_dir, + load_project_ide_data) class ProjectGenerator(object): @@ -39,57 +37,45 @@ class ProjectGenerator(object): self.ide = str(ide) self.env_name = env_name - self._tplvars = {} - self._gather_tplvars() - @staticmethod def get_supported_ides(): tpls_dir = join(util.get_source_dir(), "ide", "tpls") return sorted( [d for d in os.listdir(tpls_dir) if isdir(join(tpls_dir, d))]) - @util.memoized() - def get_project_env(self): - data = {} - config = ProjectConfig.get_instance( - join(self.project_dir, "platformio.ini")) - for env in config.envs(): - if self.env_name != env: - continue - data = config.items(env=env, as_dict=True) - data['env_name'] = self.env_name - return data + def _load_tplvars(self): + tpl_vars = {"env_name": self.env_name} + # default env configuration + tpl_vars.update( + ProjectConfig.get_instance(join( + self.project_dir, "platformio.ini")).items(env=self.env_name, + as_dict=True)) + # build data + tpl_vars.update( + load_project_ide_data(self.project_dir, self.env_name) or {}) - def get_project_build_data(self): - data = { - "defines": [], - "includes": [], - "cxx_path": None, - "prog_path": None - } - envdata = self.get_project_env() - if not envdata: - return data + with util.cd(self.project_dir): + tpl_vars.update({ + "project_name": basename(self.project_dir), + "src_files": self.get_src_files(), + "user_home_dir": abspath(expanduser("~")), + "project_dir": self.project_dir, + "project_src_dir": get_project_src_dir(), + "project_lib_dir": get_project_lib_dir(), + "project_libdeps_dir": join( + get_project_libdeps_dir(), self.env_name), + "systype": util.get_systype(), + "platformio_path": self._fix_os_path( + sys.argv[0] if isfile(sys.argv[0]) + else where_is_program("platformio")), + "env_pathsep": os.pathsep, + "env_path": self._fix_os_path(os.getenv("PATH")) + }) # yapf: disable + return tpl_vars - result = CliRunner().invoke(cmd_run, [ - "--project-dir", self.project_dir, "--environment", - envdata['env_name'], "--target", "idedata" - ]) - - if result.exit_code != 0 and not isinstance(result.exception, - exception.ReturnErrorCode): - raise result.exception - if '"includes":' not in result.output: - raise exception.PlatformioException(result.output) - - for line in result.output.split("\n"): - line = line.strip() - if line.startswith('{"') and line.endswith("}"): - data = json.loads(line) - return data - - def get_project_name(self): - return basename(self.project_dir) + @staticmethod + def _fix_os_path(path): + return (re.sub(r"[\\]+", '\\' * 4, path) if WINDOWS else path) def get_src_files(self): result = [] @@ -113,6 +99,7 @@ class ProjectGenerator(object): return tpls def generate(self): + tpl_vars = self._load_tplvars() for tpl_relpath, tpl_path in self.get_tpls(): dst_dir = self.project_dir if tpl_relpath: @@ -121,11 +108,12 @@ class ProjectGenerator(object): os.makedirs(dst_dir) file_name = basename(tpl_path)[:-4] - contents = self._render_tpl(tpl_path) + contents = self._render_tpl(tpl_path, tpl_vars) self._merge_contents(join(dst_dir, file_name), contents) - def _render_tpl(self, tpl_path): - return bottle.template(get_file_contents(tpl_path), **self._tplvars) + @staticmethod + def _render_tpl(tpl_path, tpl_vars): + return bottle.template(get_file_contents(tpl_path), **tpl_vars) @staticmethod def _merge_contents(dst_path, contents): @@ -133,28 +121,3 @@ class ProjectGenerator(object): return with codecs.open(dst_path, "w", encoding="utf8") as fp: fp.write(contents) - - def _gather_tplvars(self): - self._tplvars.update(self.get_project_env()) - self._tplvars.update(self.get_project_build_data()) - with util.cd(self.project_dir): - self._tplvars.update({ - "project_name": self.get_project_name(), - "src_files": self.get_src_files(), - "user_home_dir": abspath(expanduser("~")), - "project_dir": self.project_dir, - "project_src_dir": get_project_src_dir(), - "project_lib_dir": get_project_lib_dir(), - "project_libdeps_dir": join( - get_project_libdeps_dir(), self.env_name), - "systype": util.get_systype(), - "platformio_path": self._fix_os_path( - sys.argv[0] if isfile(sys.argv[0]) - else where_is_program("platformio")), - "env_pathsep": os.pathsep, - "env_path": self._fix_os_path(os.getenv("PATH")) - }) # yapf: disable - - @staticmethod - def _fix_os_path(path): - return (re.sub(r"[\\]+", '\\' * 4, path) if WINDOWS else path) diff --git a/platformio/project/helpers.py b/platformio/project/helpers.py index 71f73649..3c017666 100644 --- a/platformio/project/helpers.py +++ b/platformio/project/helpers.py @@ -12,13 +12,16 @@ # See the License for the specific language governing permissions and # limitations under the License. +import json import os from hashlib import sha1 from os import walk from os.path import (basename, dirname, expanduser, isdir, isfile, join, realpath, splitdrive) -from platformio import __version__ +from click.testing import CliRunner + +from platformio import __version__, exception from platformio.compat import WINDOWS, hashlib_encode_data from platformio.project.config import ProjectConfig @@ -179,3 +182,22 @@ def calculate_project_hash(): # A case of disk drive can differ... chunks_to_str = chunks_to_str.lower() return sha1(hashlib_encode_data(chunks_to_str)).hexdigest() + + +def load_project_ide_data(project_dir, env_name): + from platformio.commands.run import cli as cmd_run + result = CliRunner().invoke(cmd_run, [ + "--project-dir", project_dir, "--environment", env_name, "--target", + "idedata" + ]) + if result.exit_code != 0 and not isinstance(result.exception, + exception.ReturnErrorCode): + raise result.exception + if '"includes":' not in result.output: + raise exception.PlatformioException(result.output) + + for line in result.output.split("\n"): + line = line.strip() + if line.startswith('{"') and line.endswith("}"): + return json.loads(line) + return None