Initial support for debugging // Resolve #514

This commit is contained in:
Ivan Kravets
2017-03-31 18:55:19 +03:00
parent fe7c93d004
commit 75e1173f80
9 changed files with 305 additions and 84 deletions

2
docs

Submodule docs updated: 00f8cfe790...ecd4a795a4

View File

@ -62,13 +62,20 @@ commonvars.AddVariables(
("UPLOAD_PROTOCOL",),
("UPLOAD_SPEED",),
("UPLOAD_FLAGS",),
("UPLOAD_RESETMETHOD",)
("UPLOAD_RESETMETHOD",),
# debug options
("DEBUG_LINK",),
("DEBUG_PORT",),
("DEBUG_GDBINIT",)
) # yapf: disable
DEFAULT_ENV_OPTIONS = dict(
tools=[
"ar", "as", "gcc", "g++", "gnulink", "platformio", "pioplatform",
"piowinhooks", "piolib", "piotest", "pioupload", "piomisc"
"piowinhooks", "piolib", "piotest", "pioupload", "piomisc", "pioide",
"piodebug"
], # yapf: disable
toolpath=[join(util.get_source_dir(), "builder", "tools")],
variables=commonvars,
@ -77,7 +84,6 @@ DEFAULT_ENV_OPTIONS = dict(
PIOVARIABLES=commonvars.keys(),
ENV=environ,
UNIX_TIME=int(time()),
PROGNAME="program",
PIOHOME_DIR=util.get_home_dir(),
PROJECT_DIR=util.get_project_dir(),
PROJECTSRC_DIR=util.get_projectsrc_dir(),
@ -91,6 +97,8 @@ DEFAULT_ENV_OPTIONS = dict(
util.get_projectlib_dir(), util.get_projectlibdeps_dir(),
join("$PIOHOME_DIR", "lib")
],
PROGNAME="program",
PROG_PATH=join("$BUILD_DIR", "$PROGNAME$PROGSUFFIX"),
PYTHONEXE=util.get_pythonexe_path())
if not int(ARGUMENTS.get("PIOVERBOSE", 0)):
@ -150,6 +158,7 @@ env.SConscriptChdir(0)
env.SConsignFile(join("$PROJECTPIOENVS_DIR", ".sconsign.dblite"))
env.SConscript("$BUILD_SCRIPT")
AlwaysBuild(env.Alias("__debug", DEFAULT_TARGETS + ["size"]))
AlwaysBuild(env.Alias("__test", DEFAULT_TARGETS + ["size"]))
if "UPLOAD_FLAGS" in env:

View File

@ -0,0 +1,98 @@
# Copyright 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 __future__ import absolute_import
import sys
from fnmatch import fnmatch
from platformio import util
def ProcessDebug(env):
env.Append(
BUILD_FLAGS=["-Og", "-ggdb"],
BUILD_UNFLAGS=["-Os", "-O0", "-O1", "-O2", "-O3"])
def DebugLinkSettings(env):
if "BOARD" not in env:
return
board_debug = env.BoardConfig().get("debug")
if not board_debug or not board_debug.get("links"):
return
debug_links = board_debug.get("links")
link_name = (env.subst("$DEBUG_LINK") or
board_debug.get("default_link", debug_links.keys()[0]))
settings = debug_links.get(link_name)
if not settings:
return
settings.update({"name": link_name})
return settings
def AutodetectDebugPort(env):
def _get_pattern():
if "DEBUG_PORT" not in env:
return None
if set(["*", "?", "[", "]"]) & set(env['DEBUG_PORT']):
return env['DEBUG_PORT']
return None
def _is_match_pattern(port):
pattern = _get_pattern()
if not pattern:
return True
return fnmatch(port, pattern)
def _look_for_serial_port(hwids):
port = None
for item in util.get_serialports(filter_hwid=True):
if not _is_match_pattern(item['port']):
continue
if "GDB" in item['port']:
return item['port']
for hwid in hwids:
hwid_str = ("%s:%s" % (hwid[0], hwid[1])).replace("0x", "")
if hwid_str in item['hwid']:
return port
return port
if "BOARD" not in env or ("DEBUG_PORT" in env and not _get_pattern()):
return
link_settings = env.DebugLinkSettings()
if not link_settings:
return
if not link_settings.get("require_debug_port"):
return
env.Replace(
DEBUG_PORT=_look_for_serial_port(link_settings.get("hwids", [])))
if not env.subst("$DEBUG_PORT"):
sys.stderr.write(
"Error: Please specify `debug_port` for environment.\n")
env.Exit(1)
def exists(_):
return True
def generate(env):
env.AddMethod(ProcessDebug)
env.AddMethod(DebugLinkSettings)
env.AddMethod(AutodetectDebugPort)
return env

View File

@ -0,0 +1,149 @@
# Copyright 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 __future__ import absolute_import
from glob import glob
from os.path import join
from SCons.Defaults import processDefines
from platformio import util
def dump_includes(env):
includes = []
for item in env.get("CPPPATH", []):
includes.append(env.subst(item))
# installed libs
for lb in env.GetLibBuilders():
includes.extend(lb.get_inc_dirs())
# includes from toolchains
p = env.PioPlatform()
for name in p.get_installed_packages():
if p.get_package_type(name) != "toolchain":
continue
toolchain_dir = util.glob_escape(p.get_package_dir(name))
toolchain_incglobs = [
join(toolchain_dir, "*", "include*"),
join(toolchain_dir, "lib", "gcc", "*", "*", "include*")
]
for g in toolchain_incglobs:
includes.extend(glob(g))
return includes
def dump_defines(env):
defines = []
# global symbols
for item in processDefines(env.get("CPPDEFINES", [])):
defines.append(env.subst(item).replace('\\', ''))
# special symbol for Atmel AVR MCU
if env['PIOPLATFORM'] == "atmelavr":
defines.append(
"__AVR_%s__" % env.BoardConfig().get("build.mcu").upper()
.replace("ATMEGA", "ATmega").replace("ATTINY", "ATtiny"))
return defines
def dump_debug(env):
def _dump_server(configuration):
if not configuration:
return
if not set(configuration.keys()) >= set(["package", "executable"]):
return
pkg_dir = env.PioPlatform().get_package_dir(configuration['package'])
if not pkg_dir:
return
return {
"cwd": pkg_dir,
"executable": configuration['executable'],
"arguments": configuration.get("arguments")
}
gdbinit = None
if "DEBUG_GDBINIT" in env:
if isinstance(env['DEBUG_GDBINIT'], list):
gdbinit = env['DEBUG_GDBINIT']
else:
gdbinit = [env['DEBUG_GDBINIT']]
link_settings = env.DebugLinkSettings()
if link_settings and not gdbinit:
gdbinit = link_settings.get("gdbinit")
env.AutodetectDebugPort()
return {
"gdb_path": util.where_is_program(
env.subst("$GDB"), env.subst("${ENV['PATH']}")),
"prog_path": env.subst("$PROG_PATH"),
"link": link_settings['name'] if link_settings else None,
"gdbinit": [env.subst(cmd) for cmd in gdbinit] if gdbinit else None,
"port": env.subst("$DEBUG_PORT"),
"server": (_dump_server(link_settings['server'])
if link_settings and "server" in link_settings else None)
}
def DumpIDEData(env):
LINTCCOM = "$CFLAGS $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS"
LINTCXXCOM = "$CXXFLAGS $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS"
data = {
"libsource_dirs":
[env.subst(l) for l in env.get("LIBSOURCE_DIRS", [])],
"defines": dump_defines(env),
"includes": dump_includes(env),
"debug": dump_debug(env),
"cc_flags": env.subst(LINTCCOM),
"cxx_flags": env.subst(LINTCXXCOM),
"cc_path": util.where_is_program(
env.subst("$CC"), env.subst("${ENV['PATH']}")),
"cxx_path": util.where_is_program(
env.subst("$CXX"), env.subst("${ENV['PATH']}")),
}
env_ = env.Clone()
# https://github.com/platformio/platformio-atom-ide/issues/34
_new_defines = []
for item in processDefines(env_.get("CPPDEFINES", [])):
item = item.replace('\\"', '"')
if " " in item:
_new_defines.append(item.replace(" ", "\\\\ "))
else:
_new_defines.append(item)
env_.Replace(CPPDEFINES=_new_defines)
data.update({
"cc_flags": env_.subst(LINTCCOM),
"cxx_flags": env_.subst(LINTCXXCOM)
})
return data
def exists(_):
return True
def generate(env):
env.AddMethod(DumpIDEData)
return env

View File

@ -17,13 +17,11 @@ from __future__ import absolute_import
import atexit
import re
import sys
from glob import glob
from os import environ, remove, walk
from os.path import basename, isdir, isfile, join, relpath
from tempfile import mkstemp
from SCons.Action import Action
from SCons.Defaults import processDefines
from SCons.Script import ARGUMENTS
from platformio import util
@ -200,81 +198,6 @@ def _delete_file(path):
pass
def DumpIDEData(env):
def get_includes(env_):
includes = []
for item in env_.get("CPPPATH", []):
includes.append(env_.subst(item))
# installed libs
for lb in env.GetLibBuilders():
includes.extend(lb.get_inc_dirs())
# includes from toolchains
p = env.PioPlatform()
for name in p.get_installed_packages():
if p.get_package_type(name) != "toolchain":
continue
toolchain_dir = util.glob_escape(p.get_package_dir(name))
toolchain_incglobs = [
join(toolchain_dir, "*", "include*"),
join(toolchain_dir, "lib", "gcc", "*", "*", "include*")
]
for g in toolchain_incglobs:
includes.extend(glob(g))
return includes
def get_defines(env_):
defines = []
# global symbols
for item in processDefines(env_.get("CPPDEFINES", [])):
defines.append(env_.subst(item).replace('\\', ''))
# special symbol for Atmel AVR MCU
if env['PIOPLATFORM'] == "atmelavr":
defines.append(
"__AVR_%s__" % env.BoardConfig().get("build.mcu").upper()
.replace("ATMEGA", "ATmega").replace("ATTINY", "ATtiny"))
return defines
LINTCCOM = "$CFLAGS $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS"
LINTCXXCOM = "$CXXFLAGS $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS"
env_ = env.Clone()
data = {
"libsource_dirs":
[env_.subst(l) for l in env_.get("LIBSOURCE_DIRS", [])],
"defines": get_defines(env_),
"includes": get_includes(env_),
"cc_flags": env_.subst(LINTCCOM),
"cxx_flags": env_.subst(LINTCXXCOM),
"cc_path": util.where_is_program(
env_.subst("$CC"), env_.subst("${ENV['PATH']}")),
"cxx_path": util.where_is_program(
env_.subst("$CXX"), env_.subst("${ENV['PATH']}"))
}
# https://github.com/platformio/platformio-atom-ide/issues/34
_new_defines = []
for item in processDefines(env_.get("CPPDEFINES", [])):
item = item.replace('\\"', '"')
if " " in item:
_new_defines.append(item.replace(" ", "\\\\ "))
else:
_new_defines.append(item)
env_.Replace(CPPDEFINES=_new_defines)
data.update({
"cc_flags": env_.subst(LINTCCOM),
"cxx_flags": env_.subst(LINTCXXCOM)
})
return data
def GetCompilerType(env):
try:
sysenv = environ.copy()
@ -352,7 +275,6 @@ def exists(_):
def generate(env):
env.AddMethod(ConvertInoToCpp)
env.AddMethod(DumpIDEData)
env.AddMethod(GetCompilerType)
env.AddMethod(GetActualLDScript)
env.AddMethod(VerboseAction)

View File

@ -45,6 +45,9 @@ def BuildProgram(env):
if not case_sensitive_suffixes(".s", ".S"):
env.Replace(AS="$CC", ASCOM="$ASPPCOM")
if "__debug" in COMMAND_LINE_TARGETS:
env.ProcessDebug()
# process extra flags from board
if "BOARD" in env and "build.extra_flags" in env.BoardConfig():
env.ProcessFlags(env.BoardConfig().get("build.extra_flags"))

View File

@ -0,0 +1,39 @@
# Copyright 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 sys
from os import getcwd
import click
from platformio.managers.core import pioplus_call
@click.command("debug", short_help="Project Debugger")
@click.option(
"-d",
"--project-dir",
default=getcwd,
type=click.Path(
exists=True,
file_okay=False,
dir_okay=True,
writable=True,
resolve_path=True))
@click.option("--environment", "-e", metavar="<environment>")
@click.option("--configuration", is_flag=True)
@click.option("--json-output", is_flag=True)
@click.option("--verbose", "-v", is_flag=True)
def cli(*args, **kwargs): # pylint: disable=unused-argument
pioplus_call(sys.argv[1:])

View File

@ -129,7 +129,8 @@ class EnvironmentProcessor(object):
"upload_port", "upload_protocol", "upload_speed", "upload_flags",
"upload_resetmethod", "lib_install", "lib_deps", "lib_force",
"lib_ignore", "lib_extra_dirs", "lib_ldf_mode", "lib_compat_mode",
"test_ignore", "test_port", "piotest")
"test_ignore", "test_port", "piotest", "debug_link", "debug_port",
"debug_gdbinit")
REMAPED_OPTIONS = {"framework": "pioframework", "platform": "pioplatform"}

View File

@ -22,7 +22,7 @@ from platformio.managers.package import PackageManager
CORE_PACKAGES = {
"pysite-pioplus": ">=0.3.0,<2",
"tool-pioplus": ">=0.6.10,<2",
"tool-pioplus": ">=0.7.1,<2",
"tool-unity": "~1.20302.1",
"tool-scons": "~3.20501.2"
}