# blocker validation is only able to account for one package per slot.
self._slot_collision_nodes = set()
self._serialized_tasks_cache = None
+ self._scheduler_graph = None
self._displayed_list = None
self._pprovided_args = []
self._missing_args = []
while self._serialized_tasks_cache is None:
self._resolve_conflicts()
try:
- self._serialized_tasks_cache = self._serialize_tasks()
+ self._serialized_tasks_cache, self._scheduler_graph = \
+ self._serialize_tasks()
except self._serialize_tasks_retry:
pass
retlist.reverse()
return retlist
+ def schedulerGraph(self):
+ """
+ The scheduler graph is identical to the normal one except that
+ uninstall edges are reversed in specific cases that require
+ conflicting packages to be temporarily installed simultaneously.
+ This is intended for use by the Scheduler in it's parallelization
+ logic. It ensures that temporary simultaneous installation of
+ conflicting packages is avoided when appropriate (especially for
+ !!atom blockers), but allowed in specific cases that require it.
+
+ Note that this method calls break_refs() which alters the state of
+ internal Package instances such that this depgraph instance should
+ not be used to perform any more calculations.
+ """
+ if self._scheduler_graph is None:
+ self.altlist()
+ self.break_refs(self._scheduler_graph.order)
+ return self._scheduler_graph
+
def break_refs(self, nodes):
"""
Take a mergelist like that returned from self.altlist() and
raise self._unknown_internal_error()
def _serialize_tasks(self):
+ scheduler_graph = self.digraph.copy()
mygraph=self.digraph.copy()
# Prune "nomerge" root nodes if nothing depends on them, since
# otherwise they slow down merge order calculation. Don't remove
for blocked_pkg in parent_nodes:
mygraph.add(blocked_pkg, uninst_task,
priority=BlockerDepPriority.instance)
+ scheduler_graph.remove_edge(uninst_task, blocked_pkg)
+ scheduler_graph.add(blocked_pkg, uninst_task,
+ priority=BlockerDepPriority.instance)
+
else:
# None of the Uninstall tasks are acceptable, so
# the corresponding blockers are unresolvable.
not self._accept_blocker_conflicts():
self._unsatisfied_blockers_for_display = unsolvable_blockers
self._serialized_tasks_cache = retlist[:]
+ self._scheduler_graph = scheduler_graph
raise self._unknown_internal_error()
if self._slot_collision_info and \
not self._accept_blocker_conflicts():
self._serialized_tasks_cache = retlist[:]
+ self._scheduler_graph = scheduler_graph
raise self._unknown_internal_error()
- return retlist
+ return retlist, scheduler_graph
def _show_circular_deps(self, mygraph):
# No leaf nodes are available, so we have a circular
return
self._digraph = digraph
- self._reverse_uninstall_edges()
self._prune_digraph()
- def _reverse_uninstall_edges(self):
- """
- The uninstall is performed only after blocking packages have been
- merged on top of it (similar to how a normal upgrade is performed
- by first merging the new version on top of the old version). This
- is implemented by reversing the parent -> uninstall edges in
- the graph.
- """
-
- graph = self._digraph
-
- # Iterate over all nodes rather than just the merge list, because
- # some uninstall nodes may not be in the merge list since they will
- # be performed as part of an upgrade within a slot.
- for node in graph.all_nodes():
- if not isinstance(node, Package) or \
- node.operation != "uninstall":
- continue
-
- parent_nodes = graph.parent_nodes(node)
- graph.remove(node)
- for blocked_pkg in parent_nodes:
- graph.add(blocked_pkg, node,
- priority=BlockerDepPriority.instance)
-
def _prune_digraph(self):
"""
Prune any root nodes that are irrelevant.
mylist = mydepgraph.altlist()
mydepgraph.break_refs(mylist)
- mydepgraph.break_refs(dropped_tasks)
- mydepgraph.break_refs(mydepgraph.digraph.order)
-
self._mergelist = mylist
- self._set_digraph(mydepgraph.digraph)
+ self._set_digraph(mydepgraph.schedulerGraph())
msg_width = 75
for task in dropped_tasks:
time.sleep(3) # allow the parent to have first fetch
mymergelist = mydepgraph.altlist()
mydepgraph.break_refs(mymergelist)
- mydepgraph.break_refs(mydepgraph.digraph.order)
mergetask = Scheduler(settings, trees, mtimedb, myopts,
- spinner, mymergelist, favorites, mydepgraph.digraph)
- del mydepgraph
+ spinner, mymergelist, favorites, mydepgraph.schedulerGraph())
+ del mydepgraph, mymergelist
clear_caches(trees)
retval = mergetask.merge()
pkglist = mydepgraph.altlist()
mydepgraph.saveNomergeFavorites()
mydepgraph.break_refs(pkglist)
- mydepgraph.break_refs(mydepgraph.digraph.order)
mergetask = Scheduler(settings, trees, mtimedb, myopts,
- spinner, pkglist, favorites, mydepgraph.digraph)
- del mydepgraph
+ spinner, pkglist, favorites, mydepgraph.schedulerGraph())
+ del mydepgraph, pkglist
clear_caches(trees)
retval = mergetask.merge()
In some cases, not all consumers are returned. This may occur when
an soname symlink referencing a library is in an object's runpath while
- the actual library is not.
+ the actual library is not. For example, this problem is noticeable for
+ binutils since it's libraries are added to the path via symlinks that
+ are gemerated in the /usr/$CHOST/lib/ directory by binutils-config.
+ Failure to recognize consumers of these symlinks makes preserve-libs
+ fail to preserve binutils libs that are needed by these unrecognized
+ consumers.
@param obj: absolute path to an object or a key from _obj_properties
@type obj: string (example: '/usr/bin/bar') or _ObjectKey
# Copy contents entries from the old package to the new one.
new_contents = self.getcontents().copy()
old_contents = self._installed_instance.getcontents()
- for f in list(preserve_paths):
+ for f in sorted(preserve_paths):
f_abs = os.path.join(root, f.lstrip(os.sep))
contents_entry = old_contents.get(f_abs)
if contents_entry is None:
continue
new_contents[f_abs] = contents_entry
obj_type = contents_entry[0]
- showMessage(">>> needed %s %s\n" % (obj_type, f_abs))
+ showMessage(">>> needed %s %s\n" % (obj_type, f_abs),
+ noiselevel=-1)
# Add parent directories to contents if necessary.
parent_dir = os.path.dirname(f_abs)
while len(parent_dir) > len(root):
raise
del e
else:
- showMessage("<<< !needed %s %s\n" % (obj_type, obj))
+ showMessage("<<< !needed %s %s\n" % (obj_type, obj),
+ noiselevel=-1)
# Remove empty parent directories if possible.
while parent_dirs: