diff options
author | Zac Medico <zmedico@gentoo.org> | 2009-06-26 20:06:08 +0000 |
---|---|---|
committer | Zac Medico <zmedico@gentoo.org> | 2009-06-26 20:06:08 +0000 |
commit | 2801d985efc89ea124489b8b4b5d8452e40615a7 (patch) | |
tree | a0d4f13c42a5b58fc1e358b8c2a3275ff31d3b02 /pym/_emerge/main.py | |
parent | 6113aa2cd60a36ecd95bc379d0012bcb0c44ddfa (diff) | |
download | portage-2801d985efc89ea124489b8b4b5d8452e40615a7.tar.gz portage-2801d985efc89ea124489b8b4b5d8452e40615a7.tar.bz2 portage-2801d985efc89ea124489b8b4b5d8452e40615a7.zip |
Move __init__.py to main.py.
svn path=/main/trunk/; revision=13697
Diffstat (limited to 'pym/_emerge/main.py')
-rw-r--r-- | pym/_emerge/main.py | 1282 |
1 files changed, 1282 insertions, 0 deletions
diff --git a/pym/_emerge/main.py b/pym/_emerge/main.py new file mode 100644 index 000000000..f73bc6db4 --- /dev/null +++ b/pym/_emerge/main.py @@ -0,0 +1,1282 @@ +# Copyright 1999-2009 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 +# $Id$ + +import logging +import shlex +import signal +import sys +import textwrap +import os +import platform + +try: + import portage +except ImportError: + from os import path as osp + sys.path.insert(0, osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), "pym")) + import portage + +import _emerge.help +import portage.xpak, commands, errno, re, time +from portage.output import colorize, xtermTitleReset +from portage.output import create_color_func +good = create_color_func("GOOD") +bad = create_color_func("BAD") + +import portage.elog +import portage.dep +portage.dep._dep_check_strict = True +import portage.util +import portage.locks +import portage.exception +from portage.data import secpass +from portage.util import normalize_path as normpath +from portage.util import writemsg, writemsg_level +from portage.sets import SETPREFIX + +from _emerge.actions import action_config, action_sync, action_metadata, \ + action_regen, action_search, action_uninstall, action_info, action_build, \ + adjust_config, chk_updated_cfg_files, display_missing_pkg_set, \ + display_news_notification, getportageversion, load_emerge_config +from _emerge.emergelog import emergelog +from _emerge._flush_elog_mod_echo import _flush_elog_mod_echo +from _emerge.is_valid_package_atom import is_valid_package_atom +from _emerge.stdout_spinner import stdout_spinner + +options=[ +"--ask", "--alphabetical", +"--buildpkg", "--buildpkgonly", +"--changelog", "--columns", +"--complete-graph", +"--debug", "--deep", +"--digest", +"--emptytree", +"--fetchonly", "--fetch-all-uri", +"--getbinpkg", "--getbinpkgonly", +"--help", "--ignore-default-opts", +"--keep-going", +"--noconfmem", +"--newuse", +"--nodeps", "--noreplace", +"--nospinner", "--oneshot", +"--onlydeps", "--pretend", +"--quiet", "--resume", +"--searchdesc", "--selective", +"--skipfirst", +"--tree", +"--update", +"--usepkg", "--usepkgonly", +"--verbose", +] + +shortmapping={ +"1":"--oneshot", +"a":"--ask", +"b":"--buildpkg", "B":"--buildpkgonly", +"c":"--clean", "C":"--unmerge", +"d":"--debug", "D":"--deep", +"e":"--emptytree", +"f":"--fetchonly", "F":"--fetch-all-uri", +"g":"--getbinpkg", "G":"--getbinpkgonly", +"h":"--help", +"k":"--usepkg", "K":"--usepkgonly", +"l":"--changelog", +"n":"--noreplace", "N":"--newuse", +"o":"--onlydeps", "O":"--nodeps", +"p":"--pretend", "P":"--prune", +"q":"--quiet", +"s":"--search", "S":"--searchdesc", +"t":"--tree", +"u":"--update", +"v":"--verbose", "V":"--version" +} + +def chk_updated_info_files(root, infodirs, prev_mtimes, retval): + + if os.path.exists("/usr/bin/install-info"): + out = portage.output.EOutput() + regen_infodirs=[] + for z in infodirs: + if z=='': + continue + inforoot=normpath(root+z) + if os.path.isdir(inforoot): + infomtime = long(os.stat(inforoot).st_mtime) + if inforoot not in prev_mtimes or \ + prev_mtimes[inforoot] != infomtime: + regen_infodirs.append(inforoot) + + if not regen_infodirs: + portage.writemsg_stdout("\n") + out.einfo("GNU info directory index is up-to-date.") + else: + portage.writemsg_stdout("\n") + out.einfo("Regenerating GNU info directory index...") + + dir_extensions = ("", ".gz", ".bz2") + icount=0 + badcount=0 + errmsg = "" + for inforoot in regen_infodirs: + if inforoot=='': + continue + + if not os.path.isdir(inforoot) or \ + not os.access(inforoot, os.W_OK): + continue + + file_list = os.listdir(inforoot) + file_list.sort() + dir_file = os.path.join(inforoot, "dir") + moved_old_dir = False + processed_count = 0 + for x in file_list: + if x.startswith(".") or \ + os.path.isdir(os.path.join(inforoot, x)): + continue + if x.startswith("dir"): + skip = False + for ext in dir_extensions: + if x == "dir" + ext or \ + x == "dir" + ext + ".old": + skip = True + break + if skip: + continue + if processed_count == 0: + for ext in dir_extensions: + try: + os.rename(dir_file + ext, dir_file + ext + ".old") + moved_old_dir = True + except EnvironmentError, e: + if e.errno != errno.ENOENT: + raise + del e + processed_count += 1 + myso=commands.getstatusoutput("LANG=C LANGUAGE=C /usr/bin/install-info --dir-file="+inforoot+"/dir "+inforoot+"/"+x)[1] + existsstr="already exists, for file `" + if myso!="": + if re.search(existsstr,myso): + # Already exists... Don't increment the count for this. + pass + elif myso[:44]=="install-info: warning: no info dir entry in ": + # This info file doesn't contain a DIR-header: install-info produces this + # (harmless) warning (the --quiet switch doesn't seem to work). + # Don't increment the count for this. + pass + else: + badcount=badcount+1 + errmsg += myso + "\n" + icount=icount+1 + + if moved_old_dir and not os.path.exists(dir_file): + # We didn't generate a new dir file, so put the old file + # back where it was originally found. + for ext in dir_extensions: + try: + os.rename(dir_file + ext + ".old", dir_file + ext) + except EnvironmentError, e: + if e.errno != errno.ENOENT: + raise + del e + + # Clean dir.old cruft so that they don't prevent + # unmerge of otherwise empty directories. + for ext in dir_extensions: + try: + os.unlink(dir_file + ext + ".old") + except EnvironmentError, e: + if e.errno != errno.ENOENT: + raise + del e + + #update mtime so we can potentially avoid regenerating. + prev_mtimes[inforoot] = long(os.stat(inforoot).st_mtime) + + if badcount: + out.eerror("Processed %d info files; %d errors." % \ + (icount, badcount)) + writemsg_level(errmsg, level=logging.ERROR, noiselevel=-1) + else: + if icount > 0: + out.einfo("Processed %d info files." % (icount,)) + + +def display_preserved_libs(vardbapi): + MAX_DISPLAY = 3 + + # Ensure the registry is consistent with existing files. + vardbapi.plib_registry.pruneNonExisting() + + if vardbapi.plib_registry.hasEntries(): + print + print colorize("WARN", "!!!") + " existing preserved libs:" + plibdata = vardbapi.plib_registry.getPreservedLibs() + linkmap = vardbapi.linkmap + consumer_map = {} + owners = {} + linkmap_broken = False + + try: + linkmap.rebuild() + except portage.exception.CommandNotFound, e: + writemsg_level("!!! Command Not Found: %s\n" % (e,), + level=logging.ERROR, noiselevel=-1) + del e + linkmap_broken = True + else: + search_for_owners = set() + for cpv in plibdata: + internal_plib_keys = set(linkmap._obj_key(f) \ + for f in plibdata[cpv]) + for f in plibdata[cpv]: + if f in consumer_map: + continue + consumers = [] + for c in linkmap.findConsumers(f): + # Filter out any consumers that are also preserved libs + # belonging to the same package as the provider. + if linkmap._obj_key(c) not in internal_plib_keys: + consumers.append(c) + consumers.sort() + consumer_map[f] = consumers + search_for_owners.update(consumers[:MAX_DISPLAY+1]) + + owners = vardbapi._owners.getFileOwnerMap(search_for_owners) + + for cpv in plibdata: + print colorize("WARN", ">>>") + " package: %s" % cpv + samefile_map = {} + for f in plibdata[cpv]: + obj_key = linkmap._obj_key(f) + alt_paths = samefile_map.get(obj_key) + if alt_paths is None: + alt_paths = set() + samefile_map[obj_key] = alt_paths + alt_paths.add(f) + + for alt_paths in samefile_map.itervalues(): + alt_paths = sorted(alt_paths) + for p in alt_paths: + print colorize("WARN", " * ") + " - %s" % (p,) + f = alt_paths[0] + consumers = consumer_map.get(f, []) + for c in consumers[:MAX_DISPLAY]: + print colorize("WARN", " * ") + " used by %s (%s)" % \ + (c, ", ".join(x.mycpv for x in owners.get(c, []))) + if len(consumers) == MAX_DISPLAY + 1: + print colorize("WARN", " * ") + " used by %s (%s)" % \ + (consumers[MAX_DISPLAY], ", ".join(x.mycpv \ + for x in owners.get(consumers[MAX_DISPLAY], []))) + elif len(consumers) > MAX_DISPLAY: + print colorize("WARN", " * ") + " used by %d other files" % (len(consumers) - MAX_DISPLAY) + print "Use " + colorize("GOOD", "emerge @preserved-rebuild") + " to rebuild packages using these libraries" + + +def post_emerge(root_config, myopts, 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 + Exit Emerge + + @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 + @rype: None + @returns: + 1. Calls sys.exit(retval) + """ + + target_root = root_config.root + trees = { target_root : root_config.trees } + 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 = settings.get("CONFIG_PROTECT","").split() + 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() + + counter_hash = settings.get("PORTAGE_COUNTER_HASH") + if "--pretend" in myopts or (counter_hash is not None and \ + counter_hash == vardbapi._counter_hash()): + display_news_notification(root_config, myopts) + # If vdb state has not changed then there's nothing else to do. + sys.exit(retval) + + vdb_path = os.path.join(target_root, 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: + vdb_lock = portage.locks.lockdir(vdb_path) + + if vdb_lock: + try: + if "noinfo" not in settings.features: + chk_updated_info_files(target_root, + infodirs, info_mtimes, retval) + mtimedb.commit() + finally: + if vdb_lock: + portage.locks.unlockdir(vdb_lock) + + chk_updated_cfg_files(target_root, config_protect) + + display_news_notification(root_config, myopts) + if retval in (None, os.EX_OK) or (not "--pretend" in myopts): + display_preserved_libs(vardbapi) + + sys.exit(retval) + + +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)) + sys.exit(1) + +def insert_optional_args(args): + """ + Parse optional arguments and insert a value if one has + not been provided. This is done before feeding the args + to the optparse parser since that parser does not support + this feature natively. + """ + + new_args = [] + jobs_opts = ("-j", "--jobs") + default_arg_opts = { + '--deselect' : ('n',), + '--root-deps' : ('rdeps',), + } + arg_stack = args[:] + arg_stack.reverse() + while arg_stack: + arg = arg_stack.pop() + + default_arg_choices = default_arg_opts.get(arg) + if default_arg_choices is not None: + new_args.append(arg) + if arg_stack and arg_stack[-1] in default_arg_choices: + new_args.append(arg_stack.pop()) + else: + # insert default argument + new_args.append('True') + continue + + short_job_opt = bool("j" in arg and arg[:1] == "-" and arg[:2] != "--") + if not (short_job_opt or arg in jobs_opts): + new_args.append(arg) + continue + + # Insert an empty placeholder in order to + # satisfy the requirements of optparse. + + new_args.append("--jobs") + job_count = None + saved_opts = None + if short_job_opt and len(arg) > 2: + if arg[:2] == "-j": + try: + job_count = int(arg[2:]) + except ValueError: + saved_opts = arg[2:] + else: + job_count = "True" + saved_opts = arg[1:].replace("j", "") + + if job_count is None and arg_stack: + try: + job_count = int(arg_stack[-1]) + except ValueError: + pass + else: + # Discard the job count from the stack + # since we're consuming it here. + arg_stack.pop() + + if job_count is None: + # unlimited number of jobs + new_args.append("True") + else: + new_args.append(str(job_count)) + + if saved_opts is not None: + new_args.append("-" + saved_opts) + + return new_args + +def parse_opts(tmpcmdline, silent=False): + myaction=None + myopts = {} + myfiles=[] + + global options, shortmapping + + actions = frozenset([ + "clean", "config", "depclean", + "info", "list-sets", "metadata", + "prune", "regen", "search", + "sync", "unmerge", "version", + ]) + + longopt_aliases = {"--cols":"--columns", "--skip-first":"--skipfirst"} + argument_options = { + "--config-root": { + "help":"specify the location for portage configuration files", + "action":"store" + }, + "--color": { + "help":"enable or disable color output", + "type":"choice", + "choices":("y", "n") + }, + + "--deselect": { + "help" : "remove atoms from the world file", + "type" : "choice", + "choices" : ("True", "n") + }, + + "--jobs": { + + "help" : "Specifies the number of packages to build " + \ + "simultaneously.", + + "action" : "store" + }, + + "--load-average": { + + "help" :"Specifies that no new builds should be started " + \ + "if there are other builds running and the load average " + \ + "is at least LOAD (a floating-point number).", + + "action" : "store" + }, + + "--with-bdeps": { + "help":"include unnecessary build time dependencies", + "type":"choice", + "choices":("y", "n") + }, + "--reinstall": { + "help":"specify conditions to trigger package reinstallation", + "type":"choice", + "choices":["changed-use"] + }, + "--root": { + "help" : "specify the target root filesystem for merging packages", + "action" : "store" + }, + + "--root-deps": { + "help" : "modify interpretation of depedencies", + "type" : "choice", + "choices" :("True", "rdeps") + }, + } + + from optparse import OptionParser + parser = OptionParser() + if parser.has_option("--help"): + parser.remove_option("--help") + + for action_opt in actions: + parser.add_option("--" + action_opt, action="store_true", + dest=action_opt.replace("-", "_"), default=False) + for myopt in options: + parser.add_option(myopt, action="store_true", + dest=myopt.lstrip("--").replace("-", "_"), default=False) + for shortopt, longopt in shortmapping.iteritems(): + parser.add_option("-" + shortopt, action="store_true", + dest=longopt.lstrip("--").replace("-", "_"), default=False) + for myalias, myopt in longopt_aliases.iteritems(): + parser.add_option(myalias, action="store_true", + dest=myopt.lstrip("--").replace("-", "_"), default=False) + + for myopt, kwargs in argument_options.iteritems(): + parser.add_option(myopt, + dest=myopt.lstrip("--").replace("-", "_"), **kwargs) + + tmpcmdline = insert_optional_args(tmpcmdline) + + myoptions, myargs = parser.parse_args(args=tmpcmdline) + + if myoptions.deselect == "True": + myoptions.deselect = True + + if myoptions.root_deps == "True": + myoptions.root_deps = True + + if myoptions.jobs: + jobs = None + if myoptions.jobs == "True": + jobs = True + else: + try: + jobs = int(myoptions.jobs) + except ValueError: + jobs = -1 + + if jobs is not True and \ + jobs < 1: + jobs = None + if not silent: + writemsg("!!! Invalid --jobs parameter: '%s'\n" % \ + (myoptions.jobs,), noiselevel=-1) + + myoptions.jobs = jobs + + if myoptions.load_average: + try: + load_average = float(myoptions.load_average) + except ValueError: + load_average = 0.0 + + if load_average <= 0.0: + load_average = None + if not silent: + writemsg("!!! Invalid --load-average parameter: '%s'\n" % \ + (myoptions.load_average,), noiselevel=-1) + + myoptions.load_average = load_average + + for myopt in options: + v = getattr(myoptions, myopt.lstrip("--").replace("-", "_")) + if v: + myopts[myopt] = True + + for myopt in argument_options: + v = getattr(myoptions, myopt.lstrip("--").replace("-", "_"), None) + if v is not None: + myopts[myopt] = v + + if myoptions.searchdesc: + myoptions.search = True + + for action_opt in actions: + v = getattr(myoptions, action_opt.replace("-", "_")) + if v: + if myaction: + multiple_actions(myaction, action_opt) + sys.exit(1) + myaction = action_opt + + if myaction is None and myoptions.deselect is True: + myaction = 'deselect' + + myfiles += myargs + + return myaction, myopts, myfiles + +def validate_ebuild_environment(trees): + for myroot in trees: + settings = trees[myroot]["vartree"].settings + settings.validate() + +def apply_priorities(settings): + ionice(settings) + nice(settings) + +def nice(settings): + try: + os.nice(int(settings.get("PORTAGE_NICENESS", "0"))) + except (OSError, ValueError), 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 = shlex.split(ionice_cmd) + if not ionice_cmd: + return + + from portage.util import varexpand + 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 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 = "}" + + # WARNING: all operators must be of equal length + IS_OPERATOR = "/@" + DIFF_OPERATOR = "-@" + UNION_OPERATOR = "+@" + + 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 occured while loading the SetConfig instance + for e in setconfig.errors: + print colorize("BAD", "Error during set creation: %s" % e) + + # emerge relies on the existance of sets with names "world" and "system" + required_sets = ("world", "system") + missing_sets = [] + + for s in required_sets: + if s not in sets: + missing_sets.append(s) + if 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 sets: + msg.append(" sets defined: %s" % ", ".join(sets)) + msg.append(" This usually means that '%s'" % \ + (os.path.join(portage.const.GLOBAL_CONFIG_PATH, "sets.conf"),)) + msg.append(" is missing or corrupt.") + for line in msg: + writemsg_level(line + "\n", level=logging.ERROR, noiselevel=-1) + return (None, 1) + unmerge_actions = ("unmerge", "prune", "clean", "depclean") + + for a in myfiles: + if a.startswith(SETPREFIX): + # support simple set operations (intersection, difference and union) + # on the commandline. Expressions are evaluated strictly left-to-right + if IS_OPERATOR in a or DIFF_OPERATOR in a or UNION_OPERATOR in a: + expression = a[len(SETPREFIX):] + expr_sets = [] + expr_ops = [] + while IS_OPERATOR in expression or DIFF_OPERATOR in expression or UNION_OPERATOR in expression: + is_pos = expression.rfind(IS_OPERATOR) + diff_pos = expression.rfind(DIFF_OPERATOR) + union_pos = expression.rfind(UNION_OPERATOR) + op_pos = max(is_pos, diff_pos, union_pos) + s1 = expression[:op_pos] + s2 = expression[op_pos+len(IS_OPERATOR):] + op = expression[op_pos:op_pos+len(IS_OPERATOR)] + if not s2 in sets: + display_missing_pkg_set(root_config, s2) + return (None, 1) + expr_sets.insert(0, s2) + expr_ops.insert(0, op) + expression = s1 + if not expression in sets: + display_missing_pkg_set(root_config, expression) + return (None, 1) + expr_sets.insert(0, expression) + result = set(setconfig.getSetAtoms(expression)) + for i in range(0, len(expr_ops)): + s2 = setconfig.getSetAtoms(expr_sets[i+1]) + if expr_ops[i] == IS_OPERATOR: + result.intersection_update(s2) + elif expr_ops[i] == DIFF_OPERATOR: + result.difference_update(s2) + elif expr_ops[i] == UNION_OPERATOR: + result.update(s2) + else: + raise NotImplementedError("unknown set operator %s" % expr_ops[i]) + newargs.extend(result) + else: + s = a[len(SETPREFIX):] + if s not in sets: + display_missing_pkg_set(root_config, s) + return (None, 1) + setconfig.active.append(s) + try: + set_atoms = setconfig.getSetAtoms(s) + except portage.exception.PackageSetNotFound, e: + writemsg_level(("emerge: the given set '%s' " + \ + "contains a non-existent set named '%s'.\n") % \ + (s, 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, root_trees in trees.iteritems(): + if "porttree" in root_trees: + portdb = root_trees["porttree"].dbapi + missing_repo_names.update(portdb.porttrees) + repos = portdb.getRepositories() + for r in repos: + missing_repo_names.discard(portdb.getRepositoryPath(r)) + 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)) + 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.iteritems(): + if 'porttree' in root_trees: + portdb = root_trees['porttree'].dbapi + if portdb.mysettings.get('PORTAGE_REPO_DUPLICATE_WARN') != '0': + for repo_name, paths in portdb._ignored_repos: + 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' % (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.")) + 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.iteritems(): + if not root_trees["root_config"].settings.get("CONFIG_PROTECT"): + msg = "!!! CONFIG_PROTECT is empty" + if root != "/": + msg += " for '%s'" % root + writemsg_level(msg, level=logging.WARN, noiselevel=-1) + +def profile_check(trees, myaction, myopts): + if myaction in ("info", "sync"): + return os.EX_OK + elif "--version" in myopts or "--help" in myopts: + return os.EX_OK + for root, root_trees in trees.iteritems(): + if root_trees["root_config"].settings.profiles: + continue + # generate some profile related warning messages + validate_ebuild_environment(trees) + msg = "If you have just changed your profile configuration, you " + \ + "should revert back to the previous configuration. Due to " + \ + "your current profile being invalid, allowed actions are " + \ + "limited to --help, --info, --sync, and --version." + writemsg_level("".join("!!! %s\n" % l for l in textwrap.wrap(msg, 70)), + level=logging.ERROR, noiselevel=-1) + return 1 + return os.EX_OK + +def emerge_main(): + global portage # NFC why this is necessary now - genone + portage._disable_legacy_globals() + # Disable color until we're sure that it should be enabled (after + # EMERGE_DEFAULT_OPTS has been parsed). + portage.output.havecolor = 0 + # This first pass is just for options that need to be known as early as + # possible, such as --config-root. They will be parsed again later, + # together with EMERGE_DEFAULT_OPTS (which may vary depending on the + # the value of --config-root). + myaction, myopts, myfiles = parse_opts(sys.argv[1:], silent=True) + if "--debug" in myopts: + os.environ["PORTAGE_DEBUG"] = "1" + if "--config-root" in myopts: + os.environ["PORTAGE_CONFIGROOT"] = myopts["--config-root"] + if "--root" in myopts: + os.environ["ROOT"] = myopts["--root"] + + # Portage needs to ensure a sane umask for the files it creates. + os.umask(022) + settings, trees, mtimedb = load_emerge_config() + portdb = trees[settings["ROOT"]]["porttree"].dbapi + rval = profile_check(trees, myaction, myopts) + if rval != os.EX_OK: + return rval + + if portage._global_updates(trees, mtimedb["updates"]): + mtimedb.commit() + # Reload the whole config from scratch. + settings, trees, mtimedb = load_emerge_config(trees=trees) + portdb = trees[settings["ROOT"]]["porttree"].dbapi + + xterm_titles = "notitles" not in settings.features + + tmpcmdline = [] + 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) + + 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["ROOT"]]["porttree"].dbapi + + for myroot in trees: + mysettings = trees[myroot]["vartree"].settings + mysettings.unlock() + adjust_config(myopts, mysettings) + if '--pretend' not in myopts and myaction in \ + (None, 'clean', 'depclean', 'prune', 'unmerge'): + mysettings["PORTAGE_COUNTER_HASH"] = \ + trees[myroot]["vartree"].dbapi._counter_hash() + mysettings.backup_changes("PORTAGE_COUNTER_HASH") + mysettings.lock() + del myroot, mysettings + + apply_priorities(settings) + + spinner = stdout_spinner() + if "candy" in settings.features: + spinner.update = spinner.update_scroll + + if "--quiet" not in myopts: + portage.deprecated_profile_check(settings=settings) + repo_name_check(trees) + repo_name_duplicate_check(trees) + config_protect_check(trees) + + for mytrees in trees.itervalues(): + mydb = mytrees["porttree"].dbapi + # Freeze the portdbapi for performance (memoize all xmatch results). + mydb.freeze() + del mytrees, mydb + + if "moo" in myfiles: + print """ + + Larry loves Gentoo (""" + platform.system() + """) + + _______________________ +< Have you mooed today? > + ----------------------- + \ ^__^ + \ (oo)\_______ + (__)\ )\/\ + ||----w | + || || + +""" + + 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["ROOT"]]["root_config"] + if myaction == "list-sets": + sys.stdout.write("".join("%s\n" % s for s in sorted(root_config.sets))) + sys.stdout.flush() + return os.EX_OK + + # 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 ("--quiet" in myopts): + spinner.update = spinner.update_quiet + portage.util.noiselimit = -1 + + # Always create packages if FEATURES=buildpkg + # Imply --buildpkg if --buildpkgonly + if ("buildpkg" in settings.features) or ("--buildpkgonly" in myopts): + if "--buildpkg" not in myopts: + myopts["--buildpkg"] = True + + # Always try and fetch binary packages if FEATURES=getbinpkg + if ("getbinpkg" in settings.features): + myopts["--getbinpkg"] = 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) + + if "--fetch-all-uri" in myopts: + myopts["--fetchonly"] = True + + if "--skipfirst" in myopts and "--resume" not in myopts: + myopts["--resume"] = True + + if ("--getbinpkgonly" in myopts) and not ("--usepkgonly" in myopts): + myopts["--usepkgonly"] = True + + if ("--getbinpkgonly" in myopts) and not ("--getbinpkg" in myopts): + myopts["--getbinpkg"] = True + + if ("--getbinpkg" in myopts) and not ("--usepkg" in myopts): + myopts["--usepkg"] = True + + # Also allow -K to apply --usepkg/-k + if ("--usepkgonly" in myopts) and not ("--usepkg" in myopts): + myopts["--usepkg"] = 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.debug=1 + if "python-trace" in settings.features: + import portage.debug + portage.debug.set_trace(True) + + if not ("--quiet" in myopts): + if not sys.stdout.isatty() or ("--nospinner" in myopts): + spinner.update = spinner.update_basic + + if myaction == 'version': + print getportageversion(settings["PORTDIR"], settings["ROOT"], + settings.profile_path, settings["CHOST"], + trees[settings["ROOT"]]["vartree"].dbapi) + return 0 + elif "--help" in myopts: + _emerge.help.help(myaction, myopts, portage.output.havecolor) + return 0 + + if "--debug" in myopts: + print "myaction", myaction + print "myopts", myopts + + if not myaction and not myfiles and "--resume" not in myopts: + _emerge.help.help(myaction, myopts, portage.output.havecolor) + 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 secpass >= 1) or \ + 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: + 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. + from portage.data import portage_group_warning + if "--ask" in myopts: + myopts["--pretend"] = True + del myopts["--ask"] + 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 is required\n") \ + % access_desc) + if portage.secpass < 1 and not need_superuser: + portage_group_warning() + return 1 + + disable_emergelog = False + for x in ("--pretend", "--fetchonly", "--fetch-all-uri"): + if x in myopts: + disable_emergelog = True + break + if myaction in ("search", "info"): + disable_emergelog = True + if disable_emergelog: + """ Disable emergelog for everything except build or unmerge + operations. This helps minimize parallel emerge.log entries that can + confuse log parsers. We especially want it disabled during + parallel-fetch, which uses --resume --fetchonly.""" + global emergelog + def emergelog(*pargs, **kargs): + pass + + else: + 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, e: + writemsg_level("!!! Error creating directory for " + \ + "EMERGE_LOG_DIR='%s':\n!!! %s\n" % \ + (settings['EMERGE_LOG_DIR'], e), + noiselevel=-1, level=logging.ERROR) + else: + global _emerge_log_dir + _emerge_log_dir = settings['EMERGE_LOG_DIR'] + + if not "--pretend" in myopts: + emergelog(xterm_titles, "Started emerge on: "+\ + time.strftime("%b %d, %Y %H:%M:%S", time.localtime())) + myelogstr="" + if myopts: + myelogstr=" ".join(myopts) + if myaction: + myelogstr+=" "+myaction + if myfiles: + myelogstr += " " + " ".join(oldargs) + emergelog(xterm_titles, " *** emerge " + myelogstr) + del oldargs + + def emergeexitsig(signum, frame): + signal.signal(signal.SIGINT, signal.SIG_IGN) + signal.signal(signal.SIGTERM, signal.SIG_IGN) + portage.util.writemsg("\n\nExiting on signal %(signal)s\n" % {"signal":signum}) + sys.exit(100+signum) + signal.signal(signal.SIGINT, emergeexitsig) + 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 "notitles" not in settings.features: + 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["ROOT"]]["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(root_config, myopts, mtimedb, rval) + return rval + + elif myaction == 'info': + + # Ensure atoms are valid before calling unmerge(). + vardb = trees[settings["ROOT"]]["vartree"].dbapi + valid_atoms = [] + for x in myfiles: + if is_valid_package_atom(x): + try: + valid_atoms.append( + portage.dep_expand(x, mydb=vardb, settings=settings)) + except portage.exception.AmbiguousPackageName, 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[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): + 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 + + if "--pretend" not in myopts: + display_news_notification(root_config, myopts) + retval = action_build(settings, trees, mtimedb, + myopts, myaction, myfiles, spinner) + root_config = trees[settings["ROOT"]]["root_config"] + post_emerge(root_config, myopts, mtimedb, retval) + + return retval |