Merged from trunk 4740:4753
authorFabian Groffen <grobian@gentoo.org>
Wed, 18 Oct 2006 16:51:39 +0000 (16:51 -0000)
committerFabian Groffen <grobian@gentoo.org>
Wed, 18 Oct 2006 16:51:39 +0000 (16:51 -0000)
Filter empty elements in cumulative env-update variables.  Thanks to
Brian Harring for this patch from bug #151694.

Fix CONFIG_PROTECT so that is works with symlinked directories for bug
#151502.

Make empty digest-* files 0 bytes instead of 1 byte.  Thanks to Sven
Wegener for reporting this.

Eliminate an unnecessary string concatenation.

Add more dependency priority levels for merge order calculations.
Previously,  all installed deps were categorized at the same level with
other soft deps.  Now there are 3 priority levels for soft deps:
satisfied buildtime, satisfied runtime, and other.

Remove the unused system slot from DepPriority.

Fix --usepkg --newuse logic so that it doesn't throw a KeyError if
there's no matching ebuild in the tree.

Add __weakref__ to __slots__ so that weakref support isn't disabled.

Don't initialize __weakref__ in the DepPriority constructor.

All packages to be added to the world favorites via --noreplace, even
when --ask is enabled.  Thanks to leio for reporting this issue.

Explicitly set the priority when a node is added to enforce merge order
for blocker avoidance.

Handle unresolvable blockers properly for bug #151822.

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

bin/emerge
pym/portage.py
pym/portage_manifest.py
pym/portage_util.py

index 9cbeec6f90dd797f1de49786a4a89c1d408e39a6..9d18770c0b7eba0b47b097da3311093127d0a610 100755 (executable)
@@ -630,6 +630,76 @@ def filter_iuse_defaults(iuse):
                else:
                        yield flag
 
+class DepPriority(object):
+       """
+               This class generates an integer priority level based of various
+               attributes of the dependency relationship.  Attributes can be assigned
+               at any time and the new integer value will be generated on calls to the
+               __int__() method.  Rich comparison operators are supported.
+
+               The boolean attributes that affect the integer value are "satisfied",
+               "buildtime", "runtime", and "system".  Various combinations of
+               attributes lead to the following priority levels:
+
+               Combination of properties    Priority level
+
+               not satisfied and buildtime     0
+               not satisfied and runtime      -1
+               satisfied and buildtime        -2
+               satisfied and runtime          -3
+               (none of the above)            -4
+
+               Several integer constants are defined for categorization of priority
+               levels:
+
+               MEDIUM   The upper boundary for medium dependencies.
+               SOFT     The upper boundary for soft dependencies.
+               MIN      The lower boundary for soft dependencies.
+       """
+       __slots__ = ["__weakref__", "satisfied", "buildtime", "runtime"]
+       MEDIUM = -1
+       SOFT   = -2
+       MIN    = -4
+       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:
+                               return 0
+                       if self.runtime:
+                               return -1
+               if self.buildtime:
+                       return -2
+               if self.runtime:
+                       return -3
+               return -4
+       def __lt__(self, other):
+               return int(self) < other
+       def __le__(self, other):
+               return int(self) <= other
+       def __eq__(self, other):
+               return int(self) == other
+       def __ne__(self, other):
+               return int(self) != other
+       def __gt__(self, other):
+               return int(self) > other
+       def __ge__(self, other):
+               return int(self) >= other
+       def copy(self):
+               import copy
+               return copy.copy(self)
+       def __str__(self):
+               myvalue = int(self)
+               if myvalue > self.MEDIUM:
+                       return "hard"
+               if myvalue > self.SOFT:
+                       return "medium"
+               return "soft"
+
 class depgraph:
 
        pkg_tree_map = {
@@ -692,7 +762,7 @@ class depgraph:
                self.blocker_parents = {}
 
        def create(self, mybigkey, myparent=None, addme=1, myuse=None,
-               priority=digraph.HARD, rev_dep=False, arg=None):
+               priority=DepPriority(), rev_dep=False, arg=None):
                """
                Fills the digraph with nodes comprised of packages to merge.
                mybigkey is the package spec of the package to merge.
@@ -735,7 +805,7 @@ class depgraph:
                        if addme and "--buildpkgonly" not in self.myopts and myparent:
                                mybigkey[1] = myparent.split()[1]
                                self.digraph.addnode(" ".join(mybigkey), myparent,
-                                       priority=digraph.SOFT)
+                                       priority=DepPriority())
                        return 1
                if not arg:
                        arg = portage.best_match_to_list(mykey, self.args_keys)
@@ -846,7 +916,8 @@ class depgraph:
 
                try:
                        if not self.select_dep("/", edepend["DEPEND"], myparent=mp,
-                               myuse=myuse, parent_arg=arg):
+                               myuse=myuse, priority=DepPriority(buildtime=True),
+                               parent_arg=arg):
                                return 0
                        """RDEPEND is soft by definition.  However, in order to ensure
                        correct merge order, we make it a hard dependency.  Otherwise, a
@@ -854,13 +925,15 @@ class depgraph:
                        dependencies not being installed yet.
                        """
                        if not self.select_dep(myroot,edepend["RDEPEND"], myparent=mp,
-                               myuse=myuse, priority=digraph.MEDIUM, parent_arg=arg):
+                               myuse=myuse, priority=DepPriority(runtime=True),
+                               parent_arg=arg):
                                return 0
                        if edepend.has_key("PDEPEND") and edepend["PDEPEND"]:
                                # Post Depend -- Add to the list without a parent, as it depends
                                # on a package being present AND must be built after that package.
                                if not self.select_dep(myroot, edepend["PDEPEND"], myparent=mp,
-                                       myuse=myuse, priority=digraph.SOFT, rev_deps=True, parent_arg=arg):
+                                       myuse=myuse, priority=DepPriority(), rev_deps=True,
+                                       parent_arg=arg):
                                        return 0
                except ValueError, e:
                        pkgs = e.args[0]
@@ -1056,8 +1129,8 @@ class depgraph:
                                                return match
 
        def select_dep(self, myroot, depstring, myparent=None, arg=None,
-               myuse=None, raise_on_missing=False, priority=digraph.HARD, rev_deps=False,
-               parent_arg=None):
+               myuse=None, raise_on_missing=False, priority=DepPriority(),
+               rev_deps=False, parent_arg=None):
                """ Given a depstring, create the depgraph such that all dependencies are satisfied.
                    myroot = $ROOT from environment, where {R,P}DEPENDs are merged to.
                    myparent = the node whose depstring is being passed in
@@ -1176,7 +1249,10 @@ class depgraph:
                                        iuses = set(filter_iuse_defaults(
                                                bindb.aux_get(myeb_pkg, ["IUSE"])[0].split()))
                                        old_use = bindb.aux_get(myeb_pkg, ["USE"])[0].split()
-                                       pkgsettings.setcpv(myeb_pkg, mydb=portdb)
+                                       mydb = None
+                                       if "--usepkgonly" not in self.myopts and myeb:
+                                               mydb = portdb
+                                       pkgsettings.setcpv(myeb, mydb=mydb)
                                        now_use = pkgsettings["USE"].split()
                                        forced_flags = set()
                                        forced_flags.update(pkgsettings.useforce)
@@ -1302,9 +1378,9 @@ class depgraph:
 
                        if myparent:
                                #we are a dependency, so we want to be unconditionally added
-                               mypriority = priority
-                               if mypriority > digraph.SOFT and vardb.match(x):
-                                       mypriority = digraph.SOFT
+                               mypriority = priority.copy()
+                               if vardb.match(x):
+                                       mypriority.satisfied = True
                                if not self.create(selected_pkg[0:3], myparent,
                                        myuse=selected_pkg[-1], priority=mypriority,
                                        rev_dep=rev_deps, arg=arg):
@@ -1357,11 +1433,22 @@ class depgraph:
                                enforce correct merge order."""
                                fakedb = self.mydbapi[myroot]
                                new_pkgs = []
+                               unresolveable = False
                                for cpv in blocked_pkgs:
                                        myslot = vardb.aux_get(cpv, ["SLOT"])[0]
                                        myslot_atom = "%s:%s" % (portage.dep_getkey(cpv), myslot)
-                                       new_pkgs.append(
-                                               (myslot_atom, fakedb.match(myslot_atom)[0]))
+                                       mymatches = fakedb.match(myslot_atom)
+                                       if mymatches:
+                                               new_pkgs.append((myslot_atom, mymatches[0]))
+                                       else:
+                                               """There's an installed package that's blocked and
+                                               there's no upgrade found to invalidate it, so leave
+                                               this blocker in the digraph."""
+                                               unresolveable = True
+                                               break
+                               if unresolveable:
+                                       continue
+
                                for parent in self.digraph.parent_nodes(blocker):
                                        ptype, proot, pcpv, pstatus = parent.split()
                                        pdbapi = self.trees[proot][self.pkg_tree_map[ptype]].dbapi
@@ -1374,7 +1461,8 @@ class depgraph:
                                                        continue
                                                # Enforce correct merge order with a hard dep.
                                                node = self.pkg_node_map[pkg]
-                                               self.digraph.addnode(node, parent)
+                                               self.digraph.addnode(node, parent,
+                                                       priority=DepPriority(buildtime=True))
                                                """Count references to this blocker so that it can be
                                                invalidated after nodes referencing it have been merged."""
                                                self.blocker_digraph.addnode(node, blocker)
@@ -1399,15 +1487,17 @@ class depgraph:
                        get_nodes = mygraph.root_nodes
                else:
                        get_nodes = mygraph.leaf_nodes
+               ignore_priority_range = [None]
+               ignore_priority_range.extend(
+                       xrange(DepPriority.MIN, DepPriority.MEDIUM + 1))
                while not mygraph.empty():
-                       for ignore_priority in \
-                               (digraph.NONE, digraph.SOFT, digraph.MEDIUM):
+                       for ignore_priority in ignore_priority_range:
                                nodes = get_nodes(ignore_priority=ignore_priority)
                                if nodes:
                                        break
                        selected_nodes = None
                        if nodes:
-                               if ignore_priority <= digraph.SOFT:
+                               if ignore_priority <= DepPriority.SOFT:
                                        selected_nodes = [nodes[0]]
                                else:
                                        """Recursively gather a group of nodes that RDEPEND on
@@ -1420,7 +1510,7 @@ class depgraph:
                                                        return False
                                                selected_nodes.add(node)
                                                for child in mygraph.child_nodes(node,
-                                                       ignore_priority=digraph.SOFT):
+                                                       ignore_priority=DepPriority.SOFT):
                                                        if not gather_deps(
                                                                mergeable_nodes, selected_nodes, child):
                                                                return False
@@ -1477,7 +1567,7 @@ class depgraph:
                        for node in myblockers.root_nodes():
                                retlist.append(node.split())
                                for parent in self.blocker_parents[node]:
-                                       self.digraph.add(node, parent, priority=digraph.SOFT)
+                                       self.digraph.add(node, parent, priority=DepPriority())
 
                return retlist
 
@@ -3826,7 +3916,12 @@ def action_build(settings, trees, mtimedb,
                                                show_blocker_docs_link()
                                        sys.exit(1)
                        if mergecount==0:
-                               if settings["AUTOCLEAN"] and "yes"==settings["AUTOCLEAN"]:
+                               if "--noreplace" in myopts and favorites:
+                                       print
+                                       for x in favorites:
+                                               print " %s %s" % (good("*"), x)
+                                       prompt="Would you like to add these packages to your world favorites?"
+                               elif settings["AUTOCLEAN"] and "yes"==settings["AUTOCLEAN"]:
                                        prompt="Nothing to merge; would you like to auto-clean packages?"
                                else:
                                        print
index 8b0c9d7a3cbac5725d064bb03771afff8aa6538f..21b2b444273672f7fde2de051f664aa538522e96 100644 (file)
@@ -312,10 +312,6 @@ def flatten(mytokens):
 #beautiful directed graph object
 
 class digraph:
-       NONE   = -1
-       SOFT   = 0
-       MEDIUM = 1
-       HARD   = 2
        def __init__(self):
                """Create an empty digraph"""
                
@@ -323,7 +319,7 @@ class digraph:
                self.nodes = {}
                self.order = []
 
-       def add(self, node, parent, priority=2):
+       def add(self, node, parent, priority=0):
                """Adds the specified node with the specified parent.
                
                If the dep is a soft-dep and the node already has a hard
@@ -376,9 +372,9 @@ class digraph:
                """Return a list of all nodes in the graph"""
                return self.order[:]
 
-       def child_nodes(self, node, ignore_priority=-1):
+       def child_nodes(self, node, ignore_priority=None):
                """Return all children of the specified node"""
-               if ignore_priority == -1:
+               if ignore_priority is None:
                        return self.nodes[node][0].keys()
                children = []
                for child, priority in self.nodes[node][0].iteritems():
@@ -390,7 +386,7 @@ class digraph:
                """Return all parents of the specified node"""
                return self.nodes[node][1].keys()
 
-       def leaf_nodes(self, ignore_priority=-1):
+       def leaf_nodes(self, ignore_priority=None):
                """Return all nodes that have no children
                
                If ignore_soft_deps is True, soft deps are not counted as
@@ -407,7 +403,7 @@ class digraph:
                                leaf_nodes.append(node)
                return leaf_nodes
 
-       def root_nodes(self, ignore_priority=-1):
+       def root_nodes(self, ignore_priority=None):
                """Return all nodes that have no parents.
                
                If ignore_soft_deps is True, soft deps are not counted as
@@ -454,7 +450,7 @@ class digraph:
                        return leaf_nodes[0]
                return None
 
-       def hasallzeros(self, ignore_priority=-1):
+       def hasallzeros(self, ignore_priority=None):
                return len(self.leaf_nodes(ignore_priority=ignore_priority)) == \
                        len(self.order)
 
@@ -467,12 +463,7 @@ class digraph:
                                print "(no children)"
                        for child in self.nodes[node][0]:
                                print "  ",child,
-                               if self.nodes[node][0][child] == self.HARD:
-                                       print "(hard)"
-                               elif self.nodes[node][0][child] == self.MEDIUM:
-                                       print "(medium)"
-                               else:
-                                       print "(soft)"
+                               print "(%s)" % self.nodes[node][0][child]
 
 
 
@@ -593,7 +584,7 @@ def env_update(makelinks=1, target_root=None, prev_mtimes=None, contents=None):
                mylist = []
                for myconfig in config_list:
                        if var in myconfig:
-                               mylist.extend(myconfig[var].split())
+                               mylist.extend(filter(None, myconfig[var].split()))
                                del myconfig[var] # prepare for env.update(myconfig)
                if mylist:
                        env[var] = " ".join(mylist)
@@ -603,7 +594,7 @@ def env_update(makelinks=1, target_root=None, prev_mtimes=None, contents=None):
                mylist = []
                for myconfig in config_list:
                        if var in myconfig:
-                               mylist.extend(myconfig[var].split(":"))
+                               mylist.extend(filter(None, myconfig[var].split(":")))
                                del myconfig[var] # prepare for env.update(myconfig)
                if mylist:
                        env[var] = ":".join(mylist)
@@ -1530,13 +1521,12 @@ class config:
                self.configdict["pkginternal"]["USE"] = pkginternaluse
                defaults = []
                for i in xrange(len(self.profiles)):
-                       profile_use = self.make_defaults_use[i]
+                       defaults.append(self.make_defaults_use[i])
                        cpdict = self.pkgprofileuse[i].get(cp, None)
                        if cpdict:
                                best_match = best_match_to_list(self.mycpv, cpdict.keys())
                                if best_match:
-                                       profile_use += " " + cpdict[best_match]
-                       defaults.append(profile_use)
+                                       defaults.append(cpdict[best_match])
                self.configdict["defaults"]["USE"] = " ".join(defaults)
                useforce = []
                for i in xrange(len(self.profiles)):
index aa6f0c015a34e6be11875eccb52cd14c7202015c..1266e33df435b7a6b0ee0e8ef342627f154663db 100644 (file)
@@ -252,8 +252,12 @@ class Manifest(object):
                                        else:
                                                raise
                        if update_digest:
-                               write_atomic(dname,
-                               "\n".join(self._createDigestLines1(distlist, self.fhashdict))+"\n")
+                               mylines = self._createDigestLines1(distlist, self.fhashdict)
+                               if mylines:
+                                       mylines = "\n".join(mylines) + "\n"
+                               else:
+                                       mylines = ""
+                               write_atomic(dname, mylines)
                        rval.append(dname)
                return rval
 
index 81f84204469f58e725d18403a7cd0b70f3a6a446..bfa0554e8841e0c36a0c5cd401da0d2b272c9e6a 100644 (file)
@@ -848,9 +848,14 @@ class ConfigProtect(object):
                                os.path.join(self.myroot, x.lstrip(os.path.sep)))
                        mystat = None
                        try:
+                               """Use lstat so that anything, even a broken symlink can be
+                               protected."""
                                if stat.S_ISDIR(os.lstat(ppath).st_mode):
                                        self._dirs.add(ppath)
                                self.protect.append(ppath)
+                               """Now use stat in case this is a symlink to a directory."""
+                               if stat.S_ISDIR(os.stat(ppath).st_mode):
+                                       self._dirs.add(ppath)
                        except OSError:
                                # If it doesn't exist, there's no need to protect it.
                                pass
@@ -861,9 +866,14 @@ class ConfigProtect(object):
                                os.path.join(self.myroot, x.lstrip(os.path.sep)))
                        mystat = None
                        try:
+                               """Use lstat so that anything, even a broken symlink can be
+                               protected."""
                                if stat.S_ISDIR(os.lstat(ppath).st_mode):
                                        self._dirs.add(ppath)
                                self.protectmask.append(ppath)
+                               """Now use stat in case this is a symlink to a directory."""
+                               if stat.S_ISDIR(os.stat(ppath).st_mode):
+                                       self._dirs.add(ppath)
                        except OSError:
                                # If it doesn't exist, there's no need to mask it.
                                pass