From bf5896ab6449da9f8d3026dd0c3a086a58a3b454 Mon Sep 17 00:00:00 2001 From: Mounir Lamouri Date: Tue, 11 Aug 2009 20:45:35 +0000 Subject: Add ACCEPT_PROPERTIES variable which has default value * and can be set in 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 | 2 +- pym/_emerge/visible.py | 2 + pym/portage/__init__.py | 158 +++++++++++++++++++++++++++++++++++++++++- pym/portage/dbapi/porttree.py | 8 ++- 4 files changed, 166 insertions(+), 4 deletions(-) diff --git a/bin/isolated-functions.sh b/bin/isolated-functions.sh index bb9b9a042..cd60f05ee 100755 --- a/bin/isolated-functions.sh +++ b/bin/isolated-functions.sh @@ -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 \ diff --git a/pym/_emerge/visible.py b/pym/_emerge/visible.py index f2af7c5c1..8e9a74440 100644 --- a/pym/_emerge/visible.py +++ b/pym/_emerge/visible.py @@ -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 diff --git a/pym/portage/__init__.py b/pym/portage/__init__.py index 1ed91a336..0d819393a 100644 --- a/pym/portage/__init__.py +++ b/pym/portage/__init__.py @@ -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): diff --git a/pym/portage/dbapi/porttree.py b/pym/portage/dbapi/porttree.py index d9b85b18a..7ee889a7c 100644 --- a/pym/portage/dbapi/porttree.py +++ b/pym/portage/dbapi/porttree.py @@ -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) -- cgit v1.2.3-1-g7c22