blob: 62236fd89dacc196191de616200dc43272ba586d [file] [log] [blame]
# Copyright 2010 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
__all__ = (
'features_set',
)
import logging
from portage.const import SUPPORTED_FEATURES
from portage.localization import _
from portage.output import colorize
from portage.util import writemsg_level
class features_set(object):
"""
Provides relevant set operations needed for access and modification of
config.features. The FEATURES variable is automatically synchronized
upon modification.
Modifications result in a permanent override that will cause the change
to propagate to the incremental stacking mechanism in config.regenerate().
This eliminates the need to call config.backup_changes() when FEATURES
is modified, since any overrides are guaranteed to persist despite calls
to config.reset().
"""
def __init__(self, settings):
self._settings = settings
self._features = set()
def __contains__(self, k):
return k in self._features
def __iter__(self):
return iter(self._features)
def _sync_env_var(self):
self._settings['FEATURES'] = ' '.join(sorted(self._features))
def add(self, k):
self._settings.modifying()
self._settings._features_overrides.append(k)
if k not in self._features:
self._features.add(k)
self._sync_env_var()
def update(self, values):
self._settings.modifying()
values = list(values)
self._settings._features_overrides.extend(values)
need_sync = False
for k in values:
if k in self._features:
continue
self._features.add(k)
need_sync = True
if need_sync:
self._sync_env_var()
def difference_update(self, values):
self._settings.modifying()
values = list(values)
self._settings._features_overrides.extend('-' + k for k in values)
remove_us = self._features.intersection(values)
if remove_us:
self._features.difference_update(values)
self._sync_env_var()
def remove(self, k):
"""
This never raises KeyError, since it records a permanent override
that will prevent the given flag from ever being added again by
incremental stacking in config.regenerate().
"""
self.discard(k)
def discard(self, k):
self._settings.modifying()
self._settings._features_overrides.append('-' + k)
if k in self._features:
self._features.remove(k)
self._sync_env_var()
def _validate(self):
"""
Implements unknown-features-warn and unknown-features-filter.
"""
if 'unknown-features-warn' in self._features:
unknown_features = \
self._features.difference(SUPPORTED_FEATURES)
if unknown_features:
unknown_features = unknown_features.difference(
self._settings._unknown_features)
if unknown_features:
self._settings._unknown_features.update(unknown_features)
writemsg_level(colorize("BAD",
_("FEATURES variable contains unknown value(s): %s") % \
", ".join(sorted(unknown_features))) \
+ "\n", level=logging.WARNING, noiselevel=-1)
if 'unknown-features-filter' in self._features:
unknown_features = \
self._features.difference(SUPPORTED_FEATURES)
if unknown_features:
self.difference_update(unknown_features)
self._prune_overrides()
def _prune_overrides(self):
"""
If there are lots of invalid package.env FEATURES settings
then unknown-features-filter can make _features_overrides
grow larger and larger, so prune it. This performs incremental
stacking with preservation of negative values since they need
to persist for future config.regenerate() calls.
"""
overrides_set = set(self._settings._features_overrides)
positive = set()
negative = set()
for x in self._settings._features_overrides:
if x[:1] == '-':
positive.discard(x[1:])
negative.add(x[1:])
else:
positive.add(x)
negative.discard(x)
self._settings._features_overrides[:] = \
list(positive) + list('-' + x for x in negative)