Skip to content
Browse files

scripts: runners: abstract jlink's missing program support

The runners/ script has a mechanism for erroring out if a host
tool is not installed. Abstract it into runners/ and handle it
from This will let it be used in more places.

Signed-off-by: Marti Bolivar <>
  • Loading branch information...
mbolivar authored and carlescufi committed May 25, 2019
1 parent db7b9a9 commit c07267a26a6edac8f1f5ffebd2836b0f5e8d97ad
@@ -17,7 +17,7 @@
from west.commands import CommandContextError

from runners import get_runner_cls, ZephyrBinaryRunner
from runners import get_runner_cls, ZephyrBinaryRunner, MissingProgram

from zephyr_ext_common import cached_runner_config

@@ -229,7 +229,11 @@ def do_run_common(command, args, runner_args, cached_runner_var):
if unknown:
log.die('Runner', runner, 'received unknown arguments:', unknown)
runner = runner_cls.create(cfg, parsed_args)
except MissingProgram as e:
log.die('required program', e.filename,
'not found; install it or add its location to PATH')

@@ -2,7 +2,7 @@
# SPDX-License-Identifier: Apache-2.0

from runners.core import ZephyrBinaryRunner
from runners.core import ZephyrBinaryRunner, MissingProgram

# We import these here to ensure the ZephyrBinaryRunner subclasses are
# defined; otherwise, ZephyrBinaryRunner.create_for_shell_script()
@@ -13,8 +13,10 @@

import abc
import argparse
import errno
import os
import platform
import shutil
import signal
import subprocess

@@ -157,6 +159,19 @@ def _parse_value(self, value):
return value

class MissingProgram(FileNotFoundError):
'''FileNotFoundError subclass for missing program dependencies.
No significant changes from the parent FileNotFoundError; this is
useful for explicitly signaling that the file in question is a
program that some class requires to proceed.
The filename attribute contains the missing program.'''

def __init__(self, program):
super().__init__(errno.ENOENT, os.strerror(errno.ENOENT), program)

class RunnerCaps:
'''This class represents a runner class's capabilities.
@@ -415,6 +430,20 @@ def do_run(self, command, **kwargs):
In case of an unsupported command, raise a ValueError.'''

def require(self, program):
'''Require that a program is installed before proceeding.
:param program: name of the program that is required,
or path to a program binary.
If ``program`` is an absolute path to an existing program
binary, this call succeeds. Otherwise, try to find the program
by name on the system PATH.
On error, raises MissingProgram.'''
if shutil.which(program) is None:
raise MissingProgram(program)

def run_server_and_client(self, server, client):
'''Run a server that ignores SIGINT, and a client that handles it.
@@ -107,10 +107,6 @@ def create(cls, cfg, args):
def print_gdbserver_message(self):
log.inf('J-Link GDB server running on port {}'.format(self.gdb_port))

def check_cmd(self, cmd):
if shutil.which(cmd) is None:
log.die('{} is not installed or cannot be found'.format(cmd))

def do_run(self, command, **kwargs):
server_cmd = ([self.gdbserver] +
['-select', 'usb', # only USB connections supported
@@ -125,11 +121,11 @@ def do_run(self, command, **kwargs):
if command == 'flash':
elif command == 'debugserver':
if self.gdb_cmd is None:
raise ValueError('Cannot debug; gdb is missing')
if self.elf_name is None:
@@ -149,7 +145,7 @@ def do_run(self, command, **kwargs):
self.run_server_and_client(server_cmd, client_cmd)

def flash(self, **kwargs):
if self.bin_name is None:
raise ValueError('Cannot flash; bin_name is missing')

0 comments on commit c07267a

Please sign in to comment.
You can’t perform that action at this time.