emerge_main: split out run_action
authorZac Medico <zmedico@gentoo.org>
Tue, 16 Oct 2012 08:33:54 +0000 (01:33 -0700)
committerZac Medico <zmedico@gentoo.org>
Tue, 16 Oct 2012 08:33:54 +0000 (01:33 -0700)
Also move post_emerge and chk_updated_cfg_files to separate files.

pym/_emerge/actions.py
pym/_emerge/chk_updated_cfg_files.py [new file with mode: 0644]
pym/_emerge/main.py
pym/_emerge/post_emerge.py [new file with mode: 0644]

index 3003afc65c1f1070b9a36a7640cc7071a0236a62..3336e9fcae74d9a50223d6d7a2c01a36f7ccb0e0 100644 (file)
@@ -22,13 +22,18 @@ from itertools import chain
 
 import portage
 portage.proxy.lazyimport.lazyimport(globals(),
+       'portage.debug',
        'portage.news:count_unread_news,display_news_notifications',
+       '_emerge.chk_updated_cfg_files:chk_updated_cfg_files',
+       '_emerge.help:help@emerge_help',
+       '_emerge.post_emerge:display_news_notification,post_emerge',
+       '_emerge.stdout_spinner:stdout_spinner',
 )
 
 from portage.localization import _
 from portage import os
 from portage import shutil
-from portage import eapi_is_supported, _unicode_decode
+from portage import eapi_is_supported, _encodings, _unicode_decode
 from portage.cache.cache_errors import CacheError
 from portage.const import GLOBAL_CONFIG_PATH
 from portage.const import _DEPCLEAN_LIB_CHECK_DEFAULT
@@ -38,7 +43,7 @@ from portage.dep import Atom
 from portage.eclass_cache import hashed_path
 from portage.exception import InvalidAtom, InvalidData
 from portage.output import blue, bold, colorize, create_color_func, darkgreen, \
-       red, yellow
+       red, xtermTitle, xtermTitleReset, yellow
 good = create_color_func("GOOD")
 bad = create_color_func("BAD")
 warn = create_color_func("WARN")
@@ -46,7 +51,7 @@ from portage.package.ebuild._ipc.QueryCommand import QueryCommand
 from portage.package.ebuild.doebuild import _check_temp_dir
 from portage._sets import load_default_config, SETPREFIX
 from portage._sets.base import InternalPackageSet
-from portage.util import cmp_sort_key, writemsg, \
+from portage.util import cmp_sort_key, writemsg, varexpand, \
        writemsg_level, writemsg_stdout
 from portage.util.digraph import digraph
 from portage.util._async.SchedulerInterface import SchedulerInterface
@@ -82,6 +87,21 @@ if sys.hexversion >= 0x3000000:
 else:
        _unicode = unicode
 
+COWSAY_MOO = """
+
+  Larry loves Gentoo (%s)
+
+ _______________________
+< Have you mooed today? >
+ -----------------------
+        \   ^__^
+         \  (oo)\_______
+            (__)\       )\/\ 
+                ||----w |
+                ||     ||
+
+"""
+
 def action_build(settings, trees, mtimedb,
        myopts, myaction, myfiles, spinner):
 
@@ -3057,42 +3077,6 @@ def load_emerge_config(trees=None):
        QueryCommand._db = trees
        return settings, trees, mtimedb
 
-def chk_updated_cfg_files(eroot, config_protect):
-       target_root = eroot
-       result = list(
-               portage.util.find_updated_config_files(target_root, config_protect))
-
-       for x in result:
-               writemsg_level("\n %s " % (colorize("WARN", "* " + _("IMPORTANT:"))),
-                       level=logging.INFO, noiselevel=-1)
-               if not x[1]: # it's a protected file
-                       writemsg_level( _("config file '%s' needs updating.\n") % x[0],
-                               level=logging.INFO, noiselevel=-1)
-               else: # it's a protected dir
-                       if len(x[1]) == 1:
-                               head, tail = os.path.split(x[1][0])
-                               tail = tail[len("._cfg0000_"):]
-                               fpath = os.path.join(head, tail)
-                               writemsg_level(_("config file '%s' needs updating.\n") % fpath,
-                                       level=logging.INFO, noiselevel=-1)
-                       else:
-                               writemsg_level( _("%d config files in '%s' need updating.\n") % \
-                                       (len(x[1]), x[0]), level=logging.INFO, noiselevel=-1)
-
-       if result:
-               print(" "+yellow("*")+ " See the "+colorize("INFORM", _("CONFIGURATION FILES"))\
-                               + " " + _("section of the") + " " + bold("emerge"))
-               print(" "+yellow("*")+ " " + _("man page to learn how to update config files."))
-
-
-def display_news_notification(root_config, myopts):
-       if "news" not in root_config.settings.features:
-               return
-       portdb = root_config.trees["porttree"].dbapi
-       vardb = root_config.trees["vartree"].dbapi
-       news_counts = count_unread_news(portdb, vardb)
-       display_news_notifications(news_counts)
-
 def getgccversion(chost):
        """
        rtype: C{str}
@@ -3147,3 +3131,718 @@ def getgccversion(chost):
 
        portage.writemsg(gcc_not_found_error, noiselevel=-1)
        return "[unavailable]"
+
+# Warn about features that may confuse users and
+# lead them to report invalid bugs.
+_emerge_features_warn = frozenset(['keeptemp', 'keepwork'])
+
+def validate_ebuild_environment(trees):
+       features_warn = set()
+       for myroot in trees:
+               settings = trees[myroot]["vartree"].settings
+               settings.validate()
+               features_warn.update(
+                       _emerge_features_warn.intersection(settings.features))
+
+       if features_warn:
+               msg = "WARNING: The FEATURES variable contains one " + \
+                       "or more values that should be disabled under " + \
+                       "normal circumstances: %s" % " ".join(features_warn)
+               out = portage.output.EOutput()
+               for line in textwrap.wrap(msg, 65):
+                       out.ewarn(line)
+
+def check_procfs():
+       procfs_path = '/proc'
+       if platform.system() not in ("Linux",) or \
+               os.path.ismount(procfs_path):
+               return os.EX_OK
+       msg = "It seems that %s is not mounted. You have been warned." % procfs_path
+       writemsg_level("".join("!!! %s\n" % l for l in textwrap.wrap(msg, 70)),
+               level=logging.ERROR, noiselevel=-1)
+       return 1
+
+def config_protect_check(trees):
+       for root, root_trees in trees.items():
+               settings = root_trees["root_config"].settings
+               if not settings.get("CONFIG_PROTECT"):
+                       msg = "!!! CONFIG_PROTECT is empty"
+                       if settings["ROOT"] != "/":
+                               msg += " for '%s'" % root
+                       msg += "\n"
+                       writemsg_level(msg, level=logging.WARN, noiselevel=-1)
+
+def apply_priorities(settings):
+       ionice(settings)
+       nice(settings)
+
+def nice(settings):
+       try:
+               os.nice(int(settings.get("PORTAGE_NICENESS", "0")))
+       except (OSError, ValueError) as e:
+               out = portage.output.EOutput()
+               out.eerror("Failed to change nice value to '%s'" % \
+                       settings["PORTAGE_NICENESS"])
+               out.eerror("%s\n" % str(e))
+
+def ionice(settings):
+
+       ionice_cmd = settings.get("PORTAGE_IONICE_COMMAND")
+       if ionice_cmd:
+               ionice_cmd = portage.util.shlex_split(ionice_cmd)
+       if not ionice_cmd:
+               return
+
+       variables = {"PID" : str(os.getpid())}
+       cmd = [varexpand(x, mydict=variables) for x in ionice_cmd]
+
+       try:
+               rval = portage.process.spawn(cmd, env=os.environ)
+       except portage.exception.CommandNotFound:
+               # The OS kernel probably doesn't support ionice,
+               # so return silently.
+               return
+
+       if rval != os.EX_OK:
+               out = portage.output.EOutput()
+               out.eerror("PORTAGE_IONICE_COMMAND returned %d" % (rval,))
+               out.eerror("See the make.conf(5) man page for PORTAGE_IONICE_COMMAND usage instructions.")
+
+def setconfig_fallback(root_config):
+       setconfig = root_config.setconfig
+       setconfig._create_default_config()
+       setconfig._parse(update=True)
+       root_config.sets = setconfig.getSets()
+
+def get_missing_sets(root_config):
+       # emerge requires existence of "world", "selected", and "system"
+       missing_sets = []
+
+       for s in ("selected", "system", "world",):
+               if s not in root_config.sets:
+                       missing_sets.append(s)
+
+       return missing_sets
+
+def missing_sets_warning(root_config, missing_sets):
+       if len(missing_sets) > 2:
+               missing_sets_str = ", ".join('"%s"' % s for s in missing_sets[:-1])
+               missing_sets_str += ', and "%s"' % missing_sets[-1]
+       elif len(missing_sets) == 2:
+               missing_sets_str = '"%s" and "%s"' % tuple(missing_sets)
+       else:
+               missing_sets_str = '"%s"' % missing_sets[-1]
+       msg = ["emerge: incomplete set configuration, " + \
+               "missing set(s): %s" % missing_sets_str]
+       if root_config.sets:
+               msg.append("        sets defined: %s" % ", ".join(root_config.sets))
+       global_config_path = portage.const.GLOBAL_CONFIG_PATH
+       if root_config.settings['EPREFIX']:
+               global_config_path = os.path.join(root_config.settings['EPREFIX'],
+                               portage.const.GLOBAL_CONFIG_PATH.lstrip(os.sep))
+       msg.append("        This usually means that '%s'" % \
+               (os.path.join(global_config_path, "sets/portage.conf"),))
+       msg.append("        is missing or corrupt.")
+       msg.append("        Falling back to default world and system set configuration!!!")
+       for line in msg:
+               writemsg_level(line + "\n", level=logging.ERROR, noiselevel=-1)
+
+def ensure_required_sets(trees):
+       warning_shown = False
+       for root_trees in trees.values():
+               missing_sets = get_missing_sets(root_trees["root_config"])
+               if missing_sets and not warning_shown:
+                       warning_shown = True
+                       missing_sets_warning(root_trees["root_config"], missing_sets)
+               if missing_sets:
+                       setconfig_fallback(root_trees["root_config"])
+
+def expand_set_arguments(myfiles, myaction, root_config):
+       retval = os.EX_OK
+       setconfig = root_config.setconfig
+
+       sets = setconfig.getSets()
+
+       # In order to know exactly which atoms/sets should be added to the
+       # world file, the depgraph performs set expansion later. It will get
+       # confused about where the atoms came from if it's not allowed to
+       # expand them itself.
+       do_not_expand = (None, )
+       newargs = []
+       for a in myfiles:
+               if a in ("system", "world"):
+                       newargs.append(SETPREFIX+a)
+               else:
+                       newargs.append(a)
+       myfiles = newargs
+       del newargs
+       newargs = []
+
+       # separators for set arguments
+       ARG_START = "{"
+       ARG_END = "}"
+
+       for i in range(0, len(myfiles)):
+               if myfiles[i].startswith(SETPREFIX):
+                       start = 0
+                       end = 0
+                       x = myfiles[i][len(SETPREFIX):]
+                       newset = ""
+                       while x:
+                               start = x.find(ARG_START)
+                               end = x.find(ARG_END)
+                               if start > 0 and start < end:
+                                       namepart = x[:start]
+                                       argpart = x[start+1:end]
+
+                                       # TODO: implement proper quoting
+                                       args = argpart.split(",")
+                                       options = {}
+                                       for a in args:
+                                               if "=" in a:
+                                                       k, v  = a.split("=", 1)
+                                                       options[k] = v
+                                               else:
+                                                       options[a] = "True"
+                                       setconfig.update(namepart, options)
+                                       newset += (x[:start-len(namepart)]+namepart)
+                                       x = x[end+len(ARG_END):]
+                               else:
+                                       newset += x
+                                       x = ""
+                       myfiles[i] = SETPREFIX+newset
+
+       sets = setconfig.getSets()
+
+       # display errors that occurred while loading the SetConfig instance
+       for e in setconfig.errors:
+               print(colorize("BAD", "Error during set creation: %s" % e))
+
+       unmerge_actions = ("unmerge", "prune", "clean", "depclean")
+
+       for a in myfiles:
+               if a.startswith(SETPREFIX):             
+                               s = a[len(SETPREFIX):]
+                               if s not in sets:
+                                       display_missing_pkg_set(root_config, s)
+                                       return (None, 1)
+                               if s == "installed":
+                                       msg = ("The @installed set is deprecated and will soon be "
+                                       "removed. Please refer to bug #387059 for details.")
+                                       out = portage.output.EOutput()
+                                       for line in textwrap.wrap(msg, 50):
+                                               out.ewarn(line)
+                               setconfig.active.append(s)
+                               try:
+                                       set_atoms = setconfig.getSetAtoms(s)
+                               except portage.exception.PackageSetNotFound as e:
+                                       writemsg_level(("emerge: the given set '%s' " + \
+                                               "contains a non-existent set named '%s'.\n") % \
+                                               (s, e), level=logging.ERROR, noiselevel=-1)
+                                       if s in ('world', 'selected') and \
+                                               SETPREFIX + e.value in sets['selected']:
+                                               writemsg_level(("Use `emerge --deselect %s%s` to "
+                                                       "remove this set from world_sets.\n") %
+                                                       (SETPREFIX, e,), level=logging.ERROR,
+                                                       noiselevel=-1)
+                                       return (None, 1)
+                               if myaction in unmerge_actions and \
+                                               not sets[s].supportsOperation("unmerge"):
+                                       sys.stderr.write("emerge: the given set '%s' does " % s + \
+                                               "not support unmerge operations\n")
+                                       retval = 1
+                               elif not set_atoms:
+                                       print("emerge: '%s' is an empty set" % s)
+                               elif myaction not in do_not_expand:
+                                       newargs.extend(set_atoms)
+                               else:
+                                       newargs.append(SETPREFIX+s)
+                               for e in sets[s].errors:
+                                       print(e)
+               else:
+                       newargs.append(a)
+       return (newargs, retval)
+
+def repo_name_check(trees):
+       missing_repo_names = set()
+       for root_trees in trees.values():
+               porttree = root_trees.get("porttree")
+               if porttree:
+                       portdb = porttree.dbapi
+                       missing_repo_names.update(portdb.getMissingRepoNames())
+                       if portdb.porttree_root in missing_repo_names and \
+                               not os.path.exists(os.path.join(
+                               portdb.porttree_root, "profiles")):
+                               # This is normal if $PORTDIR happens to be empty,
+                               # so don't warn about it.
+                               missing_repo_names.remove(portdb.porttree_root)
+
+       if missing_repo_names:
+               msg = []
+               msg.append("WARNING: One or more repositories " + \
+                       "have missing repo_name entries:")
+               msg.append("")
+               for p in missing_repo_names:
+                       msg.append("\t%s/profiles/repo_name" % (p,))
+               msg.append("")
+               msg.extend(textwrap.wrap("NOTE: Each repo_name entry " + \
+                       "should be a plain text file containing a unique " + \
+                       "name for the repository on the first line.", 70))
+               msg.append("\n")
+               writemsg_level("".join("%s\n" % l for l in msg),
+                       level=logging.WARNING, noiselevel=-1)
+
+       return bool(missing_repo_names)
+
+def repo_name_duplicate_check(trees):
+       ignored_repos = {}
+       for root, root_trees in trees.items():
+               if 'porttree' in root_trees:
+                       portdb = root_trees['porttree'].dbapi
+                       if portdb.settings.get('PORTAGE_REPO_DUPLICATE_WARN') != '0':
+                               for repo_name, paths in portdb.getIgnoredRepos():
+                                       k = (root, repo_name, portdb.getRepositoryPath(repo_name))
+                                       ignored_repos.setdefault(k, []).extend(paths)
+
+       if ignored_repos:
+               msg = []
+               msg.append('WARNING: One or more repositories ' + \
+                       'have been ignored due to duplicate')
+               msg.append('  profiles/repo_name entries:')
+               msg.append('')
+               for k in sorted(ignored_repos):
+                       msg.append('  %s overrides' % ", ".join(k))
+                       for path in ignored_repos[k]:
+                               msg.append('    %s' % (path,))
+                       msg.append('')
+               msg.extend('  ' + x for x in textwrap.wrap(
+                       "All profiles/repo_name entries must be unique in order " + \
+                       "to avoid having duplicates ignored. " + \
+                       "Set PORTAGE_REPO_DUPLICATE_WARN=\"0\" in " + \
+                       "/etc/make.conf if you would like to disable this warning."))
+               msg.append("\n")
+               writemsg_level(''.join('%s\n' % l for l in msg),
+                       level=logging.WARNING, noiselevel=-1)
+
+       return bool(ignored_repos)
+
+def run_action(settings, trees, mtimedb, myaction, myopts, myfiles):
+
+       # skip global updates prior to sync, since it's called after sync
+       if myaction not in ('help', 'info', 'sync', 'version') and \
+               myopts.get('--package-moves') != 'n' and \
+               _global_updates(trees, mtimedb["updates"], quiet=("--quiet" in myopts)):
+               mtimedb.commit()
+               # Reload the whole config from scratch.
+               settings, trees, mtimedb = load_emerge_config(trees=trees)
+
+       xterm_titles = "notitles" not in settings.features
+       if xterm_titles:
+               xtermTitle("emerge")
+
+       if "--digest" in myopts:
+               os.environ["FEATURES"] = os.environ.get("FEATURES","") + " digest"
+               # Reload the whole config from scratch so that the portdbapi internal
+               # config is updated with new FEATURES.
+               settings, trees, mtimedb = load_emerge_config(trees=trees)
+
+       # NOTE: adjust_configs() can map options to FEATURES, so any relevant
+       # options adjustments should be made prior to calling adjust_configs().
+       if "--buildpkgonly" in myopts:
+               myopts["--buildpkg"] = True
+
+       if "getbinpkg" in settings.features:
+               myopts["--getbinpkg"] = True
+
+       if "--getbinpkgonly" in myopts:
+               myopts["--getbinpkg"] = True
+
+       if "--getbinpkgonly" in myopts:
+               myopts["--usepkgonly"] = True
+
+       if "--getbinpkg" in myopts:
+               myopts["--usepkg"] = True
+
+       if "--usepkgonly" in myopts:
+               myopts["--usepkg"] = True
+
+       if "--buildpkgonly" in myopts:
+               # --buildpkgonly will not merge anything, so
+               # it cancels all binary package options.
+               for opt in ("--getbinpkg", "--getbinpkgonly",
+                       "--usepkg", "--usepkgonly"):
+                       myopts.pop(opt, None)
+
+       adjust_configs(myopts, trees)
+       apply_priorities(settings)
+
+       if myaction == 'version':
+               writemsg_stdout(getportageversion(
+                       settings["PORTDIR"], None,
+                       settings.profile_path, settings["CHOST"],
+                       trees[settings['EROOT']]['vartree'].dbapi) + '\n', noiselevel=-1)
+               return 0
+       elif myaction == 'help':
+               emerge_help()
+               return 0
+
+       spinner = stdout_spinner()
+       if "candy" in settings.features:
+               spinner.update = spinner.update_scroll
+
+       if "--quiet" not in myopts:
+               portage.deprecated_profile_check(settings=settings)
+               if portage.const._ENABLE_REPO_NAME_WARN:
+                       # Bug #248603 - Disable warnings about missing
+                       # repo_name entries for stable branch.
+                       repo_name_check(trees)
+               repo_name_duplicate_check(trees)
+               config_protect_check(trees)
+       check_procfs()
+
+       for mytrees in trees.values():
+               mydb = mytrees["porttree"].dbapi
+               # Freeze the portdbapi for performance (memoize all xmatch results).
+               mydb.freeze()
+
+               if myaction in ('search', None) and \
+                       "--usepkg" in myopts:
+                       # Populate the bintree with current --getbinpkg setting.
+                       # This needs to happen before expand_set_arguments(), in case
+                       # any sets use the bintree.
+                       mytrees["bintree"].populate(
+                               getbinpkgs="--getbinpkg" in myopts)
+
+       del mytrees, mydb
+
+       if "moo" in myfiles:
+               print(COWSAY_MOO % platform.system())
+               msg = ("The above `emerge moo` display is deprecated. "
+                       "Please use `emerge --moo` instead.")
+               for line in textwrap.wrap(msg, 50):
+                       print(" %s %s" % (colorize("WARN", "*"), line))
+
+       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(colorize("BAD", "\n*** emerging by path is broken "
+                               "and may not always work!!!\n"))
+                       break
+
+       root_config = trees[settings['EROOT']]['root_config']
+       if myaction == "moo":
+               print(COWSAY_MOO % platform.system())
+               return os.EX_OK
+       elif myaction == "list-sets":
+               writemsg_stdout("".join("%s\n" % s for s in sorted(root_config.sets)))
+               return os.EX_OK
+       elif myaction == "check-news":
+               news_counts = count_unread_news(
+                       root_config.trees["porttree"].dbapi,
+                       root_config.trees["vartree"].dbapi)
+               if any(news_counts.values()):
+                       display_news_notifications(news_counts)
+               elif "--quiet" not in myopts:
+                       print("", colorize("GOOD", "*"), "No news items were found.")
+               return os.EX_OK
+
+       ensure_required_sets(trees)
+
+       # only expand sets for actions taking package arguments
+       oldargs = myfiles[:]
+       if myaction in ("clean", "config", "depclean",
+               "info", "prune", "unmerge", None):
+               myfiles, retval = expand_set_arguments(myfiles, myaction, root_config)
+               if retval != os.EX_OK:
+                       return retval
+
+               # Need to handle empty sets specially, otherwise emerge will react 
+               # with the help message for empty argument lists
+               if oldargs and not myfiles:
+                       print("emerge: no targets left after set expansion")
+                       return 0
+
+       if ("--tree" in myopts) and ("--columns" in myopts):
+               print("emerge: can't specify both of \"--tree\" and \"--columns\".")
+               return 1
+
+       if '--emptytree' in myopts and '--noreplace' in myopts:
+               writemsg_level("emerge: can't specify both of " + \
+                       "\"--emptytree\" and \"--noreplace\".\n",
+                       level=logging.ERROR, noiselevel=-1)
+               return 1
+
+       if ("--quiet" in myopts):
+               spinner.update = spinner.update_quiet
+               portage.util.noiselimit = -1
+
+       if "--fetch-all-uri" in myopts:
+               myopts["--fetchonly"] = True
+
+       if "--skipfirst" in myopts and "--resume" not in myopts:
+               myopts["--resume"] = True
+
+       # Allow -p to remove --ask
+       if "--pretend" in myopts:
+               myopts.pop("--ask", None)
+
+       # forbid --ask when not in a terminal
+       # note: this breaks `emerge --ask | tee logfile`, but that doesn't work anyway.
+       if ("--ask" in myopts) and (not sys.stdin.isatty()):
+               portage.writemsg("!!! \"--ask\" should only be used in a terminal. Exiting.\n",
+                       noiselevel=-1)
+               return 1
+
+       if settings.get("PORTAGE_DEBUG", "") == "1":
+               spinner.update = spinner.update_quiet
+               portage.util.noiselimit = 0
+               if "python-trace" in settings.features:
+                       portage.debug.set_trace(True)
+
+       if not ("--quiet" in myopts):
+               if '--nospinner' in myopts or \
+                       settings.get('TERM') == 'dumb' or \
+                       not sys.stdout.isatty():
+                       spinner.update = spinner.update_basic
+
+       if "--debug" in myopts:
+               print("myaction", myaction)
+               print("myopts", myopts)
+
+       if not myaction and not myfiles and "--resume" not in myopts:
+               emerge_help()
+               return 1
+
+       pretend = "--pretend" in myopts
+       fetchonly = "--fetchonly" in myopts or "--fetch-all-uri" in myopts
+       buildpkgonly = "--buildpkgonly" in myopts
+
+       # check if root user is the current user for the actions where emerge needs this
+       if portage.data.secpass < 2:
+               # We've already allowed "--version" and "--help" above.
+               if "--pretend" not in myopts and myaction not in ("search","info"):
+                       need_superuser = myaction in ('clean', 'depclean', 'deselect',
+                               'prune', 'unmerge') or not \
+                               (fetchonly or \
+                               (buildpkgonly and portage.data.secpass >= 1) or \
+                               myaction in ("metadata", "regen", "sync"))
+                       if portage.data.secpass < 1 or \
+                               need_superuser:
+                               if need_superuser:
+                                       access_desc = "superuser"
+                               else:
+                                       access_desc = "portage group"
+                               # Always show portage_group_warning() when only portage group
+                               # access is required but the user is not in the portage group.
+                               if "--ask" in myopts:
+                                       writemsg_stdout("This action requires %s access...\n" % \
+                                               (access_desc,), noiselevel=-1)
+                                       if portage.data.secpass < 1 and not need_superuser:
+                                               portage.data.portage_group_warning()
+                                       if userquery("Would you like to add --pretend to options?",
+                                               "--ask-enter-invalid" in myopts) == "No":
+                                               return 128 + signal.SIGINT
+                                       myopts["--pretend"] = True
+                                       myopts.pop("--ask")
+                               else:
+                                       sys.stderr.write(("emerge: %s access is required\n") \
+                                               % access_desc)
+                                       if portage.data.secpass < 1 and not need_superuser:
+                                               portage.data.portage_group_warning()
+                                       return 1
+
+       # Disable emergelog for everything except build or unmerge operations.
+       # This helps minimize parallel emerge.log entries that can confuse log
+       # parsers like genlop.
+       disable_emergelog = False
+       for x in ("--pretend", "--fetchonly", "--fetch-all-uri"):
+               if x in myopts:
+                       disable_emergelog = True
+                       break
+       if disable_emergelog:
+               pass
+       elif myaction in ("search", "info"):
+               disable_emergelog = True
+       elif portage.data.secpass < 1:
+               disable_emergelog = True
+
+       import _emerge.emergelog
+       _emerge.emergelog._disable = disable_emergelog
+
+       if not disable_emergelog:
+               if 'EMERGE_LOG_DIR' in settings:
+                       try:
+                               # At least the parent needs to exist for the lock file.
+                               portage.util.ensure_dirs(settings['EMERGE_LOG_DIR'])
+                       except portage.exception.PortageException as e:
+                               writemsg_level("!!! Error creating directory for " + \
+                                       "EMERGE_LOG_DIR='%s':\n!!! %s\n" % \
+                                       (settings['EMERGE_LOG_DIR'], e),
+                                       noiselevel=-1, level=logging.ERROR)
+                               portage.util.ensure_dirs(_emerge.emergelog._emerge_log_dir)
+                       else:
+                               _emerge.emergelog._emerge_log_dir = settings["EMERGE_LOG_DIR"]
+               else:
+                       _emerge.emergelog._emerge_log_dir = os.path.join(os.sep,
+                               settings["EPREFIX"].lstrip(os.sep), "var", "log")
+                       portage.util.ensure_dirs(_emerge.emergelog._emerge_log_dir)
+
+       if not "--pretend" in myopts:
+               emergelog(xterm_titles, "Started emerge on: "+\
+                       _unicode_decode(
+                               time.strftime("%b %d, %Y %H:%M:%S", time.localtime()),
+                               encoding=_encodings['content'], errors='replace'))
+               myelogstr=""
+               if myopts:
+                       opt_list = []
+                       for opt, arg in myopts.items():
+                               if arg is True:
+                                       opt_list.append(opt)
+                               elif isinstance(arg, list):
+                                       # arguments like --exclude that use 'append' action
+                                       for x in arg:
+                                               opt_list.append("%s=%s" % (opt, x))
+                               else:
+                                       opt_list.append("%s=%s" % (opt, arg))
+                       myelogstr=" ".join(opt_list)
+               if myaction:
+                       myelogstr += " --" + myaction
+               if myfiles:
+                       myelogstr += " " + " ".join(oldargs)
+               emergelog(xterm_titles, " *** emerge " + myelogstr)
+
+       oldargs = None
+
+       def emergeexitsig(signum, frame):
+               signal.signal(signal.SIGTERM, signal.SIG_IGN)
+               portage.util.writemsg(
+                       "\n\nExiting on signal %(signal)s\n" % {"signal":signum})
+               sys.exit(128 + signum)
+
+       signal.signal(signal.SIGTERM, emergeexitsig)
+
+       def emergeexit():
+               """This gets out final log message in before we quit."""
+               if "--pretend" not in myopts:
+                       emergelog(xterm_titles, " *** terminating.")
+               if xterm_titles:
+                       xtermTitleReset()
+       portage.atexit_register(emergeexit)
+
+       if myaction in ("config", "metadata", "regen", "sync"):
+               if "--pretend" in myopts:
+                       sys.stderr.write(("emerge: The '%s' action does " + \
+                               "not support '--pretend'.\n") % myaction)
+                       return 1
+
+       if "sync" == myaction:
+               return action_sync(settings, trees, mtimedb, myopts, myaction)
+       elif "metadata" == myaction:
+               action_metadata(settings,
+                       trees[settings['EROOT']]['porttree'].dbapi, myopts)
+       elif myaction=="regen":
+               validate_ebuild_environment(trees)
+               return action_regen(settings,
+                       trees[settings['EROOT']]['porttree'].dbapi, myopts.get("--jobs"),
+                       myopts.get("--load-average"))
+       # HELP action
+       elif "config"==myaction:
+               validate_ebuild_environment(trees)
+               action_config(settings, trees, myopts, myfiles)
+
+       # SEARCH action
+       elif "search"==myaction:
+               validate_ebuild_environment(trees)
+               action_search(trees[settings['EROOT']]['root_config'],
+                       myopts, myfiles, spinner)
+
+       elif myaction in ('clean', 'depclean', 'deselect', 'prune', 'unmerge'):
+               validate_ebuild_environment(trees)
+               rval = action_uninstall(settings, trees, mtimedb["ldpath"],
+                       myopts, myaction, myfiles, spinner)
+               if not (myaction == 'deselect' or
+                       buildpkgonly or fetchonly or pretend):
+                       post_emerge(myaction, myopts, myfiles, settings['EROOT'],
+                               trees, mtimedb, rval)
+               return rval
+
+       elif myaction == 'info':
+
+               # Ensure atoms are valid before calling unmerge().
+               vardb = trees[settings['EROOT']]['vartree'].dbapi
+               portdb = trees[settings['EROOT']]['porttree'].dbapi
+               bindb = trees[settings['EROOT']]["bintree"].dbapi
+               valid_atoms = []
+               for x in myfiles:
+                       if is_valid_package_atom(x, allow_repo=True):
+                               try:
+                                       #look at the installed files first, if there is no match
+                                       #look at the ebuilds, since EAPI 4 allows running pkg_info
+                                       #on non-installed packages
+                                       valid_atom = dep_expand(x, mydb=vardb, settings=settings)
+                                       if valid_atom.cp.split("/")[0] == "null":
+                                               valid_atom = dep_expand(x,
+                                                       mydb=portdb, settings=settings)
+
+                                       if valid_atom.cp.split("/")[0] == "null" and \
+                                               "--usepkg" in myopts:
+                                               valid_atom = dep_expand(x,
+                                                       mydb=bindb, settings=settings)
+
+                                       valid_atoms.append(valid_atom)
+
+                               except portage.exception.AmbiguousPackageName as e:
+                                       msg = "The short ebuild name \"" + x + \
+                                               "\" is ambiguous.  Please specify " + \
+                                               "one of the following " + \
+                                               "fully-qualified ebuild names instead:"
+                                       for line in textwrap.wrap(msg, 70):
+                                               writemsg_level("!!! %s\n" % (line,),
+                                                       level=logging.ERROR, noiselevel=-1)
+                                       for i in e.args[0]:
+                                               writemsg_level("    %s\n" % colorize("INFORM", i),
+                                                       level=logging.ERROR, noiselevel=-1)
+                                       writemsg_level("\n", level=logging.ERROR, noiselevel=-1)
+                                       return 1
+                               continue
+                       msg = []
+                       msg.append("'%s' is not a valid package atom." % (x,))
+                       msg.append("Please check ebuild(5) for full details.")
+                       writemsg_level("".join("!!! %s\n" % line for line in msg),
+                               level=logging.ERROR, noiselevel=-1)
+                       return 1
+
+               return action_info(settings, trees, myopts, valid_atoms)
+
+       # "update", "system", or just process files:
+       else:
+               validate_ebuild_environment(trees)
+
+               for x in myfiles:
+                       if x.startswith(SETPREFIX) or \
+                               is_valid_package_atom(x, allow_repo=True):
+                               continue
+                       if x[:1] == os.sep:
+                               continue
+                       try:
+                               os.lstat(x)
+                               continue
+                       except OSError:
+                               pass
+                       msg = []
+                       msg.append("'%s' is not a valid package atom." % (x,))
+                       msg.append("Please check ebuild(5) for full details.")
+                       writemsg_level("".join("!!! %s\n" % line for line in msg),
+                               level=logging.ERROR, noiselevel=-1)
+                       return 1
+
+               # GLEP 42 says to display news *after* an emerge --pretend
+               if "--pretend" not in myopts:
+                       display_news_notification(root_config, myopts)
+               retval = action_build(settings, trees, mtimedb,
+                       myopts, myaction, myfiles, spinner)
+               post_emerge(myaction, myopts, myfiles, settings['EROOT'],
+                       trees, mtimedb, retval)
+
+               return retval
diff --git a/pym/_emerge/chk_updated_cfg_files.py b/pym/_emerge/chk_updated_cfg_files.py
new file mode 100644 (file)
index 0000000..9f2ab6f
--- /dev/null
@@ -0,0 +1,42 @@
+# Copyright 1999-2012 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+
+from __future__ import print_function
+
+import logging
+
+import portage
+from portage import os
+from portage.localization import _
+from portage.output import bold, colorize, yellow
+from portage.util import writemsg_level
+
+def chk_updated_cfg_files(eroot, config_protect):
+       target_root = eroot
+       result = list(
+               portage.util.find_updated_config_files(target_root, config_protect))
+
+       for x in result:
+               writemsg_level("\n %s " % (colorize("WARN", "* " + _("IMPORTANT:"))),
+                       level=logging.INFO, noiselevel=-1)
+               if not x[1]: # it's a protected file
+                       writemsg_level( _("config file '%s' needs updating.\n") % x[0],
+                               level=logging.INFO, noiselevel=-1)
+               else: # it's a protected dir
+                       if len(x[1]) == 1:
+                               head, tail = os.path.split(x[1][0])
+                               tail = tail[len("._cfg0000_"):]
+                               fpath = os.path.join(head, tail)
+                               writemsg_level(_("config file '%s' needs updating.\n") % fpath,
+                                       level=logging.INFO, noiselevel=-1)
+                       else:
+                               writemsg_level(
+                                       _("%d config files in '%s' need updating.\n") % \
+                                       (len(x[1]), x[0]), level=logging.INFO, noiselevel=-1)
+
+       if result:
+               print(" " + yellow("*") + " See the " +
+                       colorize("INFORM", _("CONFIGURATION FILES")) +
+                       " " + _("section of the") + " " + bold("emerge"))
+               print(" " + yellow("*") + " " +
+                       _("man page to learn how to update config files."))
index eed39993e76a60025c69a5e66825b6c32f95f0bd..fd1fd069bb57c406a0e9da74b663f0a379f11f2e 100644 (file)
@@ -3,40 +3,18 @@
 
 from __future__ import print_function
 
-import platform
-import signal
 import sys
 
 import portage
 portage.proxy.lazyimport.lazyimport(globals(),
        'logging',
-       'portage.debug',
-       'portage.dbapi.dep_expand:dep_expand',
-       'portage.news:count_unread_news,display_news_notifications',
-       'portage.emaint.modules.logs.logs:CleanLogs',
-       'portage.output:colorize,xtermTitle,xtermTitleReset',
-       'portage._global_updates:_global_updates',
-       'portage._sets:SETPREFIX',
-       'portage.util:shlex_split,varexpand,writemsg_level,writemsg_stdout',
-       'portage.util._dyn_libs.display_preserved_libs:display_preserved_libs',
-       'portage.util._info_files:chk_updated_info_files',
+       'portage.util:writemsg_level',
        'textwrap',
-       'time',
-       '_emerge.actions:action_build,action_config,action_info,' + \
-               'action_metadata,action_regen,action_search,action_sync,' + \
-               'action_uninstall,adjust_configs,chk_updated_cfg_files,'+ \
-               'display_missing_pkg_set,display_news_notification,' + \
-               'getportageversion,load_emerge_config',
-       '_emerge.emergelog:emergelog',
+       '_emerge.actions:load_emerge_config,run_action,' + \
+               'validate_ebuild_environment',
        '_emerge.help:help@emerge_help',
-       '_emerge.is_valid_package_atom:is_valid_package_atom',
-       '_emerge.stdout_spinner:stdout_spinner',
-       '_emerge.userquery:userquery',
-       '_emerge._flush_elog_mod_echo:_flush_elog_mod_echo',
 )
 from portage import os
-from portage import _encodings
-from portage import _unicode_decode
 
 if sys.hexversion >= 0x3000000:
        long = int
@@ -89,141 +67,6 @@ shortmapping={
 "v":"--verbose",   "V":"--version"
 }
 
-COWSAY_MOO = """
-
-  Larry loves Gentoo (%s)
-
- _______________________
-< Have you mooed today? >
- -----------------------
-        \   ^__^
-         \  (oo)\_______
-            (__)\       )\/\ 
-                ||----w |
-                ||     ||
-
-"""
-
-def post_emerge(myaction, myopts, myfiles,
-       target_root, trees, mtimedb, retval):
-       """
-       Misc. things to run at the end of a merge session.
-
-       Update Info Files
-       Update Config Files
-       Update News Items
-       Commit mtimeDB
-       Display preserved libs warnings
-
-       @param myaction: The action returned from parse_opts()
-       @type myaction: String
-       @param myopts: emerge options
-       @type myopts: dict
-       @param myfiles: emerge arguments
-       @type myfiles: list
-       @param target_root: The target EROOT for myaction
-       @type target_root: String
-       @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
-       @type retval: Int
-       """
-
-       root_config = trees[target_root]["root_config"]
-       vardbapi = trees[target_root]['vartree'].dbapi
-       settings = vardbapi.settings
-       info_mtimes = mtimedb["info"]
-
-       # Load the most current variables from ${ROOT}/etc/profile.env
-       settings.unlock()
-       settings.reload()
-       settings.regenerate()
-       settings.lock()
-
-       config_protect = shlex_split(settings.get("CONFIG_PROTECT", ""))
-       infodirs = settings.get("INFOPATH","").split(":") + \
-               settings.get("INFODIR","").split(":")
-
-       os.chdir("/")
-
-       if retval == os.EX_OK:
-               exit_msg = " *** exiting successfully."
-       else:
-               exit_msg = " *** exiting unsuccessfully with status '%s'." % retval
-       emergelog("notitles" not in settings.features, exit_msg)
-
-       _flush_elog_mod_echo()
-
-       if not vardbapi._pkgs_changed:
-               # GLEP 42 says to display news *after* an emerge --pretend
-               if "--pretend" in myopts:
-                       display_news_notification(root_config, myopts)
-               # If vdb state has not changed then there's nothing else to do.
-               return
-
-       vdb_path = os.path.join(root_config.settings['EROOT'], portage.VDB_PATH)
-       portage.util.ensure_dirs(vdb_path)
-       vdb_lock = None
-       if os.access(vdb_path, os.W_OK) and not "--pretend" in myopts:
-               vardbapi.lock()
-               vdb_lock = True
-
-       if vdb_lock:
-               try:
-                       if "noinfo" not in settings.features:
-                               chk_updated_info_files(target_root,
-                                       infodirs, info_mtimes)
-                       mtimedb.commit()
-               finally:
-                       if vdb_lock:
-                               vardbapi.unlock()
-
-       # Explicitly load and prune the PreservedLibsRegistry in order
-       # to ensure that we do not display stale data.
-       vardbapi._plib_registry.load()
-
-       if vardbapi._plib_registry.hasEntries():
-               if "--quiet" in myopts:
-                       print()
-                       print(colorize("WARN", "!!!") + " existing preserved libs found")
-               else:
-                       print()
-                       print(colorize("WARN", "!!!") + " existing preserved libs:")
-                       display_preserved_libs(vardbapi)
-                       print("Use " + colorize("GOOD", "emerge @preserved-rebuild") +
-                               " to rebuild packages using these libraries")
-
-       chk_updated_cfg_files(settings['EROOT'], config_protect)
-
-       display_news_notification(root_config, myopts)
-
-       postemerge = os.path.join(settings["PORTAGE_CONFIGROOT"],
-               portage.USER_CONFIG_PATH, "bin", "post_emerge")
-       if os.access(postemerge, os.X_OK):
-               hook_retval = portage.process.spawn(
-                                               [postemerge], env=settings.environ())
-               if hook_retval != os.EX_OK:
-                       writemsg_level(
-                               " %s spawn failed of %s\n" %
-                               (colorize("BAD", "*"), postemerge,),
-                               level=logging.ERROR, noiselevel=-1)
-
-       clean_logs(settings)
-
-       if "--quiet" not in myopts and \
-               myaction is None and "@world" in myfiles:
-               show_depclean_suggestion()
-
-def show_depclean_suggestion():
-       out = portage.output.EOutput()
-       msg = "After world updates, it is important to remove " + \
-               "obsolete packages with emerge --depclean. Refer " + \
-               "to `man emerge` for more information."
-       for line in textwrap.wrap(msg, 72):
-               out.ewarn(line)
-
 def multiple_actions(action1, action2):
        sys.stderr.write("\n!!! Multiple actions requested... Please choose one only.\n")
        sys.stderr.write("!!! '%s' or '%s'\n\n" % (action1, action2))
@@ -1090,306 +933,6 @@ def parse_opts(tmpcmdline, silent=False):
 
        return myaction, myopts, myfiles
 
-# Warn about features that may confuse users and
-# lead them to report invalid bugs.
-_emerge_features_warn = frozenset(['keeptemp', 'keepwork'])
-
-def validate_ebuild_environment(trees):
-       features_warn = set()
-       for myroot in trees:
-               settings = trees[myroot]["vartree"].settings
-               settings.validate()
-               features_warn.update(
-                       _emerge_features_warn.intersection(settings.features))
-
-       if features_warn:
-               msg = "WARNING: The FEATURES variable contains one " + \
-                       "or more values that should be disabled under " + \
-                       "normal circumstances: %s" % " ".join(features_warn)
-               out = portage.output.EOutput()
-               for line in textwrap.wrap(msg, 65):
-                       out.ewarn(line)
-
-def apply_priorities(settings):
-       ionice(settings)
-       nice(settings)
-
-def nice(settings):
-       try:
-               os.nice(int(settings.get("PORTAGE_NICENESS", "0")))
-       except (OSError, ValueError) as e:
-               out = portage.output.EOutput()
-               out.eerror("Failed to change nice value to '%s'" % \
-                       settings["PORTAGE_NICENESS"])
-               out.eerror("%s\n" % str(e))
-
-def ionice(settings):
-
-       ionice_cmd = settings.get("PORTAGE_IONICE_COMMAND")
-       if ionice_cmd:
-               ionice_cmd = portage.util.shlex_split(ionice_cmd)
-       if not ionice_cmd:
-               return
-
-       variables = {"PID" : str(os.getpid())}
-       cmd = [varexpand(x, mydict=variables) for x in ionice_cmd]
-
-       try:
-               rval = portage.process.spawn(cmd, env=os.environ)
-       except portage.exception.CommandNotFound:
-               # The OS kernel probably doesn't support ionice,
-               # so return silently.
-               return
-
-       if rval != os.EX_OK:
-               out = portage.output.EOutput()
-               out.eerror("PORTAGE_IONICE_COMMAND returned %d" % (rval,))
-               out.eerror("See the make.conf(5) man page for PORTAGE_IONICE_COMMAND usage instructions.")
-
-def clean_logs(settings):
-
-       if "clean-logs" not in settings.features:
-               return
-
-       logdir = settings.get("PORT_LOGDIR")
-       if logdir is None or not os.path.isdir(logdir):
-               return
-
-       cleanlogs = CleanLogs()
-       errors = cleanlogs.clean(settings=settings)
-       if errors:
-               out = portage.output.EOutput()
-               for msg in errors:
-                       out.eerror(msg)
-
-def setconfig_fallback(root_config):
-       setconfig = root_config.setconfig
-       setconfig._create_default_config()
-       setconfig._parse(update=True)
-       root_config.sets = setconfig.getSets()
-
-def get_missing_sets(root_config):
-       # emerge requires existence of "world", "selected", and "system"
-       missing_sets = []
-
-       for s in ("selected", "system", "world",):
-               if s not in root_config.sets:
-                       missing_sets.append(s)
-
-       return missing_sets
-
-def missing_sets_warning(root_config, missing_sets):
-       if len(missing_sets) > 2:
-               missing_sets_str = ", ".join('"%s"' % s for s in missing_sets[:-1])
-               missing_sets_str += ', and "%s"' % missing_sets[-1]
-       elif len(missing_sets) == 2:
-               missing_sets_str = '"%s" and "%s"' % tuple(missing_sets)
-       else:
-               missing_sets_str = '"%s"' % missing_sets[-1]
-       msg = ["emerge: incomplete set configuration, " + \
-               "missing set(s): %s" % missing_sets_str]
-       if root_config.sets:
-               msg.append("        sets defined: %s" % ", ".join(root_config.sets))
-       global_config_path = portage.const.GLOBAL_CONFIG_PATH
-       if root_config.settings['EPREFIX']:
-               global_config_path = os.path.join(root_config.settings['EPREFIX'],
-                               portage.const.GLOBAL_CONFIG_PATH.lstrip(os.sep))
-       msg.append("        This usually means that '%s'" % \
-               (os.path.join(global_config_path, "sets/portage.conf"),))
-       msg.append("        is missing or corrupt.")
-       msg.append("        Falling back to default world and system set configuration!!!")
-       for line in msg:
-               writemsg_level(line + "\n", level=logging.ERROR, noiselevel=-1)
-
-def ensure_required_sets(trees):
-       warning_shown = False
-       for root_trees in trees.values():
-               missing_sets = get_missing_sets(root_trees["root_config"])
-               if missing_sets and not warning_shown:
-                       warning_shown = True
-                       missing_sets_warning(root_trees["root_config"], missing_sets)
-               if missing_sets:
-                       setconfig_fallback(root_trees["root_config"])
-
-def expand_set_arguments(myfiles, myaction, root_config):
-       retval = os.EX_OK
-       setconfig = root_config.setconfig
-
-       sets = setconfig.getSets()
-
-       # In order to know exactly which atoms/sets should be added to the
-       # world file, the depgraph performs set expansion later. It will get
-       # confused about where the atoms came from if it's not allowed to
-       # expand them itself.
-       do_not_expand = (None, )
-       newargs = []
-       for a in myfiles:
-               if a in ("system", "world"):
-                       newargs.append(SETPREFIX+a)
-               else:
-                       newargs.append(a)
-       myfiles = newargs
-       del newargs
-       newargs = []
-
-       # separators for set arguments
-       ARG_START = "{"
-       ARG_END = "}"
-
-       for i in range(0, len(myfiles)):
-               if myfiles[i].startswith(SETPREFIX):
-                       start = 0
-                       end = 0
-                       x = myfiles[i][len(SETPREFIX):]
-                       newset = ""
-                       while x:
-                               start = x.find(ARG_START)
-                               end = x.find(ARG_END)
-                               if start > 0 and start < end:
-                                       namepart = x[:start]
-                                       argpart = x[start+1:end]
-
-                                       # TODO: implement proper quoting
-                                       args = argpart.split(",")
-                                       options = {}
-                                       for a in args:
-                                               if "=" in a:
-                                                       k, v  = a.split("=", 1)
-                                                       options[k] = v
-                                               else:
-                                                       options[a] = "True"
-                                       setconfig.update(namepart, options)
-                                       newset += (x[:start-len(namepart)]+namepart)
-                                       x = x[end+len(ARG_END):]
-                               else:
-                                       newset += x
-                                       x = ""
-                       myfiles[i] = SETPREFIX+newset
-
-       sets = setconfig.getSets()
-
-       # display errors that occurred while loading the SetConfig instance
-       for e in setconfig.errors:
-               print(colorize("BAD", "Error during set creation: %s" % e))
-
-       unmerge_actions = ("unmerge", "prune", "clean", "depclean")
-
-       for a in myfiles:
-               if a.startswith(SETPREFIX):             
-                               s = a[len(SETPREFIX):]
-                               if s not in sets:
-                                       display_missing_pkg_set(root_config, s)
-                                       return (None, 1)
-                               if s == "installed":
-                                       msg = ("The @installed set is deprecated and will soon be "
-                                       "removed. Please refer to bug #387059 for details.")
-                                       out = portage.output.EOutput()
-                                       for line in textwrap.wrap(msg, 50):
-                                               out.ewarn(line)
-                               setconfig.active.append(s)
-                               try:
-                                       set_atoms = setconfig.getSetAtoms(s)
-                               except portage.exception.PackageSetNotFound as e:
-                                       writemsg_level(("emerge: the given set '%s' " + \
-                                               "contains a non-existent set named '%s'.\n") % \
-                                               (s, e), level=logging.ERROR, noiselevel=-1)
-                                       if s in ('world', 'selected') and \
-                                               SETPREFIX + e.value in sets['selected']:
-                                               writemsg_level(("Use `emerge --deselect %s%s` to "
-                                                       "remove this set from world_sets.\n") %
-                                                       (SETPREFIX, e,), level=logging.ERROR,
-                                                       noiselevel=-1)
-                                       return (None, 1)
-                               if myaction in unmerge_actions and \
-                                               not sets[s].supportsOperation("unmerge"):
-                                       sys.stderr.write("emerge: the given set '%s' does " % s + \
-                                               "not support unmerge operations\n")
-                                       retval = 1
-                               elif not set_atoms:
-                                       print("emerge: '%s' is an empty set" % s)
-                               elif myaction not in do_not_expand:
-                                       newargs.extend(set_atoms)
-                               else:
-                                       newargs.append(SETPREFIX+s)
-                               for e in sets[s].errors:
-                                       print(e)
-               else:
-                       newargs.append(a)
-       return (newargs, retval)
-
-def repo_name_check(trees):
-       missing_repo_names = set()
-       for root_trees in trees.values():
-               porttree = root_trees.get("porttree")
-               if porttree:
-                       portdb = porttree.dbapi
-                       missing_repo_names.update(portdb.getMissingRepoNames())
-                       if portdb.porttree_root in missing_repo_names and \
-                               not os.path.exists(os.path.join(
-                               portdb.porttree_root, "profiles")):
-                               # This is normal if $PORTDIR happens to be empty,
-                               # so don't warn about it.
-                               missing_repo_names.remove(portdb.porttree_root)
-
-       if missing_repo_names:
-               msg = []
-               msg.append("WARNING: One or more repositories " + \
-                       "have missing repo_name entries:")
-               msg.append("")
-               for p in missing_repo_names:
-                       msg.append("\t%s/profiles/repo_name" % (p,))
-               msg.append("")
-               msg.extend(textwrap.wrap("NOTE: Each repo_name entry " + \
-                       "should be a plain text file containing a unique " + \
-                       "name for the repository on the first line.", 70))
-               msg.append("\n")
-               writemsg_level("".join("%s\n" % l for l in msg),
-                       level=logging.WARNING, noiselevel=-1)
-
-       return bool(missing_repo_names)
-
-def repo_name_duplicate_check(trees):
-       ignored_repos = {}
-       for root, root_trees in trees.items():
-               if 'porttree' in root_trees:
-                       portdb = root_trees['porttree'].dbapi
-                       if portdb.settings.get('PORTAGE_REPO_DUPLICATE_WARN') != '0':
-                               for repo_name, paths in portdb.getIgnoredRepos():
-                                       k = (root, repo_name, portdb.getRepositoryPath(repo_name))
-                                       ignored_repos.setdefault(k, []).extend(paths)
-
-       if ignored_repos:
-               msg = []
-               msg.append('WARNING: One or more repositories ' + \
-                       'have been ignored due to duplicate')
-               msg.append('  profiles/repo_name entries:')
-               msg.append('')
-               for k in sorted(ignored_repos):
-                       msg.append('  %s overrides' % ", ".join(k))
-                       for path in ignored_repos[k]:
-                               msg.append('    %s' % (path,))
-                       msg.append('')
-               msg.extend('  ' + x for x in textwrap.wrap(
-                       "All profiles/repo_name entries must be unique in order " + \
-                       "to avoid having duplicates ignored. " + \
-                       "Set PORTAGE_REPO_DUPLICATE_WARN=\"0\" in " + \
-                       "/etc/make.conf if you would like to disable this warning."))
-               msg.append("\n")
-               writemsg_level(''.join('%s\n' % l for l in msg),
-                       level=logging.WARNING, noiselevel=-1)
-
-       return bool(ignored_repos)
-
-def config_protect_check(trees):
-       for root, root_trees in trees.items():
-               settings = root_trees["root_config"].settings
-               if not settings.get("CONFIG_PROTECT"):
-                       msg = "!!! CONFIG_PROTECT is empty"
-                       if settings["ROOT"] != "/":
-                               msg += " for '%s'" % root
-                       msg += "\n"
-                       writemsg_level(msg, level=logging.WARN, noiselevel=-1)
-
 def profile_check(trees, myaction):
        if myaction in ("help", "info", "search", "sync", "version"):
                return os.EX_OK
@@ -1407,16 +950,6 @@ def profile_check(trees, myaction):
                return 1
        return os.EX_OK
 
-def check_procfs():
-       procfs_path = '/proc'
-       if platform.system() not in ("Linux",) or \
-               os.path.ismount(procfs_path):
-               return os.EX_OK
-       msg = "It seems that %s is not mounted. You have been warned." % procfs_path
-       writemsg_level("".join("!!! %s\n" % l for l in textwrap.wrap(msg, 70)),
-               level=logging.ERROR, noiselevel=-1)
-       return 1
-
 def emerge_main(args=None):
        """
        @param args: command arguments (default: sys.argv[1:])
@@ -1456,7 +989,6 @@ def emerge_main(args=None):
        if myaction == "sync":
                portage._sync_disabled_warnings = True
        settings, trees, mtimedb = load_emerge_config()
-       portdb = trees[settings['EROOT']]['porttree'].dbapi
        rval = profile_check(trees, myaction)
        if rval != os.EX_OK:
                return rval
@@ -1467,409 +999,4 @@ def emerge_main(args=None):
        tmpcmdline.extend(args)
        myaction, myopts, myfiles = parse_opts(tmpcmdline)
 
-       # skip global updates prior to sync, since it's called after sync
-       if myaction not in ('help', 'info', 'sync', 'version') and \
-               myopts.get('--package-moves') != 'n' and \
-               _global_updates(trees, mtimedb["updates"], quiet=("--quiet" in myopts)):
-               mtimedb.commit()
-               # Reload the whole config from scratch.
-               settings, trees, mtimedb = load_emerge_config(trees=trees)
-               portdb = trees[settings['EROOT']]['porttree'].dbapi
-
-       xterm_titles = "notitles" not in settings.features
-       if xterm_titles:
-               xtermTitle("emerge")
-
-       if "--digest" in myopts:
-               os.environ["FEATURES"] = os.environ.get("FEATURES","") + " digest"
-               # Reload the whole config from scratch so that the portdbapi internal
-               # config is updated with new FEATURES.
-               settings, trees, mtimedb = load_emerge_config(trees=trees)
-               portdb = trees[settings['EROOT']]['porttree'].dbapi
-
-       # NOTE: adjust_configs() can map options to FEATURES, so any relevant
-       # options adjustments should be made prior to calling adjust_configs().
-       if "--buildpkgonly" in myopts:
-               myopts["--buildpkg"] = True
-
-       adjust_configs(myopts, trees)
-       apply_priorities(settings)
-
-       if myaction == 'version':
-               writemsg_stdout(getportageversion(
-                       settings["PORTDIR"], None,
-                       settings.profile_path, settings["CHOST"],
-                       trees[settings['EROOT']]['vartree'].dbapi) + '\n', noiselevel=-1)
-               return 0
-       elif myaction == 'help':
-               emerge_help()
-               return 0
-
-       spinner = stdout_spinner()
-       if "candy" in settings.features:
-               spinner.update = spinner.update_scroll
-
-       if "--quiet" not in myopts:
-               portage.deprecated_profile_check(settings=settings)
-               if portage.const._ENABLE_REPO_NAME_WARN:
-                       # Bug #248603 - Disable warnings about missing
-                       # repo_name entries for stable branch.
-                       repo_name_check(trees)
-               repo_name_duplicate_check(trees)
-               config_protect_check(trees)
-       check_procfs()
-
-       if "getbinpkg" in settings.features:
-               myopts["--getbinpkg"] = True
-
-       if "--getbinpkgonly" in myopts:
-               myopts["--getbinpkg"] = True
-
-       if "--getbinpkgonly" in myopts:
-               myopts["--usepkgonly"] = True
-
-       if "--getbinpkg" in myopts:
-               myopts["--usepkg"] = True
-
-       if "--usepkgonly" in myopts:
-               myopts["--usepkg"] = True
-
-       if "--buildpkgonly" in myopts:
-               # --buildpkgonly will not merge anything, so
-               # it cancels all binary package options.
-               for opt in ("--getbinpkg", "--getbinpkgonly",
-                       "--usepkg", "--usepkgonly"):
-                       myopts.pop(opt, None)
-
-       for mytrees in trees.values():
-               mydb = mytrees["porttree"].dbapi
-               # Freeze the portdbapi for performance (memoize all xmatch results).
-               mydb.freeze()
-
-               if myaction in ('search', None) and \
-                       "--usepkg" in myopts:
-                       # Populate the bintree with current --getbinpkg setting.
-                       # This needs to happen before expand_set_arguments(), in case
-                       # any sets use the bintree.
-                       mytrees["bintree"].populate(
-                               getbinpkgs="--getbinpkg" in myopts)
-
-       del mytrees, mydb
-
-       if "moo" in myfiles:
-               print(COWSAY_MOO % platform.system())
-               msg = ("The above `emerge moo` display is deprecated. "
-                       "Please use `emerge --moo` instead.")
-               for line in textwrap.wrap(msg, 50):
-                       print(" %s %s" % (colorize("WARN", "*"), line))
-
-       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(colorize("BAD", "\n*** emerging by path is broken and may not always work!!!\n"))
-                       break
-
-       root_config = trees[settings['EROOT']]['root_config']
-       if myaction == "moo":
-               print(COWSAY_MOO % platform.system())
-               return os.EX_OK
-       elif myaction == "list-sets":
-               writemsg_stdout("".join("%s\n" % s for s in sorted(root_config.sets)))
-               return os.EX_OK
-       elif myaction == "check-news":
-               news_counts = count_unread_news(
-                       root_config.trees["porttree"].dbapi,
-                       root_config.trees["vartree"].dbapi)
-               if any(news_counts.values()):
-                       display_news_notifications(news_counts)
-               elif "--quiet" not in myopts:
-                       print("", colorize("GOOD", "*"), "No news items were found.")
-               return os.EX_OK
-
-       ensure_required_sets(trees)
-
-       # only expand sets for actions taking package arguments
-       oldargs = myfiles[:]
-       if myaction in ("clean", "config", "depclean", "info", "prune", "unmerge", None):
-               myfiles, retval = expand_set_arguments(myfiles, myaction, root_config)
-               if retval != os.EX_OK:
-                       return retval
-
-               # Need to handle empty sets specially, otherwise emerge will react 
-               # with the help message for empty argument lists
-               if oldargs and not myfiles:
-                       print("emerge: no targets left after set expansion")
-                       return 0
-
-       if ("--tree" in myopts) and ("--columns" in myopts):
-               print("emerge: can't specify both of \"--tree\" and \"--columns\".")
-               return 1
-
-       if '--emptytree' in myopts and '--noreplace' in myopts:
-               writemsg_level("emerge: can't specify both of " + \
-                       "\"--emptytree\" and \"--noreplace\".\n",
-                       level=logging.ERROR, noiselevel=-1)
-               return 1
-
-       if ("--quiet" in myopts):
-               spinner.update = spinner.update_quiet
-               portage.util.noiselimit = -1
-
-       if "--fetch-all-uri" in myopts:
-               myopts["--fetchonly"] = True
-
-       if "--skipfirst" in myopts and "--resume" not in myopts:
-               myopts["--resume"] = True
-
-       # Allow -p to remove --ask
-       if "--pretend" in myopts:
-               myopts.pop("--ask", None)
-
-       # forbid --ask when not in a terminal
-       # note: this breaks `emerge --ask | tee logfile`, but that doesn't work anyway.
-       if ("--ask" in myopts) and (not sys.stdin.isatty()):
-               portage.writemsg("!!! \"--ask\" should only be used in a terminal. Exiting.\n",
-                       noiselevel=-1)
-               return 1
-
-       if settings.get("PORTAGE_DEBUG", "") == "1":
-               spinner.update = spinner.update_quiet
-               portage.util.noiselimit = 0
-               if "python-trace" in settings.features:
-                       portage.debug.set_trace(True)
-
-       if not ("--quiet" in myopts):
-               if '--nospinner' in myopts or \
-                       settings.get('TERM') == 'dumb' or \
-                       not sys.stdout.isatty():
-                       spinner.update = spinner.update_basic
-
-       if "--debug" in myopts:
-               print("myaction", myaction)
-               print("myopts", myopts)
-
-       if not myaction and not myfiles and "--resume" not in myopts:
-               emerge_help()
-               return 1
-
-       pretend = "--pretend" in myopts
-       fetchonly = "--fetchonly" in myopts or "--fetch-all-uri" in myopts
-       buildpkgonly = "--buildpkgonly" in myopts
-
-       # 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"):
-                       need_superuser = myaction in ('clean', 'depclean', 'deselect',
-                               'prune', 'unmerge') or not \
-                               (fetchonly or \
-                               (buildpkgonly and portage.data.secpass >= 1) or \
-                               myaction in ("metadata", "regen", "sync"))
-                       if portage.secpass < 1 or \
-                               need_superuser:
-                               if need_superuser:
-                                       access_desc = "superuser"
-                               else:
-                                       access_desc = "portage group"
-                               # Always show portage_group_warning() when only portage group
-                               # access is required but the user is not in the portage group.
-                               if "--ask" in myopts:
-                                       writemsg_stdout("This action requires %s access...\n" % \
-                                               (access_desc,), noiselevel=-1)
-                                       if portage.secpass < 1 and not need_superuser:
-                                               portage.data.portage_group_warning()
-                                       if userquery("Would you like to add --pretend to options?",
-                                               "--ask-enter-invalid" in myopts) == "No":
-                                               return 128 + signal.SIGINT
-                                       myopts["--pretend"] = True
-                                       del myopts["--ask"]
-                               else:
-                                       sys.stderr.write(("emerge: %s access is required\n") \
-                                               % access_desc)
-                                       if portage.secpass < 1 and not need_superuser:
-                                               portage.data.portage_group_warning()
-                                       return 1
-
-       # Disable emergelog for everything except build or unmerge operations.
-       # This helps minimize parallel emerge.log entries that can confuse log
-       # parsers like genlop.
-       disable_emergelog = False
-       for x in ("--pretend", "--fetchonly", "--fetch-all-uri"):
-               if x in myopts:
-                       disable_emergelog = True
-                       break
-       if disable_emergelog:
-               pass
-       elif myaction in ("search", "info"):
-               disable_emergelog = True
-       elif portage.data.secpass < 1:
-               disable_emergelog = True
-
-       import _emerge.emergelog
-       _emerge.emergelog._disable = disable_emergelog
-
-       if not disable_emergelog:
-               if 'EMERGE_LOG_DIR' in settings:
-                       try:
-                               # At least the parent needs to exist for the lock file.
-                               portage.util.ensure_dirs(settings['EMERGE_LOG_DIR'])
-                       except portage.exception.PortageException as e:
-                               writemsg_level("!!! Error creating directory for " + \
-                                       "EMERGE_LOG_DIR='%s':\n!!! %s\n" % \
-                                       (settings['EMERGE_LOG_DIR'], e),
-                                       noiselevel=-1, level=logging.ERROR)
-                               portage.util.ensure_dirs(_emerge.emergelog._emerge_log_dir)
-                       else:
-                               _emerge.emergelog._emerge_log_dir = settings["EMERGE_LOG_DIR"]
-               else:
-                       _emerge.emergelog._emerge_log_dir = os.path.join(os.sep,
-                               settings["EPREFIX"].lstrip(os.sep), "var", "log")
-                       portage.util.ensure_dirs(_emerge.emergelog._emerge_log_dir)
-
-       if not "--pretend" in myopts:
-               emergelog(xterm_titles, "Started emerge on: "+\
-                       _unicode_decode(
-                               time.strftime("%b %d, %Y %H:%M:%S", time.localtime()),
-                               encoding=_encodings['content'], errors='replace'))
-               myelogstr=""
-               if myopts:
-                       opt_list = []
-                       for opt, arg in myopts.items():
-                               if arg is True:
-                                       opt_list.append(opt)
-                               elif isinstance(arg, list):
-                                       # arguments like --exclude that use 'append' action
-                                       for x in arg:
-                                               opt_list.append("%s=%s" % (opt, x))
-                               else:
-                                       opt_list.append("%s=%s" % (opt, arg))
-                       myelogstr=" ".join(opt_list)
-               if myaction:
-                       myelogstr += " --" + myaction
-               if myfiles:
-                       myelogstr += " " + " ".join(oldargs)
-               emergelog(xterm_titles, " *** emerge " + myelogstr)
-       del oldargs
-
-       def emergeexitsig(signum, frame):
-               signal.signal(signal.SIGTERM, signal.SIG_IGN)
-               portage.util.writemsg("\n\nExiting on signal %(signal)s\n" % {"signal":signum})
-               sys.exit(128 + signum)
-
-       signal.signal(signal.SIGTERM, emergeexitsig)
-
-       def emergeexit():
-               """This gets out final log message in before we quit."""
-               if "--pretend" not in myopts:
-                       emergelog(xterm_titles, " *** terminating.")
-               if xterm_titles:
-                       xtermTitleReset()
-       portage.atexit_register(emergeexit)
-
-       if myaction in ("config", "metadata", "regen", "sync"):
-               if "--pretend" in myopts:
-                       sys.stderr.write(("emerge: The '%s' action does " + \
-                               "not support '--pretend'.\n") % myaction)
-                       return 1
-
-       if "sync" == myaction:
-               return action_sync(settings, trees, mtimedb, myopts, myaction)
-       elif "metadata" == myaction:
-               action_metadata(settings, portdb, myopts)
-       elif myaction=="regen":
-               validate_ebuild_environment(trees)
-               return action_regen(settings, portdb, myopts.get("--jobs"),
-                       myopts.get("--load-average"))
-       # HELP action
-       elif "config"==myaction:
-               validate_ebuild_environment(trees)
-               action_config(settings, trees, myopts, myfiles)
-
-       # SEARCH action
-       elif "search"==myaction:
-               validate_ebuild_environment(trees)
-               action_search(trees[settings['EROOT']]['root_config'],
-                       myopts, myfiles, spinner)
-
-       elif myaction in ('clean', 'depclean', 'deselect', 'prune', 'unmerge'):
-               validate_ebuild_environment(trees)
-               rval = action_uninstall(settings, trees, mtimedb["ldpath"],
-                       myopts, myaction, myfiles, spinner)
-               if not (myaction == 'deselect' or buildpkgonly or fetchonly or pretend):
-                       post_emerge(myaction, myopts, myfiles, settings['EROOT'],
-                               trees, mtimedb, rval)
-               return rval
-
-       elif myaction == 'info':
-
-               # Ensure atoms are valid before calling unmerge().
-               vardb = trees[settings['EROOT']]['vartree'].dbapi
-               portdb = trees[settings['EROOT']]['porttree'].dbapi
-               bindb = trees[settings['EROOT']]["bintree"].dbapi
-               valid_atoms = []
-               for x in myfiles:
-                       if is_valid_package_atom(x, allow_repo=True):
-                               try:
-                                       #look at the installed files first, if there is no match
-                                       #look at the ebuilds, since EAPI 4 allows running pkg_info
-                                       #on non-installed packages
-                                       valid_atom = dep_expand(x, mydb=vardb, settings=settings)
-                                       if valid_atom.cp.split("/")[0] == "null":
-                                               valid_atom = dep_expand(x, mydb=portdb, settings=settings)
-                                       if valid_atom.cp.split("/")[0] == "null" and "--usepkg" in myopts:
-                                               valid_atom = dep_expand(x, mydb=bindb, settings=settings)
-                                       valid_atoms.append(valid_atom)
-                               except portage.exception.AmbiguousPackageName as e:
-                                       msg = "The short ebuild name \"" + x + \
-                                               "\" is ambiguous.  Please specify " + \
-                                               "one of the following " + \
-                                               "fully-qualified ebuild names instead:"
-                                       for line in textwrap.wrap(msg, 70):
-                                               writemsg_level("!!! %s\n" % (line,),
-                                                       level=logging.ERROR, noiselevel=-1)
-                                       for i in e.args[0]:
-                                               writemsg_level("    %s\n" % colorize("INFORM", i),
-                                                       level=logging.ERROR, noiselevel=-1)
-                                       writemsg_level("\n", level=logging.ERROR, noiselevel=-1)
-                                       return 1
-                               continue
-                       msg = []
-                       msg.append("'%s' is not a valid package atom." % (x,))
-                       msg.append("Please check ebuild(5) for full details.")
-                       writemsg_level("".join("!!! %s\n" % line for line in msg),
-                               level=logging.ERROR, noiselevel=-1)
-                       return 1
-
-               return action_info(settings, trees, myopts, valid_atoms)
-
-       # "update", "system", or just process files:
-       else:
-               validate_ebuild_environment(trees)
-
-               for x in myfiles:
-                       if x.startswith(SETPREFIX) or \
-                               is_valid_package_atom(x, allow_repo=True):
-                               continue
-                       if x[:1] == os.sep:
-                               continue
-                       try:
-                               os.lstat(x)
-                               continue
-                       except OSError:
-                               pass
-                       msg = []
-                       msg.append("'%s' is not a valid package atom." % (x,))
-                       msg.append("Please check ebuild(5) for full details.")
-                       writemsg_level("".join("!!! %s\n" % line for line in msg),
-                               level=logging.ERROR, noiselevel=-1)
-                       return 1
-
-               # GLEP 42 says to display news *after* an emerge --pretend
-               if "--pretend" not in myopts:
-                       display_news_notification(root_config, myopts)
-               retval = action_build(settings, trees, mtimedb,
-                       myopts, myaction, myfiles, spinner)
-               post_emerge(myaction, myopts, myfiles, settings['EROOT'],
-                       trees, mtimedb, retval)
-
-               return retval
+       return run_action(settings, trees, mtimedb, myaction, myopts, myfiles)
diff --git a/pym/_emerge/post_emerge.py b/pym/_emerge/post_emerge.py
new file mode 100644 (file)
index 0000000..d5f1ba5
--- /dev/null
@@ -0,0 +1,165 @@
+# Copyright 1999-2012 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+
+from __future__ import print_function
+
+import logging
+import textwrap
+
+import portage
+from portage import os
+from portage.emaint.modules.logs.logs import CleanLogs
+from portage.news import count_unread_news, display_news_notifications
+from portage.output import colorize
+from portage.util._dyn_libs.display_preserved_libs import \
+       display_preserved_libs
+from portage.util._info_files import chk_updated_info_files
+
+from .chk_updated_cfg_files import chk_updated_cfg_files
+from .emergelog import emergelog
+from ._flush_elog_mod_echo import _flush_elog_mod_echo
+
+def clean_logs(settings):
+
+       if "clean-logs" not in settings.features:
+               return
+
+       logdir = settings.get("PORT_LOGDIR")
+       if logdir is None or not os.path.isdir(logdir):
+               return
+
+       cleanlogs = CleanLogs()
+       errors = cleanlogs.clean(settings=settings)
+       if errors:
+               out = portage.output.EOutput()
+               for msg in errors:
+                       out.eerror(msg)
+
+def display_news_notification(root_config, myopts):
+       if "news" not in root_config.settings.features:
+               return
+       portdb = root_config.trees["porttree"].dbapi
+       vardb = root_config.trees["vartree"].dbapi
+       news_counts = count_unread_news(portdb, vardb)
+       display_news_notifications(news_counts)
+
+def show_depclean_suggestion():
+       out = portage.output.EOutput()
+       msg = "After world updates, it is important to remove " + \
+               "obsolete packages with emerge --depclean. Refer " + \
+               "to `man emerge` for more information."
+       for line in textwrap.wrap(msg, 72):
+               out.ewarn(line)
+
+def post_emerge(myaction, myopts, myfiles,
+       target_root, trees, mtimedb, retval):
+       """
+       Misc. things to run at the end of a merge session.
+
+       Update Info Files
+       Update Config Files
+       Update News Items
+       Commit mtimeDB
+       Display preserved libs warnings
+
+       @param myaction: The action returned from parse_opts()
+       @type myaction: String
+       @param myopts: emerge options
+       @type myopts: dict
+       @param myfiles: emerge arguments
+       @type myfiles: list
+       @param target_root: The target EROOT for myaction
+       @type target_root: String
+       @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
+       @type retval: Int
+       """
+
+       root_config = trees[target_root]["root_config"]
+       vardbapi = trees[target_root]['vartree'].dbapi
+       settings = vardbapi.settings
+       info_mtimes = mtimedb["info"]
+
+       # Load the most current variables from ${ROOT}/etc/profile.env
+       settings.unlock()
+       settings.reload()
+       settings.regenerate()
+       settings.lock()
+
+       config_protect = portage.util.shlex_split(
+               settings.get("CONFIG_PROTECT", ""))
+       infodirs = settings.get("INFOPATH","").split(":") + \
+               settings.get("INFODIR","").split(":")
+
+       os.chdir("/")
+
+       if retval == os.EX_OK:
+               exit_msg = " *** exiting successfully."
+       else:
+               exit_msg = " *** exiting unsuccessfully with status '%s'." % retval
+       emergelog("notitles" not in settings.features, exit_msg)
+
+       _flush_elog_mod_echo()
+
+       if not vardbapi._pkgs_changed:
+               # GLEP 42 says to display news *after* an emerge --pretend
+               if "--pretend" in myopts:
+                       display_news_notification(root_config, myopts)
+               # If vdb state has not changed then there's nothing else to do.
+               return
+
+       vdb_path = os.path.join(root_config.settings['EROOT'], portage.VDB_PATH)
+       portage.util.ensure_dirs(vdb_path)
+       vdb_lock = None
+       if os.access(vdb_path, os.W_OK) and not "--pretend" in myopts:
+               vardbapi.lock()
+               vdb_lock = True
+
+       if vdb_lock:
+               try:
+                       if "noinfo" not in settings.features:
+                               chk_updated_info_files(target_root,
+                                       infodirs, info_mtimes)
+                       mtimedb.commit()
+               finally:
+                       if vdb_lock:
+                               vardbapi.unlock()
+
+       # Explicitly load and prune the PreservedLibsRegistry in order
+       # to ensure that we do not display stale data.
+       vardbapi._plib_registry.load()
+
+       if vardbapi._plib_registry.hasEntries():
+               if "--quiet" in myopts:
+                       print()
+                       print(colorize("WARN", "!!!") + " existing preserved libs found")
+               else:
+                       print()
+                       print(colorize("WARN", "!!!") + " existing preserved libs:")
+                       display_preserved_libs(vardbapi)
+                       print("Use " + colorize("GOOD", "emerge @preserved-rebuild") +
+                               " to rebuild packages using these libraries")
+
+       chk_updated_cfg_files(settings['EROOT'], config_protect)
+
+       display_news_notification(root_config, myopts)
+
+       postemerge = os.path.join(settings["PORTAGE_CONFIGROOT"],
+               portage.USER_CONFIG_PATH, "bin", "post_emerge")
+       if os.access(postemerge, os.X_OK):
+               hook_retval = portage.process.spawn(
+                                               [postemerge], env=settings.environ())
+               if hook_retval != os.EX_OK:
+                       portage.util.writemsg_level(
+                               " %s spawn failed of %s\n" %
+                               (colorize("BAD", "*"), postemerge,),
+                               level=logging.ERROR, noiselevel=-1)
+
+       clean_logs(settings)
+
+       if "--quiet" not in myopts and \
+               myaction is None and "@world" in myfiles:
+               show_depclean_suggestion()