Beispiel #1
0
def config_from_file(file_obj):
    """
    generate a config dict

    :param file_obj: file protocol instance
    :return: :obj:`snakeoil.mappings.LazyValDict` instance
    """
    cparser = CaseSensitiveConfigParser()
    try:
        cparser.read_file(file_obj)
    except configparser.ParsingError as e:
        raise errors.ParsingError(f'while parsing {file_obj}', e) from e

    def get_section(section):
        return basics.ConfigSectionFromStringDict(dict(cparser.items(section)))

    return mappings.LazyValDict(cparser.sections, get_section)
Beispiel #2
0
    def load_make_conf(vars_dict,
                       path,
                       allow_sourcing=False,
                       required=True,
                       allow_recurse=True,
                       incrementals=False):
        """parse make.conf files

        Args:
            vars_dict (dict): dictionary to add parsed variables to
            path (str): path to the make.conf which can be a regular file or
                directory, if a directory is passed all the non-hidden files within
                that directory are parsed in alphabetical order.
        """
        sourcing_command = 'source' if allow_sourcing else None

        if allow_recurse:
            files = sorted_scan(os.path.realpath(path),
                                follow_symlinks=True,
                                nonexistent=True,
                                hidden=False,
                                backup=False)
        else:
            files = (path, )

        for fp in files:
            try:
                new_vars = read_bash_dict(fp,
                                          vars_dict=vars_dict,
                                          sourcing_command=sourcing_command)
            except PermissionError as e:
                raise base_errors.PermissionDenied(fp, write=False) from e
            except EnvironmentError as e:
                if e.errno != errno.ENOENT or required:
                    raise config_errors.ParsingError(f"parsing {fp!r}",
                                                     exception=e) from e
                return

            if incrementals:
                for key in econst.incrementals:
                    if key in vars_dict and key in new_vars:
                        new_vars[key] = f"{vars_dict[key]} {new_vars[key]}"
            # quirk of read_bash_dict; it returns only what was mutated.
            vars_dict.update(new_vars)
Beispiel #3
0
def config_from_file(file_obj):
    """
    generate a config dict

    :param file_obj: file protocol instance
    :return: :obj:`snakeoil.mappings.LazyValDict` instance
    """
    cparser = CaseSensitiveConfigParser()
    try:
        if sys.hexversion < 0x03020000:
            cparser.readfp(file_obj)
        else:
            cparser.read_file(file_obj)
    except ConfigParser.ParsingError as pe:
        raise errors.ParsingError("while parsing %s" % (file_obj, ), pe)

    def get_section(section):
        return basics.ConfigSectionFromStringDict(dict(cparser.items(section)))

    return mappings.LazyValDict(cparser.sections, get_section)
Beispiel #4
0
    def _atoms(self):
        try:
            s = set()
            for x in readlines_ascii(self.path, True):
                if not x or x.startswith("#"):
                    continue
                elif x.startswith("@"):
                    if self.error_on_subsets:
                        raise ValueError(
                            "set %s isn't a valid atom in pkgset %r" %
                            (x, self.path))
                    logger.warning(
                        "set item %r found in pkgset %r: it will be "
                        "wiped on update since portage/pkgcore store set items "
                        "in a separate way", x[1:], self.path)
                    continue
                s.add(atom(x))
        except InvalidDependency as e:
            compatibility.raise_from(
                errors.ParsingError("parsing %r" % self.path, exception=e))

        return s
Beispiel #5
0
    def __init__(self, location=None, profile_override=None, **kwargs):
        """
        Args:
            location (optional[str]): path to the portage config directory,
                (defaults to /etc/portage)
            profile_override (optional[str]): profile to use instead of the current system
                profile, i.e. the target of the /etc/portage/make.profile symlink
            configroot (optional[str]): location for various portage config files (defaults to /)
            root (optional[str]): target root filesystem (defaults to /)
            buildpkg (optional[bool]): forcibly disable/enable building binpkgs, otherwise
                FEATURES=buildpkg from make.conf is used

        Returns:
            dict: config settings
        """
        self._config = {}
        location = location if location is not None else '/etc/portage'
        self.dir = pjoin(
            os.environ.get('PORTAGE_CONFIGROOT', kwargs.pop('configroot',
                                                            '/')),
            location.lstrip('/'))

        # this actually differs from portage parsing- we allow
        # make.globals to provide vars used in make.conf, portage keeps
        # them separate (kind of annoying)
        #
        # this isn't preserving incremental behaviour for features/use unfortunately

        make_conf = {}
        try:
            self.load_make_conf(make_conf,
                                pjoin(const.CONFIG_PATH, 'make.globals'))
        except IGNORED_EXCEPTIONS:
            raise
        except Exception as e:
            raise config_errors.ParsingError(
                "failed to load make.globals") from e
        self.load_make_conf(make_conf,
                            pjoin(self.dir, 'make.conf'),
                            required=False,
                            allow_sourcing=True,
                            incrementals=True)

        self.root = os.environ.get(
            "ROOT", kwargs.pop('root', make_conf.get("ROOT", "/")))
        gentoo_mirrors = [
            x.rstrip("/") + "/distfiles"
            for x in make_conf.pop("GENTOO_MIRRORS", "").split()
        ]

        self.features = frozenset(
            optimize_incrementals(make_conf.get('FEATURES', '').split()))

        self._add_sets()
        self._add_profile(profile_override)

        self['vdb'] = basics.AutoConfigSection({
            'class':
            'pkgcore.vdb.ondisk.tree',
            'location':
            pjoin(self.root, 'var', 'db', 'pkg'),
            'cache_location':
            '/var/cache/edb/dep/var/db/pkg',
        })

        try:
            repos_conf_defaults, repos_conf = self.load_repos_conf(
                pjoin(self.dir, 'repos.conf'))
        except config_errors.ParsingError as e:
            if not getattr(getattr(e, 'exc', None), 'errno',
                           None) == errno.ENOENT:
                raise
            try:
                # fallback to defaults provided by pkgcore
                repos_conf_defaults, repos_conf = self.load_repos_conf(
                    pjoin(const.CONFIG_PATH, 'repos.conf'))
            except IGNORED_EXCEPTIONS:
                raise
            except Exception as e:
                raise config_errors.ParsingError(
                    'failed to find a usable repos.conf') from e

        self['ebuild-repo-common'] = basics.AutoConfigSection({
            'class':
            'pkgcore.ebuild.repository.tree',
            'default_mirrors':
            gentoo_mirrors,
            'inherit-only':
            True,
        })

        repo_map = {}

        for repo_name, repo_opts in list(repos_conf.items()):
            repo_cls = repo_opts.pop('repo-type')
            try:
                repo = repo_cls(self,
                                repo_name=repo_name,
                                repo_opts=repo_opts,
                                repo_map=repo_map,
                                defaults=repos_conf_defaults)
            except repo_errors.UnsupportedRepo as e:
                logger.warning(
                    f'skipping {repo_name!r} repo: unsupported EAPI {str(e.repo.eapi)!r}'
                )
                del repos_conf[repo_name]
                continue

            self[repo_name] = basics.AutoConfigSection(repo)

        # XXX: Hack for portage-2 profile format support. We need to figure out how
        # to dynamically create this from the config at runtime on attr access.
        profiles.ProfileNode._repo_map = ImmutableDict(repo_map)

        self._make_repo_syncers(repos_conf, make_conf)
        repos = [name for name in repos_conf.keys()]
        if repos:
            self['repo-stack'] = basics.FakeIncrementalDictConfigSection(
                my_convert_hybrid, {
                    'class': 'pkgcore.repository.multiplex.config_tree',
                    'repos': tuple(repos)
                })

            self['vuln'] = basics.AutoConfigSection({
                'class': SecurityUpgradesViaProfile,
                'ebuild_repo': 'repo-stack',
                'vdb': 'vdb',
                'profile': 'profile',
            })

        # check if package building was forced on by the user
        forced_buildpkg = kwargs.pop('buildpkg', False)
        if forced_buildpkg:
            make_conf['FEATURES'] += ' buildpkg'

        # now add the fetcher- we delay it till here to clean out the environ
        # it passes to the command.
        # *everything* in make_conf must be str values also.
        self._add_fetcher(make_conf)

        # finally... domain.
        make_conf.update({
            'class': 'pkgcore.ebuild.domain.domain',
            'repos': tuple(repos),
            'fetcher': 'fetcher',
            'default': True,
            'vdb': ('vdb', ),
            'profile': 'profile',
            'name': 'livefs',
            'root': self.root,
            'config_dir': self.dir,
        })

        self['livefs'] = basics.FakeIncrementalDictConfigSection(
            my_convert_hybrid, make_conf)
Beispiel #6
0
    def load_repos_conf(cls, path):
        """parse repos.conf files

        Args:
            path (str): path to the repos.conf which can be a regular file or
                directory, if a directory is passed all the non-hidden files within
                that directory are parsed in alphabetical order.

        Returns:
            dict: global repo settings
            dict: repo settings
        """
        main_defaults = {}
        repos = {}

        parser = ParseConfig()

        for fp in sorted_scan(os.path.realpath(path),
                              follow_symlinks=True,
                              nonexistent=True,
                              hidden=False,
                              backup=False):
            try:
                with open(fp) as f:
                    defaults, repo_confs = parser.parse_file(f)
            except PermissionError as e:
                raise base_errors.PermissionDenied(fp, write=False) from e
            except EnvironmentError as e:
                raise config_errors.ParsingError(f"parsing {fp!r}",
                                                 exception=e) from e
            except configparser.Error as e:
                raise config_errors.ParsingError(f"repos.conf: {fp!r}",
                                                 exception=e) from e

            if defaults and main_defaults:
                logger.warning(
                    f"repos.conf: parsing {fp!r}: overriding DEFAULT section")
            main_defaults.update(defaults)

            for name, repo_conf in repo_confs.items():
                if name in repos:
                    logger.warning(
                        f"repos.conf: parsing {fp!r}: overriding {name!r} repo"
                    )

                # ignore repo if location is unset
                location = repo_conf.get('location', None)
                if location is None:
                    logger.warning(
                        f"repos.conf: parsing {fp!r}: "
                        f"{name!r} repo missing location setting, ignoring repo"
                    )
                    continue
                repo_conf['location'] = os.path.abspath(location)

                # repo type defaults to ebuild for compat with portage
                repo_type = repo_conf.get('repo-type', 'ebuild-v1')
                try:
                    repo_conf['repo-type'] = cls._supported_repo_types[
                        repo_type]
                except KeyError:
                    logger.warning(
                        f"repos.conf: parsing {fp!r}: "
                        f"{name!r} repo has unsupported repo-type {repo_type!r}, "
                        "ignoring repo")
                    continue

                # Priority defaults to zero if unset or invalid for ebuild repos
                # while binpkg repos have the lowest priority by default.
                priority = repo_conf.get('priority', None)
                if priority is None:
                    if repo_type.startswith('binpkg'):
                        priority = -10000
                    else:
                        priority = 0

                try:
                    priority = int(priority)
                except ValueError:
                    logger.warning(
                        f"repos.conf: parsing {fp!r}: {name!r} repo has invalid priority "
                        f"setting: {priority!r} (defaulting to 0)")
                    priority = 0
                finally:
                    repo_conf['priority'] = priority

                # register repo
                repos[name] = repo_conf

        if repos:
            # the default repo is gentoo if unset and gentoo exists
            default_repo = main_defaults.get('main-repo', 'gentoo')
            if default_repo not in repos:
                raise config_errors.UserConfigError(
                    f"repos.conf: default repo {default_repo!r} is undefined or invalid"
                )

            if 'main-repo' not in main_defaults:
                main_defaults['main-repo'] = default_repo

            # the default repo has a low priority if unset or zero
            if repos[default_repo]['priority'] == 0:
                repos[default_repo]['priority'] = -1000

        # sort repos via priority, in this case high values map to high priorities
        repos = OrderedDict((k, v) for k, v in sorted(
            repos.items(), key=lambda d: d[1]['priority'], reverse=True))

        return main_defaults, repos