Use a directory for the default set configuration.
[portage.git] / pym / _emerge / main.py
1 # Copyright 1999-2009 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 sys
10 import textwrap
11 import platform
12 try:
13         from subprocess import getstatusoutput as subprocess_getstatusoutput
14 except ImportError:
15         from commands import getstatusoutput as subprocess_getstatusoutput
16 import portage
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 import portage.elog
28 import portage.dep
29 portage.dep._dep_check_strict = True
30 import portage.util
31 import portage.locks
32 import portage.exception
33 from portage.data import secpass
34 from portage.dbapi.dep_expand import dep_expand
35 from portage.util import normalize_path as normpath
36 from portage.util import writemsg, 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
50 if sys.hexversion >= 0x3000000:
51         long = int
52
53 options=[
54 "--ask",          "--alphabetical",
55 "--ask-enter-invalid",
56 "--buildpkg",     "--buildpkgonly",
57 "--changed-use",
58 "--changelog",    "--columns",
59 "--debug",
60 "--digest",
61 "--emptytree",
62 "--fetchonly",    "--fetch-all-uri",
63 "--ignore-default-opts",
64 "--noconfmem",
65 "--newuse",
66 "--nodeps",       "--noreplace",
67 "--nospinner",    "--oneshot",
68 "--onlydeps",     "--pretend",
69 "--quiet",
70 "--quiet-build",
71 "--quiet-unmerge-warn",
72 "--resume",
73 "--searchdesc",
74 "--skipfirst",
75 "--tree",
76 "--unordered-display",
77 "--update",
78 "--verbose",
79 ]
80
81 shortmapping={
82 "1":"--oneshot",
83 "a":"--ask",
84 "b":"--buildpkg",  "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 "q":"--quiet",
96 "r":"--resume",
97 "s":"--search",    "S":"--searchdesc",
98 "t":"--tree",
99 "u":"--update",
100 "v":"--verbose",   "V":"--version"
101 }
102
103 def chk_updated_info_files(root, infodirs, prev_mtimes, retval):
104
105         if os.path.exists("/usr/bin/install-info"):
106                 out = portage.output.EOutput()
107                 regen_infodirs=[]
108                 for z in infodirs:
109                         if z=='':
110                                 continue
111                         inforoot=normpath(root+z)
112                         if os.path.isdir(inforoot):
113                                 infomtime = os.stat(inforoot)[stat.ST_MTIME]
114                                 if inforoot not in prev_mtimes or \
115                                         prev_mtimes[inforoot] != infomtime:
116                                                 regen_infodirs.append(inforoot)
117
118                 if not regen_infodirs:
119                         portage.writemsg_stdout("\n")
120                         out.einfo("GNU info directory index is up-to-date.")
121                 else:
122                         portage.writemsg_stdout("\n")
123                         out.einfo("Regenerating GNU info directory index...")
124
125                         dir_extensions = ("", ".gz", ".bz2")
126                         icount=0
127                         badcount=0
128                         errmsg = ""
129                         for inforoot in regen_infodirs:
130                                 if inforoot=='':
131                                         continue
132
133                                 if not os.path.isdir(inforoot) or \
134                                         not os.access(inforoot, os.W_OK):
135                                         continue
136
137                                 file_list = os.listdir(inforoot)
138                                 file_list.sort()
139                                 dir_file = os.path.join(inforoot, "dir")
140                                 moved_old_dir = False
141                                 processed_count = 0
142                                 for x in file_list:
143                                         if x.startswith(".") or \
144                                                 os.path.isdir(os.path.join(inforoot, x)):
145                                                 continue
146                                         if x.startswith("dir"):
147                                                 skip = False
148                                                 for ext in dir_extensions:
149                                                         if x == "dir" + ext or \
150                                                                 x == "dir" + ext + ".old":
151                                                                 skip = True
152                                                                 break
153                                                 if skip:
154                                                         continue
155                                         if processed_count == 0:
156                                                 for ext in dir_extensions:
157                                                         try:
158                                                                 os.rename(dir_file + ext, dir_file + ext + ".old")
159                                                                 moved_old_dir = True
160                                                         except EnvironmentError as e:
161                                                                 if e.errno != errno.ENOENT:
162                                                                         raise
163                                                                 del e
164                                         processed_count += 1
165                                         myso=subprocess_getstatusoutput("LANG=C LANGUAGE=C /usr/bin/install-info --dir-file="+inforoot+"/dir "+inforoot+"/"+x)[1]
166                                         existsstr="already exists, for file `"
167                                         if myso!="":
168                                                 if re.search(existsstr,myso):
169                                                         # Already exists... Don't increment the count for this.
170                                                         pass
171                                                 elif myso[:44]=="install-info: warning: no info dir entry in ":
172                                                         # This info file doesn't contain a DIR-header: install-info produces this
173                                                         # (harmless) warning (the --quiet switch doesn't seem to work).
174                                                         # Don't increment the count for this.
175                                                         pass
176                                                 else:
177                                                         badcount=badcount+1
178                                                         errmsg += myso + "\n"
179                                         icount=icount+1
180
181                                 if moved_old_dir and not os.path.exists(dir_file):
182                                         # We didn't generate a new dir file, so put the old file
183                                         # back where it was originally found.
184                                         for ext in dir_extensions:
185                                                 try:
186                                                         os.rename(dir_file + ext + ".old", dir_file + ext)
187                                                 except EnvironmentError as e:
188                                                         if e.errno != errno.ENOENT:
189                                                                 raise
190                                                         del e
191
192                                 # Clean dir.old cruft so that they don't prevent
193                                 # unmerge of otherwise empty directories.
194                                 for ext in dir_extensions:
195                                         try:
196                                                 os.unlink(dir_file + ext + ".old")
197                                         except EnvironmentError as e:
198                                                 if e.errno != errno.ENOENT:
199                                                         raise
200                                                 del e
201
202                                 #update mtime so we can potentially avoid regenerating.
203                                 prev_mtimes[inforoot] = os.stat(inforoot)[stat.ST_MTIME]
204
205                         if badcount:
206                                 out.eerror("Processed %d info files; %d errors." % \
207                                         (icount, badcount))
208                                 writemsg_level(errmsg, level=logging.ERROR, noiselevel=-1)
209                         else:
210                                 if icount > 0:
211                                         out.einfo("Processed %d info files." % (icount,))
212
213 def display_preserved_libs(vardbapi, myopts):
214         MAX_DISPLAY = 3
215
216         # Ensure the registry is consistent with existing files.
217         vardbapi.plib_registry.pruneNonExisting()
218
219         if vardbapi.plib_registry.hasEntries():
220                 if "--quiet" in myopts:
221                         print()
222                         print(colorize("WARN", "!!!") + " existing preserved libs found")
223                         return
224                 else:
225                         print()
226                         print(colorize("WARN", "!!!") + " existing preserved libs:")
227
228                 plibdata = vardbapi.plib_registry.getPreservedLibs()
229                 linkmap = vardbapi.linkmap
230                 consumer_map = {}
231                 owners = {}
232                 linkmap_broken = False
233
234                 try:
235                         linkmap.rebuild()
236                 except portage.exception.CommandNotFound as e:
237                         writemsg_level("!!! Command Not Found: %s\n" % (e,),
238                                 level=logging.ERROR, noiselevel=-1)
239                         del e
240                         linkmap_broken = True
241                 else:
242                         search_for_owners = set()
243                         for cpv in plibdata:
244                                 internal_plib_keys = set(linkmap._obj_key(f) \
245                                         for f in plibdata[cpv])
246                                 for f in plibdata[cpv]:
247                                         if f in consumer_map:
248                                                 continue
249                                         consumers = []
250                                         for c in linkmap.findConsumers(f):
251                                                 # Filter out any consumers that are also preserved libs
252                                                 # belonging to the same package as the provider.
253                                                 if linkmap._obj_key(c) not in internal_plib_keys:
254                                                         consumers.append(c)
255                                         consumers.sort()
256                                         consumer_map[f] = consumers
257                                         search_for_owners.update(consumers[:MAX_DISPLAY+1])
258
259                         owners = vardbapi._owners.getFileOwnerMap(search_for_owners)
260
261                 for cpv in plibdata:
262                         print(colorize("WARN", ">>>") + " package: %s" % cpv)
263                         samefile_map = {}
264                         for f in plibdata[cpv]:
265                                 obj_key = linkmap._obj_key(f)
266                                 alt_paths = samefile_map.get(obj_key)
267                                 if alt_paths is None:
268                                         alt_paths = set()
269                                         samefile_map[obj_key] = alt_paths
270                                 alt_paths.add(f)
271
272                         for alt_paths in samefile_map.values():
273                                 alt_paths = sorted(alt_paths)
274                                 for p in alt_paths:
275                                         print(colorize("WARN", " * ") + " - %s" % (p,))
276                                 f = alt_paths[0]
277                                 consumers = consumer_map.get(f, [])
278                                 for c in consumers[:MAX_DISPLAY]:
279                                         print(colorize("WARN", " * ") + "     used by %s (%s)" % \
280                                                 (c, ", ".join(x.mycpv for x in owners.get(c, []))))
281                                 if len(consumers) == MAX_DISPLAY + 1:
282                                         print(colorize("WARN", " * ") + "     used by %s (%s)" % \
283                                                 (consumers[MAX_DISPLAY], ", ".join(x.mycpv \
284                                                 for x in owners.get(consumers[MAX_DISPLAY], []))))
285                                 elif len(consumers) > MAX_DISPLAY:
286                                         print(colorize("WARN", " * ") + "     used by %d other files" % (len(consumers) - MAX_DISPLAY))
287                 print("Use " + colorize("GOOD", "emerge @preserved-rebuild") + " to rebuild packages using these libraries")
288
289 def post_emerge(root_config, myopts, mtimedb, retval):
290         """
291         Misc. things to run at the end of a merge session.
292
293         Update Info Files
294         Update Config Files
295         Update News Items
296         Commit mtimeDB
297         Display preserved libs warnings
298         Exit Emerge
299
300         @param trees: A dictionary mapping each ROOT to it's package databases
301         @type trees: dict
302         @param mtimedb: The mtimeDB to store data needed across merge invocations
303         @type mtimedb: MtimeDB class instance
304         @param retval: Emerge's return value
305         @type retval: Int
306         @rype: None
307         @returns:
308         1.  Calls sys.exit(retval)
309         """
310
311         target_root = root_config.root
312         trees = { target_root : root_config.trees }
313         vardbapi = trees[target_root]["vartree"].dbapi
314         settings = vardbapi.settings
315         info_mtimes = mtimedb["info"]
316
317         # Load the most current variables from ${ROOT}/etc/profile.env
318         settings.unlock()
319         settings.reload()
320         settings.regenerate()
321         settings.lock()
322
323         config_protect = settings.get("CONFIG_PROTECT","").split()
324         infodirs = settings.get("INFOPATH","").split(":") + \
325                 settings.get("INFODIR","").split(":")
326
327         os.chdir("/")
328
329         if retval == os.EX_OK:
330                 exit_msg = " *** exiting successfully."
331         else:
332                 exit_msg = " *** exiting unsuccessfully with status '%s'." % retval
333         emergelog("notitles" not in settings.features, exit_msg)
334
335         _flush_elog_mod_echo()
336
337         if not vardbapi._pkgs_changed:
338                 display_news_notification(root_config, myopts)
339                 # If vdb state has not changed then there's nothing else to do.
340                 sys.exit(retval)
341
342         vdb_path = os.path.join(target_root, portage.VDB_PATH)
343         portage.util.ensure_dirs(vdb_path)
344         vdb_lock = None
345         if os.access(vdb_path, os.W_OK) and not "--pretend" in myopts:
346                 vdb_lock = portage.locks.lockdir(vdb_path)
347
348         if vdb_lock:
349                 try:
350                         if "noinfo" not in settings.features:
351                                 chk_updated_info_files(target_root,
352                                         infodirs, info_mtimes, retval)
353                         mtimedb.commit()
354                 finally:
355                         if vdb_lock:
356                                 portage.locks.unlockdir(vdb_lock)
357
358         chk_updated_cfg_files(target_root, config_protect)
359
360         display_news_notification(root_config, myopts)
361         if retval in (None, os.EX_OK) or (not "--pretend" in myopts):
362                 display_preserved_libs(vardbapi, myopts)        
363
364         sys.exit(retval)
365
366 def multiple_actions(action1, action2):
367         sys.stderr.write("\n!!! Multiple actions requested... Please choose one only.\n")
368         sys.stderr.write("!!! '%s' or '%s'\n\n" % (action1, action2))
369         sys.exit(1)
370
371 def insert_optional_args(args):
372         """
373         Parse optional arguments and insert a value if one has
374         not been provided. This is done before feeding the args
375         to the optparse parser since that parser does not support
376         this feature natively.
377         """
378
379         class valid_integers(object):
380                 def __contains__(self, s):
381                         try:
382                                 return int(s) >= 0
383                         except (ValueError, OverflowError):
384                                 return False
385
386         valid_integers = valid_integers()
387
388         new_args = []
389
390         default_arg_opts = {
391                 '--complete-graph' : ('n',),
392                 '--deep'       : valid_integers,
393                 '--depclean-lib-check'   : ('n',),
394                 '--deselect'   : ('n',),
395                 '--binpkg-respect-use'   : ('n', 'y',),
396                 '--fail-clean'           : ('n',),
397                 '--getbinpkg'            : ('n',),
398                 '--getbinpkgonly'        : ('n',),
399                 '--jobs'       : valid_integers,
400                 '--keep-going'           : ('n',),
401                 '--rebuilt-binaries'     : ('n',),
402                 '--root-deps'  : ('rdeps',),
403                 '--select'               : ('n',),
404                 '--selective'            : ('n',),
405                 "--use-ebuild-visibility": ('n',),
406                 '--usepkg'               : ('n',),
407                 '--usepkgonly'           : ('n',),
408         }
409
410         short_arg_opts = {
411                 'D' : valid_integers,
412                 'j' : valid_integers,
413         }
414
415         # Don't make things like "-kn" expand to "-k n"
416         # since existence of -n makes it too ambiguous.
417         short_arg_opts_n = {
418                 'g' : ('n',),
419                 'G' : ('n',),
420                 'k' : ('n',),
421                 'K' : ('n',),
422         }
423
424         arg_stack = args[:]
425         arg_stack.reverse()
426         while arg_stack:
427                 arg = arg_stack.pop()
428
429                 default_arg_choices = default_arg_opts.get(arg)
430                 if default_arg_choices is not None:
431                         new_args.append(arg)
432                         if arg_stack and arg_stack[-1] in default_arg_choices:
433                                 new_args.append(arg_stack.pop())
434                         else:
435                                 # insert default argument
436                                 new_args.append('True')
437                         continue
438
439                 if arg[:1] != "-" or arg[:2] == "--":
440                         new_args.append(arg)
441                         continue
442
443                 match = None
444                 for k, arg_choices in short_arg_opts.items():
445                         if k in arg:
446                                 match = k
447                                 break
448
449                 if match is None:
450                         for k, arg_choices in short_arg_opts_n.items():
451                                 if k in arg:
452                                         match = k
453                                         break
454
455                 if match is None:
456                         new_args.append(arg)
457                         continue
458
459                 if len(arg) == 2:
460                         new_args.append(arg)
461                         if arg_stack and arg_stack[-1] in arg_choices:
462                                 new_args.append(arg_stack.pop())
463                         else:
464                                 # insert default argument
465                                 new_args.append('True')
466                         continue
467
468                 # Insert an empty placeholder in order to
469                 # satisfy the requirements of optparse.
470
471                 new_args.append("-" + match)
472                 opt_arg = None
473                 saved_opts = None
474
475                 if arg[1:2] == match:
476                         if match not in short_arg_opts_n and arg[2:] in arg_choices:
477                                 opt_arg = arg[2:]
478                         else:
479                                 saved_opts = arg[2:]
480                                 opt_arg = "True"
481                 else:
482                         saved_opts = arg[1:].replace(match, "")
483                         opt_arg = "True"
484
485                 if opt_arg is None and arg_stack and \
486                         arg_stack[-1] in arg_choices:
487                         opt_arg = arg_stack.pop()
488
489                 if opt_arg is None:
490                         new_args.append("True")
491                 else:
492                         new_args.append(opt_arg)
493
494                 if saved_opts is not None:
495                         # Recycle these on arg_stack since they
496                         # might contain another match.
497                         arg_stack.append("-" + saved_opts)
498
499         return new_args
500
501 def parse_opts(tmpcmdline, silent=False):
502         myaction=None
503         myopts = {}
504         myfiles=[]
505
506         global options, shortmapping
507
508         actions = frozenset([
509                 "clean", "config", "depclean", "help",
510                 "info", "list-sets", "metadata",
511                 "prune", "regen",  "search",
512                 "sync",  "unmerge", "version",
513         ])
514
515         longopt_aliases = {"--cols":"--columns", "--skip-first":"--skipfirst"}
516         argument_options = {
517                 "--accept-properties": {
518                         "help":"temporarily override ACCEPT_PROPERTIES",
519                         "action":"store"
520                 },
521
522                 "--backtrack": {
523
524                         "help"   : "Specifies how many times to backtrack if dependency " + \
525                                 "calculation fails ",
526
527                         "action" : "store"
528                 },
529
530                 "--config-root": {
531                         "help":"specify the location for portage configuration files",
532                         "action":"store"
533                 },
534                 "--color": {
535                         "help":"enable or disable color output",
536                         "type":"choice",
537                         "choices":("y", "n")
538                 },
539
540                 "--complete-graph": {
541                         "help"    : "completely account for all known dependencies",
542                         "type"    : "choice",
543                         "choices" : ("True", "n")
544                 },
545
546                 "--deep": {
547
548                         "shortopt" : "-D",
549
550                         "help"   : "Specifies how deep to recurse into dependencies " + \
551                                 "of packages given as arguments. If no argument is given, " + \
552                                 "depth is unlimited. Default behavior is to skip " + \
553                                 "dependencies of installed packages.",
554
555                         "action" : "store"
556                 },
557
558                 "--depclean-lib-check": {
559                         "help"    : "check for consumers of libraries before removing them",
560                         "type"    : "choice",
561                         "choices" : ("True", "n")
562                 },
563
564                 "--deselect": {
565                         "help"    : "remove atoms/sets from the world file",
566                         "type"    : "choice",
567                         "choices" : ("True", "n")
568                 },
569
570                 "--exclude": {
571                         "help"   :"A space separated list of package names or slot atoms. " + \
572                                 "Emerge won't  install any ebuild or binary package that " + \
573                                 "matches any of the given package atoms.",
574
575                         "action" : "append"
576                 },
577
578                 "--fail-clean": {
579                         "help"    : "clean temp files after build failure",
580                         "type"    : "choice",
581                         "choices" : ("True", "n")
582                 },
583
584                 "--jobs": {
585
586                         "shortopt" : "-j",
587
588                         "help"   : "Specifies the number of packages to build " + \
589                                 "simultaneously.",
590
591                         "action" : "store"
592                 },
593
594                 "--keep-going": {
595                         "help"    : "continue as much as possible after an error",
596                         "type"    : "choice",
597                         "choices" : ("True", "n")
598                 },
599
600                 "--load-average": {
601
602                         "help"   :"Specifies that no new builds should be started " + \
603                                 "if there are other builds running and the load average " + \
604                                 "is at least LOAD (a floating-point number).",
605
606                         "action" : "store"
607                 },
608
609                 "--with-bdeps": {
610                         "help":"include unnecessary build time dependencies",
611                         "type":"choice",
612                         "choices":("y", "n")
613                 },
614                 "--reinstall": {
615                         "help":"specify conditions to trigger package reinstallation",
616                         "type":"choice",
617                         "choices":["changed-use"]
618                 },
619
620                 "--binpkg-respect-use": {
621                         "help"    : "discard binary packages if their use flags \
622                                 don't match the current configuration",
623                         "type"    : "choice",
624                         "choices" : ("True", "y", "n")
625                 },
626
627                 "--getbinpkg": {
628                         "shortopt" : "-g",
629                         "help"     : "fetch binary packages",
630                         "type"     : "choice",
631                         "choices"  : ("True", "n")
632                 },
633
634                 "--getbinpkgonly": {
635                         "shortopt" : "-G",
636                         "help"     : "fetch binary packages only",
637                         "type"     : "choice",
638                         "choices"  : ("True", "n")
639                 },
640
641                 "--rebuilt-binaries": {
642                         "help"     : "replace installed packages with binary " + \
643                                      "packages that have been rebuilt",
644                         "type"     : "choice",
645                         "choices"  : ("True", "n")
646                 },
647                 
648                 "--rebuilt-binaries-timestamp": {
649                         "help"   : "use only binaries that are newer than this " + \
650                                    "timestamp for --rebuilt-binaries",
651                         "action" : "store"
652                 },
653
654                 "--root": {
655                  "help"   : "specify the target root filesystem for merging packages",
656                  "action" : "store"
657                 },
658
659                 "--root-deps": {
660                         "help"    : "modify interpretation of depedencies",
661                         "type"    : "choice",
662                         "choices" :("True", "rdeps")
663                 },
664
665                 "--select": {
666                         "help"    : "add specified packages to the world set " + \
667                                     "(inverse of --oneshot)",
668                         "type"    : "choice",
669                         "choices" : ("True", "n")
670                 },
671
672                 "--selective": {
673                         "help"    : "similar to the --noreplace but does not take " + \
674                                     "precedence over options such as --newuse",
675                         "type"    : "choice",
676                         "choices" : ("True", "n")
677                 },
678
679                 "--use-ebuild-visibility": {
680                         "help"     : "use unbuilt ebuild metadata for visibility checks on built packages",
681                         "type"     : "choice",
682                         "choices"  : ("True", "n")
683                 },
684
685                 "--usepkg": {
686                         "shortopt" : "-k",
687                         "help"     : "use binary packages",
688                         "type"     : "choice",
689                         "choices"  : ("True", "n")
690                 },
691
692                 "--usepkgonly": {
693                         "shortopt" : "-K",
694                         "help"     : "use only binary packages",
695                         "type"     : "choice",
696                         "choices"  : ("True", "n")
697                 },
698
699         }
700
701         from optparse import OptionParser
702         parser = OptionParser()
703         if parser.has_option("--help"):
704                 parser.remove_option("--help")
705
706         for action_opt in actions:
707                 parser.add_option("--" + action_opt, action="store_true",
708                         dest=action_opt.replace("-", "_"), default=False)
709         for myopt in options:
710                 parser.add_option(myopt, action="store_true",
711                         dest=myopt.lstrip("--").replace("-", "_"), default=False)
712         for shortopt, longopt in shortmapping.items():
713                 parser.add_option("-" + shortopt, action="store_true",
714                         dest=longopt.lstrip("--").replace("-", "_"), default=False)
715         for myalias, myopt in longopt_aliases.items():
716                 parser.add_option(myalias, action="store_true",
717                         dest=myopt.lstrip("--").replace("-", "_"), default=False)
718
719         for myopt, kwargs in argument_options.items():
720                 shortopt = kwargs.pop("shortopt", None)
721                 args = [myopt]
722                 if shortopt is not None:
723                         args.append(shortopt)
724                 parser.add_option(dest=myopt.lstrip("--").replace("-", "_"),
725                         *args, **kwargs)
726
727         tmpcmdline = insert_optional_args(tmpcmdline)
728
729         myoptions, myargs = parser.parse_args(args=tmpcmdline)
730
731         if myoptions.changed_use is not False:
732                 myoptions.reinstall = "changed-use"
733                 myoptions.changed_use = False
734
735         if myoptions.deselect == "True":
736                 myoptions.deselect = True
737
738         if myoptions.binpkg_respect_use in ("y", "True",):
739                 myoptions.binpkg_respect_use = True
740         else:
741                 myoptions.binpkg_respect_use = None
742
743         if myoptions.complete_graph in ("y", "True",):
744                 myoptions.complete_graph = True
745         else:
746                 myoptions.complete_graph = None
747
748         if myoptions.depclean_lib_check in ("True",):
749                 myoptions.depclean_lib_check = True
750
751         if myoptions.exclude:
752                 exclude = []
753                 bad_atoms = []
754                 for x in ' '.join(myoptions.exclude).split():
755                         bad_atom = False
756                         try:
757                                 atom = portage.dep.Atom(x, allow_wildcard=True)
758                         except portage.exception.InvalidAtom:
759                                 try:
760                                         atom = portage.dep.Atom("*/"+x, allow_wildcard=True)
761                                 except portage.exception.InvalidAtom:
762                                         bad_atom = True
763                         
764                         if bad_atom:
765                                 bad_atoms.append(x)
766                         else:
767                                 if atom.operator or atom.blocker or atom.use:
768                                         bad_atoms.append(x)
769                                 else:
770                                         exclude.append(atom)
771
772                 if bad_atoms and not silent:
773                         parser.error("Invalid Atom(s) in --exclude parameter: '%s' (only package names and slot atoms (with widlcards) allowed)\n" % \
774                                 (",".join(bad_atoms),))
775
776         if myoptions.fail_clean == "True":
777                 myoptions.fail_clean = True
778
779         if myoptions.getbinpkg in ("True",):
780                 myoptions.getbinpkg = True
781         else:
782                 myoptions.getbinpkg = None
783
784         if myoptions.getbinpkgonly in ("True",):
785                 myoptions.getbinpkgonly = True
786         else:
787                 myoptions.getbinpkgonly = None
788
789         if myoptions.keep_going in ("True",):
790                 myoptions.keep_going = True
791         else:
792                 myoptions.keep_going = None
793
794         if myoptions.rebuilt_binaries in ("True",):
795                 myoptions.rebuilt_binaries = True
796
797         if myoptions.root_deps == "True":
798                 myoptions.root_deps = True
799
800         if myoptions.select == "True":
801                 myoptions.select = True
802                 myoptions.oneshot = False
803         elif myoptions.select == "n":
804                 myoptions.oneshot = True
805
806         if myoptions.selective == "True":
807                 myoptions.selective = True
808
809         if myoptions.backtrack is not None:
810
811                 try:
812                         backtrack = int(myoptions.backtrack)
813                 except (OverflowError, ValueError):
814                         backtrack = -1
815
816                 if backtrack < 0:
817                         backtrack = None
818                         if not silent:
819                                 parser.error("Invalid --backtrack parameter: '%s'\n" % \
820                                         (myoptions.backtrack,))
821
822                 myoptions.backtrack = backtrack
823
824         if myoptions.deep is not None:
825                 deep = None
826                 if myoptions.deep == "True":
827                         deep = True
828                 else:
829                         try:
830                                 deep = int(myoptions.deep)
831                         except (OverflowError, ValueError):
832                                 deep = -1
833
834                 if deep is not True and deep < 0:
835                         deep = None
836                         if not silent:
837                                 parser.error("Invalid --deep parameter: '%s'\n" % \
838                                         (myoptions.deep,))
839
840                 myoptions.deep = deep
841
842         if myoptions.jobs:
843                 jobs = None
844                 if myoptions.jobs == "True":
845                         jobs = True
846                 else:
847                         try:
848                                 jobs = int(myoptions.jobs)
849                         except ValueError:
850                                 jobs = -1
851
852                 if jobs is not True and \
853                         jobs < 1:
854                         jobs = None
855                         if not silent:
856                                 parser.error("Invalid --jobs parameter: '%s'\n" % \
857                                         (myoptions.jobs,))
858
859                 myoptions.jobs = jobs
860
861         if myoptions.load_average:
862                 try:
863                         load_average = float(myoptions.load_average)
864                 except ValueError:
865                         load_average = 0.0
866
867                 if load_average <= 0.0:
868                         load_average = None
869                         if not silent:
870                                 parser.error("Invalid --load-average parameter: '%s'\n" % \
871                                         (myoptions.load_average,))
872
873                 myoptions.load_average = load_average
874         
875         if myoptions.rebuilt_binaries_timestamp:
876                 try:
877                         rebuilt_binaries_timestamp = int(myoptions.rebuilt_binaries_timestamp)
878                 except ValueError:
879                         rebuilt_binaries_timestamp = -1
880
881                 if rebuilt_binaries_timestamp < 0:
882                         rebuilt_binaries_timestamp = 0
883                         if not silent:
884                                 parser.error("Invalid --rebuilt-binaries-timestamp parameter: '%s'\n" % \
885                                         (myoptions.rebuilt_binaries_timestamp,))
886
887                 myoptions.rebuilt_binaries_timestamp = rebuilt_binaries_timestamp
888
889         if myoptions.use_ebuild_visibility in ("True",):
890                 myoptions.use_ebuild_visibility = True
891         else:
892                 myoptions.use_ebuild_visibility = None
893
894         if myoptions.usepkg in ("True",):
895                 myoptions.usepkg = True
896         else:
897                 myoptions.usepkg = None
898
899         if myoptions.usepkgonly in ("True",):
900                 myoptions.usepkgonly = True
901         else:
902                 myoptions.usepkgonly = None
903
904         for myopt in options:
905                 v = getattr(myoptions, myopt.lstrip("--").replace("-", "_"))
906                 if v:
907                         myopts[myopt] = True
908
909         for myopt in argument_options:
910                 v = getattr(myoptions, myopt.lstrip("--").replace("-", "_"), None)
911                 if v is not None:
912                         myopts[myopt] = v
913
914         if myoptions.searchdesc:
915                 myoptions.search = True
916
917         for action_opt in actions:
918                 v = getattr(myoptions, action_opt.replace("-", "_"))
919                 if v:
920                         if myaction:
921                                 multiple_actions(myaction, action_opt)
922                                 sys.exit(1)
923                         myaction = action_opt
924
925         if myaction is None and myoptions.deselect is True:
926                 myaction = 'deselect'
927
928         if myargs and sys.hexversion < 0x3000000 and \
929                 not isinstance(myargs[0], unicode):
930                 for i in range(len(myargs)):
931                         myargs[i] = portage._unicode_decode(myargs[i])
932
933         myfiles += myargs
934
935         return myaction, myopts, myfiles
936
937 def validate_ebuild_environment(trees):
938         for myroot in trees:
939                 settings = trees[myroot]["vartree"].settings
940                 settings.validate()
941
942 def apply_priorities(settings):
943         ionice(settings)
944         nice(settings)
945
946 def nice(settings):
947         try:
948                 os.nice(int(settings.get("PORTAGE_NICENESS", "0")))
949         except (OSError, ValueError) as e:
950                 out = portage.output.EOutput()
951                 out.eerror("Failed to change nice value to '%s'" % \
952                         settings["PORTAGE_NICENESS"])
953                 out.eerror("%s\n" % str(e))
954
955 def ionice(settings):
956
957         ionice_cmd = settings.get("PORTAGE_IONICE_COMMAND")
958         if ionice_cmd:
959                 ionice_cmd = portage.util.shlex_split(ionice_cmd)
960         if not ionice_cmd:
961                 return
962
963         from portage.util import varexpand
964         variables = {"PID" : str(os.getpid())}
965         cmd = [varexpand(x, mydict=variables) for x in ionice_cmd]
966
967         try:
968                 rval = portage.process.spawn(cmd, env=os.environ)
969         except portage.exception.CommandNotFound:
970                 # The OS kernel probably doesn't support ionice,
971                 # so return silently.
972                 return
973
974         if rval != os.EX_OK:
975                 out = portage.output.EOutput()
976                 out.eerror("PORTAGE_IONICE_COMMAND returned %d" % (rval,))
977                 out.eerror("See the make.conf(5) man page for PORTAGE_IONICE_COMMAND usage instructions.")
978
979 def setconfig_fallback(root_config):
980         from portage.sets.base import DummyPackageSet
981         from portage.sets.files import WorldSelectedSet
982         from portage.sets.profiles import PackagesSystemSet
983         setconfig = root_config.setconfig
984         setconfig.psets['world'] = DummyPackageSet(atoms=['@selected', '@system'])
985         setconfig.psets['selected'] = WorldSelectedSet(root_config.root)
986         setconfig.psets['system'] = \
987                 PackagesSystemSet(root_config.settings.profiles)
988         root_config.sets = setconfig.getSets()
989
990 def get_missing_sets(root_config):
991         # emerge requires existence of "world", "selected", and "system"
992         missing_sets = []
993
994         for s in ("selected", "system", "world",):
995                 if s not in root_config.sets:
996                         missing_sets.append(s)
997
998         return missing_sets
999
1000 def missing_sets_warning(root_config, missing_sets):
1001         if len(missing_sets) > 2:
1002                 missing_sets_str = ", ".join('"%s"' % s for s in missing_sets[:-1])
1003                 missing_sets_str += ', and "%s"' % missing_sets[-1]
1004         elif len(missing_sets) == 2:
1005                 missing_sets_str = '"%s" and "%s"' % tuple(missing_sets)
1006         else:
1007                 missing_sets_str = '"%s"' % missing_sets[-1]
1008         msg = ["emerge: incomplete set configuration, " + \
1009                 "missing set(s): %s" % missing_sets_str]
1010         if root_config.sets:
1011                 msg.append("        sets defined: %s" % ", ".join(root_config.sets))
1012         msg.append("        This usually means that '%s'" % \
1013                 (os.path.join(portage.const.GLOBAL_CONFIG_PATH, "sets/portage.conf"),))
1014         msg.append("        is missing or corrupt.")
1015         msg.append("        Falling back to default world and system set configuration!!!")
1016         for line in msg:
1017                 writemsg_level(line + "\n", level=logging.ERROR, noiselevel=-1)
1018
1019 def ensure_required_sets(trees):
1020         warning_shown = False
1021         for root_trees in trees.values():
1022                 missing_sets = get_missing_sets(root_trees["root_config"])
1023                 if missing_sets and not warning_shown:
1024                         warning_shown = True
1025                         missing_sets_warning(root_trees["root_config"], missing_sets)
1026                 if missing_sets:
1027                         setconfig_fallback(root_trees["root_config"])
1028
1029 def expand_set_arguments(myfiles, myaction, root_config):
1030         retval = os.EX_OK
1031         setconfig = root_config.setconfig
1032
1033         sets = setconfig.getSets()
1034
1035         # In order to know exactly which atoms/sets should be added to the
1036         # world file, the depgraph performs set expansion later. It will get
1037         # confused about where the atoms came from if it's not allowed to
1038         # expand them itself.
1039         do_not_expand = (None, )
1040         newargs = []
1041         for a in myfiles:
1042                 if a in ("system", "world"):
1043                         newargs.append(SETPREFIX+a)
1044                 else:
1045                         newargs.append(a)
1046         myfiles = newargs
1047         del newargs
1048         newargs = []
1049
1050         # separators for set arguments
1051         ARG_START = "{"
1052         ARG_END = "}"
1053
1054         for i in range(0, len(myfiles)):
1055                 if myfiles[i].startswith(SETPREFIX):
1056                         start = 0
1057                         end = 0
1058                         x = myfiles[i][len(SETPREFIX):]
1059                         newset = ""
1060                         while x:
1061                                 start = x.find(ARG_START)
1062                                 end = x.find(ARG_END)
1063                                 if start > 0 and start < end:
1064                                         namepart = x[:start]
1065                                         argpart = x[start+1:end]
1066
1067                                         # TODO: implement proper quoting
1068                                         args = argpart.split(",")
1069                                         options = {}
1070                                         for a in args:
1071                                                 if "=" in a:
1072                                                         k, v  = a.split("=", 1)
1073                                                         options[k] = v
1074                                                 else:
1075                                                         options[a] = "True"
1076                                         setconfig.update(namepart, options)
1077                                         newset += (x[:start-len(namepart)]+namepart)
1078                                         x = x[end+len(ARG_END):]
1079                                 else:
1080                                         newset += x
1081                                         x = ""
1082                         myfiles[i] = SETPREFIX+newset
1083
1084         sets = setconfig.getSets()
1085
1086         # display errors that occured while loading the SetConfig instance
1087         for e in setconfig.errors:
1088                 print(colorize("BAD", "Error during set creation: %s" % e))
1089
1090         unmerge_actions = ("unmerge", "prune", "clean", "depclean")
1091
1092         for a in myfiles:
1093                 if a.startswith(SETPREFIX):             
1094                                 s = a[len(SETPREFIX):]
1095                                 if s not in sets:
1096                                         display_missing_pkg_set(root_config, s)
1097                                         return (None, 1)
1098                                 setconfig.active.append(s)
1099                                 try:
1100                                         set_atoms = setconfig.getSetAtoms(s)
1101                                 except portage.exception.PackageSetNotFound as e:
1102                                         writemsg_level(("emerge: the given set '%s' " + \
1103                                                 "contains a non-existent set named '%s'.\n") % \
1104                                                 (s, e), level=logging.ERROR, noiselevel=-1)
1105                                         return (None, 1)
1106                                 if myaction in unmerge_actions and \
1107                                                 not sets[s].supportsOperation("unmerge"):
1108                                         sys.stderr.write("emerge: the given set '%s' does " % s + \
1109                                                 "not support unmerge operations\n")
1110                                         retval = 1
1111                                 elif not set_atoms:
1112                                         print("emerge: '%s' is an empty set" % s)
1113                                 elif myaction not in do_not_expand:
1114                                         newargs.extend(set_atoms)
1115                                 else:
1116                                         newargs.append(SETPREFIX+s)
1117                                 for e in sets[s].errors:
1118                                         print(e)
1119                 else:
1120                         newargs.append(a)
1121         return (newargs, retval)
1122
1123 def repo_name_check(trees):
1124         missing_repo_names = set()
1125         for root, root_trees in trees.items():
1126                 if "porttree" in root_trees:
1127                         portdb = root_trees["porttree"].dbapi
1128                         missing_repo_names.update(portdb.porttrees)
1129                         repos = portdb.getRepositories()
1130                         for r in repos:
1131                                 missing_repo_names.discard(portdb.getRepositoryPath(r))
1132                         if portdb.porttree_root in missing_repo_names and \
1133                                 not os.path.exists(os.path.join(
1134                                 portdb.porttree_root, "profiles")):
1135                                 # This is normal if $PORTDIR happens to be empty,
1136                                 # so don't warn about it.
1137                                 missing_repo_names.remove(portdb.porttree_root)
1138
1139         if missing_repo_names:
1140                 msg = []
1141                 msg.append("WARNING: One or more repositories " + \
1142                         "have missing repo_name entries:")
1143                 msg.append("")
1144                 for p in missing_repo_names:
1145                         msg.append("\t%s/profiles/repo_name" % (p,))
1146                 msg.append("")
1147                 msg.extend(textwrap.wrap("NOTE: Each repo_name entry " + \
1148                         "should be a plain text file containing a unique " + \
1149                         "name for the repository on the first line.", 70))
1150                 writemsg_level("".join("%s\n" % l for l in msg),
1151                         level=logging.WARNING, noiselevel=-1)
1152
1153         return bool(missing_repo_names)
1154
1155 def repo_name_duplicate_check(trees):
1156         ignored_repos = {}
1157         for root, root_trees in trees.items():
1158                 if 'porttree' in root_trees:
1159                         portdb = root_trees['porttree'].dbapi
1160                         if portdb.settings.get('PORTAGE_REPO_DUPLICATE_WARN') != '0':
1161                                 for repo_name, paths in portdb._ignored_repos:
1162                                         k = (root, repo_name, portdb.getRepositoryPath(repo_name))
1163                                         ignored_repos.setdefault(k, []).extend(paths)
1164
1165         if ignored_repos:
1166                 msg = []
1167                 msg.append('WARNING: One or more repositories ' + \
1168                         'have been ignored due to duplicate')
1169                 msg.append('  profiles/repo_name entries:')
1170                 msg.append('')
1171                 for k in sorted(ignored_repos):
1172                         msg.append('  %s overrides' % (k,))
1173                         for path in ignored_repos[k]:
1174                                 msg.append('    %s' % (path,))
1175                         msg.append('')
1176                 msg.extend('  ' + x for x in textwrap.wrap(
1177                         "All profiles/repo_name entries must be unique in order " + \
1178                         "to avoid having duplicates ignored. " + \
1179                         "Set PORTAGE_REPO_DUPLICATE_WARN=\"0\" in " + \
1180                         "/etc/make.conf if you would like to disable this warning."))
1181                 writemsg_level(''.join('%s\n' % l for l in msg),
1182                         level=logging.WARNING, noiselevel=-1)
1183
1184         return bool(ignored_repos)
1185
1186 def config_protect_check(trees):
1187         for root, root_trees in trees.items():
1188                 if not root_trees["root_config"].settings.get("CONFIG_PROTECT"):
1189                         msg = "!!! CONFIG_PROTECT is empty"
1190                         if root != "/":
1191                                 msg += " for '%s'" % root
1192                         msg += "\n"
1193                         writemsg_level(msg, level=logging.WARN, noiselevel=-1)
1194
1195 def profile_check(trees, myaction):
1196         if myaction in ("help", "info", "sync", "version"):
1197                 return os.EX_OK
1198         for root, root_trees in trees.items():
1199                 if root_trees["root_config"].settings.profiles:
1200                         continue
1201                 # generate some profile related warning messages
1202                 validate_ebuild_environment(trees)
1203                 msg = "If you have just changed your profile configuration, you " + \
1204                         "should revert back to the previous configuration. Due to " + \
1205                         "your current profile being invalid, allowed actions are " + \
1206                         "limited to --help, --info, --sync, and --version."
1207                 writemsg_level("".join("!!! %s\n" % l for l in textwrap.wrap(msg, 70)),
1208                         level=logging.ERROR, noiselevel=-1)
1209                 return 1
1210         return os.EX_OK
1211
1212 def check_procfs():
1213         procfs_path = '/proc'
1214         if platform.system() not in ("Linux",) or \
1215                 os.path.ismount(procfs_path):
1216                 return os.EX_OK
1217         msg = "It seems that %s is not mounted. You have been warned." % procfs_path
1218         writemsg_level("".join("!!! %s\n" % l for l in textwrap.wrap(msg, 70)),
1219                 level=logging.ERROR, noiselevel=-1)
1220         return 1
1221
1222 def emerge_main():
1223         global portage  # NFC why this is necessary now - genone
1224         portage._disable_legacy_globals()
1225         # Disable color until we're sure that it should be enabled (after
1226         # EMERGE_DEFAULT_OPTS has been parsed).
1227         portage.output.havecolor = 0
1228         # This first pass is just for options that need to be known as early as
1229         # possible, such as --config-root.  They will be parsed again later,
1230         # together with EMERGE_DEFAULT_OPTS (which may vary depending on the
1231         # the value of --config-root).
1232         myaction, myopts, myfiles = parse_opts(sys.argv[1:], silent=True)
1233         if "--debug" in myopts:
1234                 os.environ["PORTAGE_DEBUG"] = "1"
1235         if "--config-root" in myopts:
1236                 os.environ["PORTAGE_CONFIGROOT"] = myopts["--config-root"]
1237         if "--root" in myopts:
1238                 os.environ["ROOT"] = myopts["--root"]
1239         if "--accept-properties" in myopts:
1240                 os.environ["ACCEPT_PROPERTIES"] = myopts["--accept-properties"]
1241
1242         # Portage needs to ensure a sane umask for the files it creates.
1243         os.umask(0o22)
1244         settings, trees, mtimedb = load_emerge_config()
1245         portdb = trees[settings["ROOT"]]["porttree"].dbapi
1246         rval = profile_check(trees, myaction)
1247         if rval != os.EX_OK:
1248                 return rval
1249
1250         if myaction not in ('help', 'info', 'version') and \
1251                 _global_updates(trees, mtimedb["updates"]):
1252                 mtimedb.commit()
1253                 # Reload the whole config from scratch.
1254                 settings, trees, mtimedb = load_emerge_config(trees=trees)
1255                 portdb = trees[settings["ROOT"]]["porttree"].dbapi
1256
1257         xterm_titles = "notitles" not in settings.features
1258         if xterm_titles:
1259                 xtermTitle("emerge")
1260
1261         tmpcmdline = []
1262         if "--ignore-default-opts" not in myopts:
1263                 tmpcmdline.extend(settings["EMERGE_DEFAULT_OPTS"].split())
1264         tmpcmdline.extend(sys.argv[1:])
1265         myaction, myopts, myfiles = parse_opts(tmpcmdline)
1266
1267         if "--digest" in myopts:
1268                 os.environ["FEATURES"] = os.environ.get("FEATURES","") + " digest"
1269                 # Reload the whole config from scratch so that the portdbapi internal
1270                 # config is updated with new FEATURES.
1271                 settings, trees, mtimedb = load_emerge_config(trees=trees)
1272                 portdb = trees[settings["ROOT"]]["porttree"].dbapi
1273
1274         adjust_configs(myopts, trees)
1275         apply_priorities(settings)
1276
1277         if myaction == 'version':
1278                 writemsg_stdout(getportageversion(
1279                         settings["PORTDIR"], settings["ROOT"],
1280                         settings.profile_path, settings["CHOST"],
1281                         trees[settings["ROOT"]]["vartree"].dbapi) + '\n', noiselevel=-1)
1282                 return 0
1283         elif myaction == 'help':
1284                 _emerge.help.help(myopts, portage.output.havecolor)
1285                 return 0
1286
1287         spinner = stdout_spinner()
1288         if "candy" in settings.features:
1289                 spinner.update = spinner.update_scroll
1290
1291         if "--quiet" not in myopts:
1292                 portage.deprecated_profile_check(settings=settings)
1293                 repo_name_check(trees)
1294                 repo_name_duplicate_check(trees)
1295                 config_protect_check(trees)
1296         check_procfs()
1297
1298         if "getbinpkg" in settings.features:
1299                 myopts["--getbinpkg"] = True
1300
1301         if "--getbinpkgonly" in myopts:
1302                 myopts["--getbinpkg"] = True
1303
1304         if "--getbinpkgonly" in myopts:
1305                 myopts["--usepkgonly"] = True
1306
1307         if "--getbinpkg" in myopts:
1308                 myopts["--usepkg"] = True
1309
1310         if "--usepkgonly" in myopts:
1311                 myopts["--usepkg"] = True
1312
1313         if "buildpkg" in settings.features or "--buildpkgonly" in myopts:
1314                 myopts["--buildpkg"] = True
1315
1316         if "--buildpkgonly" in myopts:
1317                 # --buildpkgonly will not merge anything, so
1318                 # it cancels all binary package options.
1319                 for opt in ("--getbinpkg", "--getbinpkgonly",
1320                         "--usepkg", "--usepkgonly"):
1321                         myopts.pop(opt, None)
1322
1323         for mytrees in trees.values():
1324                 mydb = mytrees["porttree"].dbapi
1325                 # Freeze the portdbapi for performance (memoize all xmatch results).
1326                 mydb.freeze()
1327
1328                 if "--usepkg" in myopts:
1329                         # Populate the bintree with current --getbinpkg setting.
1330                         # This needs to happen before expand_set_arguments(), in case
1331                         # any sets use the bintree.
1332                         mytrees["bintree"].populate(
1333                                 getbinpkgs="--getbinpkg" in myopts)
1334
1335         del mytrees, mydb
1336
1337         if "moo" in myfiles:
1338                 print("""
1339
1340   Larry loves Gentoo (""" + platform.system() + """)
1341
1342  _______________________
1343 < Have you mooed today? >
1344  -----------------------
1345         \   ^__^
1346          \  (oo)\_______
1347             (__)\       )\/\ 
1348                 ||----w |
1349                 ||     ||
1350
1351 """)
1352
1353         for x in myfiles:
1354                 ext = os.path.splitext(x)[1]
1355                 if (ext == ".ebuild" or ext == ".tbz2") and os.path.exists(os.path.abspath(x)):
1356                         print(colorize("BAD", "\n*** emerging by path is broken and may not always work!!!\n"))
1357                         break
1358
1359         root_config = trees[settings["ROOT"]]["root_config"]
1360         if myaction == "list-sets":
1361                 writemsg_stdout("".join("%s\n" % s for s in sorted(root_config.sets)))
1362                 return os.EX_OK
1363
1364         ensure_required_sets(trees)
1365
1366         # only expand sets for actions taking package arguments
1367         oldargs = myfiles[:]
1368         if myaction in ("clean", "config", "depclean", "info", "prune", "unmerge", None):
1369                 myfiles, retval = expand_set_arguments(myfiles, myaction, root_config)
1370                 if retval != os.EX_OK:
1371                         return retval
1372
1373                 # Need to handle empty sets specially, otherwise emerge will react 
1374                 # with the help message for empty argument lists
1375                 if oldargs and not myfiles:
1376                         print("emerge: no targets left after set expansion")
1377                         return 0
1378
1379         if ("--tree" in myopts) and ("--columns" in myopts):
1380                 print("emerge: can't specify both of \"--tree\" and \"--columns\".")
1381                 return 1
1382
1383         if '--emptytree' in myopts and '--noreplace' in myopts:
1384                 writemsg_level("emerge: can't specify both of " + \
1385                         "\"--emptytree\" and \"--noreplace\".\n",
1386                         level=logging.ERROR, noiselevel=-1)
1387                 return 1
1388
1389         if ("--quiet" in myopts):
1390                 spinner.update = spinner.update_quiet
1391                 portage.util.noiselimit = -1
1392
1393         if "--fetch-all-uri" in myopts:
1394                 myopts["--fetchonly"] = True
1395
1396         if "--skipfirst" in myopts and "--resume" not in myopts:
1397                 myopts["--resume"] = True
1398
1399         # Allow -p to remove --ask
1400         if "--pretend" in myopts:
1401                 myopts.pop("--ask", None)
1402
1403         # forbid --ask when not in a terminal
1404         # note: this breaks `emerge --ask | tee logfile`, but that doesn't work anyway.
1405         if ("--ask" in myopts) and (not sys.stdin.isatty()):
1406                 portage.writemsg("!!! \"--ask\" should only be used in a terminal. Exiting.\n",
1407                         noiselevel=-1)
1408                 return 1
1409
1410         if settings.get("PORTAGE_DEBUG", "") == "1":
1411                 spinner.update = spinner.update_quiet
1412                 portage.debug=1
1413                 if "python-trace" in settings.features:
1414                         import portage.debug
1415                         portage.debug.set_trace(True)
1416
1417         if not ("--quiet" in myopts):
1418                 if '--nospinner' in myopts or \
1419                         settings.get('TERM') == 'dumb' or \
1420                         not sys.stdout.isatty():
1421                         spinner.update = spinner.update_basic
1422
1423         if "--debug" in myopts:
1424                 print("myaction", myaction)
1425                 print("myopts", myopts)
1426
1427         if not myaction and not myfiles and "--resume" not in myopts:
1428                 _emerge.help.help(myopts, portage.output.havecolor)
1429                 return 1
1430
1431         pretend = "--pretend" in myopts
1432         fetchonly = "--fetchonly" in myopts or "--fetch-all-uri" in myopts
1433         buildpkgonly = "--buildpkgonly" in myopts
1434
1435         # check if root user is the current user for the actions where emerge needs this
1436         if portage.secpass < 2:
1437                 # We've already allowed "--version" and "--help" above.
1438                 if "--pretend" not in myopts and myaction not in ("search","info"):
1439                         need_superuser = myaction in ('clean', 'depclean', 'deselect',
1440                                 'prune', 'unmerge') or not \
1441                                 (fetchonly or \
1442                                 (buildpkgonly and secpass >= 1) or \
1443                                 myaction in ("metadata", "regen") or \
1444                                 (myaction == "sync" and os.access(settings["PORTDIR"], os.W_OK)))
1445                         if portage.secpass < 1 or \
1446                                 need_superuser:
1447                                 if need_superuser:
1448                                         access_desc = "superuser"
1449                                 else:
1450                                         access_desc = "portage group"
1451                                 # Always show portage_group_warning() when only portage group
1452                                 # access is required but the user is not in the portage group.
1453                                 from portage.data import portage_group_warning
1454                                 if "--ask" in myopts:
1455                                         myopts["--pretend"] = True
1456                                         del myopts["--ask"]
1457                                         print(("%s access is required... " + \
1458                                                 "adding --pretend to options\n") % access_desc)
1459                                         if portage.secpass < 1 and not need_superuser:
1460                                                 portage_group_warning()
1461                                 else:
1462                                         sys.stderr.write(("emerge: %s access is required\n") \
1463                                                 % access_desc)
1464                                         if portage.secpass < 1 and not need_superuser:
1465                                                 portage_group_warning()
1466                                         return 1
1467
1468         disable_emergelog = False
1469         for x in ("--pretend", "--fetchonly", "--fetch-all-uri"):
1470                 if x in myopts:
1471                         disable_emergelog = True
1472                         break
1473         if myaction in ("search", "info"):
1474                 disable_emergelog = True
1475         if disable_emergelog:
1476                 """ Disable emergelog for everything except build or unmerge
1477                 operations.  This helps minimize parallel emerge.log entries that can
1478                 confuse log parsers.  We especially want it disabled during
1479                 parallel-fetch, which uses --resume --fetchonly."""
1480                 _emerge.emergelog._disable = True
1481
1482         else:
1483                 if 'EMERGE_LOG_DIR' in settings:
1484                         try:
1485                                 # At least the parent needs to exist for the lock file.
1486                                 portage.util.ensure_dirs(settings['EMERGE_LOG_DIR'])
1487                         except portage.exception.PortageException as e:
1488                                 writemsg_level("!!! Error creating directory for " + \
1489                                         "EMERGE_LOG_DIR='%s':\n!!! %s\n" % \
1490                                         (settings['EMERGE_LOG_DIR'], e),
1491                                         noiselevel=-1, level=logging.ERROR)
1492                         else:
1493                                 global _emerge_log_dir
1494                                 _emerge_log_dir = settings['EMERGE_LOG_DIR']
1495
1496         if not "--pretend" in myopts:
1497                 emergelog(xterm_titles, "Started emerge on: "+\
1498                         _unicode_decode(
1499                                 time.strftime("%b %d, %Y %H:%M:%S", time.localtime()),
1500                                 encoding=_encodings['content'], errors='replace'))
1501                 myelogstr=""
1502                 if myopts:
1503                         myelogstr=" ".join(myopts)
1504                 if myaction:
1505                         myelogstr+=" "+myaction
1506                 if myfiles:
1507                         myelogstr += " " + " ".join(oldargs)
1508                 emergelog(xterm_titles, " *** emerge " + myelogstr)
1509         del oldargs
1510
1511         def emergeexitsig(signum, frame):
1512                 signal.signal(signal.SIGINT, signal.SIG_IGN)
1513                 signal.signal(signal.SIGTERM, signal.SIG_IGN)
1514                 portage.util.writemsg("\n\nExiting on signal %(signal)s\n" % {"signal":signum})
1515                 sys.exit(100+signum)
1516         signal.signal(signal.SIGINT, emergeexitsig)
1517         signal.signal(signal.SIGTERM, emergeexitsig)
1518
1519         def emergeexit():
1520                 """This gets out final log message in before we quit."""
1521                 if "--pretend" not in myopts:
1522                         emergelog(xterm_titles, " *** terminating.")
1523                 if xterm_titles:
1524                         xtermTitleReset()
1525         portage.atexit_register(emergeexit)
1526
1527         if myaction in ("config", "metadata", "regen", "sync"):
1528                 if "--pretend" in myopts:
1529                         sys.stderr.write(("emerge: The '%s' action does " + \
1530                                 "not support '--pretend'.\n") % myaction)
1531                         return 1
1532
1533         if "sync" == myaction:
1534                 return action_sync(settings, trees, mtimedb, myopts, myaction)
1535         elif "metadata" == myaction:
1536                 action_metadata(settings, portdb, myopts)
1537         elif myaction=="regen":
1538                 validate_ebuild_environment(trees)
1539                 return action_regen(settings, portdb, myopts.get("--jobs"),
1540                         myopts.get("--load-average"))
1541         # HELP action
1542         elif "config"==myaction:
1543                 validate_ebuild_environment(trees)
1544                 action_config(settings, trees, myopts, myfiles)
1545
1546         # SEARCH action
1547         elif "search"==myaction:
1548                 validate_ebuild_environment(trees)
1549                 action_search(trees[settings["ROOT"]]["root_config"],
1550                         myopts, myfiles, spinner)
1551
1552         elif myaction in ('clean', 'depclean', 'deselect', 'prune', 'unmerge'):
1553                 validate_ebuild_environment(trees)
1554                 rval = action_uninstall(settings, trees, mtimedb["ldpath"],
1555                         myopts, myaction, myfiles, spinner)
1556                 if not (myaction == 'deselect' or buildpkgonly or fetchonly or pretend):
1557                         post_emerge(root_config, myopts, mtimedb, rval)
1558                 return rval
1559
1560         elif myaction == 'info':
1561
1562                 # Ensure atoms are valid before calling unmerge().
1563                 vardb = trees[settings["ROOT"]]["vartree"].dbapi
1564                 portdb = trees[settings["ROOT"]]["porttree"].dbapi
1565                 bindb = trees[settings["ROOT"]]["bintree"].dbapi
1566                 valid_atoms = []
1567                 for x in myfiles:
1568                         if is_valid_package_atom(x):
1569                                 try:
1570                                         #look at the installed files first, if there is no match
1571                                         #look at the ebuilds, since EAPI 4 allows running pkg_info
1572                                         #on non-installed packages
1573                                         valid_atom = dep_expand(x, mydb=vardb, settings=settings)
1574                                         if valid_atom.cp.split("/")[0] == "null":
1575                                                 valid_atom = dep_expand(x, mydb=portdb, settings=settings)
1576                                         if valid_atom.cp.split("/")[0] == "null" and "--usepkg" in myopts:
1577                                                 valid_atom = dep_expand(x, mydb=bindb, settings=settings)
1578                                         valid_atoms.append(valid_atom)
1579                                 except portage.exception.AmbiguousPackageName as e:
1580                                         msg = "The short ebuild name \"" + x + \
1581                                                 "\" is ambiguous.  Please specify " + \
1582                                                 "one of the following " + \
1583                                                 "fully-qualified ebuild names instead:"
1584                                         for line in textwrap.wrap(msg, 70):
1585                                                 writemsg_level("!!! %s\n" % (line,),
1586                                                         level=logging.ERROR, noiselevel=-1)
1587                                         for i in e[0]:
1588                                                 writemsg_level("    %s\n" % colorize("INFORM", i),
1589                                                         level=logging.ERROR, noiselevel=-1)
1590                                         writemsg_level("\n", level=logging.ERROR, noiselevel=-1)
1591                                         return 1
1592                                 continue
1593                         msg = []
1594                         msg.append("'%s' is not a valid package atom." % (x,))
1595                         msg.append("Please check ebuild(5) for full details.")
1596                         writemsg_level("".join("!!! %s\n" % line for line in msg),
1597                                 level=logging.ERROR, noiselevel=-1)
1598                         return 1
1599
1600                 return action_info(settings, trees, myopts, valid_atoms)
1601
1602         # "update", "system", or just process files:
1603         else:
1604                 validate_ebuild_environment(trees)
1605
1606                 for x in myfiles:
1607                         if x.startswith(SETPREFIX) or \
1608                                 is_valid_package_atom(x):
1609                                 continue
1610                         if x[:1] == os.sep:
1611                                 continue
1612                         try:
1613                                 os.lstat(x)
1614                                 continue
1615                         except OSError:
1616                                 pass
1617                         msg = []
1618                         msg.append("'%s' is not a valid package atom." % (x,))
1619                         msg.append("Please check ebuild(5) for full details.")
1620                         writemsg_level("".join("!!! %s\n" % line for line in msg),
1621                                 level=logging.ERROR, noiselevel=-1)
1622                         return 1
1623
1624                 if "--pretend" not in myopts:
1625                         display_news_notification(root_config, myopts)
1626                 retval = action_build(settings, trees, mtimedb,
1627                         myopts, myaction, myfiles, spinner)
1628                 root_config = trees[settings["ROOT"]]["root_config"]
1629                 post_emerge(root_config, myopts, mtimedb, retval)
1630
1631                 return retval