Merged from trunk 9894:9906
authorFabian Groffen <grobian@gentoo.org>
Tue, 15 Apr 2008 18:14:49 +0000 (18:14 -0000)
committerFabian Groffen <grobian@gentoo.org>
Tue, 15 Apr 2008 18:14:49 +0000 (18:14 -0000)
   | 9895    | Fix issues with an empty string being passed into            |
   | zmedico | PreservedLibsRegistry.register() now that COUNTER is not     |
   |         | loaded via config.load_infodir(). Thanks to grobian for      |
   |         | reporting.                                                   |

   | 9896    | In config.setcpv() and regenerate(), replace                 |
   | zmedico | str.startswith() calls with slice comparison. It's not       |
   |         | pretty but performance is critical in this section of code   |
   |         | and there is a measurable performance difference.            |

   | 9898    | Bug #217664 - Fix emerge 'by path' package argument handling |
   | zmedico | so that Package instances are properly cached (avoiding a    |
   |         | KeyError later).                                             |

   | 9900    | Revert part of atom selection consistency improvement from   |
   | zmedico | r9873 since it in some cases it triggers 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.       |

   | 9901    | Add support to depgraph._select_atoms() to take a "parent"   |
   | zmedico | parameter and use that to try and avoid unresolvable direct  |
   |         | circular dependencies when necessary. Also, make atom        |
   |         | selection more consistent with the graph to solve some cases |
   |         | of bug #1343. This improves the fix from bug #141118 to work |
   |         | in cases when a virtual is not yet installed but it has been |
   |         | pulled into the graph. For example, see the case of in Bug   |
   |         | #163801#c17, where we want kaffe to satisfy virtual/jdk-1.4  |
   |         | without an extra jvm being pulled in unnecessarily.          |

   | 9904    | Comment some more on the significance of passing the         |
   | zmedico | graph_db and parent into depgraph._select_atoms() to solve   |
   |         | some cases of bug #1343 while avoiding unresolvable direct   |
   |         | circular dependencies.                                       |

   | 9906    | The dependency resolver now has a feedback mechanism that    |
   | zmedico | helps make atom selections more consistent with previous     |
   |         | selections that have been added to the graph. This solves    |
   |         | some common cases of bug #1343.                              |

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

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

diff --git a/NEWS b/NEWS
index 243ef7ecc0286a8bb53502f3ee94504dd0da3c91..520f2c732cce14efe9063acb94f8c405a5a4b3a3 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -15,6 +15,13 @@ portage-2.2
 * Add support for news items (GLEP 42)
 * Add support for generic package sets (also see RELEASE-NOTES)
 
+portage-2.1.5
+==================================
+
+* The dependency resolver now has a feedback mechanism that helps make atom
+  selections more consistent with previous selections that have been added to
+  the graph. This solves some common cases of bug #1343.
+
 portage-2.1.4
 -------------
 
index 522115ddf76c15585f54b306e81f7aeed8c88426..c8c8d0168ca9f11c012db505efda03ceba74d9ae 100644 (file)
@@ -1626,11 +1626,30 @@ class depgraph(object):
                        # 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
@@ -2080,7 +2099,7 @@ class depgraph(object):
                                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
@@ -2219,6 +2238,7 @@ class depgraph(object):
                                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":
@@ -2257,6 +2277,7 @@ class depgraph(object):
                                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):
@@ -2536,7 +2557,7 @@ class depgraph(object):
                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]
@@ -2544,12 +2565,16 @@ class depgraph(object):
                        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])
index 7554b8159522301e8e3b0d05c9c24b4a0ca4afa3..7cc2e99635d0b8cb3fd18ca65f502afbcb2068b9 100644 (file)
@@ -2052,13 +2052,14 @@ class config(object):
                                        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.
@@ -2069,13 +2070,13 @@ class config(object):
                                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)
@@ -2469,6 +2470,8 @@ class config(object):
                                        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]
@@ -2498,8 +2501,9 @@ class config(object):
                                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] == "+":
@@ -5514,6 +5518,8 @@ def dep_zapdeps(unreduced, reduced, myroot, use_binaries=0, trees=None):
        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
@@ -5575,8 +5581,43 @@ def dep_zapdeps(unreduced, reduced, myroot, use_binaries=0, trees=None):
                                        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)
 
index 26946822494beac992ddd85e4b1d838e1a51e765..ec88a921abd896173862cc5a3b292f665b0fae2b 100644 (file)
@@ -1186,7 +1186,8 @@ class dblink(object):
                        
                        # Remove the registration of preserved libs for this pkg instance
                        plib_registry = self.vartree.dbapi.plib_registry
-                       plib_registry.unregister(self.mycpv, self.settings["SLOT"], self.settings["COUNTER"])
+                       plib_registry.unregister(self.mycpv, self.settings["SLOT"],
+                               self.vartree.dbapi.cpv_counter(self.mycpv))
 
                        if myebuildpath:
                                ebuild_phase = "postrm"
@@ -1919,6 +1920,10 @@ class dblink(object):
                #       has to be before the counter is written) - genone
                counter = self.vartree.dbapi.counter_tick(self.myroot, mycpv=self.mycpv)
 
+               # Save this for unregistering preserved-libs if the merge fails.
+               self.settings["COUNTER"] = str(counter)
+               self.settings.backup_changes("COUNTER")
+
                myfilelist = []
                mylinklist = []
                def onerror(e):