diff options
-rw-r--r-- | pym/portage/package/ebuild/_config/LicenseManager.py | 231 | ||||
-rw-r--r-- | pym/portage/package/ebuild/_config/__init__.py | 2 | ||||
-rw-r--r-- | pym/portage/package/ebuild/_config/helper.py | 42 | ||||
-rw-r--r-- | pym/portage/package/ebuild/config.py | 247 |
4 files changed, 303 insertions, 219 deletions
diff --git a/pym/portage/package/ebuild/_config/LicenseManager.py b/pym/portage/package/ebuild/_config/LicenseManager.py new file mode 100644 index 000000000..fa030f173 --- /dev/null +++ b/pym/portage/package/ebuild/_config/LicenseManager.py @@ -0,0 +1,231 @@ +# Copyright 2010 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 + +__all__ = ( + 'LicenseManager', +) + +import copy + +from portage import os +from portage.dep import ExtendedAtomDict, use_reduce +from portage.util import grabdict, grabdict_package, writemsg +from portage.versions import cpv_getkey + +from portage.package.ebuild._config.helper import ordered_by_atom_specificity + + +class LicenseManager(object): + + def __init__(self, clone=None): + if clone: + self._accept_license_str = copy.deepcopy(clone._accept_license_str) + self._accept_license = copy.deepcopy(clone._accept_license) + self._plicensedict = copy.deepcopy(clone._plicensedict) + self._license_groups = copy.deepcopy(clone._license_groups) + self._undef_lic_groups = copy.deepcopy(clone._undef_lic_groups) + else: + self._accept_license_str = None + self._accept_license = None + self._license_groups = {} + self._plicensedict = ExtendedAtomDict(dict) + self._undef_lic_groups = set() + + def read_config_files(self, abs_user_config): + licdict = grabdict_package(os.path.join( + abs_user_config, "package.license"), recursive=1, allow_wildcard=True) + for k, v in licdict.items(): + self._plicensedict.setdefault(k.cp, {})[k] = \ + self.expandLicenseTokens(v) + + def parse_license_groups(self, locations): + for loc in locations: + for k, v in grabdict( + os.path.join(loc, "license_groups")).items(): + self._license_groups.setdefault(k, []).extend(v) + + def extract_global_changes(self, old=""): + ret = old + atom_license_map = self._plicensedict.get("*/*") + if atom_license_map is not None: + v = atom_license_map.pop("*/*", None) + if v is not None: + ret = " ".join(v) + if old: + ret = old + " " + ret + if not atom_license_map: + #No tokens left in atom_license_map, remove it. + del self._plicensedict["*/*"] + return ret + + def expandLicenseTokens(self, tokens): + """ Take a token from ACCEPT_LICENSE or package.license and expand it + if it's a group token (indicated by @) or just return it if it's not a + group. If a group is negated then negate all group elements.""" + expanded_tokens = [] + for x in tokens: + expanded_tokens.extend(self._expandLicenseToken(x, None)) + return expanded_tokens + + def _expandLicenseToken(self, token, traversed_groups): + negate = False + rValue = [] + if token.startswith("-"): + negate = True + license_name = token[1:] + else: + license_name = token + if not license_name.startswith("@"): + rValue.append(token) + return rValue + group_name = license_name[1:] + if traversed_groups is None: + traversed_groups = set() + license_group = self._license_groups.get(group_name) + if group_name in traversed_groups: + writemsg(_("Circular license group reference" + " detected in '%s'\n") % group_name, noiselevel=-1) + rValue.append("@"+group_name) + elif license_group: + traversed_groups.add(group_name) + for l in license_group: + if l.startswith("-"): + writemsg(_("Skipping invalid element %s" + " in license group '%s'\n") % (l, group_name), + noiselevel=-1) + else: + rValue.extend(self._expandLicenseToken(l, traversed_groups)) + else: + if self._license_groups and \ + group_name not in self._undef_lic_groups: + self._undef_lic_groups.add(group_name) + writemsg(_("Undefined license group '%s'\n") % group_name, + noiselevel=-1) + rValue.append("@"+group_name) + if negate: + rValue = ["-" + token for token in rValue] + return rValue + + def _getPkgAcceptLicense(self, cpv, slot): + """ + Get an ACCEPT_LICENSE list, accounting for package.license. + """ + accept_license = self._accept_license + cp = cpv_getkey(cpv) + cpdict = self._plicensedict.get(cp) + if cpdict: + cpv_slot = "%s:%s" % (cpv, slot) + plicence_list = ordered_by_atom_specificity(cpdict, cpv_slot) + if plicence_list: + accept_license = list(self._accept_license) + for x in plicence_list: + accept_license.extend(x) + return accept_license + + def get_prunned_accept_license(self, cpv, use, lic, slot): + """ + Generate a pruned version of ACCEPT_LICENSE, by intersection with + LICENSE. This is required since otherwise ACCEPT_LICENSE might be + too big (bigger than ARG_MAX), causing execve() calls to fail with + E2BIG errors as in bug #262647. + """ + try: + licenses = set(use_reduce(lic, uselist=use, flat=True)) + except InvalidDependString: + licenses = set() + licenses.discard('||') + + accept_license = self._getPkgAcceptLicense(cpv, slot) + + if accept_license: + acceptable_licenses = set() + for x in accept_license: + if x == '*': + acceptable_licenses.update(licenses) + elif x == '-*': + acceptable_licenses.clear() + elif x[:1] == '-': + acceptable_licenses.discard(x[1:]) + elif x in licenses: + acceptable_licenses.add(x) + + licenses = acceptable_licenses + return ' '.join(sorted(licenses)) + + def getMissingLicenses(self, cpv, use, lic, slot): + """ + Take a LICENSE string and return a list any licenses that the user may + may need to accept for the given package. The returned list will not + contain any licenses that have already been accepted. This method + can throw an InvalidDependString exception. + + @param cpv: The package name (for package.license support) + @type cpv: String + @param use: "USE" from the cpv's metadata + @type use: String + @param lic: "LICENSE" from the cpv's metadata + @type lic: String + @param slot: "SLOT" from the cpv's metadata + @type slot: String + @rtype: List + @return: A list of licenses that have not been accepted. + """ + + licenses = set(use_reduce(lic, matchall=1, flat=True)) + licenses.discard('||') + + acceptable_licenses = set() + for x in self._getPkgAcceptLicense(cpv, slot): + if x == '*': + acceptable_licenses.update(licenses) + elif x == '-*': + acceptable_licenses.clear() + elif x[:1] == '-': + acceptable_licenses.discard(x[1:]) + else: + acceptable_licenses.add(x) + + license_str = lic + if "?" in license_str: + use = use.split() + else: + use = [] + + license_struct = use_reduce(license_str, uselist=use, opconvert=True) + return self._getMaskedLicenses(license_struct, acceptable_licenses) + + def _getMaskedLicenses(self, license_struct, acceptable_licenses): + if not license_struct: + return [] + if license_struct[0] == "||": + ret = [] + for element in license_struct[1:]: + if isinstance(element, list): + if element: + tmp = self._getMaskedLicenses(element, acceptable_licenses) + if not tmp: + return [] + ret.extend(tmp) + else: + if element in acceptable_licenses: + return [] + ret.append(element) + # Return all masked licenses, since we don't know which combination + # (if any) the user will decide to unmask. + return ret + + ret = [] + for element in license_struct: + if isinstance(element, list): + if element: + ret.extend(self._getMaskedLicenses(element, + acceptable_licenses)) + else: + if element not in acceptable_licenses: + ret.append(element) + return ret + + def set_accept_license_str(self, accept_license_str): + if accept_license_str != self._accept_license_str: + self._accept_license_str = accept_license_str + self._accept_license = tuple(self.expandLicenseTokens(accept_license_str.split())) diff --git a/pym/portage/package/ebuild/_config/__init__.py b/pym/portage/package/ebuild/_config/__init__.py new file mode 100644 index 000000000..21a391aee --- /dev/null +++ b/pym/portage/package/ebuild/_config/__init__.py @@ -0,0 +1,2 @@ +# Copyright 2010 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 diff --git a/pym/portage/package/ebuild/_config/helper.py b/pym/portage/package/ebuild/_config/helper.py new file mode 100644 index 000000000..00e2dfabc --- /dev/null +++ b/pym/portage/package/ebuild/_config/helper.py @@ -0,0 +1,42 @@ +# Copyright 2010 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 + +__all__ = ( + 'ordered_by_atom_specificity', +) + +from portage.dep import best_match_to_list + +def ordered_by_atom_specificity(cpdict, pkg): + """ + Return a list of matched values from the given cpdict, + in ascending order by atom specificity. The rationale + for this order is that package.* config files are + typically written in ChangeLog like fashion, so it's + most friendly if the order that the atoms are written + does not matter. Therefore, settings from more specific + atoms override those of less specific atoms. Without + this behavior, settings from relatively unspecific atoms + would (somewhat confusingly) override the settings of + more specific atoms, requiring people to make adjustments + to the order that atoms are listed in the config file in + order to achieve desired results (and thus corrupting + the ChangeLog like ordering of the file). + """ + + results = [] + keys = list(cpdict) + + while keys: + bestmatch = best_match_to_list(pkg, keys) + if bestmatch: + keys.remove(bestmatch) + results.append(cpdict[bestmatch]) + else: + break + + if results: + # reverse, so the most specific atoms come last + results.reverse() + + return results diff --git a/pym/portage/package/ebuild/config.py b/pym/portage/package/ebuild/config.py index d49b23552..315166659 100644 --- a/pym/portage/package/ebuild/config.py +++ b/pym/portage/package/ebuild/config.py @@ -31,7 +31,7 @@ from portage.const import CACHE_PATH, CUSTOM_PROFILE_PATH, \ from portage.dbapi import dbapi from portage.dbapi.porttree import portdbapi from portage.dbapi.vartree import vartree -from portage.dep import Atom, best_match_to_list, \ +from portage.dep import Atom, \ isvalidatom, match_from_list, \ remove_slot, use_reduce from portage.eapi import eapi_exports_AA, eapi_supports_prefix, eapi_exports_replace_vars @@ -47,6 +47,9 @@ from portage.util import ensure_dirs, getconfig, grabdict, \ writemsg, writemsg_level from portage.versions import catpkgsplit, catsplit, cpv_getkey +from portage.package.ebuild._config.LicenseManager import LicenseManager +from portage.package.ebuild._config.helper import ordered_by_atom_specificity + if sys.hexversion >= 0x3000000: basestring = str @@ -95,40 +98,6 @@ def best_from_dict(key, top_dict, key_order, EmptyOnError=1, FullCopy=1, AllowEm else: raise KeyError("Key not found in list; '%s'" % key) -def _ordered_by_atom_specificity(cpdict, pkg): - """ - Return a list of matched values from the given cpdict, - in ascending order by atom specificity. The rationale - for this order is that package.* config files are - typically written in ChangeLog like fashion, so it's - most friendly if the order that the atoms are written - does not matter. Therefore, settings from more specific - atoms override those of less specific atoms. Without - this behavior, settings from relatively unspecific atoms - would (somewhat confusingly) override the settings of - more specific atoms, requiring people to make adjustments - to the order that atoms are listed in the config file in - order to achieve desired results (and thus corrupting - the ChangeLog like ordering of the file). - """ - - results = [] - keys = list(cpdict) - - while keys: - bestmatch = best_match_to_list(pkg, keys) - if bestmatch: - keys.remove(bestmatch) - results.append(cpdict[bestmatch]) - else: - break - - if results: - # reverse, so the most specific atoms come last - results.reverse() - - return results - class _features_set(object): """ Provides relevant set operations needed for access and modification of @@ -190,7 +159,6 @@ class _features_set(object): self._features.remove(k) self._sync_env_var() - def _lazy_iuse_regex(iuse_implicit): """ The PORTAGE_IUSE value is lazily evaluated since re.escape() is slow @@ -402,7 +370,6 @@ class config(object): _environ_filter = frozenset(_environ_filter) - _undef_lic_groups = set() _default_globals = ( ('ACCEPT_LICENSE', '* -@EULA'), ('ACCEPT_PROPERTIES', '*'), @@ -460,9 +427,7 @@ class config(object): self.modifiedkeys = [] self.uvlist = [] self._accept_chost_re = None - self._accept_license = None - self._accept_license_str = None - self._license_groups = {} + self._license_manager = LicenseManager() self._accept_properties = None self._features_overrides = [] @@ -553,9 +518,7 @@ class config(object): self.features._features = copy.deepcopy(clone.features._features) self._features_overrides = copy.deepcopy(clone._features_overrides) - self._accept_license = copy.deepcopy(clone._accept_license) - self._plicensedict = copy.deepcopy(clone._plicensedict) - self._license_groups = copy.deepcopy(clone._license_groups) + self._license_manager = LicenseManager(clone._license_manager) self._accept_properties = copy.deepcopy(clone._accept_properties) self._ppropertiesdict = copy.deepcopy(clone._ppropertiesdict) self._penvdict = copy.deepcopy(clone._penvdict) @@ -968,7 +931,6 @@ class config(object): self.pusedict = portage.dep.ExtendedAtomDict(dict) self.pkeywordsdict = portage.dep.ExtendedAtomDict(dict) - self._plicensedict = portage.dep.ExtendedAtomDict(dict) self._ppropertiesdict = portage.dep.ExtendedAtomDict(dict) self._penvdict = portage.dep.ExtendedAtomDict(dict) self.punmaskdict = portage.dep.ExtendedAtomDict(list) @@ -1053,17 +1015,10 @@ class config(object): self.pkeywordsdict.setdefault(k.cp, {})[k] = v #package.license - licdict = grabdict_package(os.path.join( - abs_user_config, "package.license"), recursive=1, allow_wildcard=True) - v = licdict.pop("*/*", None) - if v is not None: - if "ACCEPT_LICENSE" in self.configdict["conf"]: - self.configdict["conf"]["ACCEPT_LICENSE"] += " " + " ".join(v) - else: - self.configdict["conf"]["ACCEPT_LICENSE"] = " ".join(v) - for k, v in licdict.items(): - self._plicensedict.setdefault(k.cp, {})[k] = \ - self.expandLicenseTokens(v) + self._license_manager.read_config_files(abs_user_config) + self.configdict["conf"]["ACCEPT_LICENSE"] = \ + self._license_manager.extract_global_changes(\ + self.configdict["conf"].get("ACCEPT_LICENSE", "")) #package.properties propdict = grabdict_package(os.path.join( @@ -1196,11 +1151,7 @@ class config(object): self.pprovideddict[mycatpkg]=[x] # parse licensegroups - license_groups = self._license_groups - for x in locations: - for k, v in grabdict( - os.path.join(x, "license_groups")).items(): - license_groups.setdefault(k, []).extend(v) + self._license_manager.parse_license_groups(locations) # reasonable defaults; this is important as without USE_ORDER, # USE will always be "" (nothing set)! @@ -1305,49 +1256,7 @@ class config(object): """ Take a token from ACCEPT_LICENSE or package.license and expand it if it's a group token (indicated by @) or just return it if it's not a group. If a group is negated then negate all group elements.""" - expanded_tokens = [] - for x in tokens: - expanded_tokens.extend(self._expandLicenseToken(x, None)) - return expanded_tokens - - def _expandLicenseToken(self, token, traversed_groups): - negate = False - rValue = [] - if token.startswith("-"): - negate = True - license_name = token[1:] - else: - license_name = token - if not license_name.startswith("@"): - rValue.append(token) - return rValue - group_name = license_name[1:] - if traversed_groups is None: - traversed_groups = set() - license_group = self._license_groups.get(group_name) - if group_name in traversed_groups: - writemsg(_("Circular license group reference" - " detected in '%s'\n") % group_name, noiselevel=-1) - rValue.append("@"+group_name) - elif license_group: - traversed_groups.add(group_name) - for l in license_group: - if l.startswith("-"): - writemsg(_("Skipping invalid element %s" - " in license group '%s'\n") % (l, group_name), - noiselevel=-1) - else: - rValue.extend(self._expandLicenseToken(l, traversed_groups)) - else: - if self._license_groups and \ - group_name not in self._undef_lic_groups: - self._undef_lic_groups.add(group_name) - writemsg(_("Undefined license group '%s'\n") % group_name, - noiselevel=-1) - rValue.append("@"+group_name) - if negate: - rValue = ["-" + token for token in rValue] - return rValue + return self._license_manager.expandLicenseTokens(tokens) def validate(self): """Validate miscellaneous settings and display warnings if necessary. @@ -1498,41 +1407,12 @@ class config(object): use = self.built_use if use is None: use = frozenset(settings['PORTAGE_USE'].split()) - values['ACCEPT_LICENSE'] = self._accept_license(use, settings) + + values['ACCEPT_LICENSE'] = settings._license_manager.get_prunned_accept_license( \ + settings.mycpv, use, settings['LICENSE'], settings['SLOT']) values['PORTAGE_RESTRICT'] = self._restrict(use, settings) return values - def _accept_license(self, use, settings): - """ - Generate a pruned version of ACCEPT_LICENSE, by intersection with - LICENSE. This is required since otherwise ACCEPT_LICENSE might be - too big (bigger than ARG_MAX), causing execve() calls to fail with - E2BIG errors as in bug #262647. - """ - try: - licenses = set(use_reduce(settings['LICENSE'], uselist=use, flat=True)) - except InvalidDependString: - licenses = set() - licenses.discard('||') - - accept_license = settings._getPkgAcceptLicense( - settings.mycpv, {'SLOT' : settings['SLOT']}) - - if accept_license: - acceptable_licenses = set() - for x in accept_license: - if x == '*': - acceptable_licenses.update(licenses) - elif x == '-*': - acceptable_licenses.clear() - elif x[:1] == '-': - acceptable_licenses.discard(x[1:]) - elif x in licenses: - acceptable_licenses.add(x) - - licenses = acceptable_licenses - return ' '.join(sorted(licenses)) - def _restrict(self, use, settings): try: restrict = set(use_reduce(settings['RESTRICT'], uselist=use, flat=True)) @@ -1712,7 +1592,7 @@ class config(object): defaults.append(self.make_defaults_use[i]) cpdict = pkgprofileuse_dict.get(cp) if cpdict: - pkg_defaults = _ordered_by_atom_specificity(cpdict, cpv_slot) + pkg_defaults = ordered_by_atom_specificity(cpdict, cpv_slot) if pkg_defaults: defaults.extend(pkg_defaults) defaults = " ".join(defaults) @@ -1733,7 +1613,7 @@ class config(object): self.puse = "" cpdict = self.pusedict.get(cp) if cpdict: - puse_matches = _ordered_by_atom_specificity(cpdict, cpv_slot) + puse_matches = ordered_by_atom_specificity(cpdict, cpv_slot) if puse_matches: puse_list = [] for x in puse_matches: @@ -1754,7 +1634,7 @@ class config(object): self._penv = [] cpdict = self._penvdict.get(cp) if cpdict: - penv_matches = _ordered_by_atom_specificity(cpdict, cpv_slot) + penv_matches = ordered_by_atom_specificity(cpdict, cpv_slot) if penv_matches: for x in penv_matches: self._penv.extend(x) @@ -1967,7 +1847,7 @@ class config(object): usemask.append(self.usemask_list[i]) cpdict = pusemask_dict.get(cp) if cpdict: - pkg_usemask = _ordered_by_atom_specificity(cpdict, pkg) + pkg_usemask = ordered_by_atom_specificity(cpdict, pkg) if pkg_usemask: usemask.extend(pkg_usemask) return set(stack_lists(usemask, incremental=True)) @@ -1982,7 +1862,7 @@ class config(object): useforce.append(self.useforce_list[i]) cpdict = puseforce_dict.get(cp) if cpdict: - pkg_useforce = _ordered_by_atom_specificity(cpdict, pkg) + pkg_useforce = ordered_by_atom_specificity(cpdict, pkg) if pkg_useforce: useforce.extend(pkg_useforce) return set(stack_lists(useforce, incremental=True)) @@ -2049,7 +1929,7 @@ class config(object): for pkeywords_dict in self._pkeywords_list: cpdict = pkeywords_dict.get(cp) if cpdict: - pkg_keywords = _ordered_by_atom_specificity(cpdict, pkg) + pkg_keywords = ordered_by_atom_specificity(cpdict, pkg) if pkg_keywords: keywords.extend(pkg_keywords) return stack_lists(keywords, incremental=True) @@ -2089,7 +1969,7 @@ class config(object): cpdict = d.get(cp) if cpdict: pkg_accept_keywords = \ - _ordered_by_atom_specificity(cpdict, cpv_slot) + ordered_by_atom_specificity(cpdict, cpv_slot) if pkg_accept_keywords: for x in pkg_accept_keywords: if not x: @@ -2101,7 +1981,7 @@ class config(object): if pkgdict: cpv_slot = "%s:%s" % (cpv, metadata["SLOT"]) pkg_accept_keywords = \ - _ordered_by_atom_specificity(pkgdict, cpv_slot) + ordered_by_atom_specificity(pkgdict, cpv_slot) if pkg_accept_keywords: for x in pkg_accept_keywords: pgroups.extend(x) @@ -2152,22 +2032,6 @@ class config(object): missing = mygroups return missing - def _getPkgAcceptLicense(self, cpv, metadata): - """ - Get an ACCEPT_LICENSE list, accounting for package.license. - """ - accept_license = self._accept_license - cp = cpv_getkey(cpv) - cpdict = self._plicensedict.get(cp) - if cpdict: - cpv_slot = "%s:%s" % (cpv, metadata["SLOT"]) - plicence_list = _ordered_by_atom_specificity(cpdict, cpv_slot) - if plicence_list: - accept_license = list(self._accept_license) - for x in plicence_list: - accept_license.extend(x) - return accept_license - def _getMissingLicenses(self, cpv, metadata): """ Take a LICENSE string and return a list any licenses that the user may @@ -2182,61 +2046,8 @@ class config(object): @rtype: List @return: A list of licenses that have not been accepted. """ - - - licenses = set(use_reduce(metadata["LICENSE"], matchall=1, flat=True)) - licenses.discard('||') - - acceptable_licenses = set() - for x in self._getPkgAcceptLicense(cpv, metadata): - if x == '*': - acceptable_licenses.update(licenses) - elif x == '-*': - acceptable_licenses.clear() - elif x[:1] == '-': - acceptable_licenses.discard(x[1:]) - else: - acceptable_licenses.add(x) - - license_str = metadata["LICENSE"] - if "?" in license_str: - use = metadata["USE"].split() - else: - use = [] - - license_struct = use_reduce(license_str, uselist=use, opconvert=True) - return self._getMaskedLicenses(license_struct, acceptable_licenses) - - def _getMaskedLicenses(self, license_struct, acceptable_licenses): - if not license_struct: - return [] - if license_struct[0] == "||": - ret = [] - for element in license_struct[1:]: - if isinstance(element, list): - if element: - tmp = self._getMaskedLicenses(element, acceptable_licenses) - if not tmp: - return [] - ret.extend(tmp) - else: - if element in acceptable_licenses: - return [] - ret.append(element) - # Return all masked licenses, since we don't know which combination - # (if any) the user will decide to unmask. - return ret - - ret = [] - for element in license_struct: - if isinstance(element, list): - if element: - ret.extend(self._getMaskedLicenses(element, - acceptable_licenses)) - else: - if element not in acceptable_licenses: - ret.append(element) - return ret + return self._license_manager.getMissingLicenses( \ + cpv, metadata["USE"], metadata["LICENSE"], metadata["SLOT"]) def _getMissingProperties(self, cpv, metadata): """ @@ -2257,7 +2068,7 @@ class config(object): cpdict = self._ppropertiesdict.get(cp) if cpdict: cpv_slot = "%s:%s" % (cpv, metadata["SLOT"]) - pproperties_list = _ordered_by_atom_specificity(cpdict, cpv_slot) + pproperties_list = ordered_by_atom_specificity(cpdict, cpv_slot) if pproperties_list: accept_properties = list(self._accept_properties) for x in pproperties_list: @@ -2474,12 +2285,10 @@ class config(object): mysplit = self._prune_incremental(mysplit) accept_license_str = ' '.join(mysplit) self.configlist[-1]['ACCEPT_LICENSE'] = accept_license_str - if accept_license_str != self._accept_license_str: - self._accept_license_str = accept_license_str - self._accept_license = tuple(self.expandLicenseTokens(mysplit)) + self._license_manager.set_accept_license_str(accept_license_str) else: # repoman will accept any license - self._accept_license = ('*',) + self._license_manager.set_accept_license_str("*") # ACCEPT_PROPERTIES works like ACCEPT_LICENSE, without groups if self.local_config: |