Merged from trunk 9335:9351
authorFabian Groffen <grobian@gentoo.org>
Sat, 23 Feb 2008 23:37:49 +0000 (23:37 -0000)
committerFabian Groffen <grobian@gentoo.org>
Sat, 23 Feb 2008 23:37:49 +0000 (23:37 -0000)
   | 9336    | Fix package selection logic in order to prevent downgrade in |
   | zmedico | cases when selective is True and the currently installed     |
   |         | version does not have a matching ebuild in the tree. For     |
   |         | example, this type of situation is likely to occur if the    |
   |         | user somehow obtains a binary package that has a newer       |
   |         | version than any of the ebuilds in the portage tree. If      |
   |         | package maintainers want to encourage a downgrade in a case  |
   |         | like this then they should use package.mask so that the user |
   |         | receives a warning about the installed package being masked. |

   | 9337    | Fix --newuse reinstall logic broken by the previous commit.  |
   | zmedico |                                                              |

   | 9338    | Update the DepPriority documentation so that the table shows |
   | zmedico | all the currently supported priorities and categories.       |

   | 9339    | Bug #201045 - Use a topological sort to create an unmerge    |
   | zmedico | order such that each package is unmerged before it's         |
   |         | dependencies. This is necessary to avoid breaking things     |
   |         | that may need to run during pkg_prerm or pkg_postrm phases.  |

   | 9340    | Make depclean code filter out blocker atoms where            |
   | zmedico | appropriate.                                                 |

   | 9341    | Fix depclean breakage so that it doesn't clean some packages |
   | zmedico | that shouldn't be cleaned.                                   |

   | 9342    | Add missing % x (bug #208946, comment #2).                   |
   | zmedico |                                                              |

   | 9343    | Fix reversed order of parent/child parameders to             |
   | zmedico | digraph.add().                                               |

   | 9344    | Remove IUSE.invalid from qawarnings since there's no reason  |
   | zmedico | for it not to be fatal. Thanks to Betelgeuse.                |

   | 9345    | In the action_depclean() topological sort, only add packages |
   | zmedico | to the graph if they are being removed.                      |

   | 9346    | Bug #201045 - Make unmerge() preserve the unmerge order of   |
   | zmedico | packages given to it by the user or depclean.                |

   | 9347    | Make sure all packages to be pruned or depcleaned are added  |
   | zmedico | to the graph, even when they don't have any dependencies.    |

   | 9348    | Now that unmerge() preserves the order that it's given, if   |
   | zmedico | two different slots of the same package are being unmerged   |
   |         | then they are not necessarily unmerged consecutively. To     |
   |         | make the display less confusing, never display packages as   |
   |         | "protected" or "omitted" when they actually belong to the    |
   |         | overall set of "selected" packages to be unmerged.           |

   | 9349    | Fix unmerge() breakage from due to pkgmap being changed from |
   | zmedico | a dict to a list.                                            |

   | 9350    | Fix the --depclean/--prune code to use visible() for         |
   | zmedico | visibility checks instead of the old portdbapi hack.         |

   | 9351    | Make LibraryConsumerSet.mapPathsToAtoms() produce slot atoms |
   | zmedico | so that atoms aren't restricted to the currently installed   |
   |         | version, making upgrades possible.                           |

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

bin/repoman
pym/_emerge/__init__.py
pym/portage/dbapi/vartree.py
pym/portage/sets/dbapi.py

index ba8f27b312871cdbb1510657778d0df6f78bd424..ae1dc01d3eba243dc67a7b2c2135ebde5624a735 100755 (executable)
@@ -320,7 +320,6 @@ qawarnings=[
 "DEPEND.badindev","RDEPEND.badindev","PDEPEND.badindev",
 "DEPEND.badmaskedindev","RDEPEND.badmaskedindev","PDEPEND.badmaskedindev",
 "DESCRIPTION.toolong",
-"IUSE.invalid",
 "KEYWORDS.stupid",
 "KEYWORDS.missing",
 "RESTRICT.invalid",
index f6c57ec2f528b0e27a365934c3aae38c635b94a0..f2015dfbfb39d75dc7b93914e19e319bbecbbcec 100644 (file)
@@ -840,7 +840,38 @@ def filter_iuse_defaults(iuse):
                else:
                        yield flag
 
-class DepPriority(object):
+class AbstractDepPriority(object):
+       __slots__ = ("__weakref__", "buildtime", "runtime", "runtime_post")
+       def __init__(self, **kwargs):
+               for myattr in chain(self.__slots__, AbstractDepPriority.__slots__):
+                       if myattr == "__weakref__":
+                               continue
+                       myvalue = kwargs.get(myattr, False)
+                       setattr(self, myattr, myvalue)
+
+       def __lt__(self, other):
+               return self.__int__() < other
+
+       def __le__(self, other):
+               return self.__int__() <= other
+
+       def __eq__(self, other):
+               return self.__int__() == other
+
+       def __ne__(self, other):
+               return self.__int__() != other
+
+       def __gt__(self, other):
+               return self.__int__() > other
+
+       def __ge__(self, other):
+               return self.__int__() >= other
+
+       def copy(self):
+               import copy
+               return copy.copy(self)
+
+class DepPriority(AbstractDepPriority):
        """
                This class generates an integer priority level based of various
                attributes of the dependency relationship.  Attributes can be assigned
@@ -851,13 +882,16 @@ class DepPriority(object):
                "buildtime", "runtime", and "system".  Various combinations of
                attributes lead to the following priority levels:
 
-               Combination of properties    Priority level
+               Combination of properties           Priority  Category
 
-               not satisfied and buildtime     0
-               not satisfied and runtime      -1
-               satisfied and buildtime        -2
-               satisfied and runtime          -3
-               (none of the above)            -4
+               not satisfied and buildtime            0       HARD
+               not satisfied and runtime             -1       MEDIUM
+               not satisfied and runtime_post        -2       MEDIUM_SOFT
+               satisfied and buildtime and rebuild   -3       SOFT
+               satisfied and buildtime               -4       SOFT
+               satisfied and runtime                 -5       SOFT
+               satisfied and runtime_post            -6       SOFT
+               (none of the above)                   -6       SOFT
 
                Several integer constants are defined for categorization of priority
                levels:
@@ -867,17 +901,12 @@ class DepPriority(object):
                SOFT     The upper boundary for soft dependencies.
                MIN      The lower boundary for soft dependencies.
        """
-       __slots__ = ("__weakref__", "satisfied", "buildtime", "runtime", "runtime_post", "rebuild")
+       __slots__ = ("satisfied", "rebuild")
        MEDIUM = -1
        MEDIUM_SOFT = -2
        SOFT   = -3
        MIN    = -6
-       def __init__(self, **kwargs):
-               for myattr in self.__slots__:
-                       if myattr == "__weakref__":
-                               continue
-                       myvalue = kwargs.get(myattr, False)
-                       setattr(self, myattr, myvalue)
+
        def __int__(self):
                if not self.satisfied:
                        if self.buildtime:
@@ -895,21 +924,7 @@ class DepPriority(object):
                if self.runtime_post:
                        return -6
                return -6
-       def __lt__(self, other):
-               return self.__int__() < other
-       def __le__(self, other):
-               return self.__int__() <= other
-       def __eq__(self, other):
-               return self.__int__() == other
-       def __ne__(self, other):
-               return self.__int__() != other
-       def __gt__(self, other):
-               return self.__int__() > other
-       def __ge__(self, other):
-               return self.__int__() >= other
-       def copy(self):
-               import copy
-               return copy.copy(self)
+
        def __str__(self):
                myvalue = self.__int__()
                if myvalue > self.MEDIUM:
@@ -920,6 +935,35 @@ class DepPriority(object):
                        return "medium-soft"
                return "soft"
 
+class UnmergeDepPriority(AbstractDepPriority):
+       """
+       Combination of properties           Priority  Category
+
+       runtime                                0       HARD
+       runtime_post                          -1       HARD
+       buildtime                             -2       SOFT
+       (none of the above)                   -2       SOFT
+       """
+
+       MAX    =  0
+       SOFT   = -2
+       MIN    = -2
+
+       def __int__(self):
+               if self.runtime:
+                       return 0
+               if self.runtime_post:
+                       return -1
+               if self.buildtime:
+                       return -2
+               return -2
+
+       def __str__(self):
+               myvalue = self.__int__()
+               if myvalue > self.SOFT:
+                       return "hard"
+               return "soft"
+
 class FakeVartree(portage.vartree):
        """This is implements an in-memory copy of a vartree instance that provides
        all the interfaces required for use by the depgraph.  The vardb is locked
@@ -2527,6 +2571,7 @@ class depgraph(object):
                usepkgonly = "--usepkgonly" in self.myopts
                empty = "empty" in self.myparams
                selective = "selective" in self.myparams
+               reinstall = False
                # Behavior of the "selective" parameter depends on
                # whether or not a package matches an argument atom.
                # If an installed package provides an old-style
@@ -2546,9 +2591,12 @@ class depgraph(object):
                                if existing_node:
                                        break
                                if installed and not find_existing_node and \
+                                       (reinstall or not selective) and \
                                        (matched_packages or empty):
-                                       # We only need to select an installed package here
-                                       # if there is no other choice.
+                                       # We only need to select an installed package in the
+                                       # following cases:
+                                       #   1) there is no other choice
+                                       #   2) selective is True
                                        continue
                                if hasattr(db, "xmatch"):
                                        cpv_list = db.xmatch("match-all", atom)
@@ -2656,6 +2704,8 @@ class depgraph(object):
                                                        self._reinstall_for_flags(
                                                        forced_flags, old_use, old_iuse,
                                                        cur_use, cur_iuse)
+                                               if reinstall_for_flags:
+                                                       reinstall = True
                                        if not installed:
                                                must_reinstall = empty or \
                                                        (myarg and not selective)
@@ -4944,7 +4994,7 @@ def unmerge(root_config, myopts, unmerge_action,
                        print darkgreen(newline+\
                                ">>> These are the packages that would be unmerged:")
        
-               pkgmap={}
+               pkgmap = []
                numselected=0
                for x in candidate_catpkgs:
                        # cycle through all our candidate deps and determine
@@ -4970,11 +5020,8 @@ def unmerge(root_config, myopts, unmerge_action,
                                portage.writemsg("\n--- Couldn't find '%s' to %s.\n" % \
                                        (x, unmerge_action), noiselevel=-1)
                                continue
-                       mykey = portage.key_expand(
-                               portage.dep_getkey(
-                                       mymatch[0]), mydb=vartree.dbapi, settings=settings)
-                       if not pkgmap.has_key(mykey):
-                               pkgmap[mykey]={"protected":[], "selected":[], "omitted":[] }
+                       pkgmap.append({"protected":[], "selected":[], "omitted":[] })
+                       mykey = len(pkgmap) - 1
                        if unmerge_action=="unmerge":
                                        for y in mymatch:
                                                if y not in pkgmap[mykey]["selected"]:
@@ -5044,25 +5091,38 @@ def unmerge(root_config, myopts, unmerge_action,
        finally:
                if vdb_lock:
                        portage.locks.unlockdir(vdb_lock)
+       all_selected = set()
        for x in pkgmap:
-               for y in localtree.dep_match(x):
+               all_selected.update(x["selected"])
+       for x in xrange(len(pkgmap)):
+               selected = pkgmap[x]["selected"]
+               if not selected:
+                       continue
+               for mytype, mylist in pkgmap[x].iteritems():
+                       if mytype == "selected":
+                               continue
+                       pkgmap[x][mytype] = \
+                               [cpv for cpv in mylist if cpv not in all_selected]
+               cp = portage.cpv_getkey(selected[0])
+               for y in localtree.dep_match(cp):
                        if y not in pkgmap[x]["omitted"] and \
                           y not in pkgmap[x]["selected"] and \
-                          y not in pkgmap[x]["protected"]:
+                          y not in pkgmap[x]["protected"] and \
+                          y not in all_selected:
                                pkgmap[x]["omitted"].append(y)
                if global_unmerge and not pkgmap[x]["selected"]:
                        #avoid cluttering the preview printout with stuff that isn't getting unmerged
                        continue
-               if not (pkgmap[x]["protected"] or pkgmap[x]["omitted"]) and (x in syslist):
-                       print colorize("BAD","\a\n\n!!! '%s' is part of your system profile." % x)
+               if not (pkgmap[x]["protected"] or pkgmap[x]["omitted"]) and cp in syslist:
+                       print colorize("BAD","\a\n\n!!! '%s' is part of your system profile." % cp)
                        print colorize("WARN","\a!!! Unmerging it may be damaging to your system.\n")
                        if "--pretend" not in myopts and "--ask" not in myopts:
                                countdown(int(settings["EMERGE_WARNING_DELAY"]),
                                        colorize("UNMERGE_WARN", "Press Ctrl-C to Stop"))
                if "--quiet" not in myopts:
-                       print "\n "+white(x)
+                       print "\n "+bold(cp)
                else:
-                       print white(x)+": ",
+                       print bold(cp)+": ",
                for mytype in ["selected","protected","omitted"]:
                        if "--quiet" not in myopts:
                                portage.writemsg_stdout((mytype + ": ").rjust(14), noiselevel=-1)
@@ -5109,7 +5169,7 @@ def unmerge(root_config, myopts, unmerge_action,
        if not autoclean:
                countdown(int(settings["CLEAN_DELAY"]), ">>> Unmerging")
 
-       for x in pkgmap:
+       for x in xrange(len(pkgmap)):
                for y in pkgmap[x]["selected"]:
                        print ">>> Unmerging "+y+"..."
                        emergelog(xterm_titles, "=== Unmerging... ("+y+")")
@@ -6413,23 +6473,32 @@ def action_depclean(settings, trees, ldpath_mtimes,
        if "--quiet" not in myopts:
                print "\nCalculating dependencies  ",
 
-       soft = 0
-       hard = 1
+       runtime = UnmergeDepPriority(runtime=True)
+       runtime_post = UnmergeDepPriority(runtime_post=True)
+       buildtime = UnmergeDepPriority(buildtime=True)
+
+       priority_map = {
+               "RDEPEND": runtime,
+               "PDEPEND": runtime_post,
+               "DEPEND": buildtime,
+       }
+
        remaining_atoms = []
        if action == "depclean":
                for atom in worldlist:
                        if vardb.match(atom):
-                               remaining_atoms.append((atom, 'world', hard))
+                               remaining_atoms.append((atom, 'world', runtime))
                for atom in syslist:
                        if vardb.match(atom):
-                               remaining_atoms.append((atom, 'system', hard))
+                               remaining_atoms.append((atom, 'system', runtime))
        elif action == "prune":
                for atom in syslist:
                        if vardb.match(atom):
-                               remaining_atoms.append((atom, 'system', hard))
+                               remaining_atoms.append((atom, 'system', runtime))
                # Pull in everything that's installed since we don't want to prune a
                # package if something depends on it.
-               remaining_atoms.extend((atom, 'world', hard) for atom in vardb.cp_all())
+               remaining_atoms.extend(
+                       (atom, 'world', runtime) for atom in vardb.cp_all())
                if not myfiles:
                        # Try to prune everything that's slotted.
                        for cp in vardb.cp_all():
@@ -6438,14 +6507,15 @@ def action_depclean(settings, trees, ldpath_mtimes,
 
        unresolveable = {}
        aux_keys = ["DEPEND", "RDEPEND", "PDEPEND"]
-       metadata_keys = ["PROVIDE", "SLOT", "USE"]
+       metadata_keys = depgraph._mydbapi_keys
        graph = digraph()
+       with_bdeps = myopts.get("--with-bdeps", "y") == "y"
 
        while remaining_atoms:
                atom, parent, priority = remaining_atoms.pop()
                pkgs = vardb.match(atom)
                if not pkgs:
-                       if not atom.startswith("!") and priority == hard:
+                       if priority > UnmergeDepPriority.SOFT:
                                unresolveable.setdefault(atom, []).append(parent)
                        continue
                if action == "depclean" and parent == "world" and myfiles:
@@ -6470,44 +6540,42 @@ def action_depclean(settings, trees, ldpath_mtimes,
                                        filtered_pkgs.append(pkg)
                        pkgs = filtered_pkgs
                if len(pkgs) > 1:
-                       # Prune all but the best matching slot, since that's all that a
-                       # deep world update would pull in.  Don't prune if this atom comes
-                       # directly from world though, since world atoms are greedy when
-                       # they don't specify a slot.
-                       visible_in_portdb = [cpv for cpv in pkgs if portdb.match("="+cpv)]
-                       if visible_in_portdb:
-                               # For consistency with the update algorithm, keep the highest
-                               # visible version and prune any versions that are either masked
-                               # or no longer exist in the portage tree.
-                               pkgs = visible_in_portdb
-                       pkgs = [portage.best(pkgs)]
+                       # For consistency with the update algorithm, keep the highest
+                       # visible version and prune any versions that are old or masked.
+                       for cpv in reversed(pkgs):
+                               metadata = dict(izip(metadata_keys,
+                                       vardb.aux_get(cpv, metadata_keys)))
+                               if visible(settings, cpv, metadata,
+                                       built=True, installed=True):
+                                       pkgs = [cpv]
+                                       break
+                       if len(pkgs) > 1:
+                               # They're all masked, so just keep the highest version.
+                               pkgs = [pkgs[-1]]
                for pkg in pkgs:
-                       graph.add(pkg, parent)
+                       graph.add(pkg, parent, priority=priority)
                        if fakedb.cpv_exists(pkg):
                                continue
                        spinner.update()
                        fakedb.cpv_inject(pkg)
                        myaux = dict(izip(aux_keys, vardb.aux_get(pkg, aux_keys)))
                        mydeps = []
-                       if myopts.get("--with-bdeps", "y") == "y":
-                               mydeps.append((myaux["DEPEND"], soft))
-                       del myaux["DEPEND"]
-                       mydeps.append((" ".join(myaux.values()), hard))
+
                        usedef = vardb.aux_get(pkg, ["USE"])[0].split()
-                       for depstr, priority in mydeps:
+                       for dep_type, depstr in myaux.iteritems():
 
                                if not depstr:
                                        continue
 
+                               if not with_bdeps and dep_type == "DEPEND":
+                                       continue
+
+                               priority = priority_map[dep_type]
                                if "--debug" in myopts:
                                        print
                                        print "Parent:   ", pkg
                                        print "Depstring:", depstr
-                                       print "Priority:",
-                                       if priority == soft:
-                                               print "soft"
-                                       else:
-                                               print "hard"
+                                       print "Priority:", priority
 
                                try:
                                        portage.dep._dep_check_strict = False
@@ -6525,6 +6593,8 @@ def action_depclean(settings, trees, ldpath_mtimes,
                                        print "Candidates:", atoms
 
                                for atom in atoms:
+                                       if atom.startswith("!"):
+                                               continue
                                        remaining_atoms.append((atom, pkg, priority))
 
        if "--quiet" not in myopts:
@@ -6612,6 +6682,75 @@ def action_depclean(settings, trees, ldpath_mtimes,
                                        good("--nodeps"))
 
        if len(cleanlist):
+               # Use a topological sort to create an unmerge order such that
+               # each package is unmerged before it's dependencies. This is
+               # necessary to avoid breaking things that may need to run
+               # during pkg_prerm or pkg_postrm phases.
+
+               # Create a new graph to account for dependencies between the
+               # packages being unmerged.
+               graph = digraph()
+               clean_set = set(cleanlist)
+               del cleanlist[:]
+               for node in clean_set:
+                       graph.add(node, None)
+                       myaux = dict(izip(aux_keys, vardb.aux_get(node, aux_keys)))
+                       mydeps = []
+                       usedef = vardb.aux_get(pkg, ["USE"])[0].split()
+                       for dep_type, depstr in myaux.iteritems():
+                               if not depstr:
+                                       continue
+                               try:
+                                       portage.dep._dep_check_strict = False
+                                       success, atoms = portage.dep_check(depstr, None, settings,
+                                               myuse=usedef, trees=dep_check_trees, myroot=myroot)
+                               finally:
+                                       portage.dep._dep_check_strict = True
+                               if not success:
+                                       show_invalid_depstring_notice(
+                                               ("installed", myroot, node, "nomerge"),
+                                               depstr, atoms)
+                                       return
+
+                               priority = priority_map[dep_type]
+                               for atom in atoms:
+                                       if atom.startswith("!"):
+                                               continue
+                                       matches = vardb.match(atom)
+                                       if not matches:
+                                               continue
+                                       for cpv in matches:
+                                               if cpv in clean_set:
+                                                       graph.add(cpv, node, priority=priority)
+
+               # Order nodes from lowest to highest overall reference count for
+               # optimal root node selection.
+               node_refcounts = {}
+               for node in graph.order:
+                       node_refcounts[node] = len(graph.parent_nodes(node))
+               def cmp_reference_count(node1, node2):
+                       return node_refcounts[node1] - node_refcounts[node2]
+               graph.order.sort(cmp_reference_count)
+
+               ignore_priority_range = [None]
+               ignore_priority_range.extend(
+                       xrange(UnmergeDepPriority.MIN, UnmergeDepPriority.MAX + 1))
+               while not graph.empty():
+                       for ignore_priority in ignore_priority_range:
+                               nodes = graph.root_nodes(ignore_priority=ignore_priority)
+                               if nodes:
+                                       break
+                       if not nodes:
+                               raise AssertionError("no root nodes")
+                       if ignore_priority is not None:
+                               # Some deps have been dropped due to circular dependencies,
+                               # so only pop one node in order do minimize the number that
+                               # are dropped.
+                               del nodes[1:]
+                       for node in nodes:
+                               graph.remove(node)
+                               cleanlist.append(node)
+
                unmerge(root_config, myopts,
                        "unmerge", cleanlist, ldpath_mtimes)
 
index ae37127ab66f6f939bad3cc62916df7fbc7da001..d3ecacee04104349bfed69a8eff85126d136ee81 100644 (file)
@@ -1601,7 +1601,7 @@ class dblink(object):
                for x in preserve_paths:
                        print "injecting %s into %s" % (x, srcroot)
                        if not os.path.exists(os.path.join(destroot, x.lstrip(os.sep))):
-                               print "%s does not exist so can't be preserved"
+                               print "%s does not exist so can't be preserved" % x
                                missing_paths.append(x)
                                continue
                        mydir = os.path.join(srcroot, os.path.dirname(x).lstrip(os.sep))
index a1b902fdcc4db9abf2072032ab237907ea23f656..220ff2283498a44fc5c27d7c5b2b9178b3fad581 100644 (file)
@@ -126,8 +126,10 @@ class LibraryConsumerSet(PackageSet):
                        link = dblink(mysplit[0], mysplit[1], myroot=self.dbapi.root, \
                                        mysettings=self.dbapi.settings, treetype='vartree', \
                                        vartree=self.dbapi.vartree)
-                       if paths.intersection(link.getcontents().keys()):
-                               rValue.add("/".join(catpkgsplit(cpv)[:2]))
+                       if paths.intersection(link.getcontents()):
+                               cat, pn = catpkgsplit(cpv)[:2]
+                               slot = self.dbapi.aux_get(cpv, ["SLOT"])[0]
+                               rValue.add("%s/%s:%s" % (cat, pn, slot))
                return rValue