runners: enforce RunnerCaps via create() indirection
Require all implementations to provide a do_create(), a new ZephyrBinaryRunner abstract class method, and make create() itself concrete. This allows us to enforce common conventions related to individual runner capabilities as each runner provides to the core via RunnerCaps. For now, just enforce that: - common options related to capabilities are always added, so runners can't reuse them for different ends - common options provided for runners which don't support them emit sensible error messages that should be easy to diagnose and support Signed-off-by: Martí Bolívar <marti.bolivar@nordicsemi.no>
This commit is contained in:
parent
7b93bd54d4
commit
f8e8e9229d
21 changed files with 52 additions and 24 deletions
|
@ -210,8 +210,8 @@ def do_run_common(command, user_args, user_runner_args):
|
||||||
#
|
#
|
||||||
# Use that RunnerConfig to create the ZephyrBinaryRunner instance
|
# Use that RunnerConfig to create the ZephyrBinaryRunner instance
|
||||||
# and call its run().
|
# and call its run().
|
||||||
runner = runner_cls.create(runner_cfg_from_args(args, build_dir), args)
|
|
||||||
try:
|
try:
|
||||||
|
runner = runner_cls.create(runner_cfg_from_args(args, build_dir), args)
|
||||||
runner.run(command_name)
|
runner.run(command_name)
|
||||||
except ValueError as ve:
|
except ValueError as ve:
|
||||||
log.err(str(ve), fatal=True)
|
log.err(str(ve), fatal=True)
|
||||||
|
|
|
@ -27,7 +27,7 @@ class BlackMagicProbeRunner(ZephyrBinaryRunner):
|
||||||
return RunnerCaps(commands={'flash', 'debug', 'attach'})
|
return RunnerCaps(commands={'flash', 'debug', 'attach'})
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(cls, cfg, args):
|
def do_create(cls, cfg, args):
|
||||||
return BlackMagicProbeRunner(cfg, args.gdb_serial)
|
return BlackMagicProbeRunner(cfg, args.gdb_serial)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
|
@ -41,7 +41,7 @@ class BossacBinaryRunner(ZephyrBinaryRunner):
|
||||||
help='serial port to use, default is /dev/ttyACM0')
|
help='serial port to use, default is /dev/ttyACM0')
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(cls, cfg, args):
|
def do_create(cls, cfg, args):
|
||||||
return BossacBinaryRunner(cfg, bossac=args.bossac,
|
return BossacBinaryRunner(cfg, bossac=args.bossac,
|
||||||
port=args.bossac_port, offset=args.offset)
|
port=args.bossac_port, offset=args.offset)
|
||||||
|
|
||||||
|
|
|
@ -87,7 +87,7 @@ class CANopenBinaryRunner(ZephyrBinaryRunner):
|
||||||
parser.set_defaults(confirm=True)
|
parser.set_defaults(confirm=True)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(cls, cfg, args):
|
def do_create(cls, cfg, args):
|
||||||
return CANopenBinaryRunner(cfg, int(args.node_id),
|
return CANopenBinaryRunner(cfg, int(args.node_id),
|
||||||
can_context=args.can_context,
|
can_context=args.can_context,
|
||||||
program_number=int(args.program_number),
|
program_number=int(args.program_number),
|
||||||
|
|
|
@ -199,6 +199,16 @@ class RunnerCaps:
|
||||||
self.commands, self.flash_addr)
|
self.commands, self.flash_addr)
|
||||||
|
|
||||||
|
|
||||||
|
def _missing_cap(cls, option):
|
||||||
|
# Helper function that's called when an option was given on the
|
||||||
|
# command line that corresponds to a missing capability.
|
||||||
|
#
|
||||||
|
# 'cls' is a ZephyrBinaryRunner subclass; 'option' is an option
|
||||||
|
# that can't be supported due to missing capability.
|
||||||
|
|
||||||
|
raise ValueError(f"{cls.name()} doesn't support {option} option")
|
||||||
|
|
||||||
|
|
||||||
class RunnerConfig:
|
class RunnerConfig:
|
||||||
'''Runner execution-time configuration.
|
'''Runner execution-time configuration.
|
||||||
|
|
||||||
|
@ -260,7 +270,9 @@ class _DTFlashAction(argparse.Action):
|
||||||
class ZephyrBinaryRunner(abc.ABC):
|
class ZephyrBinaryRunner(abc.ABC):
|
||||||
'''Abstract superclass for binary runners (flashers, debuggers).
|
'''Abstract superclass for binary runners (flashers, debuggers).
|
||||||
|
|
||||||
**Note**: these APIs are still evolving, and will change!
|
**Note**: this class's API has changed relatively rarely since it
|
||||||
|
as added, but it is not considered a stable Zephyr API, and may change
|
||||||
|
without notice.
|
||||||
|
|
||||||
With some exceptions, boards supported by Zephyr must provide
|
With some exceptions, boards supported by Zephyr must provide
|
||||||
generic means to be flashed (have a Zephyr firmware binary
|
generic means to be flashed (have a Zephyr firmware binary
|
||||||
|
@ -389,13 +401,20 @@ class ZephyrBinaryRunner(abc.ABC):
|
||||||
|
|
||||||
Runner-specific options are added through the do_add_parser()
|
Runner-specific options are added through the do_add_parser()
|
||||||
hook.'''
|
hook.'''
|
||||||
# Common options that depend on runner capabilities.
|
# Common options that depend on runner capabilities. If a
|
||||||
if cls.capabilities().flash_addr:
|
# capability is not supported, the option string or strings
|
||||||
|
# are added anyway, to prevent an individual runner class from
|
||||||
|
# using them to mean something else.
|
||||||
|
caps = cls.capabilities()
|
||||||
|
|
||||||
|
if caps.flash_addr:
|
||||||
parser.add_argument('--dt-flash', default='n', choices=_YN_CHOICES,
|
parser.add_argument('--dt-flash', default='n', choices=_YN_CHOICES,
|
||||||
action=_DTFlashAction,
|
action=_DTFlashAction,
|
||||||
help='''If 'yes', use configuration generated
|
help='''If 'yes', use configuration generated
|
||||||
by device tree (DT) to compute flash
|
by device tree (DT) to compute flash
|
||||||
addresses.''')
|
addresses.''')
|
||||||
|
else:
|
||||||
|
parser.add_argument('--dt-flash', help=argparse.SUPPRESS)
|
||||||
|
|
||||||
# Runner-specific options.
|
# Runner-specific options.
|
||||||
cls.do_add_parser(parser)
|
cls.do_add_parser(parser)
|
||||||
|
@ -406,13 +425,22 @@ class ZephyrBinaryRunner(abc.ABC):
|
||||||
'''Hook for adding runner-specific options.'''
|
'''Hook for adding runner-specific options.'''
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@abc.abstractmethod
|
|
||||||
def create(cls, cfg, args):
|
def create(cls, cfg, args):
|
||||||
'''Create an instance from command-line arguments.
|
'''Create an instance from command-line arguments.
|
||||||
|
|
||||||
- ``cfg``: RunnerConfig instance (pass to superclass __init__)
|
- ``cfg``: RunnerConfig instance (pass to superclass __init__)
|
||||||
- ``args``: runner-specific argument namespace parsed from
|
- ``args``: runner-specific argument namespace parsed from
|
||||||
execution environment, as specified by ``add_parser()``.'''
|
execution environment, as specified by ``add_parser()``.'''
|
||||||
|
caps = cls.capabilities()
|
||||||
|
if args.dt_flash and not caps.flash_addr:
|
||||||
|
_missing_cap(cls, '--dt-flash')
|
||||||
|
|
||||||
|
return cls.do_create(cfg, args)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
@abc.abstractmethod
|
||||||
|
def do_create(cls, cfg, args):
|
||||||
|
'''Hook for instance creation from command line arguments.'''
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_flash_address(cls, args, build_conf, default=0x0):
|
def get_flash_address(cls, args, build_conf, default=0x0):
|
||||||
|
|
|
@ -40,7 +40,7 @@ class DediProgBinaryRunner(ZephyrBinaryRunner):
|
||||||
help='Number of retries (default 5)')
|
help='Number of retries (default 5)')
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(cls, cfg, args):
|
def do_create(cls, cfg, args):
|
||||||
return DediProgBinaryRunner(cfg,
|
return DediProgBinaryRunner(cfg,
|
||||||
spi_image=args.spi_image,
|
spi_image=args.spi_image,
|
||||||
vcc=args.vcc,
|
vcc=args.vcc,
|
||||||
|
|
|
@ -74,7 +74,7 @@ class DfuUtilBinaryRunner(ZephyrBinaryRunner):
|
||||||
help='dfu-util executable; defaults to "dfu-util"')
|
help='dfu-util executable; defaults to "dfu-util"')
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(cls, cfg, args):
|
def do_create(cls, cfg, args):
|
||||||
if args.img is None:
|
if args.img is None:
|
||||||
args.img = cfg.bin_file
|
args.img = cfg.bin_file
|
||||||
|
|
||||||
|
|
|
@ -63,7 +63,7 @@ class Esp32BinaryRunner(ZephyrBinaryRunner):
|
||||||
help='Partition table to flash')
|
help='Partition table to flash')
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(cls, cfg, args):
|
def do_create(cls, cfg, args):
|
||||||
if args.esp_tool:
|
if args.esp_tool:
|
||||||
espidf = args.esp_tool
|
espidf = args.esp_tool
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -29,7 +29,7 @@ class HiFive1BinaryRunner(ZephyrBinaryRunner):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(cls, cfg, args):
|
def do_create(cls, cfg, args):
|
||||||
if cfg.gdb is None:
|
if cfg.gdb is None:
|
||||||
raise ValueError('--gdb not provided at command line')
|
raise ValueError('--gdb not provided at command line')
|
||||||
|
|
||||||
|
|
|
@ -53,7 +53,7 @@ class IntelS1000BinaryRunner(ZephyrBinaryRunner):
|
||||||
help='xt-gdb port, defaults to 20000')
|
help='xt-gdb port, defaults to 20000')
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(cls, cfg, args):
|
def do_create(cls, cfg, args):
|
||||||
return IntelS1000BinaryRunner(
|
return IntelS1000BinaryRunner(
|
||||||
cfg, args.xt_ocd_dir,
|
cfg, args.xt_ocd_dir,
|
||||||
args.ocd_topology, args.ocd_jtag_instr, args.gdb_flash_file,
|
args.ocd_topology, args.ocd_jtag_instr, args.gdb_flash_file,
|
||||||
|
|
|
@ -90,7 +90,7 @@ class JLinkBinaryRunner(ZephyrBinaryRunner):
|
||||||
parser.set_defaults(reset_after_load=False)
|
parser.set_defaults(reset_after_load=False)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(cls, cfg, args):
|
def do_create(cls, cfg, args):
|
||||||
build_conf = BuildConfiguration(cfg.build_dir)
|
build_conf = BuildConfiguration(cfg.build_dir)
|
||||||
flash_addr = cls.get_flash_address(args, build_conf)
|
flash_addr = cls.get_flash_address(args, build_conf)
|
||||||
return JLinkBinaryRunner(cfg, args.device,
|
return JLinkBinaryRunner(cfg, args.device,
|
||||||
|
|
|
@ -58,7 +58,7 @@ class MdbBinaryRunner(ZephyrBinaryRunner):
|
||||||
targets are connected''')
|
targets are connected''')
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(cls, cfg, args):
|
def do_create(cls, cfg, args):
|
||||||
return MdbBinaryRunner(
|
return MdbBinaryRunner(
|
||||||
cfg,
|
cfg,
|
||||||
cores=args.cores,
|
cores=args.cores,
|
||||||
|
|
|
@ -43,7 +43,7 @@ class MiscFlasher(ZephyrBinaryRunner):
|
||||||
directory''')
|
directory''')
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(cls, cfg, args):
|
def do_create(cls, cfg, args):
|
||||||
return MiscFlasher(cfg, args.cmd, args.args)
|
return MiscFlasher(cfg, args.cmd, args.args)
|
||||||
|
|
||||||
def do_run(self, *args, **kwargs):
|
def do_run(self, *args, **kwargs):
|
||||||
|
|
|
@ -41,7 +41,7 @@ class Nios2BinaryRunner(ZephyrBinaryRunner):
|
||||||
help='if given, GDB uses -tui')
|
help='if given, GDB uses -tui')
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(cls, cfg, args):
|
def do_create(cls, cfg, args):
|
||||||
return Nios2BinaryRunner(cfg,
|
return Nios2BinaryRunner(cfg,
|
||||||
quartus_py=args.quartus_flash,
|
quartus_py=args.quartus_flash,
|
||||||
cpu_sof=args.cpu_sof,
|
cpu_sof=args.cpu_sof,
|
||||||
|
|
|
@ -53,7 +53,7 @@ class NrfJprogBinaryRunner(ZephyrBinaryRunner):
|
||||||
e.g. "--recover"''')
|
e.g. "--recover"''')
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(cls, cfg, args):
|
def do_create(cls, cfg, args):
|
||||||
return NrfJprogBinaryRunner(cfg, args.nrf_family, args.softreset,
|
return NrfJprogBinaryRunner(cfg, args.nrf_family, args.softreset,
|
||||||
args.snr, erase=args.erase,
|
args.snr, erase=args.erase,
|
||||||
tool_opt=args.tool_opt)
|
tool_opt=args.tool_opt)
|
||||||
|
|
|
@ -45,7 +45,7 @@ class NsimBinaryRunner(ZephyrBinaryRunner):
|
||||||
help='nsim props file, defaults to nsim.props')
|
help='nsim props file, defaults to nsim.props')
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(cls, cfg, args):
|
def do_create(cls, cfg, args):
|
||||||
if cfg.gdb is None:
|
if cfg.gdb is None:
|
||||||
raise ValueError('--gdb not provided at command line')
|
raise ValueError('--gdb not provided at command line')
|
||||||
|
|
||||||
|
|
|
@ -98,7 +98,7 @@ class OpenOcdBinaryRunner(ZephyrBinaryRunner):
|
||||||
help='openocd gdb port, defaults to 3333')
|
help='openocd gdb port, defaults to 3333')
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(cls, cfg, args):
|
def do_create(cls, cfg, args):
|
||||||
return OpenOcdBinaryRunner(
|
return OpenOcdBinaryRunner(
|
||||||
cfg,
|
cfg,
|
||||||
pre_init=args.cmd_pre_init,
|
pre_init=args.cmd_pre_init,
|
||||||
|
|
|
@ -95,7 +95,7 @@ class PyOcdBinaryRunner(ZephyrBinaryRunner):
|
||||||
e.g. \'--script=user.py\' ''')
|
e.g. \'--script=user.py\' ''')
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(cls, cfg, args):
|
def do_create(cls, cfg, args):
|
||||||
build_conf = BuildConfiguration(cfg.build_dir)
|
build_conf = BuildConfiguration(cfg.build_dir)
|
||||||
flash_addr = cls.get_flash_address(args, build_conf)
|
flash_addr = cls.get_flash_address(args, build_conf)
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ class QemuBinaryRunner(ZephyrBinaryRunner):
|
||||||
pass # Nothing to do.
|
pass # Nothing to do.
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(cls, cfg, args):
|
def do_create(cls, cfg, args):
|
||||||
return QemuBinaryRunner(cfg)
|
return QemuBinaryRunner(cfg)
|
||||||
|
|
||||||
def do_run(self, command, **kwargs):
|
def do_run(self, command, **kwargs):
|
||||||
|
|
|
@ -79,7 +79,7 @@ class Stm32flashBinaryRunner(ZephyrBinaryRunner):
|
||||||
help='verify writes, default False')
|
help='verify writes, default False')
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(cls, cfg, args):
|
def do_create(cls, cfg, args):
|
||||||
return Stm32flashBinaryRunner(cfg, device=args.device, action=args.action,
|
return Stm32flashBinaryRunner(cfg, device=args.device, action=args.action,
|
||||||
baud=args.baud_rate, force_binary=args.force_binary,
|
baud=args.baud_rate, force_binary=args.force_binary,
|
||||||
start_addr=args.start_addr, exec_addr=args.execution_addr,
|
start_addr=args.start_addr, exec_addr=args.execution_addr,
|
||||||
|
|
|
@ -26,7 +26,7 @@ class XtensaBinaryRunner(ZephyrBinaryRunner):
|
||||||
help='path to XTensa tools')
|
help='path to XTensa tools')
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(cls, cfg, args):
|
def do_create(cls, cfg, args):
|
||||||
# Override any GDB with the one provided by the XTensa tools.
|
# Override any GDB with the one provided by the XTensa tools.
|
||||||
cfg.gdb = path.join(args.xcc_tools, 'bin', 'xt-gdb')
|
cfg.gdb = path.join(args.xcc_tools, 'bin', 'xt-gdb')
|
||||||
return XtensaBinaryRunner(cfg)
|
return XtensaBinaryRunner(cfg)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue