def use_runner_cls(command, board, args, runners_yaml, cache): # Get the ZephyrBinaryRunner class from its name, and make sure it # supports the command. Print a message about the choice, and # return the class. runner = args.runner or runners_yaml.get(command.runner_key) if runner is None: log.die(f'no {command.name} runner available for board {board}. ' "Check the board's documentation for instructions.") _banner(f'west {command.name}: using runner {runner}') available = runners_yaml.get('runners', []) if runner not in available: if 'BOARD_DIR' in cache: board_cmake = Path(cache['BOARD_DIR']) / 'board.cmake' else: board_cmake = 'board.cmake' log.err(f'board {board} does not support runner {runner}', fatal=True) log.inf(f'To fix, configure this runner in {board_cmake} and rebuild.') sys.exit(1) try: runner_cls = get_runner_cls(runner) except ValueError as e: log.die(e) if command.name not in runner_cls.capabilities().commands: log.die(f'runner {runner} does not support command {command.name}') return runner_cls
def main(argv=None): # Makes ANSI color escapes work on Windows, and strips them when # stdout/stderr isn't a terminal colorama.init() # Read the configuration files config.read_config() # Load any external command specs. If the config file isn't # fully set up yet, ignore the error. This allows west init to # work properly. try: externals = get_external_commands() except MalformedConfig: externals = {} if argv is None: argv = sys.argv[1:] args, unknown = parse_args(argv, externals) 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)
def _ensure_min_version(cmake, dry_run): cmd = [cmake, '--version'] if dry_run: log.inf('Dry run:', quote_sh_list(cmd)) return try: version_out = subprocess.check_output(cmd, stderr=subprocess.DEVNULL) except subprocess.CalledProcessError as cpe: log.die('cannot get cmake version:', str(cpe)) decoded = version_out.decode('utf-8') lines = decoded.splitlines() if not lines: log.die('can\'t get cmake version: ' + 'unexpected "cmake --version" output:\n{}\n'. format(decoded) + 'Please install CMake ' + _MIN_CMAKE_VERSION_STR + ' or higher (https://cmake.org/download/).') version = lines[0].split()[2] if '-' in version: # Handle semver cases like "3.19.20210206-g1e50ab6" # which Kitware uses for prerelease versions. version = version.split('-', 1)[0] if packaging.version.parse(version) < _MIN_CMAKE_VERSION: log.die('cmake version', version, 'is less than minimum version {};'. format(_MIN_CMAKE_VERSION_STR), 'please update your CMake (https://cmake.org/download/).') else: log.dbg('cmake version', version, 'is OK; minimum version is', _MIN_CMAKE_VERSION_STR)
def update_some(self, args): # The 'west update PROJECT [...]' style invocation is only # possible for "flat" manifests, i.e. manifests without import # statements. if self.manifest.has_imports: log.die("refusing to update just some projects because " "manifest imports are in use\n" ' Project arguments: {}\n' ' Manifest file with imports: {}\n' ' Please run "west update" (with no arguments) instead.'. format(' '.join(args.projects), self.manifest.path)) failed = [] for project in self._projects(args.projects): if isinstance(project, ManifestProject): continue try: self.update(project) except subprocess.CalledProcessError: failed.append(project) self._handle_failed(args, failed)
def run(self, args, unknown, topdir, manifest=None): '''Run the command. This raises `west.commands.CommandContextError` if the command cannot be run due to a context mismatch. Other exceptions may be raised as well. :param args: known arguments parsed via `WestCommand.add_parser` :param unknown: unknown arguments present on the command line; must be empty unless ``accepts_unknown_args`` is true :param topdir: west workspace topdir, accessible as ``self.topdir`` from `WestCommand.do_run` :param manifest: `west.manifest.Manifest` or ``None``, accessible as ``self.manifest`` from `WestCommand.do_run` ''' if unknown and not self.accepts_unknown_args: self.parser.error(f'unexpected arguments: {unknown}') if not topdir and self.requires_workspace: log.die(_no_topdir_msg(os.getcwd(), self.name)) self.topdir = topdir self.manifest = manifest self.do_run(args, unknown)
def setup_upstream_downstream(self, args): # set some instance state that will be useful for # comparing upstream and downstream. # # below, "pmap" means "project map": a dict mapping project # names to west.manifest.Project instances. # z_manifest: upstream zephyr west.manifest.Manifest instance # # zephyr_rev: validated --zephyr-rev argument. # (quits and print a message about what to do if things go wrong). if hasattr(args, 'zephyr_rev'): self.zephyr_rev = self.validate_zephyr_rev(args) self.z_manifest = self.zephyr_manifest() self.z_pmap = {p.name: p for p in self.z_manifest.projects} # we want to build an ncs_pmap too. if we have a projects arg, # we'll use that. otherwise, we'll just use all the projects # in the NCS west manifest. if hasattr(args, 'projects'): try: projects = self.manifest.get_projects(args.projects, only_cloned=True) except ValueError as ve: # West guarantees that get_projects()'s ValueError # has exactly two values in args. # # pylint: disable=unbalanced-tuple-unpacking unknown, uncloned = ve.args if unknown: log.die('unknown projects:', ', '.join(unknown)) if uncloned: log.die( 'uncloned downstream projects:', ', '.join(u.name for u in uncloned) + '\n' + 'Run "west update", then retry.') else: projects = self.manifest.projects self.ncs_pmap = {p.name: p for p in projects}
def update_importer(self, project, path): self.update(project) try: return _manifest_content_at(project, path) except FileNotFoundError: # FIXME we need each project to have back-pointers # to the manifest file where it was defined, so we can # tell the user better context than just "run -vvv", which # is a total fire hose. name = project.name sha = project.sha(QUAL_MANIFEST_REV) if log.VERBOSE < log.VERBOSE_EXTREME: suggest_vvv = ('\n' ' Use "west -vvv update" to debug.') else: suggest_vvv = '' log.die(f"can't import from project {name}\n" f' Expected to import from {path} at revision {sha}\n' f' Hint: possible manifest file fixes for {name}:\n' f' - set "revision:" to a git ref with this file ' f'at URL {project.url}\n' ' - remove the "import:"' + suggest_vvv)
def get_build_dir(args, die_if_none=True): # Get the build directory for the given argument list and environment. if args.build_dir: return args.build_dir guess = config.get('build', 'guess-dir', fallback='never') guess = guess == 'runners' dir = find_build_dir(None, guess) if dir and is_zephyr_build(dir): return dir elif die_if_none: msg = '--build-dir was not given, ' if dir: msg = msg + 'and neither {} nor {} are zephyr build directories.' else: msg = msg + ('{} is not a build directory and the default build ' 'directory cannot be determined. Check your ' 'build.dir-fmt configuration option') log.die(msg.format(getcwd(), dir)) else: return None
def find_imgtool(command, args): if args.tool_path: imgtool = args.tool_path if not os.path.isfile(imgtool): log.die(f'--tool-path {imgtool}: no such file') else: imgtool = shutil.which('imgtool') or shutil.which('imgtool.py') if not imgtool: log.die( 'imgtool not found; either install it', '(e.g. "pip3 install imgtool") or provide --tool-path') if platform.system() == 'Windows' and imgtool.endswith('.py'): # Windows users may not be able to run .py files # as executables in subprocesses, regardless of # what the mode says. Always run imgtool as # 'python path/to/imgtool.py' instead of # 'path/to/imgtool.py' in these cases. # https://github.com/zephyrproject-rtos/zephyr/issues/31876 return [sys.executable, imgtool] return [imgtool]
def _projects(self, ids, only_cloned=False): try: return self.manifest.get_projects(ids, only_cloned=only_cloned) except ValueError as ve: if len(ve.args) != 2: raise # not directly raised by get_projects() # Die with an error message on unknown or uncloned projects. unknown, uncloned = ve.args if unknown: s = 's' if len(unknown) > 1 else '' names = ' '.join(unknown) log.die(f'unknown project name{s}/path{s}: {names}\n' ' Hint: use "west list" to list all projects.') elif only_cloned and uncloned: s = 's' if len(uncloned) > 1 else '' names = ' '.join(p.name for p in uncloned) log.die(f'uncloned project{s}: {names}.\n' ' Hint: run "west update" and retry.') else: # Should never happen, but re-raise to fail fast and # preserve a stack trace, to encourage a bug report. raise
def do_run(self, args, user_args): for project in _projects(args): # Spelling out the format keys explicitly here gives us # future-proofing if the internal Project representation # ever changes. try: result = args.format.format( name=project.name, url=project.url, path=project.path, abspath=project.abspath, posixpath=project.posixpath, revision=project.revision, cloned="(cloned)" if _cloned(project) else "(not cloned)", clone_depth=project.clone_depth or "None") except KeyError as e: # The raised KeyError seems to just put the first # invalid argument in the args tuple, regardless of # how many unrecognizable keys there were. log.die('unknown key "{}" in format string "{}"'. format(e.args[0], args.format)) log.inf(result, colorize=False) # don't use _msg()!
def do_run(self, args, ignored): if self.topdir: log.die('already in an installation ({}), aborting'. format(self.topdir)) if shutil.which('git') is None: log.die("can't find git; install it or ensure it's on your PATH") # west.manifest will try to read manifest.path and use it when # parsing the manifest. Clear it out for now so we can parse # the manifest without it; local() or bootstrap() will set it # properly. if config.get('manifest', 'path', fallback=None) is not None: config.remove_option('manifest', 'path') if args.local: projects, manifest_dir = self.local(args) else: projects, manifest_dir = self.bootstrap(args) self.fixup_zephyr_base(projects) _banner('Initialized. Now run "west update" inside {}.'. format(self.topdir))
def _setup_build_dir(self): # Initialize build_dir and created_build_dir attributes. log.dbg('setting up build directory', level=log.VERBOSE_EXTREME) if self.args.build_dir: build_dir = self.args.build_dir else: cwd = os.getcwd() if is_zephyr_build(cwd): build_dir = cwd else: build_dir = DEFAULT_BUILD_DIR build_dir = os.path.abspath(build_dir) if os.path.exists(build_dir): if not os.path.isdir(build_dir): log.die('build directory {} exists and is not a directory'. format(build_dir)) else: os.makedirs(build_dir, exist_ok=False) self.created_build_dir = True self.run_cmake = True self.build_dir = build_dir
def create(self, directory, exist_ok=True): try: os.makedirs(directory, exist_ok=exist_ok) except PermissionError: log.die(f'Cannot initialize in {directory}: permission denied') except FileExistsError: log.die(f'Something else created {directory} concurrently') except Exception as e: log.die(f"Can't create {directory}: {e}")
def sign(self, command, build_dir, bcfg, formats): args = command.args if args.tool_path: command.check_force( shutil.which(args.tool_path), '--tool-path {}: not an executable'.format(args.tool_path)) tool_path = args.tool_path else: tool_path = shutil.which('rimage') if not tool_path: log.die('rimage not found; either install it', 'or provide --tool-path') b = pathlib.Path(build_dir) cache = CMakeCache.from_build_dir(build_dir) board = cache['CACHED_BOARD'] log.inf('Signing for board ' + board) target = self.edt_get_rimage_target(board) log.inf('Signing for SOC target ' + target) if not args.quiet: log.inf('Signing with tool {}'.format(tool_path)) bootloader = str(b / 'zephyr' / 'bootloader.elf.mod') kernel = str(b / 'zephyr' / 'zephyr.elf.mod') out_bin = str(b / 'zephyr' / 'zephyr.ri') sign_base = ([tool_path] + args.tool_args + ['-o', out_bin, '-m', target, '-i', '3'] + [bootloader, kernel]) if not args.quiet: log.inf(quote_sh_list(sign_base)) subprocess.check_call(sign_base)
def zephyr_manifest(self): # Load the upstream manifest. Since west v0.13, the resulting # projects have no absolute paths, so we'll fix those up so # they're relative to our own topdir. (We can't use # Manifest.from_file() in this case because self.zephyr_rev is # not what's checked out on the file system). z_project = self.manifest.get_projects(['zephyr'], allow_paths=False, only_cloned=True)[0] cp = z_project.git(f'show {self.zephyr_rev}:west.yml', capture_stdout=True, check=True) z_west_yml = cp.stdout.decode('utf-8') try: ret = Manifest.from_data(source_data=yaml.safe_load(z_west_yml), import_flags=ImportFlag.IGNORE) if WEST_V0_13_0_OR_LATER: for project in ret.projects: project.topdir = self.manifest.topdir return ret except MalformedManifest: log.die(f"can't load zephyr manifest; file {z_west_yml} " "is malformed")
def edt_flash_node(b): # Get the EDT Node corresponding to the zephyr,flash chosen DT # node; 'b' is the build directory as a pathlib object. # Ensure the build directory has a compiled DTS file # where we expect it to be. dts = b / 'zephyr' / 'zephyr.dts' log.dbg('DTS file:', dts, level=log.VERBOSE_VERY) edt_pickle = b / 'zephyr' / 'edt.pickle' if not edt_pickle.is_file(): log.die("can't load devicetree; expected to find:", edt_pickle) # Load the devicetree. with open(edt_pickle, 'rb') as f: edt = pickle.load(f) # By convention, the zephyr,flash chosen node contains the # partition information about the zephyr image to sign. flash = edt.chosen_node('zephyr,flash') if not flash: log.die('devicetree has no chosen zephyr,flash node;', "can't infer flash write block or image-0 slot sizes") return flash
def clone_manifest(self, url, rev, dest, exist_ok=False): log.small_banner('Cloning manifest repository from {}, rev. {}'.format( url, rev)) if not exist_ok and exists(dest): log.die('refusing to clone into existing location ' + dest) subprocess.check_call(('git', 'init', dest)) subprocess.check_call(('git', 'remote', 'add', 'origin', '--', url), cwd=dest) maybe_sha = _maybe_sha(rev) if maybe_sha: # Fetch the ref-space and hope the SHA is contained in # that ref-space subprocess.check_call(('git', 'fetch', 'origin', '--tags', '--', 'refs/heads/*:refs/remotes/origin/*'), cwd=dest) else: # Fetch the ref-space similar to git clone plus the ref # given by user. Redundancy is ok, for example if the user # specifies 'heads/master'. This allows users to specify: # pull/<no>/head for pull requests subprocess.check_call(('git', 'fetch', 'origin', '--tags', '--', rev, 'refs/heads/*:refs/remotes/origin/*'), cwd=dest) try: # Using show-ref to determine if rev is available in local repo. subprocess.check_call(('git', 'show-ref', '--', rev), cwd=dest) local_rev = True except subprocess.CalledProcessError: local_rev = False if local_rev or maybe_sha: subprocess.check_call(('git', 'checkout', rev), cwd=dest) else: subprocess.check_call(('git', 'checkout', 'FETCH_HEAD'), cwd=dest)
def local(self, args): if args.manifest_rev is not None: log.die('--mr cannot be used with -l') manifest_dir = canonical(args.directory or os.getcwd()) manifest_file = join(manifest_dir, 'west.yml') topdir = dirname(manifest_dir) rel_manifest = basename(manifest_dir) west_dir = os.path.join(topdir, WEST_DIR) _banner('Initializing from existing manifest repository ' + rel_manifest) if not exists(manifest_file): log.die('No "west.yml" found in {}'.format(manifest_dir)) self.create(west_dir) os.chdir(topdir) projects = self.projects(manifest_file) # This validates the manifest. _msg('Creating {} and local configuration'.format(west_dir)) update_config('manifest', 'path', rel_manifest) self.topdir = topdir return projects, manifest_dir
def zephyr_manifest(self): # load the upstream manifest. the west.manifest APIs guarantee # in this case that its project hierarchy is rooted in the NCS # directory. z_project = self.manifest.get_projects(['zephyr'], allow_paths=False, only_cloned=True)[0] cp = z_project.git(f'show {self.zephyr_rev}:west.yml', capture_stdout=True, check=True) z_west_yml = cp.stdout.decode('utf-8') try: # The topdir kwarg was added in a pre-release west, which # is required to use this file. The latest stable (0.6.3) # doesn't have it, so pylint is failing with a false error # report here. Turn it off for now; this can be removed # when west 0.7 is out. # # pylint: disable=unexpected-keyword-arg return Manifest.from_data(source_data=yaml.safe_load(z_west_yml), topdir=self.topdir) except MalformedManifest: log.die(f"can't load zephyr manifest; file {z_west_yml} " "is malformed")
def dump_context(command, args, unknown_args): build_dir = get_build_dir(args, die_if_none=False) if build_dir is None: log.wrn('no --build-dir given or found; output will be limited') runner_config = None else: cache = load_cmake_cache(build_dir, args) board = cache['CACHED_BOARD'] runners_yaml = runners_yaml_path(cache) runner_config = load_runners_yaml(runners_yaml, args) # Re-build unless asked not to, to make sure the output is up to date. if build_dir and not args.skip_rebuild: rebuild(command, build_dir, args) if args.runner: try: cls = get_runner_cls(args.runner) except ValueError: log.die(f'invalid runner name {args.runner}; choices: ' + ', '.join(cls.name() for cls in ZephyrBinaryRunner.get_runners())) else: cls = None if runner_config is None: dump_context_no_config(command, cls) else: log.inf(f'build configuration:', colorize=True) log.inf(f'{INDENT}build directory: {build_dir}') log.inf(f'{INDENT}board: {board}') log.inf(f'{INDENT}runners.yaml: {runners_yaml}') if cls: dump_runner_context(command, cls, runner_config) else: dump_all_runner_context(command, runner_config, board, build_dir)
def _setup_build_dir(self): # Initialize build_dir and created_build_dir attributes. # If we created the build directory, we must run CMake. log.dbg('setting up build directory', level=log.VERBOSE_EXTREME) # The CMake Cache has not been loaded yet, so this is safe board, _ = self._find_board() source_dir = self._find_source_dir() app = os.path.split(source_dir)[1] build_dir = find_build_dir(self.args.build_dir, board=board, source_dir=source_dir, app=app) if not build_dir: log.die('Unable to determine a default build folder. Check ' 'your build.dir-fmt configuration option') if os.path.exists(build_dir): if not os.path.isdir(build_dir): log.die('build directory {} exists and is not a directory'. format(build_dir)) else: os.makedirs(build_dir, exist_ok=False) self.created_build_dir = True self.run_cmake = True self.build_dir = build_dir
def create(self, directory, exist_ok=True): try: os.makedirs(directory, exist_ok=exist_ok) except PermissionError: log.die('Cannot initialize in {}: permission denied'. format(directory)) except FileExistsError: log.die('Something else created {} concurrently; quitting'. format(directory)) except Exception as e: log.die("Can't create directory {}: {}".format(directory, e.args))
def do_run(self, args, user_args): def sha_thunk(project): die_if_no_git() if not project.is_cloned(): log.die(f'cannot get sha for uncloned project {project.name}; ' f'run "west update {project.name}" and retry') elif project.revision: return project.sha(MANIFEST_REV) else: return f'{"N/A":40}' def cloned_thunk(project): die_if_no_git() return "cloned" if project.is_cloned() else "not-cloned" def delay(func, project): return DelayFormat(partial(func, project)) for project in self._projects(args.projects): # Spelling out the format keys explicitly here gives us # future-proofing if the internal Project representation # ever changes. # # Using DelayFormat delays computing derived values, such # as SHAs, unless they are specifically requested, and then # ensures they are only computed once. try: result = args.format.format(name=project.name, url=project.url or 'N/A', path=project.path, abspath=project.abspath, posixpath=project.posixpath, revision=project.revision or 'N/A', clone_depth=project.clone_depth or "None", cloned=delay( cloned_thunk, project), sha=delay(sha_thunk, project)) except KeyError as e: # The raised KeyError seems to just put the first # invalid argument in the args tuple, regardless of # how many unrecognizable keys there were. log.die(f'unknown key "{e.args[0]}" in format string ' f'{shlex.quote(args.format)}') except IndexError: self.parser.print_usage() log.die(f'invalid format string {shlex.quote(args.format)}') except subprocess.CalledProcessError: log.die(f'subprocess failed while listing {project.name}') log.inf(result, colorize=False)
def _run_pristine(self): log.inf('Making build dir {} pristine'.format(self.build_dir)) zb = os.environ.get('ZEPHYR_BASE') if not zb: log.die('Internal error: ZEPHYR_BASE not set in the environment, ' 'and should have been by the main script') if not is_zephyr_build(self.build_dir): log.die('Refusing to run pristine on a folder that is not a ' 'Zephyr build system') cmake_args = ['-P', '{}/cmake/pristine.cmake'.format(zb)] cmake = shutil.which('cmake') if cmake is None: log.die('CMake is not installed or cannot be found; cannot make ' 'the build folder pristine') cmd = [cmake] + cmake_args subprocess.check_call(cmd, cwd=self.build_dir)
def do_run(self, args, unknown_args): zb = os.environ.get('ZEPHYR_BASE') if not zb: log.die('Internal error: ZEPHYR_BASE not set in the environment, ' 'and should have been by the main script') cmake_args = [ '-DBOARD_ROOT_SPACE_SEPARATED={}'.format(zb), '-P', '{}/cmake/boards.cmake'.format(zb) ] lines = run_cmake(cmake_args, capture_output=True) arch_re = re.compile(r'\s*([\w-]+)\:') board_re = re.compile(r'\s*([\w-]+)\s*') arch = None boards = collections.OrderedDict() for line in lines: match = arch_re.match(line) if match: arch = match.group(1) boards[arch] = [] continue match = board_re.match(line) if match: if not arch: log.die( 'Invalid board output from CMake: {}'.format(lines)) board = match.group(1) boards[arch].append(board) for arch in boards: for board in boards[arch]: try: result = args.format.format(name=board, arch=arch) print(result) except KeyError as e: # The raised KeyError seems to just put the first # invalid argument in the args tuple, regardless of # how many unrecognizable keys there were. log.die('unknown key "{}" in format string "{}"'.format( e.args[0], args.format))
def do_run(self, args, ignored): if self.topdir: zb = os.environ.get('ZEPHYR_BASE') if zb: msg = textwrap.dedent(''' Note: In your environment, ZEPHYR_BASE is set to: {} This forces west to search for an installation there. Try unsetting ZEPHYR_BASE and re-running this command.'''. format(zb)) else: msg = '' log.die('already initialized in {}, aborting.{}'.format( self.topdir, msg)) if args.local and (args.manifest_url or args.manifest_rev): log.die('-l cannot be combined with -m or --mr') if shutil.which('git') is None: log.die("can't find git; install it or ensure it's on your PATH") # west.manifest will try to read manifest.path and use it when # parsing the manifest. Clear it out for now so we can parse # the manifest without it; local() or bootstrap() will set it # properly. if config.get('manifest', 'path', fallback=None) is not None: config.remove_option('manifest', 'path') if args.local: projects, manifest_dir = self.local(args) else: projects, manifest_dir = self.bootstrap(args) self.fixup_zephyr_base(projects) log.banner('Initialized. Now run "west update" inside {}.'.format( self.topdir))
def sign(self, command, build_dir, bcfg, formats): args = command.args if args.tool_path: command.check_force( shutil.which(args.tool_path), '--tool-path {}: not an executable'.format(args.tool_path)) tool_path = args.tool_path else: tool_path = shutil.which('imgtool') if not tool_path: log.die( 'imgtool not found; either install it', '(e.g. "pip3 install imgtool") or provide --tool-path') align, vtoff, slot_size = [ self.get_cfg(command, bcfg, x) for x in ('DT_FLASH_WRITE_BLOCK_SIZE', 'CONFIG_TEXT_SECTION_OFFSET', 'DT_FLASH_AREA_IMAGE_0_SIZE') ] log.dbg('build config: --align={}, --header-size={}, --slot-size={}'. format(align, vtoff, slot_size)) # Base sign command. # # We provide a default --version in case the user is just # messing around and doesn't want to set one. It will be # overridden if there is a --version in args.tool_args. sign_base = [tool_path, 'sign', '--version', '0.0.0+0'] if align: sign_base.extend(['--align', str(align)]) else: log.wrn('expected nonzero flash alignment, but ' 'DT_FLASH_WRITE_BLOCK_SIZE={} ' "in build directory's ({}) device tree".format( align, build_dir)) if vtoff: sign_base.extend(['--header-size', str(vtoff)]) else: log.wrn('expected nonzero header size, but ' 'CONFIG_TEXT_SECTION_OFFSET={} ' "in build directory's ({}) .config".format( vtoff, build_dir)) if slot_size: sign_base.extend(['--slot-size', str(slot_size)]) else: log.wrn('expected nonzero slot size, but ' 'DT_FLASH_AREA_IMAGE_0_SIZE={} ' "in build directory's ({}) device tree".format( slot_size, build_dir)) b = pathlib.Path(build_dir) cache = cmake.CMakeCache.from_build_dir(build_dir) runner_config = cached_runner_config(build_dir, cache) # Build a signed .bin if 'bin' in formats and runner_config.bin_file: out_bin = args.sbin or str(b / 'zephyr' / 'zephyr.signed.bin') log.inf('Generating:', out_bin) sign_bin = (sign_base + args.tool_args + [runner_config.bin_file, out_bin]) log.dbg(quote_sh_list(sign_bin)) subprocess.check_call(sign_bin) # Build a signed .hex if 'hex' in formats and runner_config.hex_file: out_hex = args.shex or str(b / 'zephyr' / 'zephyr.signed.hex') log.inf('Generating:', out_hex) sign_hex = (sign_base + args.tool_args + [runner_config.hex_file, out_hex]) log.dbg(quote_sh_list(sign_hex)) subprocess.check_call(sign_hex)
def check_cmd(self, cmd): if shutil.which(cmd) is None: log.die('{} is not installed or cannot be found'.format(cmd))
def _dump_context(command, args, runner_args, cached_runner_var): build_dir = _build_dir(args, die_if_none=False) # Try to figure out the CMake cache file based on the build # directory or an explicit argument. if build_dir is not None: cache_file = path.abspath( path.join(build_dir, args.cmake_cache or cmake.DEFAULT_CACHE)) elif args.cmake_cache: cache_file = path.abspath(args.cmake_cache) else: cache_file = None # Load the cache itself, if possible. if cache_file is None: log.wrn('No build directory (--build-dir) or CMake cache ' '(--cache-file) given or found; output will be limited') cache = None else: try: cache = cmake.CMakeCache(cache_file) except Exception: log.die('Cannot load cache {}.'.format(cache_file)) # If we have a build directory, try to ensure build artifacts are # up to date. If that doesn't work, still try to print information # on a best-effort basis. if build_dir and not args.skip_rebuild: try: cmake.run_build(build_dir) except CalledProcessError: msg = 'Failed re-building application; cannot load context. ' if args.build_dir: msg += 'Is {} the right --build-dir?'.format(args.build_dir) else: msg += textwrap.dedent('''\ Use --build-dir (-d) to specify a build directory; the one used was {}.'''.format(build_dir)) log.die('\n'.join( textwrap.wrap(msg, initial_indent='', subsequent_indent=INDENT, break_on_hyphens=False))) if cache is None: _dump_no_context_info(command, args) if not args.runner: return if args.runner: # Just information on one runner was requested. _dump_one_runner_info(cache, args, build_dir, INDENT) return board = cache['CACHED_BOARD'] all_cls = { cls.name(): cls for cls in ZephyrBinaryRunner.get_runners() if command.name in cls.capabilities().commands } available = [r for r in cache.get_list('ZEPHYR_RUNNERS') if r in all_cls] available_cls = {r: all_cls[r] for r in available if r in all_cls} default_runner = cache.get(cached_runner_var) cfg = cached_runner_config(build_dir, cache) log.inf('All Zephyr runners which support {}:'.format(command.name), colorize=True) for line in util.wrap(', '.join(all_cls.keys()), INDENT): log.inf(line) log.inf('(Not all may work with this build, see available runners below.)', colorize=True) if cache is None: log.warn('Missing or invalid CMake cache {}; there is no context.', 'Use --build-dir to specify the build directory.') return log.inf('Build directory:', colorize=True) log.inf(INDENT + build_dir) log.inf('Board:', colorize=True) log.inf(INDENT + board) log.inf('CMake cache:', colorize=True) log.inf(INDENT + cache_file) if not available: # Bail with a message if no runners are available. msg = ('No runners available for {}. ' 'Consult the documentation for instructions on how to run ' 'binaries on this target.').format(board) for line in util.wrap(msg, ''): log.inf(line, colorize=True) return log.inf('Available {} runners:'.format(command.name), colorize=True) log.inf(INDENT + ', '.join(available)) log.inf('Additional options for available', command.name, 'runners:', colorize=True) for runner in available: _dump_runner_opt_help(runner, all_cls[runner]) log.inf('Default {} runner:'.format(command.name), colorize=True) log.inf(INDENT + default_runner) _dump_runner_config(cfg, '', INDENT) log.inf('Runner-specific information:', colorize=True) for runner in available: log.inf('{}{}:'.format(INDENT, runner), colorize=True) _dump_runner_cached_opts(cache, runner, INDENT * 2, INDENT * 3) _dump_runner_caps(available_cls[runner], INDENT * 2) if len(available) > 1: log.inf('(Add -r RUNNER to just print information about one runner.)', colorize=True)