Skip to content

Commit ddce583

Browse files
mbolivar-nordiccarlescufi
authored andcommitted
scripts: west_commands: decouple runners pkg from west
I've had some requests to be able to use code in the runners package without having west installed. It turns out to be pretty easy to make this happen, as west is currently only used for west.log and some trivial helper methods: - To replace west log, use the standard logging module - Add an appropriate handler for each runner's logger in run_common.py which delegates to west.log, to keep output working as expected. Signed-off-by: Marti Bolivar <[email protected]>
1 parent d176cc3 commit ddce583

File tree

10 files changed

+148
-97
lines changed

10 files changed

+148
-97
lines changed

scripts/west_commands/run_common.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
'''
77

88
import argparse
9+
import logging
910
from os import getcwd, path
1011
from subprocess import CalledProcessError
1112
import textwrap
@@ -25,6 +26,44 @@
2526
# Don't change this, or output from argparse won't match up.
2627
INDENT = ' ' * 2
2728

29+
if log.VERBOSE >= log.VERBOSE_NORMAL:
30+
# Using level 1 allows sub-DEBUG levels of verbosity. The
31+
# west.log module decides whether or not to actually print the
32+
# message.
33+
#
34+
# https://docs.python.org/3.7/library/logging.html#logging-levels.
35+
LOG_LEVEL = 1
36+
else:
37+
LOG_LEVEL = logging.INFO
38+
39+
40+
class WestLogFormatter(logging.Formatter):
41+
42+
def __init__(self):
43+
super().__init__(fmt='%(message)s')
44+
45+
class WestLogHandler(logging.Handler):
46+
47+
def __init__(self, *args, **kwargs):
48+
super().__init__(*args, **kwargs)
49+
self.setFormatter(WestLogFormatter())
50+
self.setLevel(LOG_LEVEL)
51+
52+
def emit(self, record):
53+
fmt = self.format(record)
54+
lvl = record.levelno
55+
if lvl > logging.CRITICAL:
56+
log.die(fmt)
57+
elif lvl >= logging.ERROR:
58+
log.err(fmt)
59+
elif lvl >= logging.WARNING:
60+
log.wrn(fmt)
61+
elif lvl >= logging.INFO:
62+
log.inf(fmt)
63+
elif lvl >= logging.DEBUG:
64+
log.dbg(fmt)
65+
else:
66+
log.dbg(fmt, level=log.VERBOSE_EXTREME)
2867

2968
def add_parser_common(parser_adder, command):
3069
parser = parser_adder.add_parser(
@@ -201,9 +240,13 @@ def do_run_common(command, args, runner_args, cached_runner_var):
201240
# At this point, the common options above are already parsed in
202241
# 'args', and unrecognized arguments are in 'runner_args'.
203242
#
243+
# - Set up runner logging to delegate to west.
204244
# - Pull the RunnerConfig out of the cache
205245
# - Override cached values with applicable command-line options
206246

247+
logger = logging.getLogger('runners')
248+
logger.setLevel(LOG_LEVEL)
249+
logger.addHandler(WestLogHandler())
207250
cfg = cached_runner_config(build_dir, cache)
208251
_override_config_from_namespace(cfg, args)
209252

scripts/west_commands/runners/core.py

Lines changed: 38 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,21 @@
1414
import abc
1515
import argparse
1616
import errno
17+
import logging
1718
import os
1819
import platform
20+
import shlex
1921
import shutil
2022
import signal
2123
import subprocess
2224

23-
from west import log
24-
from west.util import quote_sh_list
25+
# Turn on to enable just logging the commands that would be run (at
26+
# info rather than debug level), without actually running them. This
27+
# can break runners that are expecting output or if one command
28+
# depends on another, so it's just for debugging.
29+
_DRY_RUN = False
2530

26-
# Turn on to enable just printing the commands that would be run,
27-
# without actually running them. This can break runners that are expecting
28-
# output or if one command depends on another, so it's just for debugging.
29-
JUST_PRINT = False
31+
_logger = logging.getLogger('runners')
3032

3133

3234
class _DebugDummyPopen:
@@ -313,6 +315,17 @@ class ZephyrBinaryRunner(abc.ABC):
313315
3. Give your runner's name to the Zephyr build system in your
314316
board's board.cmake.
315317
318+
Some advice on input and output:
319+
320+
- If you need to ask the user something (e.g. using input()), do it
321+
in your create() classmethod, not do_run(). That ensures your
322+
__init__() really has everything it needs to call do_run(), and also
323+
avoids calling input() when not instantiating within a command line
324+
application.
325+
326+
- Use self.logger to log messages using the standard library's
327+
logging API; your logger is named "runner.<your-runner-name()>"
328+
316329
For command-line invocation from the Zephyr build system, runners
317330
define their own argparse-based interface through the common
318331
add_parser() (and runner-specific do_add_parser() it delegates
@@ -330,6 +343,10 @@ def __init__(self, cfg):
330343
331344
``cfg`` is a RunnerConfig instance.'''
332345
self.cfg = cfg
346+
'''RunnerConfig for this instance.'''
347+
348+
self.logger = logging.getLogger('runners.{}'.format(self.name()))
349+
'''logging.Logger for this instance.'''
333350

334351
@staticmethod
335352
def get_runners():
@@ -465,20 +482,23 @@ def run_server_and_client(self, server, client):
465482
server_proc.terminate()
466483
server_proc.wait()
467484

485+
def _log_cmd(self, cmd):
486+
escaped = ' '.join(shlex.quote(s) for s in cmd)
487+
if not _DRY_RUN:
488+
self.logger.debug(escaped)
489+
else:
490+
self.logger.info(escaped)
491+
468492
def call(self, cmd):
469493
'''Subclass subprocess.call() wrapper.
470494
471495
Subclasses should use this method to run command in a
472496
subprocess and get its return code, rather than
473497
using subprocess directly, to keep accurate debug logs.
474498
'''
475-
quoted = quote_sh_list(cmd)
476-
477-
if JUST_PRINT:
478-
log.inf(quoted)
499+
self._log_cmd(cmd)
500+
if _DRY_RUN:
479501
return 0
480-
481-
log.dbg(quoted)
482502
return subprocess.call(cmd)
483503

484504
def check_call(self, cmd):
@@ -488,13 +508,9 @@ def check_call(self, cmd):
488508
subprocess and check that it executed correctly, rather than
489509
using subprocess directly, to keep accurate debug logs.
490510
'''
491-
quoted = quote_sh_list(cmd)
492-
493-
if JUST_PRINT:
494-
log.inf(quoted)
511+
self._log_cmd(cmd)
512+
if _DRY_RUN:
495513
return
496-
497-
log.dbg(quoted)
498514
try:
499515
subprocess.check_call(cmd)
500516
except subprocess.CalledProcessError:
@@ -507,13 +523,9 @@ def check_output(self, cmd):
507523
subprocess and check that it executed correctly, rather than
508524
using subprocess directly, to keep accurate debug logs.
509525
'''
510-
quoted = quote_sh_list(cmd)
511-
512-
if JUST_PRINT:
513-
log.inf(quoted)
526+
self._log_cmd(cmd)
527+
if _DRY_RUN:
514528
return b''
515-
516-
log.dbg(quoted)
517529
try:
518530
return subprocess.check_output(cmd)
519531
except subprocess.CalledProcessError:
@@ -526,16 +538,14 @@ def popen_ignore_int(self, cmd):
526538
cflags = 0
527539
preexec = None
528540
system = platform.system()
529-
quoted = quote_sh_list(cmd)
530541

531542
if system == 'Windows':
532543
cflags |= subprocess.CREATE_NEW_PROCESS_GROUP
533544
elif system in {'Linux', 'Darwin'}:
534545
preexec = os.setsid
535546

536-
if JUST_PRINT:
537-
log.inf(quoted)
547+
self._log_cmd(cmd)
548+
if _DRY_RUN:
538549
return _DebugDummyPopen()
539550

540-
log.dbg(quoted)
541551
return subprocess.Popen(cmd, creationflags=cflags, preexec_fn=preexec)

scripts/west_commands/runners/dfu.py

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@
88
import sys
99
import time
1010

11-
from west import log
12-
1311
from runners.core import ZephyrBinaryRunner, RunnerCaps, \
1412
BuildConfiguration
1513

@@ -36,6 +34,7 @@ def __init__(self, cfg, pid, alt, img, exe='dfu-util',
3634
else:
3735
self.dfuse = True
3836
self.dfuse_config = dfuse_config
37+
self.reset = False
3938

4039
@classmethod
4140
def name(cls):
@@ -80,8 +79,17 @@ def create(cls, cfg, args):
8079
else:
8180
dcfg = None
8281

83-
return DfuUtilBinaryRunner(cfg, args.pid, args.alt, args.img,
84-
exe=args.dfu_util, dfuse_config=dcfg)
82+
ret = DfuUtilBinaryRunner(cfg, args.pid, args.alt, args.img,
83+
exe=args.dfu_util, dfuse_config=dcfg)
84+
ret.ensure_device()
85+
return ret
86+
87+
def ensure_device(self):
88+
if not self.find_device():
89+
self.reset = True
90+
print('Please reset your board to switch to DFU mode...')
91+
while not self.find_device():
92+
time.sleep(0.1)
8593

8694
def find_device(self):
8795
cmd = list(self.cmd) + ['-l']
@@ -91,17 +99,9 @@ def find_device(self):
9199

92100
def do_run(self, command, **kwargs):
93101
self.require(self.cmd[0])
94-
reset = False
102+
95103
if not self.find_device():
96-
reset = True
97-
log.dbg('Device not found, waiting for it',
98-
level=log.VERBOSE_EXTREME)
99-
# Use of print() here is advised. We don't want to lose
100-
# this information in a separate log -- this is
101-
# interactive and requires a terminal.
102-
print('Please reset your board to switch to DFU mode...')
103-
while not self.find_device():
104-
time.sleep(0.1)
104+
raise RuntimeError('device not found')
105105

106106
cmd = list(self.cmd)
107107
if self.dfuse:
@@ -118,6 +118,6 @@ def do_run(self, command, **kwargs):
118118
#
119119
# DfuSe targets do as well, except when 'leave' is given
120120
# as an option.
121-
reset = False
122-
if reset:
121+
self.reset = False
122+
if self.reset:
123123
print('Now reset your board again to switch back to runtime mode.')

scripts/west_commands/runners/esp32.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@
66

77
from os import path
88

9-
from west import log
10-
119
from runners.core import ZephyrBinaryRunner, RunnerCaps
1210

1311

@@ -95,8 +93,9 @@ def do_run(self, command, **kwargs):
9593
else:
9694
cmd_flash.extend(['0x1000', bin_name])
9795

98-
log.inf("Converting ELF to BIN")
96+
self.logger.info("Converting ELF to BIN")
9997
self.check_call(cmd_convert)
10098

101-
log.inf("Flashing ESP32 on {} ({}bps)".format(self.device, self.baud))
99+
self.logger.info("Flashing ESP32 on {} ({}bps)".
100+
format(self.device, self.baud))
102101
self.check_call(cmd_flash)

scripts/west_commands/runners/intel_s1000.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
from os import path
88
import time
99
import signal
10-
from west import log
1110

1211
from runners.core import ZephyrBinaryRunner
1312

@@ -84,7 +83,7 @@ def flash(self, **kwargs):
8483
jtag_instr_file = kwargs['ocd-jtag-instr']
8584
gdb_flash_file = kwargs['gdb-flash-file']
8685

87-
self.print_gdbserver_message(self.gdb_port)
86+
self.log_gdbserver_message(self.gdb_port)
8887
server_cmd = [self.xt_ocd_dir,
8988
'-c', topology_file,
9089
'-I', jtag_instr_file]
@@ -122,7 +121,7 @@ def do_debug(self, **kwargs):
122121
topology_file = kwargs['ocd-topology']
123122
jtag_instr_file = kwargs['ocd-jtag-instr']
124123

125-
self.print_gdbserver_message(self.gdb_port)
124+
self.log_gdbserver_message(self.gdb_port)
126125
server_cmd = [self.xt_ocd_dir,
127126
'-c', topology_file,
128127
'-I', jtag_instr_file]
@@ -152,14 +151,11 @@ def do_debug(self, **kwargs):
152151
server_proc.terminate()
153152
server_proc.wait()
154153

155-
def print_gdbserver_message(self, gdb_port):
156-
log.inf('Intel S1000 GDB server running on port {}'.format(gdb_port))
157-
158154
def debugserver(self, **kwargs):
159155
topology_file = kwargs['ocd-topology']
160156
jtag_instr_file = kwargs['ocd-jtag-instr']
161157

162-
self.print_gdbserver_message(self.gdb_port)
158+
self.log_gdbserver_message(self.gdb_port)
163159
server_cmd = [self.xt_ocd_dir,
164160
'-c', topology_file,
165161
'-I', jtag_instr_file]
@@ -170,3 +166,7 @@ def debugserver(self, **kwargs):
170166
time.sleep(6)
171167
server_proc.terminate()
172168
self.check_call(server_cmd)
169+
170+
def log_gdbserver_message(self, gdb_port):
171+
self.logger.info('Intel S1000 GDB server running on port {}'.
172+
format(gdb_port))

scripts/west_commands/runners/jlink.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,9 @@
77
import argparse
88
import os
99
import shlex
10-
import shutil
1110
import sys
1211
import tempfile
1312

14-
from west import log
1513
from runners.core import ZephyrBinaryRunner, RunnerCaps, \
1614
BuildConfiguration
1715

@@ -105,7 +103,8 @@ def create(cls, cfg, args):
105103
tui=args.tui, tool_opt=args.tool_opt)
106104

107105
def print_gdbserver_message(self):
108-
log.inf('J-Link GDB server running on port {}'.format(self.gdb_port))
106+
self.logger.info('J-Link GDB server running on port {}'.
107+
format(self.gdb_port))
109108

110109
def do_run(self, command, **kwargs):
111110
server_cmd = ([self.gdbserver] +
@@ -162,8 +161,8 @@ def flash(self, **kwargs):
162161
lines.append('g') # Start the CPU
163162
lines.append('q') # Close the connection and quit
164163

165-
log.dbg('JLink commander script:')
166-
log.dbg('\n'.join(lines))
164+
self.logger.debug('JLink commander script:')
165+
self.logger.debug('\n'.join(lines))
167166

168167
# Don't use NamedTemporaryFile: the resulting file can't be
169168
# opened again on Windows.
@@ -179,5 +178,5 @@ def flash(self, **kwargs):
179178
'-CommanderScript', fname] +
180179
self.tool_opt)
181180

182-
log.inf('Flashing Target Device')
181+
self.logger.info('Flashing Target Device')
183182
self.check_call(cmd)

scripts/west_commands/runners/nios2.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
'''Runner for NIOS II, based on quartus-flash.py and GDB.'''
66

7-
from west import log
87
from runners.core import ZephyrBinaryRunner, NetworkPortHelper
98

109

@@ -65,7 +64,8 @@ def flash(self, **kwargs):
6564
self.check_call(cmd)
6665

6766
def print_gdbserver_message(self, gdb_port):
68-
log.inf('Nios II GDB server running on port {}'.format(gdb_port))
67+
self.logger.info('Nios II GDB server running on port {}'.
68+
format(gdb_port))
6969

7070
def debug_debugserver(self, command, **kwargs):
7171
# Per comments in the shell script, the NIOSII GDB server

0 commit comments

Comments
 (0)