Merged from trunk -r11841:11845
authorFabian Groffen <grobian@gentoo.org>
Sat, 15 Nov 2008 16:17:24 +0000 (16:17 -0000)
committerFabian Groffen <grobian@gentoo.org>
Sat, 15 Nov 2008 16:17:24 +0000 (16:17 -0000)
   | 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

pym/_emerge/__init__.py
pym/portage/dbapi/vartree.py

index 1d07875e5a35e9f7d9a523dc0d644a56c8a52ac2..7ab37e4c98842c4cb0996c22409cebe08d7229b9 100644 (file)
@@ -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()
index cf22d5b7c4aef9ce6c82e8662a084b61106f420c..98872c34f53b1b50231417157c24e06d2b3e2d17 100644 (file)
@@ -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: