# have already been made.
self._graph_trees[myroot]["porttree"] = graph_tree
self._graph_trees[myroot]["vartree"] = graph_tree
- self._filtered_trees[myroot]["vartree"] = graph_tree
def filtered_tree():
pass
filtered_tree.dbapi = self._dep_check_composite_db(self, myroot)
self._filtered_trees[myroot]["porttree"] = filtered_tree
+
+ # Passing in graph_tree as the vartree here could lead to better
+ # atom selections in some cases by causing atoms for packages that
+ # have been added to the graph to be preferred over other choices.
+ # However, it can trigger atom selections that result in
+ # unresolvable direct circular dependencies. For example, this
+ # happens with gwydion-dylan which depends on either itself or
+ # gwydion-dylan-bin. In case gwydion-dylan is not yet installed,
+ # gwydion-dylan-bin needs to be selected in order to avoid a
+ # an unresolvable direct circular dependency.
+ #
+ # To solve the problem described above, pass in "graph_db" so that
+ # packages that have been added to the graph are distinguishable
+ # from other available packages and installed packages. Also, pass
+ # the parent package into self._select_atoms() calls so that
+ # unresolvable direct circular dependencies can be detected and
+ # avoided when possible.
+ self._filtered_trees[myroot]["graph_db"] = graph_tree.dbapi
+ self._filtered_trees[myroot]["vartree"] = self.trees[myroot]["vartree"]
+
dbs = []
portdb = self.trees[myroot]["porttree"].dbapi
bindb = self.trees[myroot]["bintree"].dbapi
vardb = self.roots[dep_root].trees["vartree"].dbapi
try:
selected_atoms = self._select_atoms(dep_root,
- dep_string, myuse=myuse, strict=strict)
+ dep_string, myuse=myuse, parent=pkg, strict=strict)
except portage.exception.InvalidDependString, e:
show_invalid_depstring_notice(jbigkey, dep_string, str(e))
return 0
pkg = Package(type_name="binary", root=myroot,
cpv=mykey, built=True, metadata=metadata,
onlydeps=onlydeps)
+ self._pkg_cache[pkg] = pkg
args.append(PackageArg(arg=x, package=pkg,
root_config=root_config))
elif ext==".ebuild":
metadata["USE"] = pkgsettings["PORTAGE_USE"]
pkg = Package(type_name="ebuild", root=myroot,
cpv=mykey, metadata=metadata, onlydeps=onlydeps)
+ self._pkg_cache[pkg] = pkg
args.append(PackageArg(arg=x, package=pkg,
root_config=root_config))
elif x.startswith(os.path.sep):
return self._select_atoms_highest_available(*pargs, **kwargs)
def _select_atoms_highest_available(self, root, depstring,
- myuse=None, strict=True, trees=None):
+ myuse=None, parent=None, strict=True, trees=None):
"""This will raise InvalidDependString if necessary. If trees is
None then self._filtered_trees is used."""
pkgsettings = self.pkgsettings[root]
trees = self._filtered_trees
if True:
try:
+ if parent is not None:
+ trees[root]["parent"] = parent
if not strict:
portage.dep._dep_check_strict = False
mycheck = portage.dep_check(depstring, None,
pkgsettings, myuse=myuse,
myroot=root, trees=trees)
finally:
+ if parent is not None:
+ trees[root].pop("parent")
portage.dep._dep_check_strict = True
if not mycheck[0]:
raise portage.exception.InvalidDependString(mycheck[1])
self.usemask.discard("test")
# Use the calculated USE flags to regenerate the USE_EXPAND flags so
- # that they are consistent.
+ # that they are consistent. For optimal performance, use slice
+ # comparison instead of startswith().
use_expand = self.get("USE_EXPAND", "").split()
for var in use_expand:
prefix = var.lower() + "_"
prefix_len = len(prefix)
expand_flags = set([ x[prefix_len:] for x in use \
- if x.startswith(prefix) ])
+ if x[:prefix_len] == prefix ])
var_split = self.get(var, "").split()
# Preserve the order of var_split because it can matter for things
# like LINGUAS.
var_split = [ x for x in var_split if x != "*" ]
has_iuse = set()
for x in iuse_implicit:
- if x.startswith(prefix):
+ if x[:prefix_len] == prefix:
has_iuse.add(x[prefix_len:])
if has_wildcard:
# * means to enable everything in IUSE that's not masked
if has_iuse:
for x in iuse_implicit:
- if x.startswith(prefix) and x not in self.usemask:
+ if x[:prefix_len] == prefix and x not in self.usemask:
suffix = x[prefix_len:]
var_split.append(suffix)
use.add(x)
self.uvlist.append(self.configdict[x])
self.uvlist.reverse()
+ # For optimal performance, use slice
+ # comparison instead of startswith().
myflags = set()
for curdb in self.uvlist:
cur_use_expand = [x for x in use_expand if x in curdb]
is_not_incremental = var not in myincrementals
if is_not_incremental:
prefix = var_lower + "_"
+ prefix_len = len(prefix)
for x in list(myflags):
- if x.startswith(prefix):
+ if x[:prefix_len] == prefix:
myflags.remove(x)
for x in curdb[var].split():
if x[0] == "+":
other = []
# Alias the trees we'll be checking availability against
+ parent = trees[myroot].get("parent")
+ graph_db = trees[myroot].get("graph_db")
vardb = None
if "vartree" in trees[myroot]:
vardb = trees[myroot]["vartree"].dbapi
preferred.append(this_choice)
else:
preferred_any_slot.append(this_choice)
- else:
+ elif graph_db is None:
possible_upgrades.append(this_choice)
+ else:
+ all_in_graph = True
+ for slot_atom in versions:
+ # 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
+ if all_in_graph:
+ if parent is None:
+ preferred.append(this_choice)
+ else:
+ # Check if the atom would result in a direct circular
+ # dependency and try to avoid that if it seems likely
+ # to be unresolvable.
+ cpv_slot_list = [parent.cpv_slot]
+ circular_atom = None
+ for atom in atoms:
+ if "!" == atom[:1]:
+ continue
+ if vardb.match(atom):
+ # If the atom is satisfied by an installed
+ # version then it's not a circular dep.
+ continue
+ if dep_getkey(atom) != parent.cp:
+ continue
+ if match_from_list(atom, cpv_slot_list):
+ circular_atom = atom
+ break
+ if circular_atom is None:
+ preferred.append(this_choice)
+ else:
+ other.append(this_choice)
+ else:
+ possible_upgrades.append(this_choice)
else:
other.append(this_choice)