Ejemplo n.º 1
0
def test_config_global(west_init_tmpdir):
    if not os.path.exists(os.path.expanduser('~')):
        os.mkdir(os.path.expanduser('~'))

    # To ensure that GLOBAL home folder points into tox temp dir.
    # Otherwise fail the test, as we don't want to risk manipulating user's
    # west config
    assert canon_path(config.ConfigFile.GLOBAL.value) == \
        canon_path(os.path.join(os.environ.get('TOXTEMPDIR'),
                                'pytest-home', '.westconfig'))

    # Make sure the value is currently unset.
    testkey_value = cmd('config pytest.testkey_global')
    assert testkey_value == ''

    # Set value globally.
    cmd('config --global pytest.testkey_global foo')

    # Read from --local, to ensure that is empty.
    testkey_value = cmd('config --local pytest.testkey_global')
    assert testkey_value == ''

    # Read from --system, to ensure that is empty.
    testkey_value = cmd('config --system pytest.testkey_global')
    assert testkey_value == ''

    # Read from --global, and check the value.
    testkey_value = cmd('config --global pytest.testkey_global')
    assert testkey_value.rstrip() == 'foo'

    # Without an explicit config source, the global value (the only
    # one set) should be returned.
    testkey_value = cmd('config pytest.testkey_global')
    assert testkey_value.rstrip() == 'foo'
Ejemplo n.º 2
0
    def _load(self, path_hint):
        # Initialize this instance's fields from values given in the
        # manifest data, which must be validated according to the schema.

        projects = []
        projects_by_name = {}
        projects_by_ppath = {}
        manifest = self._data['manifest']

        # Create the ManifestProject instance and install it into the
        # projects list.
        assert MANIFEST_PROJECT_INDEX == 0
        projects.append(self._load_self(path_hint))

        # Map from each remote's name onto that remote's data in the manifest.
        self._remotes_by_name = {
            r['name']: Remote(r['name'], r['url-base'])
            for r in manifest.get('remotes', [])
        }

        # Get any defaults out of the manifest.
        self._defaults = self._load_defaults()

        # pdata = project data (dictionary of project information parsed from
        # the manifest file)
        for pdata in manifest['projects']:
            project = self._load_project(pdata)

            # Project names must be unique.
            if project.name in projects_by_name:
                self._malformed('project name {} is already used'.format(
                    project.name))
            # Two projects cannot have the same path. We use a PurePath
            # comparison here to ensure that platform-specific canonicalization
            # rules are handled correctly.
            ppath = PurePath(project.path)
            other = projects_by_ppath.get(ppath)
            if other:
                self._malformed(
                    'project {} path "{}" is already taken by project {}'.
                    format(project.name, project.path, other.name))
            else:
                projects_by_ppath[ppath] = project

            projects.append(project)
            projects_by_name[project.name] = project

        self.projects = tuple(projects)
        self._projects_by_name = projects_by_name
        self._projects_by_cpath = {}
        if self.topdir:
            mp = self.projects[MANIFEST_PROJECT_INDEX]
            if mp.abspath:
                self._projects_by_cpath[util.canon_path(mp.abspath)] = mp
            for p in self.projects[MANIFEST_PROJECT_INDEX + 1:]:
                assert p.abspath  # sanity check a program invariant
                self._projects_by_cpath[util.canon_path(p.abspath)] = p
Ejemplo n.º 3
0
    def local(self, args):
        if args.manifest_rev is not None:
            log.die('--mr cannot be used with -l')

        manifest_dir = util.canon_path(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)

        log.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)
        # This validates the manifest. Note we cannot use
        # self.manifest from west init, as we are in the middle of
        # creating the installation right now.
        projects = self.projects(manifest_file)
        log.small_banner(
            'Creating {} and local configuration'.format(west_dir))
        update_config('manifest', 'path', rel_manifest)

        self.topdir = topdir

        return projects, manifest_dir
Ejemplo n.º 4
0
    def bootstrap(self, args):
        manifest_url = args.manifest_url or MANIFEST_URL_DEFAULT
        manifest_rev = args.manifest_rev or MANIFEST_REV_DEFAULT
        topdir = util.canon_path(args.directory or os.getcwd())
        west_dir = join(topdir, WEST_DIR)

        try:
            already = util.west_topdir(topdir, fall_back=False)
            self.die_already(already)
        except util.WestNotFound:
            pass

        log.banner('Initializing in', topdir)
        if not isdir(topdir):
            self.create(topdir, exist_ok=False)

        # Clone the manifest repository into a temporary directory.
        tempdir = join(west_dir, 'manifest-tmp')
        if exists(tempdir):
            log.dbg('removing existing temporary manifest directory', tempdir)
            shutil.rmtree(tempdir)
        try:
            self.clone_manifest(manifest_url, manifest_rev, tempdir)
        except subprocess.CalledProcessError:
            shutil.rmtree(tempdir, ignore_errors=True)
            raise

        # Verify the manifest file exists.
        temp_manifest = join(tempdir, 'west.yml')
        if not exists(temp_manifest):
            log.die(f'can\'t init: no "west.yml" found in {tempdir}\n'
                    f'  Hint: check --manifest-url={manifest_url} and '
                    '--manifest-rev={manifest_rev}\n'
                    f'  You may need to remove {west_dir} before retrying.')

        # Parse the manifest to get the manifest path, if it declares one.
        # Otherwise, use the URL. Ignore imports -- all we really
        # want to know is if there's a "self: path:" or not.
        projects = Manifest.from_file(temp_manifest,
                                      import_flags=ImportFlag.IGNORE,
                                      topdir=topdir).projects
        manifest_project = projects[MANIFEST_PROJECT_INDEX]
        if manifest_project.path:
            manifest_path = manifest_project.path
        else:
            manifest_path = posixpath.basename(urlparse(manifest_url).path)
        manifest_abspath = join(topdir, manifest_path)

        log.dbg('moving', tempdir, 'to', manifest_abspath,
                level=log.VERBOSE_EXTREME)
        try:
            shutil.move(tempdir, manifest_abspath)
        except shutil.Error as e:
            log.die(e)
        log.small_banner('setting manifest.path to', manifest_path)
        update_config('manifest', 'path', manifest_path, topdir=topdir)

        return topdir
Ejemplo n.º 5
0
    def get_projects(self, project_ids, allow_paths=True, only_cloned=False):
        '''Get a list of `Project` objects in the manifest from
        *project_ids*.

        If *project_ids* is empty, a copy of ``self.projects``
        attribute is returned as a list. Otherwise, the returned list
        has projects in the same order as *project_ids*.

        ``ValueError`` is raised if:

            - *project_ids* contains unknown project IDs

            - (with *only_cloned*) an uncloned project was found

        The ``ValueError`` *args* attribute is a 2-tuple with a list
        of unknown *project_ids* at index 0, and a list of uncloned
        `Project` objects at index 1.

        :param project_ids: a sequence of projects, identified by name
            (these are matched first) or path (as a fallback, but only
            with *allow_paths*)
        :param allow_paths: if true, *project_ids* may also contain
            relative or absolute project paths
        :param only_cloned: raise an exception for uncloned projects
        '''
        projects = list(self.projects)
        unknown = []  # all project_ids which don't resolve to a Project
        uncloned = []  # if only_cloned, resolved Projects which aren't cloned
        ret = []  # result list of resolved Projects

        # If no project_ids are specified, use all projects.
        if not project_ids:
            if only_cloned:
                uncloned = [p for p in projects if not p.is_cloned()]
                if uncloned:
                    raise ValueError(unknown, uncloned)
            return projects

        # Otherwise, resolve each of the project_ids to a project,
        # returning the result or raising ValueError.
        for pid in project_ids:
            project = self._projects_by_name.get(pid)
            if project is None and allow_paths:
                project = self._projects_by_cpath.get(util.canon_path(pid))

            if project is None:
                unknown.append(pid)
            else:
                ret.append(project)

                if only_cloned and not project.is_cloned():
                    uncloned.append(project)

        if unknown or (only_cloned and uncloned):
            raise ValueError(unknown, uncloned)
        return ret
Ejemplo n.º 6
0
def _location(cfg, topdir=None):
    # Making this a function that gets called each time you ask for a
    # configuration file makes it respect updated environment
    # variables (such as XDG_CONFIG_HOME, PROGRAMDATA) if they're set
    # during the program lifetime.
    #
    # Its existence is also relied on in the test cases, to ensure
    # that the WEST_CONFIG_xyz variables are respected and we're not about
    # to clobber the user's own configuration files.
    env = os.environ

    if cfg == ConfigFile.ALL:
        raise ValueError('ConfigFile.ALL has no location')
    elif cfg == ConfigFile.SYSTEM:
        if 'WEST_CONFIG_SYSTEM' in env:
            return env['WEST_CONFIG_SYSTEM']

        plat = platform.system()
        if plat == 'Linux':
            return '/etc/westconfig'
        elif plat == 'Darwin':
            return '/usr/local/etc/westconfig'
        elif plat == 'Windows':
            return os.path.expandvars('%PROGRAMDATA%\\west\\config')
        elif 'BSD' in plat:
            return '/etc/westconfig'
        elif 'CYGWIN' in plat:
            # Cygwin can handle windows style paths, so make sure we
            # return one. We don't want to use os.path.join because
            # that uses '/' as separator character, and the ProgramData
            # variable is likely to be something like r'C:\ProgramData'.
            #
            # See https://github.com/zephyrproject-rtos/west/issues/300
            # for details.
            pd = pathlib.PureWindowsPath(os.environ['ProgramData'])
            return str(pd / 'west' / 'config')
        else:
            raise ValueError('unsupported platform ' + plat)
    elif cfg == ConfigFile.GLOBAL:
        if 'WEST_CONFIG_GLOBAL' in env:
            return env['WEST_CONFIG_GLOBAL']
        elif platform.system() == 'Linux' and 'XDG_CONFIG_HOME' in env:
            return os.path.join(env['XDG_CONFIG_HOME'], 'west', 'config')
        else:
            return canon_path(
                os.path.join(os.path.expanduser('~'), '.westconfig'))
    elif cfg == ConfigFile.LOCAL:
        if topdir:
            return os.path.join(topdir, '.west', 'config')
        elif 'WEST_CONFIG_LOCAL' in env:
            return env['WEST_CONFIG_LOCAL']
        else:
            # Might raise WestNotFound!
            return os.path.join(west_dir(), 'config')
    else:
        raise ValueError(f'invalid configuration file {cfg}')
Ejemplo n.º 7
0
def config_tmpdir(tmpdir):
    # Fixture for running from a temporary directory with a .west
    # inside. We also:
    #
    # - ensure we're being run under tox, to avoid messing with
    #   the user's actual configuration files
    # - ensure configuration files point where they should inside
    #   the temporary tox directories, for the same reason
    # - ensure ~ exists (since the test environment
    #   doesn't let us run in the true user $HOME).
    # - set WEST_CONFIG_SYSTEM to lie inside the tmpdir (to avoid
    #   interacting with the real system file)
    # - set ZEPHYR_BASE (to avoid complaints in subcommand stderr)
    #
    # Using this makes the tests run faster than if we used
    # west_init_tmpdir from conftest.py, and also ensures that the
    # configuration code doesn't depend on features like the existence
    # of a manifest file, helping separate concerns.
    assert 'TOXTEMPDIR' in os.environ, 'you must run tests using tox'
    toxtmp = os.environ['TOXTEMPDIR']
    toxhome = canon_path(os.path.join(toxtmp, 'pytest-home'))
    global_loc = canon_path(config._location(GLOBAL))
    assert canon_path(os.path.expanduser('~')) == toxhome
    assert global_loc == os.path.join(toxhome, '.westconfig')
    os.makedirs(toxhome, exist_ok=True)
    if os.path.exists(global_loc):
        os.remove(global_loc)
    tmpdir.mkdir('.west')
    tmpdir.chdir()

    # Make sure the 'pytest' section is not present. If it is,
    # something is wrong in either the test environment (e.g. the user
    # has a system file with a 'pytest' section in it) or the tests
    # (if we're not setting ourselves up properly)
    os.environ['ZEPHYR_BASE'] = str(tmpdir.join('no-zephyr-here'))
    os.environ['WEST_CONFIG_SYSTEM'] = str(tmpdir.join('config.system'))
    if 'pytest' in cfg():
        del os.environ['ZEPHYR_BASE']
        assert False, 'bad fixture setup'
    yield tmpdir
    del os.environ['ZEPHYR_BASE']
    del os.environ['WEST_CONFIG_SYSTEM']
Ejemplo n.º 8
0
def _ensure_config(configfile, topdir):
    # Ensure the given configfile exists, returning its path. May
    # raise permissions errors, WestNotFound, etc.
    loc = _location(configfile, topdir=topdir)
    path = pathlib.Path(loc)

    if path.is_file():
        return loc

    path.parent.mkdir(parents=True, exist_ok=True)
    path.touch(exist_ok=True)
    return canon_path(str(path))
Ejemplo n.º 9
0
def _location(cfg, topdir=None):
    # Making this a function that gets called each time you ask for a
    # configuration file makes it respect updated environment
    # variables (such as XDG_CONFIG_HOME, PROGRAMDATA) if they're set
    # during the program lifetime.
    #
    # Its existence is also relied on in the test cases, to ensure
    # that the WEST_CONFIG_xyz variables are respected and we're not about
    # to clobber the user's own configuration files.
    env = os.environ

    if cfg == ConfigFile.ALL:
        raise ValueError('ConfigFile.ALL has no location')
    elif cfg == ConfigFile.SYSTEM:
        if 'WEST_CONFIG_SYSTEM' in env:
            return env['WEST_CONFIG_SYSTEM']

        plat = platform.system()
        if plat == 'Linux':
            return '/etc/westconfig'
        elif plat == 'Darwin':
            return '/usr/local/etc/westconfig'
        elif plat == 'Windows':
            return os.path.expandvars('%PROGRAMDATA%\\west\\config')
        elif 'BSD' in plat:
            return '/etc/westconfig'
        else:
            raise ValueError('unsupported platform ' + plat)
    elif cfg == ConfigFile.GLOBAL:
        if 'WEST_CONFIG_GLOBAL' in env:
            return env['WEST_CONFIG_GLOBAL']
        elif platform.system() == 'Linux' and 'XDG_CONFIG_HOME' in env:
            return os.path.join(env['XDG_CONFIG_HOME'], 'west', 'config')
        else:
            return canon_path(
                os.path.join(os.path.expanduser('~'), '.westconfig'))
    elif cfg == ConfigFile.LOCAL:
        if topdir:
            return os.path.join(topdir, '.west', 'config')
        elif 'WEST_CONFIG_LOCAL' in env:
            return env['WEST_CONFIG_LOCAL']
        else:
            # Might raise WestNotFound!
            return os.path.join(west_dir(), 'config')
    else:
        raise ValueError('invalid configuration file {}'.format(cfg))
Ejemplo n.º 10
0
    def bootstrap(self, args):
        manifest_url = args.manifest_url or MANIFEST_URL_DEFAULT
        manifest_rev = args.manifest_rev or MANIFEST_REV_DEFAULT
        topdir = util.canon_path(args.directory or os.getcwd())
        west_dir = join(topdir, WEST_DIR)

        log.banner('Initializing in', topdir)
        if not isdir(topdir):
            self.create(topdir, exist_ok=False)
        os.chdir(topdir)

        # Clone the manifest repository into a temporary directory. It's
        # important that west_dir exists and we're under topdir, or we
        # won't be able to call self.projects() without error later.
        tempdir = join(west_dir, 'manifest-tmp')
        if exists(tempdir):
            log.dbg('removing existing temporary manifest directory', tempdir)
            shutil.rmtree(tempdir)
        try:
            self.clone_manifest(manifest_url, manifest_rev, tempdir)
            temp_manifest_file = join(tempdir, 'west.yml')
            if not exists(temp_manifest_file):
                log.die('No "west.yml" in manifest repository ({})'.format(
                    tempdir))

            projects = self.projects(temp_manifest_file)
            manifest_project = projects[MANIFEST_PROJECT_INDEX]
            if manifest_project.path:
                manifest_path = manifest_project.path
                manifest_abspath = join(topdir, manifest_path)
            else:
                url_path = urlparse(manifest_url).path
                manifest_path = posixpath.basename(url_path)
                manifest_abspath = join(topdir, manifest_path)

            shutil.move(tempdir, manifest_abspath)
            update_config('manifest', 'path', manifest_path)
        finally:
            shutil.rmtree(tempdir, ignore_errors=True)

        self.topdir = topdir

        return projects, manifest_project.abspath
Ejemplo n.º 11
0
def _location(cfg):
    # Making this a function that gets called each time you ask for a
    # configuration file makes it respect updated environment
    # variables (such as XDG_CONFIG_HOME, PROGRAMDATA) if they're set
    # during the program lifetime. It also makes it easier to
    # monkey-patch for testing :).
    env = os.environ

    if cfg == ConfigFile.ALL:
        raise ValueError('ConfigFile.ALL has no location')
    elif cfg == ConfigFile.SYSTEM:
        if 'WEST_CONFIG_SYSTEM' in os.environ:
            return os.environ['WEST_CONFIG_SYSTEM']

        plat = platform.system()
        if plat == 'Linux':
            return '/etc/westconfig'
        elif plat == 'Darwin':
            return '/usr/local/etc/westconfig'
        elif plat == 'Windows':
            return os.path.expandvars('%PROGRAMDATA%\\west\\config')
        elif 'BSD' in plat:
            return '/etc/westconfig'
        else:
            raise ValueError('unsupported platform ' + plat)
    elif cfg == ConfigFile.GLOBAL:
        if 'WEST_CONFIG_GLOBAL' in os.environ:
            return os.environ['WEST_CONFIG_GLOBAL']
        elif platform.system() == 'Linux' and 'XDG_CONFIG_HOME' in env:
            return os.path.join(env['XDG_CONFIG_HOME'], 'west', 'config')
        else:
            return canon_path(
                os.path.join(os.path.expanduser('~'), '.westconfig'))
    elif cfg == ConfigFile.LOCAL:
        if 'WEST_CONFIG_LOCAL' in os.environ:
            return os.environ['WEST_CONFIG_LOCAL']
        else:
            # Might raise WestNotFound!
            return os.path.join(west_dir(), 'config')
    else:
        raise ValueError('invalid configuration file {}'.format(cfg))
Ejemplo n.º 12
0
    def local(self, args):
        if args.manifest_rev is not None:
            log.die('--mr cannot be used with -l')

        manifest_dir = util.canon_path(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)

        if not exists(manifest_file):
            log.die(f'can\'t init: no "west.yml" found in {manifest_dir}')

        log.banner('Initializing from existing manifest repository',
                   rel_manifest)
        log.small_banner(f'Creating {west_dir} and local configuration file')
        self.create(west_dir)
        os.chdir(topdir)
        update_config('manifest', 'path', rel_manifest)

        return topdir
Ejemplo n.º 13
0
def _ensure_config(configfile):
    # Ensure the given configfile exists, returning its path. May
    # raise permissions errors, WestNotFound, etc.
    #
    # Uses pathlib as this is hard to implement correctly without it.
    loc = _location(configfile)
    path = pathlib.Path(loc)

    if path.is_file():
        return loc

    # Create the directory. We can't use
    #     path.parent.mkdir(..., exist_ok=True)
    # in Python 3.4, so roughly emulate its behavior.
    try:
        path.parent.mkdir(parents=True)
    except FileExistsError:
        pass

    path.touch(exist_ok=True)
    return canon_path(str(path))
Ejemplo n.º 14
0
    def _load(self, data, topdir, path_hint):
        # Initialize this instance's fields from values given in the
        # manifest data, which must be validated according to the schema.
        projects = []
        project_names = set()
        project_paths = set()

        # Create the ManifestProject instance and install it into the
        # Project hierarchy.
        manifest = data.get('manifest')
        slf = manifest.get('self', dict())  # the self name is already taken
        if self.path:
            # We're parsing a real file on disk. We currently require
            # that we are able to resolve a topdir. We may lift this
            # restriction in the future.
            assert topdir
        mproj = ManifestProject(path=slf.get('path', path_hint),
                                topdir=topdir,
                                west_commands=slf.get('west-commands'))
        projects.insert(MANIFEST_PROJECT_INDEX, mproj)

        # Set the topdir attribute based on the results of the above.
        self.topdir = topdir

        # Map from each remote's name onto that remote's data in the manifest.
        remotes = tuple(
            Remote(r['name'], r['url-base'])
            for r in manifest.get('remotes', []))
        remotes_dict = {r.name: r for r in remotes}

        # Get any defaults out of the manifest.
        #
        # md = manifest defaults (dictionary with values parsed from
        # the manifest)
        md = manifest.get('defaults', dict())
        mdrem = md.get('remote')
        if mdrem:
            # The default remote name, if provided, must refer to a
            # well-defined remote.
            if mdrem not in remotes_dict:
                self._malformed(
                    'default remote {} is not defined'.format(mdrem))
            default_remote = remotes_dict[mdrem]
            default_remote_name = mdrem
        else:
            default_remote = None
            default_remote_name = None
        defaults = Defaults(remote=default_remote, revision=md.get('revision'))

        # pdata = project data (dictionary of project information parsed from
        # the manifest file)
        for pdata in manifest['projects']:
            # Validate the project name.
            name = pdata['name']

            # Validate the project remote or URL.
            remote_name = pdata.get('remote')
            url = pdata.get('url')
            repo_path = pdata.get('repo-path')
            if remote_name is None and url is None:
                if default_remote_name is None:
                    self._malformed(
                        'project {} has no remote or URL (no default is set)'.
                        format(name))
                else:
                    remote_name = default_remote_name
            if remote_name:
                if remote_name not in remotes_dict:
                    self._malformed(
                        'project {} remote {} is not defined'.format(
                            name, remote_name))
                remote = remotes_dict[remote_name]
            else:
                remote = None

            # Create the project instance for final checking.
            try:
                project = Project(name,
                                  defaults,
                                  path=pdata.get('path'),
                                  clone_depth=pdata.get('clone-depth'),
                                  revision=pdata.get('revision'),
                                  west_commands=pdata.get('west-commands'),
                                  remote=remote,
                                  repo_path=repo_path,
                                  topdir=topdir,
                                  url=url)
            except ValueError as ve:
                self._malformed(ve.args[0])

            # The name "manifest" cannot be used as a project name; it
            # is reserved to refer to the manifest repository itself
            # (e.g. from "west list"). Note that this has not always
            # been enforced, but it is part of the documentation.
            if project.name == 'manifest':
                self._malformed('no project can be named "manifest"')
            # Project names must be unique.
            if project.name in project_names:
                self._malformed('project name {} is already used'.format(
                    project.name))
            # Two projects cannot have the same path. We use a PurePath
            # comparison here to ensure that platform-specific canonicalization
            # rules are handled correctly.
            if PurePath(project.path) in project_paths:
                self._malformed('project {} path {} is already in use'.format(
                    project.name, project.path))
            else:
                project_paths.add(PurePath(project.path))

            project_names.add(project.name)
            projects.append(project)

        self.defaults = defaults
        self.remotes = remotes
        self._remotes_dict = remotes_dict
        self.projects = tuple(projects)
        self._proj_name_map = {p.name: p for p in self.projects}
        pmap = dict()
        if self.topdir:
            if mproj.abspath:
                pmap[util.canon_path(mproj.abspath)] = mproj
            for p in self.projects[MANIFEST_PROJECT_INDEX + 1:]:
                assert p.abspath  # sanity check a program invariant
                pmap[util.canon_path(p.abspath)] = p
        self._proj_canon_path_map = pmap
Ejemplo n.º 15
0
    def get_projects(self, project_ids, allow_paths=True, only_cloned=False):
        '''Get a list of Projects in the manifest from given *project_ids*.

        If *project_ids* is empty, a list containing all the
        manifest's projects is returned. The manifest is present
        as a project in this list at *MANIFEST_PROJECT_INDEX*, and the
        other projects follow in the order they appear in the manifest
        file.

        Otherwise, projects in the returned list are in the same order
        as specified in *project_ids*.

        Either of these situations raises a ValueError:

        - One or more non-existent projects is in *project_ids*
        - only_cloned is True, and the returned list would have
          contained one or more uncloned projects

        On error, the *args* attribute of the ValueError is a 2-tuple,
        containing a list of unknown project_ids at index 0, and a
        list of uncloned Projects at index 1.

        :param project_ids: A sequence of projects, identified by name
                            (at first priority) or path (as a
                            fallback, when allow_paths=True).
        :param allow_paths: If False, project_ids must be a sequence of
                            project names only; paths are not allowed.
        :param only_cloned: If True, ValueError is raised if an
                            uncloned project would have been returned.

        '''
        projects = list(self.projects)
        unknown = []  # all project_ids which don't resolve to a Project
        uncloned = []  # if only_cloned, resolved Projects which aren't cloned
        ret = []  # result list of resolved Projects

        # If no project_ids are specified, use all projects.
        if not project_ids:
            if only_cloned:
                uncloned = [p for p in projects if not p.is_cloned()]
                if uncloned:
                    raise ValueError(unknown, uncloned)
            return projects

        # Otherwise, resolve each of the project_ids to a project,
        # returning the result or raising ValueError.
        for pid in project_ids:
            project = self._proj_name_map.get(pid)
            if project is None and allow_paths:
                project = self._proj_canon_path_map.get(util.canon_path(pid))

            if project is None:
                unknown.append(pid)
            else:
                ret.append(project)

                if only_cloned and not project.is_cloned():
                    uncloned.append(project)

        if unknown or (only_cloned and uncloned):
            raise ValueError(unknown, uncloned)
        return ret
Ejemplo n.º 16
0
    def _load(self, data):
        # Initialize this instance's fields from values given in the
        # manifest data, which must be validated according to the schema.
        projects = []
        project_names = set()
        project_abspaths = set()

        manifest = data.get('manifest')

        path = config.get('manifest', 'path', fallback=None)

        self_tag = manifest.get('self')
        if path is None:
            path = self_tag.get('path') if self_tag else ''
        west_commands = self_tag.get('west-commands') if self_tag else None

        project = ManifestProject(path=path, west_commands=west_commands)
        projects.insert(MANIFEST_PROJECT_INDEX, project)

        # Map from each remote's name onto that remote's data in the manifest.
        remotes = tuple(Remote(r['name'], r['url-base']) for r in
                        manifest.get('remotes', []))
        remotes_dict = {r.name: r for r in remotes}

        # Get any defaults out of the manifest.
        #
        # md = manifest defaults (dictionary with values parsed from
        # the manifest)
        md = manifest.get('defaults', dict())
        mdrem = md.get('remote')
        if mdrem:
            # The default remote name, if provided, must refer to a
            # well-defined remote.
            if mdrem not in remotes_dict:
                self._malformed('default remote {} is not defined'.
                                format(mdrem))
            default_remote = remotes_dict[mdrem]
            default_remote_name = mdrem
        else:
            default_remote = None
            default_remote_name = None
        defaults = Defaults(remote=default_remote, revision=md.get('revision'))

        # mp = manifest project (dictionary with values parsed from
        # the manifest)
        for mp in manifest['projects']:
            # Validate the project name.
            name = mp['name']

            # Validate the project remote or URL.
            remote_name = mp.get('remote')
            url = mp.get('url')
            repo_path = mp.get('repo-path')
            if remote_name is None and url is None:
                if default_remote_name is None:
                    self._malformed(
                        'project {} has no remote or URL (no default is set)'.
                        format(name))
                else:
                    remote_name = default_remote_name
            if remote_name:
                if remote_name not in remotes_dict:
                    self._malformed('project {} remote {} is not defined'.
                                    format(name, remote_name))
                remote = remotes_dict[remote_name]
            else:
                remote = None

            # Create the project instance for final checking.
            try:
                project = Project(name,
                                  defaults,
                                  path=mp.get('path'),
                                  clone_depth=mp.get('clone-depth'),
                                  revision=mp.get('revision'),
                                  west_commands=mp.get('west-commands'),
                                  remote=remote,
                                  repo_path=repo_path,
                                  url=url)
            except ValueError as ve:
                self._malformed(ve.args[0])

            # Project names must be unique.
            if project.name in project_names:
                self._malformed('project name {} is already used'.
                                format(project.name))
            # Two projects cannot have the same path. We use absolute
            # paths to check for collisions to ensure paths are
            # normalized (e.g. for case-insensitive file systems or
            # in cases like on Windows where / or \ may serve as a
            # path component separator).
            if project.abspath in project_abspaths:
                self._malformed('project {} path {} is already in use'.
                                format(project.name, project.path))

            project_names.add(project.name)
            project_abspaths.add(project.abspath)
            projects.append(project)

        self.defaults = defaults
        self.remotes = remotes
        self._remotes_dict = remotes_dict
        self.projects = tuple(projects)
        self._proj_name_map = {p.name: p for p in self.projects}
        self._proj_canon_path_map = {util.canon_path(p.abspath): p
                                     for p in self.projects}