From 1ca76fae9309425e7fc72edc67d14fa144322361 Mon Sep 17 00:00:00 2001 From: Taylor Cramer Date: Tue, 25 Jun 2024 00:26:37 +0000 Subject: [PATCH] rp2040: Unify board selection cmdline args MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix: b/348067379 Change-Id: I0fa21d1cff72a2225286b471e1b85c3613b8b8e1 Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/217716 Lint: Lint 🤖 Commit-Queue: Taylor Cramer Presubmit-Verified: CQ Bot Account Reviewed-by: Erik Gilling --- targets/rp2040/py/rp2040_utils/flasher.py | 92 +++++++----- .../py/rp2040_utils/rpc_unit_test_runner.py | 133 ++---------------- 2 files changed, 71 insertions(+), 154 deletions(-) diff --git a/targets/rp2040/py/rp2040_utils/flasher.py b/targets/rp2040/py/rp2040_utils/flasher.py index 5409d4c72d..33616fd3ca 100644 --- a/targets/rp2040/py/rp2040_utils/flasher.py +++ b/targets/rp2040/py/rp2040_utils/flasher.py @@ -95,7 +95,8 @@ def _load_picotool_binary(board_info: PicoBoardInfo, binary: Path) -> bool: _PICOTOOL_COMMAND, 'load', '-x', - str(binary), + # We use the absolute path since `cwd` is changed below. + str(binary.absolute()), ] # If the binary has not file extension, assume that it is ELF and @@ -247,9 +248,8 @@ def _load_debugprobe_binary( return True -def _parse_args(): - """Parses command-line arguments.""" - +def create_flash_parser() -> argparse.ArgumentParser: + """Returns a parser for flashing command-line arguments.""" parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('binary', type=Path, help='The target binary to flash') parser.add_argument( @@ -290,53 +290,73 @@ def _parse_args(): help='Output additional logs as the script runs', ) - return parser.parse_args() + return parser -def main(): - """Flash a binary.""" - args = _parse_args() - log_level = logging.DEBUG if args.verbose else logging.INFO - pw_cli.log.install(level=log_level) +def device_from_args( + args: argparse.Namespace, interactive: bool +) -> PicoBoardInfo: + """Select a PicoBoardInfo using the provided `args`. + This function will exit if no compatible board is discovered. + + Args: + args: The parsed args namespace. This must be a set of arguments parsed + using `create_flash_parser`. + interactive: If true, multiple detected boards will result in a user + interaction to select which to use. If false, the first compatible + board will be used. + + Returns: + Selected PicoBoardInfo. + """ if args.pico_only and args.debug_probe_only: _LOG.critical('Cannot specify both --pico-only and --debug-probe-only') sys.exit(1) # For now, require manual configurations to be fully specified. - if (args.usb_port is not None or args.usb_bus is not None) and not ( - args.usb_port is not None and args.usb_bus is not None - ): + if (args.usb_port is None) != (args.usb_bus is None): _LOG.critical( 'Must specify BOTH --usb-bus and --usb-port when manually ' 'specifying a device' ) sys.exit(1) - if args.usb_bus: - board = device_detector.board_from_usb_port(args.usb_bus, args.usb_port) - else: - _LOG.debug('Attempting to automatically detect dev board') - boards = device_detector.detect_boards( - include_picos=not args.debug_probe_only, - include_debug_probes=not args.pico_only, + if args.usb_bus and args.usb_port: + return device_detector.board_from_usb_port(args.usb_bus, args.usb_port) + + _LOG.debug('Attempting to automatically detect dev board') + boards = device_detector.detect_boards( + include_picos=not args.debug_probe_only, + include_debug_probes=not args.pico_only, + ) + if not boards: + _LOG.error('Could not find an attached device') + sys.exit(1) + if len(boards) == 1: + _LOG.info('Only one device detected.') + return boards[0] + if not interactive: + _LOG.info( + 'Interactive mode disabled. Defaulting to first discovered device.' ) - if not boards: - _LOG.error('Could not find an attached device') - sys.exit(1) - if len(boards) == 1: - _LOG.info('Only one device detected.') - board = boards[0] - else: - print('Multiple devices detected. Please select one:') - for n, board_n in enumerate(boards): - print(f' {n}: bus {board_n.bus}, port {board_n.port}') - print() - user_input = input('--> Board index (default: 0): ') - if user_input == '': - board = boards[0] - else: - board = boards[int(user_input)] + return boards[0] + print('Multiple devices detected. Please select one:') + for n, board_n in enumerate(boards): + print(f' {n}: bus {board_n.bus}, port {board_n.port}') + print() + user_input = input('--> Board index (default: 0): ') + if user_input == '': + return boards[0] + return boards[int(user_input)] + + +def main(): + """Flash a binary.""" + args = create_flash_parser().parse_args() + log_level = logging.DEBUG if args.verbose else logging.INFO + pw_cli.log.install(level=log_level) + board = device_from_args(args, interactive=True) _LOG.info('Flashing bus %s port %s', board.bus, board.port) flashed = flash(board, args.baud, args.binary) sys.exit(0 if flashed else 1) diff --git a/targets/rp2040/py/rp2040_utils/rpc_unit_test_runner.py b/targets/rp2040/py/rp2040_utils/rpc_unit_test_runner.py index 8e1fa9b778..7d661f449d 100644 --- a/targets/rp2040/py/rp2040_utils/rpc_unit_test_runner.py +++ b/targets/rp2040/py/rp2040_utils/rpc_unit_test_runner.py @@ -29,40 +29,21 @@ from pw_tokenizer import detokenize from pw_unit_test_proto import unit_test_pb2 -from rp2040_utils import device_detector from rp2040_utils.device_detector import PicoBoardInfo -from rp2040_utils.flasher import flash, find_elf +from rp2040_utils.flasher import ( + create_flash_parser, + device_from_args, + flash, + find_elf, +) _LOG = logging.getLogger() -def parse_args(): +def create_test_runner_parser() -> argparse.ArgumentParser: """Parses command-line arguments.""" - - parser = argparse.ArgumentParser(description=__doc__) - parser.add_argument( - 'binary', type=Path, help='The target test binary to run' - ) - parser.add_argument( - '--usb-bus', - type=int, - help='The bus this Pi Pico is on', - ) - parser.add_argument( - '--usb-port', - type=str, - help=( - 'The port chain as a colon-separated list of integers of this Pi ' - 'Pico on the specified USB bus (e.g. 1:4:2:2)' - ), - ) - parser.add_argument( - '-b', - '--baud', - type=int, - default=115200, - help='Baud rate to use for serial communication with target device', - ) + parser = create_flash_parser() + parser.description = __doc__ parser.add_argument( '--test-timeout', type=float, @@ -70,25 +51,7 @@ def parse_args(): help='Maximum communication delay in seconds before a ' 'test is considered unresponsive and aborted', ) - parser.add_argument( - '--debug-probe-only', - action='store_true', - help='Only run tests on detected Pi Pico debug probes', - ) - parser.add_argument( - '--pico-only', - action='store_true', - help='Only run tests on detected Pi Pico boards', - ) - parser.add_argument( - '--verbose', - '-v', - dest='verbose', - action='store_true', - help='Output additional logs as the script runs', - ) - - return parser.parse_args() + return parser def run_test_on_board( @@ -126,83 +89,17 @@ def run_test_on_board( return True -def run_device_test( - binary: Path, - test_timeout_seconds: float, - baud_rate: int, - usb_bus: int, - usb_port: str, -) -> bool: - """Flash, run, and check an on-device test binary. - - Returns true on test pass. - """ - board = device_detector.board_from_usb_port(usb_bus, usb_port) - return run_test_on_board(board, baud_rate, binary, test_timeout_seconds) - - -def detect_and_run_test( - binary: Path, - test_timeout_seconds: float, - baud_rate: int, - include_picos: bool = True, - include_debug_probes: bool = True, -) -> bool: - """Detect a dev board and run a test binary on it. - - Returns whether or not the test completed successfully. - """ - _LOG.debug('Attempting to automatically detect dev board') - boards = device_detector.detect_boards( - include_picos=include_picos, - include_debug_probes=include_debug_probes, - ) - if not boards: - _LOG.error('Could not find an attached device') - return False - return run_test_on_board(boards[0], baud_rate, binary, test_timeout_seconds) - - def main(): """Set up runner and then flash/run device test.""" - args = parse_args() - + args = create_test_runner_parser().parse_args() # Log to stdout, which will be captured by the unit test server. pw_cli.log.install( level=logging.DEBUG if args.verbose else logging.INFO, ) - - if args.pico_only and args.debug_probe_only: - _LOG.critical('Cannot specify both --pico-only and --debug-probe-only') - sys.exit(1) - - # For now, require manual configurations to be fully specified. - if (args.usb_port is not None or args.usb_bus is not None) and not ( - args.usb_port is not None and args.usb_bus is not None - ): - _LOG.critical( - 'Must specify BOTH --usb-bus and --usb-port when manually ' - 'specifying a device' - ) - sys.exit(1) - - test_passed = False - if not args.usb_bus: - test_passed = detect_and_run_test( - args.binary, - args.test_timeout, - args.baud, - not args.debug_probe_only, - not args.pico_only, - ) - else: - test_passed = run_device_test( - args.binary, - args.test_timeout, - args.baud, - args.usb_bus, - args.usb_port, - ) + board = device_from_args(args, interactive=False) + test_passed = run_test_on_board( + board, args.baud, args.binary, args.test_timeout + ) sys.exit(0 if test_passed else 1)