diff --git a/platformio/commands/debug/client.py b/platformio/commands/debug/client.py index f7a81968..72a12a84 100644 --- a/platformio/commands/debug/client.py +++ b/platformio/commands/debug/client.py @@ -30,7 +30,7 @@ from platformio import app, exception, fs, proc, util from platformio.commands.debug import helpers, initcfgs from platformio.commands.debug.process import BaseProcess from platformio.commands.debug.server import DebugServer -from platformio.compat import hashlib_encode_data +from platformio.compat import hashlib_encode_data, is_bytes from platformio.project.helpers import get_project_cache_dir from platformio.telemetry import MeasurementProtocol @@ -223,10 +223,9 @@ class GDBClient(BaseProcess): # pylint: disable=too-many-instance-attributes self._handle_error(data) def console_log(self, msg): - if helpers.is_mi_mode(self.args): - self.outReceived(('~"%s\\n"\n' % msg).encode()) - else: - self.outReceived(("%s\n" % msg).encode()) + if helpers.is_gdbmi_mode(): + msg = helpers.escape_gdbmi_stream("~", msg) + self.outReceived(msg if is_bytes(msg) else msg.encode()) def _auto_exec_continue(self): auto_exec_delay = 0.5 # in seconds @@ -239,14 +238,14 @@ class GDBClient(BaseProcess): # pylint: disable=too-many-instance-attributes if not self.debug_options["init_break"] or self._target_is_run: return self.console_log( - "PlatformIO: Resume the execution to `debug_init_break = %s`" + "PlatformIO: Resume the execution to `debug_init_break = %s`\n" % self.debug_options["init_break"] ) self.console_log( - "PlatformIO: More configuration options -> http://bit.ly/pio-debug" + "PlatformIO: More configuration options -> http://bit.ly/pio-debug\n" ) self.transport.write( - b"0-exec-continue\n" if helpers.is_mi_mode(self.args) else b"continue\n" + b"0-exec-continue\n" if helpers.is_gdbmi_mode() else b"continue\n" ) self._target_is_run = True diff --git a/platformio/commands/debug/command.py b/platformio/commands/debug/command.py index c99cc548..ab273063 100644 --- a/platformio/commands/debug/command.py +++ b/platformio/commands/debug/command.py @@ -90,10 +90,12 @@ def cli(ctx, project_dir, project_conf, environment, verbose, interface, __unpro try: fs.ensure_udev_rules() except exception.InvalidUdevRules as e: - for line in str(e).split("\n") + [""]: - click.echo( - ('~"%s\\n"' if helpers.is_mi_mode(__unprocessed) else "%s") % line - ) + click.echo( + helpers.escape_gdbmi_stream("~", str(e) + "\n") + if helpers.is_gdbmi_mode() + else str(e) + "\n", + nl=False, + ) debug_options["load_cmds"] = helpers.configure_esp32_load_cmds( debug_options, configuration @@ -118,12 +120,17 @@ def cli(ctx, project_dir, project_conf, environment, verbose, interface, __unpro debug_options["load_cmds"] = [] if rebuild_prog: - if helpers.is_mi_mode(__unprocessed): - click.echo('~"Preparing firmware for debugging...\\n"') - output = helpers.GDBBytesIO() - with util.capture_std_streams(output): + if helpers.is_gdbmi_mode(): + click.echo( + helpers.escape_gdbmi_stream( + "~", "Preparing firmware for debugging...\n" + ), + nl=False, + ) + stream = helpers.GDBMIConsoleStream() + with util.capture_std_streams(stream): helpers.predebug_project(ctx, project_dir, env_name, preload, verbose) - output.close() + stream.close() else: click.echo("Preparing firmware for debugging...") 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 3e53be8a..e8e6c525 100644 --- a/platformio/commands/debug/helpers.py +++ b/platformio/commands/debug/helpers.py @@ -21,32 +21,46 @@ from io import BytesIO from os.path import isfile from platformio import exception, fs, util +from platformio.commands import PlatformioCLI from platformio.commands.platform import platform_install as cmd_platform_install from platformio.commands.run.command import cli as cmd_run +from platformio.compat import is_bytes from platformio.managers.platform import PlatformFactory from platformio.project.config import ProjectConfig from platformio.project.options import ProjectOptions -class GDBBytesIO(BytesIO): # pylint: disable=too-few-public-methods +class GDBMIConsoleStream(BytesIO): # pylint: disable=too-few-public-methods STDOUT = sys.stdout - @staticmethod - def escape(text): - return re.sub(r"\\+", "\\\\\\\\", text) - def write(self, text): - if "\n" in text: - for line in text.strip().split("\n"): - self.STDOUT.write('~"%s\\n"\n' % self.escape(line)) - else: - self.STDOUT.write('~"%s"' % self.escape(text)) + self.STDOUT.write(escape_gdbmi_stream("~", text)) self.STDOUT.flush() -def is_mi_mode(args): - return "--interpreter" in " ".join(args) +def is_gdbmi_mode(): + return "--interpreter" in " ".join(PlatformioCLI.leftover_args) + + +def escape_gdbmi_stream(prefix, stream): + bytes_stream = False + if is_bytes(stream): + bytes_stream = True + stream = stream.decode() + + if not stream: + return b"" if bytes_stream else "" + + ends_nl = stream.endswith("\n") + stream = re.sub(r"\\+", "\\\\\\\\", stream) + stream = stream.replace('"', '\\"') + stream = stream.replace("\n", "\\n") + stream = '%s"%s"' % (prefix, stream) + if ends_nl: + stream += "\n" + + return stream.encode() if bytes_stream else stream def get_default_debug_env(config): diff --git a/platformio/commands/debug/server.py b/platformio/commands/debug/server.py index 18b39e41..cdd7fa32 100644 --- a/platformio/commands/debug/server.py +++ b/platformio/commands/debug/server.py @@ -19,6 +19,7 @@ from twisted.internet import error # pylint: disable=import-error from twisted.internet import reactor # pylint: disable=import-error from platformio import exception, fs, util +from platformio.commands.debug.helpers import escape_gdbmi_stream, is_gdbmi_mode from platformio.commands.debug.process import BaseProcess from platformio.proc import where_is_program @@ -119,6 +120,11 @@ class DebugServer(BaseProcess): def get_debug_port(self): return self._debug_port + def outReceived(self, data): + super(DebugServer, self).outReceived( + escape_gdbmi_stream("@", data) if is_gdbmi_mode() else data + ) + def processEnded(self, reason): self._process_ended = True super(DebugServer, self).processEnded(reason)