scripts: runners: rework RunnerCaps implementation

This provides miscellaneous quality of life improvements:

- We couldn't use dataclasses when this class was originally written.
  We can now, so move to dataclass to avoid having to write
  __repr__().

- Add missing validation for the advertised commands

- Add missing documentation for the 'file' capability

Signed-off-by: Martí Bolívar <mbolivar@amperecomputing.com>
This commit is contained in:
Martí Bolívar 2023-09-28 11:02:47 -07:00 committed by Carles Cufí
commit 23ed21c0a0

View file

@ -22,6 +22,7 @@ import shutil
import signal import signal
import subprocess import subprocess
import re import re
from dataclasses import dataclass, field
from functools import partial from functools import partial
from enum import Enum from enum import Enum
from inspect import isabstract from inspect import isabstract
@ -199,6 +200,9 @@ class MissingProgram(FileNotFoundError):
super().__init__(errno.ENOENT, os.strerror(errno.ENOENT), program) super().__init__(errno.ENOENT, os.strerror(errno.ENOENT), program)
_RUNNERCAPS_COMMANDS = {'flash', 'debug', 'debugserver', 'attach'}
@dataclass
class RunnerCaps: class RunnerCaps:
'''This class represents a runner class's capabilities. '''This class represents a runner class's capabilities.
@ -235,34 +239,23 @@ class RunnerCaps:
- tool_opt: whether the runner supports a --tool-opt (-O) option, which - tool_opt: whether the runner supports a --tool-opt (-O) option, which
can be given multiple times and is passed on to the underlying tool can be given multiple times and is passed on to the underlying tool
that the runner wraps. that the runner wraps.
- file: whether the runner supports a --file option, which specifies
exactly the file that should be used to flash, overriding any default
discovered in the build directory.
''' '''
def __init__(self, commands: Set[str] = field(default_factory=lambda: set(_RUNNERCAPS_COMMANDS))
commands: Set[str] = {'flash', 'debug', dev_id: bool = False
'debugserver', 'attach'}, flash_addr: bool = False
dev_id: bool = False, erase: bool = False
flash_addr: bool = False, reset: bool = False
erase: bool = False, tool_opt: bool = False
reset: bool = False, file: bool = False
tool_opt: bool = False,
file: bool = False):
self.commands = commands
self.dev_id = dev_id
self.flash_addr = bool(flash_addr)
self.erase = bool(erase)
self.reset = bool(reset)
self.tool_opt = bool(tool_opt)
self.file = bool(file)
def __str__(self): def __post_init__(self):
return (f'RunnerCaps(commands={self.commands}, ' if not self.commands.issubset(_RUNNERCAPS_COMMANDS):
f'dev_id={self.dev_id}, ' raise ValueError(f'{self.commands=} contains invalid command')
f'flash_addr={self.flash_addr}, '
f'erase={self.erase}, '
f'reset={self.reset}, '
f'tool_opt={self.tool_opt}, '
f'file={self.file}'
')')
def _missing_cap(cls: Type['ZephyrBinaryRunner'], option: str) -> NoReturn: def _missing_cap(cls: Type['ZephyrBinaryRunner'], option: str) -> NoReturn: