Use common IDE data loading for IDE and DEBUG

This commit is contained in:
Ivan Kravets
2019-06-03 19:20:10 +03:00
parent 4416c12747
commit d09964a897
8 changed files with 97 additions and 130 deletions

View File

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

View File

@ -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"],

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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