For bug #183167, explicitly call elog_process() when --buildpkgonly is enabled.
[portage.git] / pym / emerge / __init__.py
index 7ca5ece18260a5c5d3dc7a4d56052ad68260d10c..57447e8f1b63867ad4a4528dabd4eb155d6b8ba5 100644 (file)
@@ -31,7 +31,7 @@ except ImportError:
        import portage
 del os.environ["PORTAGE_LEGACY_GLOBALS"]
 from portage import digraph, portdbapi
-from portage.const import NEWS_LIB_PATH
+from portage.const import NEWS_LIB_PATH, CACHE_PATH, PRIVATE_PATH
 
 import emerge.help
 import portage.xpak, commands, errno, re, socket, time, types
@@ -49,6 +49,7 @@ import portage.locks
 import portage.exception
 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
@@ -207,7 +208,7 @@ def emergelog(xterm_titles, mystr, short_msg=None):
        if xterm_titles:
                if short_msg == None:
                        short_msg = mystr
-               if "HOSTNAME" in os.environ.keys():
+               if "HOSTNAME" in os.environ:
                        short_msg = os.environ["HOSTNAME"]+": "+short_msg
                xtermTitle(short_msg)
        try:
@@ -361,7 +362,7 @@ def create_depgraph_params(myopts, myaction):
        return myparams
 
 # search functionality
-class search:
+class search(object):
 
        #
        # class constants
@@ -398,10 +399,10 @@ class search:
                print "Searching...   ",
 
                regexsearch = False
-               if self.searchkey[0] == '%':
+               if self.searchkey.startswith('%'):
                        regexsearch = True
                        self.searchkey = self.searchkey[1:]
-               if self.searchkey[0] == '@':
+               if self.searchkey.startswith('@'):
                        match_category = 1
                        self.searchkey = self.searchkey[1:]
                if regexsearch:
@@ -440,7 +441,7 @@ class search:
                                if self.searchre.search(full_desc):
                                        self.matches["desc"].append([full_package,masked])
                self.mlen=0
-               for mtype in self.matches.keys():
+               for mtype in self.matches:
                        self.matches[mtype].sort()
                        self.mlen += len(self.matches[mtype])
 
@@ -449,7 +450,7 @@ class search:
                print "\b\b  \n[ Results for search key : "+white(self.searchkey)+" ]"
                print "[ Applications found : "+white(str(self.mlen))+" ]"
                print " "
-               for mtype in self.matches.keys():
+               for mtype in self.matches:
                        for match,masked in self.matches[mtype]:
                                if mtype=="pkg":
                                        catpack=match
@@ -585,10 +586,12 @@ def clean_world(vardb, cpv):
                else:
                        #this doesn't match the package we're unmerging; keep it.
                        newworldlist.append(x)
-
+       
+       newworldlist.sort()
+       
        portage.util.ensure_dirs(os.path.join(vardb.root, portage.PRIVATE_PATH),
                gid=portage.portage_gid, mode=02770)
-       portage.util.write_atomic(world_filename, "\n".join(newworldlist))
+       portage.util.write_atomic(world_filename, "\n".join(newworldlist)+"\n")
 
 def genericdict(mylist):
        mynewdict={}
@@ -598,7 +601,7 @@ def genericdict(mylist):
 
 def filter_iuse_defaults(iuse):
        for flag in iuse:
-               if flag.startswith("+"):
+               if flag.startswith("+") or flag.startswith("-"):
                        yield flag[1:]
                else:
                        yield flag
@@ -909,7 +912,7 @@ def show_invalid_depstring_notice(parent_node, depstring, error_msg):
                f.add_flowing_data(x)
        f.end_paragraph(1)
 
-class depgraph:
+class depgraph(object):
 
        pkg_tree_map = {
                "ebuild":"porttree",
@@ -977,6 +980,7 @@ class depgraph:
                self.args_keys = []
                self.blocker_digraph = digraph()
                self.blocker_parents = {}
+               self._unresolved_blocker_parents = {}
                self._slot_collision_info = []
                # Slot collision nodes are not allowed to block other packages since
                # blocker validation is only able to account for one package per slot.
@@ -1078,8 +1082,7 @@ class depgraph:
                                self.blocker_parents.setdefault(
                                        tuple(mybigkey), set()).add(myparent)
                        return 1
-               if not arg and myroot == self.target_root:
-                       arg = portage.best_match_to_list(mykey, self.args_keys)
+
                # select the correct /var database that we'll be checking against
                vardbapi = self.trees[myroot]["vartree"].dbapi
                portdb = self.trees[myroot]["porttree"].dbapi
@@ -1091,6 +1094,10 @@ class depgraph:
 
                mydbapi = self.trees[myroot][self.pkg_tree_map[mytype]].dbapi
 
+               if not arg and myroot == self.target_root:
+                       cpv_slot = "%s:%s" % (mykey, mydbapi.aux_get(mykey, ["SLOT"])[0])
+                       arg = portage.best_match_to_list(cpv_slot, self.args_keys)
+
                if myuse is None:
                        self.pkgsettings[myroot].setcpv(mykey, mydb=portdb)
                        myuse = self.pkgsettings[myroot]["USE"].split()
@@ -1676,7 +1683,11 @@ class depgraph:
                                                                                print filename+":"
                                                                                print comment
                                                                                oldcomment = comment
-                                                               licenses = portdb.aux_get(p, ["LICENSE"])[0]
+                                                               try:
+                                                                       licenses = portdb.aux_get(p, ["LICENSE"])[0]
+                                                               except KeyError:
+                                                                       # Corruption will have been reported above.
+                                                                       continue
                                                                uselist = []
                                                                if "?" in licenses:
                                                                        pkgsettings.setcpv(p, mydb=portdb)
@@ -1899,7 +1910,6 @@ class depgraph:
                                blocked_slots_final[cpv] = \
                                        "%s:%s" % (portage.dep_getkey(cpv),
                                                final_db.aux_get(cpv, ["SLOT"])[0])
-                       blocked_slots_final_values = set(blocked_slots_final.itervalues())
                        for parent in list(self.blocker_parents[blocker]):
                                ptype, proot, pcpv, pstatus = parent
                                pdbapi = self.trees[proot][self.pkg_tree_map[ptype]].dbapi
@@ -1911,10 +1921,10 @@ class depgraph:
                                for cpv in blocked_initial:
                                        slot_atom = blocked_slots_initial[cpv]
                                        if slot_atom == pslot_atom:
-                                               # The parent blocks an initial package in the same
-                                               # slot as itself.  The merge/nomerge status of neither
-                                               # node matters.  In any case, this particular block is
-                                               # automatically resolved.
+                                               # TODO: Support blocks within slots in cases where it
+                                               # might make sense.  For example, a new version might
+                                               # require that the old version be uninstalled at build
+                                               # time.
                                                continue
                                        if parent_static and \
                                                slot_atom not in modified_slots[myroot]:
@@ -1922,36 +1932,50 @@ class depgraph:
                                                # merge of either package is triggered.
                                                continue
                                        if pstatus == "merge" and \
-                                               slot_atom not in blocked_slots_final_values:
-                                               upgrade_matches = final_db.match(slot_atom)
-                                               if upgrade_matches:
-                                                       # Apparently an upgrade may be able to invalidate
-                                                       # this block.
-                                                       upgrade_node = \
-                                                               self.pkg_node_map[proot][upgrade_matches[0]]
-                                                       depends_on_order.add(upgrade_node)
-                                                       continue
+                                               slot_atom in modified_slots[myroot]:
+                                               replacement = final_db.match(slot_atom)
+                                               if replacement:
+                                                       if not portage.match_from_list(mydep, replacement):
+                                                               # Apparently a replacement may be able to
+                                                               # invalidate this block.
+                                                               replacement_node = \
+                                                                       self.pkg_node_map[proot][replacement[0]]
+                                                               depends_on_order.add((replacement_node, parent))
+                                                               continue
                                        # None of the above blocker resolutions techniques apply,
                                        # so apparently this one is unresolvable.
                                        unresolved_blocks = True
                                for cpv in blocked_final:
                                        slot_atom = blocked_slots_final[cpv]
                                        if slot_atom == pslot_atom:
-                                               # The parent blocks itself, so the merge order does not
-                                               # need to be enforced.
+                                               # TODO: Support blocks within slots.
                                                continue
                                        if parent_static and \
                                                slot_atom not in modified_slots[myroot]:
                                                # This blocker will be handled the next time that a
                                                # merge of either package is triggered.
                                                continue
+                                       if not parent_static and pstatus == "nomerge" and \
+                                               slot_atom in modified_slots[myroot]:
+                                               replacement = final_db.match(pslot_atom)
+                                               if replacement:
+                                                       replacement_node = \
+                                                               self.pkg_node_map[proot][replacement[0]]
+                                                       if replacement_node not in \
+                                                               self.blocker_parents[blocker]:
+                                                               # Apparently a replacement may be able to
+                                                               # invalidate this block.
+                                                               blocked_node = self.pkg_node_map[proot][cpv]
+                                                               depends_on_order.add(
+                                                                       (replacement_node, blocked_node))
+                                                               continue
                                        # None of the above blocker resolutions techniques apply,
                                        # so apparently this one is unresolvable.
                                        unresolved_blocks = True
                                if not unresolved_blocks and depends_on_order:
-                                       for node in depends_on_order:
+                                       for node, pnode in depends_on_order:
                                                # Enforce correct merge order with a hard dep.
-                                               self.digraph.addnode(node, parent,
+                                               self.digraph.addnode(node, pnode,
                                                        priority=DepPriority(buildtime=True))
                                                # Count references to this blocker so that it can be
                                                # invalidated after nodes referencing it have been
@@ -1959,6 +1983,9 @@ class depgraph:
                                                self.blocker_digraph.addnode(node, blocker)
                                if not unresolved_blocks and not depends_on_order:
                                        self.blocker_parents[blocker].remove(parent)
+                               if unresolved_blocks:
+                                       self._unresolved_blocker_parents.setdefault(
+                                               blocker, set()).add(parent)
                        if not self.blocker_parents[blocker]:
                                del self.blocker_parents[blocker]
                # Validate blockers that depend on merge order.
@@ -2095,8 +2122,16 @@ class depgraph:
                                        if not circular_blocks:
                                                circular_blocks = True
                                                blocker_deps = myblockers.leaf_nodes()
-                                       if blocker_deps:
-                                               selected_nodes = [blocker_deps.pop()]
+                                       while blocker_deps:
+                                               # Some of these nodes might have already been selected
+                                               # by the normal node selection process after the
+                                               # circular_blocks flag has been set.  Therefore, we
+                                               # have to verify that they're still in the graph so
+                                               # that they're not selected more than once.
+                                               node = blocker_deps.pop()
+                                               if mygraph.contains(node):
+                                                       selected_nodes = [node]
+                                                       break
 
                        if not selected_nodes:
                                # No leaf nodes are available, so we have a circular
@@ -2145,7 +2180,12 @@ class depgraph:
                                        for blocker in myblockers.root_nodes():
                                                if not myblockers.child_nodes(blocker):
                                                        myblockers.remove(blocker)
-                                                       del self.blocker_parents[blocker]
+                                                       unresolved = \
+                                                               self._unresolved_blocker_parents.get(blocker)
+                                                       if unresolved:
+                                                               self.blocker_parents[blocker] = unresolved
+                                                       else:
+                                                               del self.blocker_parents[blocker]
 
                if not reversed:
                        """Blocker validation does not work with reverse mode,
@@ -2176,13 +2216,25 @@ class depgraph:
                        mylist = getlist(self.settings, "system")
                        worlddict=genericdict(worldlist)
 
-                       for x in worlddict.keys():
+                       for x in worlddict:
                                if not portage.isvalidatom(x):
                                        world_problems = True
-                               elif not self.trees[self.target_root]["vartree"].dbapi.match(x):
+                                       continue
+                               elif not vardb.match(x):
                                        world_problems = True
-                               else:
-                                       mylist.append(x)
+                                       available = False
+                                       if "--usepkgonly" not in self.myopts and \
+                                               portdb.match(x):
+                                               available = True
+                                       elif "--usepkg" in self.myopts:
+                                               mymatches = bindb.match(x)
+                                               if "--usepkgonly" not in self.myopts:
+                                                       mymatches = visible(mymatches)
+                                               if mymatches:
+                                                       available = True
+                                       if not available:
+                                               continue
+                               mylist.append(x)
 
                newlist = []
                for atom in mylist:
@@ -2345,6 +2397,7 @@ class depgraph:
                mygraph = self._parent_child_digraph
                i = 0
                depth = 0
+               shown_edges = set()
                for x in mylist:
                        if "blocks" == x[0]:
                                display_list.append((x, 0, True))
@@ -2359,6 +2412,7 @@ class depgraph:
                                        tree_nodes = tree_nodes[:depth]
                                        tree_nodes.append(graph_key)
                                        display_list.append((x, depth, True))
+                                       shown_edges.add((graph_key, tree_nodes[depth-1]))
                                else:
                                        traversed_nodes = set() # prevent endless circles
                                        traversed_nodes.add(graph_key)
@@ -2371,15 +2425,22 @@ class depgraph:
                                                        for node in parent_nodes:
                                                                if node not in traversed_nodes and \
                                                                        node not in child_nodes:
+                                                                       edge = (current_node, node)
+                                                                       if edge in shown_edges:
+                                                                               continue
                                                                        selected_parent = node
                                                                        break
                                                        if not selected_parent:
                                                                # A direct cycle is unavoidable.
                                                                for node in parent_nodes:
                                                                        if node not in traversed_nodes:
+                                                                               edge = (current_node, node)
+                                                                               if edge in shown_edges:
+                                                                                       continue
                                                                                selected_parent = node
                                                                                break
                                                        if selected_parent:
+                                                               shown_edges.add((current_node, selected_parent))
                                                                traversed_nodes.add(selected_parent)
                                                                add_parents(selected_parent, False)
                                                display_list.append((list(current_node),
@@ -2411,10 +2472,14 @@ class depgraph:
                                depth >= mylist[i+1][1]:
                                        del mylist[i]
 
+               from portage import flatten
+               from portage.dep import use_reduce, paren_reduce
                display_overlays=False
                # files to fetch list - avoids counting a same file twice
                # in size display (verbose mode)
                myfetchlist=[]
+               worldlist = set(getlist(self.settings, "world"))
+
                for mylist_index in xrange(len(mylist)):
                        x, depth, ordered = mylist[mylist_index]
                        pkg_type = x[0]
@@ -2449,6 +2514,8 @@ class depgraph:
                                blockers.append(addl)
                        else:
                                mydbapi = self.trees[myroot][self.pkg_tree_map[pkg_type]].dbapi
+                               pkg_status = x[3]
+                               pkg_merge = ordered and pkg_status != "nomerge"
                                binary_package = True
                                if "ebuild" == pkg_type:
                                        if "merge" == x[3] or \
@@ -2469,9 +2536,19 @@ class depgraph:
                                        self.useFlags[myroot][pkg_key] = mydbapi.aux_get(
                                                pkg_key, ["USE"])[0].split()
 
+                               try:
+                                       restrict = flatten(use_reduce(paren_reduce(
+                                               mydbapi.aux_get(pkg_key, ["RESTRICT"])[0]),
+                                               uselist=self.useFlags[myroot][pkg_key]))
+                               except portage.exception.InvalidDependString, e:
+                                       if pkg_status != "nomerge":
+                                               restrict = mydbapi.aux_get(pkg_key, ["RESTRICT"])[0]
+                                               show_invalid_depstring_notice(x, restrict, str(e))
+                                               del e
+                                               sys.exit(1)
+                                       restrict = []
                                if "ebuild" == pkg_type and x[3] != "nomerge" and \
-                                       "fetch" in portdb.aux_get(
-                                       x[2], ["RESTRICT"])[0].split():
+                                       "fetch" in restrict:
                                        fetch = red("F")
                                        if ordered:
                                                counters.restrict_fetch += 1
@@ -2612,7 +2689,7 @@ class depgraph:
                                if verbosity == 3:
                                        # size verbose
                                        mysize=0
-                                       if x[0] == "ebuild" and ordered and x[-1] != "nomerge":
+                                       if pkg_type == "ebuild" and pkg_merge:
                                                try:
                                                        myfilesdict = portdb.getfetchsizes(pkg_key,
                                                                useflags=self.useFlags[myroot][pkg_key],
@@ -2625,7 +2702,7 @@ class depgraph:
                                                if myfilesdict is None:
                                                        myfilesdict="[empty/missing/bad digest]"
                                                else:
-                                                       for myfetchfile in myfilesdict.keys():
+                                                       for myfetchfile in myfilesdict:
                                                                if myfetchfile not in myfetchlist:
                                                                        mysize+=myfilesdict[myfetchfile]
                                                                        myfetchlist.append(myfetchfile)
@@ -2645,7 +2722,7 @@ class depgraph:
                                        else:
                                                verboseadd += "[No ebuild?]"
 
-                               xs=portage.pkgsplit(x[2])
+                               xs = list(portage.pkgsplit(x[2]))
                                if xs[2]=="r0":
                                        xs[2]=""
                                else:
@@ -2672,17 +2749,32 @@ class depgraph:
                                                myoldbest=myoldbest[:-3]
                                        myoldbest=blue("["+myoldbest+"]")
 
+                               pkg_cp = xs[0]
+                               pkg_world = pkg_cp in worldlist
+
+                               def pkgprint(pkg):
+                                       if pkg_merge:
+                                               if pkg_world:
+                                                       return colorize("PKG_MERGE_WORLD", pkg)
+                                               else:
+                                                       return colorize("PKG_MERGE", pkg)
+                                       else:
+                                               if pkg_world:
+                                                       return colorize("PKG_NOMERGE_WORLD", pkg)
+                                               else:
+                                                       return colorize("PKG_NOMERGE", pkg)
+
                                if x[1]!="/":
                                        if myoldbest:
                                                myoldbest +=" "
                                        if "--columns" in self.myopts:
                                                if "--quiet" in self.myopts:
-                                                       myprint=addl+" "+indent+darkgreen(xs[0])
+                                                       myprint=addl+" "+indent+pkgprint(pkg_cp)
                                                        myprint=myprint+darkblue(" "+xs[1]+xs[2])+" "
                                                        myprint=myprint+myoldbest
                                                        myprint=myprint+darkgreen("to "+x[1])
                                                else:
-                                                       myprint="["+x[0]+" "+addl+"] "+indent+darkgreen(xs[0])
+                                                       myprint="["+pkgprint(pkg_type)+" "+addl+"] "+indent+pkgprint(pkg_cp)
                                                        if (newlp-nc_len(myprint)) > 0:
                                                                myprint=myprint+(" "*(newlp-nc_len(myprint)))
                                                        myprint=myprint+"["+darkblue(xs[1]+xs[2])+"] "
@@ -2691,21 +2783,21 @@ class depgraph:
                                                        myprint=myprint+myoldbest
                                                        myprint=myprint+darkgreen("to "+x[1])+" "+verboseadd
                                        else:
-                                               if x[-1] == "nomerge" or not ordered:
-                                                       myprint = darkblue("[nomerge      ] ")
+                                               if not pkg_merge:
+                                                       myprint = "[%s      ] " % pkgprint("nomerge")
                                                else:
                                                        myprint = "[" + pkg_type + " " + addl + "] "
-                                               myprint += indent + darkgreen(pkg_key) + " " + \
+                                               myprint += indent + pkgprint(pkg_key) + " " + \
                                                        myoldbest + darkgreen("to " + myroot) + " " + \
                                                        verboseadd
                                else:
                                        if "--columns" in self.myopts:
                                                if "--quiet" in self.myopts:
-                                                       myprint=addl+" "+indent+darkgreen(xs[0])
+                                                       myprint=addl+" "+indent+pkgprint(pkg_cp)
                                                        myprint=myprint+" "+green(xs[1]+xs[2])+" "
                                                        myprint=myprint+myoldbest
                                                else:
-                                                       myprint="["+x[0]+" "+addl+"] "+indent+darkgreen(xs[0])
+                                                       myprint="["+pkgprint(pkg_type)+" "+addl+"] "+indent+pkgprint(pkg_cp)
                                                        if (newlp-nc_len(myprint)) > 0:
                                                                myprint=myprint+(" "*(newlp-nc_len(myprint)))
                                                        myprint=myprint+green(" ["+xs[1]+xs[2]+"] ")
@@ -2713,10 +2805,10 @@ class depgraph:
                                                                myprint=myprint+(" "*(oldlp-nc_len(myprint)))
                                                        myprint=myprint+myoldbest+"  "+verboseadd
                                        else:
-                                               if x[-1] == "nomerge" or not ordered:
-                                                       myprint=darkblue("[nomerge      ] "+indent+x[2]+" "+myoldbest+" ")+verboseadd
+                                               if not pkg_merge:
+                                                       myprint="["+pkgprint("nomerge")+"      ] "+indent+pkgprint(pkg_key)+" "+myoldbest+" "+verboseadd
                                                else:
-                                                       myprint="["+x[0]+" "+addl+"] "+indent+darkgreen(x[2])+" "+myoldbest+" "+verboseadd
+                                                       myprint="["+pkgprint(pkg_type)+" "+addl+"] "+indent+pkgprint(pkg_key)+" "+myoldbest+" "+verboseadd
                                p.append(myprint)
 
                        mysplit = portage.pkgsplit(x[2])
@@ -2774,10 +2866,14 @@ class depgraph:
                        sys.stderr.write("".join(msg))
 
        def calc_changelog(self,ebuildpath,current,next):
+               if ebuildpath == None or not os.path.exists(ebuildpath):
+                       return []
                current = '-'.join(portage.catpkgsplit(current)[1:])
-               if current.endswith('-r0'): current = current[:-3]
+               if current.endswith('-r0'):
+                       current = current[:-3]
                next = '-'.join(portage.catpkgsplit(next)[1:])
-               if next.endswith('-r0'): next = next[:-3]
+               if next.endswith('-r0'):
+                       next = next[:-3]
                changelogpath = os.path.join(os.path.split(ebuildpath)[0],'ChangeLog')
                try:
                        changelog = open(changelogpath).read()
@@ -2899,7 +2995,10 @@ class MergeTask(object):
                                portage.config(clone=trees["/"]["vartree"].settings)
 
        def merge(self, mylist, favorites, mtimedb):
+               from portage.elog import elog_process
                failed_fetches = []
+               fetchonly = "--fetchonly" in self.myopts or \
+                       "--fetch-all-uri" in self.myopts
                mymergelist=[]
                ldpath_mtimes = mtimedb["ldpath"]
                xterm_titles = "notitles" not in self.settings.features
@@ -2984,7 +3083,7 @@ class MergeTask(object):
                                "--pretend" in self.myopts):
                                portage.write_atomic(
                                        os.path.join(self.target_root, portage.WORLD_FILE),
-                                       "\n".join(myfavdict.values()))
+                                               "\n".join(sorted(myfavdict.values())) + "\n")
 
                        mtimedb["resume"]["mergelist"]=mymergelist[:]
                        mtimedb.commit()
@@ -3122,14 +3221,20 @@ class MergeTask(object):
                                                        (mergecount, len(mymergelist), pkg_key)
                                                emergelog(xterm_titles, msg, short_msg=short_msg)
                                                self.trees[myroot]["bintree"].prevent_collision(pkg_key)
+                                               binpkg_tmpfile = os.path.join(pkgsettings["PKGDIR"],
+                                                       pkg_key + ".tbz2." + str(os.getpid()))
+                                               pkgsettings["PORTAGE_BINPKG_TMPFILE"] = binpkg_tmpfile
+                                               pkgsettings.backup_changes("PORTAGE_BINPKG_TMPFILE")
                                                retval = portage.doebuild(y, "package", myroot,
                                                        pkgsettings, self.edebug, mydbapi=portdb,
                                                        tree="porttree")
+                                               del pkgsettings["PORTAGE_BINPKG_TMPFILE"]
+                                               if "--buildpkgonly" in self.myopts:
+                                                       elog_process(pkg_key, pkgsettings)
                                                if retval != os.EX_OK:
                                                        return retval
                                                bintree = self.trees[myroot]["bintree"]
-                                               if bintree.populated:
-                                                       bintree.inject(pkg_key)
+                                               bintree.inject(pkg_key, filename=binpkg_tmpfile)
                                                if "--buildpkgonly" not in self.myopts:
                                                        msg = " === (%s of %s) Merging (%s::%s)" % \
                                                                (mergecount, len(mymergelist), pkg_key, y)
@@ -3196,9 +3301,29 @@ class MergeTask(object):
                                                        short_msg = "emerge: (%s of %s) %s Fetch" % \
                                                                (mergecount, len(mymergelist), pkg_key)
                                                        emergelog(xterm_titles, msg, short_msg=short_msg)
-                                                       if not self.trees[myroot]["bintree"].gettbz2(
-                                                               pkg_key):
-                                                               return 1
+                                                       try:
+                                                               self.trees[myroot]["bintree"].gettbz2(pkg_key)
+                                                       except portage.exception.FileNotFound:
+                                                               writemsg("!!! Fetching Binary failed " + \
+                                                                       "for '%s'\n" % pkg_key, noiselevel=-1)
+                                                               if not fetchonly:
+                                                                       return 1
+                                                               failed_fetches.append(pkg_key)
+                                                       except portage.exception.DigestException, e:
+                                                               writemsg("\n!!! Digest verification failed:\n",
+                                                                       noiselevel=-1)
+                                                               writemsg("!!! %s\n" % e.value[0],
+                                                                       noiselevel=-1)
+                                                               writemsg("!!! Reason: %s\n" % e.value[1],
+                                                                       noiselevel=-1)
+                                                               writemsg("!!! Got: %s\n" % e.value[2],
+                                                                       noiselevel=-1)
+                                                               writemsg("!!! Expected: %s\n" % e.value[3],
+                                                                       noiselevel=-1)
+                                                               os.unlink(mytbz2)
+                                                               if not fetchonly:
+                                                                       return 1
+                                                               failed_fetches.append(pkg_key)
                                        finally:
                                                if tbz2_lock:
                                                        portage.locks.unlockfile(tbz2_lock)
@@ -3237,7 +3362,7 @@ class MergeTask(object):
                                                        ") Updating world file ("+x[pkgindex]+")")
                                                portage.write_atomic(
                                                os.path.join(myroot, portage.WORLD_FILE),
-                                               "\n".join(myfavdict.values()))
+                                               "\n".join(sorted(myfavdict.values()))+"\n")
 
                                if "--pretend" not in self.myopts and \
                                        "--fetchonly" not in self.myopts and \
@@ -3510,7 +3635,7 @@ def unmerge(settings, myopts, vartree, unmerge_action, unmerge_files,
                                        if not slotmap.has_key(myslot):
                                                slotmap[myslot]={}
                                        slotmap[myslot][localtree.dbapi.cpv_counter(mypkg)]=mypkg
-                               for myslot in slotmap.keys():
+                               for myslot in slotmap:
                                        counterkeys=slotmap[myslot].keys()
                                        counterkeys.sort()
                                        if not counterkeys:
@@ -3537,7 +3662,7 @@ def unmerge(settings, myopts, vartree, unmerge_action, unmerge_files,
        finally:
                if vdb_lock:
                        portage.locks.unlockdir(vdb_lock)
-       for x in pkgmap.keys():
+       for x in pkgmap:
                for y in localtree.dep_match(x):
                        if y not in pkgmap[x]["omitted"] and \
                           y not in pkgmap[x]["selected"] and \
@@ -3593,7 +3718,7 @@ def unmerge(settings, myopts, vartree, unmerge_action, unmerge_files,
        if not autoclean:
                countdown(int(settings["CLEAN_DELAY"]), ">>> Unmerging")
 
-       for x in pkgmap.keys():
+       for x in pkgmap:
                for y in pkgmap[x]["selected"]:
                        print ">>> Unmerging "+y+"..."
                        emergelog(xterm_titles, "=== Unmerging... ("+y+")")
@@ -3698,7 +3823,34 @@ def chk_updated_info_files(root, infodirs, prev_mtimes, retval):
                                print " "+green("*")+" Processed",icount,"info files."
 
 
-def post_emerge(settings, mtimedb, retval):
+def display_news_notification(trees):
+       for target_root in trees:
+               if len(trees) > 1 and target_root != "/":
+                       break
+       settings = trees[target_root]["vartree"].settings
+       portdb = trees[target_root]["porttree"].dbapi
+       vardb = trees[target_root]["vartree"].dbapi
+       NEWS_PATH = os.path.join("metadata", "news")
+       UNREAD_PATH = os.path.join(target_root, NEWS_LIB_PATH, "news")
+       newsReaderDisplay = False
+
+       for repo in portdb.getRepositories():
+               unreadItems = checkUpdatedNewsItems(
+                       portdb, vardb, NEWS_PATH, UNREAD_PATH, repo)
+               if unreadItems:
+                       if not newsReaderDisplay:
+                               newsReaderDisplay = True
+                               print
+                       print colorize("WARN", " * IMPORTANT:"),
+                       print "%s news items need reading for repository '%s'." % (unreadItems, repo)
+                       
+       
+       if newsReaderDisplay:
+               print colorize("WARN", " *"),
+               print "Use " + colorize("GOOD", "eselect news") + " to read news items."
+               print
+
+def post_emerge(trees, mtimedb, retval):
        """
        Misc. things to run at the end of a merge session.
        
@@ -3706,10 +3858,11 @@ def post_emerge(settings, mtimedb, retval):
        Update Config Files
        Update News Items
        Commit mtimeDB
+       Display preserved libs warnings
        Exit Emerge
-       
-       @param settings: Configuration settings (typically portage.settings)
-       @type settings: portage.config()
+
+       @param trees: A dictionary mapping each ROOT to it's package databases
+       @type trees: dict
        @param mtimedb: The mtimeDB to store data needed across merge invocations
        @type mtimedb: MtimeDB class instance
        @param retval: Emerge's return value
@@ -3718,7 +3871,11 @@ def post_emerge(settings, mtimedb, retval):
        @returns:
        1.  Calls sys.exit(retval)
        """
-       target_root = settings["ROOT"]
+       for target_root in trees:
+               if len(trees) > 1 and target_root != "/":
+                       break
+       vardbapi = trees[target_root]["vartree"].dbapi
+       settings = vardbapi.settings
        info_mtimes = mtimedb["info"]
 
        # Load the most current variables from ${ROOT}/etc/profile.env
@@ -3735,26 +3892,27 @@ def post_emerge(settings, mtimedb, retval):
        emergelog("notitles" not in settings.features,
                " *** exiting successfully.")
 
+       # Dump the mod_echo output now so that our other notifications are shown
+       # last.
+       from portage.elog import mod_echo
+       mod_echo.finalize()
+
        if "noinfo" not in settings.features:
                chk_updated_info_files(target_root, infodirs, info_mtimes, retval)
 
        chk_updated_cfg_files(target_root, config_protect)
        
-       NEWS_PATH = os.path.join( "metadata", "news" )
-       UNREAD_PATH = os.path.join( target_root, NEWS_LIB_PATH, 'news')
-       porttree = portdbapi( porttree_root = settings["PORTDIR"], mysettings = settings )
-       newsReaderDisplay = False
+       display_news_notification(trees)
        
-       for repo in porttree.getRepositories():
-               unreadItems = checkUpdatedNewsItems(target_root, NEWS_PATH, UNREAD_PATH, repo)
-               if unreadItems:
-                       print colorize("WARN", " * IMPORTANT:"),
-                       print "%s news items need reading for repository '%s'." % (unreadItems, repo)
-                       newsReaderDisplay = True
-       
-       if newsReaderDisplay:
-               print colorize("WARN", " *"),
-               print "Use " + colorize("GOOD", "eselect news") + " to read news items."
+       if vardbapi.plib_registry.hasEntries():
+               print colorize("WARN", "!!!") + " existing preserved libs:"
+               plibdata = vardbapi.plib_registry.getPreservedLibs()
+               for cpv in plibdata:
+                       print colorize("WARN", ">>>") + " package: %s" % cpv
+                       for f in plibdata[cpv]:
+                               print colorize("WARN", " * ") + " - %s" % f
+               print "Use " + colorize("GOOD", "revdep-rebuild") + " to rebuild packages using these libraries"
+               print "and then remerge the packages listed above."
        
        mtimedb.commit()
        sys.exit(retval)
@@ -3794,13 +3952,15 @@ def chk_updated_cfg_files(target_root, config_protect):
                        #print " "+yellow("*")+" Type "+green("emerge --help config")+" to learn how to update config files."
                        print " "+yellow("*")+" Type "+green("emerge --help config")+" to learn how to update config files."
 
-def checkUpdatedNewsItems( root, NEWS_PATH, UNREAD_PATH, repo_id ):
+def checkUpdatedNewsItems(portdb, vardb, NEWS_PATH, UNREAD_PATH, repo_id):
        """
        Examines news items in repodir + '/' + NEWS_PATH and attempts to find unread items
        Returns the number of unread (yet relevent) items.
        
-       @param root:
-       @type root:
+       @param portdb: a portage tree database
+       @type portdb: pordbapi
+       @param vardb: an installed package database
+       @type vardb: vardbapi
        @param NEWS_PATH:
        @type NEWS_PATH:
        @param UNREAD_PATH:
@@ -3813,7 +3973,7 @@ def checkUpdatedNewsItems( root, NEWS_PATH, UNREAD_PATH, repo_id ):
        
        """
        from portage.news import NewsManager
-       manager = NewsManager( root, NEWS_PATH, UNREAD_PATH )
+       manager = NewsManager(portdb, vardb, NEWS_PATH, UNREAD_PATH)
        return manager.getUnreadItems( repo_id, update=True )
 
 def is_valid_package_atom(x):
@@ -3878,7 +4038,7 @@ def action_sync(settings, trees, mtimedb, myopts, myaction):
                mytimeout=180
 
                rsync_opts = []
-
+               import shlex, StringIO
                if settings["PORTAGE_RSYNC_OPTS"] == "":
                        portage.writemsg("PORTAGE_RSYNC_OPTS empty or unset, using hardcoded defaults\n")
                        rsync_opts.extend([
@@ -3905,7 +4065,11 @@ def action_sync(settings, trees, mtimedb, myopts, myaction):
                        # defaults.
 
                        portage.writemsg("Using PORTAGE_RSYNC_OPTS instead of hardcoded defaults\n", 1)
-                       rsync_opts.extend(settings["PORTAGE_RSYNC_OPTS"].split())
+                       lexer = shlex.shlex(StringIO.StringIO(
+                               settings.get("PORTAGE_RSYNC_OPTS","")), posix=True)
+                       lexer.whitespace_split = True
+                       rsync_opts.extend(lexer)
+                       del lexer
 
                        for opt in ("--recursive", "--times"):
                                if opt not in rsync_opts:
@@ -3994,6 +4158,12 @@ def action_sync(settings, trees, mtimedb, myopts, myaction):
                                pass
                del content
 
+               try:
+                       rsync_initial_timeout = \
+                               int(settings.get("PORTAGE_RSYNC_INITIAL_TIMEOUT", "15"))
+               except ValueError:
+                       rsync_initial_timeout = 15
+
                try:
                        if settings.has_key("RSYNC_RETRIES"):
                                print yellow("WARNING:")+" usage of RSYNC_RETRIES is deprecated, use PORTAGE_RSYNC_RETRIES instead"
@@ -4014,8 +4184,12 @@ def action_sync(settings, trees, mtimedb, myopts, myaction):
                        user_name=""
                updatecache_flg=True
                all_rsync_opts = set(rsync_opts)
-               all_rsync_opts.update(
-                       settings.get("PORTAGE_RSYNC_EXTRA_OPTS","").split())
+               lexer = shlex.shlex(StringIO.StringIO(
+                       settings.get("PORTAGE_RSYNC_EXTRA_OPTS","")), posix=True)
+               lexer.whitespace_split = True
+               extra_rsync_opts = list(lexer)
+               del lexer
+               all_rsync_opts.update(extra_rsync_opts)
                family = socket.AF_INET
                if "-4" in all_rsync_opts or "--ipv4" in all_rsync_opts:
                        family = socket.AF_INET
@@ -4073,8 +4247,7 @@ def action_sync(settings, trees, mtimedb, myopts, myaction):
                        if mytimestamp != 0 and "--quiet" not in myopts:
                                print ">>> Checking server timestamp ..."
 
-                       rsynccommand = " ".join(["/usr/bin/rsync", " ".join(rsync_opts),
-                               settings.get("PORTAGE_RSYNC_EXTRA_OPTS","")])
+                       rsynccommand = ["/usr/bin/rsync"] + rsync_opts + extra_rsync_opts
 
                        if "--debug" in myopts:
                                print rsynccommand
@@ -4087,7 +4260,7 @@ def action_sync(settings, trees, mtimedb, myopts, myaction):
                        # connection attempt to an unresponsive server which rsync's
                        # --timeout option does not prevent.
                        if True:
-                               mycommand = rsynccommand.split()
+                               mycommand = rsynccommand[:]
                                mycommand.append(dosyncuri.rstrip("/") + \
                                        "/metadata/timestamp.chk")
                                mycommand.append(tmpservertimestampfile)
@@ -4100,14 +4273,16 @@ def action_sync(settings, trees, mtimedb, myopts, myaction):
                                        # Timeout here in case the server is unresponsive.  The
                                        # --timeout rsync option doesn't apply to the initial
                                        # connection attempt.
-                                       signal.alarm(15)
+                                       if rsync_initial_timeout:
+                                               signal.alarm(rsync_initial_timeout)
                                        try:
                                                mypids.extend(portage.process.spawn(
                                                        mycommand, env=settings.environ(), returnpid=True))
                                                exitcode = os.waitpid(mypids[0], 0)[1]
                                                content = portage.grabfile(tmpservertimestampfile)
                                        finally:
-                                               signal.alarm(0)
+                                               if rsync_initial_timeout:
+                                                       signal.alarm(0)
                                                try:
                                                        os.unlink(tmpservertimestampfile)
                                                except OSError:
@@ -4161,8 +4336,7 @@ def action_sync(settings, trees, mtimedb, myopts, myaction):
                                        print
                                elif (servertimestamp == 0) or (servertimestamp > mytimestamp):
                                        # actual sync
-                                       mycommand=rsynccommand+" "+dosyncuri+"/ "+myportdir
-                                       mycommand = mycommand.split()
+                                       mycommand = rsynccommand + [dosyncuri+"/", myportdir]
                                        exitcode = portage.process.spawn(mycommand,
                                                env=settings.environ())
                                        if exitcode in [0,1,3,4,11,14,20,21]:
@@ -4260,7 +4434,7 @@ def action_sync(settings, trees, mtimedb, myopts, myaction):
        if os.path.exists(myportdir+"/metadata/cache") and updatecache_flg:
                action_metadata(settings, portdb, myopts)
 
-       if portage.global_updates(settings, trees, mtimedb["updates"]):
+       if portage._global_updates(trees, mtimedb["updates"]):
                mtimedb.commit()
                # Reload the whole config from scratch.
                settings, trees, mtimedb = load_emerge_config(trees=trees)
@@ -4288,6 +4462,8 @@ def action_sync(settings, trees, mtimedb, myopts, myaction):
                print red(" * ")+"configuration files."
                print red(" * ")+"To update portage, run 'emerge portage'."
                print
+       
+       display_news_notification(trees)
 
 def action_metadata(settings, portdb, myopts):
        portage.writemsg_stdout("\n>>> Updating Portage cache:      ")
@@ -4868,6 +5044,15 @@ def action_build(settings, trees, mtimedb,
                        if myopt not in myresumeopts:
                                myresumeopts[myopt] = myarg
                myopts=myresumeopts
+
+               # Adjust config according to options of the command being resumed.
+               for myroot in trees:
+                       mysettings =  trees[myroot]["vartree"].settings
+                       mysettings.unlock()
+                       adjust_config(myopts, mysettings)
+                       mysettings.lock()
+                       del myroot, mysettings
+
                myparams = create_depgraph_params(myopts, myaction)
                if "--quiet" not in myopts and "--nodeps" not in myopts:
                        print "Calculating dependencies  ",
@@ -4982,11 +5167,11 @@ def action_build(settings, trees, mtimedb,
                else:
                        mydepgraph.display(
                                mydepgraph.altlist(reversed=("--tree" in myopts)))
-               if ("--buildpkgonly" in myopts):
-                       if 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)
+                       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)
        else:
                if ("--buildpkgonly" in myopts):
                        if not mydepgraph.digraph.hasallzeros(ignore_priority=DepPriority.MEDIUM):
@@ -5295,7 +5480,7 @@ def emerge_main():
                portage.writemsg("!!! %s\n" % str(e))
                del e
 
-       if portage.global_updates(settings, trees, mtimedb["updates"]):
+       if portage._global_updates(trees, mtimedb["updates"]):
                mtimedb.commit()
                # Reload the whole config from scratch.
                settings, trees, mtimedb = load_emerge_config(trees=trees)
@@ -5304,7 +5489,7 @@ def emerge_main():
        xterm_titles = "notitles" not in settings.features
 
        tmpcmdline = []
-       if "--ignore-default-opts" not in sys.argv:
+       if "--ignore-default-opts" not in myopts:
                tmpcmdline.extend(settings["EMERGE_DEFAULT_OPTS"].split())
        tmpcmdline.extend(sys.argv[1:])
        myaction, myopts, myfiles = parse_opts(tmpcmdline)
@@ -5327,7 +5512,8 @@ def emerge_main():
        if "candy" in settings.features:
                spinner.update = spinner.update_scroll
 
-       portage.deprecated_profile_check()
+       if "--quiet" not in myopts:
+               portage.deprecated_profile_check()
 
        #Freeze the portdbapi for enhanced performance:
        for myroot in trees:
@@ -5455,12 +5641,12 @@ def emerge_main():
        # check if root user is the current user for the actions where emerge needs this
        if portage.secpass < 2:
                # We've already allowed "--version" and "--help" above.
-               if "--pretend" not in myopts and \
-               myaction not in ("search","info"):
+               if "--pretend" not in myopts and myaction not in ("search","info"):
                        need_superuser = not \
                                ("--fetchonly" in myopts or \
                                "--fetch-all-uri" in myopts or \
-                               myaction in ("metadata", "regen"))
+                               myaction in ("metadata", "regen") or \
+                               (myaction == "sync" and os.access(settings["PORTDIR"], os.W_OK)))
                        if portage.secpass < 1 or \
                                need_superuser:
                                if need_superuser:
@@ -5473,12 +5659,12 @@ def emerge_main():
                                if "--ask" in myopts:
                                        myopts["--pretend"] = True
                                        del myopts["--ask"]
-                                       print ("%s access would be required... " + \
+                                       print ("%s access is required... " + \
                                                "adding --pretend to options.\n") % access_desc
                                        if portage.secpass < 1 and not need_superuser:
                                                portage_group_warning()
                                else:
-                                       sys.stderr.write(("emerge: %s access would be " + \
+                                       sys.stderr.write(("emerge: %s access is " + \
                                                "required.\n\n") % access_desc)
                                        if portage.secpass < 1 and not need_superuser:
                                                portage_group_warning()
@@ -5560,21 +5746,25 @@ def emerge_main():
                if 1 == unmerge(settings, myopts, vartree, myaction, myfiles,
                        mtimedb["ldpath"]):
                        if "--pretend" not in myopts:
-                               post_emerge(settings, mtimedb, 0)
+                               post_emerge(trees, mtimedb, os.EX_OK)
 
        elif "depclean"==myaction:
                validate_ebuild_environment(trees)
                action_depclean(settings, trees, mtimedb["ldpath"],
                        myopts, spinner)
                if "--pretend" not in myopts:
-                       post_emerge(settings, mtimedb, 0)
+                       post_emerge(trees, mtimedb, os.EX_OK)
        # "update", "system", or just process files:
        else:
                validate_ebuild_environment(trees)
+               if "--pretend" not in myopts:
+                       display_news_notification(trees)
                action_build(settings, trees, mtimedb,
                        myopts, myaction, myfiles, spinner)
                if "--pretend" not in myopts:
-                       post_emerge(settings, mtimedb, 0)
+                       post_emerge(trees, mtimedb, os.EX_OK)
+               else:
+                       display_news_notification(trees)
 
 if __name__ == "__main__":
        retval = emerge_main()