1 # deps.py -- Portage dependency resolution functions
2 # Copyright 2003-2012 Gentoo Foundation
3 # Distributed under the terms of the GNU General Public License v2
6 'Atom', 'best_match_to_list', 'cpvequal',
7 'dep_getcpv', 'dep_getkey', 'dep_getslot',
8 'dep_getusedeps', 'dep_opconvert', 'flatten',
9 'get_operator', 'isjustname', 'isspecific',
10 'isvalidatom', 'match_from_list', 'match_to_list',
11 'paren_enclose', 'paren_normalize', 'paren_reduce',
12 'remove_slot', 'strip_empty', 'use_reduce',
13 '_repo_separator', '_slot_separator',
18 # 'use?' only affects the immediately following word!
19 # Nesting is the only legal way to form multiple '[!]use?' requirements.
21 # Where: 'a' and 'b' are use flags, and 'z' is a depend atom.
23 # "a? z" -- If 'a' in [use], then b is valid.
24 # "a? ( z )" -- Syntax with parenthesis.
25 # "a? b? z" -- Deprecated.
26 # "a? ( b? z )" -- Valid
27 # "a? ( b? ( z ) ) -- Valid
32 from itertools import chain
35 portage.proxy.lazyimport.lazyimport(globals(),
36 'portage.util:cmp_sort_key,writemsg',
39 from portage import _unicode_decode
40 from portage.eapi import _get_eapi_attrs
41 from portage.exception import InvalidAtom, InvalidData, InvalidDependString
42 from portage.localization import _
43 from portage.versions import catpkgsplit, catsplit, \
44 vercmp, ververify, _cp, _cpv, _pkg_str, _unknown_repo
45 import portage.cache.mappings
47 if sys.hexversion >= 0x3000000:
50 # Api consumers included in portage should set this to True.
51 # Once the relevant api changes are in a portage release with
52 # stable keywords, make these warnings unconditional.
53 _internal_warnings = False
57 # PMS 3.1.3: A slot name may contain any of the characters [A-Za-z0-9+_.-].
58 # It must not begin with a hyphen or a dot.
60 _slot = r'([\w+][\w+.-]*)'
61 _slot_re = re.compile('^' + _slot + '$', re.VERBOSE)
64 _op = r'([=~]|[><]=?)'
66 _repo_separator = "::"
67 _repo_name = r'[\w][\w-]*'
68 _repo = r'(?:' + _repo_separator + '(' + _repo_name + ')' + ')?'
70 _extended_cat = r'[\w+*][\w+.*-]*'
74 def _get_atom_re(eapi_attrs):
75 cache_key = eapi_attrs.dots_in_PN
76 atom_re = _atom_re_cache.get(cache_key)
77 if atom_re is not None:
80 if eapi_attrs.dots_in_PN:
81 cp_re = _cp['dots_allowed_in_PN']
82 cpv_re = _cpv['dots_allowed_in_PN']
84 cp_re = _cp['dots_disallowed_in_PN']
85 cpv_re = _cpv['dots_disallowed_in_PN']
87 atom_re = re.compile('^(?P<without_use>(?:' +
88 '(?P<op>' + _op + cpv_re + ')|' +
89 '(?P<star>=' + cpv_re + r'\*)|' +
90 '(?P<simple>' + cp_re + '))' +
91 '(' + _slot_separator + _slot + ')?' +
92 _repo + ')(' + _use + ')?$', re.VERBOSE)
94 _atom_re_cache[cache_key] = atom_re
97 _atom_wildcard_re_cache = {}
99 def _get_atom_wildcard_re(eapi_attrs):
100 cache_key = eapi_attrs.dots_in_PN
101 atom_re = _atom_wildcard_re_cache.get(cache_key)
102 if atom_re is not None:
105 if eapi_attrs.dots_in_PN:
106 pkg_re = r'[\w+*][\w+.*-]*?'
108 pkg_re = r'[\w+*][\w+*-]*?'
110 atom_re = re.compile(r'(?P<simple>(' +
111 _extended_cat + r')/(' + pkg_re +
112 r'))(:(?P<slot>' + _slot + r'))?(' +
113 _repo_separator + r'(?P<repo>' + _repo_name + r'))?$')
115 _atom_wildcard_re_cache[cache_key] = atom_re
118 _usedep_re_cache = {}
120 def _get_usedep_re(eapi_attrs):
122 @param eapi_attrs: The EAPI attributes from _get_eapi_attrs
123 @type eapi_attrs: _eapi_attrs
124 @rtype: regular expression object
125 @return: A regular expression object that matches valid USE deps for the
128 cache_key = eapi_attrs.dots_in_use_flags
129 usedep_re = _usedep_re_cache.get(cache_key)
130 if usedep_re is not None:
133 if eapi_attrs.dots_in_use_flags:
134 _flag_re = r'[A-Za-z0-9][A-Za-z0-9+_@.-]*'
136 _flag_re = r'[A-Za-z0-9][A-Za-z0-9+_@-]*'
138 usedep_re = re.compile(r'^(?P<prefix>[!-]?)(?P<flag>' +
139 _flag_re + r')(?P<default>(\(\+\)|\(\-\))?)(?P<suffix>[?=]?)$')
141 _usedep_re_cache[cache_key] = usedep_re
144 _useflag_re_cache = {}
146 def _get_useflag_re(eapi):
148 When eapi is None then validation is not as strict, since we want the
149 same to work for multiple EAPIs that may have slightly different rules.
150 @param eapi: The EAPI
151 @type eapi: String or None
152 @rtype: regular expression object
153 @return: A regular expression object that matches valid USE flags for the
156 eapi_attrs = _get_eapi_attrs(eapi)
157 cache_key = eapi_attrs.dots_in_use_flags
158 useflag_re = _useflag_re_cache.get(cache_key)
159 if useflag_re is not None:
162 if eapi_attrs.dots_in_use_flags:
163 flag_re = r'[A-Za-z0-9][A-Za-z0-9+_@.-]*'
165 flag_re = r'[A-Za-z0-9][A-Za-z0-9+_@-]*'
167 useflag_re = re.compile(r'^' + flag_re + r'$')
169 _useflag_re_cache[cache_key] = useflag_re
172 def cpvequal(cpv1, cpv2):
175 @param cpv1: CategoryPackageVersion (no operators) Example: "sys-apps/portage-2.1"
177 @param cpv2: CategoryPackageVersion (no operators) Example: "sys-apps/portage-2.1"
181 1. True if cpv1 = cpv2
183 3. Throws PortageException if cpv1 or cpv2 is not a CPV
186 >>> from portage.dep import cpvequal
187 >>> cpvequal("sys-apps/portage-2.1","sys-apps/portage-2.1")
194 split1 = cpv1.cpv_split
195 except AttributeError:
196 cpv1 = _pkg_str(cpv1)
197 split1 = cpv1.cpv_split
200 split2 = cpv2.cpv_split
201 except AttributeError:
202 cpv2 = _pkg_str(cpv2)
203 split2 = cpv2.cpv_split
206 raise portage.exception.PortageException(_("Invalid data '%s, %s', parameter was not a CPV") % (cpv1, cpv2))
208 if split1[0] != split2[0] or \
209 split1[1] != split2[1]:
212 return vercmp(cpv1.version, cpv2.version) == 0
214 def strip_empty(myarr):
216 Strip all empty elements from an array
218 @param myarr: The list of elements
221 @return: The array with empty elements removed
223 warnings.warn(_("%s is deprecated and will be removed without replacement.") % \
224 ('portage.dep.strip_empty',), DeprecationWarning, stacklevel=2)
225 return [x for x in myarr if x]
227 def paren_reduce(mystr):
229 Take a string and convert all paren enclosed entities into sublists and
230 split the list elements by spaces. All redundant brackets are removed.
233 >>> paren_reduce('foobar foo? ( bar baz )')
234 ['foobar', 'foo?', ['bar', 'baz']]
236 @param mystr: The string to reduce
239 @return: The reduced string in an array
241 if _internal_warnings:
242 warnings.warn(_("%s is deprecated and will be removed without replacement.") % \
243 ('portage.dep.paren_reduce',), DeprecationWarning, stacklevel=2)
244 mysplit = mystr.split()
249 for token in mysplit:
256 raise InvalidDependString(
257 _("malformed syntax: '%s'") % mystr)
261 is_single = (len(l) == 1 or (len(l)==2 and (l[0] == "||" or l[0][-1] == "?")))
263 def ends_in_any_of_dep(k):
264 return k>=0 and stack[k] and stack[k][-1] == "||"
266 def ends_in_operator(k):
267 return k>=0 and stack[k] and (stack[k][-1] == "||" or stack[k][-1][-1] == "?")
269 def special_append():
271 Use extend instead of append if possible. This kills all redundant brackets.
273 if is_single and (not stack[level] or not stack[level][-1][-1] == "?"):
274 if len(l) == 1 and isinstance(l[0], list):
276 stack[level].extend(l[0])
278 stack[level].extend(l)
280 stack[level].append(l)
283 if not ends_in_any_of_dep(level-1) and not ends_in_operator(level):
284 #Optimize: ( ( ... ) ) -> ( ... ). Make sure there is no '||' hanging around.
285 stack[level].extend(l)
286 elif not stack[level]:
287 #An '||' in the level above forces us to keep to brackets.
289 elif len(l) == 1 and ends_in_any_of_dep(level):
290 #Optimize: || ( A ) -> A
293 elif len(l) == 2 and (l[0] == "||" or l[0][-1] == "?") and stack[level][-1] in (l[0], "||"):
294 #Optimize: || ( || ( ... ) ) -> || ( ... )
295 # foo? ( foo? ( ... ) ) -> foo? ( ... )
296 # || ( foo? ( ... ) ) -> foo? ( ... )
302 if stack[level] and (stack[level][-1] == "||" or stack[level][-1][-1] == "?"):
305 raise InvalidDependString(
306 _("malformed syntax: '%s'") % mystr)
309 raise InvalidDependString(
310 _("malformed syntax: '%s'") % mystr)
312 stack[level].append(token)
315 raise InvalidDependString(
316 _("malformed syntax: '%s'") % mystr)
321 stack[level].append(token)
323 if level != 0 or need_bracket:
324 raise InvalidDependString(
325 _("malformed syntax: '%s'") % mystr)
329 class paren_normalize(list):
330 """Take a dependency structure as returned by paren_reduce or use_reduce
331 and generate an equivalent structure that has no redundant lists."""
332 def __init__(self, src):
333 if _internal_warnings:
334 warnings.warn(_("%s is deprecated and will be removed without replacement.") % \
335 ('portage.dep.paren_normalize',), DeprecationWarning, stacklevel=2)
337 self._zap_parens(src, self)
339 def _zap_parens(self, src, dest, disjunction=False):
344 if isinstance(x, basestring):
345 if x in ('||', '^^'):
346 y = self._zap_parens(next(i), [], disjunction=True)
352 elif x.endswith("?"):
354 dest.append(self._zap_parens(next(i), []))
359 x = self._zap_parens(x, [])
365 self._zap_parens(x, dest)
368 def paren_enclose(mylist, unevaluated_atom=False, opconvert=False):
370 Convert a list to a string with sublists enclosed with parens.
373 >>> test = ['foobar','foo',['bar','baz']]
374 >>> paren_enclose(test)
375 'foobar foo ( bar baz )'
377 @param mylist: The list
380 @return: The paren enclosed string
384 if isinstance(x, list):
385 if opconvert and x and x[0] == "||":
386 mystrparts.append("%s ( %s )" % (x[0], paren_enclose(x[1:])))
388 mystrparts.append("( %s )" % paren_enclose(x))
391 x = getattr(x, 'unevaluated_atom', x)
393 return " ".join(mystrparts)
395 def use_reduce(depstr, uselist=[], masklist=[], matchall=False, excludeall=[], is_src_uri=False, \
396 eapi=None, opconvert=False, flat=False, is_valid_flag=None, token_class=None, matchnone=False):
398 Takes a dep string and reduces the use? conditionals out, leaving an array
399 with subarrays. All redundant brackets are removed.
401 @param deparray: depstring
402 @type deparray: String
403 @param uselist: List of use enabled flags
405 @param masklist: List of masked flags (always treated as disabled)
407 @param matchall: Treat all conditionals as active. Used by repoman.
409 @param excludeall: List of flags for which negated conditionals are always treated as inactive.
410 @type excludeall: List
411 @param is_src_uri: Indicates if depstr represents a SRC_URI
412 @type is_src_uri: Bool
413 @param eapi: Indicates the EAPI the dep string has to comply to
415 @param opconvert: Put every operator as first element into it's argument list
416 @type opconvert: Bool
417 @param flat: Create a flat list of all tokens
419 @param is_valid_flag: Function that decides if a given use flag might be used in use conditionals
420 @type is_valid_flag: Function
421 @param token_class: Convert all non operator tokens into this class
422 @type token_class: Class
423 @param matchnone: Treat all conditionals as inactive. Used by digestgen().
424 @type matchnone: Bool
426 @return: The use reduced depend array
428 if isinstance(depstr, list):
429 if _internal_warnings:
430 warnings.warn(_("Passing paren_reduced dep arrays to %s is deprecated. " + \
431 "Pass the original dep string instead.") % \
432 ('portage.dep.use_reduce',), DeprecationWarning, stacklevel=2)
433 depstr = paren_enclose(depstr)
435 if opconvert and flat:
436 raise ValueError("portage.dep.use_reduce: 'opconvert' and 'flat' are mutually exclusive")
438 if matchall and matchnone:
439 raise ValueError("portage.dep.use_reduce: 'matchall' and 'matchnone' are mutually exclusive")
441 eapi_attrs = _get_eapi_attrs(eapi)
442 useflag_re = _get_useflag_re(eapi)
444 def is_active(conditional):
446 Decides if a given use conditional is active.
448 if conditional.startswith("!"):
449 flag = conditional[1:-1]
452 flag = conditional[:-1]
456 if not is_valid_flag(flag):
457 msg = _("USE flag '%s' referenced in " + \
458 "conditional '%s' is not in IUSE") \
459 % (flag, conditional)
460 e = InvalidData(msg, category='IUSE.missing')
461 raise InvalidDependString(msg, errors=(e,))
463 if useflag_re.match(flag) is None:
464 raise InvalidDependString(
465 _("invalid use flag '%s' in conditional '%s'") % (flag, conditional))
467 if is_negated and flag in excludeall:
479 return (flag in uselist and not is_negated) or \
480 (flag not in uselist and is_negated)
482 def missing_white_space_check(token, pos):
484 Used to generate good error messages for invalid tokens.
486 for x in (")", "(", "||"):
487 if token.startswith(x) or token.endswith(x):
488 raise InvalidDependString(
489 _("missing whitespace around '%s' at '%s', token %s") % (x, token, pos+1))
491 mysplit = depstr.split()
492 #Count the bracket level.
494 #We parse into a stack. Every time we hit a '(', a new empty list is appended to the stack.
495 #When we hit a ')', the last list in the stack is merged with list one level up.
497 #Set need_bracket to True after use conditionals or ||. Other tokens need to ensure
498 #that need_bracket is not True.
500 #Set need_simple_token to True after a SRC_URI arrow. Other tokens need to ensure
501 #that need_simple_token is not True.
502 need_simple_token = False
504 for pos, token in enumerate(mysplit):
506 if need_simple_token:
507 raise InvalidDependString(
508 _("expected: file name, got: '%s', token %s") % (token, pos+1))
509 if len(mysplit) >= pos+2 and mysplit[pos+1] == ")":
510 raise InvalidDependString(
511 _("expected: dependency string, got: ')', token %s") % (pos+1,))
517 raise InvalidDependString(
518 _("expected: '(', got: '%s', token %s") % (token, pos+1))
519 if need_simple_token:
520 raise InvalidDependString(
521 _("expected: file name, got: '%s', token %s") % (token, pos+1))
526 is_single = len(l) == 1 or \
527 (opconvert and l and l[0] == "||") or \
528 (not opconvert and len(l)==2 and l[0] == "||")
532 #In 'flat' mode, we simply merge all lists into a single large one.
533 if stack[level] and stack[level][-1][-1] == "?":
534 #The last token before the '(' that matches the current ')'
535 #was a use conditional. The conditional is removed in any case.
536 #Merge the current list if needed.
537 if is_active(stack[level][-1]):
539 stack[level].extend(l)
543 stack[level].extend(l)
547 if stack[level][-1] == "||" and not l:
548 #Optimize: || ( ) -> .
550 elif stack[level][-1][-1] == "?":
551 #The last token before the '(' that matches the current ')'
552 #was a use conditional, remove it and decide if we
553 #have to keep the current list.
554 if not is_active(stack[level][-1]):
558 def ends_in_any_of_dep(k):
559 return k>=0 and stack[k] and stack[k][-1] == "||"
561 def starts_with_any_of_dep(k):
562 #'ends_in_any_of_dep' for opconvert
563 return k>=0 and stack[k] and stack[k][0] == "||"
565 def last_any_of_operator_level(k):
566 #Returns the level of the last || operator if it is in effect for
567 #the current level. It is not in effect, if there is a level, that
568 #ends in a non-operator. This is almost equivalent to stack[level][-1]=="||",
569 #expect that it skips empty levels.
572 if stack[k][-1] == "||":
574 elif stack[k][-1][-1] != "?":
579 def special_append():
581 Use extend instead of append if possible. This kills all redundant brackets.
584 #Either [A], [[...]] or [|| [...]]
585 if l[0] == "||" and ends_in_any_of_dep(level-1):
587 stack[level].extend(l[1:])
589 stack[level].extend(l[1])
590 elif len(l) == 1 and isinstance(l[0], list):
592 last = last_any_of_operator_level(level-1)
594 if opconvert and isinstance(l[0], list) \
595 and l[0] and l[0][0] == '||':
596 stack[level].append(l[0])
598 stack[level].extend(l[0])
600 if opconvert and l[0] and l[0][0] == "||":
601 stack[level].extend(l[0][1:])
603 stack[level].append(l[0])
605 stack[level].extend(l)
607 if opconvert and stack[level] and stack[level][-1] == '||':
608 stack[level][-1] = ['||'] + l
610 stack[level].append(l)
613 #The current list is not empty and we don't want to ignore it because
614 #of an inactive use conditional.
615 if not ends_in_any_of_dep(level-1) and not ends_in_any_of_dep(level):
616 #Optimize: ( ( ... ) ) -> ( ... ). Make sure there is no '||' hanging around.
617 stack[level].extend(l)
618 elif not stack[level]:
619 #An '||' in the level above forces us to keep to brackets.
621 elif is_single and ends_in_any_of_dep(level):
622 #Optimize: || ( A ) -> A, || ( || ( ... ) ) -> || ( ... )
625 elif ends_in_any_of_dep(level) and ends_in_any_of_dep(level-1):
626 #Optimize: || ( A || ( B C ) ) -> || ( A B C )
628 stack[level].extend(l)
630 if opconvert and ends_in_any_of_dep(level):
631 #In opconvert mode, we have to move the operator from the level
632 #above into the current list.
634 stack[level].append(["||"] + l)
639 raise InvalidDependString(
640 _("no matching '%s' for '%s', token %s") % ("(", ")", pos+1))
643 raise InvalidDependString(
644 _("any-of dependencies are not allowed in SRC_URI: token %s") % (pos+1,))
646 raise InvalidDependString(
647 _("expected: '(', got: '%s', token %s") % (token, pos+1))
649 stack[level].append(token)
651 if need_simple_token:
652 raise InvalidDependString(
653 _("expected: file name, got: '%s', token %s") % (token, pos+1))
655 raise InvalidDependString(
656 _("SRC_URI arrow are only allowed in SRC_URI: token %s") % (pos+1,))
657 if not eapi_attrs.src_uri_arrows:
658 raise InvalidDependString(
659 _("SRC_URI arrow not allowed in EAPI %s: token %s") % (eapi, pos+1))
660 need_simple_token = True
661 stack[level].append(token)
663 missing_white_space_check(token, pos)
666 raise InvalidDependString(
667 _("expected: '(', got: '%s', token %s") % (token, pos+1))
669 if need_simple_token and "/" in token:
670 #The last token was a SRC_URI arrow, make sure we have a simple file name.
671 raise InvalidDependString(
672 _("expected: file name, got: '%s', token %s") % (token, pos+1))
677 need_simple_token = False
678 if token_class and not is_src_uri:
679 #Add a hack for SRC_URI here, to avoid conditional code at the consumer level
681 token = token_class(token, eapi=eapi,
682 is_valid_flag=is_valid_flag)
683 except InvalidAtom as e:
684 raise InvalidDependString(
685 _("Invalid atom (%s), token %s") \
686 % (e, pos+1), errors=(e,))
689 except Exception as e:
690 raise InvalidDependString(
691 _("Invalid token '%s', token %s") % (token, pos+1))
693 if not matchall and \
694 hasattr(token, 'evaluate_conditionals'):
695 token = token.evaluate_conditionals(uselist)
697 stack[level].append(token)
700 raise InvalidDependString(
701 _("Missing '%s' at end of string") % (")",))
704 raise InvalidDependString(
705 _("Missing '%s' at end of string") % ("(",))
707 if need_simple_token:
708 raise InvalidDependString(
709 _("Missing file name at end of string"))
713 def dep_opconvert(deplist):
715 Iterate recursively through a list of deps, if the
716 dep is a '||' or '&&' operator, combine it with the
717 list of deps that follows..
720 >>> test = ["blah", "||", ["foo", "bar", "baz"]]
721 >>> dep_opconvert(test)
722 ['blah', ['||', 'foo', 'bar', 'baz']]
724 @param deplist: A list of deps to format
728 The new list with the new ordering
730 if _internal_warnings:
731 warnings.warn(_("%s is deprecated. Use %s with the opconvert parameter set to True instead.") % \
732 ('portage.dep.dep_opconvert', 'portage.dep.use_reduce'), DeprecationWarning, stacklevel=2)
736 while x != len(deplist):
737 if isinstance(deplist[x], list):
738 retlist.append(dep_opconvert(deplist[x]))
739 elif deplist[x] == "||":
740 retlist.append([deplist[x]] + dep_opconvert(deplist[x+1]))
743 retlist.append(deplist[x])
749 Recursively traverse nested lists and return a single list containing
750 all non-list elements that are found.
753 >>> flatten([1, [2, 3, [4]]])
756 @param mylist: A list containing nested lists and non-list elements.
759 @return: A single list containing only non-list elements.
761 if _internal_warnings:
762 warnings.warn(_("%s is deprecated and will be removed without replacement.") % \
763 ('portage.dep.flatten',), DeprecationWarning, stacklevel=2)
767 if isinstance(x, list):
768 newlist.extend(flatten(x))
773 class _use_dep(object):
775 __slots__ = ("_eapi_attrs", "conditional", "missing_enabled", "missing_disabled",
776 "disabled", "enabled", "tokens", "required")
778 class _conditionals_class(object):
779 __slots__ = ("enabled", "disabled", "equal", "not_equal")
782 for k in self.__slots__:
783 v = getattr(self, k, None)
788 for k in self.__slots__:
789 v = getattr(self, k, None)
793 # used in InvalidAtom messages
794 _conditional_strings = {
801 def __init__(self, use, eapi_attrs, enabled_flags=None, disabled_flags=None, missing_enabled=None,
802 missing_disabled=None, conditional=None, required=None):
804 self._eapi_attrs = eapi_attrs
806 if enabled_flags is not None:
807 #A shortcut for the classe's own methods.
809 if not isinstance(self.tokens, tuple):
810 self.tokens = tuple(self.tokens)
812 self.required = frozenset(required)
813 self.enabled = frozenset(enabled_flags)
814 self.disabled = frozenset(disabled_flags)
815 self.missing_enabled = frozenset(missing_enabled)
816 self.missing_disabled = frozenset(missing_disabled)
817 self.conditional = None
820 self.conditional = self._conditionals_class()
821 for k in "enabled", "disabled", "equal", "not_equal":
822 setattr(self.conditional, k, frozenset(conditional.get(k, [])))
826 enabled_flags = set()
827 disabled_flags = set()
828 missing_enabled = set()
829 missing_disabled = set()
833 usedep_re = _get_usedep_re(self._eapi_attrs)
836 m = usedep_re.match(x)
838 raise InvalidAtom(_("Invalid use dep: '%s'") % (x,))
840 operator = m.group("prefix") + m.group("suffix")
841 flag = m.group("flag")
842 default = m.group("default")
845 enabled_flags.add(flag)
846 elif operator == "-":
847 disabled_flags.add(flag)
848 elif operator == "?":
849 conditional.setdefault("enabled", set()).add(flag)
850 elif operator == "=":
851 conditional.setdefault("equal", set()).add(flag)
852 elif operator == "!=":
853 conditional.setdefault("not_equal", set()).add(flag)
854 elif operator == "!?":
855 conditional.setdefault("disabled", set()).add(flag)
857 raise InvalidAtom(_("Invalid use dep: '%s'") % (x,))
861 if flag in missing_disabled or flag in no_default:
862 raise InvalidAtom(_("Invalid use dep: '%s'") % (x,))
863 missing_enabled.add(flag)
865 if flag in missing_enabled or flag in no_default:
866 raise InvalidAtom(_("Invalid use dep: '%s'") % (x,))
867 missing_disabled.add(flag)
869 if flag in missing_enabled or flag in missing_disabled:
870 raise InvalidAtom(_("Invalid use dep: '%s'") % (x,))
874 if not isinstance(self.tokens, tuple):
875 self.tokens = tuple(self.tokens)
877 self.required = frozenset(no_default)
879 self.enabled = frozenset(enabled_flags)
880 self.disabled = frozenset(disabled_flags)
881 self.missing_enabled = frozenset(missing_enabled)
882 self.missing_disabled = frozenset(missing_disabled)
883 self.conditional = None
886 self.conditional = self._conditionals_class()
887 for k in "enabled", "disabled", "equal", "not_equal":
888 setattr(self.conditional, k, frozenset(conditional.get(k, [])))
891 return bool(self.tokens)
893 if sys.hexversion < 0x3000000:
894 __nonzero__ = __bool__
899 return "[%s]" % (",".join(self.tokens),)
902 return "portage.dep._use_dep(%s)" % repr(self.tokens)
904 def evaluate_conditionals(self, use):
906 Create a new instance with conditionals evaluated.
908 Conditional evaluation behavior:
910 parent state conditional result
922 Conditional syntax examples:
924 Compact Form Equivalent Expanded Form
926 foo[bar?] bar? ( foo[bar] ) !bar? ( foo )
927 foo[!bar?] bar? ( foo ) !bar? ( foo[-bar] )
928 foo[bar=] bar? ( foo[bar] ) !bar? ( foo[-bar] )
929 foo[!bar=] bar? ( foo[-bar] ) !bar? ( foo[bar] )
932 enabled_flags = set(self.enabled)
933 disabled_flags = set(self.disabled)
936 usedep_re = _get_usedep_re(self._eapi_attrs)
938 for x in self.tokens:
939 m = usedep_re.match(x)
941 operator = m.group("prefix") + m.group("suffix")
942 flag = m.group("flag")
943 default = m.group("default")
949 enabled_flags.add(flag)
950 tokens.append(flag+default)
951 elif operator == "=":
953 enabled_flags.add(flag)
954 tokens.append(flag+default)
956 disabled_flags.add(flag)
957 tokens.append("-"+flag+default)
958 elif operator == "!=":
960 disabled_flags.add(flag)
961 tokens.append("-"+flag+default)
963 enabled_flags.add(flag)
964 tokens.append(flag+default)
965 elif operator == "!?":
967 disabled_flags.add(flag)
968 tokens.append("-"+flag+default)
972 return _use_dep(tokens, self._eapi_attrs, enabled_flags=enabled_flags, disabled_flags=disabled_flags,
973 missing_enabled=self.missing_enabled, missing_disabled=self.missing_disabled, required=self.required)
975 def violated_conditionals(self, other_use, is_valid_flag, parent_use=None):
977 Create a new instance with satisfied use deps removed.
979 if parent_use is None and self.conditional:
980 raise InvalidAtom("violated_conditionals needs 'parent_use'" + \
981 " parameter for conditional flags.")
983 enabled_flags = set()
984 disabled_flags = set()
989 all_defaults = frozenset(chain(self.missing_enabled, self.missing_disabled))
991 def validate_flag(flag):
992 return is_valid_flag(flag) or flag in all_defaults
994 usedep_re = _get_usedep_re(self._eapi_attrs)
996 for x in self.tokens:
997 m = usedep_re.match(x)
999 operator = m.group("prefix") + m.group("suffix")
1000 flag = m.group("flag")
1002 if not validate_flag(flag):
1005 enabled_flags.add(flag)
1006 elif operator == "-":
1007 disabled_flags.add(flag)
1008 elif operator == "?":
1009 conditional.setdefault("enabled", set()).add(flag)
1010 elif operator == "=":
1011 conditional.setdefault("equal", set()).add(flag)
1012 elif operator == "!=":
1013 conditional.setdefault("not_equal", set()).add(flag)
1014 elif operator == "!?":
1015 conditional.setdefault("disabled", set()).add(flag)
1020 if flag not in other_use:
1021 if is_valid_flag(flag) or flag in self.missing_disabled:
1023 enabled_flags.add(flag)
1024 elif operator == "-":
1025 if flag not in other_use:
1026 if not is_valid_flag(flag):
1027 if flag in self.missing_enabled:
1029 disabled_flags.add(flag)
1032 disabled_flags.add(flag)
1033 elif operator == "?":
1034 if flag not in parent_use or flag in other_use:
1037 if is_valid_flag(flag) or flag in self.missing_disabled:
1039 conditional.setdefault("enabled", set()).add(flag)
1040 elif operator == "=":
1041 if flag in parent_use and flag not in other_use:
1042 if is_valid_flag(flag):
1044 conditional.setdefault("equal", set()).add(flag)
1046 if flag in self.missing_disabled:
1048 conditional.setdefault("equal", set()).add(flag)
1049 elif flag not in parent_use:
1050 if flag not in other_use:
1051 if not is_valid_flag(flag):
1052 if flag in self.missing_enabled:
1054 conditional.setdefault("equal", set()).add(flag)
1057 conditional.setdefault("equal", set()).add(flag)
1058 elif operator == "!=":
1059 if flag not in parent_use and flag not in other_use:
1060 if is_valid_flag(flag):
1062 conditional.setdefault("not_equal", set()).add(flag)
1064 if flag in self.missing_disabled:
1066 conditional.setdefault("not_equal", set()).add(flag)
1067 elif flag in parent_use:
1068 if flag not in other_use:
1069 if not is_valid_flag(flag):
1070 if flag in self.missing_enabled:
1072 conditional.setdefault("not_equal", set()).add(flag)
1075 conditional.setdefault("not_equal", set()).add(flag)
1076 elif operator == "!?":
1077 if flag not in parent_use:
1078 if flag not in other_use:
1079 if not is_valid_flag(flag) and flag in self.missing_enabled:
1081 conditional.setdefault("disabled", set()).add(flag)
1084 conditional.setdefault("disabled", set()).add(flag)
1086 return _use_dep(tokens, self._eapi_attrs, enabled_flags=enabled_flags, disabled_flags=disabled_flags,
1087 missing_enabled=self.missing_enabled, missing_disabled=self.missing_disabled, \
1088 conditional=conditional, required=self.required)
1090 def _eval_qa_conditionals(self, use_mask, use_force):
1092 For repoman, evaluate all possible combinations within the constraints
1093 of the given use.force and use.mask settings. The result may seem
1094 ambiguous in the sense that the same flag can be in both the enabled
1095 and disabled sets, but this is useful within the context of how its
1096 intended to be used by repoman. It is assumed that the caller has
1097 already ensured that there is no intersection between the given
1098 use_mask and use_force sets when necessary.
1100 enabled_flags = set(self.enabled)
1101 disabled_flags = set(self.disabled)
1102 missing_enabled = self.missing_enabled
1103 missing_disabled = self.missing_disabled
1106 usedep_re = _get_usedep_re(self._eapi_attrs)
1108 for x in self.tokens:
1109 m = usedep_re.match(x)
1111 operator = m.group("prefix") + m.group("suffix")
1112 flag = m.group("flag")
1113 default = m.group("default")
1118 if flag not in use_mask:
1119 enabled_flags.add(flag)
1120 tokens.append(flag+default)
1121 elif operator == "=":
1122 if flag not in use_mask:
1123 enabled_flags.add(flag)
1124 tokens.append(flag+default)
1125 if flag not in use_force:
1126 disabled_flags.add(flag)
1127 tokens.append("-"+flag+default)
1128 elif operator == "!=":
1129 if flag not in use_force:
1130 enabled_flags.add(flag)
1131 tokens.append(flag+default)
1132 if flag not in use_mask:
1133 disabled_flags.add(flag)
1134 tokens.append("-"+flag+default)
1135 elif operator == "!?":
1136 if flag not in use_force:
1137 disabled_flags.add(flag)
1138 tokens.append("-"+flag+default)
1142 return _use_dep(tokens, self._eapi_attrs, enabled_flags=enabled_flags, disabled_flags=disabled_flags,
1143 missing_enabled=missing_enabled, missing_disabled=missing_disabled, required=self.required)
1145 if sys.hexversion < 0x3000000:
1146 _atom_base = unicode
1150 class Atom(_atom_base):
1153 For compatibility with existing atom string manipulation code, this
1154 class emulates most of the str methods that are useful with atoms.
1157 class _blocker(object):
1158 __slots__ = ("overlap",)
1160 class _overlap(object):
1161 __slots__ = ("forbid",)
1163 def __init__(self, forbid=False):
1164 self.forbid = forbid
1166 def __init__(self, forbid_overlap=False):
1167 self.overlap = self._overlap(forbid=forbid_overlap)
1169 def __new__(cls, s, unevaluated_atom=None, allow_wildcard=False, allow_repo=None,
1170 _use=None, eapi=None, is_valid_flag=None):
1171 return _atom_base.__new__(cls, s)
1173 def __init__(self, s, unevaluated_atom=None, allow_wildcard=False, allow_repo=None,
1174 _use=None, eapi=None, is_valid_flag=None):
1175 if isinstance(s, Atom):
1176 # This is an efficiency assertion, to ensure that the Atom
1177 # constructor is not called redundantly.
1178 raise TypeError(_("Expected %s, got %s") % \
1179 (_atom_base, type(s)))
1181 if not isinstance(s, _atom_base):
1182 # Avoid TypeError from _atom_base.__init__ with PyPy.
1183 s = _unicode_decode(s)
1185 _atom_base.__init__(s)
1187 eapi_attrs = _get_eapi_attrs(eapi)
1188 atom_re = _get_atom_re(eapi_attrs)
1190 if eapi is not None:
1191 # Ignore allow_repo when eapi is specified.
1192 allow_repo = eapi_attrs.repo_deps
1194 if allow_repo is None:
1198 blocker = self._blocker(forbid_overlap=("!" == s[1:2]))
1199 if blocker.overlap.forbid:
1205 self.__dict__['blocker'] = blocker
1206 m = atom_re.match(s)
1207 extended_syntax = False
1210 m = _get_atom_wildcard_re(eapi_attrs).match(s)
1212 raise InvalidAtom(self)
1214 gdict = m.groupdict()
1215 cpv = cp = gdict['simple']
1216 if cpv.find("**") != -1:
1217 raise InvalidAtom(self)
1218 slot = gdict['slot']
1219 repo = gdict['repo']
1221 extended_syntax = True
1223 raise InvalidAtom(self)
1224 elif m.group('op') is not None:
1225 base = atom_re.groupindex['op']
1226 op = m.group(base + 1)
1227 cpv = m.group(base + 2)
1228 cp = m.group(base + 3)
1229 slot = m.group(atom_re.groups - 2)
1230 repo = m.group(atom_re.groups - 1)
1231 use_str = m.group(atom_re.groups)
1232 if m.group(base + 4) is not None:
1233 raise InvalidAtom(self)
1234 elif m.group('star') is not None:
1235 base = atom_re.groupindex['star']
1237 cpv = m.group(base + 1)
1238 cp = m.group(base + 2)
1239 slot = m.group(atom_re.groups - 2)
1240 repo = m.group(atom_re.groups - 1)
1241 use_str = m.group(atom_re.groups)
1242 if m.group(base + 3) is not None:
1243 raise InvalidAtom(self)
1244 elif m.group('simple') is not None:
1246 cpv = cp = m.group(atom_re.groupindex['simple'] + 1)
1247 slot = m.group(atom_re.groups - 2)
1248 repo = m.group(atom_re.groups - 1)
1249 use_str = m.group(atom_re.groups)
1250 if m.group(atom_re.groupindex['simple'] + 2) is not None:
1251 raise InvalidAtom(self)
1254 raise AssertionError(_("required group not found in atom: '%s'") % self)
1255 self.__dict__['cp'] = cp
1257 self.__dict__['cpv'] = _pkg_str(cpv)
1258 self.__dict__['version'] = self.cpv.version
1260 # plain cp, wildcard, or something
1261 self.__dict__['cpv'] = cpv
1262 self.__dict__['version'] = None
1263 self.__dict__['repo'] = repo
1264 self.__dict__['slot'] = slot
1265 self.__dict__['operator'] = op
1266 self.__dict__['extended_syntax'] = extended_syntax
1268 if not (repo is None or allow_repo):
1269 raise InvalidAtom(self)
1271 if use_str is not None:
1272 if _use is not None:
1275 use = _use_dep(use_str[1:-1].split(","), eapi_attrs)
1276 without_use = Atom(m.group('without_use'), allow_repo=allow_repo)
1279 if unevaluated_atom is not None and \
1280 unevaluated_atom.use is not None:
1281 # unevaluated_atom.use is used for IUSE checks when matching
1282 # packages, so it must not propagate to without_use
1283 without_use = Atom(s, allow_wildcard=allow_wildcard,
1284 allow_repo=allow_repo)
1288 self.__dict__['use'] = use
1289 self.__dict__['without_use'] = without_use
1291 if unevaluated_atom:
1292 self.__dict__['unevaluated_atom'] = unevaluated_atom
1294 self.__dict__['unevaluated_atom'] = self
1296 if eapi is not None:
1297 if not isinstance(eapi, basestring):
1298 raise TypeError('expected eapi argument of ' + \
1299 '%s, got %s: %s' % (basestring, type(eapi), eapi,))
1300 if self.slot and not eapi_attrs.slot_deps:
1302 _("Slot deps are not allowed in EAPI %s: '%s'") \
1303 % (eapi, self), category='EAPI.incompatible')
1305 if not eapi_attrs.use_deps:
1307 _("Use deps are not allowed in EAPI %s: '%s'") \
1308 % (eapi, self), category='EAPI.incompatible')
1309 elif not eapi_attrs.use_dep_defaults and \
1310 (self.use.missing_enabled or self.use.missing_disabled):
1312 _("Use dep defaults are not allowed in EAPI %s: '%s'") \
1313 % (eapi, self), category='EAPI.incompatible')
1314 if is_valid_flag is not None and self.use.conditional:
1317 for conditional_type, flags in \
1318 self.use.conditional.items():
1320 if not is_valid_flag(flag):
1321 invalid_flag = (conditional_type, flag)
1322 raise StopIteration()
1323 except StopIteration:
1325 if invalid_flag is not None:
1326 conditional_type, flag = invalid_flag
1327 conditional_str = _use_dep._conditional_strings[conditional_type]
1328 msg = _("USE flag '%s' referenced in " + \
1329 "conditional '%s' in atom '%s' is not in IUSE") \
1330 % (flag, conditional_str % flag, self)
1331 raise InvalidAtom(msg, category='IUSE.missing')
1332 if self.blocker and self.blocker.overlap.forbid and not eapi_attrs.strong_blocks:
1334 _("Strong blocks are not allowed in EAPI %s: '%s'") \
1335 % (eapi, self), category='EAPI.incompatible')
1338 def without_repo(self):
1339 if self.repo is None:
1341 return Atom(self.replace(_repo_separator + self.repo, '', 1),
1342 allow_wildcard=True)
1345 def without_slot(self):
1346 if self.slot is None:
1348 return Atom(self.replace(_slot_separator + self.slot, '', 1),
1349 allow_repo=True, allow_wildcard=True)
1351 def with_repo(self, repo):
1352 atom = remove_slot(self)
1353 if self.slot is not None:
1354 atom += _slot_separator + self.slot
1355 atom += _repo_separator + repo
1356 if self.use is not None:
1357 atom += str(self.use)
1358 return Atom(atom, allow_repo=True, allow_wildcard=True)
1360 def with_slot(self, slot):
1361 atom = remove_slot(self) + _slot_separator + slot
1362 if self.repo is not None:
1363 atom += _repo_separator + self.repo
1364 if self.use is not None:
1365 atom += str(self.use)
1366 return Atom(atom, allow_repo=True, allow_wildcard=True)
1368 def __setattr__(self, name, value):
1369 raise AttributeError("Atom instances are immutable",
1370 self.__class__, name, value)
1372 def intersects(self, other):
1374 Atoms with different cpv, operator or use attributes cause this method
1375 to return False even though there may actually be some intersection.
1376 TODO: Detect more forms of intersection.
1377 @param other: The package atom to match
1380 @return: True if this atom and the other atom intersect,
1383 if not isinstance(other, Atom):
1384 raise TypeError("expected %s, got %s" % \
1385 (Atom, type(other)))
1390 if self.cp != other.cp or \
1391 self.use != other.use or \
1392 self.operator != other.operator or \
1393 self.cpv != other.cpv:
1396 if self.slot is None or \
1397 other.slot is None or \
1398 self.slot == other.slot:
1403 def evaluate_conditionals(self, use):
1405 Create an atom instance with any USE conditionals evaluated.
1406 @param use: The set of enabled USE flags
1409 @return: an atom instance with any USE conditionals evaluated
1411 if not (self.use and self.use.conditional):
1413 atom = remove_slot(self)
1415 atom += ":%s" % self.slot
1416 use_dep = self.use.evaluate_conditionals(use)
1417 atom += str(use_dep)
1418 return Atom(atom, unevaluated_atom=self, allow_repo=(self.repo is not None), _use=use_dep)
1420 def violated_conditionals(self, other_use, is_valid_flag, parent_use=None):
1422 Create an atom instance with any USE conditional removed, that is
1423 satisfied by other_use.
1424 @param other_use: The set of enabled USE flags
1425 @type other_use: set
1426 @param is_valid_flag: Function that decides if a use flag is referenceable in use deps
1427 @type is_valid_flag: function
1428 @param parent_use: Set of enabled use flags of the package requiring this atom
1429 @type parent_use: set
1431 @return: an atom instance with any satisfied USE conditionals removed
1435 atom = remove_slot(self)
1437 atom += ":%s" % self.slot
1438 use_dep = self.use.violated_conditionals(other_use, is_valid_flag, parent_use)
1439 atom += str(use_dep)
1440 return Atom(atom, unevaluated_atom=self, allow_repo=(self.repo is not None), _use=use_dep)
1442 def _eval_qa_conditionals(self, use_mask, use_force):
1443 if not (self.use and self.use.conditional):
1445 atom = remove_slot(self)
1447 atom += ":%s" % self.slot
1448 use_dep = self.use._eval_qa_conditionals(use_mask, use_force)
1449 atom += str(use_dep)
1450 return Atom(atom, unevaluated_atom=self, allow_repo=(self.repo is not None), _use=use_dep)
1453 """Immutable, so returns self."""
1456 def __deepcopy__(self, memo=None):
1457 """Immutable, so returns self."""
1458 memo[id(self)] = self
1461 _extended_cp_re_cache = {}
1463 def extended_cp_match(extended_cp, other_cp):
1465 Checks if an extended syntax cp matches a non extended cp
1467 # Escape special '+' and '.' characters which are allowed in atoms,
1468 # and convert '*' to regex equivalent.
1469 global _extended_cp_re_cache
1470 extended_cp_re = _extended_cp_re_cache.get(extended_cp)
1471 if extended_cp_re is None:
1472 extended_cp_re = re.compile("^" + re.escape(extended_cp).replace(
1473 r'\*', '[^/]*') + "$")
1474 _extended_cp_re_cache[extended_cp] = extended_cp_re
1475 return extended_cp_re.match(other_cp) is not None
1477 class ExtendedAtomDict(portage.cache.mappings.MutableMapping):
1479 dict() wrapper that supports extended atoms as keys and allows lookup
1480 of a normal cp against other normal cp and extended cp.
1481 The value type has to be given to __init__ and is assumed to be the same
1485 __slots__ = ('_extended', '_normal', '_value_class')
1487 def __init__(self, value_class):
1490 self._value_class = value_class
1493 result = self.__class__(self._value_class)
1494 result._extended.update(self._extended)
1495 result._normal.update(self._normal)
1499 for k in self._normal:
1501 for k in self._extended:
1504 def iteritems(self):
1506 for item in self._normal.items():
1508 for item in self._extended.items():
1510 except AttributeError:
1511 pass # FEATURES=python-trace
1513 def __delitem__(self, cp):
1515 return self._extended.__delitem__(cp)
1517 return self._normal.__delitem__(cp)
1519 if sys.hexversion >= 0x3000000:
1524 return len(self._normal) + len(self._extended)
1526 def setdefault(self, cp, default=None):
1528 return self._extended.setdefault(cp, default)
1530 return self._normal.setdefault(cp, default)
1532 def __getitem__(self, cp):
1534 if not isinstance(cp, basestring):
1538 return self._extended[cp]
1540 ret = self._value_class()
1541 normal_match = self._normal.get(cp)
1544 if normal_match is not None:
1546 if hasattr(ret, "update"):
1547 ret.update(normal_match)
1548 elif hasattr(ret, "extend"):
1549 ret.extend(normal_match)
1551 raise NotImplementedError()
1553 for extended_cp in self._extended:
1554 if extended_cp_match(extended_cp, cp):
1556 if hasattr(ret, "update"):
1557 ret.update(self._extended[extended_cp])
1558 elif hasattr(ret, "extend"):
1559 ret.extend(self._extended[extended_cp])
1561 raise NotImplementedError()
1568 def __setitem__(self, cp, val):
1570 self._extended[cp] = val
1572 self._normal[cp] = val
1574 def __eq__(self, other):
1575 return self._value_class == other._value_class and \
1576 self._extended == other._extended and \
1577 self._normal == other._normal
1580 self._extended.clear()
1581 self._normal.clear()
1584 def get_operator(mydep):
1586 Return the operator used in a depstring.
1589 >>> from portage.dep import *
1590 >>> get_operator(">=test-1.0")
1593 @param mydep: The dep string to check
1596 @return: The operator. One of:
1597 '~', '=', '>', '<', '=*', '>=', or '<='
1599 if not isinstance(mydep, Atom):
1602 return mydep.operator
1604 def dep_getcpv(mydep):
1606 Return the category-package-version with any operators/slot specifications stripped off
1609 >>> dep_getcpv('>=media-libs/test-3.0')
1610 'media-libs/test-3.0'
1612 @param mydep: The depstring
1615 @return: The depstring with the operator removed
1617 if not isinstance(mydep, Atom):
1622 def dep_getslot(mydep):
1624 Retrieve the slot on a depend.
1627 >>> dep_getslot('app-misc/test:3')
1630 @param mydep: The depstring to retrieve the slot of
1635 slot = getattr(mydep, "slot", False)
1636 if slot is not False:
1639 #remove repo_name if present
1640 mydep = mydep.split(_repo_separator)[0]
1642 colon = mydep.find(_slot_separator)
1644 bracket = mydep.find("[", colon)
1646 return mydep[colon+1:]
1648 return mydep[colon+1:bracket]
1651 def dep_getrepo(mydep):
1653 Retrieve the repo on a depend.
1656 >>> dep_getrepo('app-misc/test::repository')
1659 @param mydep: The depstring to retrieve the repository of
1662 @return: The repository name
1664 repo = getattr(mydep, "repo", False)
1665 if repo is not False:
1668 metadata = getattr(mydep, "metadata", False)
1670 repo = metadata.get('repository', False)
1671 if repo is not False:
1674 colon = mydep.find(_repo_separator)
1676 bracket = mydep.find("[", colon)
1678 return mydep[colon+2:]
1680 return mydep[colon+2:bracket]
1682 def remove_slot(mydep):
1684 Removes dep components from the right side of an atom:
1688 And repo_name from the left side.
1690 colon = mydep.find(_slot_separator)
1692 mydep = mydep[:colon]
1694 bracket = mydep.find("[")
1696 mydep = mydep[:bracket]
1699 def dep_getusedeps( depend ):
1701 Pull a listing of USE Dependencies out of a dep atom.
1704 >>> dep_getusedeps('app-misc/test:3[foo,-bar]')
1707 @param depend: The depstring to process
1708 @type depend: String
1710 @return: List of use flags ( or [] if no flags exist )
1713 open_bracket = depend.find('[')
1714 # -1 = failure (think c++ string::npos)
1715 comma_separated = False
1717 while( open_bracket != -1 ):
1719 if bracket_count > 1:
1720 raise InvalidAtom(_("USE Dependency with more "
1721 "than one set of brackets: %s") % (depend,))
1722 close_bracket = depend.find(']', open_bracket )
1723 if close_bracket == -1:
1724 raise InvalidAtom(_("USE Dependency with no closing bracket: %s") % depend )
1725 use = depend[open_bracket + 1: close_bracket]
1726 # foo[1:1] may return '' instead of None, we don't want '' in the result
1728 raise InvalidAtom(_("USE Dependency with "
1729 "no use flag ([]): %s") % depend )
1730 if not comma_separated:
1731 comma_separated = "," in use
1733 if comma_separated and bracket_count > 1:
1734 raise InvalidAtom(_("USE Dependency contains a mixture of "
1735 "comma and bracket separators: %s") % depend )
1738 for x in use.split(","):
1742 raise InvalidAtom(_("USE Dependency with no use "
1743 "flag next to comma: %s") % depend )
1745 use_list.append(use)
1747 # Find next use flag
1748 open_bracket = depend.find( '[', open_bracket+1 )
1749 return tuple(use_list)
1751 def isvalidatom(atom, allow_blockers=False, allow_wildcard=False, allow_repo=False):
1753 Check to see if a depend atom is valid
1756 >>> isvalidatom('media-libs/test-3.0')
1758 >>> isvalidatom('>=media-libs/test-3.0')
1761 @param atom: The depend atom to check against
1762 @type atom: String or Atom
1764 @return: One of the following:
1765 1) False if the atom is invalid
1766 2) True if the atom is valid
1769 if not isinstance(atom, Atom):
1770 atom = Atom(atom, allow_wildcard=allow_wildcard, allow_repo=allow_repo)
1771 if not allow_blockers and atom.blocker:
1777 def isjustname(mypkg):
1779 Checks to see if the atom is only the package name (no version parts).
1782 >>> isjustname('=media-libs/test-3.0')
1784 >>> isjustname('media-libs/test')
1787 @param mypkg: The package atom to check
1788 @param mypkg: String or Atom
1790 @return: One of the following:
1791 1) False if the package string is not just the package name
1795 if not isinstance(mypkg, Atom):
1797 return mypkg == mypkg.cp
1801 for x in mypkg.split('-')[-2:]:
1806 def isspecific(mypkg):
1808 Checks to see if a package is in =category/package-version or
1809 package-version format.
1812 >>> isspecific('media-libs/test')
1814 >>> isspecific('=media-libs/test-3.0')
1817 @param mypkg: The package depstring to check against
1820 @return: One of the following:
1821 1) False if the package string is not specific
1825 if not isinstance(mypkg, Atom):
1827 return mypkg != mypkg.cp
1831 # Fall back to legacy code for backward compatibility.
1832 return not isjustname(mypkg)
1834 def dep_getkey(mydep):
1836 Return the category/package-name of a depstring.
1839 >>> dep_getkey('=media-libs/test-3.0')
1842 @param mydep: The depstring to retrieve the category/package-name of
1845 @return: The package category/package-name
1847 if not isinstance(mydep, Atom):
1848 mydep = Atom(mydep, allow_wildcard=True, allow_repo=True)
1852 def match_to_list(mypkg, mylist):
1854 Searches list for entries that matches the package.
1856 @param mypkg: The package atom to match
1858 @param mylist: The list of package atoms to compare against
1861 @return: A unique list of package atoms that match the given package atom
1867 if x not in matches and match_from_list(x, pkgs):
1872 def best_match_to_list(mypkg, mylist):
1874 Returns the most specific entry that matches the package given.
1876 @param mypkg: The package atom to check
1878 @param mylist: The list of package atoms to check against
1881 @return: The package atom which best matches given the following ordering:
1891 - cp:slot with extended syntax 0
1892 - cp with extended syntax -1
1894 operator_values = {'=':6, '~':5, '=*':4,
1895 '>':2, '<':2, '>=':2, '<=':2, None:1}
1899 for x in match_to_list(mypkg, mylist):
1900 if x.extended_syntax:
1901 if dep_getslot(x) is not None:
1910 if dep_getslot(x) is not None:
1914 op_val = operator_values[x.operator]
1915 if op_val > maxvalue:
1918 elif op_val == maxvalue and op_val == 2:
1919 # For >, <, >=, and <=, the one with the version
1920 # closest to mypkg is the best match.
1921 if mypkg_cpv is None:
1923 mypkg_cpv = mypkg.cpv
1924 except AttributeError:
1925 mypkg_cpv = _pkg_str(remove_slot(mypkg))
1926 if bestm.cpv == mypkg_cpv or bestm.cpv == x.cpv:
1928 elif x.cpv == mypkg_cpv:
1931 # Sort the cpvs to find the one closest to mypkg_cpv
1932 cpv_list = [bestm.cpv, mypkg_cpv, x.cpv]
1933 def cmp_cpv(cpv1, cpv2):
1934 return vercmp(cpv1.version, cpv2.version)
1935 cpv_list.sort(key=cmp_sort_key(cmp_cpv))
1936 if cpv_list[0] is mypkg_cpv or cpv_list[-1] is mypkg_cpv:
1937 if cpv_list[1] is x.cpv:
1940 # TODO: handle the case where mypkg_cpv is in the middle
1945 def match_from_list(mydep, candidate_list):
1947 Searches list for entries that matches the package.
1949 @param mydep: The package atom to match
1951 @param candidate_list: The list of package atoms to compare against
1952 @param candidate_list: List
1954 @return: A list of package atoms that match the given package atom
1957 if not candidate_list:
1960 if "!" == mydep[:1]:
1961 if "!" == mydep[1:2]:
1965 if not isinstance(mydep, Atom):
1966 mydep = Atom(mydep, allow_wildcard=True, allow_repo=True)
1969 mycpv_cps = catpkgsplit(mycpv) # Can be None if not specific
1973 cat, pkg = catsplit(mycpv)
1977 cat, pkg, ver, rev = mycpv_cps
1979 raise KeyError(_("Specific key requires an operator"
1980 " (%s) (try adding an '=')") % (mydep))
1983 operator = mydep.operator
1985 writemsg(_("!!! Invalid atom: %s\n") % mydep, noiselevel=-1)
1992 if operator is None:
1993 for x in candidate_list:
1994 cp = getattr(x, "cp", None)
1996 mysplit = catpkgsplit(remove_slot(x))
1997 if mysplit is not None:
1998 cp = mysplit[0] + '/' + mysplit[1]
2003 if cp == mycpv or (mydep.extended_syntax and \
2004 extended_cp_match(mydep.cp, cp)):
2007 elif operator == "=": # Exact match
2008 for x in candidate_list:
2009 xcpv = getattr(x, "cpv", None)
2011 xcpv = remove_slot(x)
2012 if not cpvequal(xcpv, mycpv):
2016 elif operator == "=*": # glob match
2017 # XXX: Nasty special casing for leading zeros
2018 # Required as =* is a literal prefix match, so can't
2020 mysplit = catpkgsplit(mycpv)
2021 myver = mysplit[2].lstrip("0")
2022 if not myver or not myver[0].isdigit():
2024 mycpv_cmp = mysplit[0]+"/"+mysplit[1]+"-"+myver
2025 for x in candidate_list:
2026 xs = getattr(x, "cpv_split", None)
2028 xs = catpkgsplit(remove_slot(x))
2029 myver = xs[2].lstrip("0")
2030 if not myver or not myver[0].isdigit():
2032 xcpv = xs[0]+"/"+xs[1]+"-"+myver
2033 if xcpv.startswith(mycpv_cmp):
2036 elif operator == "~": # version, any revision, match
2037 for x in candidate_list:
2038 xs = getattr(x, "cpv_split", None)
2040 xs = catpkgsplit(remove_slot(x))
2042 raise InvalidData(x)
2043 if not cpvequal(xs[0]+"/"+xs[1]+"-"+xs[2], mycpv_cps[0]+"/"+mycpv_cps[1]+"-"+mycpv_cps[2]):
2049 elif operator in [">", ">=", "<", "<="]:
2050 for x in candidate_list:
2051 if hasattr(x, 'cp'):
2055 pkg = _pkg_str(remove_slot(x))
2059 if pkg.cp != mydep.cp:
2062 result = vercmp(pkg.version, mydep.version)
2063 except ValueError: # pkgcmp may return ValueError during int() conversion
2064 writemsg(_("\nInvalid package name: %s\n") % x, noiselevel=-1)
2068 elif operator == ">":
2071 elif operator == ">=":
2074 elif operator == "<":
2077 elif operator == "<=":
2081 raise KeyError(_("Unknown operator: %s") % mydep)
2083 raise KeyError(_("Unknown operator: %s") % mydep)
2085 if slot is not None and not mydep.extended_syntax:
2086 candidate_list = mylist
2088 for x in candidate_list:
2089 xslot = getattr(x, "slot", False)
2091 xslot = dep_getslot(x)
2092 if xslot is not None and xslot != slot:
2096 if mydep.unevaluated_atom.use:
2097 candidate_list = mylist
2099 for x in candidate_list:
2100 use = getattr(x, "use", None)
2102 if mydep.unevaluated_atom.use and \
2103 not x.iuse.is_valid_flag(
2104 mydep.unevaluated_atom.use.required):
2109 missing_enabled = mydep.use.missing_enabled.difference(x.iuse.all)
2110 missing_disabled = mydep.use.missing_disabled.difference(x.iuse.all)
2112 if mydep.use.enabled:
2113 if any(f in mydep.use.enabled for f in missing_disabled):
2115 need_enabled = mydep.use.enabled.difference(use.enabled)
2117 if any(f not in missing_enabled for f in need_enabled):
2120 if mydep.use.disabled:
2121 if any(f in mydep.use.disabled for f in missing_enabled):
2123 need_disabled = mydep.use.disabled.intersection(use.enabled)
2125 if any(f not in missing_disabled for f in need_disabled):
2131 candidate_list = mylist
2133 for x in candidate_list:
2134 repo = getattr(x, "repo", False)
2136 repo = dep_getrepo(x)
2137 if repo is not None and repo != _unknown_repo and \
2144 def human_readable_required_use(required_use):
2145 return required_use.replace("^^", "exactly-one-of").replace("||", "any-of")
2147 def get_required_use_flags(required_use):
2149 Returns a set of use flags that are used in the given REQUIRED_USE string
2151 @param required_use: REQUIRED_USE string
2152 @type required_use: String
2154 @return: Set of use flags that are used in the given REQUIRED_USE string
2157 mysplit = required_use.split()
2160 need_bracket = False
2164 def register_token(token):
2165 if token.endswith("?"):
2167 if token.startswith("!"):
2169 used_flags.add(token)
2171 for token in mysplit:
2173 need_bracket = False
2178 raise InvalidDependString(
2179 _("malformed syntax: '%s'") % required_use)
2185 if stack[level][-1] in ("||", "^^") or \
2186 (not isinstance(stack[level][-1], bool) and \
2187 stack[level][-1][-1] == "?"):
2190 stack[level].append(True)
2192 if l and not ignore:
2193 stack[level].append(all(x for x in l))
2195 raise InvalidDependString(
2196 _("malformed syntax: '%s'") % required_use)
2197 elif token in ("||", "^^"):
2199 raise InvalidDependString(
2200 _("malformed syntax: '%s'") % required_use)
2202 stack[level].append(token)
2204 if need_bracket or "(" in token or ")" in token or \
2205 "|" in token or "^" in token:
2206 raise InvalidDependString(
2207 _("malformed syntax: '%s'") % required_use)
2209 if token[-1] == "?":
2211 stack[level].append(token)
2213 stack[level].append(True)
2215 register_token(token)
2217 if level != 0 or need_bracket:
2218 raise InvalidDependString(
2219 _("malformed syntax: '%s'") % required_use)
2221 return frozenset(used_flags)
2223 class _RequiredUseLeaf(object):
2225 __slots__ = ('_satisfied', '_token')
2227 def __init__(self, token, satisfied):
2229 self._satisfied = satisfied
2231 def tounicode(self):
2234 class _RequiredUseBranch(object):
2236 __slots__ = ('_children', '_operator', '_parent', '_satisfied')
2238 def __init__(self, operator=None, parent=None):
2240 self._operator = operator
2241 self._parent = parent
2242 self._satisfied = False
2245 return self._satisfied
2247 def tounicode(self):
2249 include_parens = self._parent is not None
2251 if self._operator is not None:
2252 tokens.append(self._operator)
2257 complex_nesting = False
2259 while node != None and not complex_nesting:
2260 if node._operator in ("||", "^^"):
2261 complex_nesting = True
2266 for child in self._children:
2267 tokens.append(child.tounicode())
2269 for child in self._children:
2270 if not child._satisfied:
2271 tokens.append(child.tounicode())
2276 return " ".join(tokens)
2278 if sys.hexversion < 0x3000000:
2279 __nonzero__ = __bool__
2281 def check_required_use(required_use, use, iuse_match):
2283 Checks if the use flags listed in 'use' satisfy all
2284 constraints specified in 'constraints'.
2286 @param required_use: REQUIRED_USE string
2287 @type required_use: String
2288 @param use: Enabled use flags
2290 @param iuse_match: Callable that takes a single flag argument and returns
2291 True if the flag is matched, false otherwise,
2292 @param iuse_match: Callable
2294 @return: Indicates if REQUIRED_USE constraints are satisfied
2297 def is_active(token):
2298 if token.startswith("!"):
2305 if not flag or not iuse_match(flag):
2306 msg = _("USE flag '%s' is not in IUSE") \
2308 e = InvalidData(msg, category='IUSE.missing')
2309 raise InvalidDependString(msg, errors=(e,))
2311 return (flag in use and not is_negated) or \
2312 (flag not in use and is_negated)
2314 def is_satisfied(operator, argument):
2319 if operator == "||":
2320 return (True in argument)
2321 elif operator == "^^":
2322 return (argument.count(True) == 1)
2323 elif operator[-1] == "?":
2324 return (False not in argument)
2326 mysplit = required_use.split()
2329 tree = _RequiredUseBranch()
2331 need_bracket = False
2333 for token in mysplit:
2335 if not need_bracket:
2336 child = _RequiredUseBranch(parent=node)
2337 node._children.append(child)
2340 need_bracket = False
2345 raise InvalidDependString(
2346 _("malformed syntax: '%s'") % required_use)
2352 if stack[level][-1] in ("||", "^^"):
2353 op = stack[level].pop()
2354 satisfied = is_satisfied(op, l)
2355 stack[level].append(satisfied)
2356 node._satisfied = satisfied
2358 elif not isinstance(stack[level][-1], bool) and \
2359 stack[level][-1][-1] == "?":
2360 op = stack[level].pop()
2361 if is_active(op[:-1]):
2362 satisfied = is_satisfied(op, l)
2363 stack[level].append(satisfied)
2364 node._satisfied = satisfied
2366 node._satisfied = True
2367 last_node = node._parent._children.pop()
2368 if last_node is not node:
2369 raise AssertionError(
2370 "node is not last child of parent")
2375 satisfied = False not in l
2376 node._satisfied = satisfied
2378 stack[level].append(satisfied)
2380 if len(node._children) <= 1 or \
2381 node._parent._operator not in ("||", "^^"):
2382 last_node = node._parent._children.pop()
2383 if last_node is not node:
2384 raise AssertionError(
2385 "node is not last child of parent")
2386 for child in node._children:
2387 node._parent._children.append(child)
2388 if isinstance(child, _RequiredUseBranch):
2389 child._parent = node._parent
2391 elif not node._children:
2392 last_node = node._parent._children.pop()
2393 if last_node is not node:
2394 raise AssertionError(
2395 "node is not last child of parent")
2397 elif len(node._children) == 1 and op in ("||", "^^"):
2398 last_node = node._parent._children.pop()
2399 if last_node is not node:
2400 raise AssertionError(
2401 "node is not last child of parent")
2402 node._parent._children.append(node._children[0])
2403 if isinstance(node._children[0], _RequiredUseBranch):
2404 node._children[0]._parent = node._parent
2405 node = node._children[0]
2406 if node._operator is None and \
2407 node._parent._operator not in ("||", "^^"):
2408 last_node = node._parent._children.pop()
2409 if last_node is not node:
2410 raise AssertionError(
2411 "node is not last child of parent")
2412 for child in node._children:
2413 node._parent._children.append(child)
2414 if isinstance(child, _RequiredUseBranch):
2415 child._parent = node._parent
2419 raise InvalidDependString(
2420 _("malformed syntax: '%s'") % required_use)
2421 elif token in ("||", "^^"):
2423 raise InvalidDependString(
2424 _("malformed syntax: '%s'") % required_use)
2426 stack[level].append(token)
2427 child = _RequiredUseBranch(operator=token, parent=node)
2428 node._children.append(child)
2431 if need_bracket or "(" in token or ")" in token or \
2432 "|" in token or "^" in token:
2433 raise InvalidDependString(
2434 _("malformed syntax: '%s'") % required_use)
2436 if token[-1] == "?":
2438 stack[level].append(token)
2439 child = _RequiredUseBranch(operator=token, parent=node)
2440 node._children.append(child)
2443 satisfied = is_active(token)
2444 stack[level].append(satisfied)
2445 node._children.append(_RequiredUseLeaf(token, satisfied))
2447 if level != 0 or need_bracket:
2448 raise InvalidDependString(
2449 _("malformed syntax: '%s'") % required_use)
2451 tree._satisfied = False not in stack[0]
2454 def extract_affecting_use(mystr, atom, eapi=None):
2456 Take a dep string and an atom and return the use flags
2457 that decide if the given atom is in effect.
2460 >>> extract_use_cond('sasl? ( dev-libs/cyrus-sasl ) \
2461 !minimal? ( cxx? ( dev-libs/cyrus-sasl ) )', 'dev-libs/cyrus-sasl')
2462 (['sasl', 'minimal', 'cxx'])
2464 @param dep: The dependency string
2466 @param atom: The atom to get into effect
2468 @rtype: Tuple of two lists of strings
2469 @return: List of use flags that need to be enabled, List of use flag that need to be disabled
2471 useflag_re = _get_useflag_re(eapi)
2472 mysplit = mystr.split()
2475 need_bracket = False
2476 affecting_use = set()
2478 def flag(conditional):
2479 if conditional[0] == "!":
2480 flag = conditional[1:-1]
2482 flag = conditional[:-1]
2484 if useflag_re.match(flag) is None:
2485 raise InvalidDependString(
2486 _("invalid use flag '%s' in conditional '%s'") % \
2487 (flag, conditional))
2491 for token in mysplit:
2493 need_bracket = False
2498 raise InvalidDependString(
2499 _("malformed syntax: '%s'") % mystr)
2503 is_single = (len(l) == 1 or (len(l)==2 and (l[0] == "||" or l[0][-1] == "?")))
2505 def ends_in_any_of_dep(k):
2506 return k>=0 and stack[k] and stack[k][-1] == "||"
2508 def ends_in_operator(k):
2509 return k>=0 and stack[k] and (stack[k][-1] == "||" or stack[k][-1][-1] == "?")
2511 def special_append():
2513 Use extend instead of append if possible. This kills all redundant brackets.
2515 if is_single and (not stack[level] or not stack[level][-1][-1] == "?"):
2516 if len(l) == 1 and isinstance(l[0], list):
2518 stack[level].extend(l[0])
2520 stack[level].extend(l)
2522 stack[level].append(l)
2525 if not ends_in_any_of_dep(level-1) and not ends_in_operator(level):
2526 #Optimize: ( ( ... ) ) -> ( ... ). Make sure there is no '||' hanging around.
2527 stack[level].extend(l)
2528 elif not stack[level]:
2529 #An '||' in the level above forces us to keep to brackets.
2531 elif len(l) == 1 and ends_in_any_of_dep(level):
2532 #Optimize: || ( A ) -> A
2535 elif len(l) == 2 and (l[0] == "||" or l[0][-1] == "?") and stack[level][-1] in (l[0], "||"):
2536 #Optimize: || ( || ( ... ) ) -> || ( ... )
2537 # foo? ( foo? ( ... ) ) -> foo? ( ... )
2538 # || ( foo? ( ... ) ) -> foo? ( ... )
2542 affecting_use.add(flag(l[0]))
2544 if stack[level] and stack[level][-1][-1] == "?":
2545 affecting_use.add(flag(stack[level][-1]))
2548 if stack[level] and (stack[level][-1] == "||" or stack[level][-1][-1] == "?"):
2551 raise InvalidDependString(
2552 _("malformed syntax: '%s'") % mystr)
2555 raise InvalidDependString(
2556 _("malformed syntax: '%s'") % mystr)
2558 stack[level].append(token)
2561 raise InvalidDependString(
2562 _("malformed syntax: '%s'") % mystr)
2564 if token[-1] == "?":
2566 stack[level].append(token)
2568 stack[level].append(token)
2570 if level != 0 or need_bracket:
2571 raise InvalidDependString(
2572 _("malformed syntax: '%s'") % mystr)
2574 return affecting_use