1# Copyright (c) 2023 Nordic Semiconductor
2# SPDX-License-Identifier: Apache-2.0
3
4"""This file provides a ZephyrBinaryRunner that launches GDB and enables
5flashing (running) a native application."""
6
7import argparse
8
9from runners.core import RunnerCaps, RunnerConfig, ZephyrBinaryRunner
10
11DEFAULT_GDB_PORT = 3333
12
13class NativeSimBinaryRunner(ZephyrBinaryRunner):
14    """Runs the ELF binary under GDB."""
15
16    def __init__(self, cfg,
17                 tui=False,
18                 gdb_port=DEFAULT_GDB_PORT):
19        super().__init__(cfg)
20        self.gdb_port = gdb_port
21
22        if cfg.gdb is None:
23            self.gdb_cmd = None
24        else:
25            self.gdb_cmd = [cfg.gdb] + (['-tui'] if tui else [])
26
27        if self.cfg.gdb is None:
28            raise ValueError("The provided RunnerConfig is missing the required field 'gdb'.")
29
30        if self.cfg.exe_file is None:
31            raise ValueError("The provided RunnerConfig is missing the required field 'exe_file'.")
32
33
34    @classmethod
35    def name(cls):
36        return 'native'
37
38    @classmethod
39    def capabilities(cls):
40        return RunnerCaps(commands={'debug', 'debugserver', 'flash'})
41
42    @classmethod
43    def do_add_parser(cls, parser: argparse.ArgumentParser):
44        parser.add_argument('--tui', default=False, action='store_true',
45                            help='if given, GDB uses -tui')
46        parser.add_argument('--gdb-port', default=DEFAULT_GDB_PORT,
47                            help=f'gdb port, defaults to {DEFAULT_GDB_PORT}')
48
49    @classmethod
50    def do_create(cls, cfg: RunnerConfig, args: argparse.Namespace) -> ZephyrBinaryRunner:
51        return NativeSimBinaryRunner(cfg,
52                                     tui=args.tui,
53                                     gdb_port=args.gdb_port)
54
55    def do_run(self, command: str, **kwargs):
56        if command == 'flash':
57            self.do_flash(**kwargs)
58        elif command == 'debug':
59            self.do_debug(**kwargs)
60        elif command == 'debugserver':
61            self.do_debugserver(**kwargs)
62        else:
63            raise AssertionError
64
65    def do_flash(self, **kwargs):
66        cmd = [self.cfg.exe_file]
67        self.check_call(cmd)
68
69    def do_debug(self, **kwargs):
70        # Clues to debug missing RunnerConfig values (in context of `west debug`):
71        #   build/zephyr/runners.yaml is missing `gdb` or `elf_file`.
72        #   board.cmake should have `board_finalize_runner_args(native)`.
73        #   build/CMakeCache.txt should have `CMAKE_GDB`.
74
75        cmd = (self.gdb_cmd + ['--quiet', self.cfg.exe_file])
76        self.check_call(cmd)
77
78    def do_debugserver(self, **kwargs):
79        cmd = (['gdbserver', f':{self.gdb_port}', self.cfg.exe_file])
80
81        self.check_call(cmd)
82