summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorZac Medico <zmedico@gentoo.org>2010-02-25 21:15:35 +0000
committerZac Medico <zmedico@gentoo.org>2010-02-25 21:15:35 +0000
commit55a8c061f34e15d58fb4d9889f0157c7b7e5b0c9 (patch)
tree0df455c4e890b4baa6502f9631b74ef94e8fe13a
parentcec3c56e3ff699e83754ec2e6699efb123455e43 (diff)
downloadportage-55a8c061f34e15d58fb4d9889f0157c7b7e5b0c9.tar.gz
portage-55a8c061f34e15d58fb4d9889f0157c7b7e5b0c9.tar.bz2
portage-55a8c061f34e15d58fb4d9889f0157c7b7e5b0c9.zip
Move portage.dep_check and related functions to portage.dep.dep_check.
svn path=/main/trunk/; revision=15462
-rw-r--r--pym/portage/__init__.py619
-rw-r--r--pym/portage/dep/dep_check.py634
2 files changed, 635 insertions, 618 deletions
diff --git a/pym/portage/__init__.py b/pym/portage/__init__.py
index dbce97bb3..27ea106ad 100644
--- a/pym/portage/__init__.py
+++ b/pym/portage/__init__.py
@@ -92,6 +92,7 @@ try:
'portage.dep:best_match_to_list,dep_getcpv,dep_getkey,' + \
'flatten,get_operator,isjustname,isspecific,isvalidatom,' + \
'match_from_list,match_to_list',
+ 'portage.dep.dep_check:dep_check,dep_eval,dep_wordreduce,dep_zapdeps',
'portage.eclass_cache',
'portage.env.loaders',
'portage.exception',
@@ -912,624 +913,6 @@ def dep_virtual(mysplit, mysettings):
newsplit.append(x)
return newsplit
-def _expand_new_virtuals(mysplit, edebug, mydbapi, mysettings, myroot="/",
- trees=None, use_mask=None, use_force=None, **kwargs):
- """
- In order to solve bug #141118, recursively expand new-style virtuals so
- as to collapse one or more levels of indirection, generating an expanded
- search space. In dep_zapdeps, new-style virtuals will be assigned
- zero cost regardless of whether or not they are currently installed. Virtual
- blockers are supported but only when the virtual expands to a single
- atom because it wouldn't necessarily make sense to block all the components
- of a compound virtual. When more than one new-style virtual is matched,
- the matches are sorted from highest to lowest versions and the atom is
- expanded to || ( highest match ... lowest match )."""
- newsplit = []
- mytrees = trees[myroot]
- portdb = mytrees["porttree"].dbapi
- atom_graph = mytrees.get("atom_graph")
- parent = mytrees.get("parent")
- virt_parent = mytrees.get("virt_parent")
- graph_parent = None
- eapi = None
- if parent is not None:
- if virt_parent is not None:
- graph_parent = virt_parent
- eapi = virt_parent[0].metadata['EAPI']
- else:
- graph_parent = parent
- eapi = parent.metadata["EAPI"]
- repoman = not mysettings.local_config
- if kwargs["use_binaries"]:
- portdb = trees[myroot]["bintree"].dbapi
- myvirtuals = mysettings.getvirtuals()
- pprovideddict = mysettings.pprovideddict
- myuse = kwargs["myuse"]
- for x in mysplit:
- if x == "||":
- newsplit.append(x)
- continue
- elif isinstance(x, list):
- newsplit.append(_expand_new_virtuals(x, edebug, mydbapi,
- mysettings, myroot=myroot, trees=trees, use_mask=use_mask,
- use_force=use_force, **kwargs))
- continue
-
- if not isinstance(x, portage.dep.Atom):
- try:
- x = portage.dep.Atom(x)
- except portage.exception.InvalidAtom:
- if portage.dep._dep_check_strict:
- raise portage.exception.ParseError(
- _("invalid atom: '%s'") % x)
- else:
- # Only real Atom instances are allowed past this point.
- continue
- else:
- if x.blocker and x.blocker.overlap.forbid and \
- eapi in ("0", "1") and portage.dep._dep_check_strict:
- raise portage.exception.ParseError(
- _("invalid atom: '%s'") % (x,))
- if x.use and eapi in ("0", "1") and \
- portage.dep._dep_check_strict:
- raise portage.exception.ParseError(
- _("invalid atom: '%s'") % (x,))
-
- if repoman and x.use and x.use.conditional:
- evaluated_atom = portage.dep.remove_slot(x)
- if x.slot:
- evaluated_atom += ":%s" % x.slot
- evaluated_atom += str(x.use._eval_qa_conditionals(
- use_mask, use_force))
- x = portage.dep.Atom(evaluated_atom)
-
- if not repoman and \
- myuse is not None and isinstance(x, portage.dep.Atom) and x.use:
- if x.use.conditional:
- x = x.evaluate_conditionals(myuse)
-
- mykey = x.cp
- if not mykey.startswith("virtual/"):
- newsplit.append(x)
- if atom_graph is not None:
- atom_graph.add(x, graph_parent)
- continue
- mychoices = myvirtuals.get(mykey, [])
- if x.blocker:
- # Virtual blockers are no longer expanded here since
- # the un-expanded virtual atom is more useful for
- # maintaining a cache of blocker atoms.
- newsplit.append(x)
- if atom_graph is not None:
- atom_graph.add(x, graph_parent)
- continue
-
- if repoman or not hasattr(portdb, 'match_pkgs'):
- if portdb.cp_list(x.cp):
- newsplit.append(x)
- else:
- # TODO: Add PROVIDE check for repoman.
- a = []
- for y in mychoices:
- a.append(dep.Atom(x.replace(x.cp, y.cp, 1)))
- if not a:
- newsplit.append(x)
- elif len(a) == 1:
- newsplit.append(a[0])
- else:
- newsplit.append(['||'] + a)
- continue
-
- pkgs = []
- # Ignore USE deps here, since otherwise we might not
- # get any matches. Choices with correct USE settings
- # will be preferred in dep_zapdeps().
- matches = portdb.match_pkgs(x.without_use)
- # Use descending order to prefer higher versions.
- matches.reverse()
- for pkg in matches:
- # only use new-style matches
- if pkg.cp.startswith("virtual/"):
- pkgs.append(pkg)
- if not (pkgs or mychoices):
- # This one couldn't be expanded as a new-style virtual. Old-style
- # virtuals have already been expanded by dep_virtual, so this one
- # is unavailable and dep_zapdeps will identify it as such. The
- # atom is not eliminated here since it may still represent a
- # dependency that needs to be satisfied.
- newsplit.append(x)
- if atom_graph is not None:
- atom_graph.add(x, graph_parent)
- continue
-
- a = []
- for pkg in pkgs:
- virt_atom = '=' + pkg.cpv
- if x.use:
- virt_atom += str(x.use)
- virt_atom = dep.Atom(virt_atom)
- # According to GLEP 37, RDEPEND is the only dependency
- # type that is valid for new-style virtuals. Repoman
- # should enforce this.
- depstring = pkg.metadata['RDEPEND']
- pkg_kwargs = kwargs.copy()
- pkg_kwargs["myuse"] = pkg.use.enabled
- if edebug:
- util.writemsg_level(_("Virtual Parent: %s\n") \
- % (pkg,), noiselevel=-1, level=logging.DEBUG)
- util.writemsg_level(_("Virtual Depstring: %s\n") \
- % (depstring,), noiselevel=-1, level=logging.DEBUG)
-
- # Set EAPI used for validation in dep_check() recursion.
- mytrees["virt_parent"] = (pkg, virt_atom)
-
- try:
- mycheck = dep_check(depstring, mydbapi, mysettings,
- myroot=myroot, trees=trees, **pkg_kwargs)
- finally:
- # Restore previous EAPI after recursion.
- if virt_parent is not None:
- mytrees["virt_parent"] = virt_parent
- else:
- del mytrees["virt_parent"]
-
- if not mycheck[0]:
- raise portage.exception.ParseError(
- "%s: %s '%s'" % (y[0], mycheck[1], depstring))
-
- # pull in the new-style virtual
- mycheck[1].append(virt_atom)
- a.append(mycheck[1])
- if atom_graph is not None:
- atom_graph.add(virt_atom, graph_parent)
- # Plain old-style virtuals. New-style virtuals are preferred.
- if not pkgs:
- for y in mychoices:
- new_atom = dep.Atom(x.replace(x.cp, y.cp, 1))
- matches = portdb.match(new_atom)
- # portdb is an instance of depgraph._dep_check_composite_db, so
- # USE conditionals are already evaluated.
- if matches and mykey in \
- portdb.aux_get(matches[-1], ['PROVIDE'])[0].split():
- a.append(new_atom)
- if atom_graph is not None:
- atom_graph.add(new_atom, graph_parent)
-
- if not a and mychoices:
- # Check for a virtual package.provided match.
- for y in mychoices:
- new_atom = dep.Atom(x.replace(x.cp, y.cp, 1))
- if match_from_list(new_atom,
- pprovideddict.get(new_atom.cp, [])):
- a.append(new_atom)
- if atom_graph is not None:
- atom_graph.add(new_atom, graph_parent)
-
- if not a:
- newsplit.append(x)
- if atom_graph is not None:
- atom_graph.add(x, graph_parent)
- elif len(a) == 1:
- newsplit.append(a[0])
- else:
- newsplit.append(['||'] + a)
-
- return newsplit
-
-def dep_eval(deplist):
- if not deplist:
- return 1
- if deplist[0]=="||":
- #or list; we just need one "1"
- for x in deplist[1:]:
- if isinstance(x, list):
- if dep_eval(x)==1:
- return 1
- elif x==1:
- return 1
- #XXX: unless there's no available atoms in the list
- #in which case we need to assume that everything is
- #okay as some ebuilds are relying on an old bug.
- if len(deplist) == 1:
- return 1
- return 0
- else:
- for x in deplist:
- if isinstance(x, list):
- if dep_eval(x)==0:
- return 0
- elif x==0 or x==2:
- return 0
- return 1
-
-def dep_zapdeps(unreduced, reduced, myroot, use_binaries=0, trees=None):
- """Takes an unreduced and reduced deplist and removes satisfied dependencies.
- Returned deplist contains steps that must be taken to satisfy dependencies."""
- if trees is None:
- global db
- trees = db
- writemsg("ZapDeps -- %s\n" % (use_binaries), 2)
- if not reduced or unreduced == ["||"] or dep_eval(reduced):
- return []
-
- if unreduced[0] != "||":
- unresolved = []
- for x, satisfied in zip(unreduced, reduced):
- if isinstance(x, list):
- unresolved += dep_zapdeps(x, satisfied, myroot,
- use_binaries=use_binaries, trees=trees)
- elif not satisfied:
- unresolved.append(x)
- return unresolved
-
- # We're at a ( || atom ... ) type level and need to make a choice
- deps = unreduced[1:]
- satisfieds = reduced[1:]
-
- # Our preference order is for an the first item that:
- # a) contains all unmasked packages with the same key as installed packages
- # b) contains all unmasked packages
- # c) contains masked installed packages
- # d) is the first item
-
- preferred_installed = []
- preferred_in_graph = []
- preferred_any_slot = []
- preferred_non_installed = []
- unsat_use_in_graph = []
- unsat_use_installed = []
- unsat_use_non_installed = []
- other = []
-
- # unsat_use_* must come after preferred_non_installed
- # for correct ordering in cases like || ( foo[a] foo[b] ).
- choice_bins = (
- preferred_in_graph,
- preferred_installed,
- preferred_any_slot,
- preferred_non_installed,
- unsat_use_in_graph,
- unsat_use_installed,
- unsat_use_non_installed,
- other,
- )
-
- # Alias the trees we'll be checking availability against
- parent = trees[myroot].get("parent")
- priority = trees[myroot].get("priority")
- graph_db = trees[myroot].get("graph_db")
- vardb = None
- if "vartree" in trees[myroot]:
- vardb = trees[myroot]["vartree"].dbapi
- if use_binaries:
- mydbapi = trees[myroot]["bintree"].dbapi
- else:
- mydbapi = trees[myroot]["porttree"].dbapi
-
- # Sort the deps into installed, not installed but already
- # in the graph and other, not installed and not in the graph
- # and other, with values of [[required_atom], availablility]
- for x, satisfied in zip(deps, satisfieds):
- if isinstance(x, list):
- atoms = dep_zapdeps(x, satisfied, myroot,
- use_binaries=use_binaries, trees=trees)
- else:
- atoms = [x]
- if vardb is None:
- # When called by repoman, we can simply return the first choice
- # because dep_eval() handles preference selection.
- return atoms
-
- all_available = True
- all_use_satisfied = True
- slot_map = {}
- cp_map = {}
- for atom in atoms:
- if atom.blocker:
- continue
- # Ignore USE dependencies here since we don't want USE
- # settings to adversely affect || preference evaluation.
- avail_pkg = mydbapi.match(atom.without_use)
- if avail_pkg:
- avail_pkg = avail_pkg[-1] # highest (ascending order)
- avail_slot = dep.Atom("%s:%s" % (atom.cp,
- mydbapi.aux_get(avail_pkg, ["SLOT"])[0]))
- if not avail_pkg:
- all_available = False
- all_use_satisfied = False
- break
-
- if atom.use:
- avail_pkg_use = mydbapi.match(atom)
- if not avail_pkg_use:
- all_use_satisfied = False
- else:
- # highest (ascending order)
- avail_pkg_use = avail_pkg_use[-1]
- if avail_pkg_use != avail_pkg:
- avail_pkg = avail_pkg_use
- avail_slot = dep.Atom("%s:%s" % (atom.cp,
- mydbapi.aux_get(avail_pkg, ["SLOT"])[0]))
-
- slot_map[avail_slot] = avail_pkg
- pkg_cp = cpv_getkey(avail_pkg)
- highest_cpv = cp_map.get(pkg_cp)
- if highest_cpv is None or \
- pkgcmp(catpkgsplit(avail_pkg)[1:],
- catpkgsplit(highest_cpv)[1:]) > 0:
- cp_map[pkg_cp] = avail_pkg
-
- this_choice = (atoms, slot_map, cp_map, all_available)
- if all_available:
- # The "all installed" criterion is not version or slot specific.
- # If any version of a package is already in the graph then we
- # assume that it is preferred over other possible packages choices.
- all_installed = True
- for atom in set(dep.Atom(atom.cp) for atom in atoms \
- if not atom.blocker):
- # New-style virtuals have zero cost to install.
- if not vardb.match(atom) and not atom.startswith("virtual/"):
- all_installed = False
- break
- all_installed_slots = False
- if all_installed:
- all_installed_slots = True
- for slot_atom in slot_map:
- # New-style virtuals have zero cost to install.
- if not vardb.match(slot_atom) and \
- not slot_atom.startswith("virtual/"):
- all_installed_slots = False
- break
- if graph_db is None:
- if all_use_satisfied:
- if all_installed:
- if all_installed_slots:
- preferred_installed.append(this_choice)
- else:
- preferred_any_slot.append(this_choice)
- else:
- preferred_non_installed.append(this_choice)
- else:
- if all_installed_slots:
- unsat_use_installed.append(this_choice)
- else:
- unsat_use_non_installed.append(this_choice)
- else:
- all_in_graph = True
- for slot_atom in slot_map:
- # New-style virtuals have zero cost to install.
- if not graph_db.match(slot_atom) and \
- not slot_atom.startswith("virtual/"):
- all_in_graph = False
- break
- circular_atom = None
- if all_in_graph:
- if parent is None or priority is None:
- pass
- elif priority.buildtime:
- # Check if the atom would result in a direct circular
- # dependency and try to avoid that if it seems likely
- # to be unresolvable. This is only relevant for
- # buildtime deps that aren't already satisfied by an
- # installed package.
- cpv_slot_list = [parent]
- for atom in atoms:
- if atom.blocker:
- continue
- if vardb.match(atom):
- # If the atom is satisfied by an installed
- # version then it's not a circular dep.
- continue
- if atom.cp != parent.cp:
- continue
- if match_from_list(atom, cpv_slot_list):
- circular_atom = atom
- break
- if circular_atom is not None:
- other.append(this_choice)
- else:
- if all_use_satisfied:
- if all_in_graph:
- preferred_in_graph.append(this_choice)
- elif all_installed:
- if all_installed_slots:
- preferred_installed.append(this_choice)
- else:
- preferred_any_slot.append(this_choice)
- else:
- preferred_non_installed.append(this_choice)
- else:
- if all_in_graph:
- unsat_use_in_graph.append(this_choice)
- elif all_installed_slots:
- unsat_use_installed.append(this_choice)
- else:
- unsat_use_non_installed.append(this_choice)
- else:
- other.append(this_choice)
-
- # Prefer choices which contain upgrades to higher slots. This helps
- # for deps such as || ( foo:1 foo:2 ), where we want to prefer the
- # atom which matches the higher version rather than the atom furthest
- # to the left. Sorting is done separately for each of choice_bins, so
- # as not to interfere with the ordering of the bins. Because of the
- # bin separation, the main function of this code is to allow
- # --depclean to remove old slots (rather than to pull in new slots).
- for choices in choice_bins:
- if len(choices) < 2:
- continue
- for choice_1 in choices[1:]:
- atoms_1, slot_map_1, cp_map_1, all_available_1 = choice_1
- cps = set(cp_map_1)
- for choice_2 in choices:
- if choice_1 is choice_2:
- # choice_1 will not be promoted, so move on
- break
- atoms_2, slot_map_2, cp_map_2, all_available_2 = choice_2
- intersecting_cps = cps.intersection(cp_map_2)
- if not intersecting_cps:
- continue
- has_upgrade = False
- has_downgrade = False
- for cp in intersecting_cps:
- version_1 = cp_map_1[cp]
- version_2 = cp_map_2[cp]
- difference = pkgcmp(catpkgsplit(version_1)[1:],
- catpkgsplit(version_2)[1:])
- if difference != 0:
- if difference > 0:
- has_upgrade = True
- else:
- has_downgrade = True
- break
- if has_upgrade and not has_downgrade:
- # promote choice_1 in front of choice_2
- choices.remove(choice_1)
- index_2 = choices.index(choice_2)
- choices.insert(index_2, choice_1)
- break
-
- for allow_masked in (False, True):
- for choices in choice_bins:
- for atoms, slot_map, cp_map, all_available in choices:
- if all_available or allow_masked:
- return atoms
-
- assert(False) # This point should not be reachable
-
-def dep_check(depstring, mydbapi, mysettings, use="yes", mode=None, myuse=None,
- use_cache=1, use_binaries=0, myroot="/", trees=None):
- """Takes a depend string and parses the condition."""
- edebug = mysettings.get("PORTAGE_DEBUG", None) == "1"
- #check_config_instance(mysettings)
- if trees is None:
- trees = globals()["db"]
- if use=="yes":
- if myuse is None:
- #default behavior
- myusesplit = mysettings["PORTAGE_USE"].split()
- else:
- myusesplit = myuse
- # We've been given useflags to use.
- #print "USE FLAGS PASSED IN."
- #print myuse
- #if "bindist" in myusesplit:
- # print "BINDIST is set!"
- #else:
- # print "BINDIST NOT set."
- else:
- #we are being run by autouse(), don't consult USE vars yet.
- # WE ALSO CANNOT USE SETTINGS
- myusesplit=[]
-
- #convert parenthesis to sublists
- try:
- mysplit = portage.dep.paren_reduce(depstring)
- except portage.exception.InvalidDependString as e:
- return [0, str(e)]
-
- mymasks = set()
- useforce = set()
- useforce.add(mysettings["ARCH"])
- if use == "all":
- # This masking/forcing is only for repoman. In other cases, relevant
- # masking/forcing should have already been applied via
- # config.regenerate(). Also, binary or installed packages may have
- # been built with flags that are now masked, and it would be
- # inconsistent to mask them now. Additionally, myuse may consist of
- # flags from a parent package that is being merged to a $ROOT that is
- # different from the one that mysettings represents.
- mymasks.update(mysettings.usemask)
- mymasks.update(mysettings.archlist())
- mymasks.discard(mysettings["ARCH"])
- useforce.update(mysettings.useforce)
- useforce.difference_update(mymasks)
- try:
- mysplit = portage.dep.use_reduce(mysplit, uselist=myusesplit,
- masklist=mymasks, matchall=(use=="all"), excludeall=useforce)
- except portage.exception.InvalidDependString as e:
- return [0, str(e)]
-
- # Do the || conversions
- mysplit=portage.dep.dep_opconvert(mysplit)
-
- if mysplit == []:
- #dependencies were reduced to nothing
- return [1,[]]
-
- # Recursively expand new-style virtuals so as to
- # collapse one or more levels of indirection.
- try:
- mysplit = _expand_new_virtuals(mysplit, edebug, mydbapi, mysettings,
- use=use, mode=mode, myuse=myuse,
- use_force=useforce, use_mask=mymasks, use_cache=use_cache,
- use_binaries=use_binaries, myroot=myroot, trees=trees)
- except portage.exception.ParseError as e:
- return [0, str(e)]
-
- mysplit2=mysplit[:]
- mysplit2=dep_wordreduce(mysplit2,mysettings,mydbapi,mode,use_cache=use_cache)
- if mysplit2 is None:
- return [0, _("Invalid token")]
-
- writemsg("\n\n\n", 1)
- writemsg("mysplit: %s\n" % (mysplit), 1)
- writemsg("mysplit2: %s\n" % (mysplit2), 1)
-
- try:
- selected_atoms = dep_zapdeps(mysplit, mysplit2, myroot,
- use_binaries=use_binaries, trees=trees)
- except portage.exception.InvalidAtom as e:
- if portage.dep._dep_check_strict:
- raise # This shouldn't happen.
- # dbapi.match() failed due to an invalid atom in
- # the dependencies of an installed package.
- return [0, _("Invalid atom: '%s'") % (e,)]
-
- return [1, selected_atoms]
-
-def dep_wordreduce(mydeplist,mysettings,mydbapi,mode,use_cache=1):
- "Reduces the deplist to ones and zeros"
- deplist=mydeplist[:]
- for mypos, token in enumerate(deplist):
- if isinstance(deplist[mypos], list):
- #recurse
- deplist[mypos]=dep_wordreduce(deplist[mypos],mysettings,mydbapi,mode,use_cache=use_cache)
- elif deplist[mypos]=="||":
- pass
- elif token[:1] == "!":
- deplist[mypos] = False
- else:
- mykey = deplist[mypos].cp
- if mysettings and mykey in mysettings.pprovideddict and \
- match_from_list(deplist[mypos], mysettings.pprovideddict[mykey]):
- deplist[mypos]=True
- elif mydbapi is None:
- # Assume nothing is satisfied. This forces dep_zapdeps to
- # return all of deps the deps that have been selected
- # (excluding those satisfied by package.provided).
- deplist[mypos] = False
- else:
- if mode:
- x = mydbapi.xmatch(mode, deplist[mypos])
- if mode.startswith("minimum-"):
- mydep = []
- if x:
- mydep.append(x)
- else:
- mydep = x
- else:
- mydep=mydbapi.match(deplist[mypos],use_cache=use_cache)
- if mydep!=None:
- tmp=(len(mydep)>=1)
- if deplist[mypos][0]=="!":
- tmp=False
- deplist[mypos]=tmp
- else:
- #encountered invalid string
- return None
- return deplist
-
def getmaskingreason(mycpv, metadata=None, settings=None, portdb=None, return_location=False):
from portage.util import grablines
if settings is None:
diff --git a/pym/portage/dep/dep_check.py b/pym/portage/dep/dep_check.py
new file mode 100644
index 000000000..1bc1e40dc
--- /dev/null
+++ b/pym/portage/dep/dep_check.py
@@ -0,0 +1,634 @@
+# Copyright 2010 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# $Id$
+
+__all__ = ['dep_check', 'dep_eval', 'dep_wordreduce', 'dep_zapdeps']
+
+import logging
+
+import portage
+from portage.dep import Atom, dep_opconvert, match_from_list, paren_reduce, \
+ remove_slot, use_reduce
+from portage.exception import InvalidAtom, InvalidDependString, ParseError
+from portage.localization import _
+from portage.util import writemsg, writemsg_level
+from portage.versions import catpkgsplit, cpv_getkey, pkgcmp
+
+def _expand_new_virtuals(mysplit, edebug, mydbapi, mysettings, myroot="/",
+ trees=None, use_mask=None, use_force=None, **kwargs):
+ """
+ In order to solve bug #141118, recursively expand new-style virtuals so
+ as to collapse one or more levels of indirection, generating an expanded
+ search space. In dep_zapdeps, new-style virtuals will be assigned
+ zero cost regardless of whether or not they are currently installed. Virtual
+ blockers are supported but only when the virtual expands to a single
+ atom because it wouldn't necessarily make sense to block all the components
+ of a compound virtual. When more than one new-style virtual is matched,
+ the matches are sorted from highest to lowest versions and the atom is
+ expanded to || ( highest match ... lowest match )."""
+ newsplit = []
+ mytrees = trees[myroot]
+ portdb = mytrees["porttree"].dbapi
+ atom_graph = mytrees.get("atom_graph")
+ parent = mytrees.get("parent")
+ virt_parent = mytrees.get("virt_parent")
+ graph_parent = None
+ eapi = None
+ if parent is not None:
+ if virt_parent is not None:
+ graph_parent = virt_parent
+ eapi = virt_parent[0].metadata['EAPI']
+ else:
+ graph_parent = parent
+ eapi = parent.metadata["EAPI"]
+ repoman = not mysettings.local_config
+ if kwargs["use_binaries"]:
+ portdb = trees[myroot]["bintree"].dbapi
+ myvirtuals = mysettings.getvirtuals()
+ pprovideddict = mysettings.pprovideddict
+ myuse = kwargs["myuse"]
+ for x in mysplit:
+ if x == "||":
+ newsplit.append(x)
+ continue
+ elif isinstance(x, list):
+ newsplit.append(_expand_new_virtuals(x, edebug, mydbapi,
+ mysettings, myroot=myroot, trees=trees, use_mask=use_mask,
+ use_force=use_force, **kwargs))
+ continue
+
+ if not isinstance(x, Atom):
+ try:
+ x = Atom(x)
+ except InvalidAtom:
+ if portage.dep._dep_check_strict:
+ raise ParseError(
+ _("invalid atom: '%s'") % x)
+ else:
+ # Only real Atom instances are allowed past this point.
+ continue
+ else:
+ if x.blocker and x.blocker.overlap.forbid and \
+ eapi in ("0", "1") and portage.dep._dep_check_strict:
+ raise ParseError(
+ _("invalid atom: '%s'") % (x,))
+ if x.use and eapi in ("0", "1") and \
+ portage.dep._dep_check_strict:
+ raise ParseError(
+ _("invalid atom: '%s'") % (x,))
+
+ if repoman and x.use and x.use.conditional:
+ evaluated_atom = remove_slot(x)
+ if x.slot:
+ evaluated_atom += ":%s" % x.slot
+ evaluated_atom += str(x.use._eval_qa_conditionals(
+ use_mask, use_force))
+ x = Atom(evaluated_atom)
+
+ if not repoman and \
+ myuse is not None and isinstance(x, Atom) and x.use:
+ if x.use.conditional:
+ x = x.evaluate_conditionals(myuse)
+
+ mykey = x.cp
+ if not mykey.startswith("virtual/"):
+ newsplit.append(x)
+ if atom_graph is not None:
+ atom_graph.add(x, graph_parent)
+ continue
+ mychoices = myvirtuals.get(mykey, [])
+ if x.blocker:
+ # Virtual blockers are no longer expanded here since
+ # the un-expanded virtual atom is more useful for
+ # maintaining a cache of blocker atoms.
+ newsplit.append(x)
+ if atom_graph is not None:
+ atom_graph.add(x, graph_parent)
+ continue
+
+ if repoman or not hasattr(portdb, 'match_pkgs'):
+ if portdb.cp_list(x.cp):
+ newsplit.append(x)
+ else:
+ # TODO: Add PROVIDE check for repoman.
+ a = []
+ for y in mychoices:
+ a.append(Atom(x.replace(x.cp, y.cp, 1)))
+ if not a:
+ newsplit.append(x)
+ elif len(a) == 1:
+ newsplit.append(a[0])
+ else:
+ newsplit.append(['||'] + a)
+ continue
+
+ pkgs = []
+ # Ignore USE deps here, since otherwise we might not
+ # get any matches. Choices with correct USE settings
+ # will be preferred in dep_zapdeps().
+ matches = portdb.match_pkgs(x.without_use)
+ # Use descending order to prefer higher versions.
+ matches.reverse()
+ for pkg in matches:
+ # only use new-style matches
+ if pkg.cp.startswith("virtual/"):
+ pkgs.append(pkg)
+ if not (pkgs or mychoices):
+ # This one couldn't be expanded as a new-style virtual. Old-style
+ # virtuals have already been expanded by dep_virtual, so this one
+ # is unavailable and dep_zapdeps will identify it as such. The
+ # atom is not eliminated here since it may still represent a
+ # dependency that needs to be satisfied.
+ newsplit.append(x)
+ if atom_graph is not None:
+ atom_graph.add(x, graph_parent)
+ continue
+
+ a = []
+ for pkg in pkgs:
+ virt_atom = '=' + pkg.cpv
+ if x.use:
+ virt_atom += str(x.use)
+ virt_atom = Atom(virt_atom)
+ # According to GLEP 37, RDEPEND is the only dependency
+ # type that is valid for new-style virtuals. Repoman
+ # should enforce this.
+ depstring = pkg.metadata['RDEPEND']
+ pkg_kwargs = kwargs.copy()
+ pkg_kwargs["myuse"] = pkg.use.enabled
+ if edebug:
+ writemsg_level(_("Virtual Parent: %s\n") \
+ % (pkg,), noiselevel=-1, level=logging.DEBUG)
+ writemsg_level(_("Virtual Depstring: %s\n") \
+ % (depstring,), noiselevel=-1, level=logging.DEBUG)
+
+ # Set EAPI used for validation in dep_check() recursion.
+ mytrees["virt_parent"] = (pkg, virt_atom)
+
+ try:
+ mycheck = dep_check(depstring, mydbapi, mysettings,
+ myroot=myroot, trees=trees, **pkg_kwargs)
+ finally:
+ # Restore previous EAPI after recursion.
+ if virt_parent is not None:
+ mytrees["virt_parent"] = virt_parent
+ else:
+ del mytrees["virt_parent"]
+
+ if not mycheck[0]:
+ raise ParseError(
+ "%s: %s '%s'" % (y[0], mycheck[1], depstring))
+
+ # pull in the new-style virtual
+ mycheck[1].append(virt_atom)
+ a.append(mycheck[1])
+ if atom_graph is not None:
+ atom_graph.add(virt_atom, graph_parent)
+ # Plain old-style virtuals. New-style virtuals are preferred.
+ if not pkgs:
+ for y in mychoices:
+ new_atom = Atom(x.replace(x.cp, y.cp, 1))
+ matches = portdb.match(new_atom)
+ # portdb is an instance of depgraph._dep_check_composite_db, so
+ # USE conditionals are already evaluated.
+ if matches and mykey in \
+ portdb.aux_get(matches[-1], ['PROVIDE'])[0].split():
+ a.append(new_atom)
+ if atom_graph is not None:
+ atom_graph.add(new_atom, graph_parent)
+
+ if not a and mychoices:
+ # Check for a virtual package.provided match.
+ for y in mychoices:
+ new_atom = Atom(x.replace(x.cp, y.cp, 1))
+ if match_from_list(new_atom,
+ pprovideddict.get(new_atom.cp, [])):
+ a.append(new_atom)
+ if atom_graph is not None:
+ atom_graph.add(new_atom, graph_parent)
+
+ if not a:
+ newsplit.append(x)
+ if atom_graph is not None:
+ atom_graph.add(x, graph_parent)
+ elif len(a) == 1:
+ newsplit.append(a[0])
+ else:
+ newsplit.append(['||'] + a)
+
+ return newsplit
+
+def dep_eval(deplist):
+ if not deplist:
+ return 1
+ if deplist[0]=="||":
+ #or list; we just need one "1"
+ for x in deplist[1:]:
+ if isinstance(x, list):
+ if dep_eval(x)==1:
+ return 1
+ elif x==1:
+ return 1
+ #XXX: unless there's no available atoms in the list
+ #in which case we need to assume that everything is
+ #okay as some ebuilds are relying on an old bug.
+ if len(deplist) == 1:
+ return 1
+ return 0
+ else:
+ for x in deplist:
+ if isinstance(x, list):
+ if dep_eval(x)==0:
+ return 0
+ elif x==0 or x==2:
+ return 0
+ return 1
+
+def dep_zapdeps(unreduced, reduced, myroot, use_binaries=0, trees=None):
+ """
+ Takes an unreduced and reduced deplist and removes satisfied dependencies.
+ Returned deplist contains steps that must be taken to satisfy dependencies.
+ """
+ if trees is None:
+ trees = portage.db
+ writemsg("ZapDeps -- %s\n" % (use_binaries), 2)
+ if not reduced or unreduced == ["||"] or dep_eval(reduced):
+ return []
+
+ if unreduced[0] != "||":
+ unresolved = []
+ for x, satisfied in zip(unreduced, reduced):
+ if isinstance(x, list):
+ unresolved += dep_zapdeps(x, satisfied, myroot,
+ use_binaries=use_binaries, trees=trees)
+ elif not satisfied:
+ unresolved.append(x)
+ return unresolved
+
+ # We're at a ( || atom ... ) type level and need to make a choice
+ deps = unreduced[1:]
+ satisfieds = reduced[1:]
+
+ # Our preference order is for an the first item that:
+ # a) contains all unmasked packages with the same key as installed packages
+ # b) contains all unmasked packages
+ # c) contains masked installed packages
+ # d) is the first item
+
+ preferred_installed = []
+ preferred_in_graph = []
+ preferred_any_slot = []
+ preferred_non_installed = []
+ unsat_use_in_graph = []
+ unsat_use_installed = []
+ unsat_use_non_installed = []
+ other = []
+
+ # unsat_use_* must come after preferred_non_installed
+ # for correct ordering in cases like || ( foo[a] foo[b] ).
+ choice_bins = (
+ preferred_in_graph,
+ preferred_installed,
+ preferred_any_slot,
+ preferred_non_installed,
+ unsat_use_in_graph,
+ unsat_use_installed,
+ unsat_use_non_installed,
+ other,
+ )
+
+ # Alias the trees we'll be checking availability against
+ parent = trees[myroot].get("parent")
+ priority = trees[myroot].get("priority")
+ graph_db = trees[myroot].get("graph_db")
+ vardb = None
+ if "vartree" in trees[myroot]:
+ vardb = trees[myroot]["vartree"].dbapi
+ if use_binaries:
+ mydbapi = trees[myroot]["bintree"].dbapi
+ else:
+ mydbapi = trees[myroot]["porttree"].dbapi
+
+ # Sort the deps into installed, not installed but already
+ # in the graph and other, not installed and not in the graph
+ # and other, with values of [[required_atom], availablility]
+ for x, satisfied in zip(deps, satisfieds):
+ if isinstance(x, list):
+ atoms = dep_zapdeps(x, satisfied, myroot,
+ use_binaries=use_binaries, trees=trees)
+ else:
+ atoms = [x]
+ if vardb is None:
+ # When called by repoman, we can simply return the first choice
+ # because dep_eval() handles preference selection.
+ return atoms
+
+ all_available = True
+ all_use_satisfied = True
+ slot_map = {}
+ cp_map = {}
+ for atom in atoms:
+ if atom.blocker:
+ continue
+ # Ignore USE dependencies here since we don't want USE
+ # settings to adversely affect || preference evaluation.
+ avail_pkg = mydbapi.match(atom.without_use)
+ if avail_pkg:
+ avail_pkg = avail_pkg[-1] # highest (ascending order)
+ avail_slot = Atom("%s:%s" % (atom.cp,
+ mydbapi.aux_get(avail_pkg, ["SLOT"])[0]))
+ if not avail_pkg:
+ all_available = False
+ all_use_satisfied = False
+ break
+
+ if atom.use:
+ avail_pkg_use = mydbapi.match(atom)
+ if not avail_pkg_use:
+ all_use_satisfied = False
+ else:
+ # highest (ascending order)
+ avail_pkg_use = avail_pkg_use[-1]
+ if avail_pkg_use != avail_pkg:
+ avail_pkg = avail_pkg_use
+ avail_slot = Atom("%s:%s" % (atom.cp,
+ mydbapi.aux_get(avail_pkg, ["SLOT"])[0]))
+
+ slot_map[avail_slot] = avail_pkg
+ pkg_cp = cpv_getkey(avail_pkg)
+ highest_cpv = cp_map.get(pkg_cp)
+ if highest_cpv is None or \
+ pkgcmp(catpkgsplit(avail_pkg)[1:],
+ catpkgsplit(highest_cpv)[1:]) > 0:
+ cp_map[pkg_cp] = avail_pkg
+
+ this_choice = (atoms, slot_map, cp_map, all_available)
+ if all_available:
+ # The "all installed" criterion is not version or slot specific.
+ # If any version of a package is already in the graph then we
+ # assume that it is preferred over other possible packages choices.
+ all_installed = True
+ for atom in set(Atom(atom.cp) for atom in atoms \
+ if not atom.blocker):
+ # New-style virtuals have zero cost to install.
+ if not vardb.match(atom) and not atom.startswith("virtual/"):
+ all_installed = False
+ break
+ all_installed_slots = False
+ if all_installed:
+ all_installed_slots = True
+ for slot_atom in slot_map:
+ # New-style virtuals have zero cost to install.
+ if not vardb.match(slot_atom) and \
+ not slot_atom.startswith("virtual/"):
+ all_installed_slots = False
+ break
+ if graph_db is None:
+ if all_use_satisfied:
+ if all_installed:
+ if all_installed_slots:
+ preferred_installed.append(this_choice)
+ else:
+ preferred_any_slot.append(this_choice)
+ else:
+ preferred_non_installed.append(this_choice)
+ else:
+ if all_installed_slots:
+ unsat_use_installed.append(this_choice)
+ else:
+ unsat_use_non_installed.append(this_choice)
+ else:
+ all_in_graph = True
+ for slot_atom in slot_map:
+ # New-style virtuals have zero cost to install.
+ if not graph_db.match(slot_atom) and \
+ not slot_atom.startswith("virtual/"):
+ all_in_graph = False
+ break
+ circular_atom = None
+ if all_in_graph:
+ if parent is None or priority is None:
+ pass
+ elif priority.buildtime:
+ # Check if the atom would result in a direct circular
+ # dependency and try to avoid that if it seems likely
+ # to be unresolvable. This is only relevant for
+ # buildtime deps that aren't already satisfied by an
+ # installed package.
+ cpv_slot_list = [parent]
+ for atom in atoms:
+ if atom.blocker:
+ continue
+ if vardb.match(atom):
+ # If the atom is satisfied by an installed
+ # version then it's not a circular dep.
+ continue
+ if atom.cp != parent.cp:
+ continue
+ if match_from_list(atom, cpv_slot_list):
+ circular_atom = atom
+ break
+ if circular_atom is not None:
+ other.append(this_choice)
+ else:
+ if all_use_satisfied:
+ if all_in_graph:
+ preferred_in_graph.append(this_choice)
+ elif all_installed:
+ if all_installed_slots:
+ preferred_installed.append(this_choice)
+ else:
+ preferred_any_slot.append(this_choice)
+ else:
+ preferred_non_installed.append(this_choice)
+ else:
+ if all_in_graph:
+ unsat_use_in_graph.append(this_choice)
+ elif all_installed_slots:
+ unsat_use_installed.append(this_choice)
+ else:
+ unsat_use_non_installed.append(this_choice)
+ else:
+ other.append(this_choice)
+
+ # Prefer choices which contain upgrades to higher slots. This helps
+ # for deps such as || ( foo:1 foo:2 ), where we want to prefer the
+ # atom which matches the higher version rather than the atom furthest
+ # to the left. Sorting is done separately for each of choice_bins, so
+ # as not to interfere with the ordering of the bins. Because of the
+ # bin separation, the main function of this code is to allow
+ # --depclean to remove old slots (rather than to pull in new slots).
+ for choices in choice_bins:
+ if len(choices) < 2:
+ continue
+ for choice_1 in choices[1:]:
+ atoms_1, slot_map_1, cp_map_1, all_available_1 = choice_1
+ cps = set(cp_map_1)
+ for choice_2 in choices:
+ if choice_1 is choice_2:
+ # choice_1 will not be promoted, so move on
+ break
+ atoms_2, slot_map_2, cp_map_2, all_available_2 = choice_2
+ intersecting_cps = cps.intersection(cp_map_2)
+ if not intersecting_cps:
+ continue
+ has_upgrade = False
+ has_downgrade = False
+ for cp in intersecting_cps:
+ version_1 = cp_map_1[cp]
+ version_2 = cp_map_2[cp]
+ difference = pkgcmp(catpkgsplit(version_1)[1:],
+ catpkgsplit(version_2)[1:])
+ if difference != 0:
+ if difference > 0:
+ has_upgrade = True
+ else:
+ has_downgrade = True
+ break
+ if has_upgrade and not has_downgrade:
+ # promote choice_1 in front of choice_2
+ choices.remove(choice_1)
+ index_2 = choices.index(choice_2)
+ choices.insert(index_2, choice_1)
+ break
+
+ for allow_masked in (False, True):
+ for choices in choice_bins:
+ for atoms, slot_map, cp_map, all_available in choices:
+ if all_available or allow_masked:
+ return atoms
+
+ assert(False) # This point should not be reachable
+
+def dep_check(depstring, mydbapi, mysettings, use="yes", mode=None, myuse=None,
+ use_cache=1, use_binaries=0, myroot="/", trees=None):
+ """Takes a depend string and parses the condition."""
+ edebug = mysettings.get("PORTAGE_DEBUG", None) == "1"
+ #check_config_instance(mysettings)
+ if trees is None:
+ trees = globals()["db"]
+ if use=="yes":
+ if myuse is None:
+ #default behavior
+ myusesplit = mysettings["PORTAGE_USE"].split()
+ else:
+ myusesplit = myuse
+ # We've been given useflags to use.
+ #print "USE FLAGS PASSED IN."
+ #print myuse
+ #if "bindist" in myusesplit:
+ # print "BINDIST is set!"
+ #else:
+ # print "BINDIST NOT set."
+ else:
+ #we are being run by autouse(), don't consult USE vars yet.
+ # WE ALSO CANNOT USE SETTINGS
+ myusesplit=[]
+
+ #convert parenthesis to sublists
+ try:
+ mysplit = paren_reduce(depstring)
+ except InvalidDependString as e:
+ return [0, str(e)]
+
+ mymasks = set()
+ useforce = set()
+ useforce.add(mysettings["ARCH"])
+ if use == "all":
+ # This masking/forcing is only for repoman. In other cases, relevant
+ # masking/forcing should have already been applied via
+ # config.regenerate(). Also, binary or installed packages may have
+ # been built with flags that are now masked, and it would be
+ # inconsistent to mask them now. Additionally, myuse may consist of
+ # flags from a parent package that is being merged to a $ROOT that is
+ # different from the one that mysettings represents.
+ mymasks.update(mysettings.usemask)
+ mymasks.update(mysettings.archlist())
+ mymasks.discard(mysettings["ARCH"])
+ useforce.update(mysettings.useforce)
+ useforce.difference_update(mymasks)
+ try:
+ mysplit = use_reduce(mysplit, uselist=myusesplit,
+ masklist=mymasks, matchall=(use=="all"), excludeall=useforce)
+ except InvalidDependString as e:
+ return [0, str(e)]
+
+ # Do the || conversions
+ mysplit = dep_opconvert(mysplit)
+
+ if mysplit == []:
+ #dependencies were reduced to nothing
+ return [1,[]]
+
+ # Recursively expand new-style virtuals so as to
+ # collapse one or more levels of indirection.
+ try:
+ mysplit = _expand_new_virtuals(mysplit, edebug, mydbapi, mysettings,
+ use=use, mode=mode, myuse=myuse,
+ use_force=useforce, use_mask=mymasks, use_cache=use_cache,
+ use_binaries=use_binaries, myroot=myroot, trees=trees)
+ except ParseError as e:
+ return [0, str(e)]
+
+ mysplit2=mysplit[:]
+ mysplit2=dep_wordreduce(mysplit2,mysettings,mydbapi,mode,use_cache=use_cache)
+ if mysplit2 is None:
+ return [0, _("Invalid token")]
+
+ writemsg("\n\n\n", 1)
+ writemsg("mysplit: %s\n" % (mysplit), 1)
+ writemsg("mysplit2: %s\n" % (mysplit2), 1)
+
+ try:
+ selected_atoms = dep_zapdeps(mysplit, mysplit2, myroot,
+ use_binaries=use_binaries, trees=trees)
+ except InvalidAtom as e:
+ if portage.dep._dep_check_strict:
+ raise # This shouldn't happen.
+ # dbapi.match() failed due to an invalid atom in
+ # the dependencies of an installed package.
+ return [0, _("Invalid atom: '%s'") % (e,)]
+
+ return [1, selected_atoms]
+
+def dep_wordreduce(mydeplist,mysettings,mydbapi,mode,use_cache=1):
+ "Reduces the deplist to ones and zeros"
+ deplist=mydeplist[:]
+ for mypos, token in enumerate(deplist):
+ if isinstance(deplist[mypos], list):
+ #recurse
+ deplist[mypos]=dep_wordreduce(deplist[mypos],mysettings,mydbapi,mode,use_cache=use_cache)
+ elif deplist[mypos]=="||":
+ pass
+ elif token[:1] == "!":
+ deplist[mypos] = False
+ else:
+ mykey = deplist[mypos].cp
+ if mysettings and mykey in mysettings.pprovideddict and \
+ match_from_list(deplist[mypos], mysettings.pprovideddict[mykey]):
+ deplist[mypos]=True
+ elif mydbapi is None:
+ # Assume nothing is satisfied. This forces dep_zapdeps to
+ # return all of deps the deps that have been selected
+ # (excluding those satisfied by package.provided).
+ deplist[mypos] = False
+ else:
+ if mode:
+ x = mydbapi.xmatch(mode, deplist[mypos])
+ if mode.startswith("minimum-"):
+ mydep = []
+ if x:
+ mydep.append(x)
+ else:
+ mydep = x
+ else:
+ mydep=mydbapi.match(deplist[mypos],use_cache=use_cache)
+ if mydep!=None:
+ tmp=(len(mydep)>=1)
+ if deplist[mypos][0]=="!":
+ tmp=False
+ deplist[mypos]=tmp
+ else:
+ #encountered invalid string
+ return None
+ return deplist