scripts: update west to upstream commit f01059a
The main reason to copy into upstream is to bring in a new runner. Signed-off-by: Marti Bolivar <marti@foundries.io>
This commit is contained in:
parent
cee87a1ca2
commit
0b1b4e2d23
32 changed files with 1856 additions and 675 deletions
|
@ -6,10 +6,13 @@
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
|
import configparser
|
||||||
import os
|
import os
|
||||||
import platform
|
import platform
|
||||||
|
import pykwalify.core
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
import yaml
|
||||||
|
|
||||||
import west._bootstrap.version as version
|
import west._bootstrap.version as version
|
||||||
|
|
||||||
|
@ -29,7 +32,7 @@ WEST_DIR = 'west'
|
||||||
# Subdirectory to check out the west source repository into.
|
# Subdirectory to check out the west source repository into.
|
||||||
WEST = 'west'
|
WEST = 'west'
|
||||||
# Default west repository URL.
|
# Default west repository URL.
|
||||||
WEST_DEFAULT = 'https://github.com/zephyrproject-rtos/west'
|
WEST_URL_DEFAULT = 'https://github.com/zephyrproject-rtos/west'
|
||||||
# Default revision to check out of the west repository.
|
# Default revision to check out of the west repository.
|
||||||
WEST_REV_DEFAULT = 'master'
|
WEST_REV_DEFAULT = 'master'
|
||||||
# File inside of WEST_DIR which marks it as the top level of the
|
# File inside of WEST_DIR which marks it as the top level of the
|
||||||
|
@ -43,10 +46,11 @@ WEST_MARKER = '.west_topdir'
|
||||||
# Manifest repository directory under WEST_DIR.
|
# Manifest repository directory under WEST_DIR.
|
||||||
MANIFEST = 'manifest'
|
MANIFEST = 'manifest'
|
||||||
# Default manifest repository URL.
|
# Default manifest repository URL.
|
||||||
MANIFEST_DEFAULT = 'https://github.com/zephyrproject-rtos/manifest'
|
MANIFEST_URL_DEFAULT = 'https://github.com/zephyrproject-rtos/manifest'
|
||||||
# Default revision to check out of the manifest repository.
|
# Default revision to check out of the manifest repository.
|
||||||
MANIFEST_REV_DEFAULT = 'master'
|
MANIFEST_REV_DEFAULT = 'master'
|
||||||
|
|
||||||
|
_SCHEMA_PATH = os.path.join(os.path.dirname(__file__), "west-schema.yml")
|
||||||
|
|
||||||
#
|
#
|
||||||
# Helpers shared between init and wrapper mode
|
# Helpers shared between init and wrapper mode
|
||||||
|
@ -61,13 +65,34 @@ class WestNotFound(WestError):
|
||||||
'''Neither the current directory nor any parent has a West installation.'''
|
'''Neither the current directory nor any parent has a West installation.'''
|
||||||
|
|
||||||
|
|
||||||
def find_west_topdir(start):
|
def west_dir(start=None):
|
||||||
'''Find the top-level installation directory, starting at ``start``.
|
'''
|
||||||
|
Returns the path to the west/ directory, searching ``start`` and its
|
||||||
|
parents.
|
||||||
|
|
||||||
If none is found, raises WestNotFound.'''
|
Raises WestNotFound if no west directory is found.
|
||||||
|
'''
|
||||||
|
return os.path.join(west_topdir(start), WEST_DIR)
|
||||||
|
|
||||||
|
|
||||||
|
def manifest_dir(start=None):
|
||||||
|
'''
|
||||||
|
Returns the path to the manifest/ directory, searching ``start`` and its
|
||||||
|
parents.
|
||||||
|
|
||||||
|
Raises WestNotFound if no west directory is found.
|
||||||
|
'''
|
||||||
|
return os.path.join(west_topdir(start), MANIFEST)
|
||||||
|
|
||||||
|
|
||||||
|
def west_topdir(start=None):
|
||||||
|
'''
|
||||||
|
Like west_dir(), but returns the path to the parent directory of the west/
|
||||||
|
directory instead, where project repositories are stored
|
||||||
|
'''
|
||||||
# If you change this function, make sure to update west.util.west_topdir().
|
# If you change this function, make sure to update west.util.west_topdir().
|
||||||
|
|
||||||
cur_dir = start
|
cur_dir = start or os.getcwd()
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
if os.path.isfile(os.path.join(cur_dir, WEST_DIR, WEST_MARKER)):
|
if os.path.isfile(os.path.join(cur_dir, WEST_DIR, WEST_MARKER)):
|
||||||
|
@ -81,13 +106,11 @@ def find_west_topdir(start):
|
||||||
cur_dir = parent_dir
|
cur_dir = parent_dir
|
||||||
|
|
||||||
|
|
||||||
def clone(url, rev, dest):
|
def clone(desc, url, rev, dest):
|
||||||
if os.path.exists(dest):
|
if os.path.exists(dest):
|
||||||
raise WestError('refusing to clone into existing location ' + dest)
|
raise WestError('refusing to clone into existing location ' + dest)
|
||||||
|
|
||||||
if not url.startswith(('http:', 'https:', 'git:', 'git+shh:', 'file:')):
|
print('=== Cloning {} from {}, rev. {} ==='.format(desc, url, rev))
|
||||||
raise WestError('Unknown URL scheme for repository: {}'.format(url))
|
|
||||||
|
|
||||||
subprocess.check_call(('git', 'clone', '-b', rev, '--', url, dest))
|
subprocess.check_call(('git', 'clone', '-b', rev, '--', url, dest))
|
||||||
|
|
||||||
|
|
||||||
|
@ -100,49 +123,200 @@ def init(argv):
|
||||||
'''Command line handler for ``west init`` invocations.
|
'''Command line handler for ``west init`` invocations.
|
||||||
|
|
||||||
This exits the program with a nonzero exit code if fatal errors occur.'''
|
This exits the program with a nonzero exit code if fatal errors occur.'''
|
||||||
|
|
||||||
|
# Remember to update scripts/west-completion.bash if you add or remove
|
||||||
|
# flags
|
||||||
|
|
||||||
init_parser = argparse.ArgumentParser(
|
init_parser = argparse.ArgumentParser(
|
||||||
prog='west init',
|
prog='west init',
|
||||||
description='Bootstrap initialize a Zephyr installation')
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||||
|
description='''
|
||||||
|
Initializes a Zephyr installation. Use "west clone" afterwards to fetch the
|
||||||
|
sources.
|
||||||
|
|
||||||
|
In more detail, does the following:
|
||||||
|
|
||||||
|
1. Clones the manifest repository to west/manifest, and the west repository
|
||||||
|
to west/west
|
||||||
|
|
||||||
|
2. Creates a marker file west/{}
|
||||||
|
|
||||||
|
3. Creates an initial configuration file west/config
|
||||||
|
|
||||||
|
As an alternative to manually editing west/config, 'west init' can be rerun on
|
||||||
|
an already initialized West instance to update configuration settings. Only
|
||||||
|
explicitly passed configuration values (e.g. --mr MANIFEST_REVISION) are
|
||||||
|
updated.
|
||||||
|
|
||||||
|
Updating the manifest URL or revision via 'west init' automatically runs 'west
|
||||||
|
update --reset-manifest --reset-projects' afterwards to reset the manifest to
|
||||||
|
the new revision, and all projects to their new manifest revisions.
|
||||||
|
|
||||||
|
Updating the west URL or revision also runs 'west update --reset-west'.
|
||||||
|
|
||||||
|
To suppress the reset of the manifest, west, and projects, pass --no-reset.
|
||||||
|
With --no-reset, only the configuration file will be updated, and you will have
|
||||||
|
to handle any resetting yourself.
|
||||||
|
'''.format(WEST_MARKER))
|
||||||
|
|
||||||
init_parser.add_argument(
|
init_parser.add_argument(
|
||||||
'-b', '--base-url',
|
'-m', '--manifest-url',
|
||||||
help='''Base URL for both 'manifest' and 'zephyr' repositories; cannot
|
help='Manifest repository URL (default: {})'
|
||||||
be given if either -u or -w are''')
|
.format(MANIFEST_URL_DEFAULT))
|
||||||
|
|
||||||
init_parser.add_argument(
|
init_parser.add_argument(
|
||||||
'-u', '--manifest-url',
|
'--mr', '--manifest-rev', dest='manifest_rev',
|
||||||
help='Zephyr manifest fetch URL, default ' + MANIFEST_DEFAULT)
|
help='Manifest revision to fetch (default: {})'
|
||||||
|
.format(MANIFEST_REV_DEFAULT))
|
||||||
|
|
||||||
init_parser.add_argument(
|
init_parser.add_argument(
|
||||||
'--mr', '--manifest-rev', default=MANIFEST_REV_DEFAULT,
|
'--nr', '--no-reset', dest='reset', action='store_false',
|
||||||
dest='manifest_rev',
|
help='''Suppress the automatic reset of the manifest, west, and project
|
||||||
help='Manifest revision to fetch, default ' + MANIFEST_REV_DEFAULT)
|
repositories when re-running 'west init' in an existing
|
||||||
init_parser.add_argument(
|
installation to update the manifest or west URL/revision''')
|
||||||
'-w', '--west-url',
|
|
||||||
help='West fetch URL, default ' + WEST_DEFAULT)
|
|
||||||
init_parser.add_argument(
|
|
||||||
'--wr', '--west-rev', default=WEST_REV_DEFAULT, dest='west_rev',
|
|
||||||
help='West revision to fetch, default ' + WEST_REV_DEFAULT)
|
|
||||||
init_parser.add_argument(
|
init_parser.add_argument(
|
||||||
'directory', nargs='?', default=None,
|
'directory', nargs='?', default=None,
|
||||||
help='Initializes in this directory, creating it if necessary')
|
help='''Directory to initialize West in. Missing directories will be
|
||||||
|
created automatically. (default: current directory)''')
|
||||||
|
|
||||||
args = init_parser.parse_args(args=argv)
|
args = init_parser.parse_args(args=argv)
|
||||||
directory = args.directory or os.getcwd()
|
|
||||||
|
|
||||||
if args.base_url:
|
|
||||||
if args.manifest_url or args.west_url:
|
|
||||||
sys.exit('fatal error: -b is incompatible with -u and -w')
|
|
||||||
args.manifest_url = args.base_url.rstrip('/') + '/manifest'
|
|
||||||
args.west_url = args.base_url.rstrip('/') + '/west'
|
|
||||||
else:
|
|
||||||
if not args.manifest_url:
|
|
||||||
args.manifest_url = MANIFEST_DEFAULT
|
|
||||||
if not args.west_url:
|
|
||||||
args.west_url = WEST_DEFAULT
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
topdir = find_west_topdir(directory)
|
reinit(os.path.join(west_dir(args.directory), 'config'), args)
|
||||||
init_reinit(topdir, args)
|
|
||||||
except WestNotFound:
|
except WestNotFound:
|
||||||
init_bootstrap(directory, args)
|
bootstrap(args)
|
||||||
|
|
||||||
|
|
||||||
|
def bootstrap(args):
|
||||||
|
'''Bootstrap a new manifest + West installation.'''
|
||||||
|
|
||||||
|
west_url = WEST_URL_DEFAULT
|
||||||
|
manifest_url = args.manifest_url or MANIFEST_URL_DEFAULT
|
||||||
|
|
||||||
|
west_rev = WEST_REV_DEFAULT
|
||||||
|
manifest_rev = args.manifest_rev or MANIFEST_REV_DEFAULT
|
||||||
|
|
||||||
|
directory = args.directory or os.getcwd()
|
||||||
|
|
||||||
|
if not os.path.isdir(directory):
|
||||||
|
try:
|
||||||
|
print('Initializing in new directory', directory)
|
||||||
|
os.makedirs(directory, exist_ok=False)
|
||||||
|
except PermissionError:
|
||||||
|
sys.exit('Cannot initialize in {}: permission denied'.format(
|
||||||
|
directory))
|
||||||
|
except FileExistsError:
|
||||||
|
sys.exit('Something else created {} concurrently; quitting'.format(
|
||||||
|
directory))
|
||||||
|
except Exception as e:
|
||||||
|
sys.exit("Can't create directory {}: {}".format(
|
||||||
|
directory, e.args))
|
||||||
|
else:
|
||||||
|
print('Initializing in', directory)
|
||||||
|
|
||||||
|
# Clone the west source code and the manifest into west/. Git will create
|
||||||
|
# the west/ directory if it does not exist.
|
||||||
|
|
||||||
|
clone('manifest repository', manifest_url, manifest_rev,
|
||||||
|
os.path.join(directory, WEST_DIR, MANIFEST))
|
||||||
|
|
||||||
|
# Parse the manifest and look for a section named "west"
|
||||||
|
manifest_file = os.path.join(directory, WEST_DIR, MANIFEST, 'default.yml')
|
||||||
|
with open(manifest_file, 'r') as f:
|
||||||
|
data = yaml.safe_load(f.read())
|
||||||
|
|
||||||
|
if 'west' in data:
|
||||||
|
wdata = data['west']
|
||||||
|
try:
|
||||||
|
pykwalify.core.Core(
|
||||||
|
source_data=wdata,
|
||||||
|
schema_files=[_SCHEMA_PATH]
|
||||||
|
).validate()
|
||||||
|
except pykwalify.errors.SchemaError as e:
|
||||||
|
sys.exit("Error: Failed to parse manifest file '{}': {}"
|
||||||
|
.format(manifest_file, e))
|
||||||
|
|
||||||
|
if 'url' in wdata:
|
||||||
|
west_url = wdata['url']
|
||||||
|
if 'revision' in wdata:
|
||||||
|
west_rev = wdata['revision']
|
||||||
|
|
||||||
|
print("cloning {} at revision {}".format(west_url, west_rev))
|
||||||
|
clone('west repository', west_url, west_rev,
|
||||||
|
os.path.join(directory, WEST_DIR, WEST))
|
||||||
|
|
||||||
|
# Create an initial configuration file
|
||||||
|
|
||||||
|
config_path = os.path.join(directory, WEST_DIR, 'config')
|
||||||
|
update_conf(config_path, manifest_url, manifest_rev)
|
||||||
|
print('=== Initial configuration written to {} ==='.format(config_path))
|
||||||
|
|
||||||
|
# Create a dotfile to mark the installation. Hide it on Windows.
|
||||||
|
|
||||||
|
with open(os.path.join(directory, WEST_DIR, WEST_MARKER), 'w') as f:
|
||||||
|
hide_file(f.name)
|
||||||
|
|
||||||
|
print('=== West initialized. Now run "west clone" in {}. ==='.
|
||||||
|
format(directory))
|
||||||
|
|
||||||
|
|
||||||
|
def reinit(config_path, args):
|
||||||
|
'''
|
||||||
|
Reinitialize an existing installation.
|
||||||
|
|
||||||
|
This updates the west/config configuration file, and optionally resets the
|
||||||
|
manifest, west, and project repositories to the new revision.
|
||||||
|
'''
|
||||||
|
manifest_url = args.manifest_url
|
||||||
|
|
||||||
|
if not (manifest_url or args.manifest_rev):
|
||||||
|
sys.exit('West already initialized. Please pass any settings you '
|
||||||
|
'want to change.')
|
||||||
|
|
||||||
|
update_conf(config_path, manifest_url, args.manifest_rev)
|
||||||
|
|
||||||
|
print('=== Updated configuration written to {} ==='.format(config_path))
|
||||||
|
|
||||||
|
if args.reset:
|
||||||
|
cmd = ['update', '--reset-manifest', '--reset-projects',
|
||||||
|
'--reset-west']
|
||||||
|
print("=== Running 'west {}' to update repositories ==="
|
||||||
|
.format(' '.join(cmd)))
|
||||||
|
wrap(cmd)
|
||||||
|
|
||||||
|
|
||||||
|
def update_conf(config_path, manifest_url, manifest_rev):
|
||||||
|
'''
|
||||||
|
Creates or updates the configuration file at 'config_path' with the
|
||||||
|
specified values. Values that are None/empty are ignored.
|
||||||
|
'''
|
||||||
|
config = configparser.ConfigParser()
|
||||||
|
|
||||||
|
# This is a no-op if the file doesn't exist, so no need to check
|
||||||
|
config.read(config_path)
|
||||||
|
|
||||||
|
update_key(config, 'manifest', 'remote', manifest_url)
|
||||||
|
update_key(config, 'manifest', 'revision', manifest_rev)
|
||||||
|
|
||||||
|
with open(config_path, 'w') as f:
|
||||||
|
config.write(f)
|
||||||
|
|
||||||
|
|
||||||
|
def update_key(config, section, key, value):
|
||||||
|
'''
|
||||||
|
Updates 'key' in section 'section' in ConfigParser 'config', creating
|
||||||
|
'section' if it does not exist.
|
||||||
|
|
||||||
|
If value is None/empty, 'key' is left as-is.
|
||||||
|
'''
|
||||||
|
if not value:
|
||||||
|
return
|
||||||
|
|
||||||
|
if section not in config:
|
||||||
|
config[section] = {}
|
||||||
|
|
||||||
|
config[section][key] = value
|
||||||
|
|
||||||
|
|
||||||
def hide_file(path):
|
def hide_file(path):
|
||||||
|
@ -168,66 +342,52 @@ def hide_file(path):
|
||||||
.format(system, path), file=sys.stderr)
|
.format(system, path), file=sys.stderr)
|
||||||
|
|
||||||
|
|
||||||
def init_bootstrap(directory, args):
|
|
||||||
'''Bootstrap a new manifest + West installation in the given directory.'''
|
|
||||||
if not os.path.isdir(directory):
|
|
||||||
try:
|
|
||||||
print('Initializing in new directory', directory)
|
|
||||||
os.makedirs(directory, exist_ok=False)
|
|
||||||
except PermissionError:
|
|
||||||
sys.exit('Cannot initialize in {}: permission denied'.format(
|
|
||||||
directory))
|
|
||||||
except FileExistsError:
|
|
||||||
sys.exit('Something else created {} concurrently; quitting'.format(
|
|
||||||
directory))
|
|
||||||
except Exception as e:
|
|
||||||
sys.exit("Can't create directory {}: {}".format(
|
|
||||||
directory, e.args))
|
|
||||||
else:
|
|
||||||
print('Initializing in', directory)
|
|
||||||
|
|
||||||
# Clone the west source code and the manifest into west/. Git will create
|
|
||||||
# the west/ directory if it does not exist.
|
|
||||||
|
|
||||||
clone(args.west_url, args.west_rev,
|
|
||||||
os.path.join(directory, WEST_DIR, WEST))
|
|
||||||
|
|
||||||
clone(args.manifest_url, args.manifest_rev,
|
|
||||||
os.path.join(directory, WEST_DIR, MANIFEST))
|
|
||||||
|
|
||||||
# Create a dotfile to mark the installation. Hide it on Windows.
|
|
||||||
|
|
||||||
with open(os.path.join(directory, WEST_DIR, WEST_MARKER), 'w') as f:
|
|
||||||
hide_file(f.name)
|
|
||||||
|
|
||||||
|
|
||||||
def init_reinit(directory, args):
|
|
||||||
# TODO
|
|
||||||
sys.exit('Re-initializing an existing installation is not yet supported.')
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Wrap a West command
|
# Wrap a West command
|
||||||
#
|
#
|
||||||
|
|
||||||
|
def append_to_pythonpath(directory):
|
||||||
|
pp = os.environ.get('PYTHONPATH')
|
||||||
|
os.environ['PYTHONPATH'] = ':'.join(([pp] if pp else []) + [directory])
|
||||||
|
|
||||||
|
|
||||||
def wrap(argv):
|
def wrap(argv):
|
||||||
printing_version = False
|
printing_version = False
|
||||||
|
printing_help_only = False
|
||||||
|
|
||||||
if argv and argv[0] in ('-V', '--version'):
|
if argv:
|
||||||
print('West bootstrapper version: v{} ({})'.format(version.__version__,
|
if argv[0] in ('-V', '--version'):
|
||||||
os.path.dirname(__file__)))
|
print('West bootstrapper version: v{} ({})'.
|
||||||
printing_version = True
|
format(version.__version__, os.path.dirname(__file__)))
|
||||||
|
printing_version = True
|
||||||
|
elif len(argv) == 1 and argv[0] in ('-h', '--help'):
|
||||||
|
# This only matters if we're called outside of an
|
||||||
|
# installation directory. We delegate to the main help if
|
||||||
|
# called from within one, because it includes a list of
|
||||||
|
# available commands, etc.
|
||||||
|
printing_help_only = True
|
||||||
|
|
||||||
start = os.getcwd()
|
start = os.getcwd()
|
||||||
try:
|
try:
|
||||||
topdir = find_west_topdir(start)
|
topdir = west_topdir(start)
|
||||||
except WestNotFound:
|
except WestNotFound:
|
||||||
if printing_version:
|
if printing_version:
|
||||||
sys.exit(0) # run outside of an installation directory
|
sys.exit(0) # run outside of an installation directory
|
||||||
|
elif printing_help_only:
|
||||||
|
# We call print multiple times here and below instead of using
|
||||||
|
# \n to be newline agnostic.
|
||||||
|
print('To set up a Zephyr installation here, run "west init".')
|
||||||
|
print('Run "west init -h" for additional information.')
|
||||||
|
sys.exit(0)
|
||||||
else:
|
else:
|
||||||
sys.exit('Error: not a Zephyr directory (or any parent): {}\n'
|
print('Error: "{}" is not a Zephyr installation directory.'.
|
||||||
'Use "west init" to install Zephyr here'.format(start))
|
format(start), file=sys.stderr)
|
||||||
|
print('Things to try:', file=sys.stderr)
|
||||||
|
print(' - Run "west init" to set up an installation here.',
|
||||||
|
file=sys.stderr)
|
||||||
|
print(' - Run "west init -h" for additional information.',
|
||||||
|
file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
west_git_repo = os.path.join(topdir, WEST_DIR, WEST)
|
west_git_repo = os.path.join(topdir, WEST_DIR, WEST)
|
||||||
if printing_version:
|
if printing_version:
|
||||||
|
@ -239,20 +399,23 @@ def wrap(argv):
|
||||||
print('West repository version: {} ({})'.format(git_describe,
|
print('West repository version: {} ({})'.format(git_describe,
|
||||||
west_git_repo))
|
west_git_repo))
|
||||||
except subprocess.CalledProcessError:
|
except subprocess.CalledProcessError:
|
||||||
print('West repository verison: unknown; no tags were found')
|
print('West repository version: unknown; no tags were found')
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
# Replace the wrapper process with the "real" west
|
# Import the west package from the installation and run its main
|
||||||
|
# function with the given command-line arguments.
|
||||||
# sys.argv[1:] strips the argv[0] of the wrapper script itself
|
#
|
||||||
argv = ([sys.executable,
|
# This can't be done as a subprocess: that would break the
|
||||||
os.path.join(west_git_repo, 'src', 'west', 'main.py')] +
|
# runners' debug handling for GDB, which needs to block the usual
|
||||||
argv)
|
# control-C signal handling. GDB uses Ctrl-C to halt the debug
|
||||||
|
# target. So we really do need to import west and delegate within
|
||||||
try:
|
# this bootstrap process.
|
||||||
subprocess.check_call(argv)
|
#
|
||||||
except subprocess.CalledProcessError as e:
|
# Put this at position 1 to make sure it comes before random stuff
|
||||||
sys.exit(1)
|
# that might be on a developer's PYTHONPATH in the import order.
|
||||||
|
sys.path.insert(1, os.path.join(west_git_repo, 'src'))
|
||||||
|
import west.main
|
||||||
|
west.main.main(argv)
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
|
|
|
@ -2,4 +2,4 @@
|
||||||
#
|
#
|
||||||
# This is the Python 3 version of option 3 in:
|
# This is the Python 3 version of option 3 in:
|
||||||
# https://packaging.python.org/guides/single-sourcing-package-version/#single-sourcing-the-version
|
# https://packaging.python.org/guides/single-sourcing-package-version/#single-sourcing-the-version
|
||||||
__version__ = '0.2.0rc1'
|
__version__ = '0.4.1'
|
||||||
|
|
17
scripts/meta/west/_bootstrap/west-schema.yml
Normal file
17
scripts/meta/west/_bootstrap/west-schema.yml
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
## A pykwalify schema for basic validation of the structure of a
|
||||||
|
## west YAML file. (Full validation would require additional work,
|
||||||
|
## e.g. to validate that remote URLs obey the URL format specified in
|
||||||
|
## rfc1738.)
|
||||||
|
##
|
||||||
|
|
||||||
|
# The top-level west yaml is a map. The only top-level element is
|
||||||
|
# 'west'. All other elements are contained within it. This allows
|
||||||
|
# us a bit of future-proofing.
|
||||||
|
type: map
|
||||||
|
mapping:
|
||||||
|
url:
|
||||||
|
required: false
|
||||||
|
type: str
|
||||||
|
revision:
|
||||||
|
required: false
|
||||||
|
type: str
|
|
@ -10,8 +10,8 @@ building Zephyr applications needed by multiple commands.
|
||||||
See west.cmd.build for the build command itself.
|
See west.cmd.build for the build command itself.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import cmake
|
from west import cmake
|
||||||
import log
|
from west import log
|
||||||
|
|
||||||
DEFAULT_BUILD_DIR = 'build'
|
DEFAULT_BUILD_DIR = 'build'
|
||||||
'''Name of the default Zephyr build directory.'''
|
'''Name of the default Zephyr build directory.'''
|
||||||
|
|
|
@ -10,8 +10,8 @@ import re
|
||||||
import subprocess
|
import subprocess
|
||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
import log
|
from west import log
|
||||||
from util import quote_sh_list
|
from west.util import quote_sh_list
|
||||||
|
|
||||||
__all__ = ['run_cmake', 'run_build',
|
__all__ = ['run_cmake', 'run_build',
|
||||||
'make_c_identifier',
|
'make_c_identifier',
|
||||||
|
|
|
@ -5,10 +5,11 @@
|
||||||
import argparse
|
import argparse
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import log
|
from west import log
|
||||||
import cmake
|
from west import cmake
|
||||||
from build import DEFAULT_BUILD_DIR, DEFAULT_CMAKE_GENERATOR, is_zephyr_build
|
from west.build import DEFAULT_BUILD_DIR, DEFAULT_CMAKE_GENERATOR, \
|
||||||
from commands import WestCommand
|
is_zephyr_build
|
||||||
|
from west.commands import WestCommand
|
||||||
|
|
||||||
BUILD_HELP = '''\
|
BUILD_HELP = '''\
|
||||||
Convenience wrapper for building Zephyr applications.
|
Convenience wrapper for building Zephyr applications.
|
||||||
|
@ -72,24 +73,33 @@ class Build(WestCommand):
|
||||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||||
description=self.description)
|
description=self.description)
|
||||||
|
|
||||||
|
# Remember to update scripts/west-completion.bash if you add or remove
|
||||||
|
# flags
|
||||||
|
|
||||||
parser.add_argument('-b', '--board',
|
parser.add_argument('-b', '--board',
|
||||||
help='''board to build for (must be given for the
|
help='''Board to build for (must be given for the
|
||||||
first build, can be omitted later)''')
|
first build, can be omitted later)''')
|
||||||
parser.add_argument('-s', '--source-dir',
|
parser.add_argument('-s', '--source-dir',
|
||||||
help='''explicitly sets the source directory;
|
help='''Explicitly set the source directory.
|
||||||
if not given, infer it from directory context''')
|
If not given and rebuilding an existing Zephyr
|
||||||
|
build directory, this is taken from the CMake
|
||||||
|
cache. Otherwise, the current directory is
|
||||||
|
assumed.''')
|
||||||
parser.add_argument('-d', '--build-dir',
|
parser.add_argument('-d', '--build-dir',
|
||||||
help='''explicitly sets the build directory;
|
help='''Explicitly sets the build directory.
|
||||||
if not given, infer it from directory context''')
|
If not given and the current directory is a Zephyr
|
||||||
|
build directory, it will be used; otherwise, "{}"
|
||||||
|
is assumed. The directory will be created if
|
||||||
|
it doesn't exist.'''.format(DEFAULT_BUILD_DIR))
|
||||||
parser.add_argument('-t', '--target',
|
parser.add_argument('-t', '--target',
|
||||||
help='''override the build system target (e.g.
|
help='''Override the build system target (e.g.
|
||||||
'clean', 'pristine', etc.)''')
|
'clean', 'pristine', etc.)''')
|
||||||
parser.add_argument('-c', '--cmake', action='store_true',
|
parser.add_argument('-c', '--cmake', action='store_true',
|
||||||
help='force CMake to run')
|
help='Force CMake to run')
|
||||||
parser.add_argument('-f', '--force', action='store_true',
|
parser.add_argument('-f', '--force', action='store_true',
|
||||||
help='ignore any errors and try to build anyway')
|
help='Ignore any errors and try to build anyway')
|
||||||
parser.add_argument('cmake_opts', nargs='*', metavar='cmake_opt',
|
parser.add_argument('cmake_opts', nargs='*', metavar='cmake_opt',
|
||||||
help='extra option to pass to CMake; implies -c')
|
help='Extra option to pass to CMake; implies -c')
|
||||||
|
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,9 @@
|
||||||
|
|
||||||
from textwrap import dedent
|
from textwrap import dedent
|
||||||
|
|
||||||
from commands.run_common import desc_common, add_parser_common, do_run_common
|
from west.commands.run_common import desc_common, add_parser_common, \
|
||||||
from commands import WestCommand
|
do_run_common
|
||||||
|
from west.commands import WestCommand
|
||||||
|
|
||||||
|
|
||||||
class Debug(WestCommand):
|
class Debug(WestCommand):
|
||||||
|
|
|
@ -4,8 +4,9 @@
|
||||||
|
|
||||||
'''west "flash" command'''
|
'''west "flash" command'''
|
||||||
|
|
||||||
from commands.run_common import desc_common, add_parser_common, do_run_common
|
from west.commands.run_common import desc_common, add_parser_common, \
|
||||||
from commands import WestCommand
|
do_run_common
|
||||||
|
from west.commands import WestCommand
|
||||||
|
|
||||||
|
|
||||||
class Flash(WestCommand):
|
class Flash(WestCommand):
|
||||||
|
@ -13,7 +14,7 @@ class Flash(WestCommand):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(Flash, self).__init__(
|
super(Flash, self).__init__(
|
||||||
'flash',
|
'flash',
|
||||||
'Flash and run a binary onto a board.\n\n' +
|
'Flash and run a binary on a board.\n\n' +
|
||||||
desc_common('flash'),
|
desc_common('flash'),
|
||||||
accepts_unknown_args=True)
|
accepts_unknown_args=True)
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -10,13 +10,13 @@ from os import getcwd, path
|
||||||
from subprocess import CalledProcessError
|
from subprocess import CalledProcessError
|
||||||
import textwrap
|
import textwrap
|
||||||
|
|
||||||
import cmake
|
from west import cmake
|
||||||
import log
|
from west import log
|
||||||
import util
|
from west import util
|
||||||
from build import DEFAULT_BUILD_DIR, is_zephyr_build
|
from west.build import DEFAULT_BUILD_DIR, is_zephyr_build
|
||||||
from runners import get_runner_cls, ZephyrBinaryRunner
|
from west.runners import get_runner_cls, ZephyrBinaryRunner
|
||||||
from runners.core import RunnerConfig
|
from west.runners.core import RunnerConfig
|
||||||
from commands import CommandContextError
|
from west.commands import CommandContextError
|
||||||
|
|
||||||
# Context-sensitive help indentation.
|
# Context-sensitive help indentation.
|
||||||
# Don't change this, or output from argparse won't match up.
|
# Don't change this, or output from argparse won't match up.
|
||||||
|
@ -29,6 +29,9 @@ def add_parser_common(parser_adder, command):
|
||||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||||
description=command.description)
|
description=command.description)
|
||||||
|
|
||||||
|
# Remember to update scripts/west-completion.bash if you add or remove
|
||||||
|
# flags
|
||||||
|
|
||||||
parser.add_argument('-H', '--context', action='store_true',
|
parser.add_argument('-H', '--context', action='store_true',
|
||||||
help='''Rebuild application and print context-sensitive
|
help='''Rebuild application and print context-sensitive
|
||||||
help; this may be combined with --runner to restrict
|
help; this may be combined with --runner to restrict
|
||||||
|
@ -72,14 +75,17 @@ def add_parser_common(parser_adder, command):
|
||||||
#
|
#
|
||||||
# This is how we detect if the user provided them or not when
|
# This is how we detect if the user provided them or not when
|
||||||
# overriding values from the cached configuration.
|
# overriding values from the cached configuration.
|
||||||
|
|
||||||
|
command_verb = "flash" if command == "flash" else "debug"
|
||||||
|
|
||||||
group.add_argument('--board-dir',
|
group.add_argument('--board-dir',
|
||||||
help='Zephyr board directory')
|
help='Zephyr board directory')
|
||||||
group.add_argument('--kernel-elf',
|
group.add_argument('--elf-file',
|
||||||
help='Path to kernel binary in .elf format')
|
help='Path to elf file to {0}'.format(command_verb))
|
||||||
group.add_argument('--kernel-hex',
|
group.add_argument('--hex-file',
|
||||||
help='Path to kernel binary in .hex format')
|
help='Path to hex file to {0}'.format(command_verb))
|
||||||
group.add_argument('--kernel-bin',
|
group.add_argument('--bin-file',
|
||||||
help='Path to kernel binary in .bin format')
|
help='Path to binary file to {0}'.format(command_verb))
|
||||||
group.add_argument('--gdb',
|
group.add_argument('--gdb',
|
||||||
help='Path to GDB, if applicable')
|
help='Path to GDB, if applicable')
|
||||||
group.add_argument('--openocd',
|
group.add_argument('--openocd',
|
||||||
|
@ -108,15 +114,18 @@ def desc_common(command_name):
|
||||||
def cached_runner_config(build_dir, cache):
|
def cached_runner_config(build_dir, cache):
|
||||||
'''Parse the RunnerConfig from a build directory and CMake Cache.'''
|
'''Parse the RunnerConfig from a build directory and CMake Cache.'''
|
||||||
board_dir = cache['ZEPHYR_RUNNER_CONFIG_BOARD_DIR']
|
board_dir = cache['ZEPHYR_RUNNER_CONFIG_BOARD_DIR']
|
||||||
kernel_elf = cache['ZEPHYR_RUNNER_CONFIG_KERNEL_ELF']
|
elf_file = cache.get('ZEPHYR_RUNNER_CONFIG_ELF_FILE',
|
||||||
kernel_hex = cache['ZEPHYR_RUNNER_CONFIG_KERNEL_HEX']
|
cache['ZEPHYR_RUNNER_CONFIG_KERNEL_ELF'])
|
||||||
kernel_bin = cache['ZEPHYR_RUNNER_CONFIG_KERNEL_BIN']
|
hex_file = cache.get('ZEPHYR_RUNNER_CONFIG_HEX_FILE',
|
||||||
|
cache['ZEPHYR_RUNNER_CONFIG_KERNEL_HEX'])
|
||||||
|
bin_file = cache.get('ZEPHYR_RUNNER_CONFIG_BIN_FILE',
|
||||||
|
cache['ZEPHYR_RUNNER_CONFIG_KERNEL_BIN'])
|
||||||
gdb = cache.get('ZEPHYR_RUNNER_CONFIG_GDB')
|
gdb = cache.get('ZEPHYR_RUNNER_CONFIG_GDB')
|
||||||
openocd = cache.get('ZEPHYR_RUNNER_CONFIG_OPENOCD')
|
openocd = cache.get('ZEPHYR_RUNNER_CONFIG_OPENOCD')
|
||||||
openocd_search = cache.get('ZEPHYR_RUNNER_CONFIG_OPENOCD_SEARCH')
|
openocd_search = cache.get('ZEPHYR_RUNNER_CONFIG_OPENOCD_SEARCH')
|
||||||
|
|
||||||
return RunnerConfig(build_dir, board_dir,
|
return RunnerConfig(build_dir, board_dir,
|
||||||
kernel_elf, kernel_hex, kernel_bin,
|
elf_file, hex_file, bin_file,
|
||||||
gdb=gdb, openocd=openocd,
|
gdb=gdb, openocd=openocd,
|
||||||
openocd_search=openocd_search)
|
openocd_search=openocd_search)
|
||||||
|
|
||||||
|
|
|
@ -1,135 +0,0 @@
|
||||||
## A pykwalify schema for basic validation of the structure of a
|
|
||||||
## manifest YAML file. (Full validation would require additional work,
|
|
||||||
## e.g. to validate that remote URLs obey the URL format specified in
|
|
||||||
## rfc1738.)
|
|
||||||
##
|
|
||||||
## This schema has similar semantics to the repo XML format:
|
|
||||||
##
|
|
||||||
## https://gerrit.googlesource.com/git-repo/+/master/docs/manifest-format.txt
|
|
||||||
##
|
|
||||||
## However, the features don't map 1:1.
|
|
||||||
|
|
||||||
# The top-level manifest is a map. The only top-level element is
|
|
||||||
# 'manifest'. All other elements are contained within it. This allows
|
|
||||||
# us a bit of future-proofing.
|
|
||||||
type: map
|
|
||||||
mapping:
|
|
||||||
manifest:
|
|
||||||
required: true
|
|
||||||
type: map
|
|
||||||
mapping:
|
|
||||||
# The "defaults" key specifies some default values used in the
|
|
||||||
# rest of the manifest.
|
|
||||||
#
|
|
||||||
# The value is a map with the following keys:
|
|
||||||
#
|
|
||||||
# - remote: if given, this is the default remote in each project
|
|
||||||
# - revision: if given, this is the default revision to check
|
|
||||||
# out of each project
|
|
||||||
#
|
|
||||||
# See below for more information about remotes and projects.
|
|
||||||
#
|
|
||||||
# Examples:
|
|
||||||
#
|
|
||||||
# default:
|
|
||||||
# remote: zephyrproject-rtos
|
|
||||||
# revision: master
|
|
||||||
defaults:
|
|
||||||
required: false
|
|
||||||
type: map
|
|
||||||
mapping:
|
|
||||||
remote:
|
|
||||||
required: false
|
|
||||||
type: str
|
|
||||||
revision:
|
|
||||||
required: false
|
|
||||||
type: str
|
|
||||||
|
|
||||||
# The "remotes" key specifies a sequence of remotes, each of
|
|
||||||
# which has a name and a fetch URL.
|
|
||||||
#
|
|
||||||
# These work like repo remotes, in that they specify a URL
|
|
||||||
# prefix which remote-specific Git repositories hang off of.
|
|
||||||
# (This saves typing and makes it easier to move things around
|
|
||||||
# when most repositories are on the same server or GitHub
|
|
||||||
# organization.)
|
|
||||||
#
|
|
||||||
# Example:
|
|
||||||
#
|
|
||||||
# remotes:
|
|
||||||
# - name: zephyrproject-rtos
|
|
||||||
# url: https://github.com/zephyrproject-rtos
|
|
||||||
# - name: developer-fork
|
|
||||||
# url: https://github.com/a-developer
|
|
||||||
remotes:
|
|
||||||
required: true
|
|
||||||
type: seq
|
|
||||||
sequence:
|
|
||||||
- type: map
|
|
||||||
mapping:
|
|
||||||
name:
|
|
||||||
required: true
|
|
||||||
type: str
|
|
||||||
url:
|
|
||||||
required: true
|
|
||||||
type: str
|
|
||||||
|
|
||||||
# The "projects" key specifies a sequence of "projects",
|
|
||||||
# i.e. Git repositories. These work like repo projects, in that
|
|
||||||
# each project has a name, a remote, and optional additional
|
|
||||||
# metadata.
|
|
||||||
#
|
|
||||||
# Each project is a map with the following keys:
|
|
||||||
#
|
|
||||||
# - name: Mandatory, the name of the git repository. The clone
|
|
||||||
# URL is formed by remote + '/' + name
|
|
||||||
# - remote: Optional, the name of the remote to pull it from.
|
|
||||||
# If the remote is missing, the remote'key in the top-level
|
|
||||||
# defaults key is used instead. If both are missing, it's an error.
|
|
||||||
# - revision: Optional, the name of the revision to check out.
|
|
||||||
# If not given, the value from the default element will be used.
|
|
||||||
# If both are missing, then the default is 'master'.
|
|
||||||
# - path: Where to clone the repository locally. If missing,
|
|
||||||
# it's cloned at top level in a directory given by its name.
|
|
||||||
# - clone-depth: if given, it is a number which creates a shallow
|
|
||||||
# history in the cloned repository limited to the given number
|
|
||||||
# of commits.
|
|
||||||
#
|
|
||||||
# Example, using default and non-default remotes:
|
|
||||||
#
|
|
||||||
# projects:
|
|
||||||
# # Uses default remote (zephyrproject-rtos), so clone URL is:
|
|
||||||
# # https://github.com/zephyrproject-rtos/zephyr
|
|
||||||
# - name: zephyr
|
|
||||||
# # Manually specified remote; clone URL is:
|
|
||||||
# # https://github.com/a-developer/west
|
|
||||||
# - name: west
|
|
||||||
# remote: developer-fork
|
|
||||||
# # Manually specified remote, clone URL is:
|
|
||||||
# # https://github.com/zephyrproject-rtos/some-vendor-hal
|
|
||||||
# # Local clone path (relative to installation root) is:
|
|
||||||
# # ext/hal/some-vendor
|
|
||||||
# - name: some-vendor-hal
|
|
||||||
# remote: zephyrproject-rtos
|
|
||||||
# path: ext/hal/some-vendor
|
|
||||||
projects:
|
|
||||||
required: true
|
|
||||||
type: seq
|
|
||||||
sequence:
|
|
||||||
- type: map
|
|
||||||
mapping:
|
|
||||||
name:
|
|
||||||
required: true
|
|
||||||
type: str
|
|
||||||
remote:
|
|
||||||
required: false
|
|
||||||
type: str
|
|
||||||
revision:
|
|
||||||
required: false
|
|
||||||
type: text # SHAs could be only numbers
|
|
||||||
path:
|
|
||||||
required: false
|
|
||||||
type: str
|
|
||||||
clone-depth:
|
|
||||||
required: false
|
|
||||||
type: int
|
|
95
scripts/meta/west/config.py
Normal file
95
scripts/meta/west/config.py
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
# Copyright (c) 2018, Nordic Semiconductor ASA
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
'''
|
||||||
|
Configuration file handling, using the standard configparser module.
|
||||||
|
'''
|
||||||
|
|
||||||
|
import configparser
|
||||||
|
import os
|
||||||
|
import platform
|
||||||
|
|
||||||
|
from west.util import west_dir
|
||||||
|
|
||||||
|
|
||||||
|
# Configuration values.
|
||||||
|
#
|
||||||
|
# Initially empty, populated in read_config(). Always having this available is
|
||||||
|
# nice in case something checks configuration values before the configuration
|
||||||
|
# file has been read (e.g. the log.py functions, to check color settings, and
|
||||||
|
# tests).
|
||||||
|
config = configparser.ConfigParser()
|
||||||
|
|
||||||
|
|
||||||
|
def read_config():
|
||||||
|
'''
|
||||||
|
Reads all configuration files, making the configuration values available as
|
||||||
|
a configparser.ConfigParser object in config.config. This object works
|
||||||
|
similarly to a dictionary: config.config['foo']['bar'] gets the value for
|
||||||
|
key 'bar' in section 'foo'.
|
||||||
|
|
||||||
|
Git conventions for configuration file locations are used. See the FILES
|
||||||
|
section in the git-config(1) man page.
|
||||||
|
|
||||||
|
The following configuration files are read.
|
||||||
|
|
||||||
|
System-wide:
|
||||||
|
|
||||||
|
Linux: /etc/westconfig
|
||||||
|
Mac OS: /usr/local/etc/westconfig
|
||||||
|
Windows: %PROGRAMDATA%\\west\\config
|
||||||
|
|
||||||
|
User-specific:
|
||||||
|
|
||||||
|
$XDG_CONFIG_HOME/west/config (on Linux)
|
||||||
|
and
|
||||||
|
~/.westconfig
|
||||||
|
|
||||||
|
($XDG_CONFIG_DIR defaults to ~/.config/ if unset.)
|
||||||
|
|
||||||
|
Instance-specific:
|
||||||
|
|
||||||
|
<West base directory>/west/config
|
||||||
|
|
||||||
|
Configuration values from later configuration files override configuration
|
||||||
|
from earlier ones. Instance-specific configuration values have the highest
|
||||||
|
precedence, and system-wide the lowest.
|
||||||
|
'''
|
||||||
|
|
||||||
|
# Gather (potential) configuration file paths
|
||||||
|
|
||||||
|
# System-wide and user-specific
|
||||||
|
|
||||||
|
if platform.system() == 'Linux':
|
||||||
|
# Probably wouldn't hurt to check $XDG_CONFIG_HOME (defaults to
|
||||||
|
# ~/.config) on all systems. It's listed in git-config(1). People were
|
||||||
|
# iffy about it as of writing though.
|
||||||
|
files = ['/etc/westconfig',
|
||||||
|
os.path.join(os.environ.get('XDG_CONFIG_HOME',
|
||||||
|
os.path.expanduser('~/.config')),
|
||||||
|
'west', 'config')]
|
||||||
|
|
||||||
|
elif platform.system() == 'Darwin': # Mac OS
|
||||||
|
# This was seen on a local machine ($(prefix) = /usr/local)
|
||||||
|
files = ['/usr/local/etc/westconfig']
|
||||||
|
elif platform.system() == 'Windows':
|
||||||
|
# Seen on a local machine
|
||||||
|
files = [os.path.expandvars('%PROGRAMDATA%\\west\\config')]
|
||||||
|
|
||||||
|
files.append(os.path.expanduser('~/.westconfig'))
|
||||||
|
|
||||||
|
# Repository-specific
|
||||||
|
|
||||||
|
files.append(os.path.join(west_dir(), 'config'))
|
||||||
|
|
||||||
|
#
|
||||||
|
# Parse all existing configuration files
|
||||||
|
#
|
||||||
|
|
||||||
|
config.read(files, encoding='utf-8')
|
||||||
|
|
||||||
|
|
||||||
|
def use_colors():
|
||||||
|
# Convenience function for reading the color.ui setting
|
||||||
|
return config.getboolean('color', 'ui', fallback=True)
|
|
@ -6,6 +6,8 @@
|
||||||
|
|
||||||
Provides common methods for logging messages to display to the user.'''
|
Provides common methods for logging messages to display to the user.'''
|
||||||
|
|
||||||
|
from west import config
|
||||||
|
|
||||||
import colorama
|
import colorama
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
@ -47,6 +49,10 @@ def inf(*args, colorize=False):
|
||||||
colorize (default: False):
|
colorize (default: False):
|
||||||
If True, the message is printed in bright green if stdout is a terminal.
|
If True, the message is printed in bright green if stdout is a terminal.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
if not config.use_colors():
|
||||||
|
colorize = False
|
||||||
|
|
||||||
# This approach colorizes any sep= and end= text too, as expected.
|
# This approach colorizes any sep= and end= text too, as expected.
|
||||||
#
|
#
|
||||||
# colorama automatically strips the ANSI escapes when stdout isn't a
|
# colorama automatically strips the ANSI escapes when stdout isn't a
|
||||||
|
@ -57,29 +63,43 @@ def inf(*args, colorize=False):
|
||||||
print(*args)
|
print(*args)
|
||||||
|
|
||||||
if colorize:
|
if colorize:
|
||||||
# The final flush=True avoids issues with unrelated output from
|
_reset_colors(sys.stdout)
|
||||||
# commands (usually Git) becoming green, due to the final attribute
|
|
||||||
# reset ANSI escape getting line-buffered.
|
|
||||||
print(colorama.Style.RESET_ALL, end='', flush=True)
|
|
||||||
|
|
||||||
|
|
||||||
def wrn(*args):
|
def wrn(*args):
|
||||||
'''Print a warning.'''
|
'''Print a warning.'''
|
||||||
print(colorama.Fore.LIGHTRED_EX + 'WARNING: ', end='', file=sys.stderr)
|
|
||||||
|
if config.use_colors():
|
||||||
|
print(colorama.Fore.LIGHTRED_EX, end='', file=sys.stderr)
|
||||||
|
|
||||||
|
print('WARNING: ', end='', file=sys.stderr)
|
||||||
print(*args, file=sys.stderr)
|
print(*args, file=sys.stderr)
|
||||||
print(colorama.Style.RESET_ALL, end='', file=sys.stderr, flush=True)
|
|
||||||
|
if config.use_colors():
|
||||||
|
_reset_colors(sys.stderr)
|
||||||
|
|
||||||
|
|
||||||
def err(*args, fatal=False):
|
def err(*args, fatal=False):
|
||||||
'''Print an error.'''
|
'''Print an error.'''
|
||||||
print(colorama.Fore.LIGHTRED_EX
|
|
||||||
+ ('FATAL ERROR: ' if fatal else 'ERROR: '),
|
if config.use_colors():
|
||||||
end='', file=sys.stderr)
|
print(colorama.Fore.LIGHTRED_EX, end='', file=sys.stderr)
|
||||||
|
|
||||||
|
print('FATAL ERROR: ' if fatal else 'ERROR: ', end='', file=sys.stderr)
|
||||||
print(*args, file=sys.stderr)
|
print(*args, file=sys.stderr)
|
||||||
print(colorama.Style.RESET_ALL, end='', file=sys.stderr, flush=True)
|
|
||||||
|
if config.use_colors():
|
||||||
|
_reset_colors(sys.stderr)
|
||||||
|
|
||||||
|
|
||||||
def die(*args, exit_code=1):
|
def die(*args, exit_code=1):
|
||||||
'''Print a fatal error, and abort with the given exit code.'''
|
'''Print a fatal error, and abort with the given exit code.'''
|
||||||
err(*args, fatal=True)
|
err(*args, fatal=True)
|
||||||
sys.exit(exit_code)
|
sys.exit(exit_code)
|
||||||
|
|
||||||
|
|
||||||
|
def _reset_colors(file):
|
||||||
|
# The flush=True avoids issues with unrelated output from commands (usually
|
||||||
|
# Git) becoming colorized, due to the final attribute reset ANSI escape
|
||||||
|
# getting line-buffered
|
||||||
|
print(colorama.Style.RESET_ALL, end='', file=file, flush=True)
|
||||||
|
|
|
@ -15,17 +15,19 @@ import os
|
||||||
import sys
|
import sys
|
||||||
from subprocess import CalledProcessError, check_output, DEVNULL
|
from subprocess import CalledProcessError, check_output, DEVNULL
|
||||||
|
|
||||||
import log
|
from west import log
|
||||||
from commands import CommandContextError
|
from west import config
|
||||||
from commands.build import Build
|
from west.commands import CommandContextError
|
||||||
from commands.flash import Flash
|
from west.commands.build import Build
|
||||||
from commands.debug import Debug, DebugServer, Attach
|
from west.commands.flash import Flash
|
||||||
from commands.project import ListProjects, Fetch, Pull, Rebase, Branch, \
|
from west.commands.debug import Debug, DebugServer, Attach
|
||||||
Checkout, Diff, Status, Update, ForAll, \
|
from west.commands.project import List, Clone, Fetch, Pull, Rebase, Branch, \
|
||||||
WestUpdated
|
Checkout, Diff, Status, Update, ForAll, \
|
||||||
from util import quote_sh_list, in_multirepo_install
|
WestUpdated
|
||||||
|
from west.manifest import Manifest
|
||||||
|
from west.util import quote_sh_list, in_multirepo_install, west_dir
|
||||||
|
|
||||||
IN_MULTIREPO_INSTALL = in_multirepo_install(__file__)
|
IN_MULTIREPO_INSTALL = in_multirepo_install(os.path.dirname(__file__))
|
||||||
|
|
||||||
BUILD_FLASH_COMMANDS = [
|
BUILD_FLASH_COMMANDS = [
|
||||||
Build(),
|
Build(),
|
||||||
|
@ -36,7 +38,8 @@ BUILD_FLASH_COMMANDS = [
|
||||||
]
|
]
|
||||||
|
|
||||||
PROJECT_COMMANDS = [
|
PROJECT_COMMANDS = [
|
||||||
ListProjects(),
|
List(),
|
||||||
|
Clone(),
|
||||||
Fetch(),
|
Fetch(),
|
||||||
Pull(),
|
Pull(),
|
||||||
Rebase(),
|
Rebase(),
|
||||||
|
@ -65,16 +68,55 @@ def command_handler(command, known_args, unknown_args):
|
||||||
command.run(known_args, unknown_args)
|
command.run(known_args, unknown_args)
|
||||||
|
|
||||||
|
|
||||||
def validate_context(args, unknown):
|
def set_zephyr_base(args):
|
||||||
'''Validate the run-time context expected by west.'''
|
'''Ensure ZEPHYR_BASE is set, emitting warnings if that's not
|
||||||
|
possible, or if the user is pointing it somewhere different than
|
||||||
|
what the manifest expects.'''
|
||||||
|
zb_env = os.environ.get('ZEPHYR_BASE')
|
||||||
|
|
||||||
if args.zephyr_base:
|
if args.zephyr_base:
|
||||||
os.environ['ZEPHYR_BASE'] = args.zephyr_base
|
# The command line --zephyr-base takes precedence over
|
||||||
|
# everything else.
|
||||||
|
zb = os.path.abspath(args.zephyr_base)
|
||||||
|
zb_origin = 'command line'
|
||||||
else:
|
else:
|
||||||
if 'ZEPHYR_BASE' not in os.environ:
|
# If the user doesn't specify it concretely, use the project
|
||||||
log.wrn('--zephyr-base missing and no ZEPHYR_BASE',
|
# with path 'zephyr' if that exists, or the ZEPHYR_BASE value
|
||||||
'in the environment')
|
# in the calling environment.
|
||||||
|
#
|
||||||
|
# At some point, we need a more flexible way to set environment
|
||||||
|
# variables based on manifest contents, but this is good enough
|
||||||
|
# to get started with and to ask for wider testing.
|
||||||
|
manifest = Manifest.from_file()
|
||||||
|
for project in manifest.projects:
|
||||||
|
if project.path == 'zephyr':
|
||||||
|
zb = project.abspath
|
||||||
|
zb_origin = 'manifest file {}'.format(manifest.path)
|
||||||
|
break
|
||||||
else:
|
else:
|
||||||
args.zephyr_base = os.environ['ZEPHYR_BASE']
|
if zb_env is None:
|
||||||
|
log.wrn('no --zephyr-base given, ZEPHYR_BASE is unset,',
|
||||||
|
'and no manifest project has path "zephyr"')
|
||||||
|
zb = None
|
||||||
|
zb_origin = None
|
||||||
|
else:
|
||||||
|
zb = zb_env
|
||||||
|
zb_origin = 'environment'
|
||||||
|
|
||||||
|
if zb_env and os.path.abspath(zb) != os.path.abspath(zb_env):
|
||||||
|
# The environment ZEPHYR_BASE takes precedence over either the
|
||||||
|
# command line or the manifest, but in normal multi-repo
|
||||||
|
# operation we shouldn't expect to need to set ZEPHYR_BASE to
|
||||||
|
# point to some random place. In practice, this is probably
|
||||||
|
# happening because zephyr-env.sh/cmd was run in some other
|
||||||
|
# zephyr installation, and the user forgot about that.
|
||||||
|
log.wrn('ZEPHYR_BASE={}'.format(zb_env),
|
||||||
|
'in the calling environment, but has been set to',
|
||||||
|
zb, 'instead by the', zb_origin)
|
||||||
|
|
||||||
|
os.environ['ZEPHYR_BASE'] = zb
|
||||||
|
|
||||||
|
log.dbg('ZEPHYR_BASE={} (origin: {})'.format(zb, zb_origin))
|
||||||
|
|
||||||
|
|
||||||
def print_version_info():
|
def print_version_info():
|
||||||
|
@ -96,7 +138,7 @@ def print_version_info():
|
||||||
stderr=DEVNULL,
|
stderr=DEVNULL,
|
||||||
cwd=os.path.dirname(__file__))
|
cwd=os.path.dirname(__file__))
|
||||||
west_version = desc.decode(sys.getdefaultencoding()).strip()
|
west_version = desc.decode(sys.getdefaultencoding()).strip()
|
||||||
except CalledProcessError as e:
|
except CalledProcessError:
|
||||||
west_version = 'unknown'
|
west_version = 'unknown'
|
||||||
else:
|
else:
|
||||||
west_version = 'N/A, monorepo installation'
|
west_version = 'N/A, monorepo installation'
|
||||||
|
@ -112,14 +154,21 @@ def parse_args(argv):
|
||||||
west_parser = argparse.ArgumentParser(
|
west_parser = argparse.ArgumentParser(
|
||||||
prog='west', description='The Zephyr RTOS meta-tool.',
|
prog='west', description='The Zephyr RTOS meta-tool.',
|
||||||
epilog='Run "west <command> -h" for help on each command.')
|
epilog='Run "west <command> -h" for help on each command.')
|
||||||
|
|
||||||
|
# Remember to update scripts/west-completion.bash if you add or remove
|
||||||
|
# flags
|
||||||
|
|
||||||
west_parser.add_argument('-z', '--zephyr-base', default=None,
|
west_parser.add_argument('-z', '--zephyr-base', default=None,
|
||||||
help='''Path to the Zephyr base directory. If not
|
help='''Override the Zephyr base directory. The
|
||||||
given, ZEPHYR_BASE must be defined in the
|
default is the manifest project with path
|
||||||
environment, and will be used instead.''')
|
"zephyr".''')
|
||||||
|
|
||||||
west_parser.add_argument('-v', '--verbose', default=0, action='count',
|
west_parser.add_argument('-v', '--verbose', default=0, action='count',
|
||||||
help='''Display verbose output. May be given
|
help='''Display verbose output. May be given
|
||||||
multiple times to increase verbosity.''')
|
multiple times to increase verbosity.''')
|
||||||
|
|
||||||
west_parser.add_argument('-V', '--version', action='store_true')
|
west_parser.add_argument('-V', '--version', action='store_true')
|
||||||
|
|
||||||
subparser_gen = west_parser.add_subparsers(title='commands',
|
subparser_gen = west_parser.add_subparsers(title='commands',
|
||||||
dest='command')
|
dest='command')
|
||||||
|
|
||||||
|
@ -138,16 +187,16 @@ def parse_args(argv):
|
||||||
# work properly.
|
# work properly.
|
||||||
log.set_verbosity(args.verbose)
|
log.set_verbosity(args.verbose)
|
||||||
|
|
||||||
try:
|
if IN_MULTIREPO_INSTALL:
|
||||||
validate_context(args, unknown)
|
set_zephyr_base(args)
|
||||||
except InvalidWestContext as iwc:
|
|
||||||
log.err(*iwc.args, fatal=True)
|
|
||||||
west_parser.print_usage(file=sys.stderr)
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
if 'handler' not in args:
|
if 'handler' not in args:
|
||||||
log.err('you must specify a command', fatal=True)
|
if IN_MULTIREPO_INSTALL:
|
||||||
west_parser.print_usage(file=sys.stderr)
|
log.err('west installation found (in {}), but no command given'.
|
||||||
|
format(west_dir()))
|
||||||
|
else:
|
||||||
|
log.err('no west command given')
|
||||||
|
west_parser.print_help(file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
return args, unknown
|
return args, unknown
|
||||||
|
@ -162,6 +211,10 @@ def main(argv=None):
|
||||||
argv = sys.argv[1:]
|
argv = sys.argv[1:]
|
||||||
args, unknown = parse_args(argv)
|
args, unknown = parse_args(argv)
|
||||||
|
|
||||||
|
if IN_MULTIREPO_INSTALL:
|
||||||
|
# Read the configuration files
|
||||||
|
config.read_config()
|
||||||
|
|
||||||
for_stack_trace = 'run as "west -v ... {} ..." for a stack trace'.format(
|
for_stack_trace = 'run as "west -v ... {} ..." for a stack trace'.format(
|
||||||
args.command)
|
args.command)
|
||||||
try:
|
try:
|
||||||
|
@ -169,7 +222,7 @@ def main(argv=None):
|
||||||
except WestUpdated:
|
except WestUpdated:
|
||||||
# West has been automatically updated. Restart ourselves to run the
|
# West has been automatically updated. Restart ourselves to run the
|
||||||
# latest version, with the same arguments that we were given.
|
# latest version, with the same arguments that we were given.
|
||||||
os.execv(sys.executable, [sys.executable] + sys.argv)
|
os.execv(sys.executable, [sys.executable] + argv)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
except CalledProcessError as cpe:
|
except CalledProcessError as cpe:
|
||||||
|
@ -182,12 +235,7 @@ def main(argv=None):
|
||||||
except CommandContextError as cce:
|
except CommandContextError as cce:
|
||||||
log.die('command', args.command, 'cannot be run in this context:',
|
log.die('command', args.command, 'cannot be run in this context:',
|
||||||
*cce.args)
|
*cce.args)
|
||||||
except Exception as exc:
|
|
||||||
log.err(*exc.args, fatal=True)
|
|
||||||
if args.verbose:
|
|
||||||
raise
|
|
||||||
else:
|
|
||||||
log.inf(for_stack_trace)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|
132
scripts/meta/west/manifest-schema.yml
Normal file
132
scripts/meta/west/manifest-schema.yml
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
## A pykwalify schema for basic validation of the structure of a
|
||||||
|
## manifest YAML file. (Full validation would require additional work,
|
||||||
|
## e.g. to validate that remote URLs obey the URL format specified in
|
||||||
|
## rfc1738.)
|
||||||
|
##
|
||||||
|
## This schema has similar semantics to the repo XML format:
|
||||||
|
##
|
||||||
|
## https://gerrit.googlesource.com/git-repo/+/master/docs/manifest-format.txt
|
||||||
|
##
|
||||||
|
## However, the features don't map 1:1.
|
||||||
|
|
||||||
|
# The top-level manifest is a map. There may be multiple sections in the
|
||||||
|
# manifest file. Each section can be validated by their own schema.
|
||||||
|
# This schema validates the 'manifest' section.
|
||||||
|
type: map
|
||||||
|
mapping:
|
||||||
|
# The "defaults" key specifies some default values used in the
|
||||||
|
# rest of the manifest.
|
||||||
|
#
|
||||||
|
# The value is a map with the following keys:
|
||||||
|
#
|
||||||
|
# - remote: if given, this is the default remote in each project
|
||||||
|
# - revision: if given, this is the default revision to check
|
||||||
|
# out of each project
|
||||||
|
#
|
||||||
|
# See below for more information about remotes and projects.
|
||||||
|
#
|
||||||
|
# Examples:
|
||||||
|
#
|
||||||
|
# default:
|
||||||
|
# remote: zephyrproject-rtos
|
||||||
|
# revision: master
|
||||||
|
defaults:
|
||||||
|
required: false
|
||||||
|
type: map
|
||||||
|
mapping:
|
||||||
|
remote:
|
||||||
|
required: false
|
||||||
|
type: str
|
||||||
|
revision:
|
||||||
|
required: false
|
||||||
|
type: str
|
||||||
|
|
||||||
|
# The "remotes" key specifies a sequence of remotes, each of
|
||||||
|
# which has a name and a fetch URL.
|
||||||
|
#
|
||||||
|
# These work like repo remotes, in that they specify a URL
|
||||||
|
# prefix which remote-specific Git repositories hang off of.
|
||||||
|
# (This saves typing and makes it easier to move things around
|
||||||
|
# when most repositories are on the same server or GitHub
|
||||||
|
# organization.)
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
#
|
||||||
|
# remotes:
|
||||||
|
# - name: zephyrproject-rtos
|
||||||
|
# url-base: https://github.com/zephyrproject-rtos
|
||||||
|
# - name: developer-fork
|
||||||
|
# url-base: https://github.com/a-developer
|
||||||
|
remotes:
|
||||||
|
required: true
|
||||||
|
type: seq
|
||||||
|
sequence:
|
||||||
|
- type: map
|
||||||
|
mapping:
|
||||||
|
name:
|
||||||
|
required: true
|
||||||
|
type: str
|
||||||
|
url-base:
|
||||||
|
required: true
|
||||||
|
type: str
|
||||||
|
|
||||||
|
# The "projects" key specifies a sequence of "projects",
|
||||||
|
# i.e. Git repositories. These work like repo projects, in that
|
||||||
|
# each project has a name, a remote, and optional additional
|
||||||
|
# metadata.
|
||||||
|
#
|
||||||
|
# Each project is a map with the following keys:
|
||||||
|
#
|
||||||
|
# - name: Mandatory, the name of the git repository. The clone
|
||||||
|
# URL is formed by remote url-base + '/' + name. The name cannot
|
||||||
|
# be one of the reserved values "west" and "manifest".
|
||||||
|
# - remote: Optional, the name of the remote to pull it from.
|
||||||
|
# If the remote is missing, the remote'key in the top-level
|
||||||
|
# defaults key is used instead. If both are missing, it's an error.
|
||||||
|
# - revision: Optional, the name of the revision to check out.
|
||||||
|
# If not given, the value from the default element will be used.
|
||||||
|
# If both are missing, then the default is 'master'.
|
||||||
|
# - path: Where to clone the repository locally. If missing,
|
||||||
|
# it's cloned at top level in a directory given by its name.
|
||||||
|
# - clone-depth: if given, it is a number which creates a shallow
|
||||||
|
# history in the cloned repository limited to the given number
|
||||||
|
# of commits.
|
||||||
|
#
|
||||||
|
# Example, using default and non-default remotes:
|
||||||
|
#
|
||||||
|
# projects:
|
||||||
|
# # Uses default remote (zephyrproject-rtos), so clone URL is:
|
||||||
|
# # https://github.com/zephyrproject-rtos/zephyr
|
||||||
|
# - name: zephyr
|
||||||
|
# # Manually specified remote; clone URL is:
|
||||||
|
# # https://github.com/a-developer/west
|
||||||
|
# - name: west
|
||||||
|
# remote: developer-fork
|
||||||
|
# # Manually specified remote, clone URL is:
|
||||||
|
# # https://github.com/zephyrproject-rtos/some-vendor-hal
|
||||||
|
# # Local clone path (relative to installation root) is:
|
||||||
|
# # ext/hal/some-vendor
|
||||||
|
# - name: some-vendor-hal
|
||||||
|
# remote: zephyrproject-rtos
|
||||||
|
# path: ext/hal/some-vendor
|
||||||
|
projects:
|
||||||
|
required: true
|
||||||
|
type: seq
|
||||||
|
sequence:
|
||||||
|
- type: map
|
||||||
|
mapping:
|
||||||
|
name:
|
||||||
|
required: true
|
||||||
|
type: str
|
||||||
|
remote:
|
||||||
|
required: false
|
||||||
|
type: str
|
||||||
|
revision:
|
||||||
|
required: false
|
||||||
|
type: text # SHAs could be only numbers
|
||||||
|
path:
|
||||||
|
required: false
|
||||||
|
type: str
|
||||||
|
clone-depth:
|
||||||
|
required: false
|
||||||
|
type: int
|
400
scripts/meta/west/manifest.py
Normal file
400
scripts/meta/west/manifest.py
Normal file
|
@ -0,0 +1,400 @@
|
||||||
|
# Copyright (c) 2018, Nordic Semiconductor ASA
|
||||||
|
# Copyright 2018, Foundries.io Ltd
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
'''Parser and abstract data types for west manifests.
|
||||||
|
|
||||||
|
The main class is Manifest. The recommended method for creating a
|
||||||
|
Manifest instance is via its from_file() or from_data() helper
|
||||||
|
methods.
|
||||||
|
|
||||||
|
There are additionally Defaults, Remote, and Project types defined,
|
||||||
|
which represent the values by the same names in a west
|
||||||
|
manifest. (I.e. "Remote" represents one of the elements in the
|
||||||
|
"remote" sequence in the manifest, and so on.) Some Default values,
|
||||||
|
such as the default project revision, may be supplied by this module
|
||||||
|
if they are not present in the manifest data.'''
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
import pykwalify.core
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
from west import util, log
|
||||||
|
|
||||||
|
# Todo: take from _bootstrap?
|
||||||
|
# Default west repository URL.
|
||||||
|
WEST_URL_DEFAULT = 'https://github.com/zephyrproject-rtos/west'
|
||||||
|
# Default revision to check out of the west repository.
|
||||||
|
WEST_REV_DEFAULT = 'master'
|
||||||
|
|
||||||
|
META_NAMES = ['west', 'manifest']
|
||||||
|
'''Names of the special "meta-projects", which are reserved and cannot
|
||||||
|
be used to name a project in the manifest file.'''
|
||||||
|
|
||||||
|
MANIFEST_SECTIONS = ['manifest', 'west']
|
||||||
|
'''Sections in the manifest file'''
|
||||||
|
|
||||||
|
|
||||||
|
def default_path():
|
||||||
|
'''Return the path to the default manifest in the west directory.
|
||||||
|
|
||||||
|
Raises WestNotFound if called from outside of a west working directory.'''
|
||||||
|
return os.path.join(util.west_dir(), 'manifest', 'default.yml')
|
||||||
|
|
||||||
|
|
||||||
|
class Manifest:
|
||||||
|
'''Represents the contents of a West manifest file.
|
||||||
|
|
||||||
|
The most convenient way to construct an instance is using the
|
||||||
|
from_file and from_data helper methods.'''
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_file(source_file=None, sections=MANIFEST_SECTIONS):
|
||||||
|
'''Create and return a new Manifest object given a source YAML file.
|
||||||
|
|
||||||
|
:param source_file: Path to a YAML file containing the manifest.
|
||||||
|
:param sections: Only parse specified sections from YAML file,
|
||||||
|
default: all sections are parsed.
|
||||||
|
|
||||||
|
If source_file is None, the value returned by default_path()
|
||||||
|
is used.
|
||||||
|
|
||||||
|
Raises MalformedManifest in case of validation errors.'''
|
||||||
|
if source_file is None:
|
||||||
|
source_file = default_path()
|
||||||
|
return Manifest(source_file=source_file, sections=sections)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_data(source_data, sections=MANIFEST_SECTIONS):
|
||||||
|
'''Create and return a new Manifest object given parsed YAML data.
|
||||||
|
|
||||||
|
:param source_data: Parsed YAML data as a Python object.
|
||||||
|
:param sections: Only parse specified sections from YAML data,
|
||||||
|
default: all sections are parsed.
|
||||||
|
|
||||||
|
Raises MalformedManifest in case of validation errors.'''
|
||||||
|
return Manifest(source_data=source_data, sections=sections)
|
||||||
|
|
||||||
|
def __init__(self, source_file=None, source_data=None,
|
||||||
|
sections=MANIFEST_SECTIONS):
|
||||||
|
'''Create a new Manifest object.
|
||||||
|
|
||||||
|
:param source_file: Path to a YAML file containing the manifest.
|
||||||
|
:param source_data: Parsed YAML data as a Python object.
|
||||||
|
:param sections: Only parse specified sections from YAML file,
|
||||||
|
default: all sections are parsed.
|
||||||
|
|
||||||
|
Normally, it is more convenient to use the `from_file` and
|
||||||
|
`from_data` convenience factories than calling the constructor
|
||||||
|
directly.
|
||||||
|
|
||||||
|
Exactly one of the source_file and source_data parameters must
|
||||||
|
be given.
|
||||||
|
|
||||||
|
Raises MalformedManifest in case of validation errors.'''
|
||||||
|
if source_file and source_data:
|
||||||
|
raise ValueError('both source_file and source_data were given')
|
||||||
|
|
||||||
|
if source_file:
|
||||||
|
with open(source_file, 'r') as f:
|
||||||
|
self._data = yaml.safe_load(f.read())
|
||||||
|
path = source_file
|
||||||
|
else:
|
||||||
|
self._data = source_data
|
||||||
|
path = None
|
||||||
|
|
||||||
|
self.path = path
|
||||||
|
'''Path to the file containing the manifest, or None if created
|
||||||
|
from data rather than the file system.'''
|
||||||
|
|
||||||
|
if not self._data:
|
||||||
|
self._malformed('manifest contains no data')
|
||||||
|
|
||||||
|
if 'manifest' not in self._data:
|
||||||
|
self._malformed('manifest contains no manifest element')
|
||||||
|
|
||||||
|
for key in self._data:
|
||||||
|
if key in sections:
|
||||||
|
try:
|
||||||
|
pykwalify.core.Core(
|
||||||
|
source_data=self._data[key],
|
||||||
|
schema_files=[_SCHEMA_PATH[key]]
|
||||||
|
).validate()
|
||||||
|
except pykwalify.errors.SchemaError as e:
|
||||||
|
self._malformed(e, key)
|
||||||
|
|
||||||
|
self.defaults = None
|
||||||
|
'''west.manifest.Defaults object representing default values
|
||||||
|
in the manifest, either as specified by the user or west itself.'''
|
||||||
|
|
||||||
|
self.remotes = None
|
||||||
|
'''Sequence of west.manifest.Remote objects representing manifest
|
||||||
|
remotes.'''
|
||||||
|
|
||||||
|
self.projects = None
|
||||||
|
'''Sequence of west.manifest.Project objects representing manifest
|
||||||
|
projects.
|
||||||
|
|
||||||
|
Each element's values are fully initialized; there is no need
|
||||||
|
to consult the defaults field to supply missing values.'''
|
||||||
|
|
||||||
|
self.west_project = None
|
||||||
|
'''west.manifest.SpecialProject object representing the west meta
|
||||||
|
project.'''
|
||||||
|
|
||||||
|
# Set up the public attributes documented above, as well as
|
||||||
|
# any internal attributes needed to implement the public API.
|
||||||
|
self._load(self._data, sections)
|
||||||
|
|
||||||
|
def get_remote(self, name):
|
||||||
|
'''Get a manifest Remote, given its name.'''
|
||||||
|
return self._remotes_dict[name]
|
||||||
|
|
||||||
|
def _malformed(self, complaint, section='manifest'):
|
||||||
|
context = (' file {} '.format(self.path) if self.path
|
||||||
|
else ' data:\n{}\n'.format(self._data))
|
||||||
|
raise MalformedManifest('Malformed manifest{}(schema: {}):\n{}'
|
||||||
|
.format(context, _SCHEMA_PATH[section],
|
||||||
|
complaint))
|
||||||
|
|
||||||
|
def _load(self, data, sections):
|
||||||
|
# Initialize this instance's fields from values given in the
|
||||||
|
# manifest data, which must be validated according to the schema.
|
||||||
|
if 'west' in sections:
|
||||||
|
west = data.get('west', {})
|
||||||
|
|
||||||
|
url = west.get('url') or WEST_URL_DEFAULT
|
||||||
|
revision = west.get('revision') or WEST_REV_DEFAULT
|
||||||
|
|
||||||
|
self.west_project = SpecialProject('west',
|
||||||
|
url=url,
|
||||||
|
revision=revision,
|
||||||
|
path=os.path.join('west',
|
||||||
|
'west'))
|
||||||
|
|
||||||
|
# Next is the manifest section
|
||||||
|
if 'manifest' not in sections:
|
||||||
|
return
|
||||||
|
|
||||||
|
projects = []
|
||||||
|
project_abspaths = set()
|
||||||
|
|
||||||
|
manifest = data.get('manifest')
|
||||||
|
|
||||||
|
# Map from each remote's name onto that remote's data in the manifest.
|
||||||
|
remotes = tuple(Remote(r['name'], r['url-base']) for r in
|
||||||
|
manifest['remotes'])
|
||||||
|
remotes_dict = {r.name: r for r in remotes}
|
||||||
|
|
||||||
|
# Get any defaults out of the manifest.
|
||||||
|
#
|
||||||
|
# md = manifest defaults (dictionary with values parsed from
|
||||||
|
# the manifest)
|
||||||
|
md = manifest.get('defaults', dict())
|
||||||
|
mdrem = md.get('remote')
|
||||||
|
if mdrem:
|
||||||
|
# The default remote name, if provided, must refer to a
|
||||||
|
# well-defined remote.
|
||||||
|
if mdrem not in remotes_dict:
|
||||||
|
self._malformed('default remote {} is not defined'.
|
||||||
|
format(mdrem))
|
||||||
|
default_remote = remotes_dict[mdrem]
|
||||||
|
default_remote_name = mdrem
|
||||||
|
else:
|
||||||
|
default_remote = None
|
||||||
|
default_remote_name = None
|
||||||
|
defaults = Defaults(remote=default_remote, revision=md.get('revision'))
|
||||||
|
|
||||||
|
# mp = manifest project (dictionary with values parsed from
|
||||||
|
# the manifest)
|
||||||
|
for mp in manifest['projects']:
|
||||||
|
# Validate the project name.
|
||||||
|
name = mp['name']
|
||||||
|
if name in META_NAMES:
|
||||||
|
self._malformed('the name "{}" is reserved and cannot '.
|
||||||
|
format(name) +
|
||||||
|
'be used to name a manifest project')
|
||||||
|
|
||||||
|
# Validate the project remote.
|
||||||
|
remote_name = mp.get('remote', default_remote_name)
|
||||||
|
if remote_name is None:
|
||||||
|
self._malformed('project {} does not specify a remote'.
|
||||||
|
format(name))
|
||||||
|
if remote_name not in remotes_dict:
|
||||||
|
self._malformed('project {} remote {} is not defined'.
|
||||||
|
format(name, remote_name))
|
||||||
|
project = Project(name,
|
||||||
|
remotes_dict[remote_name],
|
||||||
|
defaults,
|
||||||
|
path=mp.get('path'),
|
||||||
|
clone_depth=mp.get('clone-depth'),
|
||||||
|
revision=mp.get('revision'))
|
||||||
|
|
||||||
|
# Two projects cannot have the same path. We use absolute
|
||||||
|
# paths to check for collisions to ensure paths are
|
||||||
|
# normalized (e.g. for case-insensitive file systems or
|
||||||
|
# in cases like on Windows where / or \ may serve as a
|
||||||
|
# path component separator).
|
||||||
|
if project.abspath in project_abspaths:
|
||||||
|
self._malformed('project {} path {} is already in use'.
|
||||||
|
format(project.name, project.path))
|
||||||
|
|
||||||
|
project_abspaths.add(project.abspath)
|
||||||
|
projects.append(project)
|
||||||
|
|
||||||
|
self.defaults = defaults
|
||||||
|
self.remotes = remotes
|
||||||
|
self._remotes_dict = remotes_dict
|
||||||
|
self.projects = tuple(projects)
|
||||||
|
|
||||||
|
|
||||||
|
class MalformedManifest(Exception):
|
||||||
|
'''Exception indicating that west manifest parsing failed due to a
|
||||||
|
malformed value.'''
|
||||||
|
|
||||||
|
|
||||||
|
# Definitions for Manifest attribute types.
|
||||||
|
|
||||||
|
class Defaults:
|
||||||
|
'''Represents default values in a manifest, either specified by the
|
||||||
|
user or by west itself.
|
||||||
|
|
||||||
|
Defaults are neither comparable nor hashable.'''
|
||||||
|
|
||||||
|
__slots__ = 'remote revision'.split()
|
||||||
|
|
||||||
|
def __init__(self, remote=None, revision=None):
|
||||||
|
'''Initialize a defaults value from manifest data.
|
||||||
|
|
||||||
|
:param remote: Remote instance corresponding to the default remote,
|
||||||
|
or None (an actual Remote object, not the name of
|
||||||
|
a remote as a string).
|
||||||
|
:param revision: Default Git revision; 'master' if not given.'''
|
||||||
|
if remote is not None:
|
||||||
|
_wrn_if_not_remote(remote)
|
||||||
|
if revision is None:
|
||||||
|
revision = 'master'
|
||||||
|
|
||||||
|
self.remote = remote
|
||||||
|
self.revision = revision
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
return NotImplemented
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return 'Defaults(remote={}, revision={})'.format(repr(self.remote),
|
||||||
|
repr(self.revision))
|
||||||
|
|
||||||
|
|
||||||
|
class Remote:
|
||||||
|
'''Represents a remote defined in a west manifest.
|
||||||
|
|
||||||
|
Remotes may be compared for equality, but are not hashable.'''
|
||||||
|
|
||||||
|
__slots__ = 'name url_base'.split()
|
||||||
|
|
||||||
|
def __init__(self, name, url_base):
|
||||||
|
'''Initialize a remote from manifest data.
|
||||||
|
|
||||||
|
:param name: remote's name
|
||||||
|
:param url_base: remote's URL base.'''
|
||||||
|
if url_base.endswith('/'):
|
||||||
|
log.wrn('Remote', name, 'URL base', url_base,
|
||||||
|
'ends with a slash ("/"); these are automatically',
|
||||||
|
'appended by West')
|
||||||
|
|
||||||
|
self.name = name
|
||||||
|
self.url_base = url_base
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
return self.name == other.name and self.url_base == other.url_base
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return 'Remote(name={}, url_base={})'.format(repr(self.name),
|
||||||
|
repr(self.url_base))
|
||||||
|
|
||||||
|
|
||||||
|
class Project:
|
||||||
|
'''Represents a project defined in a west manifest.
|
||||||
|
|
||||||
|
Projects are neither comparable nor hashable.'''
|
||||||
|
|
||||||
|
__slots__ = 'name remote url path abspath clone_depth revision'.split()
|
||||||
|
|
||||||
|
def __init__(self, name, remote, defaults, path=None, clone_depth=None,
|
||||||
|
revision=None):
|
||||||
|
'''Specify a Project by name, Remote, and optional information.
|
||||||
|
|
||||||
|
:param name: Project's user-defined name in the manifest.
|
||||||
|
:param remote: Remote instance corresponding to this Project as
|
||||||
|
specified in the manifest. This is used to build
|
||||||
|
the project's URL, and is also stored as an attribute.
|
||||||
|
:param defaults: If the revision parameter is not given, the project's
|
||||||
|
revision is set to defaults.revision.
|
||||||
|
:param path: Relative path to the project in the west
|
||||||
|
installation, if present in the manifest. If not given,
|
||||||
|
the project's ``name`` is used.
|
||||||
|
:param clone_depth: Nonnegative integer clone depth if present in
|
||||||
|
the manifest.
|
||||||
|
:param revision: Project revision as given in the manifest, if present.
|
||||||
|
If not given, defaults.revision is used instead.
|
||||||
|
'''
|
||||||
|
_wrn_if_not_remote(remote)
|
||||||
|
|
||||||
|
self.name = name
|
||||||
|
self.remote = remote
|
||||||
|
self.url = remote.url_base + '/' + name
|
||||||
|
self.path = os.path.normpath(path or name)
|
||||||
|
self.abspath = os.path.realpath(os.path.join(util.west_topdir(),
|
||||||
|
self.path))
|
||||||
|
self.clone_depth = clone_depth
|
||||||
|
self.revision = revision or defaults.revision
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
return NotImplemented
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
reprs = [repr(x) for x in
|
||||||
|
(self.name, self.remote, self.url, self.path,
|
||||||
|
self.abspath, self.clone_depth, self.revision)]
|
||||||
|
return ('Project(name={}, remote={}, url={}, path={}, abspath={}, '
|
||||||
|
'clone_depth={}, revision={})').format(*reprs)
|
||||||
|
|
||||||
|
|
||||||
|
class SpecialProject(Project):
|
||||||
|
'''Represents a special project, e.g. the west or manifest project.
|
||||||
|
|
||||||
|
Projects are neither comparable nor hashable.'''
|
||||||
|
|
||||||
|
def __init__(self, name, path=None, revision=None, url=None):
|
||||||
|
'''Specify a Special Project by name, and url, and optional information.
|
||||||
|
|
||||||
|
:param name: Special Project's user-defined name in the manifest
|
||||||
|
:param path: Relative path to the project in the west
|
||||||
|
installation, if present in the manifest. If None,
|
||||||
|
the project's ``name`` is used.
|
||||||
|
:param revision: Project revision as given in the manifest, if present.
|
||||||
|
:param url: Complete URL for special project.
|
||||||
|
'''
|
||||||
|
self.name = name
|
||||||
|
self.url = url
|
||||||
|
self.path = path or name
|
||||||
|
self.abspath = os.path.realpath(os.path.join(util.west_topdir(),
|
||||||
|
self.path))
|
||||||
|
self.revision = revision
|
||||||
|
self.remote = None
|
||||||
|
self.clone_depth = None
|
||||||
|
|
||||||
|
|
||||||
|
def _wrn_if_not_remote(remote):
|
||||||
|
if not isinstance(remote, Remote):
|
||||||
|
log.wrn('Remote', remote, 'is not a Remote instance')
|
||||||
|
|
||||||
|
|
||||||
|
_SCHEMA_PATH = {'manifest': os.path.join(os.path.dirname(__file__),
|
||||||
|
"manifest-schema.yml"),
|
||||||
|
'west': os.path.join(os.path.dirname(__file__),
|
||||||
|
"_bootstrap",
|
||||||
|
"west-schema.yml")}
|
|
@ -2,7 +2,7 @@
|
||||||
#
|
#
|
||||||
# SPDX-License-Identifier: Apache-2.0
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
from runners.core import ZephyrBinaryRunner
|
from west.runners.core import ZephyrBinaryRunner
|
||||||
|
|
||||||
# We import these here to ensure the ZephyrBinaryRunner subclasses are
|
# We import these here to ensure the ZephyrBinaryRunner subclasses are
|
||||||
# defined; otherwise, ZephyrBinaryRunner.create_for_shell_script()
|
# defined; otherwise, ZephyrBinaryRunner.create_for_shell_script()
|
||||||
|
@ -10,19 +10,20 @@ from runners.core import ZephyrBinaryRunner
|
||||||
|
|
||||||
# Explicitly silence the unused import warning.
|
# Explicitly silence the unused import warning.
|
||||||
# flake8: noqa: F401
|
# flake8: noqa: F401
|
||||||
from runners import arc
|
from west.runners import arc
|
||||||
from runners import bossac
|
from west.runners import bossac
|
||||||
from runners import dfu
|
from west.runners import dfu
|
||||||
from runners import esp32
|
from west.runners import esp32
|
||||||
from runners import jlink
|
from west.runners import jlink
|
||||||
from runners import nios2
|
from west.runners import nios2
|
||||||
from runners import nrfjprog
|
from west.runners import nrfjprog
|
||||||
from runners import nsim
|
from west.runners import nsim
|
||||||
from runners import openocd
|
from west.runners import openocd
|
||||||
from runners import pyocd
|
from west.runners import pyocd
|
||||||
from runners import qemu
|
from west.runners import qemu
|
||||||
from runners import xtensa
|
from west.runners import xtensa
|
||||||
from runners import intel_s1000
|
from west.runners import intel_s1000
|
||||||
|
from west.runners import blackmagicprobe
|
||||||
|
|
||||||
def get_runner_cls(runner):
|
def get_runner_cls(runner):
|
||||||
'''Get a runner's class object, given its name.'''
|
'''Get a runner's class object, given its name.'''
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
from os import path
|
from os import path
|
||||||
|
|
||||||
from runners.core import ZephyrBinaryRunner
|
from west.runners.core import ZephyrBinaryRunner
|
||||||
|
|
||||||
DEFAULT_ARC_TCL_PORT = 6333
|
DEFAULT_ARC_TCL_PORT = 6333
|
||||||
DEFAULT_ARC_TELNET_PORT = 4444
|
DEFAULT_ARC_TELNET_PORT = 4444
|
||||||
|
@ -93,7 +93,7 @@ class EmStarterKitBinaryRunner(ZephyrBinaryRunner):
|
||||||
['-ex', 'target remote :{}'.format(self.gdb_port),
|
['-ex', 'target remote :{}'.format(self.gdb_port),
|
||||||
'-ex', 'load'] +
|
'-ex', 'load'] +
|
||||||
continue_arg +
|
continue_arg +
|
||||||
[self.cfg.kernel_elf])
|
[self.cfg.elf_file])
|
||||||
|
|
||||||
self.run_server_and_client(server_cmd, gdb_cmd)
|
self.run_server_and_client(server_cmd, gdb_cmd)
|
||||||
|
|
||||||
|
|
96
scripts/meta/west/runners/blackmagicprobe.py
Normal file
96
scripts/meta/west/runners/blackmagicprobe.py
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
# Copyright (c) 2018 Roman Tataurov <diytronic@yandex.ru>
|
||||||
|
# Modified 2018 Tavish Naruka <tavishnaruka@gmail.com>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
'''Runner for flashing with Black Magic Probe.'''
|
||||||
|
# https://github.com/blacksphere/blackmagic/wiki
|
||||||
|
|
||||||
|
from west.runners.core import ZephyrBinaryRunner, RunnerCaps
|
||||||
|
|
||||||
|
|
||||||
|
class BlackMagicProbeRunner(ZephyrBinaryRunner):
|
||||||
|
'''Runner front-end for Black Magic probe.'''
|
||||||
|
|
||||||
|
def __init__(self, cfg, gdb_serial):
|
||||||
|
super(BlackMagicProbeRunner, self).__init__(cfg)
|
||||||
|
self.gdb = [cfg.gdb] if cfg.gdb else None
|
||||||
|
self.elf_file = cfg.elf_file
|
||||||
|
self.gdb_serial = gdb_serial
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def name(cls):
|
||||||
|
return 'blackmagicprobe'
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def capabilities(cls):
|
||||||
|
return RunnerCaps(commands={'flash', 'debug', 'attach'})
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def create(cls, cfg, args):
|
||||||
|
return BlackMagicProbeRunner(cfg, args.gdb_serial)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def do_add_parser(cls, parser):
|
||||||
|
parser.add_argument('--gdb-serial', default='/dev/ttyACM0',
|
||||||
|
help='GDB serial port')
|
||||||
|
|
||||||
|
def bmp_flash(self, command, **kwargs):
|
||||||
|
if self.gdb is None:
|
||||||
|
raise ValueError('Cannot flash; gdb is missing')
|
||||||
|
if self.elf_file is None:
|
||||||
|
raise ValueError('Cannot debug; elf file is missing')
|
||||||
|
command = (self.gdb +
|
||||||
|
['-ex', "set confirm off",
|
||||||
|
'-ex', "target extended-remote {}".format(self.gdb_serial),
|
||||||
|
'-ex', "monitor swdp_scan",
|
||||||
|
'-ex', "attach 1",
|
||||||
|
'-ex', "load {}".format(self.elf_file),
|
||||||
|
'-ex', "kill",
|
||||||
|
'-ex', "quit",
|
||||||
|
'-silent'])
|
||||||
|
self.check_call(command)
|
||||||
|
|
||||||
|
def bmp_attach(self, command, **kwargs):
|
||||||
|
if self.gdb is None:
|
||||||
|
raise ValueError('Cannot attach; gdb is missing')
|
||||||
|
if self.elf_file is None:
|
||||||
|
command = (self.gdb +
|
||||||
|
['-ex', "set confirm off",
|
||||||
|
'-ex', "target extended-remote {}".format(
|
||||||
|
self.gdb_serial),
|
||||||
|
'-ex', "monitor swdp_scan",
|
||||||
|
'-ex', "attach 1"])
|
||||||
|
else:
|
||||||
|
command = (self.gdb +
|
||||||
|
['-ex', "set confirm off",
|
||||||
|
'-ex', "target extended-remote {}".format(
|
||||||
|
self.gdb_serial),
|
||||||
|
'-ex', "monitor swdp_scan",
|
||||||
|
'-ex', "attach 1",
|
||||||
|
'-ex', "file {}".format(self.elf_file)])
|
||||||
|
self.check_call(command)
|
||||||
|
|
||||||
|
def bmp_debug(self, command, **kwargs):
|
||||||
|
if self.gdb is None:
|
||||||
|
raise ValueError('Cannot debug; gdb is missing')
|
||||||
|
if self.elf_file is None:
|
||||||
|
raise ValueError('Cannot debug; elf file is missing')
|
||||||
|
command = (self.gdb +
|
||||||
|
['-ex', "set confirm off",
|
||||||
|
'-ex', "target extended-remote {}".format(self.gdb_serial),
|
||||||
|
'-ex', "monitor swdp_scan",
|
||||||
|
'-ex', "attach 1",
|
||||||
|
'-ex', "file {}".format(self.elf_file),
|
||||||
|
'-ex', "load {}".format(self.elf_file)])
|
||||||
|
self.check_call(command)
|
||||||
|
|
||||||
|
def do_run(self, command, **kwargs):
|
||||||
|
|
||||||
|
if command == 'flash':
|
||||||
|
self.bmp_flash(command, **kwargs)
|
||||||
|
elif command == 'debug':
|
||||||
|
self.bmp_debug(command, **kwargs)
|
||||||
|
elif command == 'attach':
|
||||||
|
self.bmp_attach(command, **kwargs)
|
||||||
|
else:
|
||||||
|
self.bmp_flash(command, **kwargs)
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
import platform
|
import platform
|
||||||
|
|
||||||
from runners.core import ZephyrBinaryRunner, RunnerCaps
|
from west.runners.core import ZephyrBinaryRunner, RunnerCaps
|
||||||
|
|
||||||
DEFAULT_BOSSAC_PORT = '/dev/ttyACM0'
|
DEFAULT_BOSSAC_PORT = '/dev/ttyACM0'
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ class BossacBinaryRunner(ZephyrBinaryRunner):
|
||||||
'ospeed', '1200', 'cs8', '-cstopb', 'ignpar', 'eol', '255',
|
'ospeed', '1200', 'cs8', '-cstopb', 'ignpar', 'eol', '255',
|
||||||
'eof', '255']
|
'eof', '255']
|
||||||
cmd_flash = [self.bossac, '-p', self.port, '-R', '-e', '-w', '-v',
|
cmd_flash = [self.bossac, '-p', self.port, '-R', '-e', '-w', '-v',
|
||||||
'-b', self.cfg.kernel_bin]
|
'-b', self.cfg.bin_file]
|
||||||
|
|
||||||
self.check_call(cmd_stty)
|
self.check_call(cmd_stty)
|
||||||
self.check_call(cmd_flash)
|
self.check_call(cmd_flash)
|
||||||
|
|
|
@ -18,8 +18,8 @@ import platform
|
||||||
import signal
|
import signal
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
import log
|
from west import log
|
||||||
from util import quote_sh_list
|
from west.util import quote_sh_list
|
||||||
|
|
||||||
# Turn on to enable just printing the commands that would be run,
|
# Turn on to enable just printing the commands that would be run,
|
||||||
# without actually running them. This can break runners that are expecting
|
# without actually running them. This can break runners that are expecting
|
||||||
|
@ -191,8 +191,8 @@ class RunnerConfig:
|
||||||
This class's __slots__ contains exactly the configuration variables.
|
This class's __slots__ contains exactly the configuration variables.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
__slots__ = ['build_dir', 'board_dir', 'kernel_elf', 'kernel_hex',
|
__slots__ = ['build_dir', 'board_dir', 'elf_file', 'hex_file',
|
||||||
'kernel_bin', 'gdb', 'openocd', 'openocd_search']
|
'bin_file', 'gdb', 'openocd', 'openocd_search']
|
||||||
|
|
||||||
# TODO: revisit whether we can get rid of some of these. Having
|
# TODO: revisit whether we can get rid of some of these. Having
|
||||||
# tool-specific configuration options here is a layering
|
# tool-specific configuration options here is a layering
|
||||||
|
@ -200,7 +200,7 @@ class RunnerConfig:
|
||||||
# store the locations of tools (like gdb and openocd) that are
|
# store the locations of tools (like gdb and openocd) that are
|
||||||
# needed by multiple ZephyrBinaryRunner subclasses.
|
# needed by multiple ZephyrBinaryRunner subclasses.
|
||||||
def __init__(self, build_dir, board_dir,
|
def __init__(self, build_dir, board_dir,
|
||||||
kernel_elf, kernel_hex, kernel_bin,
|
elf_file, hex_file, bin_file,
|
||||||
gdb=None, openocd=None, openocd_search=None):
|
gdb=None, openocd=None, openocd_search=None):
|
||||||
self.build_dir = build_dir
|
self.build_dir = build_dir
|
||||||
'''Zephyr application build directory'''
|
'''Zephyr application build directory'''
|
||||||
|
@ -208,14 +208,14 @@ class RunnerConfig:
|
||||||
self.board_dir = board_dir
|
self.board_dir = board_dir
|
||||||
'''Zephyr board directory'''
|
'''Zephyr board directory'''
|
||||||
|
|
||||||
self.kernel_elf = kernel_elf
|
self.elf_file = elf_file
|
||||||
'''Path to kernel binary in .elf format'''
|
'''Path to the elf file that the runner should operate on'''
|
||||||
|
|
||||||
self.kernel_hex = kernel_hex
|
self.hex_file = hex_file
|
||||||
'''Path to kernel binary in .hex format'''
|
'''Path to the hex file that the runner should operate on'''
|
||||||
|
|
||||||
self.kernel_bin = kernel_bin
|
self.bin_file = bin_file
|
||||||
'''Path to kernel binary in .bin format'''
|
'''Path to the bin file that the runner should operate on'''
|
||||||
|
|
||||||
self.gdb = gdb
|
self.gdb = gdb
|
||||||
''''Path to GDB compatible with the target, may be None.'''
|
''''Path to GDB compatible with the target, may be None.'''
|
||||||
|
|
|
@ -5,12 +5,12 @@
|
||||||
'''Runner for flashing with dfu-util.'''
|
'''Runner for flashing with dfu-util.'''
|
||||||
|
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
import os
|
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
|
||||||
import log
|
from west import log
|
||||||
from runners.core import ZephyrBinaryRunner, RunnerCaps, BuildConfiguration
|
from west.runners.core import ZephyrBinaryRunner, RunnerCaps, \
|
||||||
|
BuildConfiguration
|
||||||
|
|
||||||
|
|
||||||
DfuSeConfig = namedtuple('DfuSeConfig', ['address', 'options'])
|
DfuSeConfig = namedtuple('DfuSeConfig', ['address', 'options'])
|
||||||
|
@ -54,7 +54,7 @@ class DfuUtilBinaryRunner(ZephyrBinaryRunner):
|
||||||
|
|
||||||
# Optional:
|
# Optional:
|
||||||
parser.add_argument("--img",
|
parser.add_argument("--img",
|
||||||
help="binary to flash, default is --kernel-bin")
|
help="binary to flash, default is --bin-file")
|
||||||
parser.add_argument("--dfuse", default=False, action='store_true',
|
parser.add_argument("--dfuse", default=False, action='store_true',
|
||||||
help='''set if target is a DfuSe device;
|
help='''set if target is a DfuSe device;
|
||||||
implies --dt-flash.''')
|
implies --dt-flash.''')
|
||||||
|
@ -69,7 +69,7 @@ class DfuUtilBinaryRunner(ZephyrBinaryRunner):
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(cls, cfg, args):
|
def create(cls, cfg, args):
|
||||||
if args.img is None:
|
if args.img is None:
|
||||||
args.img = cfg.kernel_bin
|
args.img = cfg.bin_file
|
||||||
|
|
||||||
if args.dfuse:
|
if args.dfuse:
|
||||||
args.dt_flash = True # --dfuse implies --dt-flash.
|
args.dt_flash = True # --dfuse implies --dt-flash.
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
|
|
||||||
from os import path
|
from os import path
|
||||||
|
|
||||||
import log
|
from west import log
|
||||||
from runners.core import ZephyrBinaryRunner, RunnerCaps
|
from west.runners.core import ZephyrBinaryRunner, RunnerCaps
|
||||||
|
|
||||||
|
|
||||||
class Esp32BinaryRunner(ZephyrBinaryRunner):
|
class Esp32BinaryRunner(ZephyrBinaryRunner):
|
||||||
|
@ -17,7 +17,7 @@ class Esp32BinaryRunner(ZephyrBinaryRunner):
|
||||||
flash_freq='40m', flash_mode='dio', espidf='espidf',
|
flash_freq='40m', flash_mode='dio', espidf='espidf',
|
||||||
bootloader_bin=None, partition_table_bin=None):
|
bootloader_bin=None, partition_table_bin=None):
|
||||||
super(Esp32BinaryRunner, self).__init__(cfg)
|
super(Esp32BinaryRunner, self).__init__(cfg)
|
||||||
self.elf = cfg.kernel_elf
|
self.elf = cfg.elf_file
|
||||||
self.device = device
|
self.device = device
|
||||||
self.baud = baud
|
self.baud = baud
|
||||||
self.flash_size = flash_size
|
self.flash_size = flash_size
|
||||||
|
|
|
@ -6,10 +6,9 @@
|
||||||
'''Runner for debugging and flashing Intel S1000 CRB'''
|
'''Runner for debugging and flashing Intel S1000 CRB'''
|
||||||
from os import path
|
from os import path
|
||||||
import time
|
import time
|
||||||
import subprocess
|
import signal
|
||||||
|
from west import log
|
||||||
import log
|
from west.runners.core import ZephyrBinaryRunner
|
||||||
from runners.core import ZephyrBinaryRunner
|
|
||||||
|
|
||||||
DEFAULT_XT_GDB_PORT = 20000
|
DEFAULT_XT_GDB_PORT = 20000
|
||||||
|
|
||||||
|
@ -22,7 +21,7 @@ class IntelS1000BinaryRunner(ZephyrBinaryRunner):
|
||||||
gdb_port=DEFAULT_XT_GDB_PORT):
|
gdb_port=DEFAULT_XT_GDB_PORT):
|
||||||
super(IntelS1000BinaryRunner, self).__init__(cfg)
|
super(IntelS1000BinaryRunner, self).__init__(cfg)
|
||||||
self.board_dir = cfg.board_dir
|
self.board_dir = cfg.board_dir
|
||||||
self.elf_name = cfg.kernel_elf
|
self.elf_name = cfg.elf_file
|
||||||
self.gdb_cmd = cfg.gdb
|
self.gdb_cmd = cfg.gdb
|
||||||
self.xt_ocd_dir = xt_ocd_dir
|
self.xt_ocd_dir = xt_ocd_dir
|
||||||
self.ocd_topology = ocd_topology
|
self.ocd_topology = ocd_topology
|
||||||
|
@ -73,8 +72,7 @@ class IntelS1000BinaryRunner(ZephyrBinaryRunner):
|
||||||
elif command == 'debugserver':
|
elif command == 'debugserver':
|
||||||
self.debugserver(**kwargs)
|
self.debugserver(**kwargs)
|
||||||
else:
|
else:
|
||||||
self.debugserver(**kwargs)
|
self.do_debug(**kwargs)
|
||||||
self.do_debug()
|
|
||||||
|
|
||||||
def flash(self, **kwargs):
|
def flash(self, **kwargs):
|
||||||
topology_file = kwargs['ocd-topology']
|
topology_file = kwargs['ocd-topology']
|
||||||
|
@ -87,36 +85,66 @@ class IntelS1000BinaryRunner(ZephyrBinaryRunner):
|
||||||
'-I', jtag_instr_file]
|
'-I', jtag_instr_file]
|
||||||
|
|
||||||
# Start the server
|
# Start the server
|
||||||
# Note that XTOCD always fails the first time. It has to be
|
# Note that XTOCD takes a few seconds to execute and always fails the
|
||||||
# relaunched the second time to work.
|
# first time. It has to be relaunched the second time to work.
|
||||||
self.call(server_cmd)
|
|
||||||
server_proc = self.popen_ignore_int(server_cmd)
|
server_proc = self.popen_ignore_int(server_cmd)
|
||||||
time.sleep(3)
|
time.sleep(6)
|
||||||
|
server_proc.terminate()
|
||||||
|
server_proc = self.popen_ignore_int(server_cmd)
|
||||||
|
time.sleep(6)
|
||||||
|
|
||||||
# Start the client, and wait for it to finish flashing the file.
|
# Start the client
|
||||||
gdb_cmd = [self.gdb_cmd, '-x', gdb_flash_file]
|
gdb_cmd = [self.gdb_cmd, '-x', gdb_flash_file]
|
||||||
client_proc = self.popen_ignore_int(gdb_cmd)
|
client_proc = self.popen_ignore_int(gdb_cmd)
|
||||||
client_proc.wait()
|
|
||||||
|
# Wait for 3 seconds (waiting for XTGDB to finish loading the image)
|
||||||
|
time.sleep(3)
|
||||||
|
|
||||||
# At this point, the ELF image is loaded and the program is in
|
# At this point, the ELF image is loaded and the program is in
|
||||||
# execution. Now we can quit the server (xt-ocd); it is not
|
# execution. Now we can quit the client (xt-gdb) and the server
|
||||||
# needed anymore. The loaded program (ELF) will continue to
|
# (xt-ocd) as they are not needed anymore. The loaded program
|
||||||
# run.
|
# (ELF) will continue to run though.
|
||||||
|
client_proc.terminate()
|
||||||
server_proc.terminate()
|
server_proc.terminate()
|
||||||
|
|
||||||
def do_debug(self):
|
def do_debug(self, **kwargs):
|
||||||
if self.elf_name is None:
|
if self.elf_name is None:
|
||||||
raise ValueError('Cannot debug; elf is missing')
|
raise ValueError('Cannot debug; elf is missing')
|
||||||
if self.gdb_cmd is None:
|
if self.gdb_cmd is None:
|
||||||
raise ValueError('Cannot debug; no gdb specified')
|
raise ValueError('Cannot debug; no gdb specified')
|
||||||
|
|
||||||
|
topology_file = kwargs['ocd-topology']
|
||||||
|
jtag_instr_file = kwargs['ocd-jtag-instr']
|
||||||
|
|
||||||
|
self.print_gdbserver_message(self.gdb_port)
|
||||||
|
server_cmd = [self.xt_ocd_dir,
|
||||||
|
'-c', topology_file,
|
||||||
|
'-I', jtag_instr_file]
|
||||||
|
|
||||||
|
# Start the server
|
||||||
|
# Note that XTOCD takes a few seconds to execute and always fails the
|
||||||
|
# first time. It has to be relaunched the second time to work.
|
||||||
|
server_proc = self.popen_ignore_int(server_cmd)
|
||||||
|
time.sleep(6)
|
||||||
|
server_proc.terminate()
|
||||||
|
server_proc = self.popen_ignore_int(server_cmd)
|
||||||
|
time.sleep(6)
|
||||||
|
|
||||||
gdb_cmd = [self.gdb_cmd,
|
gdb_cmd = [self.gdb_cmd,
|
||||||
'-ex', 'target remote :{}'.format(self.gdb_port),
|
'-ex', 'target remote :{}'.format(self.gdb_port),
|
||||||
self.elf_name]
|
self.elf_name]
|
||||||
gdb_proc = self.popen_ignore_int(gdb_cmd)
|
|
||||||
retcode = gdb_proc.wait()
|
# Start the client
|
||||||
if retcode:
|
# The below statement will consume the "^C" keypress ensuring
|
||||||
raise subprocess.CalledProcessError((retcode, gdb_cmd))
|
# the python main application doesn't exit. This is important
|
||||||
|
# since ^C in gdb means a "halt" operation.
|
||||||
|
previous = signal.signal(signal.SIGINT, signal.SIG_IGN)
|
||||||
|
try:
|
||||||
|
self.check_call(gdb_cmd)
|
||||||
|
finally:
|
||||||
|
signal.signal(signal.SIGINT, previous)
|
||||||
|
server_proc.terminate()
|
||||||
|
server_proc.wait()
|
||||||
|
|
||||||
def print_gdbserver_message(self, gdb_port):
|
def print_gdbserver_message(self, gdb_port):
|
||||||
log.inf('Intel S1000 GDB server running on port {}'.format(gdb_port))
|
log.inf('Intel S1000 GDB server running on port {}'.format(gdb_port))
|
||||||
|
@ -130,7 +158,9 @@ class IntelS1000BinaryRunner(ZephyrBinaryRunner):
|
||||||
'-c', topology_file,
|
'-c', topology_file,
|
||||||
'-I', jtag_instr_file]
|
'-I', jtag_instr_file]
|
||||||
|
|
||||||
# Note that XTOCD always fails the first time. It has to be
|
# Note that XTOCD takes a few seconds to execute and always fails the
|
||||||
# relaunched the second time to work.
|
# first time. It has to be relaunched the second time to work.
|
||||||
self.call(server_cmd)
|
server_proc = self.popen_ignore_int(server_cmd)
|
||||||
|
time.sleep(6)
|
||||||
|
server_proc.terminate()
|
||||||
self.check_call(server_cmd)
|
self.check_call(server_cmd)
|
||||||
|
|
|
@ -6,10 +6,13 @@
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import tempfile
|
import tempfile
|
||||||
|
import sys
|
||||||
|
|
||||||
import log
|
from west import log
|
||||||
from runners.core import ZephyrBinaryRunner, RunnerCaps, BuildConfiguration
|
from west.runners.core import ZephyrBinaryRunner, RunnerCaps, \
|
||||||
|
BuildConfiguration
|
||||||
|
|
||||||
|
DEFAULT_JLINK_EXE = 'JLink.exe' if sys.platform == 'win32' else 'JLinkExe'
|
||||||
DEFAULT_JLINK_GDB_PORT = 2331
|
DEFAULT_JLINK_GDB_PORT = 2331
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,14 +20,14 @@ class JLinkBinaryRunner(ZephyrBinaryRunner):
|
||||||
'''Runner front-end for the J-Link GDB server.'''
|
'''Runner front-end for the J-Link GDB server.'''
|
||||||
|
|
||||||
def __init__(self, cfg, device,
|
def __init__(self, cfg, device,
|
||||||
commander='JLinkExe',
|
commander=DEFAULT_JLINK_EXE,
|
||||||
flash_addr=0x0, erase=True,
|
flash_addr=0x0, erase=True,
|
||||||
iface='swd', speed='auto',
|
iface='swd', speed='auto',
|
||||||
gdbserver='JLinkGDBServer', gdb_port=DEFAULT_JLINK_GDB_PORT,
|
gdbserver='JLinkGDBServer', gdb_port=DEFAULT_JLINK_GDB_PORT,
|
||||||
tui=False):
|
tui=False):
|
||||||
super(JLinkBinaryRunner, self).__init__(cfg)
|
super(JLinkBinaryRunner, self).__init__(cfg)
|
||||||
self.bin_name = cfg.kernel_bin
|
self.bin_name = cfg.bin_file
|
||||||
self.elf_name = cfg.kernel_elf
|
self.elf_name = cfg.elf_file
|
||||||
self.gdb_cmd = [cfg.gdb] if cfg.gdb else None
|
self.gdb_cmd = [cfg.gdb] if cfg.gdb else None
|
||||||
self.device = device
|
self.device = device
|
||||||
self.commander = commander
|
self.commander = commander
|
||||||
|
@ -62,7 +65,7 @@ class JLinkBinaryRunner(ZephyrBinaryRunner):
|
||||||
parser.add_argument('--gdb-port', default=DEFAULT_JLINK_GDB_PORT,
|
parser.add_argument('--gdb-port', default=DEFAULT_JLINK_GDB_PORT,
|
||||||
help='pyocd gdb port, defaults to {}'.format(
|
help='pyocd gdb port, defaults to {}'.format(
|
||||||
DEFAULT_JLINK_GDB_PORT))
|
DEFAULT_JLINK_GDB_PORT))
|
||||||
parser.add_argument('--commander', default='JLinkExe',
|
parser.add_argument('--commander', default=DEFAULT_JLINK_EXE,
|
||||||
help='J-Link Commander, default is JLinkExe')
|
help='J-Link Commander, default is JLinkExe')
|
||||||
parser.add_argument('--erase', default=False, action='store_true',
|
parser.add_argument('--erase', default=False, action='store_true',
|
||||||
help='if given, mass erase flash before loading')
|
help='if given, mass erase flash before loading')
|
||||||
|
@ -127,6 +130,9 @@ class JLinkBinaryRunner(ZephyrBinaryRunner):
|
||||||
lines.append('g') # Start the CPU
|
lines.append('g') # Start the CPU
|
||||||
lines.append('q') # Close the connection and quit
|
lines.append('q') # Close the connection and quit
|
||||||
|
|
||||||
|
log.dbg('JLink commander script:')
|
||||||
|
log.dbg('\n'.join(lines))
|
||||||
|
|
||||||
# Don't use NamedTemporaryFile: the resulting file can't be
|
# Don't use NamedTemporaryFile: the resulting file can't be
|
||||||
# opened again on Windows.
|
# opened again on Windows.
|
||||||
with tempfile.TemporaryDirectory(suffix='jlink') as d:
|
with tempfile.TemporaryDirectory(suffix='jlink') as d:
|
||||||
|
|
|
@ -4,8 +4,8 @@
|
||||||
|
|
||||||
'''Runner for NIOS II, based on quartus-flash.py and GDB.'''
|
'''Runner for NIOS II, based on quartus-flash.py and GDB.'''
|
||||||
|
|
||||||
import log
|
from west import log
|
||||||
from runners.core import ZephyrBinaryRunner, NetworkPortHelper
|
from west.runners.core import ZephyrBinaryRunner, NetworkPortHelper
|
||||||
|
|
||||||
|
|
||||||
class Nios2BinaryRunner(ZephyrBinaryRunner):
|
class Nios2BinaryRunner(ZephyrBinaryRunner):
|
||||||
|
@ -19,8 +19,8 @@ class Nios2BinaryRunner(ZephyrBinaryRunner):
|
||||||
|
|
||||||
def __init__(self, cfg, quartus_py=None, cpu_sof=None, tui=False):
|
def __init__(self, cfg, quartus_py=None, cpu_sof=None, tui=False):
|
||||||
super(Nios2BinaryRunner, self).__init__(cfg)
|
super(Nios2BinaryRunner, self).__init__(cfg)
|
||||||
self.hex_name = cfg.kernel_hex
|
self.hex_name = cfg.hex_file
|
||||||
self.elf_name = cfg.kernel_elf
|
self.elf_name = cfg.elf_file
|
||||||
self.cpu_sof = cpu_sof
|
self.cpu_sof = cpu_sof
|
||||||
self.quartus_py = quartus_py
|
self.quartus_py = quartus_py
|
||||||
self.gdb_cmd = [cfg.gdb] if cfg.gdb else None
|
self.gdb_cmd = [cfg.gdb] if cfg.gdb else None
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import log
|
from west import log
|
||||||
from runners.core import ZephyrBinaryRunner, RunnerCaps
|
from west.runners.core import ZephyrBinaryRunner, RunnerCaps
|
||||||
|
|
||||||
|
|
||||||
class NrfJprogBinaryRunner(ZephyrBinaryRunner):
|
class NrfJprogBinaryRunner(ZephyrBinaryRunner):
|
||||||
|
@ -15,7 +15,7 @@ class NrfJprogBinaryRunner(ZephyrBinaryRunner):
|
||||||
|
|
||||||
def __init__(self, cfg, family, softreset, snr, erase=False):
|
def __init__(self, cfg, family, softreset, snr, erase=False):
|
||||||
super(NrfJprogBinaryRunner, self).__init__(cfg)
|
super(NrfJprogBinaryRunner, self).__init__(cfg)
|
||||||
self.hex_ = cfg.kernel_hex
|
self.hex_ = cfg.hex_file
|
||||||
self.family = family
|
self.family = family
|
||||||
self.softreset = softreset
|
self.softreset = softreset
|
||||||
self.snr = snr
|
self.snr = snr
|
||||||
|
@ -44,19 +44,21 @@ class NrfJprogBinaryRunner(ZephyrBinaryRunner):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(cls, cfg, args):
|
def create(cls, cfg, args):
|
||||||
return NrfJprogBinaryRunner(cfg, args.nrf_family, args.softreset, args.snr,
|
return NrfJprogBinaryRunner(cfg, args.nrf_family, args.softreset,
|
||||||
erase=args.erase)
|
args.snr, erase=args.erase)
|
||||||
|
|
||||||
def get_board_snr_from_user(self):
|
def get_board_snr_from_user(self):
|
||||||
snrs = self.check_output(['nrfjprog', '--ids'])
|
snrs = self.check_output(['nrfjprog', '--ids'])
|
||||||
snrs = snrs.decode(sys.getdefaultencoding()).strip().splitlines()
|
snrs = snrs.decode(sys.getdefaultencoding()).strip().splitlines()
|
||||||
|
|
||||||
if len(snrs) == 0:
|
if len(snrs) == 0:
|
||||||
raise RuntimeError('"nrfjprog --ids" did not find a board; Is the board connected?')
|
raise RuntimeError('"nrfjprog --ids" did not find a board; '
|
||||||
|
'is the board connected?')
|
||||||
elif len(snrs) == 1:
|
elif len(snrs) == 1:
|
||||||
board_snr = snrs[0]
|
board_snr = snrs[0]
|
||||||
if board_snr == '0':
|
if board_snr == '0':
|
||||||
raise RuntimeError('"nrfjprog --ids" returned 0; is a debugger already connected?')
|
raise RuntimeError('"nrfjprog --ids" returned 0; '
|
||||||
|
'is a debugger already connected?')
|
||||||
return board_snr
|
return board_snr
|
||||||
|
|
||||||
log.dbg("Refusing the temptation to guess a board",
|
log.dbg("Refusing the temptation to guess a board",
|
||||||
|
@ -99,14 +101,14 @@ class NrfJprogBinaryRunner(ZephyrBinaryRunner):
|
||||||
'-f', self.family,
|
'-f', self.family,
|
||||||
'--snr', board_snr],
|
'--snr', board_snr],
|
||||||
program_cmd
|
program_cmd
|
||||||
])
|
])
|
||||||
else:
|
else:
|
||||||
if self.family == 'NRF51':
|
if self.family == 'NRF51':
|
||||||
commands.append(program_cmd + ['--sectorerase'])
|
commands.append(program_cmd + ['--sectorerase'])
|
||||||
else:
|
else:
|
||||||
commands.append(program_cmd + ['--sectoranduicrerase'])
|
commands.append(program_cmd + ['--sectoranduicrerase'])
|
||||||
|
|
||||||
if self.family == 'NRF52' and self.softreset == False:
|
if self.family == 'NRF52' and not self.softreset:
|
||||||
commands.extend([
|
commands.extend([
|
||||||
# Enable pin reset
|
# Enable pin reset
|
||||||
['nrfjprog', '--pinresetenable', '-f', self.family,
|
['nrfjprog', '--pinresetenable', '-f', self.family,
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
from os import path
|
from os import path
|
||||||
|
|
||||||
from runners.core import ZephyrBinaryRunner
|
from west.runners.core import ZephyrBinaryRunner
|
||||||
|
|
||||||
DEFAULT_ARC_GDB_PORT = 3333
|
DEFAULT_ARC_GDB_PORT = 3333
|
||||||
DEFAULT_PROPS_FILE = 'nsim.props'
|
DEFAULT_PROPS_FILE = 'nsim.props'
|
||||||
|
@ -56,7 +56,7 @@ class NsimBinaryRunner(ZephyrBinaryRunner):
|
||||||
|
|
||||||
def do_run(self, command, **kwargs):
|
def do_run(self, command, **kwargs):
|
||||||
kwargs['nsim-cfg'] = path.join(self.cfg.board_dir, 'support',
|
kwargs['nsim-cfg'] = path.join(self.cfg.board_dir, 'support',
|
||||||
self.props)
|
self.props)
|
||||||
|
|
||||||
if command == 'flash':
|
if command == 'flash':
|
||||||
self.do_flash(**kwargs)
|
self.do_flash(**kwargs)
|
||||||
|
@ -68,20 +68,19 @@ class NsimBinaryRunner(ZephyrBinaryRunner):
|
||||||
def do_flash(self, **kwargs):
|
def do_flash(self, **kwargs):
|
||||||
config = kwargs['nsim-cfg']
|
config = kwargs['nsim-cfg']
|
||||||
|
|
||||||
cmd = (self.nsim_cmd +
|
cmd = (self.nsim_cmd + ['-propsfile', config, self.cfg.elf_file])
|
||||||
['-propsfile', config, self.cfg.kernel_elf])
|
|
||||||
self.check_call(cmd)
|
self.check_call(cmd)
|
||||||
|
|
||||||
def do_debug(self, **kwargs):
|
def do_debug(self, **kwargs):
|
||||||
config = kwargs['nsim-cfg']
|
config = kwargs['nsim-cfg']
|
||||||
|
|
||||||
server_cmd = (self.nsim_cmd +
|
server_cmd = (self.nsim_cmd + ['-gdb',
|
||||||
['-gdb', '-port={}'.format(self.gdb_port),
|
'-port={}'.format(self.gdb_port),
|
||||||
'-propsfile', config])
|
'-propsfile', config])
|
||||||
|
|
||||||
gdb_cmd = (self.gdb_cmd +
|
gdb_cmd = (self.gdb_cmd +
|
||||||
['-ex', 'target remote :{}'.format(self.gdb_port),
|
['-ex', 'target remote :{}'.format(self.gdb_port),
|
||||||
'-ex', 'load', self.cfg.kernel_elf])
|
'-ex', 'load', self.cfg.elf_file])
|
||||||
|
|
||||||
self.run_server_and_client(server_cmd, gdb_cmd)
|
self.run_server_and_client(server_cmd, gdb_cmd)
|
||||||
|
|
||||||
|
@ -89,7 +88,7 @@ class NsimBinaryRunner(ZephyrBinaryRunner):
|
||||||
config = kwargs['nsim-cfg']
|
config = kwargs['nsim-cfg']
|
||||||
|
|
||||||
cmd = (self.nsim_cmd +
|
cmd = (self.nsim_cmd +
|
||||||
['-gdb', '-port={}'.format(self.gdb_port),
|
['-gdb', '-port={}'.format(self.gdb_port),
|
||||||
'-propsfile', config])
|
'-propsfile', config])
|
||||||
|
|
||||||
self.check_call(cmd)
|
self.check_call(cmd)
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
from os import path
|
from os import path
|
||||||
|
|
||||||
from runners.core import ZephyrBinaryRunner
|
from west.runners.core import ZephyrBinaryRunner
|
||||||
|
|
||||||
DEFAULT_OPENOCD_TCL_PORT = 6333
|
DEFAULT_OPENOCD_TCL_PORT = 6333
|
||||||
DEFAULT_OPENOCD_TELNET_PORT = 4444
|
DEFAULT_OPENOCD_TELNET_PORT = 4444
|
||||||
|
@ -30,7 +30,7 @@ class OpenOcdBinaryRunner(ZephyrBinaryRunner):
|
||||||
if cfg.openocd_search is not None:
|
if cfg.openocd_search is not None:
|
||||||
search_args = ['-s', cfg.openocd_search]
|
search_args = ['-s', cfg.openocd_search]
|
||||||
self.openocd_cmd = [cfg.openocd] + search_args
|
self.openocd_cmd = [cfg.openocd] + search_args
|
||||||
self.elf_name = cfg.kernel_elf
|
self.elf_name = cfg.elf_file
|
||||||
self.load_cmd = load_cmd
|
self.load_cmd = load_cmd
|
||||||
self.verify_cmd = verify_cmd
|
self.verify_cmd = verify_cmd
|
||||||
self.pre_cmd = pre_cmd
|
self.pre_cmd = pre_cmd
|
||||||
|
|
|
@ -5,9 +5,10 @@
|
||||||
'''Runner for pyOCD .'''
|
'''Runner for pyOCD .'''
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
from runners.core import ZephyrBinaryRunner, RunnerCaps, BuildConfiguration
|
from west.runners.core import ZephyrBinaryRunner, RunnerCaps, \
|
||||||
import log
|
BuildConfiguration
|
||||||
|
from west import log
|
||||||
|
|
||||||
DEFAULT_PYOCD_GDB_PORT = 3333
|
DEFAULT_PYOCD_GDB_PORT = 3333
|
||||||
|
|
||||||
|
@ -20,7 +21,7 @@ class PyOcdBinaryRunner(ZephyrBinaryRunner):
|
||||||
flashtool_opts=None,
|
flashtool_opts=None,
|
||||||
gdbserver='pyocd-gdbserver',
|
gdbserver='pyocd-gdbserver',
|
||||||
gdb_port=DEFAULT_PYOCD_GDB_PORT, tui=False,
|
gdb_port=DEFAULT_PYOCD_GDB_PORT, tui=False,
|
||||||
board_id=None, daparg=None):
|
board_id=None, daparg=None, frequency=None):
|
||||||
super(PyOcdBinaryRunner, self).__init__(cfg)
|
super(PyOcdBinaryRunner, self).__init__(cfg)
|
||||||
|
|
||||||
self.target_args = ['-t', target]
|
self.target_args = ['-t', target]
|
||||||
|
@ -30,8 +31,9 @@ class PyOcdBinaryRunner(ZephyrBinaryRunner):
|
||||||
self.gdbserver = gdbserver
|
self.gdbserver = gdbserver
|
||||||
self.gdb_port = gdb_port
|
self.gdb_port = gdb_port
|
||||||
self.tui_args = ['-tui'] if tui else []
|
self.tui_args = ['-tui'] if tui else []
|
||||||
self.bin_name = cfg.kernel_bin
|
self.hex_name = cfg.hex_file
|
||||||
self.elf_name = cfg.kernel_elf
|
self.bin_name = cfg.bin_file
|
||||||
|
self.elf_name = cfg.elf_file
|
||||||
|
|
||||||
board_args = []
|
board_args = []
|
||||||
if board_id is not None:
|
if board_id is not None:
|
||||||
|
@ -43,6 +45,11 @@ class PyOcdBinaryRunner(ZephyrBinaryRunner):
|
||||||
daparg_args = ['-da', daparg]
|
daparg_args = ['-da', daparg]
|
||||||
self.daparg_args = daparg_args
|
self.daparg_args = daparg_args
|
||||||
|
|
||||||
|
frequency_args = []
|
||||||
|
if frequency is not None:
|
||||||
|
frequency_args = ['-f', frequency]
|
||||||
|
self.frequency_args = frequency_args
|
||||||
|
|
||||||
self.flashtool_extra = flashtool_opts if flashtool_opts else []
|
self.flashtool_extra = flashtool_opts if flashtool_opts else []
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -66,6 +73,8 @@ class PyOcdBinaryRunner(ZephyrBinaryRunner):
|
||||||
parser.add_argument('--flashtool-opt', default=[], action='append',
|
parser.add_argument('--flashtool-opt', default=[], action='append',
|
||||||
help='''Additional options for pyocd-flashtool,
|
help='''Additional options for pyocd-flashtool,
|
||||||
e.g. -ce to chip erase''')
|
e.g. -ce to chip erase''')
|
||||||
|
parser.add_argument('--frequency',
|
||||||
|
help='SWD clock frequency in Hz')
|
||||||
parser.add_argument('--gdbserver', default='pyocd-gdbserver',
|
parser.add_argument('--gdbserver', default='pyocd-gdbserver',
|
||||||
help='GDB server, default is pyocd-gdbserver')
|
help='GDB server, default is pyocd-gdbserver')
|
||||||
parser.add_argument('--gdb-port', default=DEFAULT_PYOCD_GDB_PORT,
|
parser.add_argument('--gdb-port', default=DEFAULT_PYOCD_GDB_PORT,
|
||||||
|
@ -94,7 +103,8 @@ class PyOcdBinaryRunner(ZephyrBinaryRunner):
|
||||||
cfg, args.target, flashtool=args.flashtool,
|
cfg, args.target, flashtool=args.flashtool,
|
||||||
flash_addr=flash_addr, flashtool_opts=args.flashtool_opt,
|
flash_addr=flash_addr, flashtool_opts=args.flashtool_opt,
|
||||||
gdbserver=args.gdbserver, gdb_port=args.gdb_port, tui=args.tui,
|
gdbserver=args.gdbserver, gdb_port=args.gdb_port, tui=args.tui,
|
||||||
board_id=args.board_id, daparg=args.daparg)
|
board_id=args.board_id, daparg=args.daparg,
|
||||||
|
frequency=args.frequency)
|
||||||
|
|
||||||
def port_args(self):
|
def port_args(self):
|
||||||
return ['-p', str(self.gdb_port)]
|
return ['-p', str(self.gdb_port)]
|
||||||
|
@ -106,16 +116,23 @@ class PyOcdBinaryRunner(ZephyrBinaryRunner):
|
||||||
self.debug_debugserver(command, **kwargs)
|
self.debug_debugserver(command, **kwargs)
|
||||||
|
|
||||||
def flash(self, **kwargs):
|
def flash(self, **kwargs):
|
||||||
if self.bin_name is None:
|
if os.path.isfile(self.hex_name):
|
||||||
raise ValueError('Cannot flash; bin_name is missing')
|
fname = self.hex_name
|
||||||
|
elif os.path.isfile(self.bin_name):
|
||||||
|
fname = self.bin_name
|
||||||
|
else:
|
||||||
|
raise ValueError(
|
||||||
|
'Cannot flash; no hex ({}) or bin ({}) files'.format(
|
||||||
|
self.hex_name, self.bin_name))
|
||||||
|
|
||||||
cmd = ([self.flashtool] +
|
cmd = ([self.flashtool] +
|
||||||
self.flash_addr_args +
|
self.flash_addr_args +
|
||||||
self.daparg_args +
|
self.daparg_args +
|
||||||
self.target_args +
|
self.target_args +
|
||||||
self.board_args +
|
self.board_args +
|
||||||
|
self.frequency_args +
|
||||||
self.flashtool_extra +
|
self.flashtool_extra +
|
||||||
[self.bin_name])
|
[fname])
|
||||||
|
|
||||||
log.inf('Flashing Target Device')
|
log.inf('Flashing Target Device')
|
||||||
self.check_call(cmd)
|
self.check_call(cmd)
|
||||||
|
@ -128,7 +145,8 @@ class PyOcdBinaryRunner(ZephyrBinaryRunner):
|
||||||
self.daparg_args +
|
self.daparg_args +
|
||||||
self.port_args() +
|
self.port_args() +
|
||||||
self.target_args +
|
self.target_args +
|
||||||
self.board_args)
|
self.board_args +
|
||||||
|
self.frequency_args)
|
||||||
|
|
||||||
if command == 'debugserver':
|
if command == 'debugserver':
|
||||||
self.print_gdbserver_message()
|
self.print_gdbserver_message()
|
||||||
|
@ -143,8 +161,9 @@ class PyOcdBinaryRunner(ZephyrBinaryRunner):
|
||||||
[self.elf_name] +
|
[self.elf_name] +
|
||||||
['-ex', 'target remote :{}'.format(self.gdb_port)])
|
['-ex', 'target remote :{}'.format(self.gdb_port)])
|
||||||
if command == 'debug':
|
if command == 'debug':
|
||||||
client_cmd += ['-ex', 'load',
|
client_cmd += ['-ex', 'monitor halt',
|
||||||
'-ex', 'monitor reset halt']
|
'-ex', 'monitor reset',
|
||||||
|
'-ex', 'load']
|
||||||
|
|
||||||
self.print_gdbserver_message()
|
self.print_gdbserver_message()
|
||||||
self.run_server_and_client(server_cmd, client_cmd)
|
self.run_server_and_client(server_cmd, client_cmd)
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
'''Runner stub for QEMU.'''
|
'''Runner stub for QEMU.'''
|
||||||
|
|
||||||
from runners.core import ZephyrBinaryRunner, RunnerCaps
|
from west.runners.core import ZephyrBinaryRunner, RunnerCaps
|
||||||
|
|
||||||
|
|
||||||
class QemuBinaryRunner(ZephyrBinaryRunner):
|
class QemuBinaryRunner(ZephyrBinaryRunner):
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
from os import path
|
from os import path
|
||||||
|
|
||||||
from runners.core import ZephyrBinaryRunner, RunnerCaps
|
from west.runners.core import ZephyrBinaryRunner, RunnerCaps
|
||||||
|
|
||||||
|
|
||||||
class XtensaBinaryRunner(ZephyrBinaryRunner):
|
class XtensaBinaryRunner(ZephyrBinaryRunner):
|
||||||
|
@ -35,6 +35,6 @@ class XtensaBinaryRunner(ZephyrBinaryRunner):
|
||||||
return XtensaBinaryRunner(cfg)
|
return XtensaBinaryRunner(cfg)
|
||||||
|
|
||||||
def do_run(self, command, **kwargs):
|
def do_run(self, command, **kwargs):
|
||||||
gdb_cmd = [self.cfg.gdb, self.cfg.kernel_elf]
|
gdb_cmd = [self.cfg.gdb, self.cfg.elf_file]
|
||||||
|
|
||||||
self.check_call(gdb_cmd)
|
self.check_call(gdb_cmd)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue