Ejemplo n.º 1
0
def pioplus_call(args, **kwargs):
    if "windows" in util.get_systype() and sys.version_info < (2, 7, 6):
        raise exception.PlatformioException(
            "PlatformIO Core Plus v%s does not run under Python version %s.\n"
            "Minimum supported version is 2.7.6, please upgrade Python.\n"
            "Python 3 is not yet supported.\n" %
            (__version__, sys.version.split()[0]))

    pioplus_path = join(get_core_package_dir("tool-pioplus"), "pioplus")
    os.environ['PYTHONEXEPATH'] = util.get_pythonexe_path()
    os.environ['PYTHONPYSITEDIR'] = get_core_package_dir("pysite-pioplus")
    util.copy_pythonpath_to_osenv()
    code = subprocess.call([pioplus_path] + args, **kwargs)

    # handle remote update request
    if code == 13:
        count_attr = "_update_count"
        try:
            count_value = getattr(pioplus_call, count_attr)
        except AttributeError:
            count_value = 0
            setattr(pioplus_call, count_attr, 1)
        count_value += 1
        setattr(pioplus_call, count_attr, count_value)
        if count_value < PIOPLUS_AUTO_UPDATES_MAX:
            update_core_packages()
            return pioplus_call(args, **kwargs)

    # handle reload request
    elif code == 14:
        return pioplus_call(args, **kwargs)

    if code != 0:
        raise exception.ReturnErrorCode(1)
Ejemplo n.º 2
0
    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

        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
Ejemplo n.º 3
0
def load_json(file_path):
    try:
        with open(file_path, "r") as f:
            return json.load(f)
    except ValueError:
        raise exception.PlatformioException("Could not load broken JSON: %s" %
                                            file_path)
Ejemplo n.º 4
0
def load_project_ide_data(project_dir, envs):
    from platformio.commands.run import cli as cmd_run
    assert envs
    if not isinstance(envs, (list, tuple, set)):
        envs = [envs]
    args = ["--project-dir", project_dir, "--target", "idedata"]
    for env in envs:
        args.extend(["-e", env])
    result = CliRunner().invoke(cmd_run, args)
    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)

    data = {}
    for line in result.output.split("\n"):
        line = line.strip()
        if (line.startswith('{"') and line.endswith("}")
                and "env_name" in line):
            _data = json.loads(line)
            if "env_name" in _data:
                data[_data['env_name']] = _data
    if len(envs) == 1 and envs[0] in data:
        return data[envs[0]]
    return data or None
Ejemplo n.º 5
0
    def connect(self):
        self.log.info("Name: {name}", name=self.name)
        self.log.info("Connecting to PlatformIO Remote Development Cloud")

        # pylint: disable=protected-access
        proto, options = endpoints._parse(__pioremote_endpoint__)
        proto = proto[0]

        factory = RemoteClientFactory()
        factory.remote_client = self
        factory.sslContextFactory = None
        if proto == "ssl":
            factory.sslContextFactory = SSLContextFactory(options["host"])
            reactor.connectSSL(
                options["host"],
                int(options["port"]),
                factory,
                factory.sslContextFactory,
            )
        elif proto == "tcp":
            reactor.connectTCP(options["host"], int(options["port"]), factory)
        else:
            raise exception.PlatformioException(
                "Unknown PIO Remote Cloud protocol")
        reactor.run()

        if self._exit_code != 0:
            raise exception.ReturnErrorCode(self._exit_code)
Ejemplo n.º 6
0
    def get_project_build_data(self):
        data = {"defines": [], "includes": [], "cxx_path": None}
        envdata = self.get_project_env()
        if "env_name" not in envdata:
            return data
        cmd = [
            normpath(sys.executable), "-m",
            "platformio" + (".__main__" if sys.version_info <
                            (2, 7, 0) else ""), "-f"
        ]
        if app.get_session_var("caller_id"):
            cmd.extend(["-c", app.get_session_var("caller_id")])
        cmd.extend(["run", "-t", "idedata", "-e", envdata['env_name']])
        cmd.extend(["-d", self.project_dir])
        result = util.exec_command(cmd)

        if result['returncode'] != 0 or '"includes":' not in result['out']:
            raise exception.PlatformioException("\n".join(
                [result['out'], result['err']]))

        output = result['out']
        start_index = output.index('{"')
        stop_index = output.rindex('}')
        data = json.loads(output[start_index:stop_index + 1])

        return data
Ejemplo n.º 7
0
def main():
    try:
        if "cygwin" in system().lower():
            raise exception.CygwinEnvDetected()

        # https://urllib3.readthedocs.org
        # /en/latest/security.html#insecureplatformwarning
        try:
            requests.packages.urllib3.disable_warnings()
        except AttributeError:
            raise exception.PlatformioException(
                "Invalid installation of Python `requests` package`. See "
                "< https://github.com/platformio/platformio/issues/252 >")

        cli(None, None, None)
    except Exception as e:  # pylint: disable=W0703
        if not isinstance(e, exception.ReturnErrorCode):
            maintenance.on_platformio_exception(e)
            error_str = "Error: "
            if isinstance(e, exception.PlatformioException):
                error_str += str(e)
            else:
                error_str += format_exc()
            click.secho(error_str, fg="red", err=True)
        return 1
    return 0
Ejemplo n.º 8
0
def get_core_package_dir(name):
    if name not in CORE_PACKAGES:
        raise exception.PlatformioException("Please upgrade PIO Core")
    requirements = CORE_PACKAGES[name]
    pm = CorePackageManager()
    pkg_dir = pm.get_package_dir(name, requirements)
    if pkg_dir:
        return pkg_dir
    return pm.install(name, requirements)
Ejemplo n.º 9
0
    def configure_default_packages(self, envoptions, targets):
        if (envoptions.get("framework") == "wiringpi"
                and "linux_arm" not in util.get_systype()):
            raise exception.PlatformioException(
                "PlatformIO does not support temporary cross-compilation "
                "for WiringPi framework. Please run PlatformIO directly on "
                "Raspberry Pi")

        return BasePlatform.configure_default_packages(self, envoptions,
                                                       targets)
Ejemplo n.º 10
0
    def configure_default_packages(self, variables, targets):
        if ("wiringpi" in variables.get("pioframework")
                and "linux_arm" not in util.get_systype()):
            raise exception.PlatformioException(
                "PlatformIO temporary does not support cross-compilation "
                "for WiringPi framework. Please run PlatformIO directly on "
                "Raspberry Pi")

        return PlatformBase.configure_default_packages(self, variables,
                                                       targets)
Ejemplo n.º 11
0
    def configure_default_packages(self, variables, targets):
        if not self._is_native() and "wiringpi" in variables.get(
                "pioframework"):
            raise exception.PlatformioException(
                "PlatformIO temporary does not support cross-compilation "
                "for WiringPi framework. Please use PIO Core directly on "
                "Raspberry Pi")

        return PlatformBase.configure_default_packages(self, variables,
                                                       targets)
Ejemplo n.º 12
0
 def new(tool, project_dir, config, envname, options):
     cls = None
     if tool == "cppcheck":
         cls = CppcheckCheckTool
     elif tool == "clangtidy":
         cls = ClangtidyCheckTool
     else:
         raise exception.PlatformioException("Unknown check tool `%s`" %
                                             tool)
     return cls(project_dir, config, envname, options)
Ejemplo n.º 13
0
 def get_transport(self):
     if self.env_options.get("platform") == "native":
         transport = "native"
     elif "framework" in self.env_options:
         transport = self.env_options.get("framework")[0]
     if "test_transport" in self.env_options:
         transport = self.env_options["test_transport"]
     if transport not in TRANSPORT_OPTIONS:
         raise exception.PlatformioException(
             "Unknown Unit Test transport `%s`" % transport)
     return transport.lower()
Ejemplo n.º 14
0
 def __init__(self, manifest_path):
     self._id = basename(manifest_path)[:-5]
     assert isfile(manifest_path)
     self.manifest_path = manifest_path
     try:
         self._manifest = util.load_json(manifest_path)
     except ValueError:
         raise exception.InvalidBoardManifest(manifest_path)
     if not set(["name", "url", "vendor"]) <= set(self._manifest.keys()):
         raise exception.PlatformioException(
             "Please specify name, url and vendor fields for " +
             manifest_path)
Ejemplo n.º 15
0
def get_core_package_dir(name):
    if name not in __core_packages__:
        raise exception.PlatformioException("Please upgrade PlatformIO Core")
    pm = ToolPackageManager()
    spec = PackageSpec(owner="platformio",
                       name=name,
                       requirements=__core_packages__[name])
    pkg = pm.get_package(spec)
    if pkg:
        return pkg.path
    assert pm.install(spec)
    _remove_unnecessary_packages()
    return pm.get_package(spec).path
Ejemplo n.º 16
0
def main():
    try:
        if "cygwin" in system().lower():
            raise exception.CygwinEnvDetected()

        # https://urllib3.readthedocs.org
        # /en/latest/security.html#insecureplatformwarning
        try:
            requests.packages.urllib3.disable_warnings()
        except AttributeError:
            raise exception.PlatformioException(
                "Invalid installation of Python `requests` package`. See "
                "< https://github.com/platformio/platformio/issues/252 >")

        # handle PLATFORMIO_FORCE_COLOR
        if str(getenv("PLATFORMIO_FORCE_COLOR", "")).lower() == "true":
            try:
                # pylint: disable=protected-access
                click._compat.isatty = lambda stream: True
            except:  # pylint: disable=bare-except
                pass

        cli(None, None, None)
    except Exception as e:  # pylint: disable=W0703
        if not isinstance(e, exception.ReturnErrorCode):
            maintenance.on_platformio_exception(e)
            error_str = "Error: "
            if isinstance(e, exception.PlatformioException):
                error_str += str(e)
            else:
                error_str += format_exc()
                error_str += """
============================================================

An unexpected error occurred. Further steps:

* Verify that you have the latest version of PlatformIO using
  `pip install -U platformio` command

* Try to find answer in FAQ Troubleshooting section
  http://docs.platformio.org/en/stable/faq.html

* Report this problem to the developers
  https://github.com/platformio/platformio/issues

============================================================
"""
            click.secho(error_str, fg="red", err=True)
        return 1
    return 0
Ejemplo n.º 17
0
 def get_transport(self):
     transport = None
     if self.env_options.get("platform") == "native":
         transport = "native"
     elif "framework" in self.env_options:
         transport = self.env_options.get("framework")[0]
     if "test_transport" in self.env_options:
         transport = self.env_options["test_transport"]
     if transport not in TRANSPORT_OPTIONS:
         raise exception.PlatformioException(
             "Unknown Unit Test transport `%s`. Please check a documentation how "
             "to create an own 'Test Transport':\n"
             "- https://docs.platformio.org/page/plus/unit-testing.html" % transport
         )
     return transport.lower()
Ejemplo n.º 18
0
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
Ejemplo n.º 19
0
    def _lock_state_file(self):
        if not self.lock:
            return
        self._lockfile = LockFile(self.path)

        if self._lockfile.is_locked() and \
                (time() - getmtime(self._lockfile.lock_file)) > 10:
            self._lockfile.break_lock()

        try:
            self._lockfile.acquire()
        except LockFailed:
            raise exception.PlatformioException(
                "The directory `{0}` or its parent directory is not owned by "
                "the current user and PlatformIO can not store configuration "
                "data. \nPlease check the permissions and owner of that "
                "directory. Otherwise, please remove manually `{0}` "
                "directory and PlatformIO will create new from the current "
                "user.".format(dirname(self.path)))
Ejemplo n.º 20
0
    def get_project_build_data(self):
        data = {"defines": [], "includes": [], "cxx_path": None}
        envdata = self.get_project_env()
        if "env_name" not in envdata:
            return data
        result = util.exec_command([
            "platformio", "-f", "run", "-t", "idedata", "-e",
            envdata['env_name'], "-d", self.project_dir
        ])

        if result['returncode'] != 0 or '"includes":' not in result['out']:
            raise exception.PlatformioException("\n".join(
                [result['out'], result['err']]))

        output = result['out']
        start_index = output.index('{"')
        stop_index = output.rindex('}')
        data = json.loads(output[start_index:stop_index + 1])

        return data
Ejemplo n.º 21
0
    def get_project_build_data(self):
        data = {"defines": [], "includes": [], "cxx_path": None}
        envdata = self.get_project_env()
        if "env_name" not in envdata:
            return data
        cmd = [util.get_pythonexe_path(), "-m", "platformio", "-f"]
        if app.get_session_var("caller_id"):
            cmd.extend(["-c", app.get_session_var("caller_id")])
        cmd.extend(["run", "-t", "idedata", "-e", envdata['env_name']])
        cmd.extend(["-d", self.project_dir])
        result = util.exec_command(cmd)

        if result['returncode'] != 0 or '"includes":' not in result['out']:
            raise exception.PlatformioException("\n".join(
                [result['out'], result['err']]))

        for line in result['out'].split("\n"):
            line = line.strip()
            if line.startswith('{"') and line.endswith("}"):
                data = json.loads(line)
        return data
Ejemplo n.º 22
0
def pioplus_call(args, **kwargs):
    if WINDOWS and sys.version_info < (2, 7, 6):
        raise exception.PlatformioException(
            "PlatformIO Core Plus v%s does not run under Python version %s.\n"
            "Minimum supported version is 2.7.6, please upgrade Python.\n"
            "Python 3 is not yet supported.\n" % (__version__, sys.version))

    pioplus_path = join(get_core_package_dir("tool-pioplus"), "pioplus")
    pythonexe_path = get_pythonexe_path()
    os.environ["PYTHONEXEPATH"] = pythonexe_path
    os.environ["PYTHONPYSITEDIR"] = get_core_package_dir("contrib-pysite")
    os.environ["PIOCOREPYSITEDIR"] = dirname(fs.get_source_dir() or "")
    if dirname(pythonexe_path) not in os.environ["PATH"].split(os.pathsep):
        os.environ["PATH"] = (os.pathsep).join(
            [dirname(pythonexe_path), os.environ["PATH"]])
    copy_pythonpath_to_osenv()
    code = subprocess.call([pioplus_path] + args, **kwargs)

    # handle remote update request
    if code == 13:
        count_attr = "_update_count"
        try:
            count_value = getattr(pioplus_call, count_attr)
        except AttributeError:
            count_value = 0
            setattr(pioplus_call, count_attr, 1)
        count_value += 1
        setattr(pioplus_call, count_attr, count_value)
        if count_value < PIOPLUS_AUTO_UPDATES_MAX:
            update_core_packages()
            return pioplus_call(args, **kwargs)

    # handle reload request
    elif code == 14:
        return pioplus_call(args, **kwargs)

    if code != 0:
        raise exception.ReturnErrorCode(1)

    return True
Ejemplo n.º 23
0
    def get_project_build_data(self):
        data = {"defines": [], "includes": [], "cxx_path": None}
        envdata = self.get_project_env()
        if not envdata:
            return data

        out = StringIO()
        with util.capture_stdout(out):
            click.get_current_context().invoke(
                cmd_run,
                project_dir=self.project_dir,
                environment=[envdata['env_name']],
                target=["idedata"])
        result = out.getvalue()

        if '"includes":' not in result:
            raise exception.PlatformioException(result)

        for line in result.split("\n"):
            line = line.strip()
            if line.startswith('{"') and line.endswith("}"):
                data = json.loads(line)
        return data
Ejemplo n.º 24
0
    def get_test_port(self):
        # if test port is specified manually or in config
        if self.options.get("test_port"):
            return self.options.get("test_port")
        if self.env_options.get("test_port"):
            return self.env_options.get("test_port")

        assert set(["platform", "board"]) & set(self.env_options.keys())
        p = PlatformFactory.newPlatform(self.env_options['platform'])
        board_hwids = p.board_config(self.env_options['board']).get(
            "build.hwids", [])
        port = None
        elapsed = 0
        while elapsed < 5 and not port:
            for item in util.get_serialports():
                port = item['port']
                for hwid in board_hwids:
                    hwid_str = ("%s:%s" % (hwid[0], hwid[1])).replace("0x", "")
                    if hwid_str in item['hwid']:
                        return port

            # check if port is already configured
            try:
                serial.Serial(port, timeout=self.SERIAL_TIMEOUT).close()
            except serial.SerialException:
                port = None

            if not port:
                sleep(0.25)
                elapsed += 0.25

        if not port:
            raise exception.PlatformioException(
                "Please specify `test_port` for environment or use "
                "global `--test-port` option.")
        return port
Ejemplo n.º 25
0
def cli(port, host, no_open, shutdown_timeout):
    # pylint: disable=import-error, import-outside-toplevel

    # import contrib modules
    inject_contrib_pysite()
    from autobahn.twisted.resource import WebSocketResource
    from twisted.internet import reactor
    from twisted.web import server

    from platformio.commands.home.rpc.handlers.app import AppRPC
    from platformio.commands.home.rpc.handlers.ide import IDERPC
    from platformio.commands.home.rpc.handlers.misc import MiscRPC
    from platformio.commands.home.rpc.handlers.os import OSRPC
    from platformio.commands.home.rpc.handlers.piocore import PIOCoreRPC
    from platformio.commands.home.rpc.handlers.project import ProjectRPC
    from platformio.commands.home.rpc.server import JSONRPCServerFactory
    from platformio.commands.home.web import WebRoot

    factory = JSONRPCServerFactory(shutdown_timeout)
    factory.addHandler(AppRPC(), namespace="app")
    factory.addHandler(IDERPC(), namespace="ide")
    factory.addHandler(MiscRPC(), namespace="misc")
    factory.addHandler(OSRPC(), namespace="os")
    factory.addHandler(PIOCoreRPC(), namespace="core")
    factory.addHandler(ProjectRPC(), namespace="project")

    contrib_dir = get_core_package_dir("contrib-piohome")
    if not isdir(contrib_dir):
        raise exception.PlatformioException("Invalid path to PIO Home Contrib")

    # Ensure PIO Home mimetypes are known
    mimetypes.add_type("text/html", ".html")
    mimetypes.add_type("text/css", ".css")
    mimetypes.add_type("application/javascript", ".js")

    root = WebRoot(contrib_dir)
    root.putChild(b"wsrpc", WebSocketResource(factory))
    site = server.Site(root)

    # hook for `platformio-node-helpers`
    if host == "__do_not_start__":
        return

    # if already started
    already_started = False
    socket.setdefaulttimeout(1)
    try:
        socket.socket(socket.AF_INET, socket.SOCK_STREAM).connect((host, port))
        already_started = True
    except:  # pylint: disable=bare-except
        pass

    home_url = "http://%s:%d" % (host, port)
    if not no_open:
        if already_started:
            click.launch(home_url)
        else:
            reactor.callLater(1, lambda: click.launch(home_url))

    click.echo("\n".join([
        "",
        "  ___I_",
        " /\\-_--\\   PlatformIO Home",
        "/  \\_-__\\",
        "|[]| [] |  %s" % home_url,
        "|__|____|______________%s" % ("_" * len(host)),
    ]))
    click.echo("")
    click.echo("Open PIO Home in your browser by this URL => %s" % home_url)

    if already_started:
        return

    click.echo("PIO Home has been started. Press Ctrl+C to shutdown.")

    reactor.listenTCP(port, site, interface=host)
    reactor.run()
Ejemplo n.º 26
0
def cli(ctx, project_dir, project_conf, environment, verbose, interface,
        __unprocessed):
    # use env variables from Eclipse or CLion
    for sysenv in ("CWD", "PWD", "PLATFORMIO_PROJECT_DIR"):
        if is_platformio_project(project_dir):
            break
        if os.getenv(sysenv):
            project_dir = os.getenv(sysenv)

    with util.cd(project_dir):
        config = ProjectConfig.get_instance(
            project_conf or join(project_dir, "platformio.ini"))
        config.validate(envs=[environment] if environment else None)

        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 exception.ProjectEnvsNotAvailable()
        debug_options = helpers.validate_debug_options(ctx, env_options)
        assert debug_options

    if not interface:
        return helpers.predebug_project(ctx, project_dir, env_name, False,
                                        verbose)

    configuration = load_project_ide_data(project_dir, env_name)
    if not configuration:
        raise exception.DebugInvalidOptions(
            "Could not load debug configuration")

    if "--version" in __unprocessed:
        result = util.exec_command([configuration['gdb_path'], "--version"])
        if result['returncode'] == 0:
            return click.echo(result['out'])
        raise exception.PlatformioException("\n".join(
            [result['out'], result['err']]))

    try:
        util.ensure_udev_rules()
    except NameError:
        pass
    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)

    debug_options['load_cmds'] = helpers.configure_esp32_load_cmds(
        debug_options, configuration)

    rebuild_prog = False
    preload = debug_options['load_cmds'] == ["preload"]
    load_mode = debug_options['load_mode']
    if load_mode == "always":
        rebuild_prog = (
            preload
            or not helpers.has_debug_symbols(configuration['prog_path']))
    elif load_mode == "modified":
        rebuild_prog = (
            helpers.is_prog_obsolete(configuration['prog_path'])
            or not helpers.has_debug_symbols(configuration['prog_path']))
    else:
        rebuild_prog = not isfile(configuration['prog_path'])

    if preload or (not rebuild_prog and load_mode != "always"):
        # don't load firmware through debug server
        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):
                helpers.predebug_project(ctx, project_dir, env_name, preload,
                                         verbose)
            output.close()
        else:
            click.echo("Preparing firmware for debugging...")
            helpers.predebug_project(ctx, project_dir, env_name, preload,
                                     verbose)

        # save SHA sum of newly created prog
        if load_mode == "modified":
            helpers.is_prog_obsolete(configuration['prog_path'])

    if not isfile(configuration['prog_path']):
        raise exception.DebugInvalidOptions("Program/firmware is missed")

    # run debugging client
    inject_contrib_pysite()
    from platformio.commands.debug.client import GDBClient, reactor

    client = GDBClient(project_dir, __unprocessed, debug_options, env_options)
    client.spawn(configuration['gdb_path'], configuration['prog_path'])

    signal.signal(signal.SIGINT, lambda *args, **kwargs: None)
    reactor.run()

    return True
Ejemplo n.º 27
0
def cli(ctx, project_dir, project_conf, environment, verbose, interface,
        __unprocessed):
    app.set_session_var("custom_project_conf", project_conf)

    # use env variables from Eclipse or CLion
    for sysenv in ("CWD", "PWD", "PLATFORMIO_PROJECT_DIR"):
        if is_platformio_project(project_dir):
            break
        if os.getenv(sysenv):
            project_dir = os.getenv(sysenv)

    with fs.cd(project_dir):
        config = ProjectConfig.get_instance(project_conf)
        config.validate(envs=[environment] if environment else None)

        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()
        debug_options = helpers.validate_debug_options(ctx, env_options)
        assert debug_options

    if not interface:
        return helpers.predebug_project(ctx, project_dir, env_name, False,
                                        verbose)

    configuration = load_project_ide_data(project_dir, env_name)
    if not configuration:
        raise DebugInvalidOptionsError("Could not load debug configuration")

    if "--version" in __unprocessed:
        result = proc.exec_command([configuration["gdb_path"], "--version"])
        if result["returncode"] == 0:
            return click.echo(result["out"])
        raise exception.PlatformioException("\n".join(
            [result["out"], result["err"]]))

    try:
        fs.ensure_udev_rules()
    except exception.InvalidUdevRules as e:
        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)

    rebuild_prog = False
    preload = debug_options["load_cmds"] == ["preload"]
    load_mode = debug_options["load_mode"]
    if load_mode == "always":
        rebuild_prog = preload or not helpers.has_debug_symbols(
            configuration["prog_path"])
    elif load_mode == "modified":
        rebuild_prog = helpers.is_prog_obsolete(
            configuration["prog_path"]) or not helpers.has_debug_symbols(
                configuration["prog_path"])
    else:
        rebuild_prog = not isfile(configuration["prog_path"])

    if preload or (not rebuild_prog and load_mode != "always"):
        # don't load firmware through debug server
        debug_options["load_cmds"] = []

    if rebuild_prog:
        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)
            stream.close()
        else:
            click.echo("Preparing firmware for debugging...")
            helpers.predebug_project(ctx, project_dir, env_name, preload,
                                     verbose)

        # save SHA sum of newly created prog
        if load_mode == "modified":
            helpers.is_prog_obsolete(configuration["prog_path"])

    if not isfile(configuration["prog_path"]):
        raise DebugInvalidOptionsError("Program/firmware is missed")

    # run debugging client
    inject_contrib_pysite()

    # pylint: disable=import-outside-toplevel
    from platformio.commands.debug.process.client import GDBClient, reactor

    client = GDBClient(project_dir, __unprocessed, debug_options, env_options)
    client.spawn(configuration["gdb_path"], configuration["prog_path"])

    signal.signal(signal.SIGINT, lambda *args, **kwargs: None)
    reactor.run()

    return True