zephyr/scripts/meta/west/main.py

194 lines
6.1 KiB
Python
Raw Normal View History

#!/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
import log
from commands import CommandContextError
from commands.build import Build
from commands.flash import Flash
from commands.debug import Debug, DebugServer, Attach
from commands.project import ListProjects, Fetch, Pull, Rebase, Branch, \
Checkout, Diff, Status, Update, ForAll, \
WestUpdated
from util import quote_sh_list, in_multirepo_install
IN_MULTIREPO_INSTALL = in_multirepo_install(__file__)
BUILD_FLASH_COMMANDS = [
Build(),
Flash(),
Debug(),
DebugServer(),
Attach(),
]
PROJECT_COMMANDS = [
ListProjects(),
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 validate_context(args, unknown):
'''Validate the run-time context expected by west.'''
if args.zephyr_base:
os.environ['ZEPHYR_BASE'] = args.zephyr_base
else:
if 'ZEPHYR_BASE' not in os.environ:
log.wrn('--zephyr-base missing and no ZEPHYR_BASE',
'in the environment')
else:
args.zephyr_base = os.environ['ZEPHYR_BASE']
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 as e:
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.')
west_parser.add_argument('-z', '--zephyr-base', default=None,
help='''Path to the Zephyr base directory. If not
given, ZEPHYR_BASE must be defined in the
environment, and will be used instead.''')
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)
try:
validate_context(args, unknown)
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:
log.err('you must specify a command', fatal=True)
west_parser.print_usage(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)
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] + sys.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)
except Exception as exc:
log.err(*exc.args, fatal=True)
if args.verbose:
raise
else:
log.inf(for_stack_trace)
if __name__ == "__main__":
main()