| # deps.py -- Portage dependency resolution functions |
| # Copyright 2003-2013 Gentoo Foundation |
| # Distributed under the terms of the GNU General Public License v2 |
| |
| __all__ = [ |
| 'Atom', 'best_match_to_list', 'cpvequal', |
| 'dep_getcpv', 'dep_getkey', 'dep_getslot', |
| 'dep_getusedeps', 'dep_opconvert', 'flatten', |
| 'get_operator', 'isjustname', 'isspecific', |
| 'isvalidatom', 'match_from_list', 'match_to_list', |
| 'paren_enclose', 'paren_normalize', 'paren_reduce', |
| 'remove_slot', 'strip_empty', 'use_reduce', |
| '_repo_separator', '_slot_separator', |
| ] |
| |
| import re, sys |
| import warnings |
| from itertools import chain |
| |
| import portage |
| portage.proxy.lazyimport.lazyimport(globals(), |
| 'portage.util:cmp_sort_key,writemsg', |
| ) |
| |
| from portage import _encodings, _unicode_decode, _unicode_encode |
| from portage.eapi import _get_eapi_attrs |
| from portage.exception import InvalidAtom, InvalidData, InvalidDependString |
| from portage.localization import _ |
| from portage.versions import catpkgsplit, catsplit, \ |
| vercmp, ververify, _cp, _cpv, _pkg_str, _unknown_repo |
| import portage.cache.mappings |
| |
| if sys.hexversion >= 0x3000000: |
| basestring = str |
| _unicode = str |
| else: |
| _unicode = unicode |
| |
| # \w is [a-zA-Z0-9_] |
| |
| # PMS 3.1.3: A slot name may contain any of the characters [A-Za-z0-9+_.-]. |
| # It must not begin with a hyphen or a dot. |
| _slot_separator = ":" |
| _slot = r'([\w+][\w+.-]*)' |
| # loosly match SLOT, which may have an optional ABI part |
| _slot_loose = r'([\w+./*=-]+)' |
| |
| _use = r'\[.*\]' |
| _op = r'([=~]|[><]=?)' |
| |
| _repo_separator = "::" |
| _repo_name = r'[\w][\w-]*' |
| _repo_name_re = re.compile('^' + _repo_name + '$', re.UNICODE) |
| _repo = r'(?:' + _repo_separator + '(' + _repo_name + ')' + ')?' |
| |
| _extended_cat = r'[\w+*][\w+.*-]*' |
| |
| _slot_re_cache = {} |
| |
| def _get_slot_re(eapi_attrs): |
| cache_key = eapi_attrs.slot_operator |
| slot_re = _slot_re_cache.get(cache_key) |
| if slot_re is not None: |
| return slot_re |
| |
| if eapi_attrs.slot_operator: |
| slot_re = _slot + r'(/' + _slot + r')?' |
| else: |
| slot_re = _slot |
| |
| slot_re = re.compile('^' + slot_re + '$', re.VERBOSE | re.UNICODE) |
| |
| _slot_re_cache[cache_key] = slot_re |
| return slot_re |
| |
| _slot_dep_re_cache = {} |
| |
| def _get_slot_dep_re(eapi_attrs): |
| cache_key = eapi_attrs.slot_operator |
| slot_re = _slot_dep_re_cache.get(cache_key) |
| if slot_re is not None: |
| return slot_re |
| |
| if eapi_attrs.slot_operator: |
| slot_re = _slot + r'?(\*|=|/' + _slot + r'=?)?' |
| else: |
| slot_re = _slot |
| |
| slot_re = re.compile('^' + slot_re + '$', re.VERBOSE | re.UNICODE) |
| |
| _slot_dep_re_cache[cache_key] = slot_re |
| return slot_re |
| |
| def _match_slot(atom, pkg): |
| if pkg.slot == atom.slot: |
| if not atom.sub_slot: |
| return True |
| elif atom.sub_slot == pkg.sub_slot: |
| return True |
| return False |
| |
| _atom_re_cache = {} |
| |
| def _get_atom_re(eapi_attrs): |
| cache_key = eapi_attrs.dots_in_PN |
| atom_re = _atom_re_cache.get(cache_key) |
| if atom_re is not None: |
| return atom_re |
| |
| if eapi_attrs.dots_in_PN: |
| cp_re = _cp['dots_allowed_in_PN'] |
| cpv_re = _cpv['dots_allowed_in_PN'] |
| else: |
| cp_re = _cp['dots_disallowed_in_PN'] |
| cpv_re = _cpv['dots_disallowed_in_PN'] |
| |
| atom_re = re.compile('^(?P<without_use>(?:' + |
| '(?P<op>' + _op + cpv_re + ')|' + |
| '(?P<star>=' + cpv_re + r'\*)|' + |
| '(?P<simple>' + cp_re + '))' + |
| '(' + _slot_separator + _slot_loose + ')?' + |
| _repo + ')(' + _use + ')?$', re.VERBOSE | re.UNICODE) |
| |
| _atom_re_cache[cache_key] = atom_re |
| return atom_re |
| |
| _atom_wildcard_re_cache = {} |
| |
| def _get_atom_wildcard_re(eapi_attrs): |
| cache_key = eapi_attrs.dots_in_PN |
| atom_re = _atom_wildcard_re_cache.get(cache_key) |
| if atom_re is not None: |
| return atom_re |
| |
| if eapi_attrs.dots_in_PN: |
| pkg_re = r'[\w+*][\w+.*-]*?' |
| else: |
| pkg_re = r'[\w+*][\w+*-]*?' |
| |
| atom_re = re.compile(r'((?P<simple>(' + |
| _extended_cat + r')/(' + pkg_re + r'))' + \ |
| '|(?P<star>=((' + _extended_cat + r')/(' + pkg_re + r'))-(?P<version>\*\w+\*)))' + \ |
| '(:(?P<slot>' + _slot_loose + r'))?(' + |
| _repo_separator + r'(?P<repo>' + _repo_name + r'))?$', re.UNICODE) |
| |
| _atom_wildcard_re_cache[cache_key] = atom_re |
| return atom_re |
| |
| _usedep_re_cache = {} |
| |
| def _get_usedep_re(eapi_attrs): |
| """ |
| @param eapi_attrs: The EAPI attributes from _get_eapi_attrs |
| @type eapi_attrs: _eapi_attrs |
| @rtype: regular expression object |
| @return: A regular expression object that matches valid USE deps for the |
| given eapi. |
| """ |
| cache_key = eapi_attrs.dots_in_use_flags |
| usedep_re = _usedep_re_cache.get(cache_key) |
| if usedep_re is not None: |
| return usedep_re |
| |
| if eapi_attrs.dots_in_use_flags: |
| _flag_re = r'[A-Za-z0-9][A-Za-z0-9+_@.-]*' |
| else: |
| _flag_re = r'[A-Za-z0-9][A-Za-z0-9+_@-]*' |
| |
| usedep_re = re.compile(r'^(?P<prefix>[!-]?)(?P<flag>' + |
| _flag_re + r')(?P<default>(\(\+\)|\(\-\))?)(?P<suffix>[?=]?)$') |
| |
| _usedep_re_cache[cache_key] = usedep_re |
| return usedep_re |
| |
| _useflag_re_cache = {} |
| |
| def _get_useflag_re(eapi): |
| """ |
| When eapi is None then validation is not as strict, since we want the |
| same to work for multiple EAPIs that may have slightly different rules. |
| @param eapi: The EAPI |
| @type eapi: String or None |
| @rtype: regular expression object |
| @return: A regular expression object that matches valid USE flags for the |
| given eapi. |
| """ |
| eapi_attrs = _get_eapi_attrs(eapi) |
| cache_key = eapi_attrs.dots_in_use_flags |
| useflag_re = _useflag_re_cache.get(cache_key) |
| if useflag_re is not None: |
| return useflag_re |
| |
| if eapi_attrs.dots_in_use_flags: |
| flag_re = r'[A-Za-z0-9][A-Za-z0-9+_@.-]*' |
| else: |
| flag_re = r'[A-Za-z0-9][A-Za-z0-9+_@-]*' |
| |
| useflag_re = re.compile(r'^' + flag_re + r'$') |
| |
| _useflag_re_cache[cache_key] = useflag_re |
| return useflag_re |
| |
| def cpvequal(cpv1, cpv2): |
| """ |
| |
| @param cpv1: CategoryPackageVersion (no operators) Example: "sys-apps/portage-2.1" |
| @type cpv1: String |
| @param cpv2: CategoryPackageVersion (no operators) Example: "sys-apps/portage-2.1" |
| @type cpv2: String |
| @rtype: Boolean |
| @return: |
| 1. True if cpv1 = cpv2 |
| 2. False Otherwise |
| 3. Throws PortageException if cpv1 or cpv2 is not a CPV |
| |
| Example Usage: |
| >>> from portage.dep import cpvequal |
| >>> cpvequal("sys-apps/portage-2.1","sys-apps/portage-2.1") |
| >>> True |
| |
| """ |
| |
| try: |
| try: |
| split1 = cpv1.cpv_split |
| except AttributeError: |
| cpv1 = _pkg_str(cpv1) |
| split1 = cpv1.cpv_split |
| |
| try: |
| split2 = cpv2.cpv_split |
| except AttributeError: |
| cpv2 = _pkg_str(cpv2) |
| split2 = cpv2.cpv_split |
| |
| except InvalidData: |
| raise portage.exception.PortageException(_("Invalid data '%s, %s', parameter was not a CPV") % (cpv1, cpv2)) |
| |
| if split1[0] != split2[0] or \ |
| split1[1] != split2[1]: |
| return False |
| |
| return vercmp(cpv1.version, cpv2.version) == 0 |
| |
| def strip_empty(myarr): |
| """ |
| Strip all empty elements from an array |
| |
| @param myarr: The list of elements |
| @type myarr: List |
| @rtype: Array |
| @return: The array with empty elements removed |
| """ |
| warnings.warn(_("%s is deprecated and will be removed without replacement.") % \ |
| ('portage.dep.strip_empty',), DeprecationWarning, stacklevel=2) |
| return [x for x in myarr if x] |
| |
| def paren_reduce(mystr, _deprecation_warn=True): |
| """ |
| Take a string and convert all paren enclosed entities into sublists and |
| split the list elements by spaces. All redundant brackets are removed. |
| |
| Example usage: |
| >>> paren_reduce('foobar foo? ( bar baz )') |
| ['foobar', 'foo?', ['bar', 'baz']] |
| |
| @param mystr: The string to reduce |
| @type mystr: String |
| @rtype: Array |
| @return: The reduced string in an array |
| """ |
| if portage._internal_caller and _deprecation_warn: |
| warnings.warn(_("%s is deprecated and will be removed without replacement.") % \ |
| ('portage.dep.paren_reduce',), DeprecationWarning, stacklevel=2) |
| mysplit = mystr.split() |
| level = 0 |
| stack = [[]] |
| need_bracket = False |
| |
| for token in mysplit: |
| if token == "(": |
| need_bracket = False |
| stack.append([]) |
| level += 1 |
| elif token == ")": |
| if need_bracket: |
| raise InvalidDependString( |
| _("malformed syntax: '%s'") % mystr) |
| if level > 0: |
| level -= 1 |
| l = stack.pop() |
| is_single = (len(l) == 1 or (len(l)==2 and (l[0] == "||" or l[0][-1] == "?"))) |
| |
| def ends_in_any_of_dep(k): |
| return k>=0 and stack[k] and stack[k][-1] == "||" |
| |
| def ends_in_operator(k): |
| return k>=0 and stack[k] and (stack[k][-1] == "||" or stack[k][-1][-1] == "?") |
| |
| def special_append(): |
| """ |
| Use extend instead of append if possible. This kills all redundant brackets. |
| """ |
| if is_single and (not stack[level] or not stack[level][-1][-1] == "?"): |
| if len(l) == 1 and isinstance(l[0], list): |
| # l = [[...]] |
| stack[level].extend(l[0]) |
| else: |
| stack[level].extend(l) |
| else: |
| stack[level].append(l) |
| |
| if l: |
| if not ends_in_any_of_dep(level-1) and not ends_in_operator(level): |
| #Optimize: ( ( ... ) ) -> ( ... ). Make sure there is no '||' hanging around. |
| stack[level].extend(l) |
| elif not stack[level]: |
| #An '||' in the level above forces us to keep to brackets. |
| special_append() |
| elif len(l) == 1 and ends_in_any_of_dep(level): |
| #Optimize: || ( A ) -> A |
| stack[level].pop() |
| special_append() |
| elif len(l) == 2 and (l[0] == "||" or l[0][-1] == "?") and stack[level][-1] in (l[0], "||"): |
| #Optimize: || ( || ( ... ) ) -> || ( ... ) |
| # foo? ( foo? ( ... ) ) -> foo? ( ... ) |
| # || ( foo? ( ... ) ) -> foo? ( ... ) |
| stack[level].pop() |
| special_append() |
| else: |
| special_append() |
| else: |
| if stack[level] and (stack[level][-1] == "||" or stack[level][-1][-1] == "?"): |
| stack[level].pop() |
| else: |
| raise InvalidDependString( |
| _("malformed syntax: '%s'") % mystr) |
| elif token == "||": |
| if need_bracket: |
| raise InvalidDependString( |
| _("malformed syntax: '%s'") % mystr) |
| need_bracket = True |
| stack[level].append(token) |
| else: |
| if need_bracket: |
| raise InvalidDependString( |
| _("malformed syntax: '%s'") % mystr) |
| |
| if token[-1] == "?": |
| need_bracket = True |
| |
| stack[level].append(token) |
| |
| if level != 0 or need_bracket: |
| raise InvalidDependString( |
| _("malformed syntax: '%s'") % mystr) |
| |
| return stack[0] |
| |
| class paren_normalize(list): |
| """Take a dependency structure as returned by paren_reduce or use_reduce |
| and generate an equivalent structure that has no redundant lists.""" |
| def __init__(self, src): |
| if portage._internal_caller: |
| warnings.warn(_("%s is deprecated and will be removed without replacement.") % \ |
| ('portage.dep.paren_normalize',), DeprecationWarning, stacklevel=2) |
| list.__init__(self) |
| self._zap_parens(src, self) |
| |
| def _zap_parens(self, src, dest, disjunction=False): |
| if not src: |
| return dest |
| i = iter(src) |
| for x in i: |
| if isinstance(x, basestring): |
| if x in ('||', '^^'): |
| y = self._zap_parens(next(i), [], disjunction=True) |
| if len(y) == 1: |
| dest.append(y[0]) |
| else: |
| dest.append(x) |
| dest.append(y) |
| elif x.endswith("?"): |
| dest.append(x) |
| dest.append(self._zap_parens(next(i), [])) |
| else: |
| dest.append(x) |
| else: |
| if disjunction: |
| x = self._zap_parens(x, []) |
| if len(x) == 1: |
| dest.append(x[0]) |
| else: |
| dest.append(x) |
| else: |
| self._zap_parens(x, dest) |
| return dest |
| |
| def paren_enclose(mylist, unevaluated_atom=False, opconvert=False): |
| """ |
| Convert a list to a string with sublists enclosed with parens. |
| |
| Example usage: |
| >>> test = ['foobar','foo',['bar','baz']] |
| >>> paren_enclose(test) |
| 'foobar foo ( bar baz )' |
| |
| @param mylist: The list |
| @type mylist: List |
| @rtype: String |
| @return: The paren enclosed string |
| """ |
| mystrparts = [] |
| for x in mylist: |
| if isinstance(x, list): |
| if opconvert and x and x[0] == "||": |
| mystrparts.append("%s ( %s )" % (x[0], paren_enclose(x[1:]))) |
| else: |
| mystrparts.append("( %s )" % paren_enclose(x)) |
| else: |
| if unevaluated_atom: |
| x = getattr(x, 'unevaluated_atom', x) |
| mystrparts.append(x) |
| return " ".join(mystrparts) |
| |
| def use_reduce(depstr, uselist=[], masklist=[], matchall=False, excludeall=[], is_src_uri=False, \ |
| eapi=None, opconvert=False, flat=False, is_valid_flag=None, token_class=None, matchnone=False): |
| """ |
| Takes a dep string and reduces the use? conditionals out, leaving an array |
| with subarrays. All redundant brackets are removed. |
| |
| @param deparray: depstring |
| @type deparray: String |
| @param uselist: List of use enabled flags |
| @type uselist: List |
| @param masklist: List of masked flags (always treated as disabled) |
| @type masklist: List |
| @param matchall: Treat all conditionals as active. Used by repoman. |
| @type matchall: Bool |
| @param excludeall: List of flags for which negated conditionals are always treated as inactive. |
| @type excludeall: List |
| @param is_src_uri: Indicates if depstr represents a SRC_URI |
| @type is_src_uri: Bool |
| @param eapi: Indicates the EAPI the dep string has to comply to |
| @type eapi: String |
| @param opconvert: Put every operator as first element into it's argument list |
| @type opconvert: Bool |
| @param flat: Create a flat list of all tokens |
| @type flat: Bool |
| @param is_valid_flag: Function that decides if a given use flag might be used in use conditionals |
| @type is_valid_flag: Function |
| @param token_class: Convert all non operator tokens into this class |
| @type token_class: Class |
| @param matchnone: Treat all conditionals as inactive. Used by digestgen(). |
| @type matchnone: Bool |
| @rtype: List |
| @return: The use reduced depend array |
| """ |
| if isinstance(depstr, list): |
| if portage._internal_caller: |
| warnings.warn(_("Passing paren_reduced dep arrays to %s is deprecated. " + \ |
| "Pass the original dep string instead.") % \ |
| ('portage.dep.use_reduce',), DeprecationWarning, stacklevel=2) |
| depstr = paren_enclose(depstr) |
| |
| if opconvert and flat: |
| raise ValueError("portage.dep.use_reduce: 'opconvert' and 'flat' are mutually exclusive") |
| |
| if matchall and matchnone: |
| raise ValueError("portage.dep.use_reduce: 'matchall' and 'matchnone' are mutually exclusive") |
| |
| eapi_attrs = _get_eapi_attrs(eapi) |
| useflag_re = _get_useflag_re(eapi) |
| |
| def is_active(conditional): |
| """ |
| Decides if a given use conditional is active. |
| """ |
| if conditional.startswith("!"): |
| flag = conditional[1:-1] |
| is_negated = True |
| else: |
| flag = conditional[:-1] |
| is_negated = False |
| |
| if is_valid_flag: |
| if not is_valid_flag(flag): |
| msg = _("USE flag '%s' referenced in " + \ |
| "conditional '%s' is not in IUSE") \ |
| % (flag, conditional) |
| e = InvalidData(msg, category='IUSE.missing') |
| raise InvalidDependString(msg, errors=(e,)) |
| else: |
| if useflag_re.match(flag) is None: |
| raise InvalidDependString( |
| _("invalid use flag '%s' in conditional '%s'") % (flag, conditional)) |
| |
| if is_negated and flag in excludeall: |
| return False |
| |
| if flag in masklist: |
| return is_negated |
| |
| if matchall: |
| return True |
| |
| if matchnone: |
| return False |
| |
| return (flag in uselist and not is_negated) or \ |
| (flag not in uselist and is_negated) |
| |
| def missing_white_space_check(token, pos): |
| """ |
| Used to generate good error messages for invalid tokens. |
| """ |
| for x in (")", "(", "||"): |
| if token.startswith(x) or token.endswith(x): |
| raise InvalidDependString( |
| _("missing whitespace around '%s' at '%s', token %s") % (x, token, pos+1)) |
| |
| mysplit = depstr.split() |
| #Count the bracket level. |
| level = 0 |
| #We parse into a stack. Every time we hit a '(', a new empty list is appended to the stack. |
| #When we hit a ')', the last list in the stack is merged with list one level up. |
| stack = [[]] |
| #Set need_bracket to True after use conditionals or ||. Other tokens need to ensure |
| #that need_bracket is not True. |
| need_bracket = False |
| #Set need_simple_token to True after a SRC_URI arrow. Other tokens need to ensure |
| #that need_simple_token is not True. |
| need_simple_token = False |
| |
| for pos, token in enumerate(mysplit): |
| if token == "(": |
| if need_simple_token: |
| raise InvalidDependString( |
| _("expected: file name, got: '%s', token %s") % (token, pos+1)) |
| if len(mysplit) >= pos+2 and mysplit[pos+1] == ")": |
| raise InvalidDependString( |
| _("expected: dependency string, got: ')', token %s") % (pos+1,)) |
| need_bracket = False |
| stack.append([]) |
| level += 1 |
| elif token == ")": |
| if need_bracket: |
| raise InvalidDependString( |
| _("expected: '(', got: '%s', token %s") % (token, pos+1)) |
| if need_simple_token: |
| raise InvalidDependString( |
| _("expected: file name, got: '%s', token %s") % (token, pos+1)) |
| if level > 0: |
| level -= 1 |
| l = stack.pop() |
| |
| is_single = len(l) == 1 or \ |
| (opconvert and l and l[0] == "||") or \ |
| (not opconvert and len(l)==2 and l[0] == "||") |
| ignore = False |
| |
| if flat: |
| #In 'flat' mode, we simply merge all lists into a single large one. |
| if stack[level] and stack[level][-1][-1] == "?": |
| #The last token before the '(' that matches the current ')' |
| #was a use conditional. The conditional is removed in any case. |
| #Merge the current list if needed. |
| if is_active(stack[level][-1]): |
| stack[level].pop() |
| stack[level].extend(l) |
| else: |
| stack[level].pop() |
| else: |
| stack[level].extend(l) |
| continue |
| |
| if stack[level]: |
| if stack[level][-1] == "||" and not l: |
| #Optimize: || ( ) -> . |
| stack[level].pop() |
| elif stack[level][-1][-1] == "?": |
| #The last token before the '(' that matches the current ')' |
| #was a use conditional, remove it and decide if we |
| #have to keep the current list. |
| if not is_active(stack[level][-1]): |
| ignore = True |
| stack[level].pop() |
| |
| def ends_in_any_of_dep(k): |
| return k>=0 and stack[k] and stack[k][-1] == "||" |
| |
| def starts_with_any_of_dep(k): |
| #'ends_in_any_of_dep' for opconvert |
| return k>=0 and stack[k] and stack[k][0] == "||" |
| |
| def last_any_of_operator_level(k): |
| #Returns the level of the last || operator if it is in effect for |
| #the current level. It is not in effect, if there is a level, that |
| #ends in a non-operator. This is almost equivalent to stack[level][-1]=="||", |
| #expect that it skips empty levels. |
| while k>=0: |
| if stack[k]: |
| if stack[k][-1] == "||": |
| return k |
| elif stack[k][-1][-1] != "?": |
| return -1 |
| k -= 1 |
| return -1 |
| |
| def special_append(): |
| """ |
| Use extend instead of append if possible. This kills all redundant brackets. |
| """ |
| if is_single: |
| #Either [A], [[...]] or [|| [...]] |
| if l[0] == "||" and ends_in_any_of_dep(level-1): |
| if opconvert: |
| stack[level].extend(l[1:]) |
| else: |
| stack[level].extend(l[1]) |
| elif len(l) == 1 and isinstance(l[0], list): |
| # l = [[...]] |
| last = last_any_of_operator_level(level-1) |
| if last == -1: |
| if opconvert and isinstance(l[0], list) \ |
| and l[0] and l[0][0] == '||': |
| stack[level].append(l[0]) |
| else: |
| stack[level].extend(l[0]) |
| else: |
| if opconvert and l[0] and l[0][0] == "||": |
| stack[level].extend(l[0][1:]) |
| else: |
| stack[level].append(l[0]) |
| else: |
| stack[level].extend(l) |
| else: |
| if opconvert and stack[level] and stack[level][-1] == '||': |
| stack[level][-1] = ['||'] + l |
| else: |
| stack[level].append(l) |
| |
| if l and not ignore: |
| #The current list is not empty and we don't want to ignore it because |
| #of an inactive use conditional. |
| if not ends_in_any_of_dep(level-1) and not ends_in_any_of_dep(level): |
| #Optimize: ( ( ... ) ) -> ( ... ). Make sure there is no '||' hanging around. |
| stack[level].extend(l) |
| elif not stack[level]: |
| #An '||' in the level above forces us to keep to brackets. |
| special_append() |
| elif is_single and ends_in_any_of_dep(level): |
| #Optimize: || ( A ) -> A, || ( || ( ... ) ) -> || ( ... ) |
| stack[level].pop() |
| special_append() |
| elif ends_in_any_of_dep(level) and ends_in_any_of_dep(level-1): |
| #Optimize: || ( A || ( B C ) ) -> || ( A B C ) |
| stack[level].pop() |
| stack[level].extend(l) |
| else: |
| if opconvert and ends_in_any_of_dep(level): |
| #In opconvert mode, we have to move the operator from the level |
| #above into the current list. |
| stack[level].pop() |
| stack[level].append(["||"] + l) |
| else: |
| special_append() |
| |
| else: |
| raise InvalidDependString( |
| _("no matching '%s' for '%s', token %s") % ("(", ")", pos+1)) |
| elif token == "||": |
| if is_src_uri: |
| raise InvalidDependString( |
| _("any-of dependencies are not allowed in SRC_URI: token %s") % (pos+1,)) |
| if need_bracket: |
| raise InvalidDependString( |
| _("expected: '(', got: '%s', token %s") % (token, pos+1)) |
| need_bracket = True |
| stack[level].append(token) |
| elif token == "->": |
| if need_simple_token: |
| raise InvalidDependString( |
| _("expected: file name, got: '%s', token %s") % (token, pos+1)) |
| if not is_src_uri: |
| raise InvalidDependString( |
| _("SRC_URI arrow are only allowed in SRC_URI: token %s") % (pos+1,)) |
| if not eapi_attrs.src_uri_arrows: |
| raise InvalidDependString( |
| _("SRC_URI arrow not allowed in EAPI %s: token %s") % (eapi, pos+1)) |
| need_simple_token = True |
| stack[level].append(token) |
| else: |
| missing_white_space_check(token, pos) |
| |
| if need_bracket: |
| raise InvalidDependString( |
| _("expected: '(', got: '%s', token %s") % (token, pos+1)) |
| |
| if need_simple_token and "/" in token: |
| #The last token was a SRC_URI arrow, make sure we have a simple file name. |
| raise InvalidDependString( |
| _("expected: file name, got: '%s', token %s") % (token, pos+1)) |
| |
| if token[-1] == "?": |
| need_bracket = True |
| else: |
| need_simple_token = False |
| if token_class and not is_src_uri: |
| #Add a hack for SRC_URI here, to avoid conditional code at the consumer level |
| try: |
| token = token_class(token, eapi=eapi, |
| is_valid_flag=is_valid_flag) |
| except InvalidAtom as e: |
| raise InvalidDependString( |
| _("Invalid atom (%s), token %s") \ |
| % (e, pos+1), errors=(e,)) |
| except SystemExit: |
| raise |
| except Exception as e: |
| raise InvalidDependString( |
| _("Invalid token '%s', token %s") % (token, pos+1)) |
| |
| if not matchall and \ |
| hasattr(token, 'evaluate_conditionals'): |
| token = token.evaluate_conditionals(uselist) |
| |
| stack[level].append(token) |
| |
| if level != 0: |
| raise InvalidDependString( |
| _("Missing '%s' at end of string") % (")",)) |
| |
| if need_bracket: |
| raise InvalidDependString( |
| _("Missing '%s' at end of string") % ("(",)) |
| |
| if need_simple_token: |
| raise InvalidDependString( |
| _("Missing file name at end of string")) |
| |
| return stack[0] |
| |
| def dep_opconvert(deplist): |
| """ |
| Iterate recursively through a list of deps, if the |
| dep is a '||' or '&&' operator, combine it with the |
| list of deps that follows.. |
| |
| Example usage: |
| >>> test = ["blah", "||", ["foo", "bar", "baz"]] |
| >>> dep_opconvert(test) |
| ['blah', ['||', 'foo', 'bar', 'baz']] |
| |
| @param deplist: A list of deps to format |
| @type mydep: List |
| @rtype: List |
| @return: |
| The new list with the new ordering |
| """ |
| if portage._internal_caller: |
| warnings.warn(_("%s is deprecated. Use %s with the opconvert parameter set to True instead.") % \ |
| ('portage.dep.dep_opconvert', 'portage.dep.use_reduce'), DeprecationWarning, stacklevel=2) |
| |
| retlist = [] |
| x = 0 |
| while x != len(deplist): |
| if isinstance(deplist[x], list): |
| retlist.append(dep_opconvert(deplist[x])) |
| elif deplist[x] == "||": |
| retlist.append([deplist[x]] + dep_opconvert(deplist[x+1])) |
| x += 1 |
| else: |
| retlist.append(deplist[x]) |
| x += 1 |
| return retlist |
| |
| def flatten(mylist): |
| """ |
| Recursively traverse nested lists and return a single list containing |
| all non-list elements that are found. |
| |
| Example usage: |
| >>> flatten([1, [2, 3, [4]]]) |
| [1, 2, 3, 4] |
| |
| @param mylist: A list containing nested lists and non-list elements. |
| @type mylist: List |
| @rtype: List |
| @return: A single list containing only non-list elements. |
| """ |
| if portage._internal_caller: |
| warnings.warn(_("%s is deprecated and will be removed without replacement.") % \ |
| ('portage.dep.flatten',), DeprecationWarning, stacklevel=2) |
| |
| newlist = [] |
| for x in mylist: |
| if isinstance(x, list): |
| newlist.extend(flatten(x)) |
| else: |
| newlist.append(x) |
| return newlist |
| |
| class _use_dep(object): |
| |
| __slots__ = ("_eapi_attrs", "conditional", "missing_enabled", "missing_disabled", |
| "disabled", "enabled", "tokens", "required") |
| |
| class _conditionals_class(object): |
| __slots__ = ("enabled", "disabled", "equal", "not_equal") |
| |
| def items(self): |
| for k in self.__slots__: |
| v = getattr(self, k, None) |
| if v: |
| yield (k, v) |
| |
| def values(self): |
| for k in self.__slots__: |
| v = getattr(self, k, None) |
| if v: |
| yield v |
| |
| # used in InvalidAtom messages |
| _conditional_strings = { |
| 'enabled' : '%s?', |
| 'disabled': '!%s?', |
| 'equal': '%s=', |
| 'not_equal': '!%s=', |
| } |
| |
| def __init__(self, use, eapi_attrs, enabled_flags=None, disabled_flags=None, missing_enabled=None, |
| missing_disabled=None, conditional=None, required=None): |
| |
| self._eapi_attrs = eapi_attrs |
| |
| if enabled_flags is not None: |
| #A shortcut for the classe's own methods. |
| self.tokens = use |
| if not isinstance(self.tokens, tuple): |
| self.tokens = tuple(self.tokens) |
| |
| self.required = frozenset(required) |
| self.enabled = frozenset(enabled_flags) |
| self.disabled = frozenset(disabled_flags) |
| self.missing_enabled = frozenset(missing_enabled) |
| self.missing_disabled = frozenset(missing_disabled) |
| self.conditional = None |
| |
| if conditional: |
| self.conditional = self._conditionals_class() |
| for k in "enabled", "disabled", "equal", "not_equal": |
| setattr(self.conditional, k, frozenset(conditional.get(k, []))) |
| |
| return |
| |
| enabled_flags = set() |
| disabled_flags = set() |
| missing_enabled = set() |
| missing_disabled = set() |
| no_default = set() |
| |
| conditional = {} |
| usedep_re = _get_usedep_re(self._eapi_attrs) |
| |
| for x in use: |
| m = usedep_re.match(x) |
| if m is None: |
| raise InvalidAtom(_("Invalid use dep: '%s'") % (x,)) |
| |
| operator = m.group("prefix") + m.group("suffix") |
| flag = m.group("flag") |
| default = m.group("default") |
| |
| if not operator: |
| enabled_flags.add(flag) |
| elif operator == "-": |
| disabled_flags.add(flag) |
| elif operator == "?": |
| conditional.setdefault("enabled", set()).add(flag) |
| elif operator == "=": |
| conditional.setdefault("equal", set()).add(flag) |
| elif operator == "!=": |
| conditional.setdefault("not_equal", set()).add(flag) |
| elif operator == "!?": |
| conditional.setdefault("disabled", set()).add(flag) |
| else: |
| raise InvalidAtom(_("Invalid use dep: '%s'") % (x,)) |
| |
| if default: |
| if default == "(+)": |
| if flag in missing_disabled or flag in no_default: |
| raise InvalidAtom(_("Invalid use dep: '%s'") % (x,)) |
| missing_enabled.add(flag) |
| else: |
| if flag in missing_enabled or flag in no_default: |
| raise InvalidAtom(_("Invalid use dep: '%s'") % (x,)) |
| missing_disabled.add(flag) |
| else: |
| if flag in missing_enabled or flag in missing_disabled: |
| raise InvalidAtom(_("Invalid use dep: '%s'") % (x,)) |
| no_default.add(flag) |
| |
| self.tokens = use |
| if not isinstance(self.tokens, tuple): |
| self.tokens = tuple(self.tokens) |
| |
| self.required = frozenset(no_default) |
| |
| self.enabled = frozenset(enabled_flags) |
| self.disabled = frozenset(disabled_flags) |
| self.missing_enabled = frozenset(missing_enabled) |
| self.missing_disabled = frozenset(missing_disabled) |
| self.conditional = None |
| |
| if conditional: |
| self.conditional = self._conditionals_class() |
| for k in "enabled", "disabled", "equal", "not_equal": |
| setattr(self.conditional, k, frozenset(conditional.get(k, []))) |
| |
| def __bool__(self): |
| return bool(self.tokens) |
| |
| if sys.hexversion < 0x3000000: |
| __nonzero__ = __bool__ |
| |
| def __str__(self): |
| if not self.tokens: |
| return "" |
| return "[%s]" % (",".join(self.tokens),) |
| |
| if sys.hexversion < 0x3000000: |
| |
| __unicode__ = __str__ |
| |
| def __str__(self): |
| return _unicode_encode(self.__unicode__(), |
| encoding=_encodings['content'], errors='backslashreplace') |
| |
| def __repr__(self): |
| return "portage.dep._use_dep(%s)" % repr(self.tokens) |
| |
| def evaluate_conditionals(self, use): |
| """ |
| Create a new instance with conditionals evaluated. |
| |
| Conditional evaluation behavior: |
| |
| parent state conditional result |
| |
| x x? x |
| -x x? |
| x !x? |
| -x !x? -x |
| |
| x x= x |
| -x x= -x |
| x !x= -x |
| -x !x= x |
| |
| Conditional syntax examples: |
| |
| Compact Form Equivalent Expanded Form |
| |
| foo[bar?] bar? ( foo[bar] ) !bar? ( foo ) |
| foo[!bar?] bar? ( foo ) !bar? ( foo[-bar] ) |
| foo[bar=] bar? ( foo[bar] ) !bar? ( foo[-bar] ) |
| foo[!bar=] bar? ( foo[-bar] ) !bar? ( foo[bar] ) |
| |
| """ |
| enabled_flags = set(self.enabled) |
| disabled_flags = set(self.disabled) |
| |
| tokens = [] |
| usedep_re = _get_usedep_re(self._eapi_attrs) |
| |
| for x in self.tokens: |
| m = usedep_re.match(x) |
| |
| operator = m.group("prefix") + m.group("suffix") |
| flag = m.group("flag") |
| default = m.group("default") |
| if default is None: |
| default = "" |
| |
| if operator == "?": |
| if flag in use: |
| enabled_flags.add(flag) |
| tokens.append(flag+default) |
| elif operator == "=": |
| if flag in use: |
| enabled_flags.add(flag) |
| tokens.append(flag+default) |
| else: |
| disabled_flags.add(flag) |
| tokens.append("-"+flag+default) |
| elif operator == "!=": |
| if flag in use: |
| disabled_flags.add(flag) |
| tokens.append("-"+flag+default) |
| else: |
| enabled_flags.add(flag) |
| tokens.append(flag+default) |
| elif operator == "!?": |
| if flag not in use: |
| disabled_flags.add(flag) |
| tokens.append("-"+flag+default) |
| else: |
| tokens.append(x) |
| |
| return _use_dep(tokens, self._eapi_attrs, enabled_flags=enabled_flags, disabled_flags=disabled_flags, |
| missing_enabled=self.missing_enabled, missing_disabled=self.missing_disabled, required=self.required) |
| |
| def violated_conditionals(self, other_use, is_valid_flag, parent_use=None): |
| """ |
| Create a new instance with satisfied use deps removed. |
| """ |
| if parent_use is None and self.conditional: |
| raise InvalidAtom("violated_conditionals needs 'parent_use'" + \ |
| " parameter for conditional flags.") |
| |
| enabled_flags = set() |
| disabled_flags = set() |
| |
| conditional = {} |
| tokens = [] |
| |
| all_defaults = frozenset(chain(self.missing_enabled, self.missing_disabled)) |
| |
| def validate_flag(flag): |
| return is_valid_flag(flag) or flag in all_defaults |
| |
| usedep_re = _get_usedep_re(self._eapi_attrs) |
| |
| for x in self.tokens: |
| m = usedep_re.match(x) |
| |
| operator = m.group("prefix") + m.group("suffix") |
| flag = m.group("flag") |
| |
| if not validate_flag(flag): |
| tokens.append(x) |
| if not operator: |
| enabled_flags.add(flag) |
| elif operator == "-": |
| disabled_flags.add(flag) |
| elif operator == "?": |
| conditional.setdefault("enabled", set()).add(flag) |
| elif operator == "=": |
| conditional.setdefault("equal", set()).add(flag) |
| elif operator == "!=": |
| conditional.setdefault("not_equal", set()).add(flag) |
| elif operator == "!?": |
| conditional.setdefault("disabled", set()).add(flag) |
| |
| continue |
| |
| if not operator: |
| if flag not in other_use: |
| if is_valid_flag(flag) or flag in self.missing_disabled: |
| tokens.append(x) |
| enabled_flags.add(flag) |
| elif operator == "-": |
| if flag not in other_use: |
| if not is_valid_flag(flag): |
| if flag in self.missing_enabled: |
| tokens.append(x) |
| disabled_flags.add(flag) |
| else: |
| tokens.append(x) |
| disabled_flags.add(flag) |
| elif operator == "?": |
| if flag not in parent_use or flag in other_use: |
| continue |
| |
| if is_valid_flag(flag) or flag in self.missing_disabled: |
| tokens.append(x) |
| conditional.setdefault("enabled", set()).add(flag) |
| elif operator == "=": |
| if flag in parent_use and flag not in other_use: |
| if is_valid_flag(flag): |
| tokens.append(x) |
| conditional.setdefault("equal", set()).add(flag) |
| else: |
| if flag in self.missing_disabled: |
| tokens.append(x) |
| conditional.setdefault("equal", set()).add(flag) |
| elif flag not in parent_use: |
| if flag not in other_use: |
| if not is_valid_flag(flag): |
| if flag in self.missing_enabled: |
| tokens.append(x) |
| conditional.setdefault("equal", set()).add(flag) |
| else: |
| tokens.append(x) |
| conditional.setdefault("equal", set()).add(flag) |
| elif operator == "!=": |
| if flag not in parent_use and flag not in other_use: |
| if is_valid_flag(flag): |
| tokens.append(x) |
| conditional.setdefault("not_equal", set()).add(flag) |
| else: |
| if flag in self.missing_disabled: |
| tokens.append(x) |
| conditional.setdefault("not_equal", set()).add(flag) |
| elif flag in parent_use: |
| if flag not in other_use: |
| if not is_valid_flag(flag): |
| if flag in self.missing_enabled: |
| tokens.append(x) |
| conditional.setdefault("not_equal", set()).add(flag) |
| else: |
| tokens.append(x) |
| conditional.setdefault("not_equal", set()).add(flag) |
| elif operator == "!?": |
| if flag not in parent_use: |
| if flag not in other_use: |
| if not is_valid_flag(flag) and flag in self.missing_enabled: |
| tokens.append(x) |
| conditional.setdefault("disabled", set()).add(flag) |
| else: |
| tokens.append(x) |
| conditional.setdefault("disabled", set()).add(flag) |
| |
| return _use_dep(tokens, self._eapi_attrs, enabled_flags=enabled_flags, disabled_flags=disabled_flags, |
| missing_enabled=self.missing_enabled, missing_disabled=self.missing_disabled, \ |
| conditional=conditional, required=self.required) |
| |
| def _eval_qa_conditionals(self, use_mask, use_force): |
| """ |
| For repoman, evaluate all possible combinations within the constraints |
| of the given use.force and use.mask settings. The result may seem |
| ambiguous in the sense that the same flag can be in both the enabled |
| and disabled sets, but this is useful within the context of how its |
| intended to be used by repoman. It is assumed that the caller has |
| already ensured that there is no intersection between the given |
| use_mask and use_force sets when necessary. |
| """ |
| enabled_flags = set(self.enabled) |
| disabled_flags = set(self.disabled) |
| missing_enabled = self.missing_enabled |
| missing_disabled = self.missing_disabled |
| |
| tokens = [] |
| usedep_re = _get_usedep_re(self._eapi_attrs) |
| |
| for x in self.tokens: |
| m = usedep_re.match(x) |
| |
| operator = m.group("prefix") + m.group("suffix") |
| flag = m.group("flag") |
| default = m.group("default") |
| if default is None: |
| default = "" |
| |
| if operator == "?": |
| if flag not in use_mask: |
| enabled_flags.add(flag) |
| tokens.append(flag+default) |
| elif operator == "=": |
| if flag not in use_mask: |
| enabled_flags.add(flag) |
| tokens.append(flag+default) |
| if flag not in use_force: |
| disabled_flags.add(flag) |
| tokens.append("-"+flag+default) |
| elif operator == "!=": |
| if flag not in use_force: |
| enabled_flags.add(flag) |
| tokens.append(flag+default) |
| if flag not in use_mask: |
| disabled_flags.add(flag) |
| tokens.append("-"+flag+default) |
| elif operator == "!?": |
| if flag not in use_force: |
| disabled_flags.add(flag) |
| tokens.append("-"+flag+default) |
| else: |
| tokens.append(x) |
| |
| return _use_dep(tokens, self._eapi_attrs, enabled_flags=enabled_flags, disabled_flags=disabled_flags, |
| missing_enabled=missing_enabled, missing_disabled=missing_disabled, required=self.required) |
| |
| class Atom(_unicode): |
| |
| """ |
| For compatibility with existing atom string manipulation code, this |
| class emulates most of the str methods that are useful with atoms. |
| """ |
| |
| class _blocker(object): |
| __slots__ = ("overlap",) |
| |
| class _overlap(object): |
| __slots__ = ("forbid",) |
| |
| def __init__(self, forbid=False): |
| self.forbid = forbid |
| |
| def __init__(self, forbid_overlap=False): |
| self.overlap = self._overlap(forbid=forbid_overlap) |
| |
| def __new__(cls, s, unevaluated_atom=None, allow_wildcard=False, allow_repo=None, |
| _use=None, eapi=None, is_valid_flag=None): |
| return _unicode.__new__(cls, s) |
| |
| def __init__(self, s, unevaluated_atom=None, allow_wildcard=False, allow_repo=None, |
| _use=None, eapi=None, is_valid_flag=None): |
| if isinstance(s, Atom): |
| # This is an efficiency assertion, to ensure that the Atom |
| # constructor is not called redundantly. |
| raise TypeError(_("Expected %s, got %s") % \ |
| (_unicode, type(s))) |
| |
| if not isinstance(s, _unicode): |
| # Avoid TypeError from _unicode.__init__ with PyPy. |
| s = _unicode_decode(s) |
| |
| _unicode.__init__(s) |
| |
| eapi_attrs = _get_eapi_attrs(eapi) |
| atom_re = _get_atom_re(eapi_attrs) |
| |
| self.__dict__['eapi'] = eapi |
| if eapi is not None: |
| # Ignore allow_repo when eapi is specified. |
| allow_repo = eapi_attrs.repo_deps |
| else: |
| if allow_repo is None: |
| allow_repo = True |
| |
| blocker_prefix = "" |
| if "!" == s[:1]: |
| blocker = self._blocker(forbid_overlap=("!" == s[1:2])) |
| if blocker.overlap.forbid: |
| blocker_prefix = s[:2] |
| s = s[2:] |
| else: |
| blocker_prefix = s[:1] |
| s = s[1:] |
| else: |
| blocker = False |
| self.__dict__['blocker'] = blocker |
| m = atom_re.match(s) |
| extended_syntax = False |
| extended_version = None |
| if m is None: |
| if allow_wildcard: |
| atom_re = _get_atom_wildcard_re(eapi_attrs) |
| m = atom_re.match(s) |
| if m is None: |
| raise InvalidAtom(self) |
| gdict = m.groupdict() |
| if m.group('star') is not None: |
| op = '=*' |
| base = atom_re.groupindex['star'] |
| cp = m.group(base + 1) |
| cpv = m.group('star')[1:] |
| extended_version = m.group(base + 4) |
| else: |
| op = None |
| cpv = cp = m.group('simple') |
| if cpv.find("**") != -1: |
| raise InvalidAtom(self) |
| slot = m.group('slot') |
| repo = m.group('repo') |
| use_str = None |
| extended_syntax = True |
| else: |
| raise InvalidAtom(self) |
| elif m.group('op') is not None: |
| base = atom_re.groupindex['op'] |
| op = m.group(base + 1) |
| cpv = m.group(base + 2) |
| cp = m.group(base + 3) |
| slot = m.group(atom_re.groups - 2) |
| repo = m.group(atom_re.groups - 1) |
| use_str = m.group(atom_re.groups) |
| if m.group(base + 4) is not None: |
| raise InvalidAtom(self) |
| elif m.group('star') is not None: |
| base = atom_re.groupindex['star'] |
| op = '=*' |
| cpv = m.group(base + 1) |
| cp = m.group(base + 2) |
| slot = m.group(atom_re.groups - 2) |
| repo = m.group(atom_re.groups - 1) |
| use_str = m.group(atom_re.groups) |
| if m.group(base + 3) is not None: |
| raise InvalidAtom(self) |
| elif m.group('simple') is not None: |
| op = None |
| cpv = cp = m.group(atom_re.groupindex['simple'] + 1) |
| slot = m.group(atom_re.groups - 2) |
| repo = m.group(atom_re.groups - 1) |
| use_str = m.group(atom_re.groups) |
| if m.group(atom_re.groupindex['simple'] + 2) is not None: |
| raise InvalidAtom(self) |
| |
| else: |
| raise AssertionError(_("required group not found in atom: '%s'") % self) |
| self.__dict__['cp'] = cp |
| try: |
| self.__dict__['cpv'] = _pkg_str(cpv) |
| self.__dict__['version'] = self.cpv.version |
| except InvalidData: |
| # plain cp, wildcard, or something |
| self.__dict__['cpv'] = cpv |
| self.__dict__['version'] = extended_version |
| self.__dict__['repo'] = repo |
| if slot is None: |
| self.__dict__['slot'] = None |
| self.__dict__['sub_slot'] = None |
| self.__dict__['slot_operator'] = None |
| else: |
| slot_re = _get_slot_dep_re(eapi_attrs) |
| slot_match = slot_re.match(slot) |
| if slot_match is None: |
| raise InvalidAtom(self) |
| if eapi_attrs.slot_operator: |
| self.__dict__['slot'] = slot_match.group(1) |
| sub_slot = slot_match.group(2) |
| if sub_slot is not None: |
| sub_slot = sub_slot.lstrip("/") |
| if sub_slot in ("*", "="): |
| self.__dict__['sub_slot'] = None |
| self.__dict__['slot_operator'] = sub_slot |
| else: |
| slot_operator = None |
| if sub_slot is not None and sub_slot[-1:] == "=": |
| slot_operator = sub_slot[-1:] |
| sub_slot = sub_slot[:-1] |
| self.__dict__['sub_slot'] = sub_slot |
| self.__dict__['slot_operator'] = slot_operator |
| if self.slot is not None and self.slot_operator == "*": |
| raise InvalidAtom(self) |
| else: |
| self.__dict__['slot'] = slot |
| self.__dict__['sub_slot'] = None |
| self.__dict__['slot_operator'] = None |
| self.__dict__['operator'] = op |
| self.__dict__['extended_syntax'] = extended_syntax |
| |
| if not (repo is None or allow_repo): |
| raise InvalidAtom(self) |
| |
| if use_str is not None: |
| if _use is not None: |
| use = _use |
| else: |
| use = _use_dep(use_str[1:-1].split(","), eapi_attrs) |
| without_use = Atom(blocker_prefix + m.group('without_use'), |
| allow_repo=allow_repo) |
| else: |
| use = None |
| if unevaluated_atom is not None and \ |
| unevaluated_atom.use is not None: |
| # unevaluated_atom.use is used for IUSE checks when matching |
| # packages, so it must not propagate to without_use |
| without_use = Atom(_unicode(self), |
| allow_wildcard=allow_wildcard, |
| allow_repo=allow_repo, |
| eapi=eapi) |
| else: |
| without_use = self |
| |
| self.__dict__['use'] = use |
| self.__dict__['without_use'] = without_use |
| |
| if unevaluated_atom: |
| self.__dict__['unevaluated_atom'] = unevaluated_atom |
| else: |
| self.__dict__['unevaluated_atom'] = self |
| |
| if eapi is not None: |
| if not isinstance(eapi, basestring): |
| raise TypeError('expected eapi argument of ' + \ |
| '%s, got %s: %s' % (basestring, type(eapi), eapi,)) |
| if self.slot and not eapi_attrs.slot_deps: |
| raise InvalidAtom( |
| _("Slot deps are not allowed in EAPI %s: '%s'") \ |
| % (eapi, self), category='EAPI.incompatible') |
| if self.use: |
| if not eapi_attrs.use_deps: |
| raise InvalidAtom( |
| _("Use deps are not allowed in EAPI %s: '%s'") \ |
| % (eapi, self), category='EAPI.incompatible') |
| elif not eapi_attrs.use_dep_defaults and \ |
| (self.use.missing_enabled or self.use.missing_disabled): |
| raise InvalidAtom( |
| _("Use dep defaults are not allowed in EAPI %s: '%s'") \ |
| % (eapi, self), category='EAPI.incompatible') |
| if is_valid_flag is not None and self.use.conditional: |
| invalid_flag = None |
| try: |
| for conditional_type, flags in \ |
| self.use.conditional.items(): |
| for flag in flags: |
| if not is_valid_flag(flag): |
| invalid_flag = (conditional_type, flag) |
| raise StopIteration() |
| except StopIteration: |
| pass |
| if invalid_flag is not None: |
| conditional_type, flag = invalid_flag |
| conditional_str = _use_dep._conditional_strings[conditional_type] |
| msg = _("USE flag '%s' referenced in " + \ |
| "conditional '%s' in atom '%s' is not in IUSE") \ |
| % (flag, conditional_str % flag, self) |
| raise InvalidAtom(msg, category='IUSE.missing') |
| if self.blocker and self.blocker.overlap.forbid and not eapi_attrs.strong_blocks: |
| raise InvalidAtom( |
| _("Strong blocks are not allowed in EAPI %s: '%s'") \ |
| % (eapi, self), category='EAPI.incompatible') |
| |
| @property |
| def slot_operator_built(self): |
| """ |
| Returns True if slot_operator == "=" and sub_slot is not None. |
| NOTE: foo/bar:2= is unbuilt and returns False, whereas foo/bar:2/2= |
| is built and returns True. |
| """ |
| return self.slot_operator == "=" and self.sub_slot is not None |
| |
| @property |
| def without_repo(self): |
| if self.repo is None: |
| return self |
| return Atom(self.replace(_repo_separator + self.repo, '', 1), |
| allow_wildcard=True) |
| |
| @property |
| def without_slot(self): |
| if self.slot is None and self.slot_operator is None: |
| return self |
| atom = remove_slot(self) |
| if self.repo is not None: |
| atom += _repo_separator + self.repo |
| if self.use is not None: |
| atom += _unicode(self.use) |
| return Atom(atom, |
| allow_repo=True, allow_wildcard=True) |
| |
| def with_repo(self, repo): |
| atom = remove_slot(self) |
| if self.slot is not None or self.slot_operator is not None: |
| atom += _slot_separator |
| if self.slot is not None: |
| atom += self.slot |
| if self.sub_slot is not None: |
| atom += "/%s" % self.sub_slot |
| if self.slot_operator is not None: |
| atom += self.slot_operator |
| atom += _repo_separator + repo |
| if self.use is not None: |
| atom += _unicode(self.use) |
| return Atom(atom, allow_repo=True, allow_wildcard=True) |
| |
| def with_slot(self, slot): |
| atom = remove_slot(self) + _slot_separator + slot |
| if self.repo is not None: |
| atom += _repo_separator + self.repo |
| if self.use is not None: |
| atom += _unicode(self.use) |
| return Atom(atom, allow_repo=True, allow_wildcard=True) |
| |
| def __setattr__(self, name, value): |
| raise AttributeError("Atom instances are immutable", |
| self.__class__, name, value) |
| |
| def intersects(self, other): |
| """ |
| Atoms with different cpv, operator or use attributes cause this method |
| to return False even though there may actually be some intersection. |
| TODO: Detect more forms of intersection. |
| @param other: The package atom to match |
| @type other: Atom |
| @rtype: Boolean |
| @return: True if this atom and the other atom intersect, |
| False otherwise. |
| """ |
| if not isinstance(other, Atom): |
| raise TypeError("expected %s, got %s" % \ |
| (Atom, type(other))) |
| |
| if self == other: |
| return True |
| |
| if self.cp != other.cp or \ |
| self.use != other.use or \ |
| self.operator != other.operator or \ |
| self.cpv != other.cpv: |
| return False |
| |
| if self.slot is None or \ |
| other.slot is None or \ |
| self.slot == other.slot: |
| return True |
| |
| return False |
| |
| def evaluate_conditionals(self, use): |
| """ |
| Create an atom instance with any USE conditionals evaluated. |
| @param use: The set of enabled USE flags |
| @type use: set |
| @rtype: Atom |
| @return: an atom instance with any USE conditionals evaluated |
| """ |
| if not (self.use and self.use.conditional): |
| return self |
| atom = remove_slot(self) |
| if self.slot is not None or self.slot_operator is not None: |
| atom += _slot_separator |
| if self.slot is not None: |
| atom += self.slot |
| if self.sub_slot is not None: |
| atom += "/%s" % self.sub_slot |
| if self.slot_operator is not None: |
| atom += self.slot_operator |
| use_dep = self.use.evaluate_conditionals(use) |
| atom += _unicode(use_dep) |
| return Atom(atom, unevaluated_atom=self, allow_repo=(self.repo is not None), _use=use_dep) |
| |
| def violated_conditionals(self, other_use, is_valid_flag, parent_use=None): |
| """ |
| Create an atom instance with any USE conditional removed, that is |
| satisfied by other_use. |
| @param other_use: The set of enabled USE flags |
| @type other_use: set |
| @param is_valid_flag: Function that decides if a use flag is referenceable in use deps |
| @type is_valid_flag: function |
| @param parent_use: Set of enabled use flags of the package requiring this atom |
| @type parent_use: set |
| @rtype: Atom |
| @return: an atom instance with any satisfied USE conditionals removed |
| """ |
| if not self.use: |
| return self |
| atom = remove_slot(self) |
| if self.slot is not None or self.slot_operator is not None: |
| atom += _slot_separator |
| if self.slot is not None: |
| atom += self.slot |
| if self.sub_slot is not None: |
| atom += "/%s" % self.sub_slot |
| if self.slot_operator is not None: |
| atom += self.slot_operator |
| use_dep = self.use.violated_conditionals(other_use, is_valid_flag, parent_use) |
| atom += _unicode(use_dep) |
| return Atom(atom, unevaluated_atom=self, allow_repo=(self.repo is not None), _use=use_dep) |
| |
| def _eval_qa_conditionals(self, use_mask, use_force): |
| if not (self.use and self.use.conditional): |
| return self |
| atom = remove_slot(self) |
| if self.slot is not None or self.slot_operator is not None: |
| atom += _slot_separator |
| if self.slot is not None: |
| atom += self.slot |
| if self.sub_slot is not None: |
| atom += "/%s" % self.sub_slot |
| if self.slot_operator is not None: |
| atom += self.slot_operator |
| use_dep = self.use._eval_qa_conditionals(use_mask, use_force) |
| atom += _unicode(use_dep) |
| return Atom(atom, unevaluated_atom=self, allow_repo=(self.repo is not None), _use=use_dep) |
| |
| def __copy__(self): |
| """Immutable, so returns self.""" |
| return self |
| |
| def __deepcopy__(self, memo=None): |
| """Immutable, so returns self.""" |
| memo[id(self)] = self |
| return self |
| |
| _extended_cp_re_cache = {} |
| |
| def extended_cp_match(extended_cp, other_cp): |
| """ |
| Checks if an extended syntax cp matches a non extended cp |
| """ |
| # Escape special '+' and '.' characters which are allowed in atoms, |
| # and convert '*' to regex equivalent. |
| global _extended_cp_re_cache |
| extended_cp_re = _extended_cp_re_cache.get(extended_cp) |
| if extended_cp_re is None: |
| extended_cp_re = re.compile("^" + re.escape(extended_cp).replace( |
| r'\*', '[^/]*') + "$", re.UNICODE) |
| _extended_cp_re_cache[extended_cp] = extended_cp_re |
| return extended_cp_re.match(other_cp) is not None |
| |
| class ExtendedAtomDict(portage.cache.mappings.MutableMapping): |
| """ |
| dict() wrapper that supports extended atoms as keys and allows lookup |
| of a normal cp against other normal cp and extended cp. |
| The value type has to be given to __init__ and is assumed to be the same |
| for all values. |
| """ |
| |
| __slots__ = ('_extended', '_normal', '_value_class') |
| |
| def __init__(self, value_class): |
| self._extended = {} |
| self._normal = {} |
| self._value_class = value_class |
| |
| def copy(self): |
| result = self.__class__(self._value_class) |
| result._extended.update(self._extended) |
| result._normal.update(self._normal) |
| return result |
| |
| def __iter__(self): |
| for k in self._normal: |
| yield k |
| for k in self._extended: |
| yield k |
| |
| def iteritems(self): |
| try: |
| for item in self._normal.items(): |
| yield item |
| for item in self._extended.items(): |
| yield item |
| except AttributeError: |
| pass # FEATURES=python-trace |
| |
| def __delitem__(self, cp): |
| if "*" in cp: |
| return self._extended.__delitem__(cp) |
| else: |
| return self._normal.__delitem__(cp) |
| |
| if sys.hexversion >= 0x3000000: |
| keys = __iter__ |
| items = iteritems |
| |
| def __len__(self): |
| return len(self._normal) + len(self._extended) |
| |
| def setdefault(self, cp, default=None): |
| if "*" in cp: |
| return self._extended.setdefault(cp, default) |
| else: |
| return self._normal.setdefault(cp, default) |
| |
| def __getitem__(self, cp): |
| |
| if not isinstance(cp, basestring): |
| raise KeyError(cp) |
| |
| if '*' in cp: |
| return self._extended[cp] |
| |
| ret = self._value_class() |
| normal_match = self._normal.get(cp) |
| match = False |
| |
| if normal_match is not None: |
| match = True |
| if hasattr(ret, "update"): |
| ret.update(normal_match) |
| elif hasattr(ret, "extend"): |
| ret.extend(normal_match) |
| else: |
| raise NotImplementedError() |
| |
| for extended_cp in self._extended: |
| if extended_cp_match(extended_cp, cp): |
| match = True |
| if hasattr(ret, "update"): |
| ret.update(self._extended[extended_cp]) |
| elif hasattr(ret, "extend"): |
| ret.extend(self._extended[extended_cp]) |
| else: |
| raise NotImplementedError() |
| |
| if not match: |
| raise KeyError(cp) |
| |
| return ret |
| |
| def __setitem__(self, cp, val): |
| if "*" in cp: |
| self._extended[cp] = val |
| else: |
| self._normal[cp] = val |
| |
| def __eq__(self, other): |
| return self._value_class == other._value_class and \ |
| self._extended == other._extended and \ |
| self._normal == other._normal |
| |
| def clear(self): |
| self._extended.clear() |
| self._normal.clear() |
| |
| |
| def get_operator(mydep): |
| """ |
| Return the operator used in a depstring. |
| |
| Example usage: |
| >>> from portage.dep import * |
| >>> get_operator(">=test-1.0") |
| '>=' |
| |
| @param mydep: The dep string to check |
| @type mydep: String |
| @rtype: String |
| @return: The operator. One of: |
| '~', '=', '>', '<', '=*', '>=', or '<=' |
| """ |
| if not isinstance(mydep, Atom): |
| mydep = Atom(mydep) |
| |
| return mydep.operator |
| |
| def dep_getcpv(mydep): |
| """ |
| Return the category-package-version with any operators/slot specifications stripped off |
| |
| Example usage: |
| >>> dep_getcpv('>=media-libs/test-3.0') |
| 'media-libs/test-3.0' |
| |
| @param mydep: The depstring |
| @type mydep: String |
| @rtype: String |
| @return: The depstring with the operator removed |
| """ |
| if not isinstance(mydep, Atom): |
| mydep = Atom(mydep) |
| |
| return mydep.cpv |
| |
| def dep_getslot(mydep): |
| """ |
| Retrieve the slot on a depend. |
| |
| Example usage: |
| >>> dep_getslot('app-misc/test:3') |
| '3' |
| |
| @param mydep: The depstring to retrieve the slot of |
| @type mydep: String |
| @rtype: String |
| @return: The slot |
| """ |
| slot = getattr(mydep, "slot", False) |
| if slot is not False: |
| return slot |
| |
| #remove repo_name if present |
| mydep = mydep.split(_repo_separator)[0] |
| |
| colon = mydep.find(_slot_separator) |
| if colon != -1: |
| bracket = mydep.find("[", colon) |
| if bracket == -1: |
| return mydep[colon+1:] |
| else: |
| return mydep[colon+1:bracket] |
| return None |
| |
| def dep_getrepo(mydep): |
| """ |
| Retrieve the repo on a depend. |
| |
| Example usage: |
| >>> dep_getrepo('app-misc/test::repository') |
| 'repository' |
| |
| @param mydep: The depstring to retrieve the repository of |
| @type mydep: String |
| @rtype: String |
| @return: The repository name |
| """ |
| repo = getattr(mydep, "repo", False) |
| if repo is not False: |
| return repo |
| |
| metadata = getattr(mydep, "metadata", False) |
| if metadata: |
| repo = metadata.get('repository', False) |
| if repo is not False: |
| return repo |
| |
| colon = mydep.find(_repo_separator) |
| if colon != -1: |
| bracket = mydep.find("[", colon) |
| if bracket == -1: |
| return mydep[colon+2:] |
| else: |
| return mydep[colon+2:bracket] |
| return None |
| def remove_slot(mydep): |
| """ |
| Removes dep components from the right side of an atom: |
| * slot |
| * use |
| * repo |
| And repo_name from the left side. |
| """ |
| colon = mydep.find(_slot_separator) |
| if colon != -1: |
| mydep = mydep[:colon] |
| else: |
| bracket = mydep.find("[") |
| if bracket != -1: |
| mydep = mydep[:bracket] |
| return mydep |
| |
| def dep_getusedeps( depend ): |
| """ |
| Pull a listing of USE Dependencies out of a dep atom. |
| |
| Example usage: |
| >>> dep_getusedeps('app-misc/test:3[foo,-bar]') |
| ('foo', '-bar') |
| |
| @param depend: The depstring to process |
| @type depend: String |
| @rtype: List |
| @return: List of use flags ( or [] if no flags exist ) |
| """ |
| use_list = [] |
| open_bracket = depend.find('[') |
| # -1 = failure (think c++ string::npos) |
| comma_separated = False |
| bracket_count = 0 |
| while( open_bracket != -1 ): |
| bracket_count += 1 |
| if bracket_count > 1: |
| raise InvalidAtom(_("USE Dependency with more " |
| "than one set of brackets: %s") % (depend,)) |
| close_bracket = depend.find(']', open_bracket ) |
| if close_bracket == -1: |
| raise InvalidAtom(_("USE Dependency with no closing bracket: %s") % depend ) |
| use = depend[open_bracket + 1: close_bracket] |
| # foo[1:1] may return '' instead of None, we don't want '' in the result |
| if not use: |
| raise InvalidAtom(_("USE Dependency with " |
| "no use flag ([]): %s") % depend ) |
| if not comma_separated: |
| comma_separated = "," in use |
| |
| if comma_separated and bracket_count > 1: |
| raise InvalidAtom(_("USE Dependency contains a mixture of " |
| "comma and bracket separators: %s") % depend ) |
| |
| if comma_separated: |
| for x in use.split(","): |
| if x: |
| use_list.append(x) |
| else: |
| raise InvalidAtom(_("USE Dependency with no use " |
| "flag next to comma: %s") % depend ) |
| else: |
| use_list.append(use) |
| |
| # Find next use flag |
| open_bracket = depend.find( '[', open_bracket+1 ) |
| return tuple(use_list) |
| |
| def isvalidatom(atom, allow_blockers=False, allow_wildcard=False, |
| allow_repo=False, eapi=None): |
| """ |
| Check to see if a depend atom is valid |
| |
| Example usage: |
| >>> isvalidatom('media-libs/test-3.0') |
| False |
| >>> isvalidatom('>=media-libs/test-3.0') |
| True |
| |
| @param atom: The depend atom to check against |
| @type atom: String or Atom |
| @rtype: Boolean |
| @return: One of the following: |
| 1) False if the atom is invalid |
| 2) True if the atom is valid |
| """ |
| |
| if eapi is not None and isinstance(atom, Atom) and atom.eapi != eapi: |
| # We'll construct a new atom with the given eapi. |
| atom = _unicode(atom) |
| |
| try: |
| if not isinstance(atom, Atom): |
| atom = Atom(atom, allow_wildcard=allow_wildcard, |
| allow_repo=allow_repo, eapi=eapi) |
| if not allow_blockers and atom.blocker: |
| return False |
| return True |
| except InvalidAtom: |
| return False |
| |
| def isjustname(mypkg): |
| """ |
| Checks to see if the atom is only the package name (no version parts). |
| |
| Example usage: |
| >>> isjustname('=media-libs/test-3.0') |
| False |
| >>> isjustname('media-libs/test') |
| True |
| |
| @param mypkg: The package atom to check |
| @param mypkg: String or Atom |
| @rtype: Integer |
| @return: One of the following: |
| 1) False if the package string is not just the package name |
| 2) True if it is |
| """ |
| try: |
| if not isinstance(mypkg, Atom): |
| mypkg = Atom(mypkg) |
| return mypkg == mypkg.cp |
| except InvalidAtom: |
| pass |
| |
| for x in mypkg.split('-')[-2:]: |
| if ververify(x): |
| return False |
| return True |
| |
| def isspecific(mypkg): |
| """ |
| Checks to see if a package is in =category/package-version or |
| package-version format. |
| |
| Example usage: |
| >>> isspecific('media-libs/test') |
| False |
| >>> isspecific('=media-libs/test-3.0') |
| True |
| |
| @param mypkg: The package depstring to check against |
| @type mypkg: String |
| @rtype: Boolean |
| @return: One of the following: |
| 1) False if the package string is not specific |
| 2) True if it is |
| """ |
| try: |
| if not isinstance(mypkg, Atom): |
| mypkg = Atom(mypkg) |
| return mypkg != mypkg.cp |
| except InvalidAtom: |
| pass |
| |
| # Fall back to legacy code for backward compatibility. |
| return not isjustname(mypkg) |
| |
| def dep_getkey(mydep): |
| """ |
| Return the category/package-name of a depstring. |
| |
| Example usage: |
| >>> dep_getkey('=media-libs/test-3.0') |
| 'media-libs/test' |
| |
| @param mydep: The depstring to retrieve the category/package-name of |
| @type mydep: String |
| @rtype: String |
| @return: The package category/package-name |
| """ |
| if not isinstance(mydep, Atom): |
| mydep = Atom(mydep, allow_wildcard=True, allow_repo=True) |
| |
| return mydep.cp |
| |
| def match_to_list(mypkg, mylist): |
| """ |
| Searches list for entries that matches the package. |
| |
| @param mypkg: The package atom to match |
| @type mypkg: String |
| @param mylist: The list of package atoms to compare against |
| @param mylist: List |
| @rtype: List |
| @return: A unique list of package atoms that match the given package atom |
| """ |
| matches = set() |
| result = [] |
| pkgs = [mypkg] |
| for x in mylist: |
| if x not in matches and match_from_list(x, pkgs): |
| matches.add(x) |
| result.append(x) |
| return result |
| |
| def best_match_to_list(mypkg, mylist): |
| """ |
| Returns the most specific entry that matches the package given. |
| |
| @param mypkg: The package atom to check |
| @type mypkg: String |
| @param mylist: The list of package atoms to check against |
| @type mylist: List |
| @rtype: String |
| @return: The package atom which best matches given the following ordering: |
| - =cpv 6 |
| - ~cpv 5 |
| - =cpv* 4 |
| - cp:slot 3 |
| - >cpv 2 |
| - <cpv 2 |
| - >=cpv 2 |
| - <=cpv 2 |
| - cp 1 |
| - cp:slot with extended syntax 0 |
| - cp with extended syntax -1 |
| """ |
| operator_values = {'=':6, '~':5, '=*':4, |
| '>':2, '<':2, '>=':2, '<=':2, None:1} |
| maxvalue = -99 |
| bestm = None |
| mypkg_cpv = None |
| for x in match_to_list(mypkg, mylist): |
| if x.extended_syntax: |
| if x.operator == '=*': |
| if maxvalue < 0: |
| maxvalue = 0 |
| bestm = x |
| elif x.slot is not None: |
| if maxvalue < -1: |
| maxvalue = -1 |
| bestm = x |
| else: |
| if maxvalue < -2: |
| maxvalue = -2 |
| bestm = x |
| continue |
| if dep_getslot(x) is not None: |
| if maxvalue < 3: |
| maxvalue = 3 |
| bestm = x |
| op_val = operator_values[x.operator] |
| if op_val > maxvalue: |
| maxvalue = op_val |
| bestm = x |
| elif op_val == maxvalue and op_val == 2: |
| # For >, <, >=, and <=, the one with the version |
| # closest to mypkg is the best match. |
| if mypkg_cpv is None: |
| try: |
| mypkg_cpv = mypkg.cpv |
| except AttributeError: |
| mypkg_cpv = _pkg_str(remove_slot(mypkg)) |
| if bestm.cpv == mypkg_cpv or bestm.cpv == x.cpv: |
| pass |
| elif x.cpv == mypkg_cpv: |
| bestm = x |
| else: |
| # Sort the cpvs to find the one closest to mypkg_cpv |
| cpv_list = [bestm.cpv, mypkg_cpv, x.cpv] |
| def cmp_cpv(cpv1, cpv2): |
| return vercmp(cpv1.version, cpv2.version) |
| cpv_list.sort(key=cmp_sort_key(cmp_cpv)) |
| if cpv_list[0] is mypkg_cpv or cpv_list[-1] is mypkg_cpv: |
| if cpv_list[1] is x.cpv: |
| bestm = x |
| else: |
| # TODO: handle the case where mypkg_cpv is in the middle |
| pass |
| |
| return bestm |
| |
| def match_from_list(mydep, candidate_list): |
| """ |
| Searches list for entries that matches the package. |
| |
| @param mydep: The package atom to match |
| @type mydep: String |
| @param candidate_list: The list of package atoms to compare against |
| @param candidate_list: List |
| @rtype: List |
| @return: A list of package atoms that match the given package atom |
| """ |
| |
| if not candidate_list: |
| return [] |
| |
| if "!" == mydep[:1]: |
| if "!" == mydep[1:2]: |
| mydep = mydep[2:] |
| else: |
| mydep = mydep[1:] |
| if not isinstance(mydep, Atom): |
| mydep = Atom(mydep, allow_wildcard=True, allow_repo=True) |
| |
| mycpv = mydep.cpv |
| mycpv_cps = catpkgsplit(mycpv) # Can be None if not specific |
| slot = mydep.slot |
| |
| if not mycpv_cps: |
| cat, pkg = catsplit(mycpv) |
| ver = None |
| rev = None |
| else: |
| cat, pkg, ver, rev = mycpv_cps |
| if mydep == mycpv: |
| raise KeyError(_("Specific key requires an operator" |
| " (%s) (try adding an '=')") % (mydep)) |
| |
| if ver and rev: |
| operator = mydep.operator |
| if not operator: |
| writemsg(_("!!! Invalid atom: %s\n") % mydep, noiselevel=-1) |
| return [] |
| else: |
| operator = None |
| |
| mylist = [] |
| |
| if mydep.extended_syntax: |
| |
| for x in candidate_list: |
| cp = getattr(x, "cp", None) |
| if cp is None: |
| mysplit = catpkgsplit(remove_slot(x)) |
| if mysplit is not None: |
| cp = mysplit[0] + '/' + mysplit[1] |
| |
| if cp is None: |
| continue |
| |
| if cp == mycpv or extended_cp_match(mydep.cp, cp): |
| mylist.append(x) |
| |
| if mylist and mydep.operator == "=*": |
| |
| candidate_list = mylist |
| mylist = [] |
| # Currently, only \*\w+\* is supported. |
| ver = mydep.version[1:-1] |
| |
| for x in candidate_list: |
| x_ver = getattr(x, "version", None) |
| if x_ver is None: |
| xs = catpkgsplit(remove_slot(x)) |
| if xs is None: |
| continue |
| x_ver = "-".join(xs[-2:]) |
| if ver in x_ver: |
| mylist.append(x) |
| |
| elif operator is None: |
| for x in candidate_list: |
| cp = getattr(x, "cp", None) |
| if cp is None: |
| mysplit = catpkgsplit(remove_slot(x)) |
| if mysplit is not None: |
| cp = mysplit[0] + '/' + mysplit[1] |
| |
| if cp is None: |
| continue |
| |
| if cp == mydep.cp: |
| mylist.append(x) |
| |
| elif operator == "=": # Exact match |
| for x in candidate_list: |
| xcpv = getattr(x, "cpv", None) |
| if xcpv is None: |
| xcpv = remove_slot(x) |
| if not cpvequal(xcpv, mycpv): |
| continue |
| mylist.append(x) |
| |
| elif operator == "=*": # glob match |
| # XXX: Nasty special casing for leading zeros |
| # Required as =* is a literal prefix match, so can't |
| # use vercmp |
| myver = mycpv_cps[2].lstrip("0") |
| if not myver or not myver[0].isdigit(): |
| myver = "0"+myver |
| mycpv_cmp = mycpv_cps[0] + "/" + mycpv_cps[1] + "-" + myver |
| for x in candidate_list: |
| xs = getattr(x, "cpv_split", None) |
| if xs is None: |
| xs = catpkgsplit(remove_slot(x)) |
| myver = xs[2].lstrip("0") |
| if not myver or not myver[0].isdigit(): |
| myver = "0"+myver |
| xcpv = xs[0]+"/"+xs[1]+"-"+myver |
| if xcpv.startswith(mycpv_cmp): |
| mylist.append(x) |
| |
| elif operator == "~": # version, any revision, match |
| for x in candidate_list: |
| xs = getattr(x, "cpv_split", None) |
| if xs is None: |
| xs = catpkgsplit(remove_slot(x)) |
| if xs is None: |
| raise InvalidData(x) |
| if not cpvequal(xs[0]+"/"+xs[1]+"-"+xs[2], mycpv_cps[0]+"/"+mycpv_cps[1]+"-"+mycpv_cps[2]): |
| continue |
| if xs[2] != ver: |
| continue |
| mylist.append(x) |
| |
| elif operator in [">", ">=", "<", "<="]: |
| for x in candidate_list: |
| if hasattr(x, 'cp'): |
| pkg = x |
| else: |
| try: |
| pkg = _pkg_str(remove_slot(x)) |
| except InvalidData: |
| continue |
| |
| if pkg.cp != mydep.cp: |
| continue |
| try: |
| result = vercmp(pkg.version, mydep.version) |
| except ValueError: # pkgcmp may return ValueError during int() conversion |
| writemsg(_("\nInvalid package name: %s\n") % x, noiselevel=-1) |
| raise |
| if result is None: |
| continue |
| elif operator == ">": |
| if result > 0: |
| mylist.append(x) |
| elif operator == ">=": |
| if result >= 0: |
| mylist.append(x) |
| elif operator == "<": |
| if result < 0: |
| mylist.append(x) |
| elif operator == "<=": |
| if result <= 0: |
| mylist.append(x) |
| else: |
| raise KeyError(_("Unknown operator: %s") % mydep) |
| else: |
| raise KeyError(_("Unknown operator: %s") % mydep) |
| |
| if mydep.slot is not None: |
| candidate_list = mylist |
| mylist = [] |
| for x in candidate_list: |
| x_pkg = None |
| try: |
| x.cpv |
| except AttributeError: |
| xslot = dep_getslot(x) |
| if xslot is not None: |
| try: |
| x_pkg = _pkg_str(remove_slot(x), slot=xslot) |
| except InvalidData: |
| continue |
| else: |
| x_pkg = x |
| |
| if x_pkg is None: |
| mylist.append(x) |
| else: |
| try: |
| x_pkg.slot |
| except AttributeError: |
| mylist.append(x) |
| else: |
| if _match_slot(mydep, x_pkg): |
| mylist.append(x) |
| |
| if mydep.unevaluated_atom.use: |
| candidate_list = mylist |
| mylist = [] |
| for x in candidate_list: |
| use = getattr(x, "use", None) |
| if use is not None: |
| if mydep.unevaluated_atom.use and \ |
| not x.iuse.is_valid_flag( |
| mydep.unevaluated_atom.use.required): |
| continue |
| |
| if mydep.use: |
| |
| missing_enabled = mydep.use.missing_enabled.difference(x.iuse.all) |
| missing_disabled = mydep.use.missing_disabled.difference(x.iuse.all) |
| |
| if mydep.use.enabled: |
| if any(f in mydep.use.enabled for f in missing_disabled): |
| continue |
| need_enabled = mydep.use.enabled.difference(use.enabled) |
| if need_enabled: |
| if any(f not in missing_enabled for f in need_enabled): |
| continue |
| |
| if mydep.use.disabled: |
| if any(f in mydep.use.disabled for f in missing_enabled): |
| continue |
| need_disabled = mydep.use.disabled.intersection(use.enabled) |
| if need_disabled: |
| if any(f not in missing_disabled for f in need_disabled): |
| continue |
| |
| mylist.append(x) |
| |
| if mydep.repo: |
| candidate_list = mylist |
| mylist = [] |
| for x in candidate_list: |
| repo = getattr(x, "repo", False) |
| if repo is False: |
| repo = dep_getrepo(x) |
| if repo is not None and repo != _unknown_repo and \ |
| repo != mydep.repo: |
| continue |
| mylist.append(x) |
| |
| return mylist |
| |
| def human_readable_required_use(required_use): |
| return required_use.replace("^^", "exactly-one-of").replace("||", "any-of").replace("??", "at-most-one-of") |
| |
| def get_required_use_flags(required_use, eapi=None): |
| """ |
| Returns a set of use flags that are used in the given REQUIRED_USE string |
| |
| @param required_use: REQUIRED_USE string |
| @type required_use: String |
| @rtype: Set |
| @return: Set of use flags that are used in the given REQUIRED_USE string |
| """ |
| |
| eapi_attrs = _get_eapi_attrs(eapi) |
| if eapi_attrs.required_use_at_most_one_of: |
| valid_operators = ("||", "^^", "??") |
| else: |
| valid_operators = ("||", "^^") |
| |
| mysplit = required_use.split() |
| level = 0 |
| stack = [[]] |
| need_bracket = False |
| |
| used_flags = set() |
| |
| def register_token(token): |
| if token.endswith("?"): |
| token = token[:-1] |
| if token.startswith("!"): |
| token = token[1:] |
| used_flags.add(token) |
| |
| for token in mysplit: |
| if token == "(": |
| need_bracket = False |
| stack.append([]) |
| level += 1 |
| elif token == ")": |
| if need_bracket: |
| raise InvalidDependString( |
| _("malformed syntax: '%s'") % required_use) |
| if level > 0: |
| level -= 1 |
| l = stack.pop() |
| ignore = False |
| if stack[level]: |
| if stack[level][-1] in valid_operators or \ |
| (not isinstance(stack[level][-1], bool) and \ |
| stack[level][-1][-1] == "?"): |
| ignore = True |
| stack[level].pop() |
| stack[level].append(True) |
| |
| if l and not ignore: |
| stack[level].append(all(x for x in l)) |
| else: |
| raise InvalidDependString( |
| _("malformed syntax: '%s'") % required_use) |
| elif token in valid_operators: |
| if need_bracket: |
| raise InvalidDependString( |
| _("malformed syntax: '%s'") % required_use) |
| need_bracket = True |
| stack[level].append(token) |
| else: |
| if need_bracket: |
| raise InvalidDependString( |
| _("malformed syntax: '%s'") % required_use) |
| |
| if token[-1] == "?": |
| need_bracket = True |
| stack[level].append(token) |
| else: |
| stack[level].append(True) |
| |
| register_token(token) |
| |
| if level != 0 or need_bracket: |
| raise InvalidDependString( |
| _("malformed syntax: '%s'") % required_use) |
| |
| return frozenset(used_flags) |
| |
| class _RequiredUseLeaf(object): |
| |
| __slots__ = ('_satisfied', '_token') |
| |
| def __init__(self, token, satisfied): |
| self._token = token |
| self._satisfied = satisfied |
| |
| def tounicode(self): |
| return self._token |
| |
| class _RequiredUseBranch(object): |
| |
| __slots__ = ('_children', '_operator', '_parent', '_satisfied') |
| |
| def __init__(self, operator=None, parent=None): |
| self._children = [] |
| self._operator = operator |
| self._parent = parent |
| self._satisfied = False |
| |
| def __bool__(self): |
| return self._satisfied |
| |
| def tounicode(self): |
| |
| include_parens = self._parent is not None |
| tokens = [] |
| if self._operator is not None: |
| tokens.append(self._operator) |
| |
| if include_parens: |
| tokens.append("(") |
| |
| complex_nesting = False |
| node = self |
| while node != None and not complex_nesting: |
| if node._operator in ("||", "^^", "??"): |
| complex_nesting = True |
| else: |
| node = node._parent |
| |
| if complex_nesting: |
| for child in self._children: |
| tokens.append(child.tounicode()) |
| else: |
| for child in self._children: |
| if not child._satisfied: |
| tokens.append(child.tounicode()) |
| |
| if include_parens: |
| tokens.append(")") |
| |
| return " ".join(tokens) |
| |
| if sys.hexversion < 0x3000000: |
| __nonzero__ = __bool__ |
| |
| def check_required_use(required_use, use, iuse_match, eapi=None): |
| """ |
| Checks if the use flags listed in 'use' satisfy all |
| constraints specified in 'constraints'. |
| |
| @param required_use: REQUIRED_USE string |
| @type required_use: String |
| @param use: Enabled use flags |
| @param use: List |
| @param iuse_match: Callable that takes a single flag argument and returns |
| True if the flag is matched, false otherwise, |
| @param iuse_match: Callable |
| @rtype: Bool |
| @return: Indicates if REQUIRED_USE constraints are satisfied |
| """ |
| |
| eapi_attrs = _get_eapi_attrs(eapi) |
| if eapi_attrs.required_use_at_most_one_of: |
| valid_operators = ("||", "^^", "??") |
| else: |
| valid_operators = ("||", "^^") |
| |
| def is_active(token): |
| if token.startswith("!"): |
| flag = token[1:] |
| is_negated = True |
| else: |
| flag = token |
| is_negated = False |
| |
| if not flag or not iuse_match(flag): |
| if not eapi_attrs.required_use_at_most_one_of and flag == "?": |
| msg = _("Operator '??' is not supported with EAPI '%s'") \ |
| % (eapi,) |
| e = InvalidData(msg, category='EAPI.incompatible') |
| raise InvalidDependString(msg, errors=(e,)) |
| msg = _("USE flag '%s' is not in IUSE") \ |
| % (flag,) |
| e = InvalidData(msg, category='IUSE.missing') |
| raise InvalidDependString(msg, errors=(e,)) |
| |
| return (flag in use and not is_negated) or \ |
| (flag not in use and is_negated) |
| |
| def is_satisfied(operator, argument): |
| if not argument: |
| #|| ( ) -> True |
| return True |
| |
| if operator == "||": |
| return (True in argument) |
| elif operator == "^^": |
| return (argument.count(True) == 1) |
| elif operator == "??": |
| return (argument.count(True) <= 1) |
| elif operator[-1] == "?": |
| return (False not in argument) |
| |
| mysplit = required_use.split() |
| level = 0 |
| stack = [[]] |
| tree = _RequiredUseBranch() |
| node = tree |
| need_bracket = False |
| |
| for token in mysplit: |
| if token == "(": |
| if not need_bracket: |
| child = _RequiredUseBranch(parent=node) |
| node._children.append(child) |
| node = child |
| |
| need_bracket = False |
| stack.append([]) |
| level += 1 |
| elif token == ")": |
| if need_bracket: |
| raise InvalidDependString( |
| _("malformed syntax: '%s'") % required_use) |
| if level > 0: |
| level -= 1 |
| l = stack.pop() |
| op = None |
| if stack[level]: |
| if stack[level][-1] in valid_operators: |
| op = stack[level].pop() |
| satisfied = is_satisfied(op, l) |
| stack[level].append(satisfied) |
| node._satisfied = satisfied |
| |
| elif not isinstance(stack[level][-1], bool) and \ |
| stack[level][-1][-1] == "?": |
| op = stack[level].pop() |
| if is_active(op[:-1]): |
| satisfied = is_satisfied(op, l) |
| stack[level].append(satisfied) |
| node._satisfied = satisfied |
| else: |
| node._satisfied = True |
| last_node = node._parent._children.pop() |
| if last_node is not node: |
| raise AssertionError( |
| "node is not last child of parent") |
| node = node._parent |
| continue |
| |
| if op is None: |
| satisfied = False not in l |
| node._satisfied = satisfied |
| if l: |
| stack[level].append(satisfied) |
| |
| if len(node._children) <= 1 or \ |
| node._parent._operator not in valid_operators: |
| last_node = node._parent._children.pop() |
| if last_node is not node: |
| raise AssertionError( |
| "node is not last child of parent") |
| for child in node._children: |
| node._parent._children.append(child) |
| if isinstance(child, _RequiredUseBranch): |
| child._parent = node._parent |
| |
| elif not node._children: |
| last_node = node._parent._children.pop() |
| if last_node is not node: |
| raise AssertionError( |
| "node is not last child of parent") |
| |
| elif len(node._children) == 1 and op in valid_operators: |
| last_node = node._parent._children.pop() |
| if last_node is not node: |
| raise AssertionError( |
| "node is not last child of parent") |
| node._parent._children.append(node._children[0]) |
| if isinstance(node._children[0], _RequiredUseBranch): |
| node._children[0]._parent = node._parent |
| node = node._children[0] |
| if node._operator is None and \ |
| node._parent._operator not in valid_operators: |
| last_node = node._parent._children.pop() |
| if last_node is not node: |
| raise AssertionError( |
| "node is not last child of parent") |
| for child in node._children: |
| node._parent._children.append(child) |
| if isinstance(child, _RequiredUseBranch): |
| child._parent = node._parent |
| |
| node = node._parent |
| else: |
| raise InvalidDependString( |
| _("malformed syntax: '%s'") % required_use) |
| elif token in valid_operators: |
| if need_bracket: |
| raise InvalidDependString( |
| _("malformed syntax: '%s'") % required_use) |
| need_bracket = True |
| stack[level].append(token) |
| child = _RequiredUseBranch(operator=token, parent=node) |
| node._children.append(child) |
| node = child |
| else: |
| if need_bracket: |
| raise InvalidDependString( |
| _("malformed syntax: '%s'") % required_use) |
| |
| if token[-1] == "?": |
| need_bracket = True |
| stack[level].append(token) |
| child = _RequiredUseBranch(operator=token, parent=node) |
| node._children.append(child) |
| node = child |
| else: |
| satisfied = is_active(token) |
| stack[level].append(satisfied) |
| node._children.append(_RequiredUseLeaf(token, satisfied)) |
| |
| if level != 0 or need_bracket: |
| raise InvalidDependString( |
| _("malformed syntax: '%s'") % required_use) |
| |
| tree._satisfied = False not in stack[0] |
| return tree |
| |
| def extract_affecting_use(mystr, atom, eapi=None): |
| """ |
| Take a dep string and an atom and return the use flags |
| that decide if the given atom is in effect. |
| |
| Example usage: |
| >>> extract_affecting_use('sasl? ( dev-libs/cyrus-sasl ) \ |
| !minimal? ( cxx? ( dev-libs/cyrus-sasl ) )', 'dev-libs/cyrus-sasl') |
| {'cxx', 'minimal', 'sasl'} |
| |
| @param mystr: The dependency string |
| @type mystr: String |
| @param atom: The atom to get into effect |
| @type atom: String |
| @rtype: Set of strings |
| @return: Set of use flags affecting given atom |
| """ |
| useflag_re = _get_useflag_re(eapi) |
| mysplit = mystr.split() |
| level = 0 |
| stack = [[]] |
| need_bracket = False |
| affecting_use = set() |
| |
| def flag(conditional): |
| if conditional[0] == "!": |
| flag = conditional[1:-1] |
| else: |
| flag = conditional[:-1] |
| |
| if useflag_re.match(flag) is None: |
| raise InvalidDependString( |
| _("invalid use flag '%s' in conditional '%s'") % \ |
| (flag, conditional)) |
| |
| return flag |
| |
| for token in mysplit: |
| if token == "(": |
| need_bracket = False |
| stack.append([]) |
| level += 1 |
| elif token == ")": |
| if need_bracket: |
| raise InvalidDependString( |
| _("malformed syntax: '%s'") % mystr) |
| if level > 0: |
| level -= 1 |
| l = stack.pop() |
| is_single = (len(l) == 1 or (len(l)==2 and (l[0] == "||" or l[0][-1] == "?"))) |
| |
| def ends_in_any_of_dep(k): |
| return k>=0 and stack[k] and stack[k][-1] == "||" |
| |
| def ends_in_operator(k): |
| return k>=0 and stack[k] and (stack[k][-1] == "||" or stack[k][-1][-1] == "?") |
| |
| def special_append(): |
| """ |
| Use extend instead of append if possible. This kills all redundant brackets. |
| """ |
| if is_single and (not stack[level] or not stack[level][-1][-1] == "?"): |
| if len(l) == 1 and isinstance(l[0], list): |
| # l = [[...]] |
| stack[level].extend(l[0]) |
| else: |
| stack[level].extend(l) |
| else: |
| stack[level].append(l) |
| |
| if l: |
| if not ends_in_any_of_dep(level-1) and not ends_in_operator(level): |
| #Optimize: ( ( ... ) ) -> ( ... ). Make sure there is no '||' hanging around. |
| stack[level].extend(l) |
| elif not stack[level]: |
| #An '||' in the level above forces us to keep to brackets. |
| special_append() |
| elif len(l) == 1 and ends_in_any_of_dep(level): |
| #Optimize: || ( A ) -> A |
| stack[level].pop() |
| special_append() |
| elif len(l) == 2 and (l[0] == "||" or l[0][-1] == "?") and stack[level][-1] in (l[0], "||"): |
| #Optimize: || ( || ( ... ) ) -> || ( ... ) |
| # foo? ( foo? ( ... ) ) -> foo? ( ... ) |
| # || ( foo? ( ... ) ) -> foo? ( ... ) |
| stack[level].pop() |
| special_append() |
| if l[0][-1] == "?": |
| affecting_use.add(flag(l[0])) |
| else: |
| if stack[level] and stack[level][-1][-1] == "?": |
| affecting_use.add(flag(stack[level][-1])) |
| special_append() |
| else: |
| if stack[level] and (stack[level][-1] == "||" or stack[level][-1][-1] == "?"): |
| stack[level].pop() |
| else: |
| raise InvalidDependString( |
| _("malformed syntax: '%s'") % mystr) |
| elif token == "||": |
| if need_bracket: |
| raise InvalidDependString( |
| _("malformed syntax: '%s'") % mystr) |
| need_bracket = True |
| stack[level].append(token) |
| else: |
| if need_bracket: |
| raise InvalidDependString( |
| _("malformed syntax: '%s'") % mystr) |
| |
| if token[-1] == "?": |
| need_bracket = True |
| stack[level].append(token) |
| elif token == atom: |
| stack[level].append(token) |
| |
| if level != 0 or need_bracket: |
| raise InvalidDependString( |
| _("malformed syntax: '%s'") % mystr) |
| |
| return affecting_use |
| |
| def extract_unpack_dependencies(src_uri, unpackers): |
| """ |
| Return unpack dependencies string for given SRC_URI string. |
| |
| @param src_uri: SRC_URI string |
| @type src_uri: String |
| @param unpackers: Dictionary mapping archive suffixes to dependency strings |
| @type unpackers: Dictionary |
| @rtype: String |
| @return: Dependency string specifying packages required to unpack archives. |
| """ |
| src_uri = src_uri.split() |
| |
| depend = [] |
| for i in range(len(src_uri)): |
| if src_uri[i][-1] == "?" or src_uri[i] in ("(", ")"): |
| depend.append(src_uri[i]) |
| elif (i+1 < len(src_uri) and src_uri[i+1] == "->") or src_uri[i] == "->": |
| continue |
| else: |
| for suffix in sorted(unpackers, key=lambda x: len(x), reverse=True): |
| suffix = suffix.lower() |
| if src_uri[i].lower().endswith(suffix): |
| depend.append(unpackers[suffix]) |
| break |
| |
| while True: |
| cleaned_depend = depend[:] |
| for i in range(len(cleaned_depend)): |
| if cleaned_depend[i] is None: |
| continue |
| elif cleaned_depend[i] == "(" and cleaned_depend[i+1] == ")": |
| cleaned_depend[i] = None |
| cleaned_depend[i+1] = None |
| elif cleaned_depend[i][-1] == "?" and cleaned_depend[i+1] == "(" and cleaned_depend[i+2] == ")": |
| cleaned_depend[i] = None |
| cleaned_depend[i+1] = None |
| cleaned_depend[i+2] = None |
| if depend == cleaned_depend: |
| break |
| else: |
| depend = [x for x in cleaned_depend if x is not None] |
| |
| return " ".join(depend) |