Add ACCEPT_PROPERTIES variable which has default value * and can be set in
authorMounir Lamouri <volkmar@gentoo.org>
Tue, 11 Aug 2009 20:45:35 +0000 (20:45 -0000)
committerMounir Lamouri <volkmar@gentoo.org>
Tue, 11 Aug 2009 20:45:35 +0000 (20:45 -0000)
make.conf or /etc/portage/package.properties.
If ACCEPT_PROPERTIES doesn't match ebuild PROPERTIES, the ebuild is masked.

svn path=/main/trunk/; revision=13997

bin/isolated-functions.sh
pym/_emerge/visible.py
pym/portage/__init__.py
pym/portage/dbapi/porttree.py

index bb9b9a042a0543bd8796b1e91fcef99870a5d07f..cd60f05eecd32b0fe49b1ee1ab06e918fb636f90 100755 (executable)
@@ -538,7 +538,7 @@ save_ebuild_env() {
                        ${QA_INTERCEPTORS}
 
                # portage config variables and variables set directly by portage
-               unset ACCEPT_LICENSE BAD BRACKET BUILD_PREFIX COLS \
+               unset ACCEPT_LICENSE ACCEPT_PROPERTIES BAD BRACKET BUILD_PREFIX COLS \
                        DISTCC_DIR DISTDIR DOC_SYMLINKS_DIR \
                        EBUILD_EXIT_STATUS_FILE EBUILD_FORCE_TEST EBUILD_MASTER_PID \
                        ECLASSDIR ECLASS_DEPTH ENDCOL FAKEROOTKEY \
index f2af7c5c1ace47153a3617d59bc7aaa8c459485f..8e9a744401ac194e5dd18d154b815c6775e28979 100644 (file)
@@ -34,6 +34,8 @@ def visible(pkgsettings, pkg):
        try:
                if pkgsettings._getMissingLicenses(pkg.cpv, pkg.metadata):
                        return False
+               if pkgsettings._getMissingProperties(pkg.cpv, pkg.metadata):
+                       return False
        except portage.exception.InvalidDependString:
                return False
        return True
index 1ed91a33652d5941d449cf7cd54b63614bb1c7d0..0d819393a85b4b95eb039a170a76ac93426a9554 100644 (file)
@@ -1131,7 +1131,7 @@ class config(object):
        # environment in order to prevent sandbox from sourcing /etc/profile
        # in it's bashrc (causing major leakage).
        _environ_whitelist += [
-               "ACCEPT_LICENSE", "BASH_ENV", "BUILD_PREFIX", "D",
+               "ACCEPT_LICENSE", "ACCEPT_PROPERTIES", "BASH_ENV", "BUILD_PREFIX", "D",
                "DISTDIR", "DOC_SYMLINKS_DIR", "EBUILD",
                "EBUILD_EXIT_STATUS_FILE", "EBUILD_FORCE_TEST",
                "EBUILD_PHASE", "ECLASSDIR", "ECLASS_DEPTH", "EMERGE_FROM",
@@ -1274,6 +1274,7 @@ class config(object):
                self._accept_license = None
                self._accept_license_str = None
                self._license_groups = {}
+               self._accept_properties = None
 
                self.virtuals = {}
                self.virts_p = {}
@@ -1360,6 +1361,8 @@ class config(object):
                        self._accept_license = copy.deepcopy(clone._accept_license)
                        self._plicensedict = copy.deepcopy(clone._plicensedict)
                        self._license_groups = copy.deepcopy(clone._license_groups)
+                       self._accept_properties = copy.deepcopy(clone._accept_properties)
+                       self._ppropertiesdict = copy.deepcopy(clone._ppropertiesdict)
                else:
 
                        def check_var_directory(varname, var):
@@ -1686,6 +1689,7 @@ class config(object):
                        self.pusedict = {}
                        self.pkeywordsdict = {}
                        self._plicensedict = {}
+                       self._ppropertiesdict = {}
                        self.punmaskdict = {}
                        abs_user_config = os.path.join(config_root, USER_CONFIG_PATH)
 
@@ -1750,6 +1754,17 @@ class config(object):
                                                self._plicensedict[cp] = cp_dict
                                        cp_dict[k] = self.expandLicenseTokens(v)
 
+                               #package.properties
+                               propdict = grabdict_package(os.path.join(
+                                       abs_user_config, "package.properties"), recursive=1)
+                               for k, v in propdict.iteritems():
+                                       cp = dep_getkey(k)
+                                       cp_dict = self._ppropertiesdict.get(cp)
+                                       if not cp_dict:
+                                               cp_dict = {}
+                                               self._ppropertiesdict[cp] = cp_dict
+                                       cp_dict[k] = v
+
                                self._local_repo_configs = {}
                                self._local_repo_conf_path = \
                                        os.path.join(abs_user_config, 'repos.conf')
@@ -2147,6 +2162,7 @@ class config(object):
                        if use is None:
                                use = frozenset(settings['PORTAGE_USE'].split())
                        values['ACCEPT_LICENSE'] = self._accept_license(use, settings)
+                       values['ACCEPT_PROPERTIES'] = self._accept_properties(use, settings)
                        values['PORTAGE_RESTRICT'] = self._restrict(use, settings)
                        return values
 
@@ -2180,6 +2196,35 @@ class config(object):
                                licenses = acceptable_licenses
                        return ' '.join(sorted(licenses))
 
+               def _accept_properties(self, use, settings):
+                       """
+                       Generated a pruned version of ACCEPT_PROPERTIES, by intersection with
+                       PROPERTIES.
+                       Please, look at self._accept_license() to know why it is required.
+                       """
+                       try:
+                               properties = set(flatten(
+                                       dep.use_reduce(dep.paren_reduce(
+                                               settings['PROPERTIES']),
+                                               uselist=use)))
+                       except exception.InvalidDependString:
+                               properties = set()
+                       properties.discard('||')
+                       if settings._accept_properties:
+                               acceptable_properties = set()
+                               for x in settings._accept_properties:
+                                       if x == '*':
+                                               acceptable_properties.update(properties)
+                                       elif x == '-*':
+                                               acceptable_properties.clear()
+                                       elif x[1] == '-':
+                                               acceptable_properties.discard(x[1:])
+                                       elif x in properties:
+                                               acceptable_properties.add(x)
+
+                               properties = acceptable_properties
+                       return ' '.join(sorted(properties))
+
                def _restrict(self, use, settings):
                        try:
                                restrict = set(flatten(
@@ -2418,6 +2463,8 @@ class config(object):
                lazy_vars = self._lazy_vars(built_use, self)
                env_configdict.addLazySingleton('ACCEPT_LICENSE',
                        lazy_vars.__getitem__, 'ACCEPT_LICENSE')
+               env_configdict.addLazySingleton('ACCEPT_PROPERTIES',
+                       lazy_vars.__getitem__, 'ACCEPT_PROPERTIES')
                env_configdict.addLazySingleton('PORTAGE_RESTRICT',
                        lazy_vars.__getitem__, 'PORTAGE_RESTRICT')
 
@@ -2810,6 +2857,87 @@ class config(object):
                                        ret.append(element)
                return ret
 
+       def _getMissingProperties(self, cpv, metadata):
+               """
+               Take a PROPERTIES string and return a list of any properties the user may
+               may need to accept for the given package.  The returned list will not
+               contain any properties that have already been accepted.  This method
+               can throw an InvalidDependString exception.
+
+               @param cpv: The package name (for package.properties support)
+               @type cpv: String
+               @param metadata: A dictionary of raw package metadata
+               @type metadata: dict
+               @rtype: List
+               @return: A list of properties that have not been accepted.
+               """
+               if not self._accept_properties:
+                       return []
+               accept_properties = self._accept_properties
+               cpdict = self._ppropertiesdict.get(dep_getkey(cpv), None)
+               if cpdict:
+                       accept_properties = list(self._accept_properties)
+                       cpv_slot = "%s:%s" % (cpv, metadata["SLOT"])
+                       for atom in match_to_list(cpv_slot, cpdict.keys()):
+                               accept_properties.extend(cpdict[atom])
+
+               properties = set(flatten(dep.use_reduce(dep.paren_reduce(
+                       metadata["PROPERTIES"]), matchall=1)))
+               properties.discard('||')
+
+               acceptable_properties = set()
+               for x in accept_properties:
+                       if x == '*':
+                               acceptable_properties.update(properties)
+                       elif x == '-*':
+                               acceptable_properties.clear()
+                       elif x[:1] == '-':
+                               acceptable_properties.discard(x[1:])
+                       else:
+                               acceptable_properties.add(x)
+
+               properties_str = metadata["PROPERTIES"]
+               if "?" in properties_str:
+                       use = metadata["USE"].split()
+               else:
+                       use = []
+
+               properties_struct = portage.dep.use_reduce(
+                       portage.dep.paren_reduce(properties_str), uselist=use)
+               properties_struct = portage.dep.dep_opconvert(properties_struct)
+               return self._getMaskedProperties(properties_struct, acceptable_properties)
+
+       def _getMaskedProperties(self, properties_struct, acceptable_properties):
+               if not properties_struct:
+                       return []
+               if properties_struct[0] == "||":
+                       ret = []
+                       for element in properties_struct[1:]:
+                               if isinstance(element, list):
+                                       if element:
+                                               ret.append(self._getMaskedProperties(
+                                                       element, acceptable_properties))
+                                               if not ret[-1]:
+                                                       return []
+                               else:
+                                       if element in acceptable_properties:
+                                               return[]
+                                       ret.append(element)
+                       # Return all masked properties, since we don't know which combination
+                       # (if any) the user will decide to unmask
+                       return flatten(ret)
+
+               ret = []
+               for element in properties_struct:
+                       if isinstance(element, list):
+                               if element:
+                                       ret.extend(self._getMaskedProperties(element,
+                                               acceptable_properties))
+                       else:
+                               if element not in acceptable_properties:
+                                       ret.append(element)
+               return ret
+
        def _accept_chost(self, cpv, metadata):
                """
                @return True if pkg CHOST is accepted, False otherwise.
@@ -2957,6 +3085,19 @@ class config(object):
                        # repoman will accept any license
                        self._accept_license = ()
 
+               # ACCEPT_PROPERTIES works like ACCEPT_LICENSE, without groups
+               if self.local_config:
+                       mysplit = []
+                       for curdb in mydbs:
+                               mysplit.extend(curdb.get('ACCEPT_PROPERTIES', '').split())
+                       if mysplit:
+                               self.configlist[-1]['ACCEPT_PROPERTIES'] = ' '.join(mysplit)
+                       if tuple(mysplit) != self._accept_properties:
+                               self._accept_properties = tuple(mysplit)
+               else:
+                       # repoman will accept any property
+                       self._accept_properties = ()
+
                for mykey in myincrementals:
 
                        myflags=[]
@@ -7709,6 +7850,7 @@ def getmaskingstatus(mycpv, settings=None, portdb=None):
        eapi = metadata["EAPI"]
        mygroups = settings._getKeywords(mycpv, metadata)
        licenses = metadata["LICENSE"]
+       properties = metadata["PROPERTIES"]
        slot = metadata["SLOT"]
        if eapi.startswith("-"):
                eapi = eapi[1:]
@@ -7785,6 +7927,20 @@ def getmaskingstatus(mycpv, settings=None, portdb=None):
        except portage.exception.InvalidDependString, e:
                rValue.append("LICENSE: "+str(e))
 
+       try:
+               missing_properties = settings._getMissingProperties(mycpv, metadata)
+               if missing_properties:
+                       allowed_tokens = set(["||", "(", ")"])
+                       allowed_tokens.update(missing_properties)
+                       properties_split = properties.split()
+                       properties_split = [x for x in properties_split \
+                                       if x in allowed_tokens]
+                       msg = properties_split[:]
+                       msg.append("properties")
+                       rValue.append(" ".join(msg))
+       except portage.exception.InvalidDependString, e:
+               rValue.append("PROPERTIES: "+srt(e))
+
        # Only show KEYWORDS masks for installed packages
        # if they're not masked for any other reason.
        if kmask and (not installed or not rValue):
index d9b85b18abdbc981abc62cf6c95f88caf238d7f6..7ee889a7cf18392a3f58a3923bd4ad26c1f6ecae 100644 (file)
@@ -999,12 +999,14 @@ class portdbapi(dbapi):
                                        continue
                                if local_config:
                                        metadata["USE"] = ""
-                                       if "?" in metadata["LICENSE"]:
+                                       if "?" in metadata["LICENSE"] or "?" in metadata["PROPERTIES"]:
                                                self.doebuild_settings.setcpv(cpv, mydb=metadata)
                                                metadata["USE"] = self.doebuild_settings.get("USE", "")
                                        try:
                                                if settings._getMissingLicenses(cpv, metadata):
                                                        continue
+                                               if settings._getMissingProperties(cpv, metadata):
+                                                       continue
                                        except InvalidDependString:
                                                continue
                                if mydep.use:
@@ -1110,12 +1112,14 @@ class portdbapi(dbapi):
                                if not accept_chost(mycpv, metadata):
                                        continue
                                metadata["USE"] = ""
-                               if "?" in metadata["LICENSE"]:
+                               if "?" in metadata["LICENSE"] or "?" in metadata["PROPERTIES"]:
                                        self.doebuild_settings.setcpv(mycpv, mydb=metadata)
                                        metadata['USE'] = self.doebuild_settings['PORTAGE_USE']
                                try:
                                        if self.mysettings._getMissingLicenses(mycpv, metadata):
                                                continue
+                                       if self.mysettings._getMissingProperties(mycpv, metadata):
+                                               continue
                                except InvalidDependString:
                                        continue
                        newlist.append(mycpv)