blob: b162fe52339281624c253c011517c5afc544ed21 [file] [log] [blame]
Marti Bolivarab822642019-01-23 08:31:06 -07001# 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
8import argparse
Marti Bolivarddce5832019-06-02 21:33:41 -06009import logging
Martí Bolívar3124c022020-08-24 15:21:15 -070010from os import close, getcwd, path, fspath
Martí Bolívara5268772020-03-17 16:26:51 -070011from pathlib import Path
Marti Bolivarab822642019-01-23 08:31:06 -070012from subprocess import CalledProcessError
Martí Bolívara5268772020-03-17 16:26:51 -070013import sys
Marti Bolivarf08935f2019-06-03 01:05:26 -060014import tempfile
Marti Bolivarab822642019-01-23 08:31:06 -070015import textwrap
Marti Bolivarf08935f2019-06-03 01:05:26 -060016import traceback
Marti Bolivarab822642019-01-23 08:31:06 -070017
Marti Bolivarab822642019-01-23 08:31:06 -070018from west import log
Carles Cufi98980c62019-06-05 16:04:29 +020019from build_helpers import find_build_dir, is_zephyr_build, \
20 FIND_BUILD_DIR_DESCRIPTION
Marti Bolivarf08935f2019-06-03 01:05:26 -060021from west.commands import CommandError
Carles Cufi41f1f642019-06-17 11:47:11 +020022from west.configuration import config
Martí Bolívareb95bed2020-02-10 06:40:29 -080023import yaml
Marti Bolivarab822642019-01-23 08:31:06 -070024
Martí Bolívarb3c64702021-02-23 13:40:13 -080025from zephyr_ext_common import ZEPHYR_SCRIPTS
26
27# Runners depend on edtlib. Make sure the copy in the tree is
28# available to them before trying to import any.
Jordan Yates8e4107f2022-04-30 21:13:52 +100029sys.path.insert(0, str(ZEPHYR_SCRIPTS / 'dts' / 'python-devicetree' / 'src'))
Martí Bolívarb3c64702021-02-23 13:40:13 -080030
Marti Bolivarc07267a2019-05-25 15:41:36 -060031from runners import get_runner_cls, ZephyrBinaryRunner, MissingProgram
Martí Bolívareb95bed2020-02-10 06:40:29 -080032from runners.core import RunnerConfig
33import zcmake
Marti Bolivarab822642019-01-23 08:31:06 -070034
35# Context-sensitive help indentation.
36# Don't change this, or output from argparse won't match up.
37INDENT = ' ' * 2
38
Marti Bolivarddce5832019-06-02 21:33:41 -060039if log.VERBOSE >= log.VERBOSE_NORMAL:
40 # Using level 1 allows sub-DEBUG levels of verbosity. The
41 # west.log module decides whether or not to actually print the
42 # message.
43 #
44 # https://docs.python.org/3.7/library/logging.html#logging-levels.
45 LOG_LEVEL = 1
46else:
47 LOG_LEVEL = logging.INFO
48
Marti Bolivarec8dbf32019-06-02 22:46:02 -060049def _banner(msg):
50 log.inf('-- ' + msg, colorize=True)
Marti Bolivarddce5832019-06-02 21:33:41 -060051
52class WestLogFormatter(logging.Formatter):
53
54 def __init__(self):
Marti Bolivarec8dbf32019-06-02 22:46:02 -060055 super().__init__(fmt='%(name)s: %(message)s')
Marti Bolivarddce5832019-06-02 21:33:41 -060056
57class WestLogHandler(logging.Handler):
58
59 def __init__(self, *args, **kwargs):
60 super().__init__(*args, **kwargs)
61 self.setFormatter(WestLogFormatter())
62 self.setLevel(LOG_LEVEL)
63
64 def emit(self, record):
65 fmt = self.format(record)
66 lvl = record.levelno
67 if lvl > logging.CRITICAL:
68 log.die(fmt)
69 elif lvl >= logging.ERROR:
70 log.err(fmt)
71 elif lvl >= logging.WARNING:
72 log.wrn(fmt)
73 elif lvl >= logging.INFO:
Marti Bolivarec8dbf32019-06-02 22:46:02 -060074 _banner(fmt)
Marti Bolivarddce5832019-06-02 21:33:41 -060075 elif lvl >= logging.DEBUG:
76 log.dbg(fmt)
77 else:
78 log.dbg(fmt, level=log.VERBOSE_EXTREME)
Marti Bolivarab822642019-01-23 08:31:06 -070079
Martí Bolívareb95bed2020-02-10 06:40:29 -080080def command_verb(command):
81 return "flash" if command.name == "flash" else "debug"
82
83def add_parser_common(command, parser_adder=None, parser=None):
84 if parser_adder is not None:
85 parser = parser_adder.add_parser(
86 command.name,
87 formatter_class=argparse.RawDescriptionHelpFormatter,
88 help=command.help,
Martí Bolívar6e4c2b92020-06-25 12:58:58 -070089 description=command.description)
Marti Bolivarab822642019-01-23 08:31:06 -070090
Martí Bolívar0d5e6c12020-12-09 15:53:00 -080091 # Remember to update west-completion.bash if you add or remove
Marti Bolivarab822642019-01-23 08:31:06 -070092 # flags
93
Martí Bolívar6e4c2b92020-06-25 12:58:58 -070094 group = parser.add_argument_group('general options',
95 FIND_BUILD_DIR_DESCRIPTION)
Marti Bolivarab822642019-01-23 08:31:06 -070096
Martí Bolívareb95bed2020-02-10 06:40:29 -080097 group.add_argument('-d', '--build-dir', metavar='DIR',
Martí Bolívar6e4c2b92020-06-25 12:58:58 -070098 help='application build directory')
99 # still supported for backwards compatibility, but questionably
100 # useful now that we do everything with runners.yaml
Martí Bolívareb95bed2020-02-10 06:40:29 -0800101 group.add_argument('-c', '--cmake-cache', metavar='FILE',
Martí Bolívar6e4c2b92020-06-25 12:58:58 -0700102 help=argparse.SUPPRESS)
Marti Bolivarab822642019-01-23 08:31:06 -0700103 group.add_argument('-r', '--runner',
Martí Bolívar6e4c2b92020-06-25 12:58:58 -0700104 help='override default runner from --build-dir')
Marti Bolivarab822642019-01-23 08:31:06 -0700105 group.add_argument('--skip-rebuild', action='store_true',
Martí Bolívar6e4c2b92020-06-25 12:58:58 -0700106 help='do not refresh cmake dependencies first')
Marti Bolivarab822642019-01-23 08:31:06 -0700107
Martí Bolívar6e4c2b92020-06-25 12:58:58 -0700108 group = parser.add_argument_group(
Martí Bolívar4ac96072020-11-02 08:30:14 -0800109 'runner configuration',
Martí Bolívar6e4c2b92020-06-25 12:58:58 -0700110 textwrap.dedent(f'''\
Martí Bolívar4ac96072020-11-02 08:30:14 -0800111 ===================================================================
112 IMPORTANT:
113 Individual runners support additional options not printed here.
114 ===================================================================
Marti Bolivarab822642019-01-23 08:31:06 -0700115
Martí Bolívar4ac96072020-11-02 08:30:14 -0800116 Run "west {command.name} --context" for runner-specific options.
117
118 If a build directory is found, --context also prints per-runner
119 settings found in that build directory's runners.yaml file.
120
121 Use "west {command.name} --context -r RUNNER" to limit output to a
122 specific RUNNER.
123
124 Some runner settings also can be overridden with options like
125 --hex-file. However, this depends on the runner: not all runners
126 respect --elf-file / --hex-file / --bin-file, nor use gdb or openocd,
127 etc.'''))
128 group.add_argument('-H', '--context', action='store_true',
129 help='print runner- and build-specific help')
Martí Bolívar3124c022020-08-24 15:21:15 -0700130 # Options used to override RunnerConfig values in runners.yaml.
Martí Bolívar6e4c2b92020-06-25 12:58:58 -0700131 # TODO: is this actually useful?
132 group.add_argument('--board-dir', metavar='DIR', help='board directory')
133 # FIXME: we should just have a single --file argument. The variation
134 # between runners is confusing people.
Martí Bolívareb95bed2020-02-10 06:40:29 -0800135 group.add_argument('--elf-file', metavar='FILE', help='path to zephyr.elf')
136 group.add_argument('--hex-file', metavar='FILE', help='path to zephyr.hex')
137 group.add_argument('--bin-file', metavar='FILE', help='path to zephyr.bin')
Martí Bolívar6e4c2b92020-06-25 12:58:58 -0700138 # FIXME: these are runner-specific and should be moved to where --context
139 # can find them instead.
140 group.add_argument('--gdb', help='path to GDB')
141 group.add_argument('--openocd', help='path to openocd')
Marti Bolivarab822642019-01-23 08:31:06 -0700142 group.add_argument(
Martí Bolívareb95bed2020-02-10 06:40:29 -0800143 '--openocd-search', metavar='DIR',
Martí Bolívar6e4c2b92020-06-25 12:58:58 -0700144 help='path to add to openocd search path, if applicable')
Marti Bolivarab822642019-01-23 08:31:06 -0700145
146 return parser
147
Martí Bolívarf8cb3d42020-02-28 11:21:59 -0800148def do_run_common(command, user_args, user_runner_args):
Martí Bolívareb95bed2020-02-10 06:40:29 -0800149 # This is the main routine for all the "west flash", "west debug",
150 # etc. commands.
Marti Bolivarab822642019-01-23 08:31:06 -0700151
Martí Bolívarf8cb3d42020-02-28 11:21:59 -0800152 if user_args.context:
153 dump_context(command, user_args, user_runner_args)
Martí Bolívareb95bed2020-02-10 06:40:29 -0800154 return
Marti Bolivarab822642019-01-23 08:31:06 -0700155
Martí Bolívareb95bed2020-02-10 06:40:29 -0800156 command_name = command.name
Martí Bolívarf8cb3d42020-02-28 11:21:59 -0800157 build_dir = get_build_dir(user_args)
158 cache = load_cmake_cache(build_dir, user_args)
Martí Bolívareb95bed2020-02-10 06:40:29 -0800159 board = cache['CACHED_BOARD']
Martí Bolívarf8cb3d42020-02-28 11:21:59 -0800160 if not user_args.skip_rebuild:
161 rebuild(command, build_dir, user_args)
Marti Bolivarab822642019-01-23 08:31:06 -0700162
Martí Bolívareb95bed2020-02-10 06:40:29 -0800163 # Load runners.yaml.
Martí Bolívar3124c022020-08-24 15:21:15 -0700164 yaml_path = runners_yaml_path(build_dir, board)
165 runners_yaml = load_runners_yaml(yaml_path)
Marti Bolivarab822642019-01-23 08:31:06 -0700166
Martí Bolívareb95bed2020-02-10 06:40:29 -0800167 # Get a concrete ZephyrBinaryRunner subclass to use based on
168 # runners.yaml and command line arguments.
Martí Bolívare63d2992020-08-26 11:10:27 -0700169 runner_cls = use_runner_cls(command, board, user_args, runners_yaml,
Martí Bolívara5268772020-03-17 16:26:51 -0700170 cache)
Martí Bolívareb95bed2020-02-10 06:40:29 -0800171 runner_name = runner_cls.name()
172
173 # Set up runner logging to delegate to west.log commands.
174 logger = logging.getLogger('runners')
175 logger.setLevel(LOG_LEVEL)
176 logger.addHandler(WestLogHandler())
177
178 # If the user passed -- to force the parent argument parser to stop
179 # parsing, it will show up here, and needs to be filtered out.
Martí Bolívar97dbda22020-02-28 11:13:37 -0800180 runner_args = [arg for arg in user_runner_args if arg != '--']
Martí Bolívareb95bed2020-02-10 06:40:29 -0800181
Martí Bolívar3124c022020-08-24 15:21:15 -0700182 # Arguments in this order to allow specific to override general:
Martí Bolívareb95bed2020-02-10 06:40:29 -0800183 #
Martí Bolívareb95bed2020-02-10 06:40:29 -0800184 # - runner-specific runners.yaml arguments
Martí Bolívar3124c022020-08-24 15:21:15 -0700185 # - user-provided command line arguments
186 final_argv = runners_yaml['args'][runner_name] + runner_args
Martí Bolívareb95bed2020-02-10 06:40:29 -0800187
Martí Bolívarf8cb3d42020-02-28 11:21:59 -0800188 # 'user_args' contains parsed arguments which are:
Martí Bolívareb95bed2020-02-10 06:40:29 -0800189 #
Martí Bolívarf8cb3d42020-02-28 11:21:59 -0800190 # 1. provided on the command line, and
191 # 2. handled by add_parser_common(), and
192 # 3. *not* runner-specific
Martí Bolívareb95bed2020-02-10 06:40:29 -0800193 #
Martí Bolívarf8cb3d42020-02-28 11:21:59 -0800194 # 'final_argv' contains unparsed arguments from either:
Martí Bolívareb95bed2020-02-10 06:40:29 -0800195 #
Martí Bolívarf8cb3d42020-02-28 11:21:59 -0800196 # 1. runners.yaml, or
197 # 2. the command line
198 #
199 # We next have to:
200 #
201 # - parse 'final_argv' now that we have all the command line
202 # arguments
203 # - create a RunnerConfig using 'user_args' and the result
204 # of parsing 'final_argv'
Martí Bolívareb95bed2020-02-10 06:40:29 -0800205 parser = argparse.ArgumentParser(prog=runner_name)
206 add_parser_common(command, parser=parser)
207 runner_cls.add_parser(parser)
Martí Bolívardc094842020-02-28 11:31:28 -0800208 args, unknown = parser.parse_known_args(args=final_argv)
Martí Bolívareb95bed2020-02-10 06:40:29 -0800209 if unknown:
210 log.die(f'runner {runner_name} received unknown arguments: {unknown}')
211
Martí Bolívardc094842020-02-28 11:31:28 -0800212 # Override args with any user_args. The latter must take
213 # precedence, or e.g. --hex-file on the command line would be
214 # ignored in favor of a board.cmake setting.
215 for a, v in vars(user_args).items():
216 if v is not None:
217 setattr(args, a, v)
218
Martí Bolívar3124c022020-08-24 15:21:15 -0700219 # Create the RunnerConfig from runners.yaml and any command line
220 # overrides.
221 runner_config = get_runner_config(build_dir, yaml_path, runners_yaml, args)
222 log.dbg(f'runner_config: {runner_config}', level=log.VERBOSE_VERY)
223
Martí Bolívareb95bed2020-02-10 06:40:29 -0800224 # Use that RunnerConfig to create the ZephyrBinaryRunner instance
225 # and call its run().
Martí Bolívareb95bed2020-02-10 06:40:29 -0800226 try:
Martí Bolívar3124c022020-08-24 15:21:15 -0700227 runner = runner_cls.create(runner_config, args)
Martí Bolívareb95bed2020-02-10 06:40:29 -0800228 runner.run(command_name)
229 except ValueError as ve:
230 log.err(str(ve), fatal=True)
231 dump_traceback()
232 raise CommandError(1)
233 except MissingProgram as e:
234 log.die('required program', e.filename,
235 'not found; install it or add its location to PATH')
236 except RuntimeError as re:
Martí Bolívarf8cb3d42020-02-28 11:21:59 -0800237 if not user_args.verbose:
Martí Bolívareb95bed2020-02-10 06:40:29 -0800238 log.die(re)
239 else:
240 log.err('verbose mode enabled, dumping stack:', fatal=True)
241 raise
242
243def get_build_dir(args, die_if_none=True):
Marti Bolivarab822642019-01-23 08:31:06 -0700244 # Get the build directory for the given argument list and environment.
245 if args.build_dir:
246 return args.build_dir
247
Carles Cufi41f1f642019-06-17 11:47:11 +0200248 guess = config.get('build', 'guess-dir', fallback='never')
Ulf Magnusson46993752019-09-05 20:10:53 +0200249 guess = guess == 'runners'
Carles Cufi41f1f642019-06-17 11:47:11 +0200250 dir = find_build_dir(None, guess)
Carles Cufi49c4b1c2019-06-03 12:43:38 +0200251
Carles Cufi98980c62019-06-05 16:04:29 +0200252 if dir and is_zephyr_build(dir):
Carles Cufi49c4b1c2019-06-03 12:43:38 +0200253 return dir
Marti Bolivarab822642019-01-23 08:31:06 -0700254 elif die_if_none:
Carles Cufi98980c62019-06-05 16:04:29 +0200255 msg = '--build-dir was not given, '
256 if dir:
257 msg = msg + 'and neither {} nor {} are zephyr build directories.'
258 else:
259 msg = msg + ('{} is not a build directory and the default build '
260 'directory cannot be determined. Check your '
261 'build.dir-fmt configuration option')
262 log.die(msg.format(getcwd(), dir))
Marti Bolivarab822642019-01-23 08:31:06 -0700263 else:
264 return None
265
Martí Bolívareb95bed2020-02-10 06:40:29 -0800266def load_cmake_cache(build_dir, args):
267 cache_file = path.join(build_dir, args.cmake_cache or zcmake.DEFAULT_CACHE)
268 try:
269 return zcmake.CMakeCache(cache_file)
270 except FileNotFoundError:
271 log.die(f'no CMake cache found (expected one at {cache_file})')
272
273def rebuild(command, build_dir, args):
274 _banner(f'west {command.name}: rebuilding')
275 try:
Torsten Rasmussen3dd65a72021-03-17 14:09:37 +0100276 zcmake.run_build(build_dir)
Martí Bolívareb95bed2020-02-10 06:40:29 -0800277 except CalledProcessError:
278 if args.build_dir:
279 log.die(f're-build in {args.build_dir} failed')
280 else:
281 log.die(f're-build in {build_dir} failed (no --build-dir given)')
282
Martí Bolívar3124c022020-08-24 15:21:15 -0700283def runners_yaml_path(build_dir, board):
284 ret = Path(build_dir) / 'zephyr' / 'runners.yaml'
285 if not ret.is_file():
Martí Bolívareb95bed2020-02-10 06:40:29 -0800286 log.die(f'either a pristine build is needed, or board {board} '
287 "doesn't support west flash/debug "
288 '(no ZEPHYR_RUNNERS_YAML in CMake cache)')
Martí Bolívar3124c022020-08-24 15:21:15 -0700289 return ret
Martí Bolívareb95bed2020-02-10 06:40:29 -0800290
Martí Bolívar3124c022020-08-24 15:21:15 -0700291def load_runners_yaml(path):
292 # Load runners.yaml and convert to Python object.
Martí Bolívareb95bed2020-02-10 06:40:29 -0800293
294 try:
295 with open(path, 'r') as f:
Martí Bolívare63d2992020-08-26 11:10:27 -0700296 content = yaml.safe_load(f.read())
Martí Bolívareb95bed2020-02-10 06:40:29 -0800297 except FileNotFoundError:
298 log.die(f'runners.yaml file not found: {path}')
299
Martí Bolívare63d2992020-08-26 11:10:27 -0700300 if not content.get('runners'):
Martí Bolívareb95bed2020-02-10 06:40:29 -0800301 log.wrn(f'no pre-configured runners in {path}; '
302 "this probably won't work")
303
Martí Bolívare63d2992020-08-26 11:10:27 -0700304 return content
Martí Bolívareb95bed2020-02-10 06:40:29 -0800305
Martí Bolívare63d2992020-08-26 11:10:27 -0700306def use_runner_cls(command, board, args, runners_yaml, cache):
Martí Bolívareb95bed2020-02-10 06:40:29 -0800307 # Get the ZephyrBinaryRunner class from its name, and make sure it
308 # supports the command. Print a message about the choice, and
309 # return the class.
310
Martí Bolívare63d2992020-08-26 11:10:27 -0700311 runner = args.runner or runners_yaml.get(command.runner_key)
Martí Bolívareb95bed2020-02-10 06:40:29 -0800312 if runner is None:
313 log.die(f'no {command.name} runner available for board {board}. '
314 "Check the board's documentation for instructions.")
315
316 _banner(f'west {command.name}: using runner {runner}')
317
Martí Bolívare63d2992020-08-26 11:10:27 -0700318 available = runners_yaml.get('runners', [])
Martí Bolívareb95bed2020-02-10 06:40:29 -0800319 if runner not in available:
Martí Bolívara5268772020-03-17 16:26:51 -0700320 if 'BOARD_DIR' in cache:
321 board_cmake = Path(cache['BOARD_DIR']) / 'board.cmake'
322 else:
323 board_cmake = 'board.cmake'
324 log.err(f'board {board} does not support runner {runner}',
325 fatal=True)
326 log.inf(f'To fix, configure this runner in {board_cmake} and rebuild.')
327 sys.exit(1)
Martí Bolívar847ba432021-02-23 14:00:18 -0800328 try:
329 runner_cls = get_runner_cls(runner)
330 except ValueError as e:
331 log.die(e)
Martí Bolívareb95bed2020-02-10 06:40:29 -0800332 if command.name not in runner_cls.capabilities().commands:
333 log.die(f'runner {runner} does not support command {command.name}')
334
335 return runner_cls
336
Martí Bolívar3124c022020-08-24 15:21:15 -0700337def get_runner_config(build_dir, yaml_path, runners_yaml, args=None):
338 # Get a RunnerConfig object for the current run. yaml_config is
339 # runners.yaml's config: map, and args are the command line arguments.
340 yaml_config = runners_yaml['config']
341 yaml_dir = yaml_path.parent
342 if args is None:
343 args = argparse.Namespace()
344
345 def output_file(filetype):
346
347 from_args = getattr(args, f'{filetype}_file', None)
348 if from_args is not None:
349 return from_args
350
351 from_yaml = yaml_config.get(f'{filetype}_file')
352 if from_yaml is not None:
353 # Output paths in runners.yaml are relative to the
354 # directory containing the runners.yaml file.
355 return fspath(yaml_dir / from_yaml)
356
Martí Bolívar32045542021-02-02 08:55:16 -0800357 return None
Martí Bolívar3124c022020-08-24 15:21:15 -0700358
Gerson Fernando Budkec3db83b2021-08-25 22:07:54 -0300359 def config(attr, default=None):
360 return getattr(args, attr, None) or yaml_config.get(attr, default)
Martí Bolívar3124c022020-08-24 15:21:15 -0700361
362 return RunnerConfig(build_dir,
363 yaml_config['board_dir'],
364 output_file('elf'),
365 output_file('hex'),
366 output_file('bin'),
367 config('gdb'),
368 config('openocd'),
Gerson Fernando Budkec3db83b2021-08-25 22:07:54 -0300369 config('openocd_search', []))
Martí Bolívareb95bed2020-02-10 06:40:29 -0800370
Marti Bolivarf08935f2019-06-03 01:05:26 -0600371def dump_traceback():
372 # Save the current exception to a file and return its path.
373 fd, name = tempfile.mkstemp(prefix='west-exc-', suffix='.txt')
374 close(fd) # traceback has no use for the fd
375 with open(name, 'w') as f:
376 traceback.print_exc(file=f)
377 log.inf("An exception trace has been saved in", name)
Marti Bolivarab822642019-01-23 08:31:06 -0700378
Marti Bolivarab822642019-01-23 08:31:06 -0700379#
Martí Bolívareb95bed2020-02-10 06:40:29 -0800380# west {command} --context
Marti Bolivarab822642019-01-23 08:31:06 -0700381#
382
Martí Bolívareb95bed2020-02-10 06:40:29 -0800383def dump_context(command, args, unknown_args):
384 build_dir = get_build_dir(args, die_if_none=False)
385 if build_dir is None:
386 log.wrn('no --build-dir given or found; output will be limited')
Martí Bolívare63d2992020-08-26 11:10:27 -0700387 runners_yaml = None
Marti Bolivarab822642019-01-23 08:31:06 -0700388 else:
Martí Bolívareb95bed2020-02-10 06:40:29 -0800389 cache = load_cmake_cache(build_dir, args)
390 board = cache['CACHED_BOARD']
Martí Bolívar3124c022020-08-24 15:21:15 -0700391 yaml_path = runners_yaml_path(build_dir, board)
392 runners_yaml = load_runners_yaml(yaml_path)
Marti Bolivarab822642019-01-23 08:31:06 -0700393
Martí Bolívareb95bed2020-02-10 06:40:29 -0800394 # Re-build unless asked not to, to make sure the output is up to date.
Marti Bolivarab822642019-01-23 08:31:06 -0700395 if build_dir and not args.skip_rebuild:
Martí Bolívareb95bed2020-02-10 06:40:29 -0800396 rebuild(command, build_dir, args)
Marti Bolivarab822642019-01-23 08:31:06 -0700397
398 if args.runner:
Martí Bolívareb95bed2020-02-10 06:40:29 -0800399 try:
400 cls = get_runner_cls(args.runner)
401 except ValueError:
402 log.die(f'invalid runner name {args.runner}; choices: ' +
403 ', '.join(cls.name() for cls in
404 ZephyrBinaryRunner.get_runners()))
405 else:
406 cls = None
Marti Bolivarab822642019-01-23 08:31:06 -0700407
Martí Bolívare63d2992020-08-26 11:10:27 -0700408 if runners_yaml is None:
Martí Bolívareb95bed2020-02-10 06:40:29 -0800409 dump_context_no_config(command, cls)
410 else:
411 log.inf(f'build configuration:', colorize=True)
412 log.inf(f'{INDENT}build directory: {build_dir}')
413 log.inf(f'{INDENT}board: {board}')
Martí Bolívare63d2992020-08-26 11:10:27 -0700414 log.inf(f'{INDENT}runners.yaml: {yaml_path}')
Martí Bolívareb95bed2020-02-10 06:40:29 -0800415 if cls:
Martí Bolívare63d2992020-08-26 11:10:27 -0700416 dump_runner_context(command, cls, runners_yaml)
Martí Bolívareb95bed2020-02-10 06:40:29 -0800417 else:
Martí Bolívare63d2992020-08-26 11:10:27 -0700418 dump_all_runner_context(command, runners_yaml, board, build_dir)
Marti Bolivarab822642019-01-23 08:31:06 -0700419
Martí Bolívareb95bed2020-02-10 06:40:29 -0800420def dump_context_no_config(command, cls):
421 if not cls:
422 all_cls = {cls.name(): cls for cls in ZephyrBinaryRunner.get_runners()
423 if command.name in cls.capabilities().commands}
424 log.inf('all Zephyr runners which support {}:'.format(command.name),
Marti Bolivarab822642019-01-23 08:31:06 -0700425 colorize=True)
Martí Bolívareb95bed2020-02-10 06:40:29 -0800426 dump_wrapped_lines(', '.join(all_cls.keys()), INDENT)
427 log.inf()
428 log.inf('Note: use -r RUNNER to limit information to one runner.')
429 else:
Martí Bolívare63d2992020-08-26 11:10:27 -0700430 # This does the right thing with a None argument.
Martí Bolívareb95bed2020-02-10 06:40:29 -0800431 dump_runner_context(command, cls, None)
Marti Bolivarab822642019-01-23 08:31:06 -0700432
Martí Bolívare63d2992020-08-26 11:10:27 -0700433def dump_runner_context(command, cls, runners_yaml, indent=''):
Martí Bolívareb95bed2020-02-10 06:40:29 -0800434 dump_runner_caps(cls, indent)
435 dump_runner_option_help(cls, indent)
Marti Bolivarab822642019-01-23 08:31:06 -0700436
Martí Bolívare63d2992020-08-26 11:10:27 -0700437 if runners_yaml is None:
Marti Bolivarab822642019-01-23 08:31:06 -0700438 return
439
Martí Bolívare63d2992020-08-26 11:10:27 -0700440 if cls.name() in runners_yaml['runners']:
441 dump_runner_args(cls.name(), runners_yaml, indent)
Martí Bolívareb95bed2020-02-10 06:40:29 -0800442 else:
443 log.wrn(f'support for runner {cls.name()} is not configured '
444 f'in this build directory')
Marti Bolivarab822642019-01-23 08:31:06 -0700445
Martí Bolívareb95bed2020-02-10 06:40:29 -0800446def dump_runner_caps(cls, indent=''):
447 # Print RunnerCaps for the given runner class.
Marti Bolivarab822642019-01-23 08:31:06 -0700448
Martí Bolívareb95bed2020-02-10 06:40:29 -0800449 log.inf(f'{indent}{cls.name()} capabilities:', colorize=True)
450 log.inf(f'{indent}{INDENT}{cls.capabilities()}')
Marti Bolivarab822642019-01-23 08:31:06 -0700451
Martí Bolívareb95bed2020-02-10 06:40:29 -0800452def dump_runner_option_help(cls, indent=''):
453 # Print help text for class-specific command line options for the
454 # given runner class.
Marti Bolivarab822642019-01-23 08:31:06 -0700455
Marti Bolivarab822642019-01-23 08:31:06 -0700456 dummy_parser = argparse.ArgumentParser(prog='', add_help=False)
457 cls.add_parser(dummy_parser)
458 formatter = dummy_parser._get_formatter()
459 for group in dummy_parser._action_groups:
460 # Break the abstraction to filter out the 'flash', 'debug', etc.
461 # TODO: come up with something cleaner (may require changes
462 # in the runner core).
463 actions = group._group_actions
464 if len(actions) == 1 and actions[0].dest == 'command':
465 # This is the lone positional argument. Skip it.
466 continue
467 formatter.start_section('REMOVE ME')
468 formatter.add_text(group.description)
469 formatter.add_arguments(actions)
470 formatter.end_section()
471 # Get the runner help, with the "REMOVE ME" string gone
Martí Bolívareb95bed2020-02-10 06:40:29 -0800472 runner_help = f'\n{indent}'.join(formatter.format_help().splitlines()[1:])
Marti Bolivarab822642019-01-23 08:31:06 -0700473
Martí Bolívareb95bed2020-02-10 06:40:29 -0800474 log.inf(f'{indent}{cls.name()} options:', colorize=True)
475 log.inf(indent + runner_help)
Marti Bolivarab822642019-01-23 08:31:06 -0700476
Martí Bolívare63d2992020-08-26 11:10:27 -0700477def dump_runner_args(group, runners_yaml, indent=''):
Martí Bolívareb95bed2020-02-10 06:40:29 -0800478 msg = f'{indent}{group} arguments from runners.yaml:'
Martí Bolívare63d2992020-08-26 11:10:27 -0700479 args = runners_yaml['args'][group]
Martí Bolívareb95bed2020-02-10 06:40:29 -0800480 if args:
481 log.inf(msg, colorize=True)
482 for arg in args:
483 log.inf(f'{indent}{INDENT}{arg}')
484 else:
485 log.inf(f'{msg} (none)', colorize=True)
Marti Bolivarab822642019-01-23 08:31:06 -0700486
Martí Bolívare63d2992020-08-26 11:10:27 -0700487def dump_all_runner_context(command, runners_yaml, board, build_dir):
Martí Bolívareb95bed2020-02-10 06:40:29 -0800488 all_cls = {cls.name(): cls for cls in ZephyrBinaryRunner.get_runners() if
489 command.name in cls.capabilities().commands}
Martí Bolívare63d2992020-08-26 11:10:27 -0700490 available = runners_yaml['runners']
Martí Bolívareb95bed2020-02-10 06:40:29 -0800491 available_cls = {r: all_cls[r] for r in available if r in all_cls}
Martí Bolívare63d2992020-08-26 11:10:27 -0700492 default_runner = runners_yaml[command.runner_key]
Martí Bolívar3124c022020-08-24 15:21:15 -0700493 yaml_path = runners_yaml_path(build_dir, board)
494 runners_yaml = load_runners_yaml(yaml_path)
Martí Bolívareb95bed2020-02-10 06:40:29 -0800495
496 log.inf(f'zephyr runners which support "west {command.name}":',
Marti Bolivarab822642019-01-23 08:31:06 -0700497 colorize=True)
Martí Bolívareb95bed2020-02-10 06:40:29 -0800498 dump_wrapped_lines(', '.join(all_cls.keys()), INDENT)
499 log.inf()
500 dump_wrapped_lines('Note: not all may work with this board and build '
501 'directory. Available runners are listed below.',
502 INDENT)
Marti Bolivarab822642019-01-23 08:31:06 -0700503
Martí Bolívareb95bed2020-02-10 06:40:29 -0800504 log.inf(f'available runners in runners.yaml:',
Marti Bolivarab822642019-01-23 08:31:06 -0700505 colorize=True)
Martí Bolívareb95bed2020-02-10 06:40:29 -0800506 dump_wrapped_lines(', '.join(available), INDENT)
507 log.inf(f'default runner in runners.yaml:', colorize=True)
508 log.inf(INDENT + default_runner)
Martí Bolívar3124c022020-08-24 15:21:15 -0700509 log.inf('common runner configuration:', colorize=True)
510 runner_config = get_runner_config(build_dir, yaml_path, runners_yaml)
511 for field, value in zip(runner_config._fields, runner_config):
512 log.inf(f'{INDENT}- {field}: {value}')
Martí Bolívareb95bed2020-02-10 06:40:29 -0800513 log.inf('runner-specific context:', colorize=True)
514 for cls in available_cls.values():
Martí Bolívare63d2992020-08-26 11:10:27 -0700515 dump_runner_context(command, cls, runners_yaml, INDENT)
Marti Bolivarab822642019-01-23 08:31:06 -0700516
Martí Bolívareb95bed2020-02-10 06:40:29 -0800517 if len(available) > 1:
518 log.inf()
519 log.inf('Note: use -r RUNNER to limit information to one runner.')
Marti Bolivarab822642019-01-23 08:31:06 -0700520
Martí Bolívareb95bed2020-02-10 06:40:29 -0800521def dump_wrapped_lines(text, indent):
522 for line in textwrap.wrap(text, initial_indent=indent,
523 subsequent_indent=indent,
524 break_on_hyphens=False,
525 break_long_words=False):
526 log.inf(line)