--- .../package/ebuild/_config/InstallMaskManager.py | 59 ++++++++++++++++++++++ pym/portage/package/ebuild/config.py | 34 ++++++++++++- pym/portage/util/configparser.py | 19 ++++++- 3 files changed, 110 insertions(+), 2 deletions(-) create mode 100644 pym/portage/package/ebuild/_config/InstallMaskManager.py
diff --git a/pym/portage/package/ebuild/_config/InstallMaskManager.py b/pym/portage/package/ebuild/_config/InstallMaskManager.py new file mode 100644 index 0000000..96cb539 --- /dev/null +++ b/pym/portage/package/ebuild/_config/InstallMaskManager.py @@ -0,0 +1,59 @@ +# Copyright 2010-2016 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 + +__all__ = ( + 'InstallMaskManager', +) + +import sys + +from portage import os +from portage.localization import _ +from portage.util import writemsg +from portage.util.configparser import (SafeConfigParser, ConfigParserError, + MultiValueConfigParserDict, read_configs) + + +class InstallMaskManager(object): + def __init__(self, repositories, abs_user_config, user_config=True): + self._groups = {} + + # read repository defined groups + self._read_config_from_repositories(repositories) + + if user_config: + self._read_config(os.path.join(abs_user_config, 'install-mask.conf'), True) + + def _read_config_from_repositories(self, repositories): + for r in repositories.repos_with_profiles(): + self._read_config(os.path.join(r.location, 'metadata', 'install-mask.conf')) + + def _read_config(self, path, is_user_config=False): + # use separate parsers to detect collisions properly + cfp_kwargs = {} + if sys.hexversion >= 0x03020000: + cfp_kwargs['strict'] = False + parser = SafeConfigParser(dict_type=MultiValueConfigParserDict, + **cfp_kwargs) + try: + read_configs(parser, [path]) + except ConfigParserError as e: + writemsg( + _("!!! Error while reading %s: %s\n") % (path, e), + noiselevel=-1) + return + + for sname in parser.sections(): + if not is_user_config and sname in self._groups: + writemsg( + _("!!! Error while reading %s: duplicate group %s found\n") % (path, sname), + noiselevel=-1) + continue + if not parser.has_option(sname, 'path'): + continue + + paths = parser.get(sname, 'path').split('\n') + self._groups[sname] = paths + + def expand_group(self, gname): + return self._groups[gname] diff --git a/pym/portage/package/ebuild/config.py b/pym/portage/package/ebuild/config.py index 9d13703..dfbd7f2 100644 --- a/pym/portage/package/ebuild/config.py +++ b/pym/portage/package/ebuild/config.py @@ -60,6 +60,7 @@ from portage.package.ebuild._config.features_set import features_set from portage.package.ebuild._config.KeywordsManager import KeywordsManager from portage.package.ebuild._config.LicenseManager import LicenseManager from portage.package.ebuild._config.UseManager import UseManager +from portage.package.ebuild._config.InstallMaskManager import InstallMaskManager from portage.package.ebuild._config.LocationsManager import LocationsManager from portage.package.ebuild._config.MaskManager import MaskManager from portage.package.ebuild._config.VirtualsManager import VirtualsManager @@ -277,6 +278,7 @@ class config(object): # force instantiation of lazy immutable objects when cloning, so # that they're not instantiated more than once self._keywords_manager_obj = clone._keywords_manager + self._install_mask_manager_obj = clone._install_mask_manager self._mask_manager_obj = clone._mask_manager # shared mutable attributes @@ -329,6 +331,7 @@ class config(object): else: # lazily instantiated objects self._keywords_manager_obj = None + self._install_mask_manager_obj = None self._mask_manager_obj = None self._virtuals_manager_obj = None @@ -1032,6 +1035,15 @@ class config(object): return self._keywords_manager_obj @property + def _install_mask_manager(self): + if self._install_mask_manager_obj is None: + self._install_mask_manager_obj = InstallMaskManager( + self.repositories, + self._locations_manager.abs_user_config, + user_config=self.local_config) + return self._install_mask_manager_obj + + @property def _mask_manager(self): if self._mask_manager_obj is None: self._mask_manager_obj = MaskManager(self.repositories, @@ -1774,7 +1786,8 @@ class config(object): _eapi_cache.clear() # Prepare the final value of INSTALL_MASK - install_mask = self.get("INSTALL_MASK", "").split() + install_mask = list(self._replace_install_mask_groups( + self.get("INSTALL_MASK", "").split())) if 'nodoc' in self.features: install_mask.append("/usr/share/doc") if 'noinfo' in self.features: @@ -1782,6 +1795,25 @@ class config(object): if 'noman' in self.features: install_mask.append("/usr/share/man") self.install_mask = install_mask + print(install_mask) + + def _replace_install_mask_groups(self, vals): + for v in vals: + if v.startswith('@') or v.startswith('-@'): + neg = '-' if v.startswith('-') else '' + gname = v[2:] if neg else v[1:] + for p in self._expand_install_mask_group(gname): + yield '%s%s' % (neg, p) + else: + yield v + + def _expand_install_mask_group(self, gname): + try: + return self._install_mask_manager.expand_group(gname) + except KeyError: + writemsg(_("!!! Undefined INSTALL_MASK group: '%s'!\n") + % gname, noiselevel=-1) + return () def _grab_pkg_env(self, penv, container, protected_keys=None): if protected_keys is None: diff --git a/pym/portage/util/configparser.py b/pym/portage/util/configparser.py index c4c92a6..290bc5e 100644 --- a/pym/portage/util/configparser.py +++ b/pym/portage/util/configparser.py @@ -2,7 +2,8 @@ # Distributed under the terms of the GNU General Public License v2 __all__ = ['ConfigParserError', 'NoOptionError', 'ParsingError', - 'RawConfigParser', 'SafeConfigParser', 'read_configs'] + 'RawConfigParser', 'SafeConfigParser', 'read_configs', + 'MultiValueConfigParserDict'] # the following scary compatibility thing provides two classes: # - SafeConfigParser that provides safe interpolation for values, @@ -74,3 +75,19 @@ def read_configs(parser, paths): read_file(p, **kwargs) else: raise TypeError("Unsupported type %r of element %r of 'paths' argument" % (type(p), p)) + + +class MultiValueConfigParserDict(dict): + """ + A special variant of dict that stores all subsequent values assigned + to its keys as a list, and returns this list when retrieved. Meant + to be used with ConfigParser to process multi-key config files. + """ + + def __setitem__(self, k, v): + if isinstance(v, list): + if not k in self: + super(MultiValueConfigParserDict, self).__setitem__(k, []) + self[k].extend(v) + else: + super(MultiValueConfigParserDict, self).__setitem__(k, v) -- 2.8.3