efd954bb921b6677dd320ffd6d7c15cf5a4fef29
[portage.git] / pym / _emerge / main.py
1 # Copyright 1999-2012 Gentoo Foundation
2 # Distributed under the terms of the GNU General Public License v2
3
4 from __future__ import print_function
5
6 import logging
7 import signal
8 import stat
9 import subprocess
10 import sys
11 import textwrap
12 import platform
13 import portage
14 portage.proxy.lazyimport.lazyimport(globals(),
15         'portage.news:count_unread_news,display_news_notifications',
16 )
17 from portage import os
18 from portage import _encodings
19 from portage import _unicode_decode
20 import _emerge.help
21 import portage.xpak, errno, re, time
22 from portage.output import colorize, xtermTitle, xtermTitleReset
23 from portage.output import create_color_func
24 good = create_color_func("GOOD")
25 bad = create_color_func("BAD")
26
27 from portage.const import _ENABLE_DYN_LINK_MAP
28 import portage.elog
29 import portage.util
30 import portage.locks
31 import portage.exception
32 from portage.data import secpass
33 from portage.dbapi.dep_expand import dep_expand
34 from portage.util import normalize_path as normpath
35 from portage.util import (shlex_split, varexpand,
36         writemsg_level, writemsg_stdout)
37 from portage._sets import SETPREFIX
38 from portage._global_updates import _global_updates
39
40 from _emerge.actions import action_config, action_sync, action_metadata, \
41         action_regen, action_search, action_uninstall, action_info, action_build, \
42         adjust_configs, chk_updated_cfg_files, display_missing_pkg_set, \
43         display_news_notification, getportageversion, load_emerge_config
44 import _emerge
45 from _emerge.emergelog import emergelog
46 from _emerge._flush_elog_mod_echo import _flush_elog_mod_echo
47 from _emerge.is_valid_package_atom import is_valid_package_atom
48 from _emerge.stdout_spinner import stdout_spinner
49 from _emerge.userquery import userquery
50
51 if sys.hexversion >= 0x3000000:
52         long = int
53
54 options=[
55 "--alphabetical",
56 "--ask-enter-invalid",
57 "--buildpkgonly",
58 "--changed-use",
59 "--changelog",    "--columns",
60 "--debug",
61 "--digest",
62 "--emptytree",
63 "--fetchonly",    "--fetch-all-uri",
64 "--ignore-default-opts",
65 "--noconfmem",
66 "--newuse",
67 "--nodeps",       "--noreplace",
68 "--nospinner",    "--oneshot",
69 "--onlydeps",     "--pretend",
70 "--quiet-repo-display",
71 "--quiet-unmerge-warn",
72 "--resume",
73 "--searchdesc",
74 "--skipfirst",
75 "--tree",
76 "--unordered-display",
77 "--update",
78 "--verbose",
79 "--verbose-main-repo-display",
80 ]
81
82 shortmapping={
83 "1":"--oneshot",
84 "B":"--buildpkgonly",
85 "c":"--depclean",
86 "C":"--unmerge",
87 "d":"--debug",
88 "e":"--emptytree",
89 "f":"--fetchonly", "F":"--fetch-all-uri",
90 "h":"--help",
91 "l":"--changelog",
92 "n":"--noreplace", "N":"--newuse",
93 "o":"--onlydeps",  "O":"--nodeps",
94 "p":"--pretend",   "P":"--prune",
95 "r":"--resume",
96 "s":"--search",    "S":"--searchdesc",
97 "t":"--tree",
98 "u":"--update",
99 "v":"--verbose",   "V":"--version"
100 }
101
102 COWSAY_MOO = """
103
104   Larry loves Gentoo (%s)
105
106  _______________________
107 < Have you mooed today? >
108  -----------------------
109         \   ^__^
110          \  (oo)\_______
111             (__)\       )\/\ 
112                 ||----w |
113                 ||     ||
114
115 """
116
117 def chk_updated_info_files(root, infodirs, prev_mtimes, retval):
118
119         if os.path.exists("/usr/bin/install-info"):
120                 out = portage.output.EOutput()
121                 regen_infodirs=[]
122                 for z in infodirs:
123                         if z=='':
124                                 continue
125                         inforoot=normpath(root+z)
126                         if os.path.isdir(inforoot) and \
127                                 not [x for x in os.listdir(inforoot) \
128                                 if x.startswith('.keepinfodir')]:
129                                         infomtime = os.stat(inforoot)[stat.ST_MTIME]
130                                         if inforoot not in prev_mtimes or \
131                                                 prev_mtimes[inforoot] != infomtime:
132                                                         regen_infodirs.append(inforoot)
133
134                 if not regen_infodirs:
135                         portage.writemsg_stdout("\n")
136                         if portage.util.noiselimit >= 0:
137                                 out.einfo("GNU info directory index is up-to-date.")
138                 else:
139                         portage.writemsg_stdout("\n")
140                         if portage.util.noiselimit >= 0:
141                                 out.einfo("Regenerating GNU info directory index...")
142
143                         dir_extensions = ("", ".gz", ".bz2")
144                         icount=0
145                         badcount=0
146                         errmsg = ""
147                         for inforoot in regen_infodirs:
148                                 if inforoot=='':
149                                         continue
150
151                                 if not os.path.isdir(inforoot) or \
152                                         not os.access(inforoot, os.W_OK):
153                                         continue
154
155                                 file_list = os.listdir(inforoot)
156                                 file_list.sort()
157                                 dir_file = os.path.join(inforoot, "dir")
158                                 moved_old_dir = False
159                                 processed_count = 0
160                                 for x in file_list:
161                                         if x.startswith(".") or \
162                                                 os.path.isdir(os.path.join(inforoot, x)):
163                                                 continue
164                                         if x.startswith("dir"):
165                                                 skip = False
166                                                 for ext in dir_extensions:
167                                                         if x == "dir" + ext or \
168                                                                 x == "dir" + ext + ".old":
169                                                                 skip = True
170                                                                 break
171                                                 if skip:
172                                                         continue
173                                         if processed_count == 0:
174                                                 for ext in dir_extensions:
175                                                         try:
176                                                                 os.rename(dir_file + ext, dir_file + ext + ".old")
177                                                                 moved_old_dir = True
178                                                         except EnvironmentError as e:
179                                                                 if e.errno != errno.ENOENT:
180                                                                         raise
181                                                                 del e
182                                         processed_count += 1
183                                         try:
184                                                 proc = subprocess.Popen(
185                                                         ['/usr/bin/install-info',
186                                                         '--dir-file=%s' % os.path.join(inforoot, "dir"),
187                                                         os.path.join(inforoot, x)],
188                                                         env=dict(os.environ, LANG="C", LANGUAGE="C"),
189                                                         stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
190                                         except OSError:
191                                                 myso = None
192                                         else:
193                                                 myso = _unicode_decode(
194                                                         proc.communicate()[0]).rstrip("\n")
195                                                 proc.wait()
196                                         existsstr="already exists, for file `"
197                                         if myso:
198                                                 if re.search(existsstr,myso):
199                                                         # Already exists... Don't increment the count for this.
200                                                         pass
201                                                 elif myso[:44]=="install-info: warning: no info dir entry in ":
202                                                         # This info file doesn't contain a DIR-header: install-info produces this
203                                                         # (harmless) warning (the --quiet switch doesn't seem to work).
204                                                         # Don't increment the count for this.
205                                                         pass
206                                                 else:
207                                                         badcount=badcount+1
208                                                         errmsg += myso + "\n"
209                                         icount=icount+1
210
211                                 if moved_old_dir and not os.path.exists(dir_file):
212                                         # We didn't generate a new dir file, so put the old file
213                                         # back where it was originally found.
214                                         for ext in dir_extensions:
215                                                 try:
216                                                         os.rename(dir_file + ext + ".old", dir_file + ext)
217                                                 except EnvironmentError as e:
218                                                         if e.errno != errno.ENOENT:
219                                                                 raise
220                                                         del e
221
222                                 # Clean dir.old cruft so that they don't prevent
223                                 # unmerge of otherwise empty directories.
224                                 for ext in dir_extensions:
225                                         try:
226                                                 os.unlink(dir_file + ext + ".old")
227                                         except EnvironmentError as e:
228                                                 if e.errno != errno.ENOENT:
229                                                         raise
230                                                 del e
231
232                                 #update mtime so we can potentially avoid regenerating.
233                                 prev_mtimes[inforoot] = os.stat(inforoot)[stat.ST_MTIME]
234
235                         if badcount:
236                                 out.eerror("Processed %d info files; %d errors." % \
237                                         (icount, badcount))
238                                 writemsg_level(errmsg, level=logging.ERROR, noiselevel=-1)
239                         else:
240                                 if icount > 0 and portage.util.noiselimit >= 0:
241                                         out.einfo("Processed %d info files." % (icount,))
242
243 def display_preserved_libs(vardbapi, myopts):
244         MAX_DISPLAY = 3
245
246         if vardbapi._linkmap is None or \
247                 vardbapi._plib_registry is None:
248                 # preserve-libs is entirely disabled
249                 return
250
251         # Explicitly load and prune the PreservedLibsRegistry in order
252         # to ensure that we do not display stale data.
253         vardbapi._plib_registry.load()
254
255         if vardbapi._plib_registry.hasEntries():
256                 if "--quiet" in myopts:
257                         print()
258                         print(colorize("WARN", "!!!") + " existing preserved libs found")
259                         return
260                 else:
261                         print()
262                         print(colorize("WARN", "!!!") + " existing preserved libs:")
263
264                 plibdata = vardbapi._plib_registry.getPreservedLibs()
265                 linkmap = vardbapi._linkmap
266                 consumer_map = {}
267                 owners = {}
268
269                 try:
270                         linkmap.rebuild()
271                 except portage.exception.CommandNotFound as e:
272                         writemsg_level("!!! Command Not Found: %s\n" % (e,),
273                                 level=logging.ERROR, noiselevel=-1)
274                         del e
275                 else:
276                         search_for_owners = set()
277                         for cpv in plibdata:
278                                 internal_plib_keys = set(linkmap._obj_key(f) \
279                                         for f in plibdata[cpv])
280                                 for f in plibdata[cpv]:
281                                         if f in consumer_map:
282                                                 continue
283                                         consumers = []
284                                         for c in linkmap.findConsumers(f):
285                                                 # Filter out any consumers that are also preserved libs
286                                                 # belonging to the same package as the provider.
287                                                 if linkmap._obj_key(c) not in internal_plib_keys:
288                                                         consumers.append(c)
289                                         consumers.sort()
290                                         consumer_map[f] = consumers
291                                         search_for_owners.update(consumers[:MAX_DISPLAY+1])
292
293                         owners = {}
294                         for f in search_for_owners:
295                                 owner_set = set()
296                                 for owner in linkmap.getOwners(f):
297                                         owner_dblink = vardbapi._dblink(owner)
298                                         if owner_dblink.exists():
299                                                 owner_set.add(owner_dblink)
300                                 if owner_set:
301                                         owners[f] = owner_set
302
303                 for cpv in plibdata:
304                         print(colorize("WARN", ">>>") + " package: %s" % cpv)
305                         samefile_map = {}
306                         for f in plibdata[cpv]:
307                                 obj_key = linkmap._obj_key(f)
308                                 alt_paths = samefile_map.get(obj_key)
309                                 if alt_paths is None:
310                                         alt_paths = set()
311                                         samefile_map[obj_key] = alt_paths
312                                 alt_paths.add(f)
313
314                         for alt_paths in samefile_map.values():
315                                 alt_paths = sorted(alt_paths)
316                                 for p in alt_paths:
317                                         print(colorize("WARN", " * ") + " - %s" % (p,))
318                                 f = alt_paths[0]
319                                 consumers = consumer_map.get(f, [])
320                                 for c in consumers[:MAX_DISPLAY]:
321                                         print(colorize("WARN", " * ") + "     used by %s (%s)" % \
322                                                 (c, ", ".join(x.mycpv for x in owners.get(c, []))))
323                                 if len(consumers) == MAX_DISPLAY + 1:
324                                         print(colorize("WARN", " * ") + "     used by %s (%s)" % \
325                                                 (consumers[MAX_DISPLAY], ", ".join(x.mycpv \
326                                                 for x in owners.get(consumers[MAX_DISPLAY], []))))
327                                 elif len(consumers) > MAX_DISPLAY:
328                                         print(colorize("WARN", " * ") + "     used by %d other files" % (len(consumers) - MAX_DISPLAY))
329                 print("Use " + colorize("GOOD", "emerge @preserved-rebuild") + " to rebuild packages using these libraries")
330
331 def post_emerge(myaction, myopts, myfiles,
332         target_root, trees, mtimedb, retval):
333         """
334         Misc. things to run at the end of a merge session.
335
336         Update Info Files
337         Update Config Files
338         Update News Items
339         Commit mtimeDB
340         Display preserved libs warnings
341
342         @param myaction: The action returned from parse_opts()
343         @type myaction: String
344         @param myopts: emerge options
345         @type myopts: dict
346         @param myfiles: emerge arguments
347         @type myfiles: list
348         @param target_root: The target EROOT for myaction
349         @type target_root: String
350         @param trees: A dictionary mapping each ROOT to it's package databases
351         @type trees: dict
352         @param mtimedb: The mtimeDB to store data needed across merge invocations
353         @type mtimedb: MtimeDB class instance
354         @param retval: Emerge's return value
355         @type retval: Int
356         """
357
358         root_config = trees[target_root]["root_config"]
359         vardbapi = trees[target_root]['vartree'].dbapi
360         settings = vardbapi.settings
361         info_mtimes = mtimedb["info"]
362
363         # Load the most current variables from ${ROOT}/etc/profile.env
364         settings.unlock()
365         settings.reload()
366         settings.regenerate()
367         settings.lock()
368
369         config_protect = shlex_split(settings.get("CONFIG_PROTECT", ""))
370         infodirs = settings.get("INFOPATH","").split(":") + \
371                 settings.get("INFODIR","").split(":")
372
373         os.chdir("/")
374
375         if retval == os.EX_OK:
376                 exit_msg = " *** exiting successfully."
377         else:
378                 exit_msg = " *** exiting unsuccessfully with status '%s'." % retval
379         emergelog("notitles" not in settings.features, exit_msg)
380
381         _flush_elog_mod_echo()
382
383         if not vardbapi._pkgs_changed:
384                 # GLEP 42 says to display news *after* an emerge --pretend
385                 if "--pretend" in myopts:
386                         display_news_notification(root_config, myopts)
387                 # If vdb state has not changed then there's nothing else to do.
388                 return
389
390         vdb_path = os.path.join(root_config.settings['EROOT'], portage.VDB_PATH)
391         portage.util.ensure_dirs(vdb_path)
392         vdb_lock = None
393         if os.access(vdb_path, os.W_OK) and not "--pretend" in myopts:
394                 vardbapi.lock()
395                 vdb_lock = True
396
397         if vdb_lock:
398                 try:
399                         if "noinfo" not in settings.features:
400                                 chk_updated_info_files(target_root,
401                                         infodirs, info_mtimes, retval)
402                         mtimedb.commit()
403                 finally:
404                         if vdb_lock:
405                                 vardbapi.unlock()
406
407         display_preserved_libs(vardbapi, myopts)
408         chk_updated_cfg_files(settings['EROOT'], config_protect)
409
410         display_news_notification(root_config, myopts)
411
412         postemerge = os.path.join(settings["PORTAGE_CONFIGROOT"],
413                 portage.USER_CONFIG_PATH, "bin", "post_emerge")
414         if os.access(postemerge, os.X_OK):
415                 hook_retval = portage.process.spawn(
416                                                 [postemerge], env=settings.environ())
417                 if hook_retval != os.EX_OK:
418                         writemsg_level(
419                                 " %s spawn failed of %s\n" % (bad("*"), postemerge,),
420                                 level=logging.ERROR, noiselevel=-1)
421
422         clean_logs(settings)
423
424         if "--quiet" not in myopts and \
425                 myaction is None and "@world" in myfiles:
426                 show_depclean_suggestion()
427
428 def show_depclean_suggestion():
429         out = portage.output.EOutput()
430         msg = "After world updates, it is important to remove " + \
431                 "obsolete packages with emerge --depclean. Refer " + \
432                 "to `man emerge` for more information."
433         for line in textwrap.wrap(msg, 72):
434                 out.ewarn(line)
435
436 def multiple_actions(action1, action2):
437         sys.stderr.write("\n!!! Multiple actions requested... Please choose one only.\n")
438         sys.stderr.write("!!! '%s' or '%s'\n\n" % (action1, action2))
439         sys.exit(1)
440
441 def insert_optional_args(args):
442         """
443         Parse optional arguments and insert a value if one has
444         not been provided. This is done before feeding the args
445         to the optparse parser since that parser does not support
446         this feature natively.
447         """
448
449         class valid_integers(object):
450                 def __contains__(self, s):
451                         try:
452                                 return int(s) >= 0
453                         except (ValueError, OverflowError):
454                                 return False
455
456         valid_integers = valid_integers()
457         y_or_n = ('y', 'n',)
458
459         new_args = []
460
461         default_arg_opts = {
462                 '--ask'                  : y_or_n,
463                 '--autounmask'           : y_or_n,
464                 '--autounmask-keep-masks': y_or_n,
465                 '--autounmask-unrestricted-atoms' : y_or_n,
466                 '--autounmask-write'     : y_or_n,
467                 '--buildpkg'             : y_or_n,
468                 '--complete-graph'       : y_or_n,
469                 '--deep'       : valid_integers,
470                 '--deselect'             : y_or_n,
471                 '--binpkg-respect-use'   : y_or_n,
472                 '--fail-clean'           : y_or_n,
473                 '--getbinpkg'            : y_or_n,
474                 '--getbinpkgonly'        : y_or_n,
475                 '--jobs'       : valid_integers,
476                 '--keep-going'           : y_or_n,
477                 '--package-moves'        : y_or_n,
478                 '--quiet'                : y_or_n,
479                 '--quiet-build'          : y_or_n,
480                 '--rebuild-if-new-slot-abi': y_or_n,
481                 '--rebuild-if-new-rev'   : y_or_n,
482                 '--rebuild-if-new-ver'   : y_or_n,
483                 '--rebuild-if-unbuilt'   : y_or_n,
484                 '--rebuilt-binaries'     : y_or_n,
485                 '--root-deps'  : ('rdeps',),
486                 '--select'               : y_or_n,
487                 '--selective'            : y_or_n,
488                 "--use-ebuild-visibility": y_or_n,
489                 '--usepkg'               : y_or_n,
490                 '--usepkgonly'           : y_or_n,
491         }
492
493         if _ENABLE_DYN_LINK_MAP:
494                 default_arg_opts['--depclean-lib-check'] = y_or_n
495
496         short_arg_opts = {
497                 'D' : valid_integers,
498                 'j' : valid_integers,
499         }
500
501         # Don't make things like "-kn" expand to "-k n"
502         # since existence of -n makes it too ambiguous.
503         short_arg_opts_n = {
504                 'a' : y_or_n,
505                 'b' : y_or_n,
506                 'g' : y_or_n,
507                 'G' : y_or_n,
508                 'k' : y_or_n,
509                 'K' : y_or_n,
510                 'q' : y_or_n,
511         }
512
513         arg_stack = args[:]
514         arg_stack.reverse()
515         while arg_stack:
516                 arg = arg_stack.pop()
517
518                 default_arg_choices = default_arg_opts.get(arg)
519                 if default_arg_choices is not None:
520                         new_args.append(arg)
521                         if arg_stack and arg_stack[-1] in default_arg_choices:
522                                 new_args.append(arg_stack.pop())
523                         else:
524                                 # insert default argument
525                                 new_args.append('True')
526                         continue
527
528                 if arg[:1] != "-" or arg[:2] == "--":
529                         new_args.append(arg)
530                         continue
531
532                 match = None
533                 for k, arg_choices in short_arg_opts.items():
534                         if k in arg:
535                                 match = k
536                                 break
537
538                 if match is None:
539                         for k, arg_choices in short_arg_opts_n.items():
540                                 if k in arg:
541                                         match = k
542                                         break
543
544                 if match is None:
545                         new_args.append(arg)
546                         continue
547
548                 if len(arg) == 2:
549                         new_args.append(arg)
550                         if arg_stack and arg_stack[-1] in arg_choices:
551                                 new_args.append(arg_stack.pop())
552                         else:
553                                 # insert default argument
554                                 new_args.append('True')
555                         continue
556
557                 # Insert an empty placeholder in order to
558                 # satisfy the requirements of optparse.
559
560                 new_args.append("-" + match)
561                 opt_arg = None
562                 saved_opts = None
563
564                 if arg[1:2] == match:
565                         if match not in short_arg_opts_n and arg[2:] in arg_choices:
566                                 opt_arg = arg[2:]
567                         else:
568                                 saved_opts = arg[2:]
569                                 opt_arg = "True"
570                 else:
571                         saved_opts = arg[1:].replace(match, "")
572                         opt_arg = "True"
573
574                 if opt_arg is None and arg_stack and \
575                         arg_stack[-1] in arg_choices:
576                         opt_arg = arg_stack.pop()
577
578                 if opt_arg is None:
579                         new_args.append("True")
580                 else:
581                         new_args.append(opt_arg)
582
583                 if saved_opts is not None:
584                         # Recycle these on arg_stack since they
585                         # might contain another match.
586                         arg_stack.append("-" + saved_opts)
587
588         return new_args
589
590 def _find_bad_atoms(atoms, less_strict=False):
591         """
592         Declares all atoms as invalid that have an operator,
593         a use dependency, a blocker or a repo spec.
594         It accepts atoms with wildcards.
595         In less_strict mode it accepts operators and repo specs.
596         """
597         bad_atoms = []
598         for x in ' '.join(atoms).split():
599                 bad_atom = False
600                 try:
601                         atom = portage.dep.Atom(x, allow_wildcard=True, allow_repo=less_strict)
602                 except portage.exception.InvalidAtom:
603                         try:
604                                 atom = portage.dep.Atom("*/"+x, allow_wildcard=True, allow_repo=less_strict)
605                         except portage.exception.InvalidAtom:
606                                 bad_atom = True
607
608                 if bad_atom or (atom.operator and not less_strict) or atom.blocker or atom.use:
609                         bad_atoms.append(x)
610         return bad_atoms
611
612
613 def parse_opts(tmpcmdline, silent=False):
614         myaction=None
615         myopts = {}
616         myfiles=[]
617
618         actions = frozenset([
619                 "clean", "check-news", "config", "depclean", "help",
620                 "info", "list-sets", "metadata", "moo",
621                 "prune", "regen",  "search",
622                 "sync",  "unmerge", "version",
623         ])
624
625         longopt_aliases = {"--cols":"--columns", "--skip-first":"--skipfirst"}
626         y_or_n = ("y", "n")
627         true_y_or_n = ("True", "y", "n")
628         true_y = ("True", "y")
629         argument_options = {
630
631                 "--ask": {
632                         "shortopt" : "-a",
633                         "help"    : "prompt before performing any actions",
634                         "type"    : "choice",
635                         "choices" : true_y_or_n
636                 },
637
638                 "--autounmask": {
639                         "help"    : "automatically unmask packages",
640                         "type"    : "choice",
641                         "choices" : true_y_or_n
642                 },
643
644                 "--autounmask-unrestricted-atoms": {
645                         "help"    : "write autounmask changes with >= atoms if possible",
646                         "type"    : "choice",
647                         "choices" : true_y_or_n
648                 },
649
650                 "--autounmask-keep-masks": {
651                         "help"    : "don't add package.unmask entries",
652                         "type"    : "choice",
653                         "choices" : true_y_or_n
654                 },
655
656                 "--autounmask-write": {
657                         "help"    : "write changes made by --autounmask to disk",
658                         "type"    : "choice",
659                         "choices" : true_y_or_n
660                 },
661
662                 "--accept-properties": {
663                         "help":"temporarily override ACCEPT_PROPERTIES",
664                         "action":"store"
665                 },
666
667                 "--backtrack": {
668
669                         "help"   : "Specifies how many times to backtrack if dependency " + \
670                                 "calculation fails ",
671
672                         "action" : "store"
673                 },
674
675                 "--buildpkg": {
676                         "shortopt" : "-b",
677                         "help"     : "build binary packages",
678                         "type"     : "choice",
679                         "choices"  : true_y_or_n
680                 },
681
682                 "--buildpkg-exclude": {
683                         "help"   :"A space separated list of package atoms for which " + \
684                                 "no binary packages should be built. This option overrides all " + \
685                                 "possible ways to enable building of binary packages.",
686
687                         "action" : "append"
688                 },
689
690                 "--config-root": {
691                         "help":"specify the location for portage configuration files",
692                         "action":"store"
693                 },
694                 "--color": {
695                         "help":"enable or disable color output",
696                         "type":"choice",
697                         "choices":("y", "n")
698                 },
699
700                 "--complete-graph": {
701                         "help"    : "completely account for all known dependencies",
702                         "type"    : "choice",
703                         "choices" : true_y_or_n
704                 },
705
706                 "--complete-graph-if-new-ver": {
707                         "help"    : "trigger --complete-graph behavior if an installed package version will change (upgrade or downgrade)",
708                         "type"    : "choice",
709                         "choices" : y_or_n
710                 },
711
712                 "--deep": {
713
714                         "shortopt" : "-D",
715
716                         "help"   : "Specifies how deep to recurse into dependencies " + \
717                                 "of packages given as arguments. If no argument is given, " + \
718                                 "depth is unlimited. Default behavior is to skip " + \
719                                 "dependencies of installed packages.",
720
721                         "action" : "store"
722                 },
723
724                 "--deselect": {
725                         "help"    : "remove atoms/sets from the world file",
726                         "type"    : "choice",
727                         "choices" : true_y_or_n
728                 },
729
730                 "--dynamic-deps": {
731                         "help": "substitute the dependencies of installed packages with the dependencies of unbuilt ebuilds",
732                         "type": "choice",
733                         "choices": y_or_n
734                 },
735
736                 "--exclude": {
737                         "help"   :"A space separated list of package names or slot atoms. " + \
738                                 "Emerge won't  install any ebuild or binary package that " + \
739                                 "matches any of the given package atoms.",
740
741                         "action" : "append"
742                 },
743
744                 "--fail-clean": {
745                         "help"    : "clean temp files after build failure",
746                         "type"    : "choice",
747                         "choices" : true_y_or_n
748                 },
749
750                 "--ignore-built-slot-abi-deps": {
751                         "help": "Ignore the SLOT/ABI := operator parts of dependencies that have "
752                                 "been recorded when packages where built. This option is intended "
753                                 "only for debugging purposes, and it only affects built packages "
754                                 "that specify SLOT/ABI := operator dependencies using the "
755                                 "experimental \"4-slot-abi\" EAPI.",
756                         "type": "choice",
757                         "choices": y_or_n
758                 },
759
760                 "--jobs": {
761
762                         "shortopt" : "-j",
763
764                         "help"   : "Specifies the number of packages to build " + \
765                                 "simultaneously.",
766
767                         "action" : "store"
768                 },
769
770                 "--keep-going": {
771                         "help"    : "continue as much as possible after an error",
772                         "type"    : "choice",
773                         "choices" : true_y_or_n
774                 },
775
776                 "--load-average": {
777
778                         "help"   :"Specifies that no new builds should be started " + \
779                                 "if there are other builds running and the load average " + \
780                                 "is at least LOAD (a floating-point number).",
781
782                         "action" : "store"
783                 },
784
785                 "--misspell-suggestions": {
786                         "help"    : "enable package name misspell suggestions",
787                         "type"    : "choice",
788                         "choices" : ("y", "n")
789                 },
790
791                 "--with-bdeps": {
792                         "help":"include unnecessary build time dependencies",
793                         "type":"choice",
794                         "choices":("y", "n")
795                 },
796                 "--reinstall": {
797                         "help":"specify conditions to trigger package reinstallation",
798                         "type":"choice",
799                         "choices":["changed-use"]
800                 },
801
802                 "--reinstall-atoms": {
803                         "help"   :"A space separated list of package names or slot atoms. " + \
804                                 "Emerge will treat matching packages as if they are not " + \
805                                 "installed, and reinstall them if necessary. Implies --deep.",
806
807                         "action" : "append",
808                 },
809
810                 "--binpkg-respect-use": {
811                         "help"    : "discard binary packages if their use flags \
812                                 don't match the current configuration",
813                         "type"    : "choice",
814                         "choices" : true_y_or_n
815                 },
816
817                 "--getbinpkg": {
818                         "shortopt" : "-g",
819                         "help"     : "fetch binary packages",
820                         "type"     : "choice",
821                         "choices"  : true_y_or_n
822                 },
823
824                 "--getbinpkgonly": {
825                         "shortopt" : "-G",
826                         "help"     : "fetch binary packages only",
827                         "type"     : "choice",
828                         "choices"  : true_y_or_n
829                 },
830
831                 "--usepkg-exclude": {
832                         "help"   :"A space separated list of package names or slot atoms. " + \
833                                 "Emerge will ignore matching binary packages. ",
834
835                         "action" : "append",
836                 },
837
838                 "--rebuild-exclude": {
839                         "help"   :"A space separated list of package names or slot atoms. " + \
840                                 "Emerge will not rebuild these packages due to the " + \
841                                 "--rebuild flag. ",
842
843                         "action" : "append",
844                 },
845
846                 "--rebuild-ignore": {
847                         "help"   :"A space separated list of package names or slot atoms. " + \
848                                 "Emerge will not rebuild packages that depend on matching " + \
849                                 "packages due to the --rebuild flag. ",
850
851                         "action" : "append",
852                 },
853
854                 "--package-moves": {
855                         "help"     : "perform package moves when necessary",
856                         "type"     : "choice",
857                         "choices"  : true_y_or_n
858                 },
859
860                 "--quiet": {
861                         "shortopt" : "-q",
862                         "help"     : "reduced or condensed output",
863                         "type"     : "choice",
864                         "choices"  : true_y_or_n
865                 },
866
867                 "--quiet-build": {
868                         "help"     : "redirect build output to logs",
869                         "type"     : "choice",
870                         "choices"  : true_y_or_n,
871                 },
872
873                 "--rebuild-if-new-slot-abi": {
874                         "help"     : ("Automatically rebuild or reinstall packages when SLOT/ABI := "
875                                 "operator dependencies can be satisfied by a newer slot, so that "
876                                 "older packages slots will become eligible for removal by the "
877                                 "--depclean action as soon as possible."),
878                         "type"     : "choice",
879                         "choices"  : true_y_or_n
880                 },
881
882                 "--rebuild-if-new-rev": {
883                         "help"     : "Rebuild packages when dependencies that are " + \
884                                 "used at both build-time and run-time are built, " + \
885                                 "if the dependency is not already installed with the " + \
886                                 "same version and revision.",
887                         "type"     : "choice",
888                         "choices"  : true_y_or_n
889                 },
890
891                 "--rebuild-if-new-ver": {
892                         "help"     : "Rebuild packages when dependencies that are " + \
893                                 "used at both build-time and run-time are built, " + \
894                                 "if the dependency is not already installed with the " + \
895                                 "same version. Revision numbers are ignored.",
896                         "type"     : "choice",
897                         "choices"  : true_y_or_n
898                 },
899
900                 "--rebuild-if-unbuilt": {
901                         "help"     : "Rebuild packages when dependencies that are " + \
902                                 "used at both build-time and run-time are built.",
903                         "type"     : "choice",
904                         "choices"  : true_y_or_n
905                 },
906
907                 "--rebuilt-binaries": {
908                         "help"     : "replace installed packages with binary " + \
909                                      "packages that have been rebuilt",
910                         "type"     : "choice",
911                         "choices"  : true_y_or_n
912                 },
913                 
914                 "--rebuilt-binaries-timestamp": {
915                         "help"   : "use only binaries that are newer than this " + \
916                                    "timestamp for --rebuilt-binaries",
917                         "action" : "store"
918                 },
919
920                 "--root": {
921                  "help"   : "specify the target root filesystem for merging packages",
922                  "action" : "store"
923                 },
924
925                 "--root-deps": {
926                         "help"    : "modify interpretation of depedencies",
927                         "type"    : "choice",
928                         "choices" :("True", "rdeps")
929                 },
930
931                 "--select": {
932                         "help"    : "add specified packages to the world set " + \
933                                     "(inverse of --oneshot)",
934                         "type"    : "choice",
935                         "choices" : true_y_or_n
936                 },
937
938                 "--selective": {
939                         "help"    : "identical to --noreplace",
940                         "type"    : "choice",
941                         "choices" : true_y_or_n
942                 },
943
944                 "--use-ebuild-visibility": {
945                         "help"     : "use unbuilt ebuild metadata for visibility checks on built packages",
946                         "type"     : "choice",
947                         "choices"  : true_y_or_n
948                 },
949
950                 "--useoldpkg-atoms": {
951                         "help"   :"A space separated list of package names or slot atoms. " + \
952                                 "Emerge will prefer matching binary packages over newer unbuilt packages. ",
953
954                         "action" : "append",
955                 },
956
957                 "--usepkg": {
958                         "shortopt" : "-k",
959                         "help"     : "use binary packages",
960                         "type"     : "choice",
961                         "choices"  : true_y_or_n
962                 },
963
964                 "--usepkgonly": {
965                         "shortopt" : "-K",
966                         "help"     : "use only binary packages",
967                         "type"     : "choice",
968                         "choices"  : true_y_or_n
969                 },
970
971         }
972
973         if _ENABLE_DYN_LINK_MAP:
974                 argument_options["--depclean-lib-check"] = {
975                         "help"    : "check for consumers of libraries before removing them",
976                         "type"    : "choice",
977                         "choices" : true_y_or_n
978                 }
979
980         from optparse import OptionParser
981         parser = OptionParser()
982         if parser.has_option("--help"):
983                 parser.remove_option("--help")
984
985         for action_opt in actions:
986                 parser.add_option("--" + action_opt, action="store_true",
987                         dest=action_opt.replace("-", "_"), default=False)
988         for myopt in options:
989                 parser.add_option(myopt, action="store_true",
990                         dest=myopt.lstrip("--").replace("-", "_"), default=False)
991         for shortopt, longopt in shortmapping.items():
992                 parser.add_option("-" + shortopt, action="store_true",
993                         dest=longopt.lstrip("--").replace("-", "_"), default=False)
994         for myalias, myopt in longopt_aliases.items():
995                 parser.add_option(myalias, action="store_true",
996                         dest=myopt.lstrip("--").replace("-", "_"), default=False)
997
998         for myopt, kwargs in argument_options.items():
999                 shortopt = kwargs.pop("shortopt", None)
1000                 args = [myopt]
1001                 if shortopt is not None:
1002                         args.append(shortopt)
1003                 parser.add_option(dest=myopt.lstrip("--").replace("-", "_"),
1004                         *args, **kwargs)
1005
1006         tmpcmdline = insert_optional_args(tmpcmdline)
1007
1008         myoptions, myargs = parser.parse_args(args=tmpcmdline)
1009
1010         if myoptions.ask in true_y:
1011                 myoptions.ask = True
1012         else:
1013                 myoptions.ask = None
1014
1015         if myoptions.autounmask in true_y:
1016                 myoptions.autounmask = True
1017
1018         if myoptions.autounmask_unrestricted_atoms in true_y:
1019                 myoptions.autounmask_unrestricted_atoms = True
1020
1021         if myoptions.autounmask_keep_masks in true_y:
1022                 myoptions.autounmask_keep_masks = True
1023
1024         if myoptions.autounmask_write in true_y:
1025                 myoptions.autounmask_write = True
1026
1027         if myoptions.buildpkg in true_y:
1028                 myoptions.buildpkg = True
1029
1030         if myoptions.buildpkg_exclude:
1031                 bad_atoms = _find_bad_atoms(myoptions.buildpkg_exclude, less_strict=True)
1032                 if bad_atoms and not silent:
1033                         parser.error("Invalid Atom(s) in --buildpkg-exclude parameter: '%s'\n" % \
1034                                 (",".join(bad_atoms),))
1035
1036         if myoptions.changed_use is not False:
1037                 myoptions.reinstall = "changed-use"
1038                 myoptions.changed_use = False
1039
1040         if myoptions.deselect in true_y:
1041                 myoptions.deselect = True
1042
1043         if myoptions.binpkg_respect_use is not None:
1044                 if myoptions.binpkg_respect_use in true_y:
1045                         myoptions.binpkg_respect_use = 'y'
1046                 else:
1047                         myoptions.binpkg_respect_use = 'n'
1048
1049         if myoptions.complete_graph in true_y:
1050                 myoptions.complete_graph = True
1051         else:
1052                 myoptions.complete_graph = None
1053
1054         if _ENABLE_DYN_LINK_MAP:
1055                 if myoptions.depclean_lib_check in true_y:
1056                         myoptions.depclean_lib_check = True
1057
1058         if myoptions.exclude:
1059                 bad_atoms = _find_bad_atoms(myoptions.exclude)
1060                 if bad_atoms and not silent:
1061                         parser.error("Invalid Atom(s) in --exclude parameter: '%s' (only package names and slot atoms (with wildcards) allowed)\n" % \
1062                                 (",".join(bad_atoms),))
1063
1064         if myoptions.reinstall_atoms:
1065                 bad_atoms = _find_bad_atoms(myoptions.reinstall_atoms)
1066                 if bad_atoms and not silent:
1067                         parser.error("Invalid Atom(s) in --reinstall-atoms parameter: '%s' (only package names and slot atoms (with wildcards) allowed)\n" % \
1068                                 (",".join(bad_atoms),))
1069
1070         if myoptions.rebuild_exclude:
1071                 bad_atoms = _find_bad_atoms(myoptions.rebuild_exclude)
1072                 if bad_atoms and not silent:
1073                         parser.error("Invalid Atom(s) in --rebuild-exclude parameter: '%s' (only package names and slot atoms (with wildcards) allowed)\n" % \
1074                                 (",".join(bad_atoms),))
1075
1076         if myoptions.rebuild_ignore:
1077                 bad_atoms = _find_bad_atoms(myoptions.rebuild_ignore)
1078                 if bad_atoms and not silent:
1079                         parser.error("Invalid Atom(s) in --rebuild-ignore parameter: '%s' (only package names and slot atoms (with wildcards) allowed)\n" % \
1080                                 (",".join(bad_atoms),))
1081
1082         if myoptions.usepkg_exclude:
1083                 bad_atoms = _find_bad_atoms(myoptions.usepkg_exclude)
1084                 if bad_atoms and not silent:
1085                         parser.error("Invalid Atom(s) in --usepkg-exclude parameter: '%s' (only package names and slot atoms (with wildcards) allowed)\n" % \
1086                                 (",".join(bad_atoms),))
1087
1088         if myoptions.useoldpkg_atoms:
1089                 bad_atoms = _find_bad_atoms(myoptions.useoldpkg_atoms)
1090                 if bad_atoms and not silent:
1091                         parser.error("Invalid Atom(s) in --useoldpkg-atoms parameter: '%s' (only package names and slot atoms (with wildcards) allowed)\n" % \
1092                                 (",".join(bad_atoms),))
1093
1094         if myoptions.fail_clean in true_y:
1095                 myoptions.fail_clean = True
1096
1097         if myoptions.getbinpkg in true_y:
1098                 myoptions.getbinpkg = True
1099         else:
1100                 myoptions.getbinpkg = None
1101
1102         if myoptions.getbinpkgonly in true_y:
1103                 myoptions.getbinpkgonly = True
1104         else:
1105                 myoptions.getbinpkgonly = None
1106
1107         if myoptions.keep_going in true_y:
1108                 myoptions.keep_going = True
1109         else:
1110                 myoptions.keep_going = None
1111
1112         if myoptions.package_moves in true_y:
1113                 myoptions.package_moves = True
1114
1115         if myoptions.quiet in true_y:
1116                 myoptions.quiet = True
1117         else:
1118                 myoptions.quiet = None
1119
1120         if myoptions.quiet_build in true_y:
1121                 myoptions.quiet_build = 'y'
1122
1123         if myoptions.rebuild_if_new_slot_abi in true_y:
1124                 myoptions.rebuild_if_new_slot_abi = 'y'
1125
1126         if myoptions.rebuild_if_new_ver in true_y:
1127                 myoptions.rebuild_if_new_ver = True
1128         else:
1129                 myoptions.rebuild_if_new_ver = None
1130
1131         if myoptions.rebuild_if_new_rev in true_y:
1132                 myoptions.rebuild_if_new_rev = True
1133                 myoptions.rebuild_if_new_ver = None
1134         else:
1135                 myoptions.rebuild_if_new_rev = None
1136
1137         if myoptions.rebuild_if_unbuilt in true_y:
1138                 myoptions.rebuild_if_unbuilt = True
1139                 myoptions.rebuild_if_new_rev = None
1140                 myoptions.rebuild_if_new_ver = None
1141         else:
1142                 myoptions.rebuild_if_unbuilt = None
1143
1144         if myoptions.rebuilt_binaries in true_y:
1145                 myoptions.rebuilt_binaries = True
1146
1147         if myoptions.root_deps in true_y:
1148                 myoptions.root_deps = True
1149
1150         if myoptions.select in true_y:
1151                 myoptions.select = True
1152                 myoptions.oneshot = False
1153         elif myoptions.select == "n":
1154                 myoptions.oneshot = True
1155
1156         if myoptions.selective in true_y:
1157                 myoptions.selective = True
1158
1159         if myoptions.backtrack is not None:
1160
1161                 try:
1162                         backtrack = int(myoptions.backtrack)
1163                 except (OverflowError, ValueError):
1164                         backtrack = -1
1165
1166                 if backtrack < 0:
1167                         backtrack = None
1168                         if not silent:
1169                                 parser.error("Invalid --backtrack parameter: '%s'\n" % \
1170                                         (myoptions.backtrack,))
1171
1172                 myoptions.backtrack = backtrack
1173
1174         if myoptions.deep is not None:
1175                 deep = None
1176                 if myoptions.deep == "True":
1177                         deep = True
1178                 else:
1179                         try:
1180                                 deep = int(myoptions.deep)
1181                         except (OverflowError, ValueError):
1182                                 deep = -1
1183
1184                 if deep is not True and deep < 0:
1185                         deep = None
1186                         if not silent:
1187                                 parser.error("Invalid --deep parameter: '%s'\n" % \
1188                                         (myoptions.deep,))
1189
1190                 myoptions.deep = deep
1191
1192         if myoptions.jobs:
1193                 jobs = None
1194                 if myoptions.jobs == "True":
1195                         jobs = True
1196                 else:
1197                         try:
1198                                 jobs = int(myoptions.jobs)
1199                         except ValueError:
1200                                 jobs = -1
1201
1202                 if jobs is not True and \
1203                         jobs < 1:
1204                         jobs = None
1205                         if not silent:
1206                                 parser.error("Invalid --jobs parameter: '%s'\n" % \
1207                                         (myoptions.jobs,))
1208
1209                 myoptions.jobs = jobs
1210
1211         if myoptions.load_average:
1212                 try:
1213                         load_average = float(myoptions.load_average)
1214                 except ValueError:
1215                         load_average = 0.0
1216
1217                 if load_average <= 0.0:
1218                         load_average = None
1219                         if not silent:
1220                                 parser.error("Invalid --load-average parameter: '%s'\n" % \
1221                                         (myoptions.load_average,))
1222
1223                 myoptions.load_average = load_average
1224         
1225         if myoptions.rebuilt_binaries_timestamp:
1226                 try:
1227                         rebuilt_binaries_timestamp = int(myoptions.rebuilt_binaries_timestamp)
1228                 except ValueError:
1229                         rebuilt_binaries_timestamp = -1
1230
1231                 if rebuilt_binaries_timestamp < 0:
1232                         rebuilt_binaries_timestamp = 0
1233                         if not silent:
1234                                 parser.error("Invalid --rebuilt-binaries-timestamp parameter: '%s'\n" % \
1235                                         (myoptions.rebuilt_binaries_timestamp,))
1236
1237                 myoptions.rebuilt_binaries_timestamp = rebuilt_binaries_timestamp
1238
1239         if myoptions.use_ebuild_visibility in true_y:
1240                 myoptions.use_ebuild_visibility = True
1241         else:
1242                 # None or "n"
1243                 pass
1244
1245         if myoptions.usepkg in true_y:
1246                 myoptions.usepkg = True
1247         else:
1248                 myoptions.usepkg = None
1249
1250         if myoptions.usepkgonly in true_y:
1251                 myoptions.usepkgonly = True
1252         else:
1253                 myoptions.usepkgonly = None
1254
1255         for myopt in options:
1256                 v = getattr(myoptions, myopt.lstrip("--").replace("-", "_"))
1257                 if v:
1258                         myopts[myopt] = True
1259
1260         for myopt in argument_options:
1261                 v = getattr(myoptions, myopt.lstrip("--").replace("-", "_"), None)
1262                 if v is not None:
1263                         myopts[myopt] = v
1264
1265         if myoptions.searchdesc:
1266                 myoptions.search = True
1267
1268         for action_opt in actions:
1269                 v = getattr(myoptions, action_opt.replace("-", "_"))
1270                 if v:
1271                         if myaction:
1272                                 multiple_actions(myaction, action_opt)
1273                                 sys.exit(1)
1274                         myaction = action_opt
1275
1276         if myaction is None and myoptions.deselect is True:
1277                 myaction = 'deselect'
1278
1279         if myargs and isinstance(myargs[0], bytes):
1280                 for i in range(len(myargs)):
1281                         myargs[i] = portage._unicode_decode(myargs[i])
1282
1283         myfiles += myargs
1284
1285         return myaction, myopts, myfiles
1286
1287 # Warn about features that may confuse users and
1288 # lead them to report invalid bugs.
1289 _emerge_features_warn = frozenset(['keeptemp', 'keepwork'])
1290
1291 def validate_ebuild_environment(trees):
1292         features_warn = set()
1293         for myroot in trees:
1294                 settings = trees[myroot]["vartree"].settings
1295                 settings.validate()
1296                 features_warn.update(
1297                         _emerge_features_warn.intersection(settings.features))
1298
1299         if features_warn:
1300                 msg = "WARNING: The FEATURES variable contains one " + \
1301                         "or more values that should be disabled under " + \
1302                         "normal circumstances: %s" % " ".join(features_warn)
1303                 out = portage.output.EOutput()
1304                 for line in textwrap.wrap(msg, 65):
1305                         out.ewarn(line)
1306
1307 def apply_priorities(settings):
1308         ionice(settings)
1309         nice(settings)
1310
1311 def nice(settings):
1312         try:
1313                 os.nice(int(settings.get("PORTAGE_NICENESS", "0")))
1314         except (OSError, ValueError) as e:
1315                 out = portage.output.EOutput()
1316                 out.eerror("Failed to change nice value to '%s'" % \
1317                         settings["PORTAGE_NICENESS"])
1318                 out.eerror("%s\n" % str(e))
1319
1320 def ionice(settings):
1321
1322         ionice_cmd = settings.get("PORTAGE_IONICE_COMMAND")
1323         if ionice_cmd:
1324                 ionice_cmd = portage.util.shlex_split(ionice_cmd)
1325         if not ionice_cmd:
1326                 return
1327
1328         variables = {"PID" : str(os.getpid())}
1329         cmd = [varexpand(x, mydict=variables) for x in ionice_cmd]
1330
1331         try:
1332                 rval = portage.process.spawn(cmd, env=os.environ)
1333         except portage.exception.CommandNotFound:
1334                 # The OS kernel probably doesn't support ionice,
1335                 # so return silently.
1336                 return
1337
1338         if rval != os.EX_OK:
1339                 out = portage.output.EOutput()
1340                 out.eerror("PORTAGE_IONICE_COMMAND returned %d" % (rval,))
1341                 out.eerror("See the make.conf(5) man page for PORTAGE_IONICE_COMMAND usage instructions.")
1342
1343 def clean_logs(settings):
1344
1345         if "clean-logs" not in settings.features:
1346                 return
1347
1348         clean_cmd = settings.get("PORT_LOGDIR_CLEAN")
1349         if clean_cmd:
1350                 clean_cmd = shlex_split(clean_cmd)
1351         if not clean_cmd:
1352                 return
1353
1354         logdir = settings.get("PORT_LOGDIR")
1355         if logdir is None or not os.path.isdir(logdir):
1356                 return
1357
1358         variables = {"PORT_LOGDIR" : logdir}
1359         cmd = [varexpand(x, mydict=variables) for x in clean_cmd]
1360
1361         try:
1362                 rval = portage.process.spawn(cmd, env=os.environ)
1363         except portage.exception.CommandNotFound:
1364                 rval = 127
1365
1366         if rval != os.EX_OK:
1367                 out = portage.output.EOutput()
1368                 out.eerror("PORT_LOGDIR_CLEAN returned %d" % (rval,))
1369                 out.eerror("See the make.conf(5) man page for "
1370                         "PORT_LOGDIR_CLEAN usage instructions.")
1371
1372 def setconfig_fallback(root_config):
1373         from portage._sets.base import DummyPackageSet
1374         from portage._sets.files import WorldSelectedSet
1375         from portage._sets.profiles import PackagesSystemSet
1376         setconfig = root_config.setconfig
1377         setconfig.psets['world'] = DummyPackageSet(atoms=['@selected', '@system'])
1378         setconfig.psets['selected'] = WorldSelectedSet(root_config.settings['EROOT'])
1379         setconfig.psets['system'] = \
1380                 PackagesSystemSet(root_config.settings.profiles)
1381         root_config.sets = setconfig.getSets()
1382
1383 def get_missing_sets(root_config):
1384         # emerge requires existence of "world", "selected", and "system"
1385         missing_sets = []
1386
1387         for s in ("selected", "system", "world",):
1388                 if s not in root_config.sets:
1389                         missing_sets.append(s)
1390
1391         return missing_sets
1392
1393 def missing_sets_warning(root_config, missing_sets):
1394         if len(missing_sets) > 2:
1395                 missing_sets_str = ", ".join('"%s"' % s for s in missing_sets[:-1])
1396                 missing_sets_str += ', and "%s"' % missing_sets[-1]
1397         elif len(missing_sets) == 2:
1398                 missing_sets_str = '"%s" and "%s"' % tuple(missing_sets)
1399         else:
1400                 missing_sets_str = '"%s"' % missing_sets[-1]
1401         msg = ["emerge: incomplete set configuration, " + \
1402                 "missing set(s): %s" % missing_sets_str]
1403         if root_config.sets:
1404                 msg.append("        sets defined: %s" % ", ".join(root_config.sets))
1405         global_config_path = portage.const.GLOBAL_CONFIG_PATH
1406         if root_config.settings['EPREFIX']:
1407                 global_config_path = os.path.join(root_config.settings['EPREFIX'],
1408                                 portage.const.GLOBAL_CONFIG_PATH.lstrip(os.sep))
1409         msg.append("        This usually means that '%s'" % \
1410                 (os.path.join(global_config_path, "sets/portage.conf"),))
1411         msg.append("        is missing or corrupt.")
1412         msg.append("        Falling back to default world and system set configuration!!!")
1413         for line in msg:
1414                 writemsg_level(line + "\n", level=logging.ERROR, noiselevel=-1)
1415
1416 def ensure_required_sets(trees):
1417         warning_shown = False
1418         for root_trees in trees.values():
1419                 missing_sets = get_missing_sets(root_trees["root_config"])
1420                 if missing_sets and not warning_shown:
1421                         warning_shown = True
1422                         missing_sets_warning(root_trees["root_config"], missing_sets)
1423                 if missing_sets:
1424                         setconfig_fallback(root_trees["root_config"])
1425
1426 def expand_set_arguments(myfiles, myaction, root_config):
1427         retval = os.EX_OK
1428         setconfig = root_config.setconfig
1429
1430         sets = setconfig.getSets()
1431
1432         # In order to know exactly which atoms/sets should be added to the
1433         # world file, the depgraph performs set expansion later. It will get
1434         # confused about where the atoms came from if it's not allowed to
1435         # expand them itself.
1436         do_not_expand = (None, )
1437         newargs = []
1438         for a in myfiles:
1439                 if a in ("system", "world"):
1440                         newargs.append(SETPREFIX+a)
1441                 else:
1442                         newargs.append(a)
1443         myfiles = newargs
1444         del newargs
1445         newargs = []
1446
1447         # separators for set arguments
1448         ARG_START = "{"
1449         ARG_END = "}"
1450
1451         for i in range(0, len(myfiles)):
1452                 if myfiles[i].startswith(SETPREFIX):
1453                         start = 0
1454                         end = 0
1455                         x = myfiles[i][len(SETPREFIX):]
1456                         newset = ""
1457                         while x:
1458                                 start = x.find(ARG_START)
1459                                 end = x.find(ARG_END)
1460                                 if start > 0 and start < end:
1461                                         namepart = x[:start]
1462                                         argpart = x[start+1:end]
1463
1464                                         # TODO: implement proper quoting
1465                                         args = argpart.split(",")
1466                                         options = {}
1467                                         for a in args:
1468                                                 if "=" in a:
1469                                                         k, v  = a.split("=", 1)
1470                                                         options[k] = v
1471                                                 else:
1472                                                         options[a] = "True"
1473                                         setconfig.update(namepart, options)
1474                                         newset += (x[:start-len(namepart)]+namepart)
1475                                         x = x[end+len(ARG_END):]
1476                                 else:
1477                                         newset += x
1478                                         x = ""
1479                         myfiles[i] = SETPREFIX+newset
1480
1481         sets = setconfig.getSets()
1482
1483         # display errors that occurred while loading the SetConfig instance
1484         for e in setconfig.errors:
1485                 print(colorize("BAD", "Error during set creation: %s" % e))
1486
1487         unmerge_actions = ("unmerge", "prune", "clean", "depclean")
1488
1489         for a in myfiles:
1490                 if a.startswith(SETPREFIX):             
1491                                 s = a[len(SETPREFIX):]
1492                                 if s not in sets:
1493                                         display_missing_pkg_set(root_config, s)
1494                                         return (None, 1)
1495                                 setconfig.active.append(s)
1496                                 try:
1497                                         set_atoms = setconfig.getSetAtoms(s)
1498                                 except portage.exception.PackageSetNotFound as e:
1499                                         writemsg_level(("emerge: the given set '%s' " + \
1500                                                 "contains a non-existent set named '%s'.\n") % \
1501                                                 (s, e), level=logging.ERROR, noiselevel=-1)
1502                                         return (None, 1)
1503                                 if myaction in unmerge_actions and \
1504                                                 not sets[s].supportsOperation("unmerge"):
1505                                         sys.stderr.write("emerge: the given set '%s' does " % s + \
1506                                                 "not support unmerge operations\n")
1507                                         retval = 1
1508                                 elif not set_atoms:
1509                                         print("emerge: '%s' is an empty set" % s)
1510                                 elif myaction not in do_not_expand:
1511                                         newargs.extend(set_atoms)
1512                                 else:
1513                                         newargs.append(SETPREFIX+s)
1514                                 for e in sets[s].errors:
1515                                         print(e)
1516                 else:
1517                         newargs.append(a)
1518         return (newargs, retval)
1519
1520 def repo_name_check(trees):
1521         missing_repo_names = set()
1522         for root_trees in trees.values():
1523                 porttree = root_trees.get("porttree")
1524                 if porttree:
1525                         portdb = porttree.dbapi
1526                         missing_repo_names.update(portdb.getMissingRepoNames())
1527                         if portdb.porttree_root in missing_repo_names and \
1528                                 not os.path.exists(os.path.join(
1529                                 portdb.porttree_root, "profiles")):
1530                                 # This is normal if $PORTDIR happens to be empty,
1531                                 # so don't warn about it.
1532                                 missing_repo_names.remove(portdb.porttree_root)
1533
1534         if missing_repo_names:
1535                 msg = []
1536                 msg.append("WARNING: One or more repositories " + \
1537                         "have missing repo_name entries:")
1538                 msg.append("")
1539                 for p in missing_repo_names:
1540                         msg.append("\t%s/profiles/repo_name" % (p,))
1541                 msg.append("")
1542                 msg.extend(textwrap.wrap("NOTE: Each repo_name entry " + \
1543                         "should be a plain text file containing a unique " + \
1544                         "name for the repository on the first line.", 70))
1545                 msg.append("\n")
1546                 writemsg_level("".join("%s\n" % l for l in msg),
1547                         level=logging.WARNING, noiselevel=-1)
1548
1549         return bool(missing_repo_names)
1550
1551 def repo_name_duplicate_check(trees):
1552         ignored_repos = {}
1553         for root, root_trees in trees.items():
1554                 if 'porttree' in root_trees:
1555                         portdb = root_trees['porttree'].dbapi
1556                         if portdb.settings.get('PORTAGE_REPO_DUPLICATE_WARN') != '0':
1557                                 for repo_name, paths in portdb.getIgnoredRepos():
1558                                         k = (root, repo_name, portdb.getRepositoryPath(repo_name))
1559                                         ignored_repos.setdefault(k, []).extend(paths)
1560
1561         if ignored_repos:
1562                 msg = []
1563                 msg.append('WARNING: One or more repositories ' + \
1564                         'have been ignored due to duplicate')
1565                 msg.append('  profiles/repo_name entries:')
1566                 msg.append('')
1567                 for k in sorted(ignored_repos):
1568                         msg.append('  %s overrides' % ", ".join(k))
1569                         for path in ignored_repos[k]:
1570                                 msg.append('    %s' % (path,))
1571                         msg.append('')
1572                 msg.extend('  ' + x for x in textwrap.wrap(
1573                         "All profiles/repo_name entries must be unique in order " + \
1574                         "to avoid having duplicates ignored. " + \
1575                         "Set PORTAGE_REPO_DUPLICATE_WARN=\"0\" in " + \
1576                         "/etc/make.conf if you would like to disable this warning."))
1577                 msg.append("\n")
1578                 writemsg_level(''.join('%s\n' % l for l in msg),
1579                         level=logging.WARNING, noiselevel=-1)
1580
1581         return bool(ignored_repos)
1582
1583 def config_protect_check(trees):
1584         for root, root_trees in trees.items():
1585                 settings = root_trees["root_config"].settings
1586                 if not settings.get("CONFIG_PROTECT"):
1587                         msg = "!!! CONFIG_PROTECT is empty"
1588                         if settings["ROOT"] != "/":
1589                                 msg += " for '%s'" % root
1590                         msg += "\n"
1591                         writemsg_level(msg, level=logging.WARN, noiselevel=-1)
1592
1593 def profile_check(trees, myaction):
1594         if myaction in ("help", "info", "search", "sync", "version"):
1595                 return os.EX_OK
1596         for root_trees in trees.values():
1597                 if root_trees["root_config"].settings.profiles:
1598                         continue
1599                 # generate some profile related warning messages
1600                 validate_ebuild_environment(trees)
1601                 msg = ("Your current profile is invalid. If you have just changed "
1602                         "your profile configuration, you should revert back to the "
1603                         "previous configuration. Allowed actions are limited to "
1604                         "--help, --info, --search, --sync, and --version.")
1605                 writemsg_level("".join("!!! %s\n" % l for l in textwrap.wrap(msg, 70)),
1606                         level=logging.ERROR, noiselevel=-1)
1607                 return 1
1608         return os.EX_OK
1609
1610 def check_procfs():
1611         procfs_path = '/proc'
1612         if platform.system() not in ("Linux",) or \
1613                 os.path.ismount(procfs_path):
1614                 return os.EX_OK
1615         msg = "It seems that %s is not mounted. You have been warned." % procfs_path
1616         writemsg_level("".join("!!! %s\n" % l for l in textwrap.wrap(msg, 70)),
1617                 level=logging.ERROR, noiselevel=-1)
1618         return 1
1619
1620 def emerge_main(args=None):
1621         """
1622         @param args: command arguments (default: sys.argv[1:])
1623         @type args: list
1624         """
1625         if args is None:
1626                 args = sys.argv[1:]
1627
1628         portage._disable_legacy_globals()
1629         portage.dep._internal_warnings = True
1630         # Disable color until we're sure that it should be enabled (after
1631         # EMERGE_DEFAULT_OPTS has been parsed).
1632         portage.output.havecolor = 0
1633         # This first pass is just for options that need to be known as early as
1634         # possible, such as --config-root.  They will be parsed again later,
1635         # together with EMERGE_DEFAULT_OPTS (which may vary depending on the
1636         # the value of --config-root).
1637         myaction, myopts, myfiles = parse_opts(args, silent=True)
1638         if "--debug" in myopts:
1639                 os.environ["PORTAGE_DEBUG"] = "1"
1640         if "--config-root" in myopts:
1641                 os.environ["PORTAGE_CONFIGROOT"] = myopts["--config-root"]
1642         if "--root" in myopts:
1643                 os.environ["ROOT"] = myopts["--root"]
1644         if "--accept-properties" in myopts:
1645                 os.environ["ACCEPT_PROPERTIES"] = myopts["--accept-properties"]
1646
1647         # Portage needs to ensure a sane umask for the files it creates.
1648         os.umask(0o22)
1649         settings, trees, mtimedb = load_emerge_config()
1650         portdb = trees[settings['EROOT']]['porttree'].dbapi
1651         rval = profile_check(trees, myaction)
1652         if rval != os.EX_OK:
1653                 return rval
1654
1655         tmpcmdline = []
1656         if "--ignore-default-opts" not in myopts:
1657                 tmpcmdline.extend(settings["EMERGE_DEFAULT_OPTS"].split())
1658         tmpcmdline.extend(args)
1659         myaction, myopts, myfiles = parse_opts(tmpcmdline)
1660
1661         # skip global updates prior to sync, since it's called after sync
1662         if myaction not in ('help', 'info', 'sync', 'version') and \
1663                 myopts.get('--package-moves') != 'n' and \
1664                 _global_updates(trees, mtimedb["updates"], quiet=("--quiet" in myopts)):
1665                 mtimedb.commit()
1666                 # Reload the whole config from scratch.
1667                 settings, trees, mtimedb = load_emerge_config(trees=trees)
1668                 portdb = trees[settings['EROOT']]['porttree'].dbapi
1669
1670         xterm_titles = "notitles" not in settings.features
1671         if xterm_titles:
1672                 xtermTitle("emerge")
1673
1674         if "--digest" in myopts:
1675                 os.environ["FEATURES"] = os.environ.get("FEATURES","") + " digest"
1676                 # Reload the whole config from scratch so that the portdbapi internal
1677                 # config is updated with new FEATURES.
1678                 settings, trees, mtimedb = load_emerge_config(trees=trees)
1679                 portdb = trees[settings['EROOT']]['porttree'].dbapi
1680
1681         # NOTE: adjust_configs() can map options to FEATURES, so any relevant
1682         # options adjustments should be made prior to calling adjust_configs().
1683         if "--buildpkgonly" in myopts:
1684                 myopts["--buildpkg"] = True
1685
1686         adjust_configs(myopts, trees)
1687         apply_priorities(settings)
1688
1689         if myaction == 'version':
1690                 writemsg_stdout(getportageversion(
1691                         settings["PORTDIR"], None,
1692                         settings.profile_path, settings["CHOST"],
1693                         trees[settings['EROOT']]['vartree'].dbapi) + '\n', noiselevel=-1)
1694                 return 0
1695         elif myaction == 'help':
1696                 _emerge.help.help()
1697                 return 0
1698
1699         spinner = stdout_spinner()
1700         if "candy" in settings.features:
1701                 spinner.update = spinner.update_scroll
1702
1703         if "--quiet" not in myopts:
1704                 portage.deprecated_profile_check(settings=settings)
1705                 if portage.const._ENABLE_REPO_NAME_WARN:
1706                         # Bug #248603 - Disable warnings about missing
1707                         # repo_name entries for stable branch.
1708                         repo_name_check(trees)
1709                 repo_name_duplicate_check(trees)
1710                 config_protect_check(trees)
1711         check_procfs()
1712
1713         if "getbinpkg" in settings.features:
1714                 myopts["--getbinpkg"] = True
1715
1716         if "--getbinpkgonly" in myopts:
1717                 myopts["--getbinpkg"] = True
1718
1719         if "--getbinpkgonly" in myopts:
1720                 myopts["--usepkgonly"] = True
1721
1722         if "--getbinpkg" in myopts:
1723                 myopts["--usepkg"] = True
1724
1725         if "--usepkgonly" in myopts:
1726                 myopts["--usepkg"] = True
1727
1728         if "--buildpkgonly" in myopts:
1729                 # --buildpkgonly will not merge anything, so
1730                 # it cancels all binary package options.
1731                 for opt in ("--getbinpkg", "--getbinpkgonly",
1732                         "--usepkg", "--usepkgonly"):
1733                         myopts.pop(opt, None)
1734
1735         for mytrees in trees.values():
1736                 mydb = mytrees["porttree"].dbapi
1737                 # Freeze the portdbapi for performance (memoize all xmatch results).
1738                 mydb.freeze()
1739
1740                 if myaction in ('search', None) and \
1741                         "--usepkg" in myopts:
1742                         # Populate the bintree with current --getbinpkg setting.
1743                         # This needs to happen before expand_set_arguments(), in case
1744                         # any sets use the bintree.
1745                         mytrees["bintree"].populate(
1746                                 getbinpkgs="--getbinpkg" in myopts)
1747
1748         del mytrees, mydb
1749
1750         if "moo" in myfiles:
1751                 print(COWSAY_MOO % platform.system())
1752                 msg = ("The above `emerge moo` display is deprecated. "
1753                         "Please use `emerge --moo` instead.")
1754                 for line in textwrap.wrap(msg, 50):
1755                         print(" %s %s" % (colorize("WARN", "*"), line))
1756
1757         for x in myfiles:
1758                 ext = os.path.splitext(x)[1]
1759                 if (ext == ".ebuild" or ext == ".tbz2") and os.path.exists(os.path.abspath(x)):
1760                         print(colorize("BAD", "\n*** emerging by path is broken and may not always work!!!\n"))
1761                         break
1762
1763         root_config = trees[settings['EROOT']]['root_config']
1764         if myaction == "moo":
1765                 print(COWSAY_MOO % platform.system())
1766                 return os.EX_OK
1767         elif myaction == "list-sets":
1768                 writemsg_stdout("".join("%s\n" % s for s in sorted(root_config.sets)))
1769                 return os.EX_OK
1770         elif myaction == "check-news":
1771                 news_counts = count_unread_news(
1772                         root_config.trees["porttree"].dbapi,
1773                         root_config.trees["vartree"].dbapi)
1774                 if any(news_counts.values()):
1775                         display_news_notifications(news_counts)
1776                 elif "--quiet" not in myopts:
1777                         print("", colorize("GOOD", "*"), "No news items were found.")
1778                 return os.EX_OK
1779
1780         ensure_required_sets(trees)
1781
1782         # only expand sets for actions taking package arguments
1783         oldargs = myfiles[:]
1784         if myaction in ("clean", "config", "depclean", "info", "prune", "unmerge", None):
1785                 myfiles, retval = expand_set_arguments(myfiles, myaction, root_config)
1786                 if retval != os.EX_OK:
1787                         return retval
1788
1789                 # Need to handle empty sets specially, otherwise emerge will react 
1790                 # with the help message for empty argument lists
1791                 if oldargs and not myfiles:
1792                         print("emerge: no targets left after set expansion")
1793                         return 0
1794
1795         if ("--tree" in myopts) and ("--columns" in myopts):
1796                 print("emerge: can't specify both of \"--tree\" and \"--columns\".")
1797                 return 1
1798
1799         if '--emptytree' in myopts and '--noreplace' in myopts:
1800                 writemsg_level("emerge: can't specify both of " + \
1801                         "\"--emptytree\" and \"--noreplace\".\n",
1802                         level=logging.ERROR, noiselevel=-1)
1803                 return 1
1804
1805         if ("--quiet" in myopts):
1806                 spinner.update = spinner.update_quiet
1807                 portage.util.noiselimit = -1
1808
1809         if "--fetch-all-uri" in myopts:
1810                 myopts["--fetchonly"] = True
1811
1812         if "--skipfirst" in myopts and "--resume" not in myopts:
1813                 myopts["--resume"] = True
1814
1815         # Allow -p to remove --ask
1816         if "--pretend" in myopts:
1817                 myopts.pop("--ask", None)
1818
1819         # forbid --ask when not in a terminal
1820         # note: this breaks `emerge --ask | tee logfile`, but that doesn't work anyway.
1821         if ("--ask" in myopts) and (not sys.stdin.isatty()):
1822                 portage.writemsg("!!! \"--ask\" should only be used in a terminal. Exiting.\n",
1823                         noiselevel=-1)
1824                 return 1
1825
1826         if settings.get("PORTAGE_DEBUG", "") == "1":
1827                 spinner.update = spinner.update_quiet
1828                 portage.util.noiselimit = 0
1829                 if "python-trace" in settings.features:
1830                         import portage.debug as portage_debug
1831                         portage_debug.set_trace(True)
1832
1833         if not ("--quiet" in myopts):
1834                 if '--nospinner' in myopts or \
1835                         settings.get('TERM') == 'dumb' or \
1836                         not sys.stdout.isatty():
1837                         spinner.update = spinner.update_basic
1838
1839         if "--debug" in myopts:
1840                 print("myaction", myaction)
1841                 print("myopts", myopts)
1842
1843         if not myaction and not myfiles and "--resume" not in myopts:
1844                 _emerge.help.help()
1845                 return 1
1846
1847         pretend = "--pretend" in myopts
1848         fetchonly = "--fetchonly" in myopts or "--fetch-all-uri" in myopts
1849         buildpkgonly = "--buildpkgonly" in myopts
1850
1851         # check if root user is the current user for the actions where emerge needs this
1852         if portage.secpass < 2:
1853                 # We've already allowed "--version" and "--help" above.
1854                 if "--pretend" not in myopts and myaction not in ("search","info"):
1855                         need_superuser = myaction in ('clean', 'depclean', 'deselect',
1856                                 'prune', 'unmerge') or not \
1857                                 (fetchonly or \
1858                                 (buildpkgonly and secpass >= 1) or \
1859                                 myaction in ("metadata", "regen", "sync"))
1860                         if portage.secpass < 1 or \
1861                                 need_superuser:
1862                                 if need_superuser:
1863                                         access_desc = "superuser"
1864                                 else:
1865                                         access_desc = "portage group"
1866                                 # Always show portage_group_warning() when only portage group
1867                                 # access is required but the user is not in the portage group.
1868                                 from portage.data import portage_group_warning
1869                                 if "--ask" in myopts:
1870                                         writemsg_stdout("This action requires %s access...\n" % \
1871                                                 (access_desc,), noiselevel=-1)
1872                                         if portage.secpass < 1 and not need_superuser:
1873                                                 portage_group_warning()
1874                                         if userquery("Would you like to add --pretend to options?",
1875                                                 "--ask-enter-invalid" in myopts) == "No":
1876                                                 return 128 + signal.SIGINT
1877                                         myopts["--pretend"] = True
1878                                         del myopts["--ask"]
1879                                 else:
1880                                         sys.stderr.write(("emerge: %s access is required\n") \
1881                                                 % access_desc)
1882                                         if portage.secpass < 1 and not need_superuser:
1883                                                 portage_group_warning()
1884                                         return 1
1885
1886         # Disable emergelog for everything except build or unmerge operations.
1887         # This helps minimize parallel emerge.log entries that can confuse log
1888         # parsers like genlop.
1889         disable_emergelog = False
1890         for x in ("--pretend", "--fetchonly", "--fetch-all-uri"):
1891                 if x in myopts:
1892                         disable_emergelog = True
1893                         break
1894         if disable_emergelog:
1895                 pass
1896         elif myaction in ("search", "info"):
1897                 disable_emergelog = True
1898         elif portage.data.secpass < 1:
1899                 disable_emergelog = True
1900
1901         _emerge.emergelog._disable = disable_emergelog
1902
1903         if not disable_emergelog:
1904                 if 'EMERGE_LOG_DIR' in settings:
1905                         try:
1906                                 # At least the parent needs to exist for the lock file.
1907                                 portage.util.ensure_dirs(settings['EMERGE_LOG_DIR'])
1908                         except portage.exception.PortageException as e:
1909                                 writemsg_level("!!! Error creating directory for " + \
1910                                         "EMERGE_LOG_DIR='%s':\n!!! %s\n" % \
1911                                         (settings['EMERGE_LOG_DIR'], e),
1912                                         noiselevel=-1, level=logging.ERROR)
1913                                 portage.util.ensure_dirs(_emerge.emergelog._emerge_log_dir)
1914                         else:
1915                                 _emerge.emergelog._emerge_log_dir = settings["EMERGE_LOG_DIR"]
1916                 else:
1917                         _emerge.emergelog._emerge_log_dir = os.path.join(os.sep,
1918                                 settings["EPREFIX"].lstrip(os.sep), "var", "log")
1919                         portage.util.ensure_dirs(_emerge.emergelog._emerge_log_dir)
1920
1921         if not "--pretend" in myopts:
1922                 emergelog(xterm_titles, "Started emerge on: "+\
1923                         _unicode_decode(
1924                                 time.strftime("%b %d, %Y %H:%M:%S", time.localtime()),
1925                                 encoding=_encodings['content'], errors='replace'))
1926                 myelogstr=""
1927                 if myopts:
1928                         opt_list = []
1929                         for opt, arg in myopts.items():
1930                                 if arg is True:
1931                                         opt_list.append(opt)
1932                                 elif isinstance(arg, list):
1933                                         # arguments like --exclude that use 'append' action
1934                                         for x in arg:
1935                                                 opt_list.append("%s=%s" % (opt, x))
1936                                 else:
1937                                         opt_list.append("%s=%s" % (opt, arg))
1938                         myelogstr=" ".join(opt_list)
1939                 if myaction:
1940                         myelogstr += " --" + myaction
1941                 if myfiles:
1942                         myelogstr += " " + " ".join(oldargs)
1943                 emergelog(xterm_titles, " *** emerge " + myelogstr)
1944         del oldargs
1945
1946         def emergeexitsig(signum, frame):
1947                 signal.signal(signal.SIGINT, signal.SIG_IGN)
1948                 signal.signal(signal.SIGTERM, signal.SIG_IGN)
1949                 portage.util.writemsg("\n\nExiting on signal %(signal)s\n" % {"signal":signum})
1950                 sys.exit(128 + signum)
1951         signal.signal(signal.SIGINT, emergeexitsig)
1952         signal.signal(signal.SIGTERM, emergeexitsig)
1953
1954         def emergeexit():
1955                 """This gets out final log message in before we quit."""
1956                 if "--pretend" not in myopts:
1957                         emergelog(xterm_titles, " *** terminating.")
1958                 if xterm_titles:
1959                         xtermTitleReset()
1960         portage.atexit_register(emergeexit)
1961
1962         if myaction in ("config", "metadata", "regen", "sync"):
1963                 if "--pretend" in myopts:
1964                         sys.stderr.write(("emerge: The '%s' action does " + \
1965                                 "not support '--pretend'.\n") % myaction)
1966                         return 1
1967
1968         if "sync" == myaction:
1969                 return action_sync(settings, trees, mtimedb, myopts, myaction)
1970         elif "metadata" == myaction:
1971                 action_metadata(settings, portdb, myopts)
1972         elif myaction=="regen":
1973                 validate_ebuild_environment(trees)
1974                 return action_regen(settings, portdb, myopts.get("--jobs"),
1975                         myopts.get("--load-average"))
1976         # HELP action
1977         elif "config"==myaction:
1978                 validate_ebuild_environment(trees)
1979                 action_config(settings, trees, myopts, myfiles)
1980
1981         # SEARCH action
1982         elif "search"==myaction:
1983                 validate_ebuild_environment(trees)
1984                 action_search(trees[settings['EROOT']]['root_config'],
1985                         myopts, myfiles, spinner)
1986
1987         elif myaction in ('clean', 'depclean', 'deselect', 'prune', 'unmerge'):
1988                 validate_ebuild_environment(trees)
1989                 rval = action_uninstall(settings, trees, mtimedb["ldpath"],
1990                         myopts, myaction, myfiles, spinner)
1991                 if not (myaction == 'deselect' or buildpkgonly or fetchonly or pretend):
1992                         post_emerge(myaction, myopts, myfiles, settings['EROOT'],
1993                                 trees, mtimedb, rval)
1994                 return rval
1995
1996         elif myaction == 'info':
1997
1998                 # Ensure atoms are valid before calling unmerge().
1999                 vardb = trees[settings['EROOT']]['vartree'].dbapi
2000                 portdb = trees[settings['EROOT']]['porttree'].dbapi
2001                 bindb = trees[settings['EROOT']]["bintree"].dbapi
2002                 valid_atoms = []
2003                 for x in myfiles:
2004                         if is_valid_package_atom(x, allow_repo=True):
2005                                 try:
2006                                         #look at the installed files first, if there is no match
2007                                         #look at the ebuilds, since EAPI 4 allows running pkg_info
2008                                         #on non-installed packages
2009                                         valid_atom = dep_expand(x, mydb=vardb, settings=settings)
2010                                         if valid_atom.cp.split("/")[0] == "null":
2011                                                 valid_atom = dep_expand(x, mydb=portdb, settings=settings)
2012                                         if valid_atom.cp.split("/")[0] == "null" and "--usepkg" in myopts:
2013                                                 valid_atom = dep_expand(x, mydb=bindb, settings=settings)
2014                                         valid_atoms.append(valid_atom)
2015                                 except portage.exception.AmbiguousPackageName as e:
2016                                         msg = "The short ebuild name \"" + x + \
2017                                                 "\" is ambiguous.  Please specify " + \
2018                                                 "one of the following " + \
2019                                                 "fully-qualified ebuild names instead:"
2020                                         for line in textwrap.wrap(msg, 70):
2021                                                 writemsg_level("!!! %s\n" % (line,),
2022                                                         level=logging.ERROR, noiselevel=-1)
2023                                         for i in e.args[0]:
2024                                                 writemsg_level("    %s\n" % colorize("INFORM", i),
2025                                                         level=logging.ERROR, noiselevel=-1)
2026                                         writemsg_level("\n", level=logging.ERROR, noiselevel=-1)
2027                                         return 1
2028                                 continue
2029                         msg = []
2030                         msg.append("'%s' is not a valid package atom." % (x,))
2031                         msg.append("Please check ebuild(5) for full details.")
2032                         writemsg_level("".join("!!! %s\n" % line for line in msg),
2033                                 level=logging.ERROR, noiselevel=-1)
2034                         return 1
2035
2036                 return action_info(settings, trees, myopts, valid_atoms)
2037
2038         # "update", "system", or just process files:
2039         else:
2040                 validate_ebuild_environment(trees)
2041
2042                 for x in myfiles:
2043                         if x.startswith(SETPREFIX) or \
2044                                 is_valid_package_atom(x, allow_repo=True):
2045                                 continue
2046                         if x[:1] == os.sep:
2047                                 continue
2048                         try:
2049                                 os.lstat(x)
2050                                 continue
2051                         except OSError:
2052                                 pass
2053                         msg = []
2054                         msg.append("'%s' is not a valid package atom." % (x,))
2055                         msg.append("Please check ebuild(5) for full details.")
2056                         writemsg_level("".join("!!! %s\n" % line for line in msg),
2057                                 level=logging.ERROR, noiselevel=-1)
2058                         return 1
2059
2060                 # GLEP 42 says to display news *after* an emerge --pretend
2061                 if "--pretend" not in myopts:
2062                         display_news_notification(root_config, myopts)
2063                 retval = action_build(settings, trees, mtimedb,
2064                         myopts, myaction, myfiles, spinner)
2065                 post_emerge(myaction, myopts, myfiles, settings['EROOT'],
2066                         trees, mtimedb, retval)
2067
2068                 return retval