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
|
||||
# and call its run().
|
||||
runner = runner_cls.create(runner_cfg_from_args(args, build_dir), args)
|
||||
try:
|
||||
runner = runner_cls.create(runner_cfg_from_args(args, build_dir), args)
|
||||
runner.run(command_name)
|
||||
except ValueError as ve:
|
||||
log.err(str(ve), fatal=True)
|
||||
|
|
|
@ -27,7 +27,7 @@ class BlackMagicProbeRunner(ZephyrBinaryRunner):
|
|||
return RunnerCaps(commands={'flash', 'debug', 'attach'})
|
||||
|
||||
@classmethod
|
||||
def create(cls, cfg, args):
|
||||
def do_create(cls, cfg, args):
|
||||
return BlackMagicProbeRunner(cfg, args.gdb_serial)
|
||||
|
||||
@classmethod
|
||||
|
|
|
@ -41,7 +41,7 @@ class BossacBinaryRunner(ZephyrBinaryRunner):
|
|||
help='serial port to use, default is /dev/ttyACM0')
|
||||
|
||||
@classmethod
|
||||
def create(cls, cfg, args):
|
||||
def do_create(cls, cfg, args):
|
||||
return BossacBinaryRunner(cfg, bossac=args.bossac,
|
||||
port=args.bossac_port, offset=args.offset)
|
||||
|
||||
|
|
|
@ -87,7 +87,7 @@ class CANopenBinaryRunner(ZephyrBinaryRunner):
|
|||
parser.set_defaults(confirm=True)
|
||||
|
||||
@classmethod
|
||||
def create(cls, cfg, args):
|
||||
def do_create(cls, cfg, args):
|
||||
return CANopenBinaryRunner(cfg, int(args.node_id),
|
||||
can_context=args.can_context,
|
||||
program_number=int(args.program_number),
|
||||
|
|
|
@ -199,6 +199,16 @@ class RunnerCaps:
|
|||
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:
|
||||
'''Runner execution-time configuration.
|
||||
|
||||
|
@ -260,7 +270,9 @@ class _DTFlashAction(argparse.Action):
|
|||
class ZephyrBinaryRunner(abc.ABC):
|
||||
'''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
|
||||
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()
|
||||
hook.'''
|
||||
# Common options that depend on runner capabilities.
|
||||
if cls.capabilities().flash_addr:
|
||||
# Common options that depend on runner capabilities. If a
|
||||
# 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,
|
||||
action=_DTFlashAction,
|
||||
help='''If 'yes', use configuration generated
|
||||
by device tree (DT) to compute flash
|
||||
addresses.''')
|
||||
else:
|
||||
parser.add_argument('--dt-flash', help=argparse.SUPPRESS)
|
||||
|
||||
# Runner-specific options.
|
||||
cls.do_add_parser(parser)
|
||||
|
@ -406,13 +425,22 @@ class ZephyrBinaryRunner(abc.ABC):
|
|||
'''Hook for adding runner-specific options.'''
|
||||
|
||||
@classmethod
|
||||
@abc.abstractmethod
|
||||
def create(cls, cfg, args):
|
||||
'''Create an instance from command-line arguments.
|
||||
|
||||
- ``cfg``: RunnerConfig instance (pass to superclass __init__)
|
||||
- ``args``: runner-specific argument namespace parsed from
|
||||
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
|
||||
def get_flash_address(cls, args, build_conf, default=0x0):
|
||||
|
|
|
@ -40,7 +40,7 @@ class DediProgBinaryRunner(ZephyrBinaryRunner):
|
|||
help='Number of retries (default 5)')
|
||||
|
||||
@classmethod
|
||||
def create(cls, cfg, args):
|
||||
def do_create(cls, cfg, args):
|
||||
return DediProgBinaryRunner(cfg,
|
||||
spi_image=args.spi_image,
|
||||
vcc=args.vcc,
|
||||
|
|
|
@ -74,7 +74,7 @@ class DfuUtilBinaryRunner(ZephyrBinaryRunner):
|
|||
help='dfu-util executable; defaults to "dfu-util"')
|
||||
|
||||
@classmethod
|
||||
def create(cls, cfg, args):
|
||||
def do_create(cls, cfg, args):
|
||||
if args.img is None:
|
||||
args.img = cfg.bin_file
|
||||
|
||||
|
|
|
@ -63,7 +63,7 @@ class Esp32BinaryRunner(ZephyrBinaryRunner):
|
|||
help='Partition table to flash')
|
||||
|
||||
@classmethod
|
||||
def create(cls, cfg, args):
|
||||
def do_create(cls, cfg, args):
|
||||
if args.esp_tool:
|
||||
espidf = args.esp_tool
|
||||
else:
|
||||
|
|
|
@ -29,7 +29,7 @@ class HiFive1BinaryRunner(ZephyrBinaryRunner):
|
|||
pass
|
||||
|
||||
@classmethod
|
||||
def create(cls, cfg, args):
|
||||
def do_create(cls, cfg, args):
|
||||
if cfg.gdb is None:
|
||||
raise ValueError('--gdb not provided at command line')
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@ class IntelS1000BinaryRunner(ZephyrBinaryRunner):
|
|||
help='xt-gdb port, defaults to 20000')
|
||||
|
||||
@classmethod
|
||||
def create(cls, cfg, args):
|
||||
def do_create(cls, cfg, args):
|
||||
return IntelS1000BinaryRunner(
|
||||
cfg, args.xt_ocd_dir,
|
||||
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)
|
||||
|
||||
@classmethod
|
||||
def create(cls, cfg, args):
|
||||
def do_create(cls, cfg, args):
|
||||
build_conf = BuildConfiguration(cfg.build_dir)
|
||||
flash_addr = cls.get_flash_address(args, build_conf)
|
||||
return JLinkBinaryRunner(cfg, args.device,
|
||||
|
|
|
@ -58,7 +58,7 @@ class MdbBinaryRunner(ZephyrBinaryRunner):
|
|||
targets are connected''')
|
||||
|
||||
@classmethod
|
||||
def create(cls, cfg, args):
|
||||
def do_create(cls, cfg, args):
|
||||
return MdbBinaryRunner(
|
||||
cfg,
|
||||
cores=args.cores,
|
||||
|
|
|
@ -43,7 +43,7 @@ class MiscFlasher(ZephyrBinaryRunner):
|
|||
directory''')
|
||||
|
||||
@classmethod
|
||||
def create(cls, cfg, args):
|
||||
def do_create(cls, cfg, args):
|
||||
return MiscFlasher(cfg, args.cmd, args.args)
|
||||
|
||||
def do_run(self, *args, **kwargs):
|
||||
|
|
|
@ -41,7 +41,7 @@ class Nios2BinaryRunner(ZephyrBinaryRunner):
|
|||
help='if given, GDB uses -tui')
|
||||
|
||||
@classmethod
|
||||
def create(cls, cfg, args):
|
||||
def do_create(cls, cfg, args):
|
||||
return Nios2BinaryRunner(cfg,
|
||||
quartus_py=args.quartus_flash,
|
||||
cpu_sof=args.cpu_sof,
|
||||
|
|
|
@ -53,7 +53,7 @@ class NrfJprogBinaryRunner(ZephyrBinaryRunner):
|
|||
e.g. "--recover"''')
|
||||
|
||||
@classmethod
|
||||
def create(cls, cfg, args):
|
||||
def do_create(cls, cfg, args):
|
||||
return NrfJprogBinaryRunner(cfg, args.nrf_family, args.softreset,
|
||||
args.snr, erase=args.erase,
|
||||
tool_opt=args.tool_opt)
|
||||
|
|
|
@ -45,7 +45,7 @@ class NsimBinaryRunner(ZephyrBinaryRunner):
|
|||
help='nsim props file, defaults to nsim.props')
|
||||
|
||||
@classmethod
|
||||
def create(cls, cfg, args):
|
||||
def do_create(cls, cfg, args):
|
||||
if cfg.gdb is None:
|
||||
raise ValueError('--gdb not provided at command line')
|
||||
|
||||
|
|
|
@ -98,7 +98,7 @@ class OpenOcdBinaryRunner(ZephyrBinaryRunner):
|
|||
help='openocd gdb port, defaults to 3333')
|
||||
|
||||
@classmethod
|
||||
def create(cls, cfg, args):
|
||||
def do_create(cls, cfg, args):
|
||||
return OpenOcdBinaryRunner(
|
||||
cfg,
|
||||
pre_init=args.cmd_pre_init,
|
||||
|
|
|
@ -95,7 +95,7 @@ class PyOcdBinaryRunner(ZephyrBinaryRunner):
|
|||
e.g. \'--script=user.py\' ''')
|
||||
|
||||
@classmethod
|
||||
def create(cls, cfg, args):
|
||||
def do_create(cls, cfg, args):
|
||||
build_conf = BuildConfiguration(cfg.build_dir)
|
||||
flash_addr = cls.get_flash_address(args, build_conf)
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ class QemuBinaryRunner(ZephyrBinaryRunner):
|
|||
pass # Nothing to do.
|
||||
|
||||
@classmethod
|
||||
def create(cls, cfg, args):
|
||||
def do_create(cls, cfg, args):
|
||||
return QemuBinaryRunner(cfg)
|
||||
|
||||
def do_run(self, command, **kwargs):
|
||||
|
|
|
@ -79,7 +79,7 @@ class Stm32flashBinaryRunner(ZephyrBinaryRunner):
|
|||
help='verify writes, default False')
|
||||
|
||||
@classmethod
|
||||
def create(cls, cfg, args):
|
||||
def do_create(cls, cfg, args):
|
||||
return Stm32flashBinaryRunner(cfg, device=args.device, action=args.action,
|
||||
baud=args.baud_rate, force_binary=args.force_binary,
|
||||
start_addr=args.start_addr, exec_addr=args.execution_addr,
|
||||
|
|
|
@ -26,7 +26,7 @@ class XtensaBinaryRunner(ZephyrBinaryRunner):
|
|||
help='path to XTensa tools')
|
||||
|
||||
@classmethod
|
||||
def create(cls, cfg, args):
|
||||
def do_create(cls, cfg, args):
|
||||
# Override any GDB with the one provided by the XTensa tools.
|
||||
cfg.gdb = path.join(args.xcc_tools, 'bin', 'xt-gdb')
|
||||
return XtensaBinaryRunner(cfg)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue