mirror of
https://github.com/platformio/platformio-core.git
synced 2025-07-30 10:07:14 +02:00
Refactor debugging configuration, add support for server_ready_pattern // Resolve #3401
This commit is contained in:
2
docs
2
docs
Submodule docs updated: 3293903cac...0487c24f93
@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import os
|
import os
|
||||||
|
import subprocess
|
||||||
|
|
||||||
import click
|
import click
|
||||||
|
|
||||||
@ -24,13 +25,14 @@ from platformio import app, exception, fs, proc
|
|||||||
from platformio.commands.platform import platform_install as cmd_platform_install
|
from platformio.commands.platform import platform_install as cmd_platform_install
|
||||||
from platformio.compat import WINDOWS
|
from platformio.compat import WINDOWS
|
||||||
from platformio.debug import helpers
|
from platformio.debug import helpers
|
||||||
|
from platformio.debug.config.factory import DebugConfigFactory
|
||||||
from platformio.debug.exception import DebugInvalidOptionsError
|
from platformio.debug.exception import DebugInvalidOptionsError
|
||||||
from platformio.debug.process.client import DebugClientProcess
|
from platformio.debug.process.client import DebugClientProcess
|
||||||
from platformio.platform.exception import UnknownPlatform
|
from platformio.platform.exception import UnknownPlatform
|
||||||
from platformio.platform.factory import PlatformFactory
|
from platformio.platform.factory import PlatformFactory
|
||||||
from platformio.project.config import ProjectConfig
|
from platformio.project.config import ProjectConfig
|
||||||
from platformio.project.exception import ProjectEnvsNotAvailableError
|
from platformio.project.exception import ProjectEnvsNotAvailableError
|
||||||
from platformio.project.helpers import is_platformio_project, load_project_ide_data
|
from platformio.project.helpers import is_platformio_project
|
||||||
|
|
||||||
|
|
||||||
@click.command(
|
@click.command(
|
||||||
@ -62,46 +64,40 @@ def cli(ctx, project_dir, project_conf, environment, verbose, interface, __unpro
|
|||||||
app.set_session_var("custom_project_conf", project_conf)
|
app.set_session_var("custom_project_conf", project_conf)
|
||||||
|
|
||||||
# use env variables from Eclipse or CLion
|
# use env variables from Eclipse or CLion
|
||||||
for sysenv in ("CWD", "PWD", "PLATFORMIO_PROJECT_DIR"):
|
for name in ("CWD", "PWD", "PLATFORMIO_PROJECT_DIR"):
|
||||||
if is_platformio_project(project_dir):
|
if is_platformio_project(project_dir):
|
||||||
break
|
break
|
||||||
if os.getenv(sysenv):
|
if os.getenv(name):
|
||||||
project_dir = os.getenv(sysenv)
|
project_dir = os.getenv(name)
|
||||||
|
|
||||||
with fs.cd(project_dir):
|
with fs.cd(project_dir):
|
||||||
config = ProjectConfig.get_instance(project_conf)
|
project_config = ProjectConfig.get_instance(project_conf)
|
||||||
config.validate(envs=[environment] if environment else None)
|
project_config.validate(envs=[environment] if environment else None)
|
||||||
|
env_name = environment or helpers.get_default_debug_env(project_config)
|
||||||
env_name = environment or helpers.get_default_debug_env(config)
|
|
||||||
env_options = config.items(env=env_name, as_dict=True)
|
|
||||||
if not set(env_options.keys()) >= set(["platform", "board"]):
|
|
||||||
raise ProjectEnvsNotAvailableError()
|
|
||||||
|
|
||||||
try:
|
|
||||||
platform = PlatformFactory.new(env_options["platform"])
|
|
||||||
except UnknownPlatform:
|
|
||||||
ctx.invoke(
|
|
||||||
cmd_platform_install,
|
|
||||||
platforms=[env_options["platform"]],
|
|
||||||
skip_default_package=True,
|
|
||||||
)
|
|
||||||
platform = PlatformFactory.new(env_options["platform"])
|
|
||||||
|
|
||||||
debug_options = helpers.configure_initial_debug_options(platform, env_options)
|
|
||||||
assert debug_options
|
|
||||||
|
|
||||||
if not interface:
|
if not interface:
|
||||||
return helpers.predebug_project(ctx, project_dir, env_name, False, verbose)
|
return helpers.predebug_project(ctx, project_dir, env_name, False, verbose)
|
||||||
|
|
||||||
ide_data = load_project_ide_data(project_dir, env_name)
|
env_options = project_config.items(env=env_name, as_dict=True)
|
||||||
if not ide_data:
|
if not set(env_options.keys()) >= set(["platform", "board"]):
|
||||||
raise DebugInvalidOptionsError("Could not load a build configuration")
|
raise ProjectEnvsNotAvailableError()
|
||||||
|
|
||||||
|
try:
|
||||||
|
platform = PlatformFactory.new(env_options["platform"])
|
||||||
|
except UnknownPlatform:
|
||||||
|
ctx.invoke(
|
||||||
|
cmd_platform_install,
|
||||||
|
platforms=[env_options["platform"]],
|
||||||
|
skip_default_package=True,
|
||||||
|
)
|
||||||
|
platform = PlatformFactory.new(env_options["platform"])
|
||||||
|
|
||||||
|
debug_config = DebugConfigFactory.new(platform, project_config, env_name)
|
||||||
|
|
||||||
if "--version" in __unprocessed:
|
if "--version" in __unprocessed:
|
||||||
result = proc.exec_command([ide_data["gdb_path"], "--version"])
|
return subprocess.run(
|
||||||
if result["returncode"] == 0:
|
[debug_config.client_executable_path, "--version"], check=True
|
||||||
return click.echo(result["out"])
|
)
|
||||||
raise exception.PlatformioException("\n".join([result["out"], result["err"]]))
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
fs.ensure_udev_rules()
|
fs.ensure_udev_rules()
|
||||||
@ -113,29 +109,23 @@ def cli(ctx, project_dir, project_conf, environment, verbose, interface, __unpro
|
|||||||
nl=False,
|
nl=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
|
||||||
debug_options = platform.configure_debug_options(debug_options, ide_data)
|
|
||||||
except NotImplementedError:
|
|
||||||
# legacy for ESP32 dev-platform <=2.0.0
|
|
||||||
debug_options["load_cmds"] = helpers.configure_esp32_load_cmds(
|
|
||||||
debug_options, ide_data
|
|
||||||
)
|
|
||||||
|
|
||||||
rebuild_prog = False
|
rebuild_prog = False
|
||||||
preload = debug_options["load_cmds"] == ["preload"]
|
preload = debug_config.load_cmds == ["preload"]
|
||||||
load_mode = debug_options["load_mode"]
|
load_mode = debug_config.load_mode
|
||||||
if load_mode == "always":
|
if load_mode == "always":
|
||||||
rebuild_prog = preload or not helpers.has_debug_symbols(ide_data["prog_path"])
|
rebuild_prog = preload or not helpers.has_debug_symbols(
|
||||||
|
debug_config.program_path
|
||||||
|
)
|
||||||
elif load_mode == "modified":
|
elif load_mode == "modified":
|
||||||
rebuild_prog = helpers.is_prog_obsolete(
|
rebuild_prog = helpers.is_prog_obsolete(
|
||||||
ide_data["prog_path"]
|
debug_config.program_path
|
||||||
) or not helpers.has_debug_symbols(ide_data["prog_path"])
|
) or not helpers.has_debug_symbols(debug_config.program_path)
|
||||||
else:
|
else:
|
||||||
rebuild_prog = not os.path.isfile(ide_data["prog_path"])
|
rebuild_prog = not os.path.isfile(debug_config.program_path)
|
||||||
|
|
||||||
if preload or (not rebuild_prog and load_mode != "always"):
|
if preload or (not rebuild_prog and load_mode != "always"):
|
||||||
# don't load firmware through debug server
|
# don't load firmware through debug server
|
||||||
debug_options["load_cmds"] = []
|
debug_config.load_cmds = []
|
||||||
|
|
||||||
if rebuild_prog:
|
if rebuild_prog:
|
||||||
if helpers.is_gdbmi_mode():
|
if helpers.is_gdbmi_mode():
|
||||||
@ -155,15 +145,15 @@ def cli(ctx, project_dir, project_conf, environment, verbose, interface, __unpro
|
|||||||
|
|
||||||
# save SHA sum of newly created prog
|
# save SHA sum of newly created prog
|
||||||
if load_mode == "modified":
|
if load_mode == "modified":
|
||||||
helpers.is_prog_obsolete(ide_data["prog_path"])
|
helpers.is_prog_obsolete(debug_config.program_path)
|
||||||
|
|
||||||
if not os.path.isfile(ide_data["prog_path"]):
|
if not os.path.isfile(debug_config.program_path):
|
||||||
raise DebugInvalidOptionsError("Program/firmware is missed")
|
raise DebugInvalidOptionsError("Program/firmware is missed")
|
||||||
|
|
||||||
loop = asyncio.ProactorEventLoop() if WINDOWS else asyncio.get_event_loop()
|
loop = asyncio.ProactorEventLoop() if WINDOWS else asyncio.get_event_loop()
|
||||||
asyncio.set_event_loop(loop)
|
asyncio.set_event_loop(loop)
|
||||||
client = DebugClientProcess(project_dir, __unprocessed, debug_options, env_options)
|
client = DebugClientProcess(project_dir, debug_config)
|
||||||
coro = client.run(ide_data["gdb_path"], ide_data["prog_path"])
|
coro = client.run(__unprocessed)
|
||||||
loop.run_until_complete(coro)
|
loop.run_until_complete(coro)
|
||||||
if WINDOWS:
|
if WINDOWS:
|
||||||
# an issue with asyncio executor and STIDIN, it cannot be closed gracefully
|
# an issue with asyncio executor and STIDIN, it cannot be closed gracefully
|
||||||
|
13
platformio/debug/config/__init__.py
Normal file
13
platformio/debug/config/__init__.py
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# Copyright (c) 2014-present PlatformIO <contact@platformio.org>
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
227
platformio/debug/config/base.py
Normal file
227
platformio/debug/config/base.py
Normal file
@ -0,0 +1,227 @@
|
|||||||
|
# Copyright (c) 2014-present PlatformIO <contact@platformio.org>
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from platformio import fs, proc, util
|
||||||
|
from platformio.compat import string_types
|
||||||
|
from platformio.debug.exception import DebugInvalidOptionsError
|
||||||
|
from platformio.debug.helpers import reveal_debug_port
|
||||||
|
from platformio.project.config import ProjectConfig
|
||||||
|
from platformio.project.helpers import get_project_core_dir, load_project_ide_data
|
||||||
|
from platformio.project.options import ProjectOptions
|
||||||
|
|
||||||
|
|
||||||
|
class DebugConfigBase: # pylint: disable=too-many-instance-attributes
|
||||||
|
def __init__(self, platform, project_config, env_name):
|
||||||
|
self.platform = platform
|
||||||
|
self.project_config = project_config
|
||||||
|
self.env_name = env_name
|
||||||
|
self.env_options = project_config.items(env=env_name, as_dict=True)
|
||||||
|
self.board_config = platform.board_config(self.env_options["board"])
|
||||||
|
self.build_data = self._load_build_data()
|
||||||
|
self.tool_name = self.board_config.get_debug_tool_name(
|
||||||
|
self.env_options.get("debug_tool")
|
||||||
|
)
|
||||||
|
self.tool_settings = (
|
||||||
|
self.board_config.get("debug", {}).get("tools", {}).get(self.tool_name, {})
|
||||||
|
)
|
||||||
|
|
||||||
|
self._load_cmds = None
|
||||||
|
self._port = None
|
||||||
|
|
||||||
|
self.server = self._configure_server()
|
||||||
|
|
||||||
|
try:
|
||||||
|
platform.configure_debug_session(self)
|
||||||
|
except NotImplementedError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def cleanup_cmds(items):
|
||||||
|
items = ProjectConfig.parse_multi_values(items)
|
||||||
|
return ["$LOAD_CMDS" if item == "$LOAD_CMD" else item for item in items]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def program_path(self):
|
||||||
|
return self.build_data["prog_path"]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def client_executable_path(self):
|
||||||
|
return self.build_data["gdb_path"]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def load_cmds(self):
|
||||||
|
if self._load_cmds is not None:
|
||||||
|
return self._load_cmds
|
||||||
|
result = self.env_options.get("debug_load_cmds")
|
||||||
|
if not result:
|
||||||
|
result = self.tool_settings.get("load_cmds")
|
||||||
|
if not result:
|
||||||
|
# legacy
|
||||||
|
result = self.tool_settings.get("load_cmd")
|
||||||
|
if not result:
|
||||||
|
result = ProjectOptions["env.debug_load_cmds"].default
|
||||||
|
return self.cleanup_cmds(result)
|
||||||
|
|
||||||
|
@load_cmds.setter
|
||||||
|
def load_cmds(self, cmds):
|
||||||
|
self._load_cmds = cmds
|
||||||
|
|
||||||
|
@property
|
||||||
|
def load_mode(self):
|
||||||
|
result = self.env_options.get("debug_load_mode")
|
||||||
|
if not result:
|
||||||
|
result = self.tool_settings.get("load_mode")
|
||||||
|
return result or ProjectOptions["env.debug_load_mode"].default
|
||||||
|
|
||||||
|
@property
|
||||||
|
def init_break(self):
|
||||||
|
result = self.env_options.get("debug_init_break")
|
||||||
|
if not result:
|
||||||
|
result = self.tool_settings.get("init_break")
|
||||||
|
return result or ProjectOptions["env.debug_init_break"].default
|
||||||
|
|
||||||
|
@property
|
||||||
|
def init_cmds(self):
|
||||||
|
return self.env_options.get(
|
||||||
|
"debug_init_cmds", self.tool_settings.get("init_cmds")
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def extra_cmds(self):
|
||||||
|
return self.cleanup_cmds(
|
||||||
|
self.env_options.get("debug_extra_cmds")
|
||||||
|
) + self.cleanup_cmds(self.tool_settings.get("extra_cmds"))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def port(self):
|
||||||
|
return reveal_debug_port(
|
||||||
|
self.env_options.get("debug_port", self.tool_settings.get("port"))
|
||||||
|
or self._port,
|
||||||
|
self.tool_name,
|
||||||
|
self.tool_settings,
|
||||||
|
)
|
||||||
|
|
||||||
|
@port.setter
|
||||||
|
def port(self, value):
|
||||||
|
self._port = value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def upload_protocol(self):
|
||||||
|
return self.env_options.get(
|
||||||
|
"upload_protocol", self.board_config.get("upload", {}).get("protocol")
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def speed(self):
|
||||||
|
return self.env_options.get("debug_speed", self.tool_settings.get("speed"))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def server_ready_pattern(self):
|
||||||
|
return (self.server or {}).get("ready_pattern")
|
||||||
|
|
||||||
|
def _load_build_data(self):
|
||||||
|
data = load_project_ide_data(self.project_config.path, self.env_name)
|
||||||
|
if data:
|
||||||
|
return data
|
||||||
|
raise DebugInvalidOptionsError("Could not load a build configuration")
|
||||||
|
|
||||||
|
def _configure_server(self):
|
||||||
|
result = None
|
||||||
|
# specific server per a system
|
||||||
|
if isinstance(self.tool_settings.get("server", {}), list):
|
||||||
|
for item in self.tool_settings["server"][:]:
|
||||||
|
self.tool_settings["server"] = item
|
||||||
|
if util.get_systype() in item.get("system", []):
|
||||||
|
break
|
||||||
|
|
||||||
|
# user overwrites debug server
|
||||||
|
if self.env_options.get("debug_server"):
|
||||||
|
result = {
|
||||||
|
"cwd": None,
|
||||||
|
"executable": None,
|
||||||
|
"arguments": self.env_options.get("debug_server"),
|
||||||
|
}
|
||||||
|
result["executable"] = result["arguments"][0]
|
||||||
|
result["arguments"] = result["arguments"][1:]
|
||||||
|
elif "server" in self.tool_settings:
|
||||||
|
result = self.tool_settings["server"]
|
||||||
|
server_package = result.get("package")
|
||||||
|
server_package_dir = (
|
||||||
|
self.platform.get_package_dir(server_package)
|
||||||
|
if server_package
|
||||||
|
else None
|
||||||
|
)
|
||||||
|
if server_package and not server_package_dir:
|
||||||
|
self.platform.install_packages(
|
||||||
|
with_packages=[server_package],
|
||||||
|
skip_default_package=True,
|
||||||
|
silent=True,
|
||||||
|
)
|
||||||
|
server_package_dir = self.platform.get_package_dir(server_package)
|
||||||
|
result.update(
|
||||||
|
dict(
|
||||||
|
cwd=server_package_dir if server_package else None,
|
||||||
|
executable=result.get("executable"),
|
||||||
|
arguments=[
|
||||||
|
a.replace("$PACKAGE_DIR", server_package_dir)
|
||||||
|
if server_package_dir
|
||||||
|
else a
|
||||||
|
for a in result.get("arguments", [])
|
||||||
|
],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return self.reveal_patterns(result) if result else None
|
||||||
|
|
||||||
|
def get_init_script(self, debugger):
|
||||||
|
try:
|
||||||
|
return getattr(self, "%s_INIT_SCRIPT" % debugger.upper())
|
||||||
|
except AttributeError:
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def reveal_patterns(self, source, recursive=True):
|
||||||
|
patterns = {
|
||||||
|
"PLATFORMIO_CORE_DIR": get_project_core_dir(),
|
||||||
|
"PYTHONEXE": proc.get_pythonexe_path(),
|
||||||
|
"PROJECT_DIR": self.project_config.path,
|
||||||
|
"PROG_PATH": self.program_path,
|
||||||
|
"PROG_DIR": os.path.dirname(self.program_path),
|
||||||
|
"PROG_NAME": os.path.basename(os.path.splitext(self.program_path)[0]),
|
||||||
|
"DEBUG_PORT": self.port,
|
||||||
|
"UPLOAD_PROTOCOL": self.upload_protocol,
|
||||||
|
"INIT_BREAK": self.init_break or "",
|
||||||
|
"LOAD_CMDS": "\n".join(self.load_cmds or []),
|
||||||
|
}
|
||||||
|
for key, value in patterns.items():
|
||||||
|
if key.endswith(("_DIR", "_PATH")):
|
||||||
|
patterns[key] = fs.to_unix_path(value)
|
||||||
|
|
||||||
|
def _replace(text):
|
||||||
|
for key, value in patterns.items():
|
||||||
|
pattern = "$%s" % key
|
||||||
|
text = text.replace(pattern, value or "")
|
||||||
|
return text
|
||||||
|
|
||||||
|
if isinstance(source, string_types):
|
||||||
|
source = _replace(source)
|
||||||
|
elif isinstance(source, (list, dict)):
|
||||||
|
items = enumerate(source) if isinstance(source, list) else source.items()
|
||||||
|
for key, value in items:
|
||||||
|
if isinstance(value, string_types):
|
||||||
|
source[key] = _replace(value)
|
||||||
|
elif isinstance(value, (list, dict)) and recursive:
|
||||||
|
source[key] = self.reveal_patterns(value, patterns)
|
||||||
|
|
||||||
|
return source
|
49
platformio/debug/config/blackmagic.py
Normal file
49
platformio/debug/config/blackmagic.py
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
# Copyright (c) 2014-present PlatformIO <contact@platformio.org>
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
from platformio.debug.config.base import DebugConfigBase
|
||||||
|
|
||||||
|
|
||||||
|
class BlackmagicDebugConfig(DebugConfigBase):
|
||||||
|
|
||||||
|
GDB_INIT_SCRIPT = """
|
||||||
|
define pio_reset_halt_target
|
||||||
|
set language c
|
||||||
|
set *0xE000ED0C = 0x05FA0004
|
||||||
|
set $busy = (*0xE000ED0C & 0x4)
|
||||||
|
while ($busy)
|
||||||
|
set $busy = (*0xE000ED0C & 0x4)
|
||||||
|
end
|
||||||
|
set language auto
|
||||||
|
end
|
||||||
|
|
||||||
|
define pio_reset_run_target
|
||||||
|
pio_reset_halt_target
|
||||||
|
end
|
||||||
|
|
||||||
|
target extended-remote $DEBUG_PORT
|
||||||
|
monitor swdp_scan
|
||||||
|
attach 1
|
||||||
|
set mem inaccessible-by-default off
|
||||||
|
$LOAD_CMDS
|
||||||
|
$INIT_BREAK
|
||||||
|
|
||||||
|
set language c
|
||||||
|
set *0xE000ED0C = 0x05FA0004
|
||||||
|
set $busy = (*0xE000ED0C & 0x4)
|
||||||
|
while ($busy)
|
||||||
|
set $busy = (*0xE000ED0C & 0x4)
|
||||||
|
end
|
||||||
|
set language auto
|
||||||
|
"""
|
41
platformio/debug/config/factory.py
Normal file
41
platformio/debug/config/factory.py
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
# Copyright (c) 2014-present PlatformIO <contact@platformio.org>
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
import importlib
|
||||||
|
import re
|
||||||
|
|
||||||
|
from platformio.debug.config.generic import GenericDebugConfig
|
||||||
|
|
||||||
|
|
||||||
|
class DebugConfigFactory(object):
|
||||||
|
@staticmethod
|
||||||
|
def get_clsname(name):
|
||||||
|
name = re.sub(r"[^\da-z\_\-]+", "", name, flags=re.I)
|
||||||
|
return "%s%sDebugConfig" % (name.upper()[0], name.lower()[1:])
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def new(cls, platform, project_config, env_name):
|
||||||
|
board_config = platform.board_config(
|
||||||
|
project_config.get("env:" + env_name, "board")
|
||||||
|
)
|
||||||
|
tool_name = board_config.get_debug_tool_name(
|
||||||
|
project_config.get("env:" + env_name, "debug_tool")
|
||||||
|
)
|
||||||
|
config_cls = None
|
||||||
|
try:
|
||||||
|
mod = importlib.import_module("platformio.debug.config.%s" % tool_name)
|
||||||
|
config_cls = getattr(mod, cls.get_clsname(tool_name))
|
||||||
|
except ModuleNotFoundError:
|
||||||
|
config_cls = GenericDebugConfig
|
||||||
|
return config_cls(platform, project_config, env_name)
|
38
platformio/debug/config/generic.py
Normal file
38
platformio/debug/config/generic.py
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
# Copyright (c) 2014-present PlatformIO <contact@platformio.org>
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
from platformio.debug.config.base import DebugConfigBase
|
||||||
|
|
||||||
|
|
||||||
|
class GenericDebugConfig(DebugConfigBase):
|
||||||
|
|
||||||
|
GDB_INIT_SCRIPT = """
|
||||||
|
define pio_reset_halt_target
|
||||||
|
monitor reset halt
|
||||||
|
end
|
||||||
|
|
||||||
|
define pio_reset_run_target
|
||||||
|
monitor reset
|
||||||
|
end
|
||||||
|
|
||||||
|
target extended-remote $DEBUG_PORT
|
||||||
|
monitor init
|
||||||
|
$LOAD_CMDS
|
||||||
|
pio_reset_halt_target
|
||||||
|
$INIT_BREAK
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(GenericDebugConfig, self).__init__(*args, **kwargs)
|
||||||
|
self.port = ":3333"
|
48
platformio/debug/config/jlink.py
Normal file
48
platformio/debug/config/jlink.py
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
# Copyright (c) 2014-present PlatformIO <contact@platformio.org>
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
from platformio.debug.config.base import DebugConfigBase
|
||||||
|
|
||||||
|
|
||||||
|
class JlinkDebugConfig(DebugConfigBase):
|
||||||
|
|
||||||
|
GDB_INIT_SCRIPT = """
|
||||||
|
define pio_reset_halt_target
|
||||||
|
monitor reset
|
||||||
|
monitor halt
|
||||||
|
end
|
||||||
|
|
||||||
|
define pio_reset_run_target
|
||||||
|
monitor clrbp
|
||||||
|
monitor reset
|
||||||
|
monitor go
|
||||||
|
end
|
||||||
|
|
||||||
|
target extended-remote $DEBUG_PORT
|
||||||
|
monitor clrbp
|
||||||
|
monitor speed auto
|
||||||
|
pio_reset_halt_target
|
||||||
|
$LOAD_CMDS
|
||||||
|
$INIT_BREAK
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(JlinkDebugConfig, self).__init__(*args, **kwargs)
|
||||||
|
self.port = ":2331"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def server_ready_pattern(self):
|
||||||
|
return super(JlinkDebugConfig, self).server_ready_pattern or (
|
||||||
|
"Waiting for GDB connection"
|
||||||
|
)
|
36
platformio/debug/config/mspdebug.py
Normal file
36
platformio/debug/config/mspdebug.py
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
# Copyright (c) 2014-present PlatformIO <contact@platformio.org>
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
from platformio.debug.config.base import DebugConfigBase
|
||||||
|
|
||||||
|
|
||||||
|
class MspdebugDebugConfig(DebugConfigBase):
|
||||||
|
|
||||||
|
GDB_INIT_SCRIPT = """
|
||||||
|
define pio_reset_halt_target
|
||||||
|
end
|
||||||
|
|
||||||
|
define pio_reset_run_target
|
||||||
|
end
|
||||||
|
|
||||||
|
target extended-remote $DEBUG_PORT
|
||||||
|
monitor erase
|
||||||
|
$LOAD_CMDS
|
||||||
|
pio_reset_halt_target
|
||||||
|
$INIT_BREAK
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(MspdebugDebugConfig, self).__init__(*args, **kwargs)
|
||||||
|
self.port = ":2000"
|
37
platformio/debug/config/qemu.py
Normal file
37
platformio/debug/config/qemu.py
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
# Copyright (c) 2014-present PlatformIO <contact@platformio.org>
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
from platformio.debug.config.base import DebugConfigBase
|
||||||
|
|
||||||
|
|
||||||
|
class QemuDebugConfig(DebugConfigBase):
|
||||||
|
|
||||||
|
GDB_INIT_SCRIPT = """
|
||||||
|
define pio_reset_halt_target
|
||||||
|
monitor system_reset
|
||||||
|
end
|
||||||
|
|
||||||
|
define pio_reset_run_target
|
||||||
|
monitor system_reset
|
||||||
|
end
|
||||||
|
|
||||||
|
target extended-remote $DEBUG_PORT
|
||||||
|
$LOAD_CMDS
|
||||||
|
pio_reset_halt_target
|
||||||
|
$INIT_BREAK
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(QemuDebugConfig, self).__init__(*args, **kwargs)
|
||||||
|
self.port = ":1234"
|
45
platformio/debug/config/renode.py
Normal file
45
platformio/debug/config/renode.py
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
# Copyright (c) 2014-present PlatformIO <contact@platformio.org>
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
from platformio.debug.config.base import DebugConfigBase
|
||||||
|
|
||||||
|
|
||||||
|
class RenodeDebugConfig(DebugConfigBase):
|
||||||
|
|
||||||
|
GDB_INIT_SCRIPT = """
|
||||||
|
define pio_reset_halt_target
|
||||||
|
monitor machine Reset
|
||||||
|
$LOAD_CMDS
|
||||||
|
monitor start
|
||||||
|
end
|
||||||
|
|
||||||
|
define pio_reset_run_target
|
||||||
|
pio_reset_halt_target
|
||||||
|
end
|
||||||
|
|
||||||
|
target extended-remote $DEBUG_PORT
|
||||||
|
$LOAD_CMDS
|
||||||
|
$INIT_BREAK
|
||||||
|
monitor start
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(RenodeDebugConfig, self).__init__(*args, **kwargs)
|
||||||
|
self.port = ":3333"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def server_ready_pattern(self):
|
||||||
|
return super(RenodeDebugConfig, self).server_ready_pattern or (
|
||||||
|
"GDB server with all CPUs started on port"
|
||||||
|
)
|
@ -20,13 +20,11 @@ from hashlib import sha1
|
|||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from os.path import isfile
|
from os.path import isfile
|
||||||
|
|
||||||
from platformio import fs, util
|
from platformio import util
|
||||||
from platformio.commands import PlatformioCLI
|
from platformio.commands import PlatformioCLI
|
||||||
from platformio.commands.run.command import cli as cmd_run
|
from platformio.commands.run.command import cli as cmd_run
|
||||||
from platformio.compat import is_bytes
|
from platformio.compat import is_bytes
|
||||||
from platformio.debug.exception import DebugInvalidOptionsError
|
from platformio.debug.exception import DebugInvalidOptionsError
|
||||||
from platformio.project.config import ProjectConfig
|
|
||||||
from platformio.project.options import ProjectOptions
|
|
||||||
|
|
||||||
|
|
||||||
class GDBMIConsoleStream(BytesIO): # pylint: disable=too-few-public-methods
|
class GDBMIConsoleStream(BytesIO): # pylint: disable=too-few-public-methods
|
||||||
@ -86,130 +84,6 @@ def predebug_project(ctx, project_dir, env_name, preload, verbose):
|
|||||||
time.sleep(5)
|
time.sleep(5)
|
||||||
|
|
||||||
|
|
||||||
def configure_initial_debug_options(platform, env_options):
|
|
||||||
def _cleanup_cmds(items):
|
|
||||||
items = ProjectConfig.parse_multi_values(items)
|
|
||||||
return ["$LOAD_CMDS" if item == "$LOAD_CMD" else item for item in items]
|
|
||||||
|
|
||||||
board_config = platform.board_config(env_options["board"])
|
|
||||||
tool_name = board_config.get_debug_tool_name(env_options.get("debug_tool"))
|
|
||||||
tool_settings = board_config.get("debug", {}).get("tools", {}).get(tool_name, {})
|
|
||||||
server_options = None
|
|
||||||
|
|
||||||
# specific server per a system
|
|
||||||
if isinstance(tool_settings.get("server", {}), list):
|
|
||||||
for item in tool_settings["server"][:]:
|
|
||||||
tool_settings["server"] = item
|
|
||||||
if util.get_systype() in item.get("system", []):
|
|
||||||
break
|
|
||||||
|
|
||||||
# user overwrites debug server
|
|
||||||
if env_options.get("debug_server"):
|
|
||||||
server_options = {
|
|
||||||
"cwd": None,
|
|
||||||
"executable": None,
|
|
||||||
"arguments": env_options.get("debug_server"),
|
|
||||||
}
|
|
||||||
server_options["executable"] = server_options["arguments"][0]
|
|
||||||
server_options["arguments"] = server_options["arguments"][1:]
|
|
||||||
elif "server" in tool_settings:
|
|
||||||
server_options = tool_settings["server"]
|
|
||||||
server_package = server_options.get("package")
|
|
||||||
server_package_dir = (
|
|
||||||
platform.get_package_dir(server_package) if server_package else None
|
|
||||||
)
|
|
||||||
if server_package and not server_package_dir:
|
|
||||||
platform.install_packages(
|
|
||||||
with_packages=[server_package], skip_default_package=True, silent=True
|
|
||||||
)
|
|
||||||
server_package_dir = platform.get_package_dir(server_package)
|
|
||||||
server_options.update(
|
|
||||||
dict(
|
|
||||||
cwd=server_package_dir if server_package else None,
|
|
||||||
executable=server_options.get("executable"),
|
|
||||||
arguments=[
|
|
||||||
a.replace("$PACKAGE_DIR", server_package_dir)
|
|
||||||
if server_package_dir
|
|
||||||
else a
|
|
||||||
for a in server_options.get("arguments", [])
|
|
||||||
],
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
extra_cmds = _cleanup_cmds(env_options.get("debug_extra_cmds"))
|
|
||||||
extra_cmds.extend(_cleanup_cmds(tool_settings.get("extra_cmds")))
|
|
||||||
result = dict(
|
|
||||||
tool=tool_name,
|
|
||||||
upload_protocol=env_options.get(
|
|
||||||
"upload_protocol", board_config.get("upload", {}).get("protocol")
|
|
||||||
),
|
|
||||||
load_cmds=_cleanup_cmds(
|
|
||||||
env_options.get(
|
|
||||||
"debug_load_cmds",
|
|
||||||
tool_settings.get(
|
|
||||||
"load_cmds",
|
|
||||||
tool_settings.get(
|
|
||||||
"load_cmd", ProjectOptions["env.debug_load_cmds"].default
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
),
|
|
||||||
load_mode=env_options.get(
|
|
||||||
"debug_load_mode",
|
|
||||||
tool_settings.get(
|
|
||||||
"load_mode", ProjectOptions["env.debug_load_mode"].default
|
|
||||||
),
|
|
||||||
),
|
|
||||||
init_break=env_options.get(
|
|
||||||
"debug_init_break",
|
|
||||||
tool_settings.get(
|
|
||||||
"init_break", ProjectOptions["env.debug_init_break"].default
|
|
||||||
),
|
|
||||||
),
|
|
||||||
init_cmds=_cleanup_cmds(
|
|
||||||
env_options.get("debug_init_cmds", tool_settings.get("init_cmds"))
|
|
||||||
),
|
|
||||||
extra_cmds=extra_cmds,
|
|
||||||
require_debug_port=tool_settings.get("require_debug_port", False),
|
|
||||||
port=reveal_debug_port(
|
|
||||||
env_options.get("debug_port", tool_settings.get("port")),
|
|
||||||
tool_name,
|
|
||||||
tool_settings,
|
|
||||||
),
|
|
||||||
speed=env_options.get("debug_speed", tool_settings.get("speed")),
|
|
||||||
server=server_options,
|
|
||||||
)
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
def configure_esp32_load_cmds(debug_options, configuration):
|
|
||||||
"""
|
|
||||||
DEPRECATED: Moved to ESP32 dev-platform
|
|
||||||
See platform.py::configure_debug_options
|
|
||||||
"""
|
|
||||||
flash_images = configuration.get("extra", {}).get("flash_images")
|
|
||||||
ignore_conds = [
|
|
||||||
debug_options["load_cmds"] != ["load"],
|
|
||||||
"xtensa-esp32" not in configuration.get("cc_path", ""),
|
|
||||||
not flash_images,
|
|
||||||
not all(isfile(item["path"]) for item in flash_images),
|
|
||||||
]
|
|
||||||
if any(ignore_conds):
|
|
||||||
return debug_options["load_cmds"]
|
|
||||||
|
|
||||||
mon_cmds = [
|
|
||||||
'monitor program_esp32 "{{{path}}}" {offset} verify'.format(
|
|
||||||
path=fs.to_unix_path(item["path"]), offset=item["offset"]
|
|
||||||
)
|
|
||||||
for item in flash_images
|
|
||||||
]
|
|
||||||
mon_cmds.append(
|
|
||||||
'monitor program_esp32 "{%s.bin}" 0x10000 verify'
|
|
||||||
% fs.to_unix_path(configuration["prog_path"][:-4])
|
|
||||||
)
|
|
||||||
return mon_cmds
|
|
||||||
|
|
||||||
|
|
||||||
def has_debug_symbols(prog_path):
|
def has_debug_symbols(prog_path):
|
||||||
if not isfile(prog_path):
|
if not isfile(prog_path):
|
||||||
return False
|
return False
|
||||||
|
@ -1,161 +0,0 @@
|
|||||||
# Copyright (c) 2014-present PlatformIO <contact@platformio.org>
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
|
|
||||||
GDB_DEFAULT_INIT_CONFIG = """
|
|
||||||
define pio_reset_halt_target
|
|
||||||
monitor reset halt
|
|
||||||
end
|
|
||||||
|
|
||||||
define pio_reset_run_target
|
|
||||||
monitor reset
|
|
||||||
end
|
|
||||||
|
|
||||||
target extended-remote $DEBUG_PORT
|
|
||||||
monitor init
|
|
||||||
$LOAD_CMDS
|
|
||||||
pio_reset_halt_target
|
|
||||||
$INIT_BREAK
|
|
||||||
"""
|
|
||||||
|
|
||||||
GDB_STUTIL_INIT_CONFIG = """
|
|
||||||
define pio_reset_halt_target
|
|
||||||
monitor reset
|
|
||||||
monitor halt
|
|
||||||
end
|
|
||||||
|
|
||||||
define pio_reset_run_target
|
|
||||||
monitor reset
|
|
||||||
end
|
|
||||||
|
|
||||||
target extended-remote $DEBUG_PORT
|
|
||||||
$LOAD_CMDS
|
|
||||||
pio_reset_halt_target
|
|
||||||
$INIT_BREAK
|
|
||||||
"""
|
|
||||||
|
|
||||||
GDB_JLINK_INIT_CONFIG = """
|
|
||||||
define pio_reset_halt_target
|
|
||||||
monitor reset
|
|
||||||
monitor halt
|
|
||||||
end
|
|
||||||
|
|
||||||
define pio_reset_run_target
|
|
||||||
monitor clrbp
|
|
||||||
monitor reset
|
|
||||||
monitor go
|
|
||||||
end
|
|
||||||
|
|
||||||
target extended-remote $DEBUG_PORT
|
|
||||||
monitor clrbp
|
|
||||||
monitor speed auto
|
|
||||||
pio_reset_halt_target
|
|
||||||
$LOAD_CMDS
|
|
||||||
$INIT_BREAK
|
|
||||||
"""
|
|
||||||
|
|
||||||
GDB_BLACKMAGIC_INIT_CONFIG = """
|
|
||||||
define pio_reset_halt_target
|
|
||||||
set language c
|
|
||||||
set *0xE000ED0C = 0x05FA0004
|
|
||||||
set $busy = (*0xE000ED0C & 0x4)
|
|
||||||
while ($busy)
|
|
||||||
set $busy = (*0xE000ED0C & 0x4)
|
|
||||||
end
|
|
||||||
set language auto
|
|
||||||
end
|
|
||||||
|
|
||||||
define pio_reset_run_target
|
|
||||||
pio_reset_halt_target
|
|
||||||
end
|
|
||||||
|
|
||||||
target extended-remote $DEBUG_PORT
|
|
||||||
monitor swdp_scan
|
|
||||||
attach 1
|
|
||||||
set mem inaccessible-by-default off
|
|
||||||
$LOAD_CMDS
|
|
||||||
$INIT_BREAK
|
|
||||||
|
|
||||||
set language c
|
|
||||||
set *0xE000ED0C = 0x05FA0004
|
|
||||||
set $busy = (*0xE000ED0C & 0x4)
|
|
||||||
while ($busy)
|
|
||||||
set $busy = (*0xE000ED0C & 0x4)
|
|
||||||
end
|
|
||||||
set language auto
|
|
||||||
"""
|
|
||||||
|
|
||||||
GDB_MSPDEBUG_INIT_CONFIG = """
|
|
||||||
define pio_reset_halt_target
|
|
||||||
end
|
|
||||||
|
|
||||||
define pio_reset_run_target
|
|
||||||
end
|
|
||||||
|
|
||||||
target extended-remote $DEBUG_PORT
|
|
||||||
monitor erase
|
|
||||||
$LOAD_CMDS
|
|
||||||
pio_reset_halt_target
|
|
||||||
$INIT_BREAK
|
|
||||||
"""
|
|
||||||
|
|
||||||
GDB_QEMU_INIT_CONFIG = """
|
|
||||||
define pio_reset_halt_target
|
|
||||||
monitor system_reset
|
|
||||||
end
|
|
||||||
|
|
||||||
define pio_reset_run_target
|
|
||||||
monitor system_reset
|
|
||||||
end
|
|
||||||
|
|
||||||
target extended-remote $DEBUG_PORT
|
|
||||||
$LOAD_CMDS
|
|
||||||
pio_reset_halt_target
|
|
||||||
$INIT_BREAK
|
|
||||||
"""
|
|
||||||
|
|
||||||
GDB_RENODE_INIT_CONFIG = """
|
|
||||||
define pio_reset_halt_target
|
|
||||||
monitor machine Reset
|
|
||||||
$LOAD_CMDS
|
|
||||||
monitor start
|
|
||||||
end
|
|
||||||
|
|
||||||
define pio_reset_run_target
|
|
||||||
pio_reset_halt_target
|
|
||||||
end
|
|
||||||
|
|
||||||
target extended-remote $DEBUG_PORT
|
|
||||||
$LOAD_CMDS
|
|
||||||
$INIT_BREAK
|
|
||||||
monitor start
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
TOOL_TO_CONFIG = {
|
|
||||||
"jlink": GDB_JLINK_INIT_CONFIG,
|
|
||||||
"mspdebug": GDB_MSPDEBUG_INIT_CONFIG,
|
|
||||||
"qemu": GDB_QEMU_INIT_CONFIG,
|
|
||||||
"blackmagic": GDB_BLACKMAGIC_INIT_CONFIG,
|
|
||||||
"renode": GDB_RENODE_INIT_CONFIG,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def get_gdb_init_config(debug_options):
|
|
||||||
tool = debug_options.get("tool")
|
|
||||||
if tool and tool in TOOL_TO_CONFIG:
|
|
||||||
return TOOL_TO_CONFIG[tool]
|
|
||||||
server_exe = (debug_options.get("server") or {}).get("executable", "").lower()
|
|
||||||
if "st-util" in server_exe:
|
|
||||||
return GDB_STUTIL_INIT_CONFIG
|
|
||||||
return GDB_DEFAULT_INIT_CONFIG
|
|
@ -18,16 +18,12 @@ import subprocess
|
|||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from platformio import fs
|
|
||||||
from platformio.compat import (
|
from platformio.compat import (
|
||||||
WINDOWS,
|
WINDOWS,
|
||||||
aio_create_task,
|
aio_create_task,
|
||||||
aio_get_running_loop,
|
aio_get_running_loop,
|
||||||
get_locale_encoding,
|
get_locale_encoding,
|
||||||
string_types,
|
|
||||||
)
|
)
|
||||||
from platformio.proc import get_pythonexe_path
|
|
||||||
from platformio.project.helpers import get_project_core_dir
|
|
||||||
|
|
||||||
|
|
||||||
class DebugSubprocessProtocol(asyncio.SubprocessProtocol):
|
class DebugSubprocessProtocol(asyncio.SubprocessProtocol):
|
||||||
@ -61,12 +57,6 @@ class DebugBaseProcess:
|
|||||||
STDOUT_CHUNK_SIZE = 2048
|
STDOUT_CHUNK_SIZE = 2048
|
||||||
LOG_FILE = None
|
LOG_FILE = None
|
||||||
|
|
||||||
COMMON_PATTERNS = {
|
|
||||||
"PLATFORMIO_HOME_DIR": get_project_core_dir(),
|
|
||||||
"PLATFORMIO_CORE_DIR": get_project_core_dir(),
|
|
||||||
"PYTHONEXE": get_pythonexe_path(),
|
|
||||||
}
|
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.transport = None
|
self.transport = None
|
||||||
self._is_running = False
|
self._is_running = False
|
||||||
@ -155,32 +145,6 @@ class DebugBaseProcess:
|
|||||||
self._exit_future.set_result(True)
|
self._exit_future.set_result(True)
|
||||||
self._exit_future = None
|
self._exit_future = None
|
||||||
|
|
||||||
def apply_patterns(self, source, patterns=None):
|
|
||||||
_patterns = self.COMMON_PATTERNS.copy()
|
|
||||||
_patterns.update(patterns or {})
|
|
||||||
|
|
||||||
for key, value in _patterns.items():
|
|
||||||
if key.endswith(("_DIR", "_PATH")):
|
|
||||||
_patterns[key] = fs.to_unix_path(value)
|
|
||||||
|
|
||||||
def _replace(text):
|
|
||||||
for key, value in _patterns.items():
|
|
||||||
pattern = "$%s" % key
|
|
||||||
text = text.replace(pattern, value or "")
|
|
||||||
return text
|
|
||||||
|
|
||||||
if isinstance(source, string_types):
|
|
||||||
source = _replace(source)
|
|
||||||
elif isinstance(source, (list, dict)):
|
|
||||||
items = enumerate(source) if isinstance(source, list) else source.items()
|
|
||||||
for key, value in items:
|
|
||||||
if isinstance(value, string_types):
|
|
||||||
source[key] = _replace(value)
|
|
||||||
elif isinstance(value, (list, dict)):
|
|
||||||
source[key] = self.apply_patterns(value, patterns)
|
|
||||||
|
|
||||||
return source
|
|
||||||
|
|
||||||
def terminate(self):
|
def terminate(self):
|
||||||
if not self.is_running() or not self.transport:
|
if not self.is_running() or not self.transport:
|
||||||
return
|
return
|
||||||
|
@ -23,8 +23,6 @@ from platformio import fs, proc, telemetry, util
|
|||||||
from platformio.cache import ContentCache
|
from platformio.cache import ContentCache
|
||||||
from platformio.compat import aio_get_running_loop, hashlib_encode_data, is_bytes
|
from platformio.compat import aio_get_running_loop, hashlib_encode_data, is_bytes
|
||||||
from platformio.debug import helpers
|
from platformio.debug import helpers
|
||||||
from platformio.debug.exception import DebugInvalidOptionsError
|
|
||||||
from platformio.debug.initcfgs import get_gdb_init_config
|
|
||||||
from platformio.debug.process.base import DebugBaseProcess
|
from platformio.debug.process.base import DebugBaseProcess
|
||||||
from platformio.debug.process.server import DebugServerProcess
|
from platformio.debug.process.server import DebugServerProcess
|
||||||
from platformio.project.helpers import get_project_cache_dir
|
from platformio.project.helpers import get_project_cache_dir
|
||||||
@ -37,14 +35,12 @@ class DebugClientProcess(
|
|||||||
PIO_SRC_NAME = ".pioinit"
|
PIO_SRC_NAME = ".pioinit"
|
||||||
INIT_COMPLETED_BANNER = "PlatformIO: Initialization completed"
|
INIT_COMPLETED_BANNER = "PlatformIO: Initialization completed"
|
||||||
|
|
||||||
def __init__(self, project_dir, args, debug_options, env_options):
|
def __init__(self, project_dir, debug_config):
|
||||||
super(DebugClientProcess, self).__init__()
|
super(DebugClientProcess, self).__init__()
|
||||||
self.project_dir = project_dir
|
self.project_dir = project_dir
|
||||||
self.args = list(args)
|
self.debug_config = debug_config
|
||||||
self.debug_options = debug_options
|
|
||||||
self.env_options = env_options
|
|
||||||
|
|
||||||
self._server_process = DebugServerProcess(debug_options, env_options)
|
self._server_process = DebugServerProcess(debug_config)
|
||||||
self._session_id = None
|
self._session_id = None
|
||||||
|
|
||||||
if not os.path.isdir(get_project_cache_dir()):
|
if not os.path.isdir(get_project_cache_dir()):
|
||||||
@ -56,27 +52,13 @@ class DebugClientProcess(
|
|||||||
self._target_is_running = False
|
self._target_is_running = False
|
||||||
self._errors_buffer = b""
|
self._errors_buffer = b""
|
||||||
|
|
||||||
async def run(self, gdb_path, prog_path):
|
async def run(self, extra_args):
|
||||||
session_hash = gdb_path + prog_path
|
gdb_path = self.debug_config.client_executable_path
|
||||||
|
session_hash = gdb_path + self.debug_config.program_path
|
||||||
self._session_id = hashlib.sha1(hashlib_encode_data(session_hash)).hexdigest()
|
self._session_id = hashlib.sha1(hashlib_encode_data(session_hash)).hexdigest()
|
||||||
self._kill_previous_session()
|
self._kill_previous_session()
|
||||||
|
self.debug_config.port = await self._server_process.run()
|
||||||
patterns = {
|
self.generate_init_script(os.path.join(self._gdbsrc_dir, self.PIO_SRC_NAME))
|
||||||
"PROJECT_DIR": self.project_dir,
|
|
||||||
"PROG_PATH": prog_path,
|
|
||||||
"PROG_DIR": os.path.dirname(prog_path),
|
|
||||||
"PROG_NAME": os.path.basename(os.path.splitext(prog_path)[0]),
|
|
||||||
"DEBUG_PORT": self.debug_options["port"],
|
|
||||||
"UPLOAD_PROTOCOL": self.debug_options["upload_protocol"],
|
|
||||||
"INIT_BREAK": self.debug_options["init_break"] or "",
|
|
||||||
"LOAD_CMDS": "\n".join(self.debug_options["load_cmds"] or []),
|
|
||||||
}
|
|
||||||
|
|
||||||
await self._server_process.run(patterns)
|
|
||||||
if not patterns["DEBUG_PORT"]:
|
|
||||||
patterns["DEBUG_PORT"] = self._server_process.get_debug_port()
|
|
||||||
|
|
||||||
self.generate_pioinit(self._gdbsrc_dir, patterns)
|
|
||||||
|
|
||||||
# start GDB client
|
# start GDB client
|
||||||
args = [
|
args = [
|
||||||
@ -89,13 +71,11 @@ class DebugClientProcess(
|
|||||||
"-l",
|
"-l",
|
||||||
"10",
|
"10",
|
||||||
]
|
]
|
||||||
args.extend(self.args)
|
args.extend(list(extra_args or []))
|
||||||
if not gdb_path:
|
|
||||||
raise DebugInvalidOptionsError("GDB client is not configured")
|
|
||||||
gdb_data_dir = self._get_data_dir(gdb_path)
|
gdb_data_dir = self._get_data_dir(gdb_path)
|
||||||
if gdb_data_dir:
|
if gdb_data_dir:
|
||||||
args.extend(["--data-directory", gdb_data_dir])
|
args.extend(["--data-directory", gdb_data_dir])
|
||||||
args.append(patterns["PROG_PATH"])
|
args.append(self.debug_config.program_path)
|
||||||
await self.spawn(*args, cwd=self.project_dir, wait_until_exit=True)
|
await self.spawn(*args, cwd=self.project_dir, wait_until_exit=True)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@ -107,13 +87,13 @@ class DebugClientProcess(
|
|||||||
)
|
)
|
||||||
return gdb_data_dir if os.path.isdir(gdb_data_dir) else None
|
return gdb_data_dir if os.path.isdir(gdb_data_dir) else None
|
||||||
|
|
||||||
def generate_pioinit(self, dst_dir, patterns):
|
def generate_init_script(self, dst):
|
||||||
# default GDB init commands depending on debug tool
|
# default GDB init commands depending on debug tool
|
||||||
commands = get_gdb_init_config(self.debug_options).split("\n")
|
commands = self.debug_config.get_init_script("gdb").split("\n")
|
||||||
|
|
||||||
if self.debug_options["init_cmds"]:
|
if self.debug_config.init_cmds:
|
||||||
commands = self.debug_options["init_cmds"]
|
commands = self.debug_config.init_cmds
|
||||||
commands.extend(self.debug_options["extra_cmds"])
|
commands.extend(self.debug_config.extra_cmds)
|
||||||
|
|
||||||
if not any("define pio_reset_run_target" in cmd for cmd in commands):
|
if not any("define pio_reset_run_target" in cmd for cmd in commands):
|
||||||
commands = [
|
commands = [
|
||||||
@ -134,20 +114,20 @@ class DebugClientProcess(
|
|||||||
"define pio_restart_target",
|
"define pio_restart_target",
|
||||||
" pio_reset_halt_target",
|
" pio_reset_halt_target",
|
||||||
" $INIT_BREAK",
|
" $INIT_BREAK",
|
||||||
" %s" % ("continue" if patterns["INIT_BREAK"] else "next"),
|
" %s" % ("continue" if self.debug_config.init_break else "next"),
|
||||||
"end",
|
"end",
|
||||||
]
|
]
|
||||||
|
|
||||||
banner = [
|
banner = [
|
||||||
"echo PlatformIO Unified Debugger -> http://bit.ly/pio-debug\\n",
|
"echo PlatformIO Unified Debugger -> http://bit.ly/pio-debug\\n",
|
||||||
"echo PlatformIO: debug_tool = %s\\n" % self.debug_options["tool"],
|
"echo PlatformIO: debug_tool = %s\\n" % self.debug_config.tool_name,
|
||||||
"echo PlatformIO: Initializing remote target...\\n",
|
"echo PlatformIO: Initializing remote target...\\n",
|
||||||
]
|
]
|
||||||
footer = ["echo %s\\n" % self.INIT_COMPLETED_BANNER]
|
footer = ["echo %s\\n" % self.INIT_COMPLETED_BANNER]
|
||||||
commands = banner + commands + footer
|
commands = banner + commands + footer
|
||||||
|
|
||||||
with open(os.path.join(dst_dir, self.PIO_SRC_NAME), "w") as fp:
|
with open(dst, "w") as fp:
|
||||||
fp.write("\n".join(self.apply_patterns(commands, patterns)))
|
fp.write("\n".join(self.debug_config.reveal_patterns(commands)))
|
||||||
|
|
||||||
def connection_made(self, transport):
|
def connection_made(self, transport):
|
||||||
super(DebugClientProcess, self).connection_made(transport)
|
super(DebugClientProcess, self).connection_made(transport)
|
||||||
@ -179,7 +159,9 @@ class DebugClientProcess(
|
|||||||
# go to init break automatically
|
# go to init break automatically
|
||||||
if self.INIT_COMPLETED_BANNER.encode() in data:
|
if self.INIT_COMPLETED_BANNER.encode() in data:
|
||||||
telemetry.send_event(
|
telemetry.send_event(
|
||||||
"Debug", "Started", telemetry.dump_run_environment(self.env_options)
|
"Debug",
|
||||||
|
"Started",
|
||||||
|
telemetry.dump_run_environment(self.debug_config.env_options),
|
||||||
)
|
)
|
||||||
self._auto_exec_continue()
|
self._auto_exec_continue()
|
||||||
|
|
||||||
@ -194,12 +176,12 @@ class DebugClientProcess(
|
|||||||
aio_get_running_loop().call_later(0.1, self._auto_exec_continue)
|
aio_get_running_loop().call_later(0.1, self._auto_exec_continue)
|
||||||
return
|
return
|
||||||
|
|
||||||
if not self.debug_options["init_break"] or self._target_is_running:
|
if not self.debug_config.init_break or self._target_is_running:
|
||||||
return
|
return
|
||||||
|
|
||||||
self.console_log(
|
self.console_log(
|
||||||
"PlatformIO: Resume the execution to `debug_init_break = %s`\n"
|
"PlatformIO: Resume the execution to `debug_init_break = %s`\n"
|
||||||
% self.debug_options["init_break"]
|
% self.debug_config.init_break
|
||||||
)
|
)
|
||||||
self.console_log(
|
self.console_log(
|
||||||
"PlatformIO: More configuration options -> http://bit.ly/pio-debug\n"
|
"PlatformIO: More configuration options -> http://bit.ly/pio-debug\n"
|
||||||
@ -226,7 +208,7 @@ class DebugClientProcess(
|
|||||||
last_erros = re.sub(r'((~|&)"|\\n\"|\\t)', " ", last_erros, flags=re.M)
|
last_erros = re.sub(r'((~|&)"|\\n\"|\\t)', " ", last_erros, flags=re.M)
|
||||||
|
|
||||||
err = "%s -> %s" % (
|
err = "%s -> %s" % (
|
||||||
telemetry.dump_run_environment(self.env_options),
|
telemetry.dump_run_environment(self.debug_config.env_options),
|
||||||
last_erros,
|
last_erros,
|
||||||
)
|
)
|
||||||
telemetry.send_exception("DebugInitError: %s" % err)
|
telemetry.send_exception("DebugInitError: %s" % err)
|
||||||
|
@ -13,10 +13,12 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import fnmatch
|
||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from platformio import fs, util
|
from platformio import fs
|
||||||
|
from platformio.compat import MACOS, WINDOWS
|
||||||
from platformio.debug.exception import DebugInvalidOptionsError
|
from platformio.debug.exception import DebugInvalidOptionsError
|
||||||
from platformio.debug.helpers import escape_gdbmi_stream, is_gdbmi_mode
|
from platformio.debug.helpers import escape_gdbmi_stream, is_gdbmi_mode
|
||||||
from platformio.debug.process.base import DebugBaseProcess
|
from platformio.debug.process.base import DebugBaseProcess
|
||||||
@ -24,27 +26,22 @@ from platformio.proc import where_is_program
|
|||||||
|
|
||||||
|
|
||||||
class DebugServerProcess(DebugBaseProcess):
|
class DebugServerProcess(DebugBaseProcess):
|
||||||
def __init__(self, debug_options, env_options):
|
def __init__(self, debug_config):
|
||||||
super(DebugServerProcess, self).__init__()
|
super(DebugServerProcess, self).__init__()
|
||||||
self.debug_options = debug_options
|
self.debug_config = debug_config
|
||||||
self.env_options = env_options
|
|
||||||
|
|
||||||
self._debug_port = ":3333"
|
|
||||||
self._ready = False
|
self._ready = False
|
||||||
|
|
||||||
async def run(self, patterns): # pylint: disable=too-many-branches
|
async def run(self): # pylint: disable=too-many-branches
|
||||||
systype = util.get_systype()
|
server = self.debug_config.server
|
||||||
server = self.debug_options.get("server")
|
|
||||||
if not server:
|
if not server:
|
||||||
return None
|
return None
|
||||||
server = self.apply_patterns(server, patterns)
|
|
||||||
server_executable = server["executable"]
|
server_executable = server["executable"]
|
||||||
if not server_executable:
|
if not server_executable:
|
||||||
return None
|
return None
|
||||||
if server["cwd"]:
|
if server["cwd"]:
|
||||||
server_executable = os.path.join(server["cwd"], server_executable)
|
server_executable = os.path.join(server["cwd"], server_executable)
|
||||||
if (
|
if (
|
||||||
"windows" in systype
|
WINDOWS
|
||||||
and not server_executable.endswith(".exe")
|
and not server_executable.endswith(".exe")
|
||||||
and os.path.isfile(server_executable + ".exe")
|
and os.path.isfile(server_executable + ".exe")
|
||||||
):
|
):
|
||||||
@ -56,15 +53,18 @@ class DebugServerProcess(DebugBaseProcess):
|
|||||||
raise DebugInvalidOptionsError(
|
raise DebugInvalidOptionsError(
|
||||||
"\nCould not launch Debug Server '%s'. Please check that it "
|
"\nCould not launch Debug Server '%s'. Please check that it "
|
||||||
"is installed and is included in a system PATH\n\n"
|
"is installed and is included in a system PATH\n\n"
|
||||||
"See documentation or contact contact@platformio.org:\n"
|
"See documentation:\n"
|
||||||
"https://docs.platformio.org/page/plus/debugging.html\n"
|
"https://docs.platformio.org/page/plus/debugging.html\n"
|
||||||
% server_executable
|
% server_executable
|
||||||
)
|
)
|
||||||
|
|
||||||
openocd_pipe_allowed = all(
|
openocd_pipe_allowed = all(
|
||||||
[not self.debug_options["port"], "openocd" in server_executable]
|
[
|
||||||
|
not self.debug_config.env_options.get("debug_port"),
|
||||||
|
"gdb" in self.debug_config.client_executable_path,
|
||||||
|
"openocd" in server_executable,
|
||||||
|
]
|
||||||
)
|
)
|
||||||
# openocd_pipe_allowed = False
|
|
||||||
if openocd_pipe_allowed:
|
if openocd_pipe_allowed:
|
||||||
args = []
|
args = []
|
||||||
if server["cwd"]:
|
if server["cwd"]:
|
||||||
@ -76,18 +76,16 @@ class DebugServerProcess(DebugBaseProcess):
|
|||||||
str_args = " ".join(
|
str_args = " ".join(
|
||||||
[arg if arg.startswith("-") else '"%s"' % arg for arg in args]
|
[arg if arg.startswith("-") else '"%s"' % arg for arg in args]
|
||||||
)
|
)
|
||||||
self._debug_port = '| "%s" %s' % (server_executable, str_args)
|
return fs.to_unix_path('| "%s" %s' % (server_executable, str_args))
|
||||||
self._debug_port = fs.to_unix_path(self._debug_port)
|
|
||||||
return self._debug_port
|
|
||||||
|
|
||||||
env = os.environ.copy()
|
env = os.environ.copy()
|
||||||
# prepend server "lib" folder to LD path
|
# prepend server "lib" folder to LD path
|
||||||
if (
|
if (
|
||||||
"windows" not in systype
|
not WINDOWS
|
||||||
and server["cwd"]
|
and server["cwd"]
|
||||||
and os.path.isdir(os.path.join(server["cwd"], "lib"))
|
and os.path.isdir(os.path.join(server["cwd"], "lib"))
|
||||||
):
|
):
|
||||||
ld_key = "DYLD_LIBRARY_PATH" if "darwin" in systype else "LD_LIBRARY_PATH"
|
ld_key = "DYLD_LIBRARY_PATH" if MACOS else "LD_LIBRARY_PATH"
|
||||||
env[ld_key] = os.path.join(server["cwd"], "lib")
|
env[ld_key] = os.path.join(server["cwd"], "lib")
|
||||||
if os.environ.get(ld_key):
|
if os.environ.get(ld_key):
|
||||||
env[ld_key] = "%s:%s" % (env[ld_key], os.environ.get(ld_key))
|
env[ld_key] = "%s:%s" % (env[ld_key], os.environ.get(ld_key))
|
||||||
@ -102,20 +100,12 @@ class DebugServerProcess(DebugBaseProcess):
|
|||||||
await self.spawn(
|
await self.spawn(
|
||||||
*([server_executable] + server["arguments"]), cwd=server["cwd"], env=env
|
*([server_executable] + server["arguments"]), cwd=server["cwd"], env=env
|
||||||
)
|
)
|
||||||
|
|
||||||
if "mspdebug" in server_executable.lower():
|
|
||||||
self._debug_port = ":2000"
|
|
||||||
elif "jlink" in server_executable.lower():
|
|
||||||
self._debug_port = ":2331"
|
|
||||||
elif "qemu" in server_executable.lower():
|
|
||||||
self._debug_port = ":1234"
|
|
||||||
|
|
||||||
await self._wait_until_ready()
|
await self._wait_until_ready()
|
||||||
|
|
||||||
return self._debug_port
|
return self.debug_config.port
|
||||||
|
|
||||||
async def _wait_until_ready(self):
|
async def _wait_until_ready(self):
|
||||||
ready_pattern = self.debug_options.get("server", {}).get("ready_pattern")
|
ready_pattern = self.debug_config.server_ready_pattern
|
||||||
timeout = 60 if ready_pattern else 10
|
timeout = 60 if ready_pattern else 10
|
||||||
elapsed = 0
|
elapsed = 0
|
||||||
delay = 0.5
|
delay = 0.5
|
||||||
@ -129,14 +119,11 @@ class DebugServerProcess(DebugBaseProcess):
|
|||||||
def _check_ready_by_pattern(self, data):
|
def _check_ready_by_pattern(self, data):
|
||||||
if self._ready:
|
if self._ready:
|
||||||
return self._ready
|
return self._ready
|
||||||
ready_pattern = self.debug_options.get("server", {}).get("ready_pattern")
|
ready_pattern = self.debug_config.server_ready_pattern
|
||||||
if ready_pattern:
|
if ready_pattern:
|
||||||
self._ready = ready_pattern.encode() in data
|
self._ready = ready_pattern.encode() in data
|
||||||
return self._ready
|
return self._ready
|
||||||
|
|
||||||
def get_debug_port(self):
|
|
||||||
return self._debug_port
|
|
||||||
|
|
||||||
def stdout_data_received(self, data):
|
def stdout_data_received(self, data):
|
||||||
super(DebugServerProcess, self).stdout_data_received(
|
super(DebugServerProcess, self).stdout_data_received(
|
||||||
escape_gdbmi_stream("@", data) if is_gdbmi_mode() else data
|
escape_gdbmi_stream("@", data) if is_gdbmi_mode() else data
|
||||||
|
@ -203,7 +203,7 @@ class PlatformBase( # pylint: disable=too-many-instance-attributes,too-many-pub
|
|||||||
elif "nobuild" in targets and opts.get("type") != "framework":
|
elif "nobuild" in targets and opts.get("type") != "framework":
|
||||||
self.packages[name]["optional"] = True
|
self.packages[name]["optional"] = True
|
||||||
|
|
||||||
def configure_debug_options(self, initial_debug_options, ide_data):
|
def configure_debug_session(self, debug_config):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def get_lib_storages(self):
|
def get_lib_storages(self):
|
||||||
|
Reference in New Issue
Block a user