242 lines
8.0 KiB
Python
Executable File
242 lines
8.0 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
# Copyright 2018 Open Source Foundries Limited.
|
|
#
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
'''Zephyr RTOS meta-tool (west) main module
|
|
'''
|
|
|
|
|
|
import argparse
|
|
import colorama
|
|
from functools import partial
|
|
import os
|
|
import sys
|
|
from subprocess import CalledProcessError, check_output, DEVNULL
|
|
|
|
from west import log
|
|
from west import config
|
|
from west.commands import CommandContextError
|
|
from west.commands.build import Build
|
|
from west.commands.flash import Flash
|
|
from west.commands.debug import Debug, DebugServer, Attach
|
|
from west.commands.project import List, Clone, Fetch, Pull, Rebase, Branch, \
|
|
Checkout, Diff, Status, Update, ForAll, \
|
|
WestUpdated
|
|
from west.manifest import Manifest
|
|
from west.util import quote_sh_list, in_multirepo_install, west_dir
|
|
|
|
IN_MULTIREPO_INSTALL = in_multirepo_install(os.path.dirname(__file__))
|
|
|
|
BUILD_FLASH_COMMANDS = [
|
|
Build(),
|
|
Flash(),
|
|
Debug(),
|
|
DebugServer(),
|
|
Attach(),
|
|
]
|
|
|
|
PROJECT_COMMANDS = [
|
|
List(),
|
|
Clone(),
|
|
Fetch(),
|
|
Pull(),
|
|
Rebase(),
|
|
Branch(),
|
|
Checkout(),
|
|
Diff(),
|
|
Status(),
|
|
Update(),
|
|
ForAll(),
|
|
]
|
|
|
|
# Built-in commands in this West. For compatibility with monorepo
|
|
# installations of West within the Zephyr tree, we only expose the
|
|
# project commands if this is a multirepo installation.
|
|
COMMANDS = BUILD_FLASH_COMMANDS
|
|
|
|
if IN_MULTIREPO_INSTALL:
|
|
COMMANDS += PROJECT_COMMANDS
|
|
|
|
|
|
class InvalidWestContext(RuntimeError):
|
|
pass
|
|
|
|
|
|
def command_handler(command, known_args, unknown_args):
|
|
command.run(known_args, unknown_args)
|
|
|
|
|
|
def set_zephyr_base(args):
|
|
'''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:
|
|
# The command line --zephyr-base takes precedence over
|
|
# everything else.
|
|
zb = os.path.abspath(args.zephyr_base)
|
|
zb_origin = 'command line'
|
|
else:
|
|
# If the user doesn't specify it concretely, use the project
|
|
# with path 'zephyr' if that exists, or the ZEPHYR_BASE value
|
|
# 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:
|
|
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():
|
|
# The bootstrapper will print its own version, as well as that of
|
|
# the west repository itself, then exit. So if this file is being
|
|
# asked to print the version, it's because it's being run
|
|
# directly, and not via the bootstrapper.
|
|
#
|
|
# Rather than play tricks like invoking "pip show west" (which
|
|
# assumes the bootstrapper was installed via pip, the common but
|
|
# not universal case), refuse the temptation to make guesses and
|
|
# print an honest answer.
|
|
log.inf('West bootstrapper version: N/A, not run via bootstrapper')
|
|
|
|
# The running west installation.
|
|
if IN_MULTIREPO_INSTALL:
|
|
try:
|
|
desc = check_output(['git', 'describe', '--tags'],
|
|
stderr=DEVNULL,
|
|
cwd=os.path.dirname(__file__))
|
|
west_version = desc.decode(sys.getdefaultencoding()).strip()
|
|
except CalledProcessError:
|
|
west_version = 'unknown'
|
|
else:
|
|
west_version = 'N/A, monorepo installation'
|
|
west_src_west = os.path.dirname(__file__)
|
|
print('West repository version: {} ({})'.
|
|
format(west_version,
|
|
os.path.dirname(os.path.dirname(west_src_west))))
|
|
|
|
|
|
def parse_args(argv):
|
|
# The prog='west' override avoids the absolute path of the main.py script
|
|
# showing up when West is run via the wrapper
|
|
west_parser = argparse.ArgumentParser(
|
|
prog='west', description='The Zephyr RTOS meta-tool.',
|
|
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,
|
|
help='''Override the Zephyr base directory. The
|
|
default is the manifest project with path
|
|
"zephyr".''')
|
|
|
|
west_parser.add_argument('-v', '--verbose', default=0, action='count',
|
|
help='''Display verbose output. May be given
|
|
multiple times to increase verbosity.''')
|
|
|
|
west_parser.add_argument('-V', '--version', action='store_true')
|
|
|
|
subparser_gen = west_parser.add_subparsers(title='commands',
|
|
dest='command')
|
|
|
|
for command in COMMANDS:
|
|
parser = command.add_parser(subparser_gen)
|
|
parser.set_defaults(handler=partial(command_handler, command))
|
|
|
|
args, unknown = west_parser.parse_known_args(args=argv)
|
|
|
|
if args.version:
|
|
print_version_info()
|
|
sys.exit(0)
|
|
|
|
# Set up logging verbosity before doing anything else, so
|
|
# e.g. verbose messages related to argument handling errors
|
|
# work properly.
|
|
log.set_verbosity(args.verbose)
|
|
|
|
if IN_MULTIREPO_INSTALL:
|
|
set_zephyr_base(args)
|
|
|
|
if 'handler' not in args:
|
|
if IN_MULTIREPO_INSTALL:
|
|
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)
|
|
|
|
return args, unknown
|
|
|
|
|
|
def main(argv=None):
|
|
# Makes ANSI color escapes work on Windows, and strips them when
|
|
# stdout/stderr isn't a terminal
|
|
colorama.init()
|
|
|
|
if argv is None:
|
|
argv = sys.argv[1:]
|
|
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(
|
|
args.command)
|
|
try:
|
|
args.handler(args, unknown)
|
|
except WestUpdated:
|
|
# West has been automatically updated. Restart ourselves to run the
|
|
# latest version, with the same arguments that we were given.
|
|
os.execv(sys.executable, [sys.executable] + argv)
|
|
except KeyboardInterrupt:
|
|
sys.exit(0)
|
|
except CalledProcessError as cpe:
|
|
log.err('command exited with status {}: {}'.format(
|
|
cpe.args[0], quote_sh_list(cpe.args[1])))
|
|
if args.verbose:
|
|
raise
|
|
else:
|
|
log.inf(for_stack_trace)
|
|
except CommandContextError as cce:
|
|
log.die('command', args.command, 'cannot be run in this context:',
|
|
*cce.args)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|