Skip to content

Commit b611e5b

Browse files
Marti Bolivarnashif
authored andcommitted
scripts: west: add flash, debug, debugserver commands
When run without any arguments, the commands work the same way that their CMake equivalents do. For example, if using the Ninja CMake generator, these are equivalent: west flash <----> ninja flash west debug <----> ninja debug west debugserver <----> ninja debugserver Like CMake's build tool mode, you can also run them from any directory in the system by passing the path to the build directory using --build-dir (-d): west flash -d build/my-board The commands will run the CMake-generated build system, so they keep dependencies up to date and users don't have to manually compile binaries between running CMake and using this tool. The commands also support important use cases that CMake can't: 1) Any arguments not handled by 'west flash' et al. are passed to the underlying runner. For example, if the runner supports --gdb-port, the default can be overridden like so: west debugserver --gdb-port=1234 Command processing by the 'west' command can also be halted using '--'; anything after that point (even if it's an option recognized by the west command) will be passed to the runner. Example: west debug -- --this-option-goes-to-the-debug-runner=foo 2) Any runner supported by the board can be selected at runtime using the -r (--runner) option. For example, if the board's flash runner defaults to nrfjprog but jlink is supported as well, it can be selected with: west flash -r jlink 3) The runner configuration can be persisted elsewhere, edited offline, and selected at runtime, using --cmake-cache (-c): west flash -c /home/me/some/other/CMakeCache.txt Signed-off-by: Marti Bolivar <[email protected]>
1 parent 4a354e8 commit b611e5b

File tree

4 files changed

+203
-1
lines changed

4 files changed

+203
-1
lines changed

scripts/meta/west/cmd/debug.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# Copyright (c) 2018 Open Source Foundries Limited.
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
'''west "debug" and "debugserver" commands.'''
6+
7+
from textwrap import dedent
8+
9+
from .run_common import desc_common, add_parser_common, do_run_common
10+
from . import WestCommand
11+
12+
13+
class Debug(WestCommand):
14+
15+
def __init__(self):
16+
super(Debug, self).__init__(
17+
'debug',
18+
'Connect to the board and start a debugging session.\n\n' +
19+
desc_common('debug'),
20+
accepts_unknown_args=True)
21+
22+
def do_add_parser(self, parser_adder):
23+
return add_parser_common(parser_adder, self)
24+
25+
def do_run(self, my_args, runner_args):
26+
do_run_common(self, my_args, runner_args,
27+
'ZEPHYR_BOARD_DEBUG_RUNNER')
28+
29+
30+
class DebugServer(WestCommand):
31+
32+
def __init__(self):
33+
super(DebugServer, self).__init__(
34+
'debugserver',
35+
dedent('''
36+
Connect to the board and accept debug networking connections.
37+
38+
The debug server binds to a known port, and allows client software
39+
started elsewhere to connect to it and debug the running
40+
Zephyr image.\n\n''') +
41+
desc_common('debugserver'),
42+
accepts_unknown_args=True)
43+
44+
def do_add_parser(self, parser_adder):
45+
return add_parser_common(parser_adder, self)
46+
47+
def do_run(self, my_args, runner_args):
48+
do_run_common(self, my_args, runner_args,
49+
'ZEPHYR_BOARD_DEBUG_RUNNER')

scripts/meta/west/cmd/flash.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Copyright (c) 2018 Open Source Foundries Limited.
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
'''west "flash" command'''
6+
7+
from .run_common import desc_common, add_parser_common, do_run_common
8+
from . import WestCommand
9+
10+
11+
class Flash(WestCommand):
12+
13+
def __init__(self):
14+
super(Flash, self).__init__(
15+
'flash',
16+
'Flash and run a binary onto a board.\n\n' +
17+
desc_common('flash'),
18+
accepts_unknown_args=True)
19+
20+
def do_add_parser(self, parser_adder):
21+
return add_parser_common(parser_adder, self)
22+
23+
def do_run(self, my_args, runner_args):
24+
do_run_common(self, my_args, runner_args,
25+
'ZEPHYR_BOARD_FLASH_RUNNER')
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
# Copyright (c) 2018 Open Source Foundries Limited.
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
'''Common code used by commands which execute runners.
6+
'''
7+
8+
import argparse
9+
from os import getcwd, chdir
10+
from subprocess import CalledProcessError
11+
from textwrap import dedent
12+
13+
from .. import cmake
14+
from .. import log
15+
from ..runner import get_runner_cls
16+
from . import CommandContextError
17+
18+
19+
def add_parser_common(parser_adder, command):
20+
parser = parser_adder.add_parser(
21+
command.name,
22+
formatter_class=argparse.RawDescriptionHelpFormatter,
23+
description=command.description)
24+
25+
parser.add_argument('-d', '--build-dir',
26+
help='''Build directory to obtain runner information
27+
from; default is the current working directory.''')
28+
parser.add_argument('-c', '--cmake-cache', default=cmake.DEFAULT_CACHE,
29+
help='''Path to CMake cache file containing runner
30+
configuration (this is generated by the Zephyr
31+
build system when compiling binaries);
32+
default: {}.
33+
34+
If this is a relative path, it is assumed relative to
35+
the build directory. An absolute path can also be
36+
given instead.'''.format(cmake.DEFAULT_CACHE))
37+
parser.add_argument('-r', '--runner',
38+
help='''If given, overrides any cached {}
39+
runner.'''.format(command.name))
40+
parser.add_argument('--skip-rebuild', action='store_true',
41+
help='''If given, do not rebuild the application
42+
before running {} commands.'''.format(command.name))
43+
44+
return parser
45+
46+
47+
def desc_common(command_name):
48+
return dedent('''\
49+
Any options not recognized by this command are passed to the
50+
back-end {command} runner.
51+
52+
If you need to pass an option to a runner which has the
53+
same name as one recognized by this command, you can
54+
end argument parsing with a '--', like so:
55+
56+
west {command} --{command}-arg=value -- --runner-arg=value2
57+
'''.format(**{'command': command_name}))
58+
59+
60+
def do_run_common(command, args, runner_args, cached_runner_var):
61+
command_name = command.name
62+
build_dir = args.build_dir or getcwd()
63+
64+
if not args.skip_rebuild:
65+
try:
66+
cmake.run_build(build_dir)
67+
except CalledProcessError:
68+
if args.build_dir:
69+
log.die('cannot run {}, build in {} failed'.format(
70+
command_name, args.build_dir))
71+
else:
72+
log.die('cannot run {}; no --build-dir given and build in '
73+
'current directory {} failed'.format(command_name,
74+
build_dir))
75+
76+
# Temporary hack: we need to ensure we're running from the build
77+
# directory for now. Otherwise, the BuildConfiguration objects
78+
# that get created by the runners look for .config in the wrong
79+
# places.
80+
chdir(build_dir)
81+
82+
# TODO: build this by joining with build_dir once the above chdir
83+
# goes away.
84+
cache_file = args.cmake_cache
85+
cache = cmake.CMakeCache(cache_file)
86+
board = cache['CACHED_BOARD']
87+
available = cache.get_list('ZEPHYR_RUNNERS')
88+
if not available:
89+
log.wrn('No cached runners are available in', cache_file)
90+
runner = args.runner or cache.get(cached_runner_var)
91+
92+
if runner is None:
93+
raise CommandContextError(dedent("""
94+
No {} runner available for {}. Please either specify one
95+
manually, or check your board's documentation for
96+
alternative instructions.""".format(command_name, board)))
97+
98+
log.inf('Using runner:', runner)
99+
if runner not in available:
100+
log.wrn('Runner {} is not configured for use with {}, '
101+
'this may not work'.format(runner, board))
102+
runner_cls = get_runner_cls(runner)
103+
if command_name not in runner_cls.capabilities().commands:
104+
log.die('Runner {} does not support command {}'.format(
105+
runner, command_name))
106+
107+
cached_common_args = cache.get_list('ZEPHYR_RUNNER_ARGS_COMMON')
108+
cached_runner_args = cache.get_list(
109+
'ZEPHYR_RUNNER_ARGS_{}'.format(cmake.make_c_identifier(runner)))
110+
# Construct the final command line arguments, create a
111+
# runner-specific parser to handle them, and run the command.
112+
assert isinstance(runner_args, list), runner_args
113+
final_runner_args = (cached_common_args + cached_runner_args +
114+
runner_args + [command_name])
115+
116+
# Having the runners themselves be the place where their argument
117+
# parsing is handled is hackish; it's an artifact of the time
118+
# before the runner package was part of west.
119+
#
120+
# TODO: refactor runner argument parsing higher up into west.
121+
parser = argparse.ArgumentParser(prog=runner)
122+
runner_cls.add_parser(parser)
123+
parsed_args = parser.parse_args(args=final_runner_args)
124+
parsed_args.verbose = args.verbose
125+
runner = runner_cls.create_from_args(parsed_args)
126+
runner.run(command_name)

scripts/meta/west/main.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,12 @@
1414

1515
from . import log
1616
from .cmd import CommandContextError
17+
from .cmd.flash import Flash
18+
from .cmd.debug import Debug, DebugServer
1719
from .util import quote_sh_list
1820

1921

20-
COMMANDS = ()
22+
COMMANDS = (Flash(), Debug(), DebugServer())
2123
'''Supported top-level commands.'''
2224

2325

0 commit comments

Comments
 (0)