From: Fabian Groffen Date: Sat, 15 Nov 2008 16:17:24 +0000 (-0000) Subject: Merged from trunk -r11841:11845 X-Git-Url: http://git.tremily.us/gitweb.cgi?a=commitdiff_plain;h=985f9f598127ba8bfd7da849b693d32756f9738d;p=portage.git Merged from trunk -r11841:11845 | 11842 | * Use noiselevel=-1 for preserve-libs ">>> needed" and "<<< | | zmedico | !needed" messages so that they're show even without | | | --verbose mode. * Sort files for the ">>> needed" display. | | 11843 | In the LinkageMap.findConsumers() docstring, add a note | | zmedico | about incompatibility with symlinks created by | | | binutils-config. | | 11844 | Revert r11839 since it's not necessarily desired to invert | | zmedico | all uninstall edges. TODO: Invert only the specific edges | | | that the depgraph has decided are necessary and allowed to | | | be inverted. | | 11845 | Fix --jobs parallel scheduling to ensure that temporary | | zmedico | simultaneous installation of conflicting packages is avoided | | | when appropriate (especially for !!atom blockers), but | | | allowed in specific cases that require it. This is | | | accomplished by reversing specific uninstall edges in the | | | digraph, while possibly leaving some edges in there original | | | state. | svn path=/main/branches/prefix/; revision=11941 --- diff --git a/pym/_emerge/__init__.py b/pym/_emerge/__init__.py index 1d07875e5..7ab37e4c9 100644 --- a/pym/_emerge/__init__.py +++ b/pym/_emerge/__init__.py @@ -4327,6 +4327,7 @@ class depgraph(object): # 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 = [] @@ -6185,7 +6186,8 @@ class depgraph(object): 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 @@ -6194,6 +6196,25 @@ class depgraph(object): 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 @@ -6220,6 +6241,7 @@ class depgraph(object): 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 @@ -6639,6 +6661,10 @@ class depgraph(object): 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. @@ -6749,14 +6775,16 @@ class depgraph(object): 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 @@ -9288,34 +9316,8 @@ class Scheduler(PollScheduler): 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. @@ -10309,11 +10311,8 @@ class Scheduler(PollScheduler): 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: @@ -13276,10 +13275,9 @@ def action_build(settings, trees, mtimedb, 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() @@ -13324,10 +13322,9 @@ def action_build(settings, trees, mtimedb, 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() diff --git a/pym/portage/dbapi/vartree.py b/pym/portage/dbapi/vartree.py index cf22d5b7c..98872c34f 100644 --- a/pym/portage/dbapi/vartree.py +++ b/pym/portage/dbapi/vartree.py @@ -624,7 +624,12 @@ class LinkageMap(object): 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 @@ -3093,7 +3098,7 @@ class dblink(object): # 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: @@ -3109,7 +3114,8 @@ class dblink(object): 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): @@ -3249,7 +3255,8 @@ class dblink(object): 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: