Bug #326561 - Implement FEATURES=unknown-features-filter. This is not
authorZac Medico <zmedico@gentoo.org>
Tue, 31 Aug 2010 01:43:21 +0000 (18:43 -0700)
committerZac Medico <zmedico@gentoo.org>
Tue, 31 Aug 2010 01:43:21 +0000 (18:43 -0700)
enabled by default now, but it may be in the future, depending on feedback
from unknown-features-warn.

man/make.conf.5
pym/portage/const.py
pym/portage/package/ebuild/_config/features_set.py
pym/portage/package/ebuild/config.py

index ee0f1c1e951d6a1c8f566960e07aaf79155fb427..fb7ee85bba4421137f05b0b350a5d648ef143e43 100644 (file)
@@ -469,6 +469,9 @@ If a file is not claimed by another package in the same slot and it is not
 protected by \fICONFIG_PROTECT\fR, unmerge it even if the modification time or
 checksum differs from the file that was originally installed.
 .TP
+.B unknown\-features\-filter
+Filter out any unknown values that the FEATURES variable contains.
+.TP
 .B unknown\-features\-warn
 Warn if FEATURES contains one or more unknown values.
 .TP
index 7edb1f19fad6eda476160d4a09ef9085685b464f..cf24fcc3156b2b948ad3a5078c8daf28dc3334e2 100644 (file)
@@ -97,7 +97,7 @@ SUPPORTED_FEATURES       = frozenset([
                            "selinux", "sesandbox", "severe", "sfperms",
                            "sign", "skiprocheck", "split-elog", "split-log", "splitdebug",
                            "strict", "stricter", "suidctl", "test", "test-fail-continue",
-                           "unknown-features-warn",
+                           "unknown-features-filter", "unknown-features-warn",
                            "unmerge-logs", "unmerge-orphans", "userfetch", "userpriv",
                            "usersandbox", "usersync", "webrsync-gpg"])
 
index 7ab148fc8162361e1f999e9c91b3509edf6a178f..a39fb89dc7d00bac0e648f6f0474b4e8822d1e44 100644 (file)
@@ -5,6 +5,13 @@ __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
@@ -51,6 +58,15 @@ class features_set(object):
                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
@@ -65,3 +81,49 @@ class features_set(object):
                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)
+               if len(overrides_set) < len(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(negative)
index ab042a235ad62bfa4d9963cf4b66e4d13e679d1b..847ac1ccb0ab0093733bc1c39f501ea922ae74a9 100644 (file)
@@ -27,7 +27,7 @@ from portage import bsd_chflags, \
 from portage.const import CACHE_PATH, \
        DEPCACHE_PATH, INCREMENTALS, MAKE_CONF_FILE, \
        MODULES_FILE_PATH, PORTAGE_BIN_PATH, PORTAGE_PYM_PATH, \
-       PRIVATE_PATH, PROFILE_PATH, SUPPORTED_FEATURES, USER_CONFIG_PATH, \
+       PRIVATE_PATH, PROFILE_PATH, USER_CONFIG_PATH, \
        USER_VIRTUALS_FILE
 from portage.dbapi import dbapi
 from portage.dbapi.porttree import portdbapi
@@ -1911,19 +1911,7 @@ class config(object):
                        self.features = features_set(self)
                self.features._features.update(self.get('FEATURES', '').split())
                self.features._sync_env_var()
-
-               if 'unknown-features-warn' in self.features:
-                       unknown_features = \
-                               self.features._features.difference(SUPPORTED_FEATURES)
-                       if unknown_features:
-                               unknown_features = \
-                                       unknown_features.difference(self._unknown_features)
-                               if unknown_features:
-                                       self._unknown_features.update(unknown_features)
-                                       writemsg_level(colorize("BAD",
-                                               _("FEATURES variable contains unknown value(s): %s") % \
-                                               ", ".join(sorted(unknown_features))) \
-                                               + "\n", noiselevel=-1)
+               self.features._validate()
 
                myflags.update(self.useforce)
                arch = self.configdict["defaults"].get("ARCH")