user doesn't necessarily need write access to the vardb in cases where
global updates are necessary (updates are performed when necessary if there
is not a matching ebuild in the tree)."""
- def __init__(self, real_vartree, portdb):
+ def __init__(self, real_vartree, portdb, db_keys):
self.root = real_vartree.root
self.settings = real_vartree.settings
+ mykeys = db_keys[:]
+ for required_key in ("COUNTER", "SLOT"):
+ if required_key not in mykeys:
+ mykeys.append(required_key)
self.dbapi = portage.fakedbapi(settings=real_vartree.settings)
vdb_path = os.path.join(self.root, portage.VDB_PATH)
try:
try:
if os.access(vdb_path, os.W_OK):
vdb_lock = portage.locks.lockdir(vdb_path)
- mykeys = ["SLOT", "COUNTER", "PROVIDE", "USE", "IUSE",
- "RESTRICT", "DEPEND", "RDEPEND", "PDEPEND", "repository"]
real_dbapi = real_vartree.dbapi
slot_counters = {}
for cpv in real_dbapi.cpv_all():
if updates:
mydb.aux_update(mycpv, updates)
+def cpv_sort_descending(cpv_list):
+ """Sort in place, returns None."""
+ if len(cpv_list) <= 1:
+ return
+ first_split = portage.catpkgsplit(cpv_list[0])
+ cat = first_split[0]
+ cpv_list[0] = first_split[1:]
+ for i in xrange(1, len(cpv_list)):
+ cpv_list[i] = portage.catpkgsplit(cpv_list[i])[1:]
+ cpv_list.sort(portage.pkgcmp, reverse=True)
+ for i, (pn, ver, rev) in enumerate(cpv_list):
+ if rev == "r0":
+ cpv = cat + "/" + pn + "-" + ver
+ else:
+ cpv = cat + "/" + pn + "-" + ver + "-" + rev
+ cpv_list[i] = cpv
+
+def visible(pkgsettings, cpv, metadata, built=False, installed=False):
+ """
+ Check if a package is visible. This can raise an InvalidDependString
+ exception if LICENSE is invalid.
+ TODO: optionally generate a list of masking reasons
+ @rtype: Boolean
+ @returns: True if the package is visible, False otherwise.
+ """
+ if built and not installed and \
+ metadata["CHOST"] != pkgsettings["CHOST"]:
+ return False
+ if not portage.eapi_is_supported(metadata["EAPI"]):
+ return False
+ if pkgsettings.getMissingKeywords(cpv, metadata):
+ return False
+ if pkgsettings.getMaskAtom(cpv, metadata):
+ return False
+ if pkgsettings.getProfileMaskAtom(cpv, metadata):
+ return False
+ if pkgsettings.getMissingLicenses(cpv, metadata):
+ return False
+ return True
+
+def iter_atoms(deps):
+ """Take a dependency structure as returned by paren_reduce or use_reduce
+ and iterate over all the atoms."""
+ i = iter(deps)
+ for x in i:
+ if isinstance(x, basestring):
+ if x == '||' or x.endswith('?'):
+ for x in iter_atoms(i.next()):
+ yield x
+ else:
+ yield x
+ else:
+ for x in iter_atoms(x):
+ yield x
+
class BlockerCache(DictMixin):
"""This caches blockers of installed packages so that dep_check does not
have to be done for every single installed package on every invocation of
"binary":"bintree",
"installed":"vartree"}
+ _mydbapi_keys = [
+ "CHOST", "DEPEND", "EAPI", "IUSE", "KEYWORDS",
+ "LICENSE", "PDEPEND", "PROVIDE", "RDEPEND",
+ "repository", "RESTRICT", "SLOT", "USE"]
+
+ _dep_keys = ["DEPEND", "RDEPEND", "PDEPEND"]
+
def __init__(self, settings, trees, myopts, myparams, spinner):
self.settings = settings
self.target_root = settings["ROOT"]
# Maps nodes to the reasons they were selected for reinstallation.
self._reinstall_nodes = {}
self.mydbapi = {}
- self._mydbapi_keys = ["SLOT", "DEPEND", "RDEPEND", "PDEPEND",
- "USE", "IUSE", "PROVIDE", "RESTRICT", "repository"]
self.trees = {}
self.roots = {}
+ # Contains a filtered view of preferred packages that are selected
+ # from available repositories.
+ self._filtered_trees = {}
for myroot in trees:
self.trees[myroot] = {}
for tree in ("porttree", "bintree"):
self.trees[myroot][tree] = trees[myroot][tree]
self.trees[myroot]["vartree"] = \
FakeVartree(trees[myroot]["vartree"],
- trees[myroot]["porttree"].dbapi)
+ trees[myroot]["porttree"].dbapi,
+ self._mydbapi_keys)
self.pkgsettings[myroot] = portage.config(
clone=self.trees[myroot]["vartree"].settings)
self.pkg_node_map[myroot] = {}
metadata=dict(izip(self._mydbapi_keys,
vardb.aux_get(pkg, self._mydbapi_keys))))
del vardb, fakedb
+ self._filtered_trees[myroot] = {}
+ self._filtered_trees[myroot]["vartree"] = self.trees[myroot]["vartree"]
+ def filtered_tree():
+ pass
+ filtered_tree.dbapi = portage.fakedbapi(
+ settings=self.pkgsettings[myroot], exclusive_slots=False)
+ self._filtered_trees[myroot]["porttree"] = filtered_tree
+ self._filtered_trees[myroot]["atoms"] = set()
if "--usepkg" in self.myopts:
self.trees[myroot]["bintree"].populate(
"--getbinpkg" in self.myopts,
# We're true here unless we are missing binaries.
return (not missing,myfavorites)
+ def _populate_filtered_repo(self, myroot, depstring,
+ myparent=None, myuse=None, exclude_installed=False):
+ """Extract all of the atoms from the depstring, select preferred
+ packages from appropriate repositories, and use them to populate
+ the filtered repository."""
+
+ filtered_db = self._filtered_trees[myroot]["porttree"].dbapi
+ pkgsettings = self.pkgsettings[myroot]
+ if myparent:
+ p_type, p_root, p_key, p_status = myparent
+
+ from portage.dep import paren_reduce, use_reduce
+ try:
+ if myparent and p_type == "installed":
+ portage.dep._dep_check_strict = False
+ try:
+ atoms = paren_reduce(depstring)
+ atoms = use_reduce(atoms, uselist=myuse)
+ atoms = list(iter_atoms(atoms))
+ for x in atoms:
+ if portage.dep._dep_check_strict and \
+ not portage.isvalidatom(x, allow_blockers=True):
+ raise portage.exception.InvalidDependString(
+ "Invalid atom: %s" % x)
+ except portage.exception.InvalidDependString, e:
+ if myparent:
+ show_invalid_depstring_notice(
+ myparent, depstring, str(e))
+ else:
+ sys.stderr.write("\n%s\n%s\n" % (depstring, str(e)))
+ return 0
+ finally:
+ portage.dep._dep_check_strict = True
+
+ filtered_atoms = self._filtered_trees[myroot]["atoms"]
+ dbs = self._filtered_trees[myroot].get("dbs")
+ if dbs is None:
+ dbs = []
+ portdb = self.trees[myroot]["porttree"].dbapi
+ bindb = self.trees[myroot]["bintree"].dbapi
+ vardb = self.trees[myroot]["vartree"].dbapi
+ # (db, pkg_type, built, installed, db_keys)
+ if "--usepkgonly" not in self.myopts:
+ db_keys = list(portdb._aux_cache_keys)
+ dbs.append((portdb, "ebuild", False, False, db_keys))
+ if "--usepkg" in self.myopts:
+ db_keys = list(bindb._aux_cache_keys)
+ dbs.append((bindb, "binary", True, False, db_keys))
+ if "--usepkgonly" in self.myopts:
+ db_keys = self._mydbapi_keys
+ dbs.append((vardb, "installed", True, True, db_keys))
+ self._filtered_trees[myroot]["dbs"] = dbs
+ old_virts = pkgsettings.getvirtuals()
+ while atoms:
+ x = atoms.pop()
+ if x.startswith("!"):
+ continue
+ if x in filtered_atoms:
+ continue
+ filtered_atoms.add(x)
+ cp = portage.dep_getkey(x)
+ cat = portage.catsplit(cp)[0]
+ slot = portage.dep.dep_getslot(x)
+ is_virt = cp.startswith("virtual/")
+ atom_populated = False
+ for db, pkg_type, built, installed, db_keys in dbs:
+ if installed and exclude_installed:
+ continue
+ cpv_list = db.cp_list(cp)
+ if not cpv_list:
+ if is_virt:
+ # old-style virtual
+ # Create a transformed atom for each choice
+ # and add it to the stack for processing.
+ for choice in old_virts.get(cp, []):
+ atoms.append(x.replace(cp, choice))
+ # Maybe a new-style virtual exists in another db, so
+ # we have to try all of them to prevent the old-style
+ # virtuals from overriding available new-styles.
+ continue
+ cpv_sort_descending(cpv_list)
+ for cpv in cpv_list:
+ if filtered_db.cpv_exists(cpv):
+ continue
+ if not portage.match_from_list(x, [cpv]):
+ continue
+ if is_virt:
+ mykeys = db_keys[:]
+ mykeys.extend(self._dep_keys)
+ else:
+ mykeys = db_keys
+ try:
+ metadata = dict(izip(mykeys,
+ db.aux_get(cpv, mykeys)))
+ except KeyError:
+ # masked by corruption
+ continue
+ if slot is not None:
+ if slot != metadata["SLOT"]:
+ continue
+ if not built and \
+ (is_virt or "?" in metadata["LICENSE"]):
+ pkgsettings.setcpv(cpv, mydb=metadata)
+ metadata["USE"] = pkgsettings["USE"]
+ else:
+ metadata["USE"] = ""
+
+ try:
+ if not visible(pkgsettings, cpv, metadata,
+ built=built, installed=installed):
+ continue
+ except portage.exception.InvalidDependString:
+ # masked by corruption
+ continue
+
+ filtered_db.cpv_inject(cpv, metadata=metadata)
+ if not is_virt:
+ # break here since we only want the best version
+ # for now (eventually will be configurable).
+ atom_populated = True
+ break
+ # For new-style virtuals, we explore all available
+ # versions and recurse on their deps. This is a
+ # preparation for the lookahead that happens when
+ # new-style virtuals are expanded by dep_check().
+ virtual_deps = " ".join(metadata[k] \
+ for k in self._dep_keys)
+ try:
+ if installed:
+ portage.dep._dep_check_strict = False
+ try:
+ deps = paren_reduce(virtual_deps)
+ deps = use_reduce(deps,
+ uselist=metadata["USE"].split())
+ for y in iter_atoms(deps):
+ if portage.dep._dep_check_strict and \
+ not portage.isvalidatom(y,
+ allow_blockers=True):
+ raise portage.exception.InvalidDependString(
+ "Invalid atom: %s" % y)
+ atoms.append(y)
+ except portage.exception.InvalidDependString, e:
+ show_invalid_depstring_notice(
+ (pkg_type, myroot, cpv, "nomerge"),
+ virtual_deps, str(e))
+ return 0
+ finally:
+ portage.dep._dep_check_strict = True
+ if atom_populated:
+ break
+ return 1
+
def select_dep(self, myroot, depstring, myparent=None, arg=None,
myuse=None, raise_on_missing=False, priority=DepPriority(),
rev_deps=False, parent_arg=None):
return 1 on success, 0 for failure
"""
+ if not depstring:
+ return 1 # nothing to do
+
+ filtered_db = self._filtered_trees[myroot]["porttree"].dbapi
portdb = self.trees[myroot]["porttree"].dbapi
bindb = self.trees[myroot]["bintree"].dbapi
vardb = self.trees[myroot]["vartree"].dbapi
print "Reverse:", rev_deps
print "Priority:", priority
+ if not self._populate_filtered_repo(
+ myroot, depstring, myparent=myparent, myuse=myuse):
+ return 0
+
#processing dependencies
""" Call portage.dep_check to evaluate the use? conditionals and make sure all
dependencies are satisfiable. """
mymerge = []
else:
try:
- if myparent and p_status == "nomerge":
+ if myparent and p_type == "installed":
portage.dep._dep_check_strict = False
mycheck = portage.dep_check(depstring, None,
pkgsettings, myuse=myuse,
- use_binaries=("--usepkgonly" in self.myopts),
- myroot=myroot, trees=self.trees)
+ myroot=myroot, trees=self._filtered_trees)
finally:
portage.dep._dep_check_strict = True
else:
# List of acceptable packages, ordered by type preference.
matched_packages = []
- myeb_matches = portdb.xmatch("match-visible", x)
+ slot = portage.dep.dep_getslot(x)
+ cpv_list = portdb.xmatch("match-all", x)
+ cpv_sort_descending(cpv_list)
myeb = None
myeb_pkg = None
metadata = None
existing_node = None
- if myeb_matches:
- myeb = portage.best(myeb_matches)
+ db_keys = list(portdb._aux_cache_keys)
+ for cpv in cpv_list:
+ try:
+ metadata = dict(izip(db_keys,
+ portdb.aux_get(cpv, db_keys)))
+ except KeyError:
+ # masked by corruption
+ continue
+ try:
+ if not visible(pkgsettings, cpv, metadata,
+ built=False, installed=False):
+ continue
+ except InvalidDependString:
+ # masked by corruption
+ continue
+
+ myeb = cpv
# For best performance, try to reuse an exising node
# and it's cached metadata. The portdbapi caches SLOT
# metadata in memory so it's really only pulled once.
- slot_atom = "%s:%s" % (portage.dep_getkey(myeb),
- portdb.aux_get(myeb, ["SLOT"])[0])
+ slot_atom = "%s:%s" % (portage.cpv_getkey(myeb),
+ metadata["SLOT"])
existing_node = self._slot_node_map[myroot].get(slot_atom)
if existing_node:
e_type, myroot, e_cpv, e_status = existing_node
([e_type, myroot, e_cpv], metadata))
else:
existing_node = None
+ break
if not existing_node and \
"--usepkg" in self.myopts:
# The next line assumes the binarytree has been populated.
# XXX: Need to work out how we use the binary tree with roots.
usepkgonly = "--usepkgonly" in self.myopts
- chost = pkgsettings["CHOST"]
myeb_pkg_matches = []
- bindb_keys = ["CHOST","EAPI"]
- for pkg in bindb.match(x):
- metadata = dict(izip(bindb_keys,
- bindb.aux_get(pkg, bindb_keys)))
- if chost != metadata["CHOST"]:
+ cpv_list = bindb.match(x)
+ cpv_sort_descending(cpv_list)
+ db_keys = list(bindb._aux_cache_keys)
+ for pkg in cpv_list:
+ if not filtered_db.cpv_exists(pkg):
continue
- if not portage.eapi_is_supported(metadata["EAPI"]):
- continue
- # Remove any binary package entries that are
- # masked in the portage tree (#55871).
- if not usepkgonly and \
- not (pkg in myeb_matches or \
- not portdb.cpv_exists(pkg)):
+ metadata = dict(izip(db_keys,
+ bindb.aux_get(pkg, db_keys)))
+ try:
+ if not visible(pkgsettings, pkg, metadata,
+ built=True, installed=False):
+ continue
+ except InvalidDependString:
+ # masked by corruption
continue
myeb_pkg_matches.append(pkg)
+ break
if myeb_pkg_matches:
myeb_pkg = portage.best(myeb_pkg_matches)
# For best performance, try to reuse an exising node
"""Fall back to the installed package database. This is a
last resort because the metadata tends to diverge from that
of the ebuild in the tree."""
- myeb_inst_matches = vardb.match(x)
- if "--usepkgonly" not in self.myopts:
- """ TODO: Improve masking check for installed and
- binary packages. bug #149816"""
- myeb_inst_matches = [pkg for pkg in myeb_inst_matches \
- if not portdb.cpv_exists(pkg)]
- myeb_inst = None
- if myeb_inst_matches:
- myeb_inst = portage.best(myeb_inst_matches)
- if myeb_inst:
+ cpv_list = vardb.match(x)
+ cpv_sort_descending(cpv_list)
+ for cpv in cpv_list:
metadata = dict(izip(self._mydbapi_keys,
- vardb.aux_get(myeb_inst, self._mydbapi_keys)))
+ vardb.aux_get(cpv, self._mydbapi_keys)))
+ # TODO: Handle masking for installed packages.
+ #try:
+ # if not visible(pkgsettings, cpv, metadata,
+ # built=True, installed=True):
+ # continue
+ #except: InvalidDependString:
+ # # masked by corruption
+ # continue
matched_packages.append(
- (["installed", myroot, myeb_inst], metadata))
+ (["installed", myroot, cpv], metadata))
+ break
if not matched_packages:
if raise_on_missing:
def xcreate(self,mode="system"):
vardb = self.trees[self.target_root]["vartree"].dbapi
- portdb = self.trees[self.target_root]["porttree"].dbapi
- bindb = self.trees[self.target_root]["bintree"].dbapi
- def visible(mylist):
- matches = portdb.gvisible(portdb.visible(mylist))
- return [x for x in mylist \
- if x in matches or not portdb.cpv_exists(x)]
+ filtered_db = self._filtered_trees[self.target_root]["porttree"].dbapi
world_problems = False
root_config = self.roots[self.target_root]
continue
elif not vardb.match(x):
world_problems = True
- available = False
- if "--usepkgonly" not in self.myopts and \
- portdb.match(x):
- available = True
- elif "--usepkg" in self.myopts:
- mymatches = bindb.match(x)
- if "--usepkgonly" not in self.myopts:
- mymatches = visible(mymatches)
- if mymatches:
- available = True
- if not available:
+ if not self._populate_filtered_repo(self.target_root, x,
+ exclude_installed=True):
+ return 0
+ if not filtered_db.match(x):
continue
mylist.append(x)
for cpv in vardb.match(mykey):
myslots.add(vardb.aux_get(cpv, ["SLOT"])[0])
if myslots:
- best_pkgs = []
- if "--usepkg" in self.myopts:
- mymatches = bindb.match(atom)
- if "--usepkgonly" not in self.myopts:
- mymatches = visible(mymatches)
- best_pkg = portage.best(mymatches)
- if best_pkg:
- best_slot = bindb.aux_get(best_pkg, ["SLOT"])[0]
- best_pkgs.append(("binary", best_pkg, best_slot))
- if "--usepkgonly" not in self.myopts:
- best_pkg = portage.best(portdb.match(atom))
- if best_pkg:
- best_slot = portdb.aux_get(best_pkg, ["SLOT"])[0]
- best_pkgs.append(("ebuild", best_pkg, best_slot))
- if best_pkgs:
- best_pkg = portage.best([x[1] for x in best_pkgs])
- best_pkgs = [x for x in best_pkgs if x[1] == best_pkg]
- best_slot = best_pkgs[0][2]
+ if not self._populate_filtered_repo(self.target_root, atom,
+ exclude_installed=True):
+ return 0
+ mymatches = filtered_db.match(atom)
+ best_pkg = portage.best(mymatches)
+ if best_pkg:
+ best_slot = filtered_db.aux_get(best_pkg, ["SLOT"])[0]
myslots.add(best_slot)
if len(myslots) > 1:
for myslot in myslots:
myslot_atom = "%s:%s" % (mykey, myslot)
- available = False
- if "--usepkgonly" not in self.myopts and \
- self.trees[self.target_root][
- "porttree"].dbapi.match(myslot_atom):
- available = True
- elif "--usepkg" in self.myopts:
- mymatches = bindb.match(myslot_atom)
- if "--usepkgonly" not in self.myopts:
- mymatches = visible(mymatches)
- if mymatches:
- available = True
- if available:
+ if not self._populate_filtered_repo(
+ self.target_root, myslot_atom,
+ exclude_installed=True):
+ return 0
+ if filtered_db.match(myslot_atom):
newlist.append(myslot_atom)
mylist = newlist
dep_check_trees = {}
dep_check_trees[myroot] = {}
dep_check_trees[myroot]["vartree"] = \
- FakeVartree(trees[myroot]["vartree"], trees[myroot]["porttree"].dbapi)
+ FakeVartree(trees[myroot]["vartree"],
+ trees[myroot]["porttree"].dbapi,
+ depgraph._mydbapi_keys)
vardb = dep_check_trees[myroot]["vartree"].dbapi
# Constrain dependency selection to the installed packages.
dep_check_trees[myroot]["porttree"] = dep_check_trees[myroot]["vartree"]