--- /dev/null
+# 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()))
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
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
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
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
_environ_filter = frozenset(_environ_filter)
- _undef_lic_groups = set()
_default_globals = (
('ACCEPT_LICENSE', '* -@EULA'),
('ACCEPT_PROPERTIES', '*'),
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 = []
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)
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)
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(
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)!
""" 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.
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))
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)
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:
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)
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))
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))
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)
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:
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)
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
@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):
"""
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:
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: