tighten up --quiet output for --unmerge
[portage.git] / pym / emerge / __init__.py
index 8d34329870647131b9319a436babc18b338318ab..9372a1dd3aaa6cbc1b8721a750b2dba75b31a0eb 100644 (file)
@@ -51,8 +51,6 @@ from portage.data import secpass
 from portage.util import normalize_path as normpath
 from portage.util import writemsg
 
-if not hasattr(__builtins__, "set"):
-       from sets import Set as set
 from itertools import chain, izip
 from UserDict import DictMixin
 
@@ -630,6 +628,8 @@ class AtomSet(object):
                        best_match = portage.best_match_to_list(cpv_slot, atoms)
                        if best_match:
                                return best_match
+               if not metadata["PROVIDE"]:
+                       return None
                provides = portage.flatten(portage.dep.use_reduce(
                        portage.dep.paren_reduce(metadata["PROVIDE"]),
                        uselist=metadata["USE"].split()))
@@ -674,6 +674,55 @@ class WorldSet(AtomSet):
                portage.locks.unlockfile(self._lock)
                self._lock = None
 
+class RootConfig(object):
+       """This is used internally by depgraph to track information about a
+       particular $ROOT."""
+       def __init__(self, trees):
+               self.trees = trees
+               self.settings = trees["vartree"].settings
+               self.root = self.settings["ROOT"]
+               self.sets = {}
+               world_set = WorldSet(self.settings)
+               world_set.load()
+               self.sets["world"] = world_set
+               system_set = SystemSet(self.settings)
+               self.sets["system"] = system_set
+
+def create_world_atom(pkg_key, metadata, args_set, sets, portdb):
+       """Create a new atom for the world file if one does not exist.  If the
+       argument atom is precise enough to identify a specific slot then a slot
+       atom will be returned. Atoms that are in the system set may also be stored
+       in world since system atoms can only match one slot while world atoms can
+       be greedy with respect to slots.  Unslotted system packages will not be
+       not be stored in world."""
+       arg_atom = args_set.findAtomForPackage(pkg_key, metadata)
+       cp = portage.dep_getkey(arg_atom)
+       new_world_atom = cp
+       available_slots = set(portdb.aux_get(cpv, ["SLOT"])[0] \
+               for cpv in portdb.match(cp))
+       slotted = len(available_slots) > 1 or "0" not in available_slots
+       if slotted and arg_atom != cp:
+               # If the user gave a specific atom, store it as a
+               # slot atom in the world file.
+               slot_atom = "%s:%s" % (cp, metadata["SLOT"])
+               # First verify the slot is in the portage tree to avoid
+               # adding a bogus slot like that produced by multislot.
+               if portdb.match(slot_atom):
+                       # Now verify that the argument is precise enough to identify a
+                       # specific slot.
+                       matches = portdb.match(arg_atom)
+                       matched_slots = set()
+                       for cpv in matches:
+                               matched_slots.add(portdb.aux_get(cpv, ["SLOT"])[0])
+                       if len(matched_slots) == 1:
+                               new_world_atom = slot_atom
+       if new_world_atom == sets["world"].findAtomForPackage(pkg_key, metadata):
+               # Both atoms would be identical, so there's nothing to add.
+               return None
+       if not slotted and sets["system"].findAtomForPackage(pkg_key, metadata):
+               return None
+       return new_world_atom
+
 def filter_iuse_defaults(iuse):
        for flag in iuse:
                if flag.startswith("+") or flag.startswith("-"):
@@ -784,7 +833,7 @@ class FakeVartree(portage.vartree):
                        if os.access(vdb_path, os.W_OK):
                                vdb_lock = portage.locks.lockdir(vdb_path)
                        mykeys = ["SLOT", "COUNTER", "PROVIDE", "USE", "IUSE",
-                               "DEPEND", "RDEPEND", "PDEPEND", "repository"]
+                               "RESTRICT", "DEPEND", "RDEPEND", "PDEPEND", "repository"]
                        real_dbapi = real_vartree.dbapi
                        slot_counters = {}
                        for cpv in real_dbapi.cpv_all():
@@ -1025,9 +1074,10 @@ class depgraph(object):
                self._slot_node_map = {}
                self.mydbapi = {}
                self._mydbapi_keys = ["SLOT", "DEPEND", "RDEPEND", "PDEPEND",
-                       "USE", "IUSE", "PROVIDE", "repository"]
+                       "USE", "IUSE", "PROVIDE", "RESTRICT", "repository"]
                self.useFlags = {}
                self.trees = {}
+               self.roots = {}
                for myroot in trees:
                        self.trees[myroot] = {}
                        for tree in ("porttree", "bintree"):
@@ -1040,6 +1090,7 @@ class depgraph(object):
                        self.pkg_node_map[myroot] = {}
                        self._slot_node_map[myroot] = {}
                        vardb = self.trees[myroot]["vartree"].dbapi
+                       self.roots[myroot] = RootConfig(self.trees[myroot])
                        # This fakedbapi instance will model the state that the vdb will
                        # have after new packages have been installed.
                        fakedb = portage.fakedbapi(settings=self.pkgsettings[myroot])
@@ -1068,10 +1119,15 @@ class depgraph(object):
                self._parent_child_digraph = digraph()
                self.orderedkeys=[]
                self.outdatedpackages=[]
-               self._args_atoms = AtomSet()
-               self._args_nodes = set()
+               # contains all sets added to the graph
                self._sets = {}
-               self._sets_nodes = {}
+               # contains atoms given as arguments
+               self._sets["args"] = AtomSet()
+               # contains all atoms from all sets added to the graph, including
+               # atoms given as arguments
+               self._set_atoms = AtomSet()
+               # contains all nodes pulled in by self._set_atoms
+               self._set_nodes = set()
                self.blocker_digraph = digraph()
                self.blocker_parents = {}
                self._unresolved_blocker_parents = {}
@@ -1211,7 +1267,7 @@ class depgraph(object):
 
                if not arg and myroot == self.target_root:
                        try:
-                               arg = self._args_atoms.findAtomForPackage(mykey, metadata)
+                               arg = self._set_atoms.findAtomForPackage(mykey, metadata)
                        except portage.exception.InvalidDependString, e:
                                if mytype != "installed":
                                        show_invalid_depstring_notice(tuple(mybigkey+["merge"]),
@@ -1321,18 +1377,7 @@ class depgraph(object):
                                        priority=priority)
 
                if arg:
-                       self._args_nodes.add(jbigkey)
-                       try:
-                               for set_name, atom_set in self._sets.iteritems():
-                                       atom = atom_set.findAtomForPackage(mykey, metadata)
-                                       if atom:
-                                               self._sets_nodes[set_name].add(jbigkey)
-                       except portage.exception.InvalidDependString, e:
-                               if mytype != "installed":
-                                       show_invalid_depstring_notice(jbigkey,
-                                               metadata["PROVIDE"], str(e))
-                                       return 0
-                               del e
+                       self._set_nodes.add(jbigkey)
 
                # Do this even when addme is False (--onlydeps) so that the
                # parent/child relationship is always known in case
@@ -1441,25 +1486,38 @@ class depgraph(object):
                                        else:
                                                print "\n\n!!! Binary package '"+str(x)+"' does not exist."
                                                print "!!! Please ensure the tbz2 exists as specified.\n"
-                                               sys.exit(1)
+                                               return 0, myfavorites
                                mytbz2=portage.xpak.tbz2(x)
                                mykey=mytbz2.getelements("CATEGORY")[0]+"/"+os.path.splitext(os.path.basename(x))[0]
                                if os.path.realpath(x) != \
                                        os.path.realpath(self.trees[myroot]["bintree"].getname(mykey)):
                                        print colorize("BAD", "\n*** You need to adjust PKGDIR to emerge this package.\n")
-                                       sys.exit(1)
+                                       return 0, myfavorites
                                if not self.create(["binary", myroot, mykey],
-                                       None, "--onlydeps" not in self.myopts):
+                                       None, "--onlydeps" not in self.myopts,
+                                       myuse=mytbz2.getelements("USE"), arg=x):
                                        return (0,myfavorites)
                                arg_atoms.append((x, "="+mykey))
                        elif ext==".ebuild":
-                               x = os.path.realpath(x)
-                               mykey=os.path.basename(os.path.normpath(x+"/../.."))+"/"+os.path.splitext(os.path.basename(x))[0]
+                               ebuild_path = portage.util.normalize_path(os.path.abspath(x))
+                               pkgdir = os.path.dirname(ebuild_path)
+                               tree_root = os.path.dirname(os.path.dirname(pkgdir))
+                               cp = pkgdir[len(tree_root)+1:]
+                               e = portage.exception.PackageNotFound(
+                                       ("%s is not in a valid portage tree " + \
+                                       "hierarchy or does not exist") % x)
+                               if not portage.isvalidatom(cp):
+                                       raise e
+                               cat = portage.catsplit(cp)[0]
+                               mykey = cat + "/" + os.path.basename(ebuild_path[:-7])
+                               if not portage.isvalidatom("="+mykey):
+                                       raise e
                                ebuild_path = portdb.findname(mykey)
                                if ebuild_path:
-                                       if os.path.realpath(ebuild_path) != x:
+                                       if ebuild_path != os.path.join(os.path.realpath(tree_root),
+                                               cp, os.path.basename(ebuild_path)):
                                                print colorize("BAD", "\n*** You need to adjust PORTDIR or PORTDIR_OVERLAY to emerge this package.\n")
-                                               sys.exit(1)
+                                               return 0, myfavorites
                                        if mykey not in portdb.xmatch(
                                                "match-visible", portage.dep_getkey(mykey)):
                                                print colorize("BAD", "\n*** You are emerging a masked package. It is MUCH better to use")
@@ -1471,7 +1529,7 @@ class depgraph(object):
                                        raise portage.exception.PackageNotFound(
                                                "%s is not in a valid portage tree hierarchy or does not exist" % x)
                                if not self.create(["ebuild", myroot, mykey],
-                                       None, "--onlydeps" not in self.myopts):
+                                       None, "--onlydeps" not in self.myopts, arg=x):
                                        return (0,myfavorites)
                                arg_atoms.append((x, "="+mykey))
                        else:
@@ -1551,13 +1609,17 @@ class depgraph(object):
                                                        greedy_atoms.append((myarg, myslot_atom))
                        arg_atoms = greedy_atoms
 
+               oneshot = "--oneshot" in self.myopts or "--onlydeps" in self.myopts
                """ These are used inside self.create() in order to ensure packages
                that happen to match arguments are not incorrectly marked as nomerge."""
+               args_set = self._sets["args"]
                for myarg, myatom in arg_atoms:
-                       if myatom in self._args_atoms:
+                       if myatom in args_set:
                                continue
-                       self._args_atoms.add(myatom)
-                       myfavorites.append(myatom)
+                       args_set.add(myatom)
+                       self._set_atoms.add(myatom)
+                       if not oneshot:
+                               myfavorites.append(myatom)
                for myarg, myatom in arg_atoms:
                                try:
                                        self.mysd = self.select_dep(myroot, myatom, arg=myarg)
@@ -1660,7 +1722,7 @@ class depgraph(object):
                if not mymerge and arg:
                        # A provided package has been specified on the command line.  The
                        # package will not be merged and a warning will be displayed.
-                       if depstring in self._args_atoms:
+                       if depstring in self._set_atoms:
                                self._pprovided_args.append((arg, depstring))
 
                if myparent:
@@ -2376,24 +2438,15 @@ class depgraph(object):
                        return [x for x in mylist \
                                if x in matches or not portdb.cpv_exists(x)]
                world_problems = False
-               if mode=="system":
-                       system_set = SystemSet(self.settings)
-                       mylist = list(system_set)
-                       self._sets["system"] = system_set
-                       self._sets_nodes["system"] = set()
-               else:
-                       #world mode
-                       world_set = WorldSet(self.settings)
-                       world_set.load()
-                       worldlist = list(world_set)
-                       self._sets["world"] = world_set
-                       self._sets_nodes["world"] = set()
-                       system_set = SystemSet(self.settings)
-                       mylist = list(system_set)
-                       self._sets["system"] = system_set
-                       self._sets_nodes["system"] = set()
 
-                       for x in worldlist:
+               root_config = self.roots[self.target_root]
+               world_set = root_config.sets["world"]
+               system_set = root_config.sets["system"]
+               mylist = list(system_set)
+               self._sets["system"] = system_set
+               if mode == "world":
+                       self._sets["world"] = world_set
+                       for x in world_set:
                                if not portage.isvalidatom(x):
                                        world_problems = True
                                        continue
@@ -2418,9 +2471,12 @@ class depgraph(object):
                        mykey = portage.dep_getkey(atom)
                        if True:
                                newlist.append(atom)
-                               """Make sure all installed slots are updated when possible.
-                               Do this with --emptytree also, to ensure that all slots are
-                               remerged."""
+                               if mode == "system" or atom not in world_set:
+                                       # only world is greedy for slots, not system
+                                       continue
+                               # Make sure all installed slots are updated when possible.
+                               # Do this with --emptytree also, to ensure that all slots are
+                               # remerged.
                                myslots = set()
                                for cpv in vardb.match(mykey):
                                        myslots.add(vardb.aux_get(cpv, ["SLOT"])[0])
@@ -2463,7 +2519,7 @@ class depgraph(object):
                mylist = newlist
 
                for myatom in mylist:
-                       self._args_atoms.add(myatom)
+                       self._set_atoms.add(myatom)
 
                missing_atoms = []
                for mydep in mylist:
@@ -2490,10 +2546,14 @@ class depgraph(object):
 
                return 1
 
-       def display(self,mylist,verbosity=None):
+       def display(self, mylist, favorites=[], verbosity=None):
                if verbosity is None:
                        verbosity = ("--quiet" in self.myopts and 1 or \
                                "--verbose" in self.myopts and 3 or 2)
+               if "--resume" in self.myopts and favorites:
+                       self._sets["args"].update(favorites)
+               favorites_set = AtomSet()
+               favorites_set.update(favorites)
                changelogs=[]
                p=[]
                blockers = []
@@ -2601,7 +2661,7 @@ class depgraph(object):
                                                # Do not traverse to parents if this node is an
                                                # an argument or a direct member of a set that has
                                                # been specified as an argument (system or world).
-                                               if current_node not in self._args_nodes:
+                                               if current_node not in self._set_nodes:
                                                        parent_nodes = mygraph.parent_nodes(current_node)
                                                if parent_nodes:
                                                        child_nodes = set(mygraph.child_nodes(current_node))
@@ -2714,6 +2774,19 @@ class depgraph(object):
                                        else:
                                                # An ebuild "nomerge" node, so USE come from the vardb.
                                                mydbapi = vartree.dbapi
+                               # reuse cached metadata from when the depgraph was built
+                               if "--resume" in self.myopts:
+                                       # Populate the fakedb with relevant metadata, just like
+                                       # would have happened when the depgraph was originally
+                                       # built.
+                                       metadata = dict(izip(self._mydbapi_keys,
+                                               mydbapi.aux_get(pkg_key, self._mydbapi_keys)))
+                                       self.mydbapi[myroot].cpv_inject(pkg_key, metadata=metadata)
+                               else:
+                                       metadata = dict(izip(self._mydbapi_keys,
+                                               self.mydbapi[myroot].aux_get(
+                                               pkg_key, self._mydbapi_keys)))
+                               mydbapi = self.mydbapi[myroot] # use the cached metadata
                                if pkg_key not in self.useFlags[myroot]:
                                        """If this is a --resume then the USE flags need to be
                                        fetched from the appropriate locations here."""
@@ -2729,7 +2802,7 @@ class depgraph(object):
                                                restrict = mydbapi.aux_get(pkg_key, ["RESTRICT"])[0]
                                                show_invalid_depstring_notice(x, restrict, str(e))
                                                del e
-                                               sys.exit(1)
+                                               return 1
                                        restrict = []
                                if "ebuild" == pkg_type and x[3] != "nomerge" and \
                                        "fetch" in restrict:
@@ -2882,7 +2955,7 @@ class depgraph(object):
                                                        src_uri = portdb.aux_get(pkg_key, ["SRC_URI"])[0]
                                                        show_invalid_depstring_notice(x, src_uri, str(e))
                                                        del e
-                                                       sys.exit(1)
+                                                       return 1
                                                if myfilesdict is None:
                                                        myfilesdict="[empty/missing/bad digest]"
                                                else:
@@ -2958,40 +3031,38 @@ class depgraph(object):
                                        myoldbest=blue("["+myoldbest+"]")
 
                                pkg_cp = xs[0]
-                               pkg_arg    = False
-                               pkg_world  = False
+                               root_config = self.roots[myroot]
+                               system_set = root_config.sets["system"]
+                               world_set  = root_config.sets["world"]
+
                                pkg_system = False
-                               pkg_node = tuple(x)
-                               if pkg_node in self._args_nodes:
-                                       pkg_arg = True
-                                       world_nodes = self._sets_nodes.get("world")
-                                       if world_nodes and pkg_node in world_nodes:
-                                               pkg_world = True
-                                       if world_nodes is None:
-                                               # Don't colorize system package when in "world" mode.
-                                               system_nodes = self._sets_nodes.get("system")
-                                               if system_nodes and pkg_node in system_nodes:
-                                                       pkg_system = True
+                               pkg_world = False
+                               try:
+                                       pkg_system = system_set.findAtomForPackage(pkg_key, metadata)
+                                       pkg_world  = world_set.findAtomForPackage(pkg_key, metadata)
+                                       if not pkg_world and myroot == self.target_root and \
+                                               favorites_set.findAtomForPackage(pkg_key, metadata):
+                                               # Maybe it will be added to world now.
+                                               if create_world_atom(pkg_key, metadata,
+                                                       favorites_set, root_config.sets, portdb):
+                                                       pkg_world = True
+                               except portage.exception.InvalidDependString:
+                                       # This is reported elsewhere if relevant.
+                                       pass
 
                                def pkgprint(pkg):
                                        if pkg_merge:
-                                               if pkg_arg:
-                                                       if pkg_world:
-                                                               return colorize("PKG_MERGE_WORLD", pkg)
-                                                       elif pkg_system:
-                                                               return colorize("PKG_MERGE_SYSTEM", pkg)
-                                                       else:
-                                                               return colorize("PKG_MERGE_ARG", pkg)
+                                               if pkg_world:
+                                                       return colorize("PKG_MERGE_WORLD", pkg)
+                                               elif pkg_system:
+                                                       return colorize("PKG_MERGE_SYSTEM", pkg)
                                                else:
                                                        return colorize("PKG_MERGE", pkg)
                                        else:
-                                               if pkg_arg:
-                                                       if pkg_world:
-                                                               return colorize("PKG_NOMERGE_WORLD", pkg)
-                                                       elif pkg_system:
-                                                               return colorize("PKG_NOMERGE_SYSTEM", pkg)
-                                                       else:
-                                                               return colorize("PKG_NOMERGE_ARG", pkg)
+                                               if pkg_world:
+                                                       return colorize("PKG_NOMERGE_WORLD", pkg)
+                                               elif pkg_system:
+                                                       return colorize("PKG_NOMERGE_SYSTEM", pkg)
                                                else:
                                                        return colorize("PKG_NOMERGE", pkg)
 
@@ -3119,6 +3190,7 @@ class depgraph(object):
                                msg.append("The best course of action depends on the reason that an offending\n")
                                msg.append("package.provided entry exists.\n\n")
                        sys.stderr.write("".join(msg))
+               return os.EX_OK
 
        def calc_changelog(self,ebuildpath,current,next):
                if ebuildpath == None or not os.path.exists(ebuildpath):
@@ -3172,62 +3244,41 @@ class depgraph(object):
                        if release.endswith('-r0'):
                                release = release[:-3]
 
-       def saveNomergeFavorites(self, mergelist, favorites):
+       def saveNomergeFavorites(self):
                """Find atoms in favorites that are not in the mergelist and add them
                to the world file if necessary."""
                for x in ("--fetchonly", "--fetch-all-uri",
                        "--oneshot", "--onlydeps", "--pretend"):
                        if x in self.myopts:
                                return
-               favorites_set = AtomSet(favorites)
-               system_set = SystemSet(self.settings)
-               world_set = WorldSet(self.settings)
+               root_config = self.roots[self.target_root]
+               world_set = root_config.sets["world"]
                world_set.lock()
                world_set.load()
-               merge_atoms = set()
-               atom_pkgs = {}
-               for x in mergelist:
-                       pkg_type = x[0]
-                       if pkg_type not in self.pkg_tree_map:
-                               continue
+               args_set = self._sets["args"]
+               portdb = self.trees[self.target_root]["porttree"].dbapi
+               added_favorites = set()
+               for x in self._set_nodes:
                        pkg_type, root, pkg_key, pkg_status = x
-                       if root != self.target_root:
+                       if pkg_status != "nomerge":
                                continue
                        metadata = dict(izip(self._mydbapi_keys,
                                self.mydbapi[root].aux_get(pkg_key, self._mydbapi_keys)))
                        try:
-                               atom = favorites_set.findAtomForPackage(pkg_key, metadata)
+                               myfavkey = create_world_atom(pkg_key, metadata,
+                                       args_set, root_config.sets, portdb)
+                               if myfavkey:
+                                       if myfavkey in added_favorites:
+                                               continue
+                                       added_favorites.add(myfavkey)
+                                       world_set.add(myfavkey)
+                                       print ">>> Recording",myfavkey,"in \"world\" favorites file..."
                        except portage.exception.InvalidDependString, e:
                                writemsg("\n\n!!! '%s' has invalid PROVIDE: %s\n" % \
                                        (pkg_key, str(e)), noiselevel=-1)
                                writemsg("!!! see '%s'\n\n" % os.path.join(
                                        root, portage.VDB_PATH, pkg_key, "PROVIDE"), noiselevel=-1)
                                del e
-                       if atom:
-                               merge_atoms.add(atom)
-               added_favorites = set()
-               root = self.target_root
-               for atom in set(favorites_set).difference(merge_atoms):
-                       pkgs = self.mydbapi[root].match(atom)
-                       for pkg_key in pkgs:
-                               metadata = dict(izip(self._mydbapi_keys,
-                                       self.mydbapi[root].aux_get(pkg_key, self._mydbapi_keys)))
-                               try:
-                                       if not (system_set.findAtomForPackage(pkg_key, metadata) or \
-                                               world_set.findAtomForPackage(pkg_key, metadata)):
-                                               myfavkey = portage.cpv_getkey(pkg_key)
-                                               if myfavkey in added_favorites:
-                                                       continue
-                                               added_favorites.add(myfavkey)
-                                               world_set.add(myfavkey)
-                                               modified = True
-                                               print ">>> Recording",myfavkey,"in \"world\" favorites file..."
-                               except portage.exception.InvalidDependString, e:
-                                       writemsg("\n\n!!! '%s' has invalid PROVIDE: %s\n" % \
-                                               (pkg_key, str(e)), noiselevel=-1)
-                                       writemsg("!!! see '%s'\n\n" % os.path.join(
-                                               root, portage.VDB_PATH, pkg_key, "PROVIDE"), noiselevel=-1)
-                                       del e
                if added_favorites:
                        world_set.save()
                world_set.unlock()
@@ -3308,6 +3359,7 @@ class MergeTask(object):
 
        def merge(self, mylist, favorites, mtimedb):
                from portage.elog import elog_process
+               from portage.elog.filtering import filter_mergephases
                failed_fetches = []
                fetchonly = "--fetchonly" in self.myopts or \
                        "--fetch-all-uri" in self.myopts
@@ -3369,9 +3421,8 @@ class MergeTask(object):
                                del x, mytype, myroot, mycpv, mystatus, quiet_config
                        del shown_verifying_msg, quiet_settings
 
-               #buildsyspkg: I need mysysdict also on resume (moved from the else block)
                system_set = SystemSet(self.settings)
-               favorites_set = AtomSet(favorites)
+               args_set = AtomSet(favorites)
                world_set = WorldSet(self.settings)
                if "--resume" not in self.myopts:
                        mymergelist = mylist
@@ -3468,7 +3519,7 @@ class MergeTask(object):
                        #buildsyspkg: Check if we need to _force_ binary package creation
                        issyspkg = ("buildsyspkg" in myfeat) \
                                        and x[0] != "blocks" \
-                                       and mysysdict.has_key(portage.cpv_getkey(x[2])) \
+                                       and system_set.findAtomForPackage(pkg_key, metadata) \
                                        and "--buildpkg" not in self.myopts
                        if x[0] in ["ebuild","blocks"]:
                                if x[0] == "blocks" and "--fetchonly" not in self.myopts:
@@ -3541,7 +3592,7 @@ class MergeTask(object):
                                                del pkgsettings["PORTAGE_BINPKG_TMPFILE"]
                                                if retval != os.EX_OK or \
                                                        "--buildpkgonly" in self.myopts:
-                                                       elog_process(pkg_key, pkgsettings)
+                                                       elog_process(pkg_key, pkgsettings, phasefilter=filter_mergephases)
                                                if retval != os.EX_OK:
                                                        return retval
                                                bintree = self.trees[myroot]["bintree"]
@@ -3658,13 +3709,13 @@ class MergeTask(object):
                                self.trees[x[1]]["vartree"].inject(x[2])
                                myfavkey = portage.cpv_getkey(x[2])
                                if not fetchonly and not pretend and \
-                                       favorites_set.findAtomForPackage(pkg_key, metadata):
+                                       args_set.findAtomForPackage(pkg_key, metadata):
                                        world_set.lock()
                                        world_set.load()
-                                       #don't record if already in system profile or already recorded
-                                       if not system_set.findAtomForPackage(pkg_key, metadata) and \
-                                               not world_set.findAtomForPackage(pkg_key, metadata):
-                                               #we don't have a favorites entry for this package yet; add one
+                                       myfavkey = create_world_atom(pkg_key, metadata,
+                                               args_set, {"world":world_set, "system":system_set},
+                                               portdb)
+                                       if myfavkey:
                                                world_set.add(myfavkey)
                                                print ">>> Recording",myfavkey,"in \"world\" favorites file..."
                                                emergelog(xterm_titles, " === ("+\
@@ -3987,9 +4038,13 @@ def unmerge(settings, myopts, vartree, unmerge_action, unmerge_files,
                        if "--pretend" not in myopts and "--ask" not in myopts:
                                countdown(int(settings["EMERGE_WARNING_DELAY"]),
                                        colorize("UNMERGE_WARN", "Press Ctrl-C to Stop"))
-               print "\n "+white(x)
+               if "--quiet" not in myopts:
+                       print "\n "+white(x)
+               else:
+                       print white(x)+": ",
                for mytype in ["selected","protected","omitted"]:
-                       portage.writemsg_stdout((mytype + ": ").rjust(14), noiselevel=-1)
+                       if "--quiet" not in myopts:
+                               portage.writemsg_stdout((mytype + ": ").rjust(14), noiselevel=-1)
                        if pkgmap[x][mytype]:
                                for mypkg in pkgmap[x][mytype]:
                                        mysplit=portage.catpkgsplit(mypkg)
@@ -4004,7 +4059,10 @@ def unmerge(settings, myopts, vartree, unmerge_action, unmerge_files,
                                                portage.writemsg_stdout(
                                                        colorize("GOOD", myversion + " "), noiselevel=-1)
                        else:
-                               portage.writemsg_stdout("none", noiselevel=-1)
+                               portage.writemsg_stdout("none ", noiselevel=-1)
+                       if "--quiet" not in myopts:
+                               portage.writemsg_stdout("\n", noiselevel=-1)
+               if "--quiet" in myopts:
                        portage.writemsg_stdout("\n", noiselevel=-1)
 
        portage.writemsg_stdout("\n>>> " + colorize("UNMERGE_WARN", "'Selected'") + \
@@ -4768,9 +4826,8 @@ def action_sync(settings, trees, mtimedb, myopts, myaction):
                print
                print red(" * ")+bold("An update to portage is available.")+" It is _highly_ recommended"
                print red(" * ")+"that you update portage now, before any other packages are updated."
-               print red(" * ")+"Please run 'emerge portage' and then update "+bold("ALL")+" of your"
-               print red(" * ")+"configuration files."
-               print red(" * ")+"To update portage, run 'emerge portage'."
+               print
+               print red(" * ")+"To update portage, run 'emerge portage' now."
                print
        
        display_news_notification(trees)
@@ -5181,10 +5238,11 @@ def action_depclean(settings, trees, ldpath_mtimes,
        vardb = dep_check_trees[myroot]["vartree"].dbapi
        # Constrain dependency selection to the installed packages.
        dep_check_trees[myroot]["porttree"] = dep_check_trees[myroot]["vartree"]
-       syslist = getlist(settings, "system")
-       worldlist = getlist(settings, "world")
-       system_and_world = AtomSet(syslist)
-       system_and_world.update(worldlist)
+       system_set = SystemSet(settings)
+       syslist = list(system_set)
+       world_set = WorldSet(settings)
+       world_set.load()
+       worldlist = list(world_set)
        fakedb = portage.fakedbapi(settings=settings)
        myvarlist = vardb.cpv_all()
 
@@ -5222,29 +5280,11 @@ def action_depclean(settings, trees, ldpath_mtimes,
                        if not atom.startswith("!") and priority == hard:
                                unresolveable.setdefault(atom, []).append(parent)
                        continue
-               system_or_world = False
-               if parent in ('world', 'system'):
-                       system_or_world = True
-               else:
-                       for pkg in pkgs:
-                               metadata = dict(izip(metadata_keys,
-                                       vardb.aux_get(pkg, metadata_keys)))
-                               try:
-                                       if system_and_world.findAtomForPackage(pkg, metadata):
-                                               system_or_world = True
-                                               break
-                               except portage.exception.InvalidDependString, e:
-                                       writemsg("\n\n!!! '%s' has invalid PROVIDE: %s\n" % \
-                                               (pkg, str(e)), noiselevel=-1)
-                                       writemsg("!!! see '%s'\n\n" % os.path.join(
-                                               myroot, portage.VDB_PATH, pkg, "PROVIDE"),
-                                               noiselevel=-1)
-                                       del e
-               if not system_or_world:
+               if len(pkgs) > 1 and parent != "world":
                        # Prune all but the best matching slot, since that's all that a
-                       # deep world update would pull in.  Don't prune if the cpv is in
-                       # system or world though, since those sets trigger greedy update
-                       # of all slots.
+                       # 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.
                        pkgs = [portage.best(pkgs)]
                for pkg in pkgs:
                        if fakedb.cpv_exists(pkg):
@@ -5392,7 +5432,7 @@ def action_build(settings, trees, mtimedb,
        else:
                if ("--resume" in myopts):
                        print darkgreen("emerge: It seems we have nothing to resume...")
-                       sys.exit(0)
+                       return os.EX_OK
 
                myparams = create_depgraph_params(myopts, myaction)
                if myaction in ["system","world"]:
@@ -5402,7 +5442,7 @@ def action_build(settings, trees, mtimedb,
                        mydepgraph = depgraph(settings, trees, myopts, myparams, spinner)
                        if not mydepgraph.xcreate(myaction):
                                print "!!! Depgraph creation failed."
-                               sys.exit(1)
+                               return 1
                        if "--quiet" not in myopts and "--nodeps" not in myopts:
                                print "\b\b... done!"
                else:
@@ -5414,9 +5454,9 @@ def action_build(settings, trees, mtimedb,
                                retval, favorites = mydepgraph.select_files(myfiles)
                        except portage.exception.PackageNotFound, e:
                                portage.writemsg("\n!!! %s\n" % str(e), noiselevel=-1)
-                               sys.exit(1)
+                               return 1
                        if not retval:
-                               sys.exit(1)
+                               return 1
                        if "--quiet" not in myopts and "--nodeps" not in myopts:
                                print "\b\b... done!"
 
@@ -5427,7 +5467,7 @@ def action_build(settings, trees, mtimedb,
                        for x in mydepgraph.missingbins:
                                sys.stderr.write("   "+str(x)+"\n")
                        sys.stderr.write("\nThese are required by '--usepkgonly' -- Terminating.\n\n")
-                       sys.exit(1)
+                       return 1
 
        if "--pretend" not in myopts and \
                ("--ask" in myopts or "--tree" in myopts or \
@@ -5440,12 +5480,18 @@ def action_build(settings, trees, mtimedb,
                                mymergelist = mymergelist[1:]
                        if len(mymergelist) == 0:
                                print colorize("INFORM", "emerge: It seems we have nothing to resume...")
-                               sys.exit(0)
-                       mydepgraph.display(mymergelist)
+                               return os.EX_OK
+                       favorites = mtimedb["resume"]["favorites"]
+                       retval = mydepgraph.display(mymergelist, favorites=favorites)
+                       if retval != os.EX_OK:
+                               return retval
                        prompt="Would you like to resume merging these packages?"
                else:
-                       mydepgraph.display(
-                               mydepgraph.altlist(reversed=("--tree" in myopts)))
+                       retval = mydepgraph.display(
+                               mydepgraph.altlist(reversed=("--tree" in myopts)),
+                               favorites=favorites)
+                       if retval != os.EX_OK:
+                               return retval
                        mergecount=0
                        for x in mydepgraph.altlist():
                                if x[0] != "blocks" and x[3] != "nomerge":
@@ -5456,7 +5502,7 @@ def action_build(settings, trees, mtimedb,
                                        print   "!!!        at the same time on the same system."
                                        if "--quiet" not in myopts:
                                                show_blocker_docs_link()
-                                       sys.exit(1)
+                                       return 1
                        if mergecount==0:
                                if "--noreplace" in myopts and favorites:
                                        print
@@ -5469,7 +5515,7 @@ def action_build(settings, trees, mtimedb,
                                        print
                                        print "Nothing to merge; quitting."
                                        print
-                                       sys.exit(0)
+                                       return os.EX_OK
                        elif "--fetchonly" in myopts or "--fetch-all-uri" in myopts:
                                prompt="Would you like to fetch the source files for these packages?"
                        else:
@@ -5479,7 +5525,7 @@ def action_build(settings, trees, mtimedb,
                        print
                        print "Quitting."
                        print
-                       sys.exit(0)
+                       return os.EX_OK
                # Don't ask again (e.g. when auto-cleaning packages after merge)
                myopts.pop("--ask", None)
 
@@ -5491,22 +5537,28 @@ def action_build(settings, trees, mtimedb,
                                mymergelist = mymergelist[1:]
                        if len(mymergelist) == 0:
                                print colorize("INFORM", "emerge: It seems we have nothing to resume...")
-                               sys.exit(0)
-                       mydepgraph.display(mymergelist)
+                               return os.EX_OK
+                       favorites = mtimedb["resume"]["favorites"]
+                       retval = mydepgraph.display(mymergelist, favorites=favorites)
+                       if retval != os.EX_OK:
+                               return retval
                else:
-                       mydepgraph.display(
-                               mydepgraph.altlist(reversed=("--tree" in myopts)))
+                       retval = mydepgraph.display(
+                               mydepgraph.altlist(reversed=("--tree" in myopts)),
+                               favorites=favorites)
+                       if retval != os.EX_OK:
+                               return retval
                        if "--buildpkgonly" in myopts and \
                                not mydepgraph.digraph.hasallzeros(ignore_priority=DepPriority.MEDIUM):
                                        print "\n!!! --buildpkgonly requires all dependencies to be merged."
                                        print "!!! You have to merge the dependencies before you can build this package.\n"
-                                       sys.exit(1)
+                                       return 1
        else:
                if ("--buildpkgonly" in myopts):
                        if not mydepgraph.digraph.hasallzeros(ignore_priority=DepPriority.MEDIUM):
                                print "\n!!! --buildpkgonly requires all dependencies to be merged."
                                print "!!! Cannot merge requested packages. Merge deps and try again.\n"
-                               sys.exit(1)
+                               return 1
 
                if ("--resume" in myopts):
                        favorites=mtimedb["resume"]["favorites"]
@@ -5520,7 +5572,7 @@ def action_build(settings, trees, mtimedb,
                        retval = mergetask.merge(
                                mtimedb["resume"]["mergelist"], favorites, mtimedb)
                        if retval != os.EX_OK:
-                               sys.exit(retval)
+                               return retval
                else:
                        if "resume" in mtimedb and \
                        "mergelist" in mtimedb["resume"] and \
@@ -5554,12 +5606,12 @@ def action_build(settings, trees, mtimedb,
                        else:
                                pkglist = mydepgraph.altlist()
                        if favorites:
-                               mydepgraph.saveNomergeFavorites(pkglist, favorites)
+                               mydepgraph.saveNomergeFavorites()
                        del mydepgraph
                        mergetask = MergeTask(settings, trees, myopts)
                        retval = mergetask.merge(pkglist, favorites, mtimedb)
                        if retval != os.EX_OK:
-                               sys.exit(retval)
+                               return retval
 
                if mtimedb.has_key("resume"):
                        del mtimedb["resume"]
@@ -5879,7 +5931,6 @@ def emerge_main():
        for x in myfiles:
                ext = os.path.splitext(x)[1]
                if (ext == ".ebuild" or ext == ".tbz2") and os.path.exists(os.path.abspath(x)):
-                       print "emerging by path implies --oneshot... adding --oneshot to options."
                        print colorize("BAD", "\n*** emerging by path is broken and may not always work!!!\n")
                        break
 
@@ -6091,12 +6142,13 @@ def emerge_main():
                validate_ebuild_environment(trees)
                if "--pretend" not in myopts:
                        display_news_notification(trees)
-               action_build(settings, trees, mtimedb,
+               retval = action_build(settings, trees, mtimedb,
                        myopts, myaction, myfiles, spinner)
                if "--pretend" not in myopts:
-                       post_emerge(trees, mtimedb, os.EX_OK)
+                       post_emerge(trees, mtimedb, retval)
                else:
                        display_news_notification(trees)
+               return retval
 
 if __name__ == "__main__":
        retval = emerge_main()