Esempio n. 1
0
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
Esempio n. 2
0
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)
Esempio n. 3
0
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)
Esempio n. 4
0
    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)
Esempio n. 5
0
    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)
Esempio n. 6
0
    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}
Esempio n. 7
0
 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)
Esempio n. 8
0
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
Esempio n. 9
0
    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]
Esempio n. 10
0
    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
Esempio n. 11
0
    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()!
Esempio n. 12
0
    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))
Esempio n. 13
0
    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
Esempio n. 14
0
 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}")
Esempio n. 15
0
    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)
Esempio n. 16
0
    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")
Esempio n. 17
0
    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
Esempio n. 18
0
    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)
Esempio n. 19
0
    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
Esempio n. 20
0
    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")
Esempio n. 21
0
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)
Esempio n. 22
0
    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
Esempio n. 23
0
 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))
Esempio n. 24
0
    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)
Esempio n. 25
0
    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)
Esempio n. 26
0
    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))
Esempio n. 27
0
    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))
Esempio n. 28
0
    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)
Esempio n. 29
0
 def check_cmd(self, cmd):
     if shutil.which(cmd) is None:
         log.die('{} is not installed or cannot be found'.format(cmd))
Esempio n. 30
0
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)