Merged from trunk -r12117:12126
authorFabian Groffen <grobian@gentoo.org>
Mon, 1 Dec 2008 21:14:48 +0000 (21:14 -0000)
committerFabian Groffen <grobian@gentoo.org>
Mon, 1 Dec 2008 21:14:48 +0000 (21:14 -0000)
   | 12120   | Add 'automatically resolved' blockers to the --pretend docs. |
   | zmedico |                                                              |

   | 12122   | Add a sanity check inside depgraph._add_pkg() to ensure that |
   | zmedico | the dependencies of the same package are never processed     |
   |         | more than once.                                              |

   | 12124   | Change depgraph._slot_collision_info() from a set to a dict  |
   | zmedico | that contains sets of packages pulled into a given slot.     |
   |         | This will make the data easier to analyze when implementing  |
   |         | a fix for bug #249185.                                       |

   | 12125   | Bug #249185 - For more useful output in cases when one or    |
   | zmedico | more USE deps trigger "SLOT conflicts", show the specific    |
   |         | atoms that triggered the conflict. TODO: Distiguish between  |
   |         | various possible causes and tailor messages to suit them.    |

   | 12126   | Fix inconsistencies between the "clean" and "cleanrm"        |
   | zmedico | phases.                                                      |

svn path=/main/branches/prefix/; revision=12133

bin/ebuild.sh
man/emerge.1
pym/_emerge/__init__.py

index d614d3b8ee41f147696c207d0c5c4bf30aea18ab..c6e3cc365def1decb6e1c50b7c546585447f8f93 100755 (executable)
@@ -1810,7 +1810,7 @@ if [[ -n ${QA_INTERCEPTORS} ]] ; then
        unset BIN_PATH BIN BODY FUNC_SRC
 fi
 
-if ! hasq ${EBUILD_PHASE} clean depend && \
+if ! hasq "$EBUILD_PHASE" clean cleanrm depend && \
        [ -f "${T}"/environment ] ; then
        # The environment may have been extracted from environment.bz2 or
        # may have come from another version of ebuild.sh or something.
@@ -1856,7 +1856,7 @@ if ! hasq ${EBUILD_PHASE} clean depend && \
        source_all_bashrcs
 fi
 
-if ! hasq ${EBUILD_PHASE} clean && \
+if ! hasq "$EBUILD_PHASE" clean cleanrm && \
        (
                hasq ${EBUILD_PHASE} depend || \
                [ ! -f "${T}"/environment ] || \
@@ -1903,7 +1903,7 @@ fi
 [[ -n ${EAPI/prefix/} ]] || EAPI="${EAPI}${EAPI:+ }0"
 
 # enable bashrc support for the clean phase
-[[ ${EBUILD_PHASE} == clean ]] && source_all_bashrcs
+hasq "$EBUILD_PHASE" clean cleanrm && source_all_bashrcs
 
 # unset USE_EXPAND variables that contain only the special "*" token
 for x in ${USE_EXPAND} ; do
index 47df88c484957f8f471e380848bd6f1dfa5ea6c1..391ec0395da9f702e061c2e753e89d2276233d31 100644 (file)
@@ -405,7 +405,8 @@ R   replacing (remerging same version))
 F      fetch restricted (must be manually downloaded)
 f      fetch restricted (already downloaded)
 I      interactive (requires user input)
-B      blocked by an already installed package
+B      blocked by another package (unresolved conflict)
+b      blocked by another package (automatically resolved conflict)
 .TE
 .TP
 .BR "\-\-quiet " (\fB\-q\fR)
index bf81246e8dfc43c26a5f2d750f71395c96d23138..75979b3399986fe974c5e55199c0b6ba030a9853 100644 (file)
@@ -4346,10 +4346,12 @@ class depgraph(object):
                self._irrelevant_blockers = digraph()
                # Contains only unsolvable Package -> Blocker edges
                self._unsolvable_blockers = digraph()
-               self._slot_collision_info = set()
+               self._slot_collision_info = {}
                # Slot collision nodes are not allowed to block other packages since
                # blocker validation is only able to account for one package per slot.
                self._slot_collision_nodes = set()
+               self._parent_atoms = {}
+               self._slot_conflict_parent_atoms = set()
                self._serialized_tasks_cache = None
                self._scheduler_graph = None
                self._displayed_list = None
@@ -4373,7 +4375,25 @@ class depgraph(object):
                the packages. In some cases it may be possible to resolve this
                automatically, but support for backtracking (removal nodes that have
                already been selected) will be required in order to handle all possible
-               cases."""
+               cases.
+
+               When a slot conflict occurs due to USE deps, there are a few
+               different cases to consider:
+
+               1) New USE are correctly set but --newuse wasn't requested so an
+                  installed package with incorrect USE happened to get pulled
+                  into graph before the new one.
+
+               2) New USE are incorrectly set but an installed package has correct
+                  USE so it got pulled into the graph, and a new instance also got
+                  pulled in due to --newuse or an upgrade.
+
+               3) Multiple USE deps exist that can't be satisfied simultaneously,
+                  and multiple package instances got pulled into the same slot to
+                  satisfy the conflicting deps.
+
+               TODO: Distinguish the above cases and tailor messages to suit them.
+               """
 
                if not self._slot_collision_info:
                        return
@@ -4388,46 +4408,64 @@ class depgraph(object):
                indent = "  "
                # Max number of parents shown, to avoid flooding the display.
                max_parents = 3
-               for slot_atom, root in self._slot_collision_info:
+               for (slot_atom, root), slot_nodes \
+                       in self._slot_collision_info.iteritems():
                        msg.append(str(slot_atom))
                        msg.append("\n\n")
-                       slot_nodes = []
-                       for node in self._slot_collision_nodes:
-                               if node.slot_atom == slot_atom:
-                                       slot_nodes.append(node)
-                       slot_nodes.append(self._slot_pkg_map[root][slot_atom])
+
                        for node in slot_nodes:
                                msg.append(indent)
                                msg.append(str(node))
-                               parents = self.digraph.parent_nodes(node)
-                               if parents:
-                                       omitted_parents = 0
-                                       if len(parents) > max_parents:
-                                               pruned_list = []
+                               parent_atoms = self._parent_atoms.get(node)
+                               if parent_atoms:
+                                       pruned_list = set()
+                                       # Prefer conflict atoms over others.
+                                       for parent_atom in parent_atoms:
+                                               if len(pruned_list) >= max_parents:
+                                                       break
+                                               if parent_atom in self._slot_conflict_parent_atoms:
+                                                       pruned_list.add(parent_atom)
+
+                                       # If this package was pulled in by conflict atoms then
+                                       # show those alone since those are the most interesting.
+                                       if not pruned_list:
                                                # When generating the pruned list, prefer instances
                                                # of DependencyArg over instances of Package.
-                                               for parent in parents:
+                                               for parent_atom in parent_atoms:
+                                                       if len(pruned_list) >= max_parents:
+                                                               break
+                                                       parent, atom = parent_atom
                                                        if isinstance(parent, DependencyArg):
-                                                               pruned_list.append(parent)
+                                                               pruned_list.add(parent_atom)
                                                # Prefer Packages instances that themselves have been
                                                # pulled into collision slots.
-                                               for parent in parents:
+                                               for parent_atom in parent_atoms:
+                                                       if len(pruned_list) >= max_parents:
+                                                               break
+                                                       parent, atom = parent_atom
                                                        if isinstance(parent, Package) and \
                                                                (parent.slot_atom, parent.root) \
                                                                in self._slot_collision_info:
-                                                               pruned_list.append(parent)
-                                               for parent in parents:
+                                                               pruned_list.add(parent_atom)
+                                               for parent_atom in parent_atoms:
                                                        if len(pruned_list) >= max_parents:
                                                                break
-                                                       if not isinstance(parent, DependencyArg) and \
-                                                               parent not in pruned_list:
-                                                               pruned_list.append(parent)
-                                               omitted_parents = len(parents) - len(pruned_list)
-                                               parents = pruned_list
+                                                       pruned_list.add(parent_atom)
+                                       omitted_parents = len(parent_atoms) - len(pruned_list)
+                                       parent_atoms = pruned_list
                                        msg.append(" pulled in by\n")
-                                       for parent in parents:
+                                       for parent_atom in parent_atoms:
+                                               parent, atom = parent_atom
                                                msg.append(2*indent)
-                                               msg.append(str(parent))
+                                               if isinstance(parent,
+                                                       (PackageArg, AtomArg)):
+                                                       # For PackageArg and AtomArg types, it's
+                                                       # redundant to display the atom attribute.
+                                                       msg.append(str(parent))
+                                               else:
+                                                       # Display the specific atom from SetArg or
+                                                       # Package types.
+                                                       msg.append("%s required by %s" % (atom, parent))
                                                msg.append("\n")
                                        if omitted_parents:
                                                msg.append(2*indent)
@@ -4467,6 +4505,40 @@ class depgraph(object):
                f.end_paragraph(1)
                f.writer.flush()
 
+       def _process_slot_conflicts(self):
+               """
+               Process slot conflict data to identify specific atoms which
+               lead to conflict. These atoms only match a subset of the
+               packages that have been pulled into a given slot.
+               """
+               for (slot_atom, root), slot_nodes \
+                       in self._slot_collision_info.iteritems():
+
+                       all_parent_atoms = set()
+                       for pkg in slot_nodes:
+                               parent_atoms = self._parent_atoms.get(pkg)
+                               if not parent_atoms:
+                                       continue
+                               all_parent_atoms.update(parent_atoms)
+
+                       for pkg in slot_nodes:
+                               parent_atoms = self._parent_atoms.get(pkg)
+                               if parent_atoms is None:
+                                       parent_atoms = set()
+                                       self._parent_atoms[pkg] = parent_atoms
+                               for parent_atom in all_parent_atoms:
+                                       if parent_atom in parent_atoms:
+                                               continue
+                                       # Use package set for matching since it will match via
+                                       # PROVIDE when necessary, while match_from_list does not.
+                                       parent, atom = parent_atom
+                                       atom_set = InternalPackageSet(
+                                               initial_atoms=(atom,))
+                                       if atom_set.findAtomForPackage(pkg):
+                                               parent_atoms.add(parent_atom)
+                                       else:
+                                               self._slot_conflict_parent_atoms.add(parent_atom)
+
        def _reinstall_for_flags(self, forced_flags,
                orig_use, orig_iuse, cur_use, cur_iuse):
                """Return a set of flags that trigger reinstallation, or None if there
@@ -4579,12 +4651,14 @@ class depgraph(object):
                #IUSE-aware emerge -> USE DEP aware depgraph
                #"no downgrade" emerge
                """
+               # Ensure that the dependencies of the same package
+               # are never processed more than once.
+               previously_added = pkg in self.digraph
 
                # select the correct /var database that we'll be checking against
                vardbapi = self.trees[pkg.root]["vartree"].dbapi
                pkgsettings = self.pkgsettings[pkg.root]
 
-               args = None
                arg_atoms = None
                if True:
                        try:
@@ -4595,8 +4669,6 @@ class depgraph(object):
                                                pkg, pkg.metadata["PROVIDE"], str(e))
                                        return 0
                                del e
-                       else:
-                               args = [arg for arg, atom in arg_atoms]
 
                if not pkg.onlydeps:
                        if not pkg.installed and \
@@ -4626,10 +4698,12 @@ class depgraph(object):
                                                existing_node_matches = False
                                if existing_node_matches:
                                        # The existing node can be reused.
-                                       if args:
-                                               for arg in args:
-                                                       self.digraph.add(existing_node, arg,
+                                       if arg_atoms:
+                                               for parent_atom in arg_atoms:
+                                                       parent, atom = parent_atom
+                                                       self.digraph.add(existing_node, parent,
                                                                priority=priority)
+                                                       self._add_parent_atom(existing_node, parent_atom)
                                        # If a direct circular dependency is not an unsatisfied
                                        # buildtime dependency then drop it here since otherwise
                                        # it can skew the merge order calculation in an unwanted
@@ -4638,32 +4712,16 @@ class depgraph(object):
                                                (priority.buildtime and not priority.satisfied):
                                                self.digraph.addnode(existing_node, myparent,
                                                        priority=priority)
+                                               if dep.atom is not None and dep.parent is not None:
+                                                       self._add_parent_atom(existing_node,
+                                                               (dep.parent, dep.atom))
                                        return 1
                                else:
 
-                                       if pkg.cpv == existing_node.cpv and \
-                                               dep.atom is not None and \
-                                               dep.atom.use:
-                                               # Multiple different instances of the same version
-                                               # (typically one installed and another not yet
-                                               # installed) have been pulled into the graph due
-                                               # to a USE dependency. The "slot collision" display
-                                               # is not helpful in a case like this, so display it
-                                               # as an unsatisfied dependency.
-                                               self._unsatisfied_deps_for_display.append(
-                                                       ((dep.root, dep.atom), {"myparent":dep.parent}))
-                                               self._slot_collision_info.add((pkg.slot_atom, pkg.root))
-                                               self._slot_collision_nodes.add(pkg)
-                                               self.digraph.addnode(pkg, myparent, priority=priority)
-                                               return 0
-
-                                       if pkg in self._slot_collision_nodes:
-                                               return 1
                                        # A slot collision has occurred.  Sometimes this coincides
                                        # with unresolvable blockers, so the slot collision will be
                                        # shown later if there are no unresolvable blockers.
-                                       self._slot_collision_info.add((pkg.slot_atom, pkg.root))
-                                       self._slot_collision_nodes.add(pkg)
+                                       self._add_slot_conflict(pkg)
                                        slot_collision = True
 
                        if slot_collision:
@@ -4682,8 +4740,6 @@ class depgraph(object):
                                self._slot_pkg_map[pkg.root][pkg.slot_atom] = pkg
                                self.mydbapi[pkg.root].cpv_inject(pkg)
 
-                       self.digraph.addnode(pkg, myparent, priority=priority)
-
                        if not pkg.installed:
                                # Allow this package to satisfy old-style virtuals in case it
                                # doesn't already. Any pre-existing providers will be preferred
@@ -4701,17 +4757,21 @@ class depgraph(object):
                                        del e
                                        return 0
 
-               if args:
+               if arg_atoms:
                        self._set_nodes.add(pkg)
 
                # Do this even when addme is False (--onlydeps) so that the
                # parent/child relationship is always known in case
                # self._show_slot_collision_notice() needs to be called later.
-               if pkg.onlydeps:
-                       self.digraph.add(pkg, myparent, priority=priority)
-               if args:
-                       for arg in args:
-                               self.digraph.add(pkg, arg, priority=priority)
+               self.digraph.add(pkg, myparent, priority=priority)
+               if dep.atom is not None and dep.parent is not None:
+                       self._add_parent_atom(pkg, (dep.parent, dep.atom))
+
+               if arg_atoms:
+                       for parent_atom in arg_atoms:
+                               parent, atom = parent_atom
+                               self.digraph.add(pkg, parent, priority=priority)
+                               self._add_parent_atom(pkg, parent_atom)
 
                """ This section determines whether we go deeper into dependencies or not.
                    We want to go deeper on a few occasions:
@@ -4728,12 +4788,30 @@ class depgraph(object):
 
                self.spinner.update()
 
-               if args:
+               if arg_atoms:
                        depth = 0
                pkg.depth = depth
-               dep_stack.append(pkg)
+               if not previously_added:
+                       dep_stack.append(pkg)
                return 1
 
+       def _add_parent_atom(self, pkg, parent_atom):
+               parent_atoms = self._parent_atoms.get(pkg)
+               if parent_atoms is None:
+                       parent_atoms = set()
+                       self._parent_atoms[pkg] = parent_atoms
+               parent_atoms.add(parent_atom)
+
+       def _add_slot_conflict(self, pkg):
+               self._slot_collision_nodes.add(pkg)
+               slot_key = (pkg.slot_atom, pkg.root)
+               slot_nodes = self._slot_collision_info.get(slot_key)
+               if slot_nodes is None:
+                       slot_nodes = set()
+                       slot_nodes.add(self._slot_pkg_map[pkg.root][pkg.slot_atom])
+                       self._slot_collision_info[slot_key] = slot_nodes
+               slot_nodes.add(pkg)
+
        def _add_pkg_deps(self, pkg, allow_unsatisfied=False):
 
                mytype = pkg.type_name
@@ -6288,6 +6366,9 @@ class depgraph(object):
                if not self.validate_blockers():
                        raise self._unknown_internal_error()
 
+               if self._slot_collision_info:
+                       self._process_slot_conflicts()
+
        def _serialize_tasks(self):
                scheduler_graph = self.digraph.copy()
                mygraph=self.digraph.copy()