# Copyright (c) 2018 Open Source Foundries Limited. # # SPDX-License-Identifier: Apache-2.0 '''Common code used by commands which execute runners. ''' import argparse from os import getcwd, chdir from subprocess import CalledProcessError from textwrap import dedent from .. import cmake from .. import log from ..runner import get_runner_cls from . import CommandContextError def add_parser_common(parser_adder, command): parser = parser_adder.add_parser( command.name, formatter_class=argparse.RawDescriptionHelpFormatter, description=command.description) parser.add_argument('-d', '--build-dir', help='''Build directory to obtain runner information from; default is the current working directory.''') parser.add_argument('-c', '--cmake-cache', default=cmake.DEFAULT_CACHE, help='''Path to CMake cache file containing runner configuration (this is generated by the Zephyr build system when compiling binaries); default: {}. If this is a relative path, it is assumed relative to the build directory. An absolute path can also be given instead.'''.format(cmake.DEFAULT_CACHE)) parser.add_argument('-r', '--runner', help='''If given, overrides any cached {} runner.'''.format(command.name)) parser.add_argument('--skip-rebuild', action='store_true', help='''If given, do not rebuild the application before running {} commands.'''.format(command.name)) return parser def desc_common(command_name): return dedent('''\ Any options not recognized by this command are passed to the back-end {command} runner. If you need to pass an option to a runner which has the same name as one recognized by this command, you can end argument parsing with a '--', like so: west {command} --{command}-arg=value -- --runner-arg=value2 '''.format(**{'command': command_name})) def do_run_common(command, args, runner_args, cached_runner_var): command_name = command.name build_dir = args.build_dir or getcwd() if not args.skip_rebuild: try: cmake.run_build(build_dir) except CalledProcessError: if args.build_dir: log.die('cannot run {}, build in {} failed'.format( command_name, args.build_dir)) else: log.die('cannot run {}; no --build-dir given and build in ' 'current directory {} failed'.format(command_name, build_dir)) # Temporary hack: we need to ensure we're running from the build # directory for now. Otherwise, the BuildConfiguration objects # that get created by the runners look for .config in the wrong # places. chdir(build_dir) # TODO: build this by joining with build_dir once the above chdir # goes away. cache_file = args.cmake_cache cache = cmake.CMakeCache(cache_file) board = cache['CACHED_BOARD'] available = cache.get_list('ZEPHYR_RUNNERS') if not available: log.wrn('No cached runners are available in', cache_file) runner = args.runner or cache.get(cached_runner_var) if runner is None: raise CommandContextError(dedent(""" No {} runner available for {}. Please either specify one manually, or check your board's documentation for alternative instructions.""".format(command_name, board))) log.inf('Using runner:', runner) if runner not in available: log.wrn('Runner {} is not configured for use with {}, ' 'this may not work'.format(runner, board)) runner_cls = get_runner_cls(runner) if command_name not in runner_cls.capabilities().commands: log.die('Runner {} does not support command {}'.format( runner, command_name)) cached_common_args = cache.get_list('ZEPHYR_RUNNER_ARGS_COMMON') cached_runner_args = cache.get_list( 'ZEPHYR_RUNNER_ARGS_{}'.format(cmake.make_c_identifier(runner))) # Construct the final command line arguments, create a # runner-specific parser to handle them, and run the command. assert isinstance(runner_args, list), runner_args final_runner_args = cached_common_args + cached_runner_args + runner_args # Having the runners themselves be the place where their argument # parsing is handled is hackish; it's an artifact of the time # before the runner package was part of west. # # TODO: refactor runner argument parsing higher up into west. parser = argparse.ArgumentParser(prog=runner) runner_cls.add_parser(parser) parsed_args = parser.parse_args(args=final_runner_args) parsed_args.verbose = args.verbose runner = runner_cls.create_from_args(parsed_args) runner.run(command_name)