summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorZac Medico <zmedico@gentoo.org>2009-06-23 18:46:38 +0000
committerZac Medico <zmedico@gentoo.org>2009-06-23 18:46:38 +0000
commit6ca0735178ef51c087f35c2bbf2bca415bd4ab55 (patch)
tree2b8a816b6c17e2fd4f2f5c8f00d67db19c0855e1
parent078ea4131bf890142cd9da68c9af64982e33a795 (diff)
downloadportage-6ca0735178ef51c087f35c2bbf2bca415bd4ab55.tar.gz
portage-6ca0735178ef51c087f35c2bbf2bca415bd4ab55.tar.bz2
portage-6ca0735178ef51c087f35c2bbf2bca415bd4ab55.zip
Bug #275047 - Split _emerge/__init__.py into smaller pieces (part 5).
Thanks to Sebastian Mingramm (few) <s.mingramm@gmx.de> for this patch. svn path=/main/trunk/; revision=13672
-rw-r--r--pym/_emerge/Scheduler.py1641
-rw-r--r--pym/_emerge/__init__.py6754
-rw-r--r--pym/_emerge/_find_deep_system_runtime_deps.py35
-rw-r--r--pym/_emerge/_flush_elog_mod_echo.py17
-rw-r--r--pym/_emerge/clear_caches.py19
-rw-r--r--pym/_emerge/create_depgraph_params.py33
-rw-r--r--pym/_emerge/create_world_atom.py91
-rw-r--r--pym/_emerge/depgraph.py4973
-rw-r--r--pym/_emerge/is_valid_package_atom.py16
9 files changed, 6845 insertions, 6734 deletions
diff --git a/pym/_emerge/Scheduler.py b/pym/_emerge/Scheduler.py
new file mode 100644
index 000000000..7cf71343a
--- /dev/null
+++ b/pym/_emerge/Scheduler.py
@@ -0,0 +1,1641 @@
+import logging
+import os
+import sys
+import textwrap
+import time
+import weakref
+from itertools import izip
+
+try:
+ import portage
+except ImportError:
+ from os import path as osp
+ sys.path.insert(0, osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), "pym"))
+ import portage
+
+from portage.cache.mappings import slot_dict_class
+from portage.elog.messages import eerror
+from portage.output import colorize, create_color_func, darkgreen, red
+bad = create_color_func("BAD")
+from portage.sets.base import InternalPackageSet
+from portage.util import writemsg, writemsg_level
+
+from _emerge.BinpkgPrefetcher import BinpkgPrefetcher
+from _emerge.Blocker import Blocker
+from _emerge.BlockerDB import BlockerDB
+from _emerge.clear_caches import clear_caches
+from _emerge.create_depgraph_params import create_depgraph_params
+from _emerge.create_world_atom import create_world_atom
+from _emerge.DepPriority import DepPriority
+from _emerge.EbuildFetcher import EbuildFetcher
+from _emerge.EbuildPhase import EbuildPhase
+from _emerge.emergelog import emergelog, _emerge_log_dir
+from _emerge._find_deep_system_runtime_deps import _find_deep_system_runtime_deps
+from _emerge._flush_elog_mod_echo import _flush_elog_mod_echo
+from _emerge.JobStatusDisplay import JobStatusDisplay
+from _emerge.MergeListItem import MergeListItem
+from _emerge.Package import Package
+from _emerge.PackageMerge import PackageMerge
+from _emerge.PollScheduler import PollScheduler
+from _emerge.RootConfig import RootConfig
+from _emerge.SlotObject import SlotObject
+from _emerge.SequentialTaskQueue import SequentialTaskQueue
+from _emerge.show_invalid_depstring_notice import show_invalid_depstring_notice
+
+import portage.proxy.lazyimport
+import portage.proxy as proxy
+proxy.lazyimport.lazyimport(globals(),
+ '_emerge.depgraph:depgraph',
+)
+
+class Scheduler(PollScheduler):
+
+ _opts_ignore_blockers = \
+ frozenset(["--buildpkgonly",
+ "--fetchonly", "--fetch-all-uri",
+ "--nodeps", "--pretend"])
+
+ _opts_no_background = \
+ frozenset(["--pretend",
+ "--fetchonly", "--fetch-all-uri"])
+
+ _opts_no_restart = frozenset(["--buildpkgonly",
+ "--fetchonly", "--fetch-all-uri", "--pretend"])
+
+ _bad_resume_opts = set(["--ask", "--changelog",
+ "--resume", "--skipfirst"])
+
+ _fetch_log = os.path.join(_emerge_log_dir, 'emerge-fetch.log')
+
+ class _iface_class(SlotObject):
+ __slots__ = ("dblinkEbuildPhase", "dblinkDisplayMerge",
+ "dblinkElog", "dblinkEmergeLog", "fetch", "register", "schedule",
+ "scheduleSetup", "scheduleUnpack", "scheduleYield",
+ "unregister")
+
+ class _fetch_iface_class(SlotObject):
+ __slots__ = ("log_file", "schedule")
+
+ _task_queues_class = slot_dict_class(
+ ("merge", "jobs", "fetch", "unpack"), prefix="")
+
+ class _build_opts_class(SlotObject):
+ __slots__ = ("buildpkg", "buildpkgonly",
+ "fetch_all_uri", "fetchonly", "pretend")
+
+ class _binpkg_opts_class(SlotObject):
+ __slots__ = ("fetchonly", "getbinpkg", "pretend")
+
+ class _pkg_count_class(SlotObject):
+ __slots__ = ("curval", "maxval")
+
+ class _emerge_log_class(SlotObject):
+ __slots__ = ("xterm_titles",)
+
+ def log(self, *pargs, **kwargs):
+ if not self.xterm_titles:
+ # Avoid interference with the scheduler's status display.
+ kwargs.pop("short_msg", None)
+ emergelog(self.xterm_titles, *pargs, **kwargs)
+
+ class _failed_pkg(SlotObject):
+ __slots__ = ("build_dir", "build_log", "pkg", "returncode")
+
+ class _ConfigPool(object):
+ """Interface for a task to temporarily allocate a config
+ instance from a pool. This allows a task to be constructed
+ long before the config instance actually becomes needed, like
+ when prefetchers are constructed for the whole merge list."""
+ __slots__ = ("_root", "_allocate", "_deallocate")
+ def __init__(self, root, allocate, deallocate):
+ self._root = root
+ self._allocate = allocate
+ self._deallocate = deallocate
+ def allocate(self):
+ return self._allocate(self._root)
+ def deallocate(self, settings):
+ self._deallocate(settings)
+
+ class _unknown_internal_error(portage.exception.PortageException):
+ """
+ Used internally to terminate scheduling. The specific reason for
+ the failure should have been dumped to stderr.
+ """
+ def __init__(self, value=""):
+ portage.exception.PortageException.__init__(self, value)
+
+ def __init__(self, settings, trees, mtimedb, myopts,
+ spinner, mergelist, favorites, digraph):
+ PollScheduler.__init__(self)
+ self.settings = settings
+ self.target_root = settings["ROOT"]
+ self.trees = trees
+ self.myopts = myopts
+ self._spinner = spinner
+ self._mtimedb = mtimedb
+ self._mergelist = mergelist
+ self._favorites = favorites
+ self._args_set = InternalPackageSet(favorites)
+ self._build_opts = self._build_opts_class()
+ for k in self._build_opts.__slots__:
+ setattr(self._build_opts, k, "--" + k.replace("_", "-") in myopts)
+ self._binpkg_opts = self._binpkg_opts_class()
+ for k in self._binpkg_opts.__slots__:
+ setattr(self._binpkg_opts, k, "--" + k.replace("_", "-") in myopts)
+
+ self.curval = 0
+ self._logger = self._emerge_log_class()
+ self._task_queues = self._task_queues_class()
+ for k in self._task_queues.allowed_keys:
+ setattr(self._task_queues, k,
+ SequentialTaskQueue())
+
+ # Holds merges that will wait to be executed when no builds are
+ # executing. This is useful for system packages since dependencies
+ # on system packages are frequently unspecified.
+ self._merge_wait_queue = []
+ # Holds merges that have been transfered from the merge_wait_queue to
+ # the actual merge queue. They are removed from this list upon
+ # completion. Other packages can start building only when this list is
+ # empty.
+ self._merge_wait_scheduled = []
+
+ # Holds system packages and their deep runtime dependencies. Before
+ # being merged, these packages go to merge_wait_queue, to be merged
+ # when no other packages are building.
+ self._deep_system_deps = set()
+
+ # Holds packages to merge which will satisfy currently unsatisfied
+ # deep runtime dependencies of system packages. If this is not empty
+ # then no parallel builds will be spawned until it is empty. This
+ # minimizes the possibility that a build will fail due to the system
+ # being in a fragile state. For example, see bug #259954.
+ self._unsatisfied_system_deps = set()
+
+ self._status_display = JobStatusDisplay(
+ xterm_titles=('notitles' not in settings.features))
+ self._max_load = myopts.get("--load-average")
+ max_jobs = myopts.get("--jobs")
+ if max_jobs is None:
+ max_jobs = 1
+ self._set_max_jobs(max_jobs)
+
+ # The root where the currently running
+ # portage instance is installed.
+ self._running_root = trees["/"]["root_config"]
+ self.edebug = 0
+ if settings.get("PORTAGE_DEBUG", "") == "1":
+ self.edebug = 1
+ self.pkgsettings = {}
+ self._config_pool = {}
+ self._blocker_db = {}
+ for root in trees:
+ self._config_pool[root] = []
+ self._blocker_db[root] = BlockerDB(trees[root]["root_config"])
+
+ fetch_iface = self._fetch_iface_class(log_file=self._fetch_log,
+ schedule=self._schedule_fetch)
+ self._sched_iface = self._iface_class(
+ dblinkEbuildPhase=self._dblink_ebuild_phase,
+ dblinkDisplayMerge=self._dblink_display_merge,
+ dblinkElog=self._dblink_elog,
+ dblinkEmergeLog=self._dblink_emerge_log,
+ fetch=fetch_iface, register=self._register,
+ schedule=self._schedule_wait,
+ scheduleSetup=self._schedule_setup,
+ scheduleUnpack=self._schedule_unpack,
+ scheduleYield=self._schedule_yield,
+ unregister=self._unregister)
+
+ self._prefetchers = weakref.WeakValueDictionary()
+ self._pkg_queue = []
+ self._completed_tasks = set()
+
+ self._failed_pkgs = []
+ self._failed_pkgs_all = []
+ self._failed_pkgs_die_msgs = []
+ self._post_mod_echo_msgs = []
+ self._parallel_fetch = False
+ merge_count = len([x for x in mergelist \
+ if isinstance(x, Package) and x.operation == "merge"])
+ self._pkg_count = self._pkg_count_class(
+ curval=0, maxval=merge_count)
+ self._status_display.maxval = self._pkg_count.maxval
+
+ # The load average takes some time to respond when new
+ # jobs are added, so we need to limit the rate of adding
+ # new jobs.
+ self._job_delay_max = 10
+ self._job_delay_factor = 1.0
+ self._job_delay_exp = 1.5
+ self._previous_job_start_time = None
+
+ self._set_digraph(digraph)
+
+ # This is used to memoize the _choose_pkg() result when
+ # no packages can be chosen until one of the existing
+ # jobs completes.
+ self._choose_pkg_return_early = False
+
+ features = self.settings.features
+ if "parallel-fetch" in features and \
+ not ("--pretend" in self.myopts or \
+ "--fetch-all-uri" in self.myopts or \
+ "--fetchonly" in self.myopts):
+ if "distlocks" not in features:
+ portage.writemsg(red("!!!")+"\n", noiselevel=-1)
+ portage.writemsg(red("!!!")+" parallel-fetching " + \
+ "requires the distlocks feature enabled"+"\n",
+ noiselevel=-1)
+ portage.writemsg(red("!!!")+" you have it disabled, " + \
+ "thus parallel-fetching is being disabled"+"\n",
+ noiselevel=-1)
+ portage.writemsg(red("!!!")+"\n", noiselevel=-1)
+ elif len(mergelist) > 1:
+ self._parallel_fetch = True
+
+ if self._parallel_fetch:
+ # clear out existing fetch log if it exists
+ try:
+ open(self._fetch_log, 'w')
+ except EnvironmentError:
+ pass
+
+ self._running_portage = None
+ portage_match = self._running_root.trees["vartree"].dbapi.match(
+ portage.const.PORTAGE_PACKAGE_ATOM)
+ if portage_match:
+ cpv = portage_match.pop()
+ self._running_portage = self._pkg(cpv, "installed",
+ self._running_root, installed=True)
+
+ def _poll(self, timeout=None):
+ self._schedule()
+ PollScheduler._poll(self, timeout=timeout)
+
+ def _set_max_jobs(self, max_jobs):
+ self._max_jobs = max_jobs
+ self._task_queues.jobs.max_jobs = max_jobs
+
+ def _background_mode(self):
+ """
+ Check if background mode is enabled and adjust states as necessary.
+
+ @rtype: bool
+ @returns: True if background mode is enabled, False otherwise.
+ """
+ background = (self._max_jobs is True or \
+ self._max_jobs > 1 or "--quiet" in self.myopts) and \
+ not bool(self._opts_no_background.intersection(self.myopts))
+
+ if background:
+ interactive_tasks = self._get_interactive_tasks()
+ if interactive_tasks:
+ background = False
+ writemsg_level(">>> Sending package output to stdio due " + \
+ "to interactive package(s):\n",
+ level=logging.INFO, noiselevel=-1)
+ msg = [""]
+ for pkg in interactive_tasks:
+ pkg_str = " " + colorize("INFORM", str(pkg.cpv))
+ if pkg.root != "/":
+ pkg_str += " for " + pkg.root
+ msg.append(pkg_str)
+ msg.append("")
+ writemsg_level("".join("%s\n" % (l,) for l in msg),
+ level=logging.INFO, noiselevel=-1)
+ if self._max_jobs is True or self._max_jobs > 1:
+ self._set_max_jobs(1)
+ writemsg_level(">>> Setting --jobs=1 due " + \
+ "to the above interactive package(s)\n",
+ level=logging.INFO, noiselevel=-1)
+
+ self._status_display.quiet = \
+ not background or \
+ ("--quiet" in self.myopts and \
+ "--verbose" not in self.myopts)
+
+ self._logger.xterm_titles = \
+ "notitles" not in self.settings.features and \
+ self._status_display.quiet
+
+ return background
+
+ def _get_interactive_tasks(self):
+ from portage import flatten
+ from portage.dep import use_reduce, paren_reduce
+ interactive_tasks = []
+ for task in self._mergelist:
+ if not (isinstance(task, Package) and \
+ task.operation == "merge"):
+ continue
+ try:
+ properties = flatten(use_reduce(paren_reduce(
+ task.metadata["PROPERTIES"]), uselist=task.use.enabled))
+ except portage.exception.InvalidDependString, e:
+ show_invalid_depstring_notice(task,
+ task.metadata["PROPERTIES"], str(e))
+ raise self._unknown_internal_error()
+ if "interactive" in properties:
+ interactive_tasks.append(task)
+ return interactive_tasks
+
+ def _set_digraph(self, digraph):
+ if "--nodeps" in self.myopts or \
+ (self._max_jobs is not True and self._max_jobs < 2):
+ # save some memory
+ self._digraph = None
+ return
+
+ self._digraph = digraph
+ self._find_system_deps()
+ self._prune_digraph()
+ self._prevent_builddir_collisions()
+
+ def _find_system_deps(self):
+ """
+ Find system packages and their deep runtime dependencies. Before being
+ merged, these packages go to merge_wait_queue, to be merged when no
+ other packages are building.
+ """
+ deep_system_deps = self._deep_system_deps
+ deep_system_deps.clear()
+ deep_system_deps.update(
+ _find_deep_system_runtime_deps(self._digraph))
+ deep_system_deps.difference_update([pkg for pkg in \
+ deep_system_deps if pkg.operation != "merge"])
+
+ def _prune_digraph(self):
+ """
+ Prune any root nodes that are irrelevant.
+ """
+
+ graph = self._digraph
+ completed_tasks = self._completed_tasks
+ removed_nodes = set()
+ while True:
+ for node in graph.root_nodes():
+ if not isinstance(node, Package) or \
+ (node.installed and node.operation == "nomerge") or \
+ node.onlydeps or \
+ node in completed_tasks:
+ removed_nodes.add(node)
+ if removed_nodes:
+ graph.difference_update(removed_nodes)
+ if not removed_nodes:
+ break
+ removed_nodes.clear()
+
+ def _prevent_builddir_collisions(self):
+ """
+ When building stages, sometimes the same exact cpv needs to be merged
+ to both $ROOTs. Add edges to the digraph in order to avoid collisions
+ in the builddir. Currently, normal file locks would be inappropriate
+ for this purpose since emerge holds all of it's build dir locks from
+ the main process.
+ """
+ cpv_map = {}
+ for pkg in self._mergelist:
+ if not isinstance(pkg, Package):
+ # a satisfied blocker
+ continue
+ if pkg.installed:
+ continue
+ if pkg.cpv not in cpv_map:
+ cpv_map[pkg.cpv] = [pkg]
+ continue
+ for earlier_pkg in cpv_map[pkg.cpv]:
+ self._digraph.add(earlier_pkg, pkg,
+ priority=DepPriority(buildtime=True))
+ cpv_map[pkg.cpv].append(pkg)
+
+ class _pkg_failure(portage.exception.PortageException):
+ """
+ An instance of this class is raised by unmerge() when
+ an uninstallation fails.
+ """
+ status = 1
+ def __init__(self, *pargs):
+ portage.exception.PortageException.__init__(self, pargs)
+ if pargs:
+ self.status = pargs[0]
+
+ def _schedule_fetch(self, fetcher):
+ """
+ Schedule a fetcher on the fetch queue, in order to
+ serialize access to the fetch log.
+ """
+ self._task_queues.fetch.addFront(fetcher)
+
+ def _schedule_setup(self, setup_phase):
+ """
+ Schedule a setup phase on the merge queue, in order to
+ serialize unsandboxed access to the live filesystem.
+ """
+ self._task_queues.merge.addFront(setup_phase)
+ self._schedule()
+
+ def _schedule_unpack(self, unpack_phase):
+ """
+ Schedule an unpack phase on the unpack queue, in order
+ to serialize $DISTDIR access for live ebuilds.
+ """
+ self._task_queues.unpack.add(unpack_phase)
+
+ def _find_blockers(self, new_pkg):
+ """
+ Returns a callable which should be called only when
+ the vdb lock has been acquired.
+ """
+ def get_blockers():
+ return self._find_blockers_with_lock(new_pkg, acquire_lock=0)
+ return get_blockers
+
+ def _find_blockers_with_lock(self, new_pkg, acquire_lock=0):
+ if self._opts_ignore_blockers.intersection(self.myopts):
+ return None
+
+ # Call gc.collect() here to avoid heap overflow that
+ # triggers 'Cannot allocate memory' errors (reported
+ # with python-2.5).
+ import gc
+ gc.collect()
+
+ blocker_db = self._blocker_db[new_pkg.root]
+
+ blocker_dblinks = []
+ for blocking_pkg in blocker_db.findInstalledBlockers(
+ new_pkg, acquire_lock=acquire_lock):
+ if new_pkg.slot_atom == blocking_pkg.slot_atom:
+ continue
+ if new_pkg.cpv == blocking_pkg.cpv:
+ continue
+ blocker_dblinks.append(portage.dblink(
+ blocking_pkg.category, blocking_pkg.pf, blocking_pkg.root,
+ self.pkgsettings[blocking_pkg.root], treetype="vartree",
+ vartree=self.trees[blocking_pkg.root]["vartree"]))
+
+ gc.collect()
+
+ return blocker_dblinks
+
+ def _dblink_pkg(self, pkg_dblink):
+ cpv = pkg_dblink.mycpv
+ type_name = RootConfig.tree_pkg_map[pkg_dblink.treetype]
+ root_config = self.trees[pkg_dblink.myroot]["root_config"]
+ installed = type_name == "installed"
+ return self._pkg(cpv, type_name, root_config, installed=installed)
+
+ def _append_to_log_path(self, log_path, msg):
+ f = open(log_path, 'a')
+ try:
+ f.write(msg)
+ finally:
+ f.close()
+
+ def _dblink_elog(self, pkg_dblink, phase, func, msgs):
+
+ log_path = pkg_dblink.settings.get("PORTAGE_LOG_FILE")
+ log_file = None
+ out = sys.stdout
+ background = self._background
+
+ if background and log_path is not None:
+ log_file = open(log_path, 'a')
+ out = log_file
+
+ try:
+ for msg in msgs:
+ func(msg, phase=phase, key=pkg_dblink.mycpv, out=out)
+ finally:
+ if log_file is not None:
+ log_file.close()
+
+ def _dblink_emerge_log(self, msg):
+ self._logger.log(msg)
+
+ def _dblink_display_merge(self, pkg_dblink, msg, level=0, noiselevel=0):
+ log_path = pkg_dblink.settings.get("PORTAGE_LOG_FILE")
+ background = self._background
+
+ if log_path is None:
+ if not (background and level < logging.WARN):
+ portage.util.writemsg_level(msg,
+ level=level, noiselevel=noiselevel)
+ else:
+ if not background:
+ portage.util.writemsg_level(msg,
+ level=level, noiselevel=noiselevel)
+ self._append_to_log_path(log_path, msg)
+
+ def _dblink_ebuild_phase(self,
+ pkg_dblink, pkg_dbapi, ebuild_path, phase):
+ """
+ Using this callback for merge phases allows the scheduler
+ to run while these phases execute asynchronously, and allows
+ the scheduler control output handling.
+ """
+
+ scheduler = self._sched_iface
+ settings = pkg_dblink.settings
+ pkg = self._dblink_pkg(pkg_dblink)
+ background = self._background
+ log_path = settings.get("PORTAGE_LOG_FILE")
+
+ ebuild_phase = EbuildPhase(background=background,
+ pkg=pkg, phase=phase, scheduler=scheduler,
+ settings=settings, tree=pkg_dblink.treetype)
+ ebuild_phase.start()
+ ebuild_phase.wait()
+
+ return ebuild_phase.returncode
+
+ def _generate_digests(self):
+ """
+ Generate digests if necessary for --digests or FEATURES=digest.
+ In order to avoid interference, this must done before parallel
+ tasks are started.
+ """
+
+ if '--fetchonly' in self.myopts:
+ return os.EX_OK
+
+ digest = '--digest' in self.myopts
+ if not digest:
+ for pkgsettings in self.pkgsettings.itervalues():
+ if 'digest' in pkgsettings.features:
+ digest = True
+ break
+
+ if not digest:
+ return os.EX_OK
+
+ for x in self._mergelist:
+ if not isinstance(x, Package) or \
+ x.type_name != 'ebuild' or \
+ x.operation != 'merge':
+ continue
+ pkgsettings = self.pkgsettings[x.root]
+ if '--digest' not in self.myopts and \
+ 'digest' not in pkgsettings.features:
+ continue
+ portdb = x.root_config.trees['porttree'].dbapi
+ ebuild_path = portdb.findname(x.cpv)
+ if not ebuild_path:
+ writemsg_level(
+ "!!! Could not locate ebuild for '%s'.\n" \
+ % x.cpv, level=logging.ERROR, noiselevel=-1)
+ return 1
+ pkgsettings['O'] = os.path.dirname(ebuild_path)
+ if not portage.digestgen([], pkgsettings, myportdb=portdb):
+ writemsg_level(
+ "!!! Unable to generate manifest for '%s'.\n" \
+ % x.cpv, level=logging.ERROR, noiselevel=-1)
+ return 1
+
+ return os.EX_OK
+
+ def _check_manifests(self):
+ # Verify all the manifests now so that the user is notified of failure
+ # as soon as possible.
+ if "strict" not in self.settings.features or \
+ "--fetchonly" in self.myopts or \
+ "--fetch-all-uri" in self.myopts:
+ return os.EX_OK
+
+ shown_verifying_msg = False
+ quiet_settings = {}
+ for myroot, pkgsettings in self.pkgsettings.iteritems():
+ quiet_config = portage.config(clone=pkgsettings)
+ quiet_config["PORTAGE_QUIET"] = "1"
+ quiet_config.backup_changes("PORTAGE_QUIET")
+ quiet_settings[myroot] = quiet_config
+ del quiet_config
+
+ for x in self._mergelist:
+ if not isinstance(x, Package) or \
+ x.type_name != "ebuild":
+ continue
+
+ if not shown_verifying_msg:
+ shown_verifying_msg = True
+ self._status_msg("Verifying ebuild manifests")
+
+ root_config = x.root_config
+ portdb = root_config.trees["porttree"].dbapi
+ quiet_config = quiet_settings[root_config.root]
+ quiet_config["O"] = os.path.dirname(portdb.findname(x.cpv))
+ if not portage.digestcheck([], quiet_config, strict=True):
+ return 1
+
+ return os.EX_OK
+
+ def _add_prefetchers(self):
+
+ if not self._parallel_fetch:
+ return
+
+ if self._parallel_fetch:
+ self._status_msg("Starting parallel fetch")
+
+ prefetchers = self._prefetchers
+ getbinpkg = "--getbinpkg" in self.myopts
+
+ # In order to avoid "waiting for lock" messages
+ # at the beginning, which annoy users, never
+ # spawn a prefetcher for the first package.
+ for pkg in self._mergelist[1:]:
+ prefetcher = self._create_prefetcher(pkg)
+ if prefetcher is not None:
+ self._task_queues.fetch.add(prefetcher)
+ prefetchers[pkg] = prefetcher
+
+ def _create_prefetcher(self, pkg):
+ """
+ @return: a prefetcher, or None if not applicable
+ """
+ prefetcher = None
+
+ if not isinstance(pkg, Package):
+ pass
+
+ elif pkg.type_name == "ebuild":
+
+ prefetcher = EbuildFetcher(background=True,
+ config_pool=self._ConfigPool(pkg.root,
+ self._allocate_config, self._deallocate_config),
+ fetchonly=1, logfile=self._fetch_log,
+ pkg=pkg, prefetch=True, scheduler=self._sched_iface)
+
+ elif pkg.type_name == "binary" and \
+ "--getbinpkg" in self.myopts and \
+ pkg.root_config.trees["bintree"].isremote(pkg.cpv):
+
+ prefetcher = BinpkgPrefetcher(background=True,
+ pkg=pkg, scheduler=self._sched_iface)
+
+ return prefetcher
+
+ def _is_restart_scheduled(self):
+ """
+ Check if the merge list contains a replacement
+ for the current running instance, that will result
+ in restart after merge.
+ @rtype: bool
+ @returns: True if a restart is scheduled, False otherwise.
+ """
+ if self._opts_no_restart.intersection(self.myopts):
+ return False
+
+ mergelist = self._mergelist
+
+ for i, pkg in enumerate(mergelist):
+ if self._is_restart_necessary(pkg) and \
+ i != len(mergelist) - 1:
+ return True
+
+ return False
+
+ def _is_restart_necessary(self, pkg):
+ """
+ @return: True if merging the given package
+ requires restart, False otherwise.
+ """
+
+ # Figure out if we need a restart.
+ if pkg.root == self._running_root.root and \
+ portage.match_from_list(
+ portage.const.PORTAGE_PACKAGE_ATOM, [pkg]):
+ if self._running_portage:
+ return pkg.cpv != self._running_portage.cpv
+ return True
+ return False
+
+ def _restart_if_necessary(self, pkg):
+ """
+ Use execv() to restart emerge. This happens
+ if portage upgrades itself and there are
+ remaining packages in the list.
+ """
+
+ if self._opts_no_restart.intersection(self.myopts):
+ return
+
+ if not self._is_restart_necessary(pkg):
+ return
+
+ if pkg == self._mergelist[-1]:
+ return
+
+ self._main_loop_cleanup()
+
+ logger = self._logger
+ pkg_count = self._pkg_count
+ mtimedb = self._mtimedb
+ bad_resume_opts = self._bad_resume_opts
+
+ logger.log(" ::: completed emerge (%s of %s) %s to %s" % \
+ (pkg_count.curval, pkg_count.maxval, pkg.cpv, pkg.root))
+
+ logger.log(" *** RESTARTING " + \
+ "emerge via exec() after change of " + \
+ "portage version.")
+
+ mtimedb["resume"]["mergelist"].remove(list(pkg))
+ mtimedb.commit()
+ portage.run_exitfuncs()
+ mynewargv = [sys.argv[0], "--resume"]
+ resume_opts = self.myopts.copy()
+ # For automatic resume, we need to prevent
+ # any of bad_resume_opts from leaking in
+ # via EMERGE_DEFAULT_OPTS.
+ resume_opts["--ignore-default-opts"] = True
+ for myopt, myarg in resume_opts.iteritems():
+ if myopt not in bad_resume_opts:
+ if myarg is True:
+ mynewargv.append(myopt)
+ else:
+ mynewargv.append(myopt +"="+ str(myarg))
+ # priority only needs to be adjusted on the first run
+ os.environ["PORTAGE_NICENESS"] = "0"
+ os.execv(mynewargv[0], mynewargv)
+
+ def merge(self):
+
+ if "--resume" in self.myopts:
+ # We're resuming.
+ portage.writemsg_stdout(
+ colorize("GOOD", "*** Resuming merge...\n"), noiselevel=-1)
+ self._logger.log(" *** Resuming merge...")
+
+ self._save_resume_list()
+
+ try:
+ self._background = self._background_mode()
+ except self._unknown_internal_error:
+ return 1
+
+ for root in self.trees:
+ root_config = self.trees[root]["root_config"]
+
+ # Even for --pretend --fetch mode, PORTAGE_TMPDIR is required
+ # since it might spawn pkg_nofetch which requires PORTAGE_BUILDDIR
+ # for ensuring sane $PWD (bug #239560) and storing elog messages.
+ tmpdir = root_config.settings.get("PORTAGE_TMPDIR", "")
+ if not tmpdir or not os.path.isdir(tmpdir):
+ msg = "The directory specified in your " + \
+ "PORTAGE_TMPDIR variable, '%s', " % tmpdir + \
+ "does not exist. Please create this " + \
+ "directory or correct your PORTAGE_TMPDIR setting."
+ msg = textwrap.wrap(msg, 70)
+ out = portage.output.EOutput()
+ for l in msg:
+ out.eerror(l)
+ return 1
+
+ if self._background:
+ root_config.settings.unlock()
+ root_config.settings["PORTAGE_BACKGROUND"] = "1"
+ root_config.settings.backup_changes("PORTAGE_BACKGROUND")
+ root_config.settings.lock()
+
+ self.pkgsettings[root] = portage.config(
+ clone=root_config.settings)
+
+ rval = self._generate_digests()
+ if rval != os.EX_OK:
+ return rval
+
+ rval = self._check_manifests()
+ if rval != os.EX_OK:
+ return rval
+
+ keep_going = "--keep-going" in self.myopts
+ fetchonly = self._build_opts.fetchonly
+ mtimedb = self._mtimedb
+ failed_pkgs = self._failed_pkgs
+
+ while True:
+ rval = self._merge()
+ if rval == os.EX_OK or fetchonly or not keep_going:
+ break
+ if "resume" not in mtimedb:
+ break
+ mergelist = self._mtimedb["resume"].get("mergelist")
+ if not mergelist:
+ break
+
+ if not failed_pkgs:
+ break
+
+ for failed_pkg in failed_pkgs:
+ mergelist.remove(list(failed_pkg.pkg))
+
+ self._failed_pkgs_all.extend(failed_pkgs)
+ del failed_pkgs[:]
+
+ if not mergelist:
+ break
+
+ if not self._calc_resume_list():
+ break
+
+ clear_caches(self.trees)
+ if not self._mergelist:
+ break
+
+ self._save_resume_list()
+ self._pkg_count.curval = 0
+ self._pkg_count.maxval = len([x for x in self._mergelist \
+ if isinstance(x, Package) and x.operation == "merge"])
+ self._status_display.maxval = self._pkg_count.maxval
+
+ self._logger.log(" *** Finished. Cleaning up...")
+
+ if failed_pkgs:
+ self._failed_pkgs_all.extend(failed_pkgs)
+ del failed_pkgs[:]
+
+ background = self._background
+ failure_log_shown = False
+ if background and len(self._failed_pkgs_all) == 1:
+ # If only one package failed then just show it's
+ # whole log for easy viewing.
+ failed_pkg = self._failed_pkgs_all[-1]
+ build_dir = failed_pkg.build_dir
+ log_file = None
+
+ log_paths = [failed_pkg.build_log]
+
+ log_path = self._locate_failure_log(failed_pkg)
+ if log_path is not None:
+ try:
+ log_file = open(log_path)
+ except IOError:
+ pass
+
+ if log_file is not None:
+ try:
+ for line in log_file:
+ writemsg_level(line, noiselevel=-1)
+ finally:
+ log_file.close()
+ failure_log_shown = True
+
+ # Dump mod_echo output now since it tends to flood the terminal.
+ # This allows us to avoid having more important output, generated
+ # later, from being swept away by the mod_echo output.
+ mod_echo_output = _flush_elog_mod_echo()
+
+ if background and not failure_log_shown and \
+ self._failed_pkgs_all and \
+ self._failed_pkgs_die_msgs and \
+ not mod_echo_output:
+
+ printer = portage.output.EOutput()
+ for mysettings, key, logentries in self._failed_pkgs_die_msgs:
+ root_msg = ""
+ if mysettings["ROOT"] != "/":
+ root_msg = " merged to %s" % mysettings["ROOT"]
+ print
+ printer.einfo("Error messages for package %s%s:" % \
+ (colorize("INFORM", key), root_msg))
+ print
+ for phase in portage.const.EBUILD_PHASES:
+ if phase not in logentries:
+ continue
+ for msgtype, msgcontent in logentries[phase]:
+ if isinstance(msgcontent, basestring):
+ msgcontent = [msgcontent]
+ for line in msgcontent:
+ printer.eerror(line.strip("\n"))
+
+ if self._post_mod_echo_msgs:
+ for msg in self._post_mod_echo_msgs:
+ msg()
+
+ if len(self._failed_pkgs_all) > 1 or \
+ (self._failed_pkgs_all and "--keep-going" in self.myopts):
+ if len(self._failed_pkgs_all) > 1:
+ msg = "The following %d packages have " % \
+ len(self._failed_pkgs_all) + \
+ "failed to build or install:"
+ else:
+ msg = "The following package has " + \
+ "failed to build or install:"
+ prefix = bad(" * ")
+ writemsg(prefix + "\n", noiselevel=-1)
+ from textwrap import wrap
+ for line in wrap(msg, 72):
+ writemsg("%s%s\n" % (prefix, line), noiselevel=-1)
+ writemsg(prefix + "\n", noiselevel=-1)
+ for failed_pkg in self._failed_pkgs_all:
+ writemsg("%s\t%s\n" % (prefix,
+ colorize("INFORM", str(failed_pkg.pkg))),
+ noiselevel=-1)
+ writemsg(prefix + "\n", noiselevel=-1)
+
+ return rval
+
+ def _elog_listener(self, mysettings, key, logentries, fulltext):
+ errors = portage.elog.filter_loglevels(logentries, ["ERROR"])
+ if errors:
+ self._failed_pkgs_die_msgs.append(
+ (mysettings, key, errors))
+
+ def _locate_failure_log(self, failed_pkg):
+
+ build_dir = failed_pkg.build_dir
+ log_file = None
+
+ log_paths = [failed_pkg.build_log]
+
+ for log_path in log_paths:
+ if not log_path:
+ continue
+
+ try:
+ log_size = os.stat(log_path).st_size
+ except OSError:
+ continue
+
+ if log_size == 0:
+ continue
+
+ return log_path
+
+ return None
+
+ def _add_packages(self):
+ pkg_queue = self._pkg_queue
+ for pkg in self._mergelist:
+ if isinstance(pkg, Package):
+ pkg_queue.append(pkg)
+ elif isinstance(pkg, Blocker):
+ pass
+
+ def _system_merge_started(self, merge):
+ """
+ Add any unsatisfied runtime deps to self._unsatisfied_system_deps.
+ """
+ graph = self._digraph
+ if graph is None:
+ return
+ pkg = merge.merge.pkg
+
+ # Skip this if $ROOT != / since it shouldn't matter if there
+ # are unsatisfied system runtime deps in this case.
+ if pkg.root != '/':
+ return
+
+ completed_tasks = self._completed_tasks
+ unsatisfied = self._unsatisfied_system_deps
+
+ def ignore_non_runtime_or_satisfied(priority):
+ """
+ Ignore non-runtime and satisfied runtime priorities.
+ """
+ if isinstance(priority, DepPriority) and \
+ not priority.satisfied and \
+ (priority.runtime or priority.runtime_post):
+ return False
+ return True
+
+ # When checking for unsatisfied runtime deps, only check
+ # direct deps since indirect deps are checked when the
+ # corresponding parent is merged.
+ for child in graph.child_nodes(pkg,
+ ignore_priority=ignore_non_runtime_or_satisfied):
+ if not isinstance(child, Package) or \
+ child.operation == 'uninstall':
+ continue
+ if child is pkg:
+ continue
+ if child.operation == 'merge' and \
+ child not in completed_tasks:
+ unsatisfied.add(child)
+
+ def _merge_wait_exit_handler(self, task):
+ self._merge_wait_scheduled.remove(task)
+ self._merge_exit(task)
+
+ def _merge_exit(self, merge):
+ self._do_merge_exit(merge)
+ self._deallocate_config(merge.merge.settings)
+ if merge.returncode == os.EX_OK and \
+ not merge.merge.pkg.installed:
+ self._status_display.curval += 1
+ self._status_display.merges = len(self._task_queues.merge)
+ self._schedule()
+
+ def _do_merge_exit(self, merge):
+ pkg = merge.merge.pkg
+ if merge.returncode != os.EX_OK:
+ settings = merge.merge.settings
+ build_dir = settings.get("PORTAGE_BUILDDIR")
+ build_log = settings.get("PORTAGE_LOG_FILE")
+
+ self._failed_pkgs.append(self._failed_pkg(
+ build_dir=build_dir, build_log=build_log,
+ pkg=pkg,
+ returncode=merge.returncode))
+ self._failed_pkg_msg(self._failed_pkgs[-1], "install", "to")
+
+ self._status_display.failed = len(self._failed_pkgs)
+ return
+
+ self._task_complete(pkg)
+ pkg_to_replace = merge.merge.pkg_to_replace
+ if pkg_to_replace is not None:
+ # When a package is replaced, mark it's uninstall
+ # task complete (if any).
+ uninst_hash_key = \
+ ("installed", pkg.root, pkg_to_replace.cpv, "uninstall")
+ self._task_complete(uninst_hash_key)
+
+ if pkg.installed:
+ return
+
+ self._restart_if_necessary(pkg)
+
+ # Call mtimedb.commit() after each merge so that
+ # --resume still works after being interrupted
+ # by reboot, sigkill or similar.
+ mtimedb = self._mtimedb
+ mtimedb["resume"]["mergelist"].remove(list(pkg))
+ if not mtimedb["resume"]["mergelist"]:
+ del mtimedb["resume"]
+ mtimedb.commit()
+
+ def _build_exit(self, build):
+ if build.returncode == os.EX_OK:
+ self.curval += 1
+ merge = PackageMerge(merge=build)
+ if not build.build_opts.buildpkgonly and \
+ build.pkg in self._deep_system_deps:
+ # Since dependencies on system packages are frequently
+ # unspecified, merge them only when no builds are executing.
+ self._merge_wait_queue.append(merge)
+ merge.addStartListener(self._system_merge_started)
+ else:
+ merge.addExitListener(self._merge_exit)
+ self._task_queues.merge.add(merge)
+ self._status_display.merges = len(self._task_queues.merge)
+ else:
+ settings = build.settings
+ build_dir = settings.get("PORTAGE_BUILDDIR")
+ build_log = settings.get("PORTAGE_LOG_FILE")
+
+ self._failed_pkgs.append(self._failed_pkg(
+ build_dir=build_dir, build_log=build_log,
+ pkg=build.pkg,
+ returncode=build.returncode))
+ self._failed_pkg_msg(self._failed_pkgs[-1], "emerge", "for")
+
+ self._status_display.failed = len(self._failed_pkgs)
+ self._deallocate_config(build.settings)
+ self._jobs -= 1
+ self._status_display.running = self._jobs
+ self._schedule()
+
+ def _extract_exit(self, build):
+ self._build_exit(build)
+
+ def _task_complete(self, pkg):
+ self._completed_tasks.add(pkg)
+ self._unsatisfied_system_deps.discard(pkg)
+ self._choose_pkg_return_early = False
+
+ def _merge(self):
+
+ self._add_prefetchers()
+ self._add_packages()
+ pkg_queue = self._pkg_queue
+ failed_pkgs = self._failed_pkgs
+ portage.locks._quiet = self._background
+ portage.elog._emerge_elog_listener = self._elog_listener
+ rval = os.EX_OK
+
+ try:
+ self._main_loop()
+ finally:
+ self._main_loop_cleanup()
+ portage.locks._quiet = False
+ portage.elog._emerge_elog_listener = None
+ if failed_pkgs:
+ rval = failed_pkgs[-1].returncode
+
+ return rval
+
+ def _main_loop_cleanup(self):
+ del self._pkg_queue[:]
+ self._completed_tasks.clear()
+ self._deep_system_deps.clear()
+ self._unsatisfied_system_deps.clear()
+ self._choose_pkg_return_early = False
+ self._status_display.reset()
+ self._digraph = None
+ self._task_queues.fetch.clear()
+
+ def _choose_pkg(self):
+ """
+ Choose a task that has all it's dependencies satisfied.
+ """
+
+ if self._choose_pkg_return_early:
+ return None
+
+ if self._digraph is None:
+ if (self._jobs or self._task_queues.merge) and \
+ not ("--nodeps" in self.myopts and \
+ (self._max_jobs is True or self._max_jobs > 1)):
+ self._choose_pkg_return_early = True
+ return None
+ return self._pkg_queue.pop(0)
+
+ if not (self._jobs or self._task_queues.merge):
+ return self._pkg_queue.pop(0)
+
+ self._prune_digraph()
+
+ chosen_pkg = None
+ later = set(self._pkg_queue)
+ for pkg in self._pkg_queue:
+ later.remove(pkg)
+ if not self._dependent_on_scheduled_merges(pkg, later):
+ chosen_pkg = pkg
+ break
+
+ if chosen_pkg is not None:
+ self._pkg_queue.remove(chosen_pkg)
+
+ if chosen_pkg is None:
+ # There's no point in searching for a package to
+ # choose until at least one of the existing jobs
+ # completes.
+ self._choose_pkg_return_early = True
+
+ return chosen_pkg
+
+ def _dependent_on_scheduled_merges(self, pkg, later):
+ """
+ Traverse the subgraph of the given packages deep dependencies
+ to see if it contains any scheduled merges.
+ @param pkg: a package to check dependencies for
+ @type pkg: Package
+ @param later: packages for which dependence should be ignored
+ since they will be merged later than pkg anyway and therefore
+ delaying the merge of pkg will not result in a more optimal
+ merge order
+ @type later: set
+ @rtype: bool
+ @returns: True if the package is dependent, False otherwise.
+ """
+
+ graph = self._digraph
+ completed_tasks = self._completed_tasks
+
+ dependent = False
+ traversed_nodes = set([pkg])
+ direct_deps = graph.child_nodes(pkg)
+ node_stack = direct_deps
+ direct_deps = frozenset(direct_deps)
+ while node_stack:
+ node = node_stack.pop()
+ if node in traversed_nodes:
+ continue
+ traversed_nodes.add(node)
+ if not ((node.installed and node.operation == "nomerge") or \
+ (node.operation == "uninstall" and \
+ node not in direct_deps) or \
+ node in completed_tasks or \
+ node in later):
+ dependent = True
+ break
+ node_stack.extend(graph.child_nodes(node))
+
+ return dependent
+
+ def _allocate_config(self, root):
+ """
+ Allocate a unique config instance for a task in order
+ to prevent interference between parallel tasks.
+ """
+ if self._config_pool[root]:
+ temp_settings = self._config_pool[root].pop()
+ else:
+ temp_settings = portage.config(clone=self.pkgsettings[root])
+ # Since config.setcpv() isn't guaranteed to call config.reset() due to
+ # performance reasons, call it here to make sure all settings from the
+ # previous package get flushed out (such as PORTAGE_LOG_FILE).
+ temp_settings.reload()
+ temp_settings.reset()
+ return temp_settings
+
+ def _deallocate_config(self, settings):
+ self._config_pool[settings["ROOT"]].append(settings)
+
+ def _main_loop(self):
+
+ # Only allow 1 job max if a restart is scheduled
+ # due to portage update.
+ if self._is_restart_scheduled() or \
+ self._opts_no_background.intersection(self.myopts):
+ self._set_max_jobs(1)
+
+ merge_queue = self._task_queues.merge
+
+ while self._schedule():
+ if self._poll_event_handlers:
+ self._poll_loop()
+
+ while True:
+ self._schedule()
+ if not (self._jobs or merge_queue):
+ break
+ if self._poll_event_handlers:
+ self._poll_loop()
+
+ def _keep_scheduling(self):
+ return bool(self._pkg_queue and \
+ not (self._failed_pkgs and not self._build_opts.fetchonly))
+
+ def _schedule_tasks(self):
+
+ # When the number of jobs drops to zero, process all waiting merges.
+ if not self._jobs and self._merge_wait_queue:
+ for task in self._merge_wait_queue:
+ task.addExitListener(self._merge_wait_exit_handler)
+ self._task_queues.merge.add(task)
+ self._status_display.merges = len(self._task_queues.merge)
+ self._merge_wait_scheduled.extend(self._merge_wait_queue)
+ del self._merge_wait_queue[:]
+
+ self._schedule_tasks_imp()
+ self._status_display.display()
+
+ state_change = 0
+ for q in self._task_queues.values():
+ if q.schedule():
+ state_change += 1
+
+ # Cancel prefetchers if they're the only reason
+ # the main poll loop is still running.
+ if self._failed_pkgs and not self._build_opts.fetchonly and \
+ not (self._jobs or self._task_queues.merge) and \
+ self._task_queues.fetch:
+ self._task_queues.fetch.clear()
+ state_change += 1
+
+ if state_change:
+ self._schedule_tasks_imp()
+ self._status_display.display()
+
+ return self._keep_scheduling()
+
+ def _job_delay(self):
+ """
+ @rtype: bool
+ @returns: True if job scheduling should be delayed, False otherwise.
+ """
+
+ if self._jobs and self._max_load is not None:
+
+ current_time = time.time()
+
+ delay = self._job_delay_factor * self._jobs ** self._job_delay_exp
+ if delay > self._job_delay_max:
+ delay = self._job_delay_max
+ if (current_time - self._previous_job_start_time) < delay:
+ return True
+
+ return False
+
+ def _schedule_tasks_imp(self):
+ """
+ @rtype: bool
+ @returns: True if state changed, False otherwise.
+ """
+
+ state_change = 0
+
+ while True:
+
+ if not self._keep_scheduling():
+ return bool(state_change)
+
+ if self._choose_pkg_return_early or \
+ self._merge_wait_scheduled or \
+ (self._jobs and self._unsatisfied_system_deps) or \
+ not self._can_add_job() or \
+ self._job_delay():
+ return bool(state_change)
+
+ pkg = self._choose_pkg()
+ if pkg is None:
+ return bool(state_change)
+
+ state_change += 1
+
+ if not pkg.installed:
+ self._pkg_count.curval += 1
+
+ task = self._task(pkg)
+
+ if pkg.installed:
+ merge = PackageMerge(merge=task)
+ merge.addExitListener(self._merge_exit)
+ self._task_queues.merge.add(merge)
+
+ elif pkg.built:
+ self._jobs += 1
+ self._previous_job_start_time = time.time()
+ self._status_display.running = self._jobs
+ task.addExitListener(self._extract_exit)
+ self._task_queues.jobs.add(task)
+
+ else:
+ self._jobs += 1
+ self._previous_job_start_time = time.time()
+ self._status_display.running = self._jobs
+ task.addExitListener(self._build_exit)
+ self._task_queues.jobs.add(task)
+
+ return bool(state_change)
+
+ def _task(self, pkg):
+
+ pkg_to_replace = None
+ if pkg.operation != "uninstall":
+ vardb = pkg.root_config.trees["vartree"].dbapi
+ previous_cpv = vardb.match(pkg.slot_atom)
+ if previous_cpv:
+ previous_cpv = previous_cpv.pop()
+ pkg_to_replace = self._pkg(previous_cpv,
+ "installed", pkg.root_config, installed=True)
+
+ task = MergeListItem(args_set=self._args_set,
+ background=self._background, binpkg_opts=self._binpkg_opts,
+ build_opts=self._build_opts,
+ config_pool=self._ConfigPool(pkg.root,
+ self._allocate_config, self._deallocate_config),
+ emerge_opts=self.myopts,
+ find_blockers=self._find_blockers(pkg), logger=self._logger,
+ mtimedb=self._mtimedb, pkg=pkg, pkg_count=self._pkg_count.copy(),
+ pkg_to_replace=pkg_to_replace,
+ prefetcher=self._prefetchers.get(pkg),
+ scheduler=self._sched_iface,
+ settings=self._allocate_config(pkg.root),
+ statusMessage=self._status_msg,
+ world_atom=self._world_atom)
+
+ return task
+
+ def _failed_pkg_msg(self, failed_pkg, action, preposition):
+ pkg = failed_pkg.pkg
+ msg = "%s to %s %s" % \
+ (bad("Failed"), action, colorize("INFORM", pkg.cpv))
+ if pkg.root != "/":
+ msg += " %s %s" % (preposition, pkg.root)
+
+ log_path = self._locate_failure_log(failed_pkg)
+ if log_path is not None:
+ msg += ", Log file:"
+ self._status_msg(msg)
+
+ if log_path is not None:
+ self._status_msg(" '%s'" % (colorize("INFORM", log_path),))
+
+ def _status_msg(self, msg):
+ """
+ Display a brief status message (no newlines) in the status display.
+ This is called by tasks to provide feedback to the user. This
+ delegates the resposibility of generating \r and \n control characters,
+ to guarantee that lines are created or erased when necessary and
+ appropriate.
+
+ @type msg: str
+ @param msg: a brief status message (no newlines allowed)
+ """
+ if not self._background:
+ writemsg_level("\n")
+ self._status_display.displayMessage(msg)
+
+ def _save_resume_list(self):
+ """
+ Do this before verifying the ebuild Manifests since it might
+ be possible for the user to use --resume --skipfirst get past
+ a non-essential package with a broken digest.
+ """
+ mtimedb = self._mtimedb
+ mtimedb["resume"]["mergelist"] = [list(x) \
+ for x in self._mergelist \
+ if isinstance(x, Package) and x.operation == "merge"]
+
+ mtimedb.commit()
+
+ def _calc_resume_list(self):
+ """
+ Use the current resume list to calculate a new one,
+ dropping any packages with unsatisfied deps.
+ @rtype: bool
+ @returns: True if successful, False otherwise.
+ """
+ print colorize("GOOD", "*** Resuming merge...")
+
+ if self._show_list():
+ if "--tree" in self.myopts:
+ portage.writemsg_stdout("\n" + \
+ darkgreen("These are the packages that " + \
+ "would be merged, in reverse order:\n\n"))
+
+ else:
+ portage.writemsg_stdout("\n" + \
+ darkgreen("These are the packages that " + \
+ "would be merged, in order:\n\n"))
+
+ show_spinner = "--quiet" not in self.myopts and \
+ "--nodeps" not in self.myopts
+
+ if show_spinner:
+ print "Calculating dependencies ",
+
+ myparams = create_depgraph_params(self.myopts, None)
+ success = False
+ e = None
+ try:
+ success, mydepgraph, dropped_tasks = resume_depgraph(
+ self.settings, self.trees, self._mtimedb, self.myopts,
+ myparams, self._spinner)
+ except depgraph.UnsatisfiedResumeDep, exc:
+ # rename variable to avoid python-3.0 error:
+ # SyntaxError: can not delete variable 'e' referenced in nested
+ # scope
+ e = exc
+ mydepgraph = e.depgraph
+ dropped_tasks = set()
+
+ if show_spinner:
+ print "\b\b... done!"
+
+ if e is not None:
+ def unsatisfied_resume_dep_msg():
+ mydepgraph.display_problems()
+ out = portage.output.EOutput()
+ out.eerror("One or more packages are either masked or " + \
+ "have missing dependencies:")
+ out.eerror("")
+ indent = " "
+ show_parents = set()
+ for dep in e.value:
+ if dep.parent in show_parents:
+ continue
+ show_parents.add(dep.parent)
+ if dep.atom is None:
+ out.eerror(indent + "Masked package:")
+ out.eerror(2 * indent + str(dep.parent))
+ out.eerror("")
+ else:
+ out.eerror(indent + str(dep.atom) + " pulled in by:")
+ out.eerror(2 * indent + str(dep.parent))
+ out.eerror("")
+ msg = "The resume list contains packages " + \
+ "that are either masked or have " + \
+ "unsatisfied dependencies. " + \
+ "Please restart/continue " + \
+ "the operation manually, or use --skipfirst " + \
+ "to skip the first package in the list and " + \
+ "any other packages that may be " + \
+ "masked or have missing dependencies."
+ for line in textwrap.wrap(msg, 72):
+ out.eerror(line)
+ self._post_mod_echo_msgs.append(unsatisfied_resume_dep_msg)
+ return False
+
+ if success and self._show_list():
+ mylist = mydepgraph.altlist()
+ if mylist:
+ if "--tree" in self.myopts:
+ mylist.reverse()
+ mydepgraph.display(mylist, favorites=self._favorites)
+
+ if not success:
+ self._post_mod_echo_msgs.append(mydepgraph.display_problems)
+ return False
+ mydepgraph.display_problems()
+
+ mylist = mydepgraph.altlist()
+ mydepgraph.break_refs(mylist)
+ mydepgraph.break_refs(dropped_tasks)
+ self._mergelist = mylist
+ self._set_digraph(mydepgraph.schedulerGraph())
+
+ msg_width = 75
+ for task in dropped_tasks:
+ if not (isinstance(task, Package) and task.operation == "merge"):
+ continue
+ pkg = task
+ msg = "emerge --keep-going:" + \
+ " %s" % (pkg.cpv,)
+ if pkg.root != "/":
+ msg += " for %s" % (pkg.root,)
+ msg += " dropped due to unsatisfied dependency."
+ for line in textwrap.wrap(msg, msg_width):
+ eerror(line, phase="other", key=pkg.cpv)
+ settings = self.pkgsettings[pkg.root]
+ # Ensure that log collection from $T is disabled inside
+ # elog_process(), since any logs that might exist are
+ # not valid here.
+ settings.pop("T", None)
+ portage.elog.elog_process(pkg.cpv, settings)
+ self._failed_pkgs_all.append(self._failed_pkg(pkg=pkg))
+
+ return True
+
+ def _show_list(self):
+ myopts = self.myopts
+ if "--quiet" not in myopts and \
+ ("--ask" in myopts or "--tree" in myopts or \
+ "--verbose" in myopts):
+ return True
+ return False
+
+ def _world_atom(self, pkg):
+ """
+ Add the package to the world file, but only if
+ it's supposed to be added. Otherwise, do nothing.
+ """
+
+ if set(("--buildpkgonly", "--fetchonly",
+ "--fetch-all-uri",
+ "--oneshot", "--onlydeps",
+ "--pretend")).intersection(self.myopts):
+ return
+
+ if pkg.root != self.target_root:
+ return
+
+ args_set = self._args_set
+ if not args_set.findAtomForPackage(pkg):
+ return
+
+ logger = self._logger
+ pkg_count = self._pkg_count
+ root_config = pkg.root_config
+ world_set = root_config.sets["world"]
+ world_locked = False
+ if hasattr(world_set, "lock"):
+ world_set.lock()
+ world_locked = True
+
+ try:
+ if hasattr(world_set, "load"):
+ world_set.load() # maybe it's changed on disk
+
+ atom = create_world_atom(pkg, args_set, root_config)
+ if atom:
+ if hasattr(world_set, "add"):
+ self._status_msg(('Recording %s in "world" ' + \
+ 'favorites file...') % atom)
+ logger.log(" === (%s of %s) Updating world file (%s)" % \
+ (pkg_count.curval, pkg_count.maxval, pkg.cpv))
+ world_set.add(atom)
+ else:
+ writemsg_level('\n!!! Unable to record %s in "world"\n' % \
+ (atom,), level=logging.WARN, noiselevel=-1)
+ finally:
+ if world_locked:
+ world_set.unlock()
+
+ def _pkg(self, cpv, type_name, root_config, installed=False):
+ """
+ Get a package instance from the cache, or create a new
+ one if necessary. Raises KeyError from aux_get if it
+ failures for some reason (package does not exist or is
+ corrupt).
+ """
+ operation = "merge"
+ if installed:
+ operation = "nomerge"
+
+ if self._digraph is not None:
+ # Reuse existing instance when available.
+ pkg = self._digraph.get(
+ (type_name, root_config.root, cpv, operation))
+ if pkg is not None:
+ return pkg
+
+ tree_type = depgraph.pkg_tree_map[type_name]
+ db = root_config.trees[tree_type].dbapi
+ db_keys = list(self.trees[root_config.root][
+ tree_type].dbapi._aux_cache_keys)
+ metadata = izip(db_keys, db.aux_get(cpv, db_keys))
+ pkg = Package(cpv=cpv, metadata=metadata,
+ root_config=root_config, installed=installed)
+ if type_name == "ebuild":
+ settings = self.pkgsettings[root_config.root]
+ settings.setcpv(pkg)
+ pkg.metadata["USE"] = settings["PORTAGE_USE"]
+ pkg.metadata['CHOST'] = settings.get('CHOST', '')
+
+ return pkg
diff --git a/pym/_emerge/__init__.py b/pym/_emerge/__init__.py
index f0b994a4c..47c20a2f3 100644
--- a/pym/_emerge/__init__.py
+++ b/pym/_emerge/__init__.py
@@ -9,8 +9,6 @@ import shlex
import signal
import sys
import textwrap
-import weakref
-import gc
import os, stat
import platform
@@ -23,13 +21,11 @@ except ImportError:
from portage import digraph
from portage.const import NEWS_LIB_PATH
-from portage.cache.mappings import slot_dict_class
import _emerge.help
import portage.xpak, commands, errno, re, socket, time
-from portage.output import blue, bold, colorize, darkblue, darkgreen, green, \
- nc_len, red, teal, turquoise, \
- xtermTitleReset, yellow
+from portage.output import blue, bold, colorize, darkgreen, \
+ red, xtermTitleReset, yellow
from portage.output import create_color_func
good = create_color_func("GOOD")
bad = create_color_func("BAD")
@@ -42,7 +38,6 @@ import portage.locks
import portage.exception
from portage.cache.cache_errors import CacheError
from portage.data import secpass
-from portage.elog.messages import eerror
from portage.util import normalize_path as normpath
from portage.util import cmp_sort_key, writemsg, writemsg_level
from portage.sets import load_default_config, SETPREFIX
@@ -50,46 +45,29 @@ from portage.sets.base import InternalPackageSet
from itertools import chain, izip
-from _emerge.SlotObject import SlotObject
-from _emerge.DepPriority import DepPriority
-from _emerge.BlockerDepPriority import BlockerDepPriority
-from _emerge.UnmergeDepPriority import UnmergeDepPriority
-from _emerge.DepPriorityNormalRange import DepPriorityNormalRange
+from _emerge.clear_caches import clear_caches
+from _emerge.countdown import countdown
+from _emerge.create_depgraph_params import create_depgraph_params
+from _emerge.Dependency import Dependency
+from _emerge.depgraph import depgraph, resume_depgraph
from _emerge.DepPrioritySatisfiedRange import DepPrioritySatisfiedRange
+from _emerge.emergelog import emergelog
+from _emerge._flush_elog_mod_echo import _flush_elog_mod_echo
+from _emerge.is_valid_package_atom import is_valid_package_atom
+from _emerge.MetadataRegen import MetadataRegen
from _emerge.Package import Package
-from _emerge.Blocker import Blocker
-from _emerge.BlockerDB import BlockerDB
-from _emerge.EbuildFetcher import EbuildFetcher
-from _emerge.EbuildPhase import EbuildPhase
-from _emerge.BinpkgPrefetcher import BinpkgPrefetcher
-from _emerge.PackageMerge import PackageMerge
-from _emerge.DependencyArg import DependencyArg
-from _emerge.AtomArg import AtomArg
-from _emerge.PackageArg import PackageArg
-from _emerge.SetArg import SetArg
-from _emerge.Dependency import Dependency
-from _emerge.BlockerCache import BlockerCache
-from _emerge.PackageVirtualDbapi import PackageVirtualDbapi
-from _emerge.RepoDisplay import RepoDisplay
-from _emerge.UseFlagDisplay import UseFlagDisplay
-from _emerge.SequentialTaskQueue import SequentialTaskQueue
from _emerge.ProgressHandler import ProgressHandler
-from _emerge.stdout_spinner import stdout_spinner
-from _emerge.JobStatusDisplay import JobStatusDisplay
-from _emerge.PollScheduler import PollScheduler
-from _emerge.search import search
-from _emerge.visible import visible
-from _emerge.emergelog import emergelog, _emerge_log_dir
-from _emerge.userquery import userquery
-from _emerge.countdown import countdown
-from _emerge.unmerge import unmerge
-from _emerge.MergeListItem import MergeListItem
-from _emerge.MetadataRegen import MetadataRegen
from _emerge.RootConfig import RootConfig
-from _emerge.format_size import format_size
-from _emerge.PackageCounters import PackageCounters
-from _emerge.FakeVartree import FakeVartree
+from _emerge.Scheduler import Scheduler
+from _emerge.search import search
+from _emerge.SetArg import SetArg
from _emerge.show_invalid_depstring_notice import show_invalid_depstring_notice
+from _emerge.stdout_spinner import stdout_spinner
+from _emerge.unmerge import unmerge
+from _emerge.UnmergeDepPriority import UnmergeDepPriority
+from _emerge.UseFlagDisplay import UseFlagDisplay
+from _emerge.userquery import userquery
+
actions = frozenset([
"clean", "config", "depclean",
@@ -208,6547 +186,6 @@ def getportageversion(portdir, target_root, profile, chost, vardb):
return "Portage " + portage.VERSION +" ("+profilever+", "+gccver+", "+libcver+", "+unameout+")"
-def create_depgraph_params(myopts, myaction):
- #configure emerge engine parameters
- #
- # self: include _this_ package regardless of if it is merged.
- # selective: exclude the package if it is merged
- # recurse: go into the dependencies
- # deep: go into the dependencies of already merged packages
- # empty: pretend nothing is merged
- # complete: completely account for all known dependencies
- # remove: build graph for use in removing packages
- myparams = set(["recurse"])
-
- if myaction == "remove":
- myparams.add("remove")
- myparams.add("complete")
- return myparams
-
- if "--update" in myopts or \
- "--newuse" in myopts or \
- "--reinstall" in myopts or \
- "--noreplace" in myopts:
- myparams.add("selective")
- if "--emptytree" in myopts:
- myparams.add("empty")
- myparams.discard("selective")
- if "--nodeps" in myopts:
- myparams.discard("recurse")
- if "--deep" in myopts:
- myparams.add("deep")
- if "--complete-graph" in myopts:
- myparams.add("complete")
- return myparams
-
-def create_world_atom(pkg, args_set, root_config):
- """Create a new atom for the world file if one does not exist. If the
- argument atom is precise enough to identify a specific slot then a slot
- atom will be returned. Atoms that are in the system set may also be stored
- in world since system atoms can only match one slot while world atoms can
- be greedy with respect to slots. Unslotted system packages will not be
- stored in world."""
-
- arg_atom = args_set.findAtomForPackage(pkg)
- if not arg_atom:
- return None
- cp = portage.dep_getkey(arg_atom)
- new_world_atom = cp
- sets = root_config.sets
- portdb = root_config.trees["porttree"].dbapi
- vardb = root_config.trees["vartree"].dbapi
- available_slots = set(portdb.aux_get(cpv, ["SLOT"])[0] \
- for cpv in portdb.match(cp))
- slotted = len(available_slots) > 1 or \
- (len(available_slots) == 1 and "0" not in available_slots)
- if not slotted:
- # check the vdb in case this is multislot
- available_slots = set(vardb.aux_get(cpv, ["SLOT"])[0] \
- for cpv in vardb.match(cp))
- slotted = len(available_slots) > 1 or \
- (len(available_slots) == 1 and "0" not in available_slots)
- if slotted and arg_atom != cp:
- # If the user gave a specific atom, store it as a
- # slot atom in the world file.
- slot_atom = pkg.slot_atom
-
- # For USE=multislot, there are a couple of cases to
- # handle here:
- #
- # 1) SLOT="0", but the real SLOT spontaneously changed to some
- # unknown value, so just record an unslotted atom.
- #
- # 2) SLOT comes from an installed package and there is no
- # matching SLOT in the portage tree.
- #
- # Make sure that the slot atom is available in either the
- # portdb or the vardb, since otherwise the user certainly
- # doesn't want the SLOT atom recorded in the world file
- # (case 1 above). If it's only available in the vardb,
- # the user may be trying to prevent a USE=multislot
- # package from being removed by --depclean (case 2 above).
-
- mydb = portdb
- if not portdb.match(slot_atom):
- # SLOT seems to come from an installed multislot package
- mydb = vardb
- # If there is no installed package matching the SLOT atom,
- # it probably changed SLOT spontaneously due to USE=multislot,
- # so just record an unslotted atom.
- if vardb.match(slot_atom):
- # Now verify that the argument is precise
- # enough to identify a specific slot.
- matches = mydb.match(arg_atom)
- matched_slots = set()
- for cpv in matches:
- matched_slots.add(mydb.aux_get(cpv, ["SLOT"])[0])
- if len(matched_slots) == 1:
- new_world_atom = slot_atom
-
- if new_world_atom == sets["world"].findAtomForPackage(pkg):
- # Both atoms would be identical, so there's nothing to add.
- return None
- if not slotted:
- # Unlike world atoms, system atoms are not greedy for slots, so they
- # can't be safely excluded from world if they are slotted.
- system_atom = sets["system"].findAtomForPackage(pkg)
- if system_atom:
- if not portage.dep_getkey(system_atom).startswith("virtual/"):
- return None
- # System virtuals aren't safe to exclude from world since they can
- # match multiple old-style virtuals but only one of them will be
- # pulled in by update or depclean.
- providers = portdb.mysettings.getvirtuals().get(
- portage.dep_getkey(system_atom))
- if providers and len(providers) == 1 and providers[0] == cp:
- return None
- return new_world_atom
-
-def filter_iuse_defaults(iuse):
- for flag in iuse:
- if flag.startswith("+") or flag.startswith("-"):
- yield flag[1:]
- else:
- yield flag
-
-def _find_deep_system_runtime_deps(graph):
- deep_system_deps = set()
- node_stack = []
- for node in graph:
- if not isinstance(node, Package) or \
- node.operation == 'uninstall':
- continue
- if node.root_config.sets['system'].findAtomForPackage(node):
- node_stack.append(node)
-
- def ignore_priority(priority):
- """
- Ignore non-runtime priorities.
- """
- if isinstance(priority, DepPriority) and \
- (priority.runtime or priority.runtime_post):
- return False
- return True
-
- while node_stack:
- node = node_stack.pop()
- if node in deep_system_deps:
- continue
- deep_system_deps.add(node)
- for child in graph.child_nodes(node, ignore_priority=ignore_priority):
- if not isinstance(child, Package) or \
- child.operation == 'uninstall':
- continue
- node_stack.append(child)
-
- return deep_system_deps
-
-def get_masking_status(pkg, pkgsettings, root_config):
-
- mreasons = portage.getmaskingstatus(
- pkg, settings=pkgsettings,
- portdb=root_config.trees["porttree"].dbapi)
-
- if not pkg.installed:
- if not pkgsettings._accept_chost(pkg.cpv, pkg.metadata):
- mreasons.append("CHOST: %s" % \
- pkg.metadata["CHOST"])
-
- if not pkg.metadata["SLOT"]:
- mreasons.append("invalid: SLOT is undefined")
-
- return mreasons
-
-def get_mask_info(root_config, cpv, pkgsettings,
- db, pkg_type, built, installed, db_keys):
- eapi_masked = False
- try:
- metadata = dict(izip(db_keys,
- db.aux_get(cpv, db_keys)))
- except KeyError:
- metadata = None
- if metadata and not built:
- pkgsettings.setcpv(cpv, mydb=metadata)
- metadata["USE"] = pkgsettings["PORTAGE_USE"]
- metadata['CHOST'] = pkgsettings.get('CHOST', '')
- if metadata is None:
- mreasons = ["corruption"]
- else:
- eapi = metadata['EAPI']
- if eapi[:1] == '-':
- eapi = eapi[1:]
- if not portage.eapi_is_supported(eapi):
- mreasons = ['EAPI %s' % eapi]
- else:
- pkg = Package(type_name=pkg_type, root_config=root_config,
- cpv=cpv, built=built, installed=installed, metadata=metadata)
- mreasons = get_masking_status(pkg, pkgsettings, root_config)
- return metadata, mreasons
-
-def show_masked_packages(masked_packages):
- shown_licenses = set()
- shown_comments = set()
- # Maybe there is both an ebuild and a binary. Only
- # show one of them to avoid redundant appearance.
- shown_cpvs = set()
- have_eapi_mask = False
- for (root_config, pkgsettings, cpv,
- metadata, mreasons) in masked_packages:
- if cpv in shown_cpvs:
- continue
- shown_cpvs.add(cpv)
- comment, filename = None, None
- if "package.mask" in mreasons:
- comment, filename = \
- portage.getmaskingreason(
- cpv, metadata=metadata,
- settings=pkgsettings,
- portdb=root_config.trees["porttree"].dbapi,
- return_location=True)
- missing_licenses = []
- if metadata:
- if not portage.eapi_is_supported(metadata["EAPI"]):
- have_eapi_mask = True
- try:
- missing_licenses = \
- pkgsettings._getMissingLicenses(
- cpv, metadata)
- except portage.exception.InvalidDependString:
- # This will have already been reported
- # above via mreasons.
- pass
-
- print "- "+cpv+" (masked by: "+", ".join(mreasons)+")"
- if comment and comment not in shown_comments:
- print filename+":"
- print comment
- shown_comments.add(comment)
- portdb = root_config.trees["porttree"].dbapi
- for l in missing_licenses:
- l_path = portdb.findLicensePath(l)
- if l in shown_licenses:
- continue
- msg = ("A copy of the '%s' license" + \
- " is located at '%s'.") % (l, l_path)
- print msg
- print
- shown_licenses.add(l)
- return have_eapi_mask
-
-class depgraph(object):
-
- pkg_tree_map = RootConfig.pkg_tree_map
-
- _dep_keys = ["DEPEND", "RDEPEND", "PDEPEND"]
-
- def __init__(self, settings, trees, myopts, myparams, spinner):
- self.settings = settings
- self.target_root = settings["ROOT"]
- self.myopts = myopts
- self.myparams = myparams
- self.edebug = 0
- if settings.get("PORTAGE_DEBUG", "") == "1":
- self.edebug = 1
- self.spinner = spinner
- self._running_root = trees["/"]["root_config"]
- self._opts_no_restart = Scheduler._opts_no_restart
- self.pkgsettings = {}
- # Maps slot atom to package for each Package added to the graph.
- self._slot_pkg_map = {}
- # Maps nodes to the reasons they were selected for reinstallation.
- self._reinstall_nodes = {}
- self.mydbapi = {}
- self.trees = {}
- self._trees_orig = trees
- self.roots = {}
- # Contains a filtered view of preferred packages that are selected
- # from available repositories.
- self._filtered_trees = {}
- # Contains installed packages and new packages that have been added
- # to the graph.
- self._graph_trees = {}
- # All Package instances
- self._pkg_cache = {}
- for myroot in trees:
- self.trees[myroot] = {}
- # Create a RootConfig instance that references
- # the FakeVartree instead of the real one.
- self.roots[myroot] = RootConfig(
- trees[myroot]["vartree"].settings,
- self.trees[myroot],
- trees[myroot]["root_config"].setconfig)
- for tree in ("porttree", "bintree"):
- self.trees[myroot][tree] = trees[myroot][tree]
- self.trees[myroot]["vartree"] = \
- FakeVartree(trees[myroot]["root_config"],
- pkg_cache=self._pkg_cache)
- self.pkgsettings[myroot] = portage.config(
- clone=self.trees[myroot]["vartree"].settings)
- self._slot_pkg_map[myroot] = {}
- vardb = self.trees[myroot]["vartree"].dbapi
- preload_installed_pkgs = "--nodeps" not in self.myopts and \
- "--buildpkgonly" not in self.myopts
- # This fakedbapi instance will model the state that the vdb will
- # have after new packages have been installed.
- fakedb = PackageVirtualDbapi(vardb.settings)
- if preload_installed_pkgs:
- for pkg in vardb:
- self.spinner.update()
- # This triggers metadata updates via FakeVartree.
- vardb.aux_get(pkg.cpv, [])
- fakedb.cpv_inject(pkg)
-
- # Now that the vardb state is cached in our FakeVartree,
- # we won't be needing the real vartree cache for awhile.
- # To make some room on the heap, clear the vardbapi
- # caches.
- trees[myroot]["vartree"].dbapi._clear_cache()
- gc.collect()
-
- self.mydbapi[myroot] = fakedb
- def graph_tree():
- pass
- graph_tree.dbapi = fakedb
- self._graph_trees[myroot] = {}
- self._filtered_trees[myroot] = {}
- # Substitute the graph tree for the vartree in dep_check() since we
- # want atom selections to be consistent with package selections
- # have already been made.
- self._graph_trees[myroot]["porttree"] = graph_tree
- self._graph_trees[myroot]["vartree"] = graph_tree
- def filtered_tree():
- pass
- filtered_tree.dbapi = self._dep_check_composite_db(self, myroot)
- self._filtered_trees[myroot]["porttree"] = filtered_tree
-
- # Passing in graph_tree as the vartree here could lead to better
- # atom selections in some cases by causing atoms for packages that
- # have been added to the graph to be preferred over other choices.
- # However, it can trigger atom selections that result in
- # unresolvable direct circular dependencies. For example, this
- # happens with gwydion-dylan which depends on either itself or
- # gwydion-dylan-bin. In case gwydion-dylan is not yet installed,
- # gwydion-dylan-bin needs to be selected in order to avoid a
- # an unresolvable direct circular dependency.
- #
- # To solve the problem described above, pass in "graph_db" so that
- # packages that have been added to the graph are distinguishable
- # from other available packages and installed packages. Also, pass
- # the parent package into self._select_atoms() calls so that
- # unresolvable direct circular dependencies can be detected and
- # avoided when possible.
- self._filtered_trees[myroot]["graph_db"] = graph_tree.dbapi
- self._filtered_trees[myroot]["vartree"] = self.trees[myroot]["vartree"]
-
- dbs = []
- portdb = self.trees[myroot]["porttree"].dbapi
- bindb = self.trees[myroot]["bintree"].dbapi
- vardb = self.trees[myroot]["vartree"].dbapi
- # (db, pkg_type, built, installed, db_keys)
- if "--usepkgonly" not in self.myopts:
- db_keys = list(portdb._aux_cache_keys)
- dbs.append((portdb, "ebuild", False, False, db_keys))
- if "--usepkg" in self.myopts:
- db_keys = list(bindb._aux_cache_keys)
- dbs.append((bindb, "binary", True, False, db_keys))
- db_keys = list(trees[myroot]["vartree"].dbapi._aux_cache_keys)
- dbs.append((vardb, "installed", True, True, db_keys))
- self._filtered_trees[myroot]["dbs"] = dbs
- if "--usepkg" in self.myopts:
- self.trees[myroot]["bintree"].populate(
- "--getbinpkg" in self.myopts,
- "--getbinpkgonly" in self.myopts)
- del trees
-
- self.digraph=portage.digraph()
- # contains all sets added to the graph
- self._sets = {}
- # contains atoms given as arguments
- self._sets["args"] = InternalPackageSet()
- # contains all atoms from all sets added to the graph, including
- # atoms given as arguments
- self._set_atoms = InternalPackageSet()
- self._atom_arg_map = {}
- # contains all nodes pulled in by self._set_atoms
- self._set_nodes = set()
- # Contains only Blocker -> Uninstall edges
- self._blocker_uninstalls = digraph()
- # Contains only Package -> Blocker edges
- self._blocker_parents = digraph()
- # Contains only irrelevant Package -> Blocker edges
- self._irrelevant_blockers = digraph()
- # Contains only unsolvable Package -> Blocker edges
- self._unsolvable_blockers = digraph()
- # Contains all Blocker -> Blocked Package edges
- self._blocked_pkgs = digraph()
- # Contains world packages that have been protected from
- # uninstallation but may not have been added to the graph
- # if the graph is not complete yet.
- self._blocked_world_pkgs = {}
- self._slot_collision_info = {}
- # Slot collision nodes are not allowed to block other packages since
- # blocker validation is only able to account for one package per slot.
- self._slot_collision_nodes = set()
- self._parent_atoms = {}
- self._slot_conflict_parent_atoms = set()
- self._serialized_tasks_cache = None
- self._scheduler_graph = None
- self._displayed_list = None
- self._pprovided_args = []
- self._missing_args = []
- self._masked_installed = set()
- self._unsatisfied_deps_for_display = []
- self._unsatisfied_blockers_for_display = None
- self._circular_deps_for_display = None
- self._dep_stack = []
- self._dep_disjunctive_stack = []
- self._unsatisfied_deps = []
- self._initially_unsatisfied_deps = []
- self._ignored_deps = []
- self._required_set_names = set(["system", "world"])
- self._select_atoms = self._select_atoms_highest_available
- self._select_package = self._select_pkg_highest_available
- self._highest_pkg_cache = {}
-
- def _show_slot_collision_notice(self):
- """Show an informational message advising the user to mask one of the
- the packages. In some cases it may be possible to resolve this
- automatically, but support for backtracking (removal nodes that have
- already been selected) will be required in order to handle all possible
- cases.
- """
-
- if not self._slot_collision_info:
- return
-
- self._show_merge_list()
-
- msg = []
- msg.append("\n!!! Multiple package instances within a single " + \
- "package slot have been pulled\n")
- msg.append("!!! into the dependency graph, resulting" + \
- " in a slot conflict:\n\n")
- indent = " "
- # Max number of parents shown, to avoid flooding the display.
- max_parents = 3
- explanation_columns = 70
- explanations = 0
- for (slot_atom, root), slot_nodes \
- in self._slot_collision_info.iteritems():
- msg.append(str(slot_atom))
- msg.append("\n\n")
-
- for node in slot_nodes:
- msg.append(indent)
- msg.append(str(node))
- parent_atoms = self._parent_atoms.get(node)
- if parent_atoms:
- pruned_list = set()
- # Prefer conflict atoms over others.
- for parent_atom in parent_atoms:
- if len(pruned_list) >= max_parents:
- break
- if parent_atom in self._slot_conflict_parent_atoms:
- pruned_list.add(parent_atom)
-
- # If this package was pulled in by conflict atoms then
- # show those alone since those are the most interesting.
- if not pruned_list:
- # When generating the pruned list, prefer instances
- # of DependencyArg over instances of Package.
- for parent_atom in parent_atoms:
- if len(pruned_list) >= max_parents:
- break
- parent, atom = parent_atom
- if isinstance(parent, DependencyArg):
- pruned_list.add(parent_atom)
- # Prefer Packages instances that themselves have been
- # pulled into collision slots.
- for parent_atom in parent_atoms:
- if len(pruned_list) >= max_parents:
- break
- parent, atom = parent_atom
- if isinstance(parent, Package) and \
- (parent.slot_atom, parent.root) \
- in self._slot_collision_info:
- pruned_list.add(parent_atom)
- for parent_atom in parent_atoms:
- if len(pruned_list) >= max_parents:
- break
- pruned_list.add(parent_atom)
- omitted_parents = len(parent_atoms) - len(pruned_list)
- parent_atoms = pruned_list
- msg.append(" pulled in by\n")
- for parent_atom in parent_atoms:
- parent, atom = parent_atom
- msg.append(2*indent)
- if isinstance(parent,
- (PackageArg, AtomArg)):
- # For PackageArg and AtomArg types, it's
- # redundant to display the atom attribute.
- msg.append(str(parent))
- else:
- # Display the specific atom from SetArg or
- # Package types.
- msg.append("%s required by %s" % (atom, parent))
- msg.append("\n")
- if omitted_parents:
- msg.append(2*indent)
- msg.append("(and %d more)\n" % omitted_parents)
- else:
- msg.append(" (no parents)\n")
- msg.append("\n")
- explanation = self._slot_conflict_explanation(slot_nodes)
- if explanation:
- explanations += 1
- msg.append(indent + "Explanation:\n\n")
- for line in textwrap.wrap(explanation, explanation_columns):
- msg.append(2*indent + line + "\n")
- msg.append("\n")
- msg.append("\n")
- sys.stderr.write("".join(msg))
- sys.stderr.flush()
-
- explanations_for_all = explanations == len(self._slot_collision_info)
-
- if explanations_for_all or "--quiet" in self.myopts:
- return
-
- msg = []
- msg.append("It may be possible to solve this problem ")
- msg.append("by using package.mask to prevent one of ")
- msg.append("those packages from being selected. ")
- msg.append("However, it is also possible that conflicting ")
- msg.append("dependencies exist such that they are impossible to ")
- msg.append("satisfy simultaneously. If such a conflict exists in ")
- msg.append("the dependencies of two different packages, then those ")
- msg.append("packages can not be installed simultaneously.")
-
- from formatter import AbstractFormatter, DumbWriter
- f = AbstractFormatter(DumbWriter(sys.stderr, maxcol=72))
- for x in msg:
- f.add_flowing_data(x)
- f.end_paragraph(1)
-
- msg = []
- msg.append("For more information, see MASKED PACKAGES ")
- msg.append("section in the emerge man page or refer ")
- msg.append("to the Gentoo Handbook.")
- for x in msg:
- f.add_flowing_data(x)
- f.end_paragraph(1)
- f.writer.flush()
-
- def _slot_conflict_explanation(self, slot_nodes):
- """
- When a slot conflict occurs due to USE deps, there are a few
- different cases to consider:
-
- 1) New USE are correctly set but --newuse wasn't requested so an
- installed package with incorrect USE happened to get pulled
- into graph before the new one.
-
- 2) New USE are incorrectly set but an installed package has correct
- USE so it got pulled into the graph, and a new instance also got
- pulled in due to --newuse or an upgrade.
-
- 3) Multiple USE deps exist that can't be satisfied simultaneously,
- and multiple package instances got pulled into the same slot to
- satisfy the conflicting deps.
-
- Currently, explanations and suggested courses of action are generated
- for cases 1 and 2. Case 3 is too complex to give a useful suggestion.
- """
-
- if len(slot_nodes) != 2:
- # Suggestions are only implemented for
- # conflicts between two packages.
- return None
-
- all_conflict_atoms = self._slot_conflict_parent_atoms
- matched_node = None
- matched_atoms = None
- unmatched_node = None
- for node in slot_nodes:
- parent_atoms = self._parent_atoms.get(node)
- if not parent_atoms:
- # Normally, there are always parent atoms. If there are
- # none then something unexpected is happening and there's
- # currently no suggestion for this case.
- return None
- conflict_atoms = all_conflict_atoms.intersection(parent_atoms)
- for parent_atom in conflict_atoms:
- parent, atom = parent_atom
- if not atom.use:
- # Suggestions are currently only implemented for cases
- # in which all conflict atoms have USE deps.
- return None
- if conflict_atoms:
- if matched_node is not None:
- # If conflict atoms match multiple nodes
- # then there's no suggestion.
- return None
- matched_node = node
- matched_atoms = conflict_atoms
- else:
- if unmatched_node is not None:
- # Neither node is matched by conflict atoms, and
- # there is no suggestion for this case.
- return None
- unmatched_node = node
-
- if matched_node is None or unmatched_node is None:
- # This shouldn't happen.
- return None
-
- if unmatched_node.installed and not matched_node.installed and \
- unmatched_node.cpv == matched_node.cpv:
- # If the conflicting packages are the same version then
- # --newuse should be all that's needed. If they are different
- # versions then there's some other problem.
- return "New USE are correctly set, but --newuse wasn't" + \
- " requested, so an installed package with incorrect USE " + \
- "happened to get pulled into the dependency graph. " + \
- "In order to solve " + \
- "this, either specify the --newuse option or explicitly " + \
- " reinstall '%s'." % matched_node.slot_atom
-
- if matched_node.installed and not unmatched_node.installed:
- atoms = sorted(set(atom for parent, atom in matched_atoms))
- explanation = ("New USE for '%s' are incorrectly set. " + \
- "In order to solve this, adjust USE to satisfy '%s'") % \
- (matched_node.slot_atom, atoms[0])
- if len(atoms) > 1:
- for atom in atoms[1:-1]:
- explanation += ", '%s'" % (atom,)
- if len(atoms) > 2:
- explanation += ","
- explanation += " and '%s'" % (atoms[-1],)
- explanation += "."
- return explanation
-
- return None
-
- def _process_slot_conflicts(self):
- """
- Process slot conflict data to identify specific atoms which
- lead to conflict. These atoms only match a subset of the
- packages that have been pulled into a given slot.
- """
- for (slot_atom, root), slot_nodes \
- in self._slot_collision_info.iteritems():
-
- all_parent_atoms = set()
- for pkg in slot_nodes:
- parent_atoms = self._parent_atoms.get(pkg)
- if not parent_atoms:
- continue
- all_parent_atoms.update(parent_atoms)
-
- for pkg in slot_nodes:
- parent_atoms = self._parent_atoms.get(pkg)
- if parent_atoms is None:
- parent_atoms = set()
- self._parent_atoms[pkg] = parent_atoms
- for parent_atom in all_parent_atoms:
- if parent_atom in parent_atoms:
- continue
- # Use package set for matching since it will match via
- # PROVIDE when necessary, while match_from_list does not.
- parent, atom = parent_atom
- atom_set = InternalPackageSet(
- initial_atoms=(atom,))
- if atom_set.findAtomForPackage(pkg):
- parent_atoms.add(parent_atom)
- else:
- self._slot_conflict_parent_atoms.add(parent_atom)
-
- def _reinstall_for_flags(self, forced_flags,
- orig_use, orig_iuse, cur_use, cur_iuse):
- """Return a set of flags that trigger reinstallation, or None if there
- are no such flags."""
- if "--newuse" in self.myopts:
- flags = set(orig_iuse.symmetric_difference(
- cur_iuse).difference(forced_flags))
- flags.update(orig_iuse.intersection(orig_use).symmetric_difference(
- cur_iuse.intersection(cur_use)))
- if flags:
- return flags
- elif "changed-use" == self.myopts.get("--reinstall"):
- flags = orig_iuse.intersection(orig_use).symmetric_difference(
- cur_iuse.intersection(cur_use))
- if flags:
- return flags
- return None
-
- def _create_graph(self, allow_unsatisfied=False):
- dep_stack = self._dep_stack
- dep_disjunctive_stack = self._dep_disjunctive_stack
- while dep_stack or dep_disjunctive_stack:
- self.spinner.update()
- while dep_stack:
- dep = dep_stack.pop()
- if isinstance(dep, Package):
- if not self._add_pkg_deps(dep,
- allow_unsatisfied=allow_unsatisfied):
- return 0
- continue
- if not self._add_dep(dep, allow_unsatisfied=allow_unsatisfied):
- return 0
- if dep_disjunctive_stack:
- if not self._pop_disjunction(allow_unsatisfied):
- return 0
- return 1
-
- def _add_dep(self, dep, allow_unsatisfied=False):
- debug = "--debug" in self.myopts
- buildpkgonly = "--buildpkgonly" in self.myopts
- nodeps = "--nodeps" in self.myopts
- empty = "empty" in self.myparams
- deep = "deep" in self.myparams
- update = "--update" in self.myopts and dep.depth <= 1
- if dep.blocker:
- if not buildpkgonly and \
- not nodeps and \
- dep.parent not in self._slot_collision_nodes:
- if dep.parent.onlydeps:
- # It's safe to ignore blockers if the
- # parent is an --onlydeps node.
- return 1
- # The blocker applies to the root where
- # the parent is or will be installed.
- blocker = Blocker(atom=dep.atom,
- eapi=dep.parent.metadata["EAPI"],
- root=dep.parent.root)
- self._blocker_parents.add(blocker, dep.parent)
- return 1
- dep_pkg, existing_node = self._select_package(dep.root, dep.atom,
- onlydeps=dep.onlydeps)
- if not dep_pkg:
- if dep.priority.optional:
- # This could be an unecessary build-time dep
- # pulled in by --with-bdeps=y.
- return 1
- if allow_unsatisfied:
- self._unsatisfied_deps.append(dep)
- return 1
- self._unsatisfied_deps_for_display.append(
- ((dep.root, dep.atom), {"myparent":dep.parent}))
- return 0
- # In some cases, dep_check will return deps that shouldn't
- # be proccessed any further, so they are identified and
- # discarded here. Try to discard as few as possible since
- # discarded dependencies reduce the amount of information
- # available for optimization of merge order.
- if dep.priority.satisfied and \
- not dep_pkg.installed and \
- not (existing_node or empty or deep or update):
- myarg = None
- if dep.root == self.target_root:
- try:
- myarg = self._iter_atoms_for_pkg(dep_pkg).next()
- except StopIteration:
- pass
- except portage.exception.InvalidDependString:
- if not dep_pkg.installed:
- # This shouldn't happen since the package
- # should have been masked.
- raise
- if not myarg:
- self._ignored_deps.append(dep)
- return 1
-
- if not self._add_pkg(dep_pkg, dep):
- return 0
- return 1
-
- def _add_pkg(self, pkg, dep):
- myparent = None
- priority = None
- depth = 0
- if dep is None:
- dep = Dependency()
- else:
- myparent = dep.parent
- priority = dep.priority
- depth = dep.depth
- if priority is None:
- priority = DepPriority()
- """
- Fills the digraph with nodes comprised of packages to merge.
- mybigkey is the package spec of the package to merge.
- myparent is the package depending on mybigkey ( or None )
- addme = Should we add this package to the digraph or are we just looking at it's deps?
- Think --onlydeps, we need to ignore packages in that case.
- #stuff to add:
- #SLOT-aware emerge
- #IUSE-aware emerge -> USE DEP aware depgraph
- #"no downgrade" emerge
- """
- # Ensure that the dependencies of the same package
- # are never processed more than once.
- previously_added = pkg in self.digraph
-
- # select the correct /var database that we'll be checking against
- vardbapi = self.trees[pkg.root]["vartree"].dbapi
- pkgsettings = self.pkgsettings[pkg.root]
-
- arg_atoms = None
- if True:
- try:
- arg_atoms = list(self._iter_atoms_for_pkg(pkg))
- except portage.exception.InvalidDependString, e:
- if not pkg.installed:
- show_invalid_depstring_notice(
- pkg, pkg.metadata["PROVIDE"], str(e))
- return 0
- del e
-
- if not pkg.onlydeps:
- if not pkg.installed and \
- "empty" not in self.myparams and \
- vardbapi.match(pkg.slot_atom):
- # Increase the priority of dependencies on packages that
- # are being rebuilt. This optimizes merge order so that
- # dependencies are rebuilt/updated as soon as possible,
- # which is needed especially when emerge is called by
- # revdep-rebuild since dependencies may be affected by ABI
- # breakage that has rendered them useless. Don't adjust
- # priority here when in "empty" mode since all packages
- # are being merged in that case.
- priority.rebuild = True
-
- existing_node = self._slot_pkg_map[pkg.root].get(pkg.slot_atom)
- slot_collision = False
- if existing_node:
- existing_node_matches = pkg.cpv == existing_node.cpv
- if existing_node_matches and \
- pkg != existing_node and \
- dep.atom is not None:
- # Use package set for matching since it will match via
- # PROVIDE when necessary, while match_from_list does not.
- atom_set = InternalPackageSet(initial_atoms=[dep.atom])
- if not atom_set.findAtomForPackage(existing_node):
- existing_node_matches = False
- if existing_node_matches:
- # The existing node can be reused.
- if arg_atoms:
- for parent_atom in arg_atoms:
- parent, atom = parent_atom
- self.digraph.add(existing_node, parent,
- priority=priority)
- self._add_parent_atom(existing_node, parent_atom)
- # If a direct circular dependency is not an unsatisfied
- # buildtime dependency then drop it here since otherwise
- # it can skew the merge order calculation in an unwanted
- # way.
- if existing_node != myparent or \
- (priority.buildtime and not priority.satisfied):
- self.digraph.addnode(existing_node, myparent,
- priority=priority)
- if dep.atom is not None and dep.parent is not None:
- self._add_parent_atom(existing_node,
- (dep.parent, dep.atom))
- return 1
- else:
-
- # A slot collision has occurred. Sometimes this coincides
- # with unresolvable blockers, so the slot collision will be
- # shown later if there are no unresolvable blockers.
- self._add_slot_conflict(pkg)
- slot_collision = True
-
- if slot_collision:
- # Now add this node to the graph so that self.display()
- # can show use flags and --tree portage.output. This node is
- # only being partially added to the graph. It must not be
- # allowed to interfere with the other nodes that have been
- # added. Do not overwrite data for existing nodes in
- # self.mydbapi since that data will be used for blocker
- # validation.
- # Even though the graph is now invalid, continue to process
- # dependencies so that things like --fetchonly can still
- # function despite collisions.
- pass
- elif not previously_added:
- self._slot_pkg_map[pkg.root][pkg.slot_atom] = pkg
- self.mydbapi[pkg.root].cpv_inject(pkg)
- self._filtered_trees[pkg.root]["porttree"].dbapi._clear_cache()
-
- if not pkg.installed:
- # Allow this package to satisfy old-style virtuals in case it
- # doesn't already. Any pre-existing providers will be preferred
- # over this one.
- try:
- pkgsettings.setinst(pkg.cpv, pkg.metadata)
- # For consistency, also update the global virtuals.
- settings = self.roots[pkg.root].settings
- settings.unlock()
- settings.setinst(pkg.cpv, pkg.metadata)
- settings.lock()
- except portage.exception.InvalidDependString, e:
- show_invalid_depstring_notice(
- pkg, pkg.metadata["PROVIDE"], str(e))
- del e
- return 0
-
- if arg_atoms:
- self._set_nodes.add(pkg)
-
- # Do this even when addme is False (--onlydeps) so that the
- # parent/child relationship is always known in case
- # self._show_slot_collision_notice() needs to be called later.
- self.digraph.add(pkg, myparent, priority=priority)
- if dep.atom is not None and dep.parent is not None:
- self._add_parent_atom(pkg, (dep.parent, dep.atom))
-
- if arg_atoms:
- for parent_atom in arg_atoms:
- parent, atom = parent_atom
- self.digraph.add(pkg, parent, priority=priority)
- self._add_parent_atom(pkg, parent_atom)
-
- """ This section determines whether we go deeper into dependencies or not.
- We want to go deeper on a few occasions:
- Installing package A, we need to make sure package A's deps are met.
- emerge --deep <pkgspec>; we need to recursively check dependencies of pkgspec
- If we are in --nodeps (no recursion) mode, we obviously only check 1 level of dependencies.
- """
- dep_stack = self._dep_stack
- if "recurse" not in self.myparams:
- return 1
- elif pkg.installed and \
- "deep" not in self.myparams:
- dep_stack = self._ignored_deps
-
- self.spinner.update()
-
- if arg_atoms:
- depth = 0
- pkg.depth = depth
- if not previously_added:
- dep_stack.append(pkg)
- return 1
-
- def _add_parent_atom(self, pkg, parent_atom):
- parent_atoms = self._parent_atoms.get(pkg)
- if parent_atoms is None:
- parent_atoms = set()
- self._parent_atoms[pkg] = parent_atoms
- parent_atoms.add(parent_atom)
-
- def _add_slot_conflict(self, pkg):
- self._slot_collision_nodes.add(pkg)
- slot_key = (pkg.slot_atom, pkg.root)
- slot_nodes = self._slot_collision_info.get(slot_key)
- if slot_nodes is None:
- slot_nodes = set()
- slot_nodes.add(self._slot_pkg_map[pkg.root][pkg.slot_atom])
- self._slot_collision_info[slot_key] = slot_nodes
- slot_nodes.add(pkg)
-
- def _add_pkg_deps(self, pkg, allow_unsatisfied=False):
-
- mytype = pkg.type_name
- myroot = pkg.root
- mykey = pkg.cpv
- metadata = pkg.metadata
- myuse = pkg.use.enabled
- jbigkey = pkg
- depth = pkg.depth + 1
- removal_action = "remove" in self.myparams
-
- edepend={}
- depkeys = ["DEPEND","RDEPEND","PDEPEND"]
- for k in depkeys:
- edepend[k] = metadata[k]
-
- if not pkg.built and \
- "--buildpkgonly" in self.myopts and \
- "deep" not in self.myparams and \
- "empty" not in self.myparams:
- edepend["RDEPEND"] = ""
- edepend["PDEPEND"] = ""
- bdeps_optional = False
-
- if pkg.built and not removal_action:
- if self.myopts.get("--with-bdeps", "n") == "y":
- # Pull in build time deps as requested, but marked them as
- # "optional" since they are not strictly required. This allows
- # more freedom in the merge order calculation for solving
- # circular dependencies. Don't convert to PDEPEND since that
- # could make --with-bdeps=y less effective if it is used to
- # adjust merge order to prevent built_with_use() calls from
- # failing.
- bdeps_optional = True
- else:
- # built packages do not have build time dependencies.
- edepend["DEPEND"] = ""
-
- if removal_action and self.myopts.get("--with-bdeps", "y") == "n":
- edepend["DEPEND"] = ""
-
- bdeps_root = "/"
- root_deps = self.myopts.get("--root-deps")
- if root_deps is not None:
- if root_deps is True:
- bdeps_root = myroot
- elif root_deps == "rdeps":
- edepend["DEPEND"] = ""
-
- deps = (
- (bdeps_root, edepend["DEPEND"],
- self._priority(buildtime=(not bdeps_optional),
- optional=bdeps_optional)),
- (myroot, edepend["RDEPEND"], self._priority(runtime=True)),
- (myroot, edepend["PDEPEND"], self._priority(runtime_post=True))
- )
-
- debug = "--debug" in self.myopts
- strict = mytype != "installed"
- try:
- if not strict:
- portage.dep._dep_check_strict = False
-
- for dep_root, dep_string, dep_priority in deps:
- if not dep_string:
- continue
- if debug:
- print
- print "Parent: ", jbigkey
- print "Depstring:", dep_string
- print "Priority:", dep_priority
-
- try:
-
- dep_string = portage.dep.paren_normalize(
- portage.dep.use_reduce(
- portage.dep.paren_reduce(dep_string),
- uselist=pkg.use.enabled))
-
- dep_string = list(self._queue_disjunctive_deps(
- pkg, dep_root, dep_priority, dep_string))
-
- except portage.exception.InvalidDependString, e:
- if pkg.installed:
- del e
- continue
- show_invalid_depstring_notice(pkg, dep_string, str(e))
- return 0
-
- if not dep_string:
- continue
-
- dep_string = portage.dep.paren_enclose(dep_string)
-
- if not self._add_pkg_dep_string(
- pkg, dep_root, dep_priority, dep_string,
- allow_unsatisfied):
- return 0
-
- except portage.exception.AmbiguousPackageName, e:
- pkgs = e.args[0]
- portage.writemsg("\n\n!!! An atom in the dependencies " + \
- "is not fully-qualified. Multiple matches:\n\n", noiselevel=-1)
- for cpv in pkgs:
- portage.writemsg(" %s\n" % cpv, noiselevel=-1)
- portage.writemsg("\n", noiselevel=-1)
- if mytype == "binary":
- portage.writemsg(
- "!!! This binary package cannot be installed: '%s'\n" % \
- mykey, noiselevel=-1)
- elif mytype == "ebuild":
- portdb = self.roots[myroot].trees["porttree"].dbapi
- myebuild, mylocation = portdb.findname2(mykey)
- portage.writemsg("!!! This ebuild cannot be installed: " + \
- "'%s'\n" % myebuild, noiselevel=-1)
- portage.writemsg("!!! Please notify the package maintainer " + \
- "that atoms must be fully-qualified.\n", noiselevel=-1)
- return 0
- finally:
- portage.dep._dep_check_strict = True
- return 1
-
- def _add_pkg_dep_string(self, pkg, dep_root, dep_priority, dep_string,
- allow_unsatisfied):
- depth = pkg.depth + 1
- debug = "--debug" in self.myopts
- strict = pkg.type_name != "installed"
-
- if debug:
- print
- print "Parent: ", pkg
- print "Depstring:", dep_string
- print "Priority:", dep_priority
-
- try:
- selected_atoms = self._select_atoms(dep_root,
- dep_string, myuse=pkg.use.enabled, parent=pkg,
- strict=strict, priority=dep_priority)
- except portage.exception.InvalidDependString, e:
- show_invalid_depstring_notice(pkg, dep_string, str(e))
- del e
- if pkg.installed:
- return 1
- return 0
-
- if debug:
- print "Candidates:", selected_atoms
-
- vardb = self.roots[dep_root].trees["vartree"].dbapi
-
- for atom in selected_atoms:
- try:
-
- atom = portage.dep.Atom(atom)
-
- mypriority = dep_priority.copy()
- if not atom.blocker and vardb.match(atom):
- mypriority.satisfied = True
-
- if not self._add_dep(Dependency(atom=atom,
- blocker=atom.blocker, depth=depth, parent=pkg,
- priority=mypriority, root=dep_root),
- allow_unsatisfied=allow_unsatisfied):
- return 0
-
- except portage.exception.InvalidAtom, e:
- show_invalid_depstring_notice(
- pkg, dep_string, str(e))
- del e
- if not pkg.installed:
- return 0
-
- if debug:
- print "Exiting...", pkg
-
- return 1
-
- def _queue_disjunctive_deps(self, pkg, dep_root, dep_priority, dep_struct):
- """
- Queue disjunctive (virtual and ||) deps in self._dep_disjunctive_stack.
- Yields non-disjunctive deps. Raises InvalidDependString when
- necessary.
- """
- i = 0
- while i < len(dep_struct):
- x = dep_struct[i]
- if isinstance(x, list):
- for y in self._queue_disjunctive_deps(
- pkg, dep_root, dep_priority, x):
- yield y
- elif x == "||":
- self._queue_disjunction(pkg, dep_root, dep_priority,
- [ x, dep_struct[ i + 1 ] ] )
- i += 1
- else:
- try:
- x = portage.dep.Atom(x)
- except portage.exception.InvalidAtom:
- if not pkg.installed:
- raise portage.exception.InvalidDependString(
- "invalid atom: '%s'" % x)
- else:
- # Note: Eventually this will check for PROPERTIES=virtual
- # or whatever other metadata gets implemented for this
- # purpose.
- if x.cp.startswith('virtual/'):
- self._queue_disjunction( pkg, dep_root,
- dep_priority, [ str(x) ] )
- else:
- yield str(x)
- i += 1
-
- def _queue_disjunction(self, pkg, dep_root, dep_priority, dep_struct):
- self._dep_disjunctive_stack.append(
- (pkg, dep_root, dep_priority, dep_struct))
-
- def _pop_disjunction(self, allow_unsatisfied):
- """
- Pop one disjunctive dep from self._dep_disjunctive_stack, and use it to
- populate self._dep_stack.
- """
- pkg, dep_root, dep_priority, dep_struct = \
- self._dep_disjunctive_stack.pop()
- dep_string = portage.dep.paren_enclose(dep_struct)
- if not self._add_pkg_dep_string(
- pkg, dep_root, dep_priority, dep_string, allow_unsatisfied):
- return 0
- return 1
-
- def _priority(self, **kwargs):
- if "remove" in self.myparams:
- priority_constructor = UnmergeDepPriority
- else:
- priority_constructor = DepPriority
- return priority_constructor(**kwargs)
-
- def _dep_expand(self, root_config, atom_without_category):
- """
- @param root_config: a root config instance
- @type root_config: RootConfig
- @param atom_without_category: an atom without a category component
- @type atom_without_category: String
- @rtype: list
- @returns: a list of atoms containing categories (possibly empty)
- """
- null_cp = portage.dep_getkey(insert_category_into_atom(
- atom_without_category, "null"))
- cat, atom_pn = portage.catsplit(null_cp)
-
- dbs = self._filtered_trees[root_config.root]["dbs"]
- categories = set()
- for db, pkg_type, built, installed, db_keys in dbs:
- for cat in db.categories:
- if db.cp_list("%s/%s" % (cat, atom_pn)):
- categories.add(cat)
-
- deps = []
- for cat in categories:
- deps.append(insert_category_into_atom(
- atom_without_category, cat))
- return deps
-
- def _have_new_virt(self, root, atom_cp):
- ret = False
- for db, pkg_type, built, installed, db_keys in \
- self._filtered_trees[root]["dbs"]:
- if db.cp_list(atom_cp):
- ret = True
- break
- return ret
-
- def _iter_atoms_for_pkg(self, pkg):
- # TODO: add multiple $ROOT support
- if pkg.root != self.target_root:
- return
- atom_arg_map = self._atom_arg_map
- root_config = self.roots[pkg.root]
- for atom in self._set_atoms.iterAtomsForPackage(pkg):
- atom_cp = portage.dep_getkey(atom)
- if atom_cp != pkg.cp and \
- self._have_new_virt(pkg.root, atom_cp):
- continue
- visible_pkgs = root_config.visible_pkgs.match_pkgs(atom)
- visible_pkgs.reverse() # descending order
- higher_slot = None
- for visible_pkg in visible_pkgs:
- if visible_pkg.cp != atom_cp:
- continue
- if pkg >= visible_pkg:
- # This is descending order, and we're not
- # interested in any versions <= pkg given.
- break
- if pkg.slot_atom != visible_pkg.slot_atom:
- higher_slot = visible_pkg
- break
- if higher_slot is not None:
- continue
- for arg in atom_arg_map[(atom, pkg.root)]:
- if isinstance(arg, PackageArg) and \
- arg.package != pkg:
- continue
- yield arg, atom
-
- def select_files(self, myfiles):
- """Given a list of .tbz2s, .ebuilds sets, and deps, create the
- appropriate depgraph and return a favorite list."""
- debug = "--debug" in self.myopts
- root_config = self.roots[self.target_root]
- sets = root_config.sets
- getSetAtoms = root_config.setconfig.getSetAtoms
- myfavorites=[]
- myroot = self.target_root
- dbs = self._filtered_trees[myroot]["dbs"]
- vardb = self.trees[myroot]["vartree"].dbapi
- real_vardb = self._trees_orig[myroot]["vartree"].dbapi
- portdb = self.trees[myroot]["porttree"].dbapi
- bindb = self.trees[myroot]["bintree"].dbapi
- pkgsettings = self.pkgsettings[myroot]
- args = []
- onlydeps = "--onlydeps" in self.myopts
- lookup_owners = []
- for x in myfiles:
- ext = os.path.splitext(x)[1]
- if ext==".tbz2":
- if not os.path.exists(x):
- if os.path.exists(
- os.path.join(pkgsettings["PKGDIR"], "All", x)):
- x = os.path.join(pkgsettings["PKGDIR"], "All", x)
- elif os.path.exists(
- os.path.join(pkgsettings["PKGDIR"], x)):
- x = os.path.join(pkgsettings["PKGDIR"], x)
- else:
- print "\n\n!!! Binary package '"+str(x)+"' does not exist."
- print "!!! Please ensure the tbz2 exists as specified.\n"
- return 0, myfavorites
- mytbz2=portage.xpak.tbz2(x)
- mykey=mytbz2.getelements("CATEGORY")[0]+"/"+os.path.splitext(os.path.basename(x))[0]
- if os.path.realpath(x) != \
- os.path.realpath(self.trees[myroot]["bintree"].getname(mykey)):
- print colorize("BAD", "\n*** You need to adjust PKGDIR to emerge this package.\n")
- return 0, myfavorites
- db_keys = list(bindb._aux_cache_keys)
- metadata = izip(db_keys, bindb.aux_get(mykey, db_keys))
- pkg = Package(type_name="binary", root_config=root_config,
- cpv=mykey, built=True, metadata=metadata,
- onlydeps=onlydeps)
- self._pkg_cache[pkg] = pkg
- args.append(PackageArg(arg=x, package=pkg,
- root_config=root_config))
- elif ext==".ebuild":
- ebuild_path = portage.util.normalize_path(os.path.abspath(x))
- pkgdir = os.path.dirname(ebuild_path)
- tree_root = os.path.dirname(os.path.dirname(pkgdir))
- cp = pkgdir[len(tree_root)+1:]
- e = portage.exception.PackageNotFound(
- ("%s is not in a valid portage tree " + \
- "hierarchy or does not exist") % x)
- if not portage.isvalidatom(cp):
- raise e
- cat = portage.catsplit(cp)[0]
- mykey = cat + "/" + os.path.basename(ebuild_path[:-7])
- if not portage.isvalidatom("="+mykey):
- raise e
- ebuild_path = portdb.findname(mykey)
- if ebuild_path:
- if ebuild_path != os.path.join(os.path.realpath(tree_root),
- cp, os.path.basename(ebuild_path)):
- print colorize("BAD", "\n*** You need to adjust PORTDIR or PORTDIR_OVERLAY to emerge this package.\n")
- return 0, myfavorites
- if mykey not in portdb.xmatch(
- "match-visible", portage.dep_getkey(mykey)):
- print colorize("BAD", "\n*** You are emerging a masked package. It is MUCH better to use")
- print colorize("BAD", "*** /etc/portage/package.* to accomplish this. See portage(5) man")
- print colorize("BAD", "*** page for details.")
- countdown(int(self.settings["EMERGE_WARNING_DELAY"]),
- "Continuing...")
- else:
- raise portage.exception.PackageNotFound(
- "%s is not in a valid portage tree hierarchy or does not exist" % x)
- db_keys = list(portdb._aux_cache_keys)
- metadata = izip(db_keys, portdb.aux_get(mykey, db_keys))
- pkg = Package(type_name="ebuild", root_config=root_config,
- cpv=mykey, metadata=metadata, onlydeps=onlydeps)
- pkgsettings.setcpv(pkg)
- pkg.metadata["USE"] = pkgsettings["PORTAGE_USE"]
- pkg.metadata['CHOST'] = pkgsettings.get('CHOST', '')
- self._pkg_cache[pkg] = pkg
- args.append(PackageArg(arg=x, package=pkg,
- root_config=root_config))
- elif x.startswith(os.path.sep):
- if not x.startswith(myroot):
- portage.writemsg(("\n\n!!! '%s' does not start with" + \
- " $ROOT.\n") % x, noiselevel=-1)
- return 0, []
- # Queue these up since it's most efficient to handle
- # multiple files in a single iter_owners() call.
- lookup_owners.append(x)
- else:
- if x in ("system", "world"):
- x = SETPREFIX + x
- if x.startswith(SETPREFIX):
- s = x[len(SETPREFIX):]
- if s not in sets:
- raise portage.exception.PackageSetNotFound(s)
- if s in self._sets:
- continue
- # Recursively expand sets so that containment tests in
- # self._get_parent_sets() properly match atoms in nested
- # sets (like if world contains system).
- expanded_set = InternalPackageSet(
- initial_atoms=getSetAtoms(s))
- self._sets[s] = expanded_set
- args.append(SetArg(arg=x, set=expanded_set,
- root_config=root_config))
- continue
- if not is_valid_package_atom(x):
- portage.writemsg("\n\n!!! '%s' is not a valid package atom.\n" % x,
- noiselevel=-1)
- portage.writemsg("!!! Please check ebuild(5) for full details.\n")
- portage.writemsg("!!! (Did you specify a version but forget to prefix with '='?)\n")
- return (0,[])
- # Don't expand categories or old-style virtuals here unless
- # necessary. Expansion of old-style virtuals here causes at
- # least the following problems:
- # 1) It's more difficult to determine which set(s) an atom
- # came from, if any.
- # 2) It takes away freedom from the resolver to choose other
- # possible expansions when necessary.
- if "/" in x:
- args.append(AtomArg(arg=x, atom=x,
- root_config=root_config))
- continue
- expanded_atoms = self._dep_expand(root_config, x)
- installed_cp_set = set()
- for atom in expanded_atoms:
- atom_cp = portage.dep_getkey(atom)
- if vardb.cp_list(atom_cp):
- installed_cp_set.add(atom_cp)
-
- if len(installed_cp_set) > 1:
- non_virtual_cps = set()
- for atom_cp in installed_cp_set:
- if not atom_cp.startswith("virtual/"):
- non_virtual_cps.add(atom_cp)
- if len(non_virtual_cps) == 1:
- installed_cp_set = non_virtual_cps
-
- if len(expanded_atoms) > 1 and len(installed_cp_set) == 1:
- installed_cp = iter(installed_cp_set).next()
- expanded_atoms = [atom for atom in expanded_atoms \
- if portage.dep_getkey(atom) == installed_cp]
-
- if len(expanded_atoms) > 1:
- print
- print
- ambiguous_package_name(x, expanded_atoms, root_config,
- self.spinner, self.myopts)
- return False, myfavorites
- if expanded_atoms:
- atom = expanded_atoms[0]
- else:
- null_atom = insert_category_into_atom(x, "null")
- null_cp = portage.dep_getkey(null_atom)
- cat, atom_pn = portage.catsplit(null_cp)
- virts_p = root_config.settings.get_virts_p().get(atom_pn)
- if virts_p:
- # Allow the depgraph to choose which virtual.
- atom = insert_category_into_atom(x, "virtual")
- else:
- atom = insert_category_into_atom(x, "null")
-
- args.append(AtomArg(arg=x, atom=atom,
- root_config=root_config))
-
- if lookup_owners:
- relative_paths = []
- search_for_multiple = False
- if len(lookup_owners) > 1:
- search_for_multiple = True
-
- for x in lookup_owners:
- if not search_for_multiple and os.path.isdir(x):
- search_for_multiple = True
- relative_paths.append(x[len(myroot):])
-
- owners = set()
- for pkg, relative_path in \
- real_vardb._owners.iter_owners(relative_paths):
- owners.add(pkg.mycpv)
- if not search_for_multiple:
- break
-
- if not owners:
- portage.writemsg(("\n\n!!! '%s' is not claimed " + \
- "by any package.\n") % lookup_owners[0], noiselevel=-1)
- return 0, []
-
- for cpv in owners:
- slot = vardb.aux_get(cpv, ["SLOT"])[0]
- if not slot:
- # portage now masks packages with missing slot, but it's
- # possible that one was installed by an older version
- atom = portage.cpv_getkey(cpv)
- else:
- atom = "%s:%s" % (portage.cpv_getkey(cpv), slot)
- args.append(AtomArg(arg=atom, atom=atom,
- root_config=root_config))
-
- if "--update" in self.myopts:
- # In some cases, the greedy slots behavior can pull in a slot that
- # the user would want to uninstall due to it being blocked by a
- # newer version in a different slot. Therefore, it's necessary to
- # detect and discard any that should be uninstalled. Each time
- # that arguments are updated, package selections are repeated in
- # order to ensure consistency with the current arguments:
- #
- # 1) Initialize args
- # 2) Select packages and generate initial greedy atoms
- # 3) Update args with greedy atoms
- # 4) Select packages and generate greedy atoms again, while
- # accounting for any blockers between selected packages
- # 5) Update args with revised greedy atoms
-
- self._set_args(args)
- greedy_args = []
- for arg in args:
- greedy_args.append(arg)
- if not isinstance(arg, AtomArg):
- continue
- for atom in self._greedy_slots(arg.root_config, arg.atom):
- greedy_args.append(
- AtomArg(arg=arg.arg, atom=atom,
- root_config=arg.root_config))
-
- self._set_args(greedy_args)
- del greedy_args
-
- # Revise greedy atoms, accounting for any blockers
- # between selected packages.
- revised_greedy_args = []
- for arg in args:
- revised_greedy_args.append(arg)
- if not isinstance(arg, AtomArg):
- continue
- for atom in self._greedy_slots(arg.root_config, arg.atom,
- blocker_lookahead=True):
- revised_greedy_args.append(
- AtomArg(arg=arg.arg, atom=atom,
- root_config=arg.root_config))
- args = revised_greedy_args
- del revised_greedy_args
-
- self._set_args(args)
-
- myfavorites = set(myfavorites)
- for arg in args:
- if isinstance(arg, (AtomArg, PackageArg)):
- myfavorites.add(arg.atom)
- elif isinstance(arg, SetArg):
- myfavorites.add(arg.arg)
- myfavorites = list(myfavorites)
-
- pprovideddict = pkgsettings.pprovideddict
- if debug:
- portage.writemsg("\n", noiselevel=-1)
- # Order needs to be preserved since a feature of --nodeps
- # is to allow the user to force a specific merge order.
- args.reverse()
- while args:
- arg = args.pop()
- for atom in arg.set:
- self.spinner.update()
- dep = Dependency(atom=atom, onlydeps=onlydeps,
- root=myroot, parent=arg)
- atom_cp = portage.dep_getkey(atom)
- try:
- pprovided = pprovideddict.get(portage.dep_getkey(atom))
- if pprovided and portage.match_from_list(atom, pprovided):
- # A provided package has been specified on the command line.
- self._pprovided_args.append((arg, atom))
- continue
- if isinstance(arg, PackageArg):
- if not self._add_pkg(arg.package, dep) or \
- not self._create_graph():
- sys.stderr.write(("\n\n!!! Problem resolving " + \
- "dependencies for %s\n") % arg.arg)
- return 0, myfavorites
- continue
- if debug:
- portage.writemsg(" Arg: %s\n Atom: %s\n" % \
- (arg, atom), noiselevel=-1)
- pkg, existing_node = self._select_package(
- myroot, atom, onlydeps=onlydeps)
- if not pkg:
- if not (isinstance(arg, SetArg) and \
- arg.name in ("system", "world")):
- self._unsatisfied_deps_for_display.append(
- ((myroot, atom), {}))
- return 0, myfavorites
- self._missing_args.append((arg, atom))
- continue
- if atom_cp != pkg.cp:
- # For old-style virtuals, we need to repeat the
- # package.provided check against the selected package.
- expanded_atom = atom.replace(atom_cp, pkg.cp)
- pprovided = pprovideddict.get(pkg.cp)
- if pprovided and \
- portage.match_from_list(expanded_atom, pprovided):
- # A provided package has been
- # specified on the command line.
- self._pprovided_args.append((arg, atom))
- continue
- if pkg.installed and "selective" not in self.myparams:
- self._unsatisfied_deps_for_display.append(
- ((myroot, atom), {}))
- # Previous behavior was to bail out in this case, but
- # since the dep is satisfied by the installed package,
- # it's more friendly to continue building the graph
- # and just show a warning message. Therefore, only bail
- # out here if the atom is not from either the system or
- # world set.
- if not (isinstance(arg, SetArg) and \
- arg.name in ("system", "world")):
- return 0, myfavorites
-
- # Add the selected package to the graph as soon as possible
- # so that later dep_check() calls can use it as feedback
- # for making more consistent atom selections.
- if not self._add_pkg(pkg, dep):
- if isinstance(arg, SetArg):
- sys.stderr.write(("\n\n!!! Problem resolving " + \
- "dependencies for %s from %s\n") % \
- (atom, arg.arg))
- else:
- sys.stderr.write(("\n\n!!! Problem resolving " + \
- "dependencies for %s\n") % atom)
- return 0, myfavorites
-
- except portage.exception.MissingSignature, e:
- portage.writemsg("\n\n!!! A missing gpg signature is preventing portage from calculating the\n")
- portage.writemsg("!!! required dependencies. This is a security feature enabled by the admin\n")
- portage.writemsg("!!! to aid in the detection of malicious intent.\n\n")
- portage.writemsg("!!! THIS IS A POSSIBLE INDICATION OF TAMPERED FILES -- CHECK CAREFULLY.\n")
- portage.writemsg("!!! Affected file: %s\n" % (e), noiselevel=-1)
- return 0, myfavorites
- except portage.exception.InvalidSignature, e:
- portage.writemsg("\n\n!!! An invalid gpg signature is preventing portage from calculating the\n")
- portage.writemsg("!!! required dependencies. This is a security feature enabled by the admin\n")
- portage.writemsg("!!! to aid in the detection of malicious intent.\n\n")
- portage.writemsg("!!! THIS IS A POSSIBLE INDICATION OF TAMPERED FILES -- CHECK CAREFULLY.\n")
- portage.writemsg("!!! Affected file: %s\n" % (e), noiselevel=-1)
- return 0, myfavorites
- except SystemExit, e:
- raise # Needed else can't exit
- except Exception, e:
- print >> sys.stderr, "\n\n!!! Problem in '%s' dependencies." % atom
- print >> sys.stderr, "!!!", str(e), getattr(e, "__module__", None)
- raise
-
- # Now that the root packages have been added to the graph,
- # process the dependencies.
- if not self._create_graph():
- return 0, myfavorites
-
- missing=0
- if "--usepkgonly" in self.myopts:
- for xs in self.digraph.all_nodes():
- if not isinstance(xs, Package):
- continue
- if len(xs) >= 4 and xs[0] != "binary" and xs[3] == "merge":
- if missing == 0:
- print
- missing += 1
- print "Missing binary for:",xs[2]
-
- try:
- self.altlist()
- except self._unknown_internal_error:
- return False, myfavorites
-
- # We're true here unless we are missing binaries.
- return (not missing,myfavorites)
-
- def _set_args(self, args):
- """
- Create the "args" package set from atoms and packages given as
- arguments. This method can be called multiple times if necessary.
- The package selection cache is automatically invalidated, since
- arguments influence package selections.
- """
- args_set = self._sets["args"]
- args_set.clear()
- for arg in args:
- if not isinstance(arg, (AtomArg, PackageArg)):
- continue
- atom = arg.atom
- if atom in args_set:
- continue
- args_set.add(atom)
-
- self._set_atoms.clear()
- self._set_atoms.update(chain(*self._sets.itervalues()))
- atom_arg_map = self._atom_arg_map
- atom_arg_map.clear()
- for arg in args:
- for atom in arg.set:
- atom_key = (atom, arg.root_config.root)
- refs = atom_arg_map.get(atom_key)
- if refs is None:
- refs = []
- atom_arg_map[atom_key] = refs
- if arg not in refs:
- refs.append(arg)
-
- # Invalidate the package selection cache, since
- # arguments influence package selections.
- self._highest_pkg_cache.clear()
- for trees in self._filtered_trees.itervalues():
- trees["porttree"].dbapi._clear_cache()
-
- def _greedy_slots(self, root_config, atom, blocker_lookahead=False):
- """
- Return a list of slot atoms corresponding to installed slots that
- differ from the slot of the highest visible match. When
- blocker_lookahead is True, slot atoms that would trigger a blocker
- conflict are automatically discarded, potentially allowing automatic
- uninstallation of older slots when appropriate.
- """
- highest_pkg, in_graph = self._select_package(root_config.root, atom)
- if highest_pkg is None:
- return []
- vardb = root_config.trees["vartree"].dbapi
- slots = set()
- for cpv in vardb.match(atom):
- # don't mix new virtuals with old virtuals
- if portage.cpv_getkey(cpv) == highest_pkg.cp:
- slots.add(vardb.aux_get(cpv, ["SLOT"])[0])
-
- slots.add(highest_pkg.metadata["SLOT"])
- if len(slots) == 1:
- return []
- greedy_pkgs = []
- slots.remove(highest_pkg.metadata["SLOT"])
- while slots:
- slot = slots.pop()
- slot_atom = portage.dep.Atom("%s:%s" % (highest_pkg.cp, slot))
- pkg, in_graph = self._select_package(root_config.root, slot_atom)
- if pkg is not None and \
- pkg.cp == highest_pkg.cp and pkg < highest_pkg:
- greedy_pkgs.append(pkg)
- if not greedy_pkgs:
- return []
- if not blocker_lookahead:
- return [pkg.slot_atom for pkg in greedy_pkgs]
-
- blockers = {}
- blocker_dep_keys = ["DEPEND", "PDEPEND", "RDEPEND"]
- for pkg in greedy_pkgs + [highest_pkg]:
- dep_str = " ".join(pkg.metadata[k] for k in blocker_dep_keys)
- try:
- atoms = self._select_atoms(
- pkg.root, dep_str, pkg.use.enabled,
- parent=pkg, strict=True)
- except portage.exception.InvalidDependString:
- continue
- blocker_atoms = (x for x in atoms if x.blocker)
- blockers[pkg] = InternalPackageSet(initial_atoms=blocker_atoms)
-
- if highest_pkg not in blockers:
- return []
-
- # filter packages with invalid deps
- greedy_pkgs = [pkg for pkg in greedy_pkgs if pkg in blockers]
-
- # filter packages that conflict with highest_pkg
- greedy_pkgs = [pkg for pkg in greedy_pkgs if not \
- (blockers[highest_pkg].findAtomForPackage(pkg) or \
- blockers[pkg].findAtomForPackage(highest_pkg))]
-
- if not greedy_pkgs:
- return []
-
- # If two packages conflict, discard the lower version.
- discard_pkgs = set()
- greedy_pkgs.sort(reverse=True)
- for i in xrange(len(greedy_pkgs) - 1):
- pkg1 = greedy_pkgs[i]
- if pkg1 in discard_pkgs:
- continue
- for j in xrange(i + 1, len(greedy_pkgs)):
- pkg2 = greedy_pkgs[j]
- if pkg2 in discard_pkgs:
- continue
- if blockers[pkg1].findAtomForPackage(pkg2) or \
- blockers[pkg2].findAtomForPackage(pkg1):
- # pkg1 > pkg2
- discard_pkgs.add(pkg2)
-
- return [pkg.slot_atom for pkg in greedy_pkgs \
- if pkg not in discard_pkgs]
-
- def _select_atoms_from_graph(self, *pargs, **kwargs):
- """
- Prefer atoms matching packages that have already been
- added to the graph or those that are installed and have
- not been scheduled for replacement.
- """
- kwargs["trees"] = self._graph_trees
- return self._select_atoms_highest_available(*pargs, **kwargs)
-
- def _select_atoms_highest_available(self, root, depstring,
- myuse=None, parent=None, strict=True, trees=None, priority=None):
- """This will raise InvalidDependString if necessary. If trees is
- None then self._filtered_trees is used."""
- pkgsettings = self.pkgsettings[root]
- if trees is None:
- trees = self._filtered_trees
- if not getattr(priority, "buildtime", False):
- # The parent should only be passed to dep_check() for buildtime
- # dependencies since that's the only case when it's appropriate
- # to trigger the circular dependency avoidance code which uses it.
- # It's important not to trigger the same circular dependency
- # avoidance code for runtime dependencies since it's not needed
- # and it can promote an incorrect package choice.
- parent = None
- if True:
- try:
- if parent is not None:
- trees[root]["parent"] = parent
- if not strict:
- portage.dep._dep_check_strict = False
- mycheck = portage.dep_check(depstring, None,
- pkgsettings, myuse=myuse,
- myroot=root, trees=trees)
- finally:
- if parent is not None:
- trees[root].pop("parent")
- portage.dep._dep_check_strict = True
- if not mycheck[0]:
- raise portage.exception.InvalidDependString(mycheck[1])
- selected_atoms = mycheck[1]
- return selected_atoms
-
- def _show_unsatisfied_dep(self, root, atom, myparent=None, arg=None):
- atom = portage.dep.Atom(atom)
- atom_set = InternalPackageSet(initial_atoms=(atom,))
- atom_without_use = atom
- if atom.use:
- atom_without_use = portage.dep.remove_slot(atom)
- if atom.slot:
- atom_without_use += ":" + atom.slot
- atom_without_use = portage.dep.Atom(atom_without_use)
- xinfo = '"%s"' % atom
- if arg:
- xinfo='"%s"' % arg
- # Discard null/ from failed cpv_expand category expansion.
- xinfo = xinfo.replace("null/", "")
- masked_packages = []
- missing_use = []
- masked_pkg_instances = set()
- missing_licenses = []
- have_eapi_mask = False
- pkgsettings = self.pkgsettings[root]
- implicit_iuse = pkgsettings._get_implicit_iuse()
- root_config = self.roots[root]
- portdb = self.roots[root].trees["porttree"].dbapi
- dbs = self._filtered_trees[root]["dbs"]
- for db, pkg_type, built, installed, db_keys in dbs:
- if installed:
- continue
- match = db.match
- if hasattr(db, "xmatch"):
- cpv_list = db.xmatch("match-all", atom_without_use)
- else:
- cpv_list = db.match(atom_without_use)
- # descending order
- cpv_list.reverse()
- for cpv in cpv_list:
- metadata, mreasons = get_mask_info(root_config, cpv,
- pkgsettings, db, pkg_type, built, installed, db_keys)
- if metadata is not None:
- pkg = Package(built=built, cpv=cpv,
- installed=installed, metadata=metadata,
- root_config=root_config)
- if pkg.cp != atom.cp:
- # A cpv can be returned from dbapi.match() as an
- # old-style virtual match even in cases when the
- # package does not actually PROVIDE the virtual.
- # Filter out any such false matches here.
- if not atom_set.findAtomForPackage(pkg):
- continue
- if mreasons:
- masked_pkg_instances.add(pkg)
- if atom.use:
- missing_use.append(pkg)
- if not mreasons:
- continue
- masked_packages.append(
- (root_config, pkgsettings, cpv, metadata, mreasons))
-
- missing_use_reasons = []
- missing_iuse_reasons = []
- for pkg in missing_use:
- use = pkg.use.enabled
- iuse = implicit_iuse.union(re.escape(x) for x in pkg.iuse.all)
- iuse_re = re.compile("^(%s)$" % "|".join(iuse))
- missing_iuse = []
- for x in atom.use.required:
- if iuse_re.match(x) is None:
- missing_iuse.append(x)
- mreasons = []
- if missing_iuse:
- mreasons.append("Missing IUSE: %s" % " ".join(missing_iuse))
- missing_iuse_reasons.append((pkg, mreasons))
- else:
- need_enable = sorted(atom.use.enabled.difference(use))
- need_disable = sorted(atom.use.disabled.intersection(use))
- if need_enable or need_disable:
- changes = []
- changes.extend(colorize("red", "+" + x) \
- for x in need_enable)
- changes.extend(colorize("blue", "-" + x) \
- for x in need_disable)
- mreasons.append("Change USE: %s" % " ".join(changes))
- missing_use_reasons.append((pkg, mreasons))
-
- unmasked_use_reasons = [(pkg, mreasons) for (pkg, mreasons) \
- in missing_use_reasons if pkg not in masked_pkg_instances]
-
- unmasked_iuse_reasons = [(pkg, mreasons) for (pkg, mreasons) \
- in missing_iuse_reasons if pkg not in masked_pkg_instances]
-
- show_missing_use = False
- if unmasked_use_reasons:
- # Only show the latest version.
- show_missing_use = unmasked_use_reasons[:1]
- elif unmasked_iuse_reasons:
- if missing_use_reasons:
- # All packages with required IUSE are masked,
- # so display a normal masking message.
- pass
- else:
- show_missing_use = unmasked_iuse_reasons
-
- if show_missing_use:
- print "\nemerge: there are no ebuilds built with USE flags to satisfy "+green(xinfo)+"."
- print "!!! One of the following packages is required to complete your request:"
- for pkg, mreasons in show_missing_use:
- print "- "+pkg.cpv+" ("+", ".join(mreasons)+")"
-
- elif masked_packages:
- print "\n!!! " + \
- colorize("BAD", "All ebuilds that could satisfy ") + \
- colorize("INFORM", xinfo) + \
- colorize("BAD", " have been masked.")
- print "!!! One of the following masked packages is required to complete your request:"
- have_eapi_mask = show_masked_packages(masked_packages)
- if have_eapi_mask:
- print
- msg = ("The current version of portage supports " + \
- "EAPI '%s'. You must upgrade to a newer version" + \
- " of portage before EAPI masked packages can" + \
- " be installed.") % portage.const.EAPI
- from textwrap import wrap
- for line in wrap(msg, 75):
- print line
- print
- show_mask_docs()
- else:
- print "\nemerge: there are no ebuilds to satisfy "+green(xinfo)+"."
-
- # Show parent nodes and the argument that pulled them in.
- traversed_nodes = set()
- node = myparent
- msg = []
- while node is not None:
- traversed_nodes.add(node)
- msg.append('(dependency required by "%s" [%s])' % \
- (colorize('INFORM', str(node.cpv)), node.type_name))
- # When traversing to parents, prefer arguments over packages
- # since arguments are root nodes. Never traverse the same
- # package twice, in order to prevent an infinite loop.
- selected_parent = None
- for parent in self.digraph.parent_nodes(node):
- if isinstance(parent, DependencyArg):
- msg.append('(dependency required by "%s" [argument])' % \
- (colorize('INFORM', str(parent))))
- selected_parent = None
- break
- if parent not in traversed_nodes:
- selected_parent = parent
- node = selected_parent
- for line in msg:
- print line
-
- print
-
- def _select_pkg_highest_available(self, root, atom, onlydeps=False):
- cache_key = (root, atom, onlydeps)
- ret = self._highest_pkg_cache.get(cache_key)
- if ret is not None:
- pkg, existing = ret
- if pkg and not existing:
- existing = self._slot_pkg_map[root].get(pkg.slot_atom)
- if existing and existing == pkg:
- # Update the cache to reflect that the
- # package has been added to the graph.
- ret = pkg, pkg
- self._highest_pkg_cache[cache_key] = ret
- return ret
- ret = self._select_pkg_highest_available_imp(root, atom, onlydeps=onlydeps)
- self._highest_pkg_cache[cache_key] = ret
- pkg, existing = ret
- if pkg is not None:
- settings = pkg.root_config.settings
- if visible(settings, pkg) and not (pkg.installed and \
- settings._getMissingKeywords(pkg.cpv, pkg.metadata)):
- pkg.root_config.visible_pkgs.cpv_inject(pkg)
- return ret
-
- def _select_pkg_highest_available_imp(self, root, atom, onlydeps=False):
- root_config = self.roots[root]
- pkgsettings = self.pkgsettings[root]
- dbs = self._filtered_trees[root]["dbs"]
- vardb = self.roots[root].trees["vartree"].dbapi
- portdb = self.roots[root].trees["porttree"].dbapi
- # List of acceptable packages, ordered by type preference.
- matched_packages = []
- highest_version = None
- if not isinstance(atom, portage.dep.Atom):
- atom = portage.dep.Atom(atom)
- atom_cp = atom.cp
- atom_set = InternalPackageSet(initial_atoms=(atom,))
- existing_node = None
- myeb = None
- usepkgonly = "--usepkgonly" in self.myopts
- empty = "empty" in self.myparams
- selective = "selective" in self.myparams
- reinstall = False
- noreplace = "--noreplace" in self.myopts
- # Behavior of the "selective" parameter depends on
- # whether or not a package matches an argument atom.
- # If an installed package provides an old-style
- # virtual that is no longer provided by an available
- # package, the installed package may match an argument
- # atom even though none of the available packages do.
- # Therefore, "selective" logic does not consider
- # whether or not an installed package matches an
- # argument atom. It only considers whether or not
- # available packages match argument atoms, which is
- # represented by the found_available_arg flag.
- found_available_arg = False
- for find_existing_node in True, False:
- if existing_node:
- break
- for db, pkg_type, built, installed, db_keys in dbs:
- if existing_node:
- break
- if installed and not find_existing_node:
- want_reinstall = reinstall or empty or \
- (found_available_arg and not selective)
- if want_reinstall and matched_packages:
- continue
- if hasattr(db, "xmatch"):
- cpv_list = db.xmatch("match-all", atom)
- else:
- cpv_list = db.match(atom)
-
- # USE=multislot can make an installed package appear as if
- # it doesn't satisfy a slot dependency. Rebuilding the ebuild
- # won't do any good as long as USE=multislot is enabled since
- # the newly built package still won't have the expected slot.
- # Therefore, assume that such SLOT dependencies are already
- # satisfied rather than forcing a rebuild.
- if installed and not cpv_list and atom.slot:
- for cpv in db.match(atom.cp):
- slot_available = False
- for other_db, other_type, other_built, \
- other_installed, other_keys in dbs:
- try:
- if atom.slot == \
- other_db.aux_get(cpv, ["SLOT"])[0]:
- slot_available = True
- break
- except KeyError:
- pass
- if not slot_available:
- continue
- inst_pkg = self._pkg(cpv, "installed",
- root_config, installed=installed)
- # Remove the slot from the atom and verify that
- # the package matches the resulting atom.
- atom_without_slot = portage.dep.remove_slot(atom)
- if atom.use:
- atom_without_slot += str(atom.use)
- atom_without_slot = portage.dep.Atom(atom_without_slot)
- if portage.match_from_list(
- atom_without_slot, [inst_pkg]):
- cpv_list = [inst_pkg.cpv]
- break
-
- if not cpv_list:
- continue
- pkg_status = "merge"
- if installed or onlydeps:
- pkg_status = "nomerge"
- # descending order
- cpv_list.reverse()
- for cpv in cpv_list:
- # Make --noreplace take precedence over --newuse.
- if not installed and noreplace and \
- cpv in vardb.match(atom):
- # If the installed version is masked, it may
- # be necessary to look at lower versions,
- # in case there is a visible downgrade.
- continue
- reinstall_for_flags = None
- cache_key = (pkg_type, root, cpv, pkg_status)
- calculated_use = True
- pkg = self._pkg_cache.get(cache_key)
- if pkg is None:
- calculated_use = False
- try:
- metadata = izip(db_keys, db.aux_get(cpv, db_keys))
- except KeyError:
- continue
- pkg = Package(built=built, cpv=cpv,
- installed=installed, metadata=metadata,
- onlydeps=onlydeps, root_config=root_config,
- type_name=pkg_type)
- metadata = pkg.metadata
- if not built:
- metadata['CHOST'] = pkgsettings.get('CHOST', '')
- if not built and ("?" in metadata["LICENSE"] or \
- "?" in metadata["PROVIDE"]):
- # This is avoided whenever possible because
- # it's expensive. It only needs to be done here
- # if it has an effect on visibility.
- pkgsettings.setcpv(pkg)
- metadata["USE"] = pkgsettings["PORTAGE_USE"]
- calculated_use = True
- self._pkg_cache[pkg] = pkg
-
- if not installed or (built and matched_packages):
- # Only enforce visibility on installed packages
- # if there is at least one other visible package
- # available. By filtering installed masked packages
- # here, packages that have been masked since they
- # were installed can be automatically downgraded
- # to an unmasked version.
- try:
- if not visible(pkgsettings, pkg):
- continue
- except portage.exception.InvalidDependString:
- if not installed:
- continue
-
- # Enable upgrade or downgrade to a version
- # with visible KEYWORDS when the installed
- # version is masked by KEYWORDS, but never
- # reinstall the same exact version only due
- # to a KEYWORDS mask.
- if built and matched_packages:
-
- different_version = None
- for avail_pkg in matched_packages:
- if not portage.dep.cpvequal(
- pkg.cpv, avail_pkg.cpv):
- different_version = avail_pkg
- break
- if different_version is not None:
-
- if installed and \
- pkgsettings._getMissingKeywords(
- pkg.cpv, pkg.metadata):
- continue
-
- # If the ebuild no longer exists or it's
- # keywords have been dropped, reject built
- # instances (installed or binary).
- # If --usepkgonly is enabled, assume that
- # the ebuild status should be ignored.
- if not usepkgonly:
- try:
- pkg_eb = self._pkg(
- pkg.cpv, "ebuild", root_config)
- except portage.exception.PackageNotFound:
- continue
- else:
- if not visible(pkgsettings, pkg_eb):
- continue
-
- if not pkg.built and not calculated_use:
- # This is avoided whenever possible because
- # it's expensive.
- pkgsettings.setcpv(pkg)
- pkg.metadata["USE"] = pkgsettings["PORTAGE_USE"]
-
- if pkg.cp != atom.cp:
- # A cpv can be returned from dbapi.match() as an
- # old-style virtual match even in cases when the
- # package does not actually PROVIDE the virtual.
- # Filter out any such false matches here.
- if not atom_set.findAtomForPackage(pkg):
- continue
-
- myarg = None
- if root == self.target_root:
- try:
- # Ebuild USE must have been calculated prior
- # to this point, in case atoms have USE deps.
- myarg = self._iter_atoms_for_pkg(pkg).next()
- except StopIteration:
- pass
- except portage.exception.InvalidDependString:
- if not installed:
- # masked by corruption
- continue
- if not installed and myarg:
- found_available_arg = True
-
- if atom.use and not pkg.built:
- use = pkg.use.enabled
- if atom.use.enabled.difference(use):
- continue
- if atom.use.disabled.intersection(use):
- continue
- if pkg.cp == atom_cp:
- if highest_version is None:
- highest_version = pkg
- elif pkg > highest_version:
- highest_version = pkg
- # At this point, we've found the highest visible
- # match from the current repo. Any lower versions
- # from this repo are ignored, so this so the loop
- # will always end with a break statement below
- # this point.
- if find_existing_node:
- e_pkg = self._slot_pkg_map[root].get(pkg.slot_atom)
- if not e_pkg:
- break
- if portage.dep.match_from_list(atom, [e_pkg]):
- if highest_version and \
- e_pkg.cp == atom_cp and \
- e_pkg < highest_version and \
- e_pkg.slot_atom != highest_version.slot_atom:
- # There is a higher version available in a
- # different slot, so this existing node is
- # irrelevant.
- pass
- else:
- matched_packages.append(e_pkg)
- existing_node = e_pkg
- break
- # Compare built package to current config and
- # reject the built package if necessary.
- if built and not installed and \
- ("--newuse" in self.myopts or \
- "--reinstall" in self.myopts):
- iuses = pkg.iuse.all
- old_use = pkg.use.enabled
- if myeb:
- pkgsettings.setcpv(myeb)
- else:
- pkgsettings.setcpv(pkg)
- now_use = pkgsettings["PORTAGE_USE"].split()
- forced_flags = set()
- forced_flags.update(pkgsettings.useforce)
- forced_flags.update(pkgsettings.usemask)
- cur_iuse = iuses
- if myeb and not usepkgonly:
- cur_iuse = myeb.iuse.all
- if self._reinstall_for_flags(forced_flags,
- old_use, iuses,
- now_use, cur_iuse):
- break
- # Compare current config to installed package
- # and do not reinstall if possible.
- if not installed and \
- ("--newuse" in self.myopts or \
- "--reinstall" in self.myopts) and \
- cpv in vardb.match(atom):
- pkgsettings.setcpv(pkg)
- forced_flags = set()
- forced_flags.update(pkgsettings.useforce)
- forced_flags.update(pkgsettings.usemask)
- old_use = vardb.aux_get(cpv, ["USE"])[0].split()
- old_iuse = set(filter_iuse_defaults(
- vardb.aux_get(cpv, ["IUSE"])[0].split()))
- cur_use = pkg.use.enabled
- cur_iuse = pkg.iuse.all
- reinstall_for_flags = \
- self._reinstall_for_flags(
- forced_flags, old_use, old_iuse,
- cur_use, cur_iuse)
- if reinstall_for_flags:
- reinstall = True
- if not built:
- myeb = pkg
- matched_packages.append(pkg)
- if reinstall_for_flags:
- self._reinstall_nodes[pkg] = \
- reinstall_for_flags
- break
-
- if not matched_packages:
- return None, None
-
- if "--debug" in self.myopts:
- for pkg in matched_packages:
- portage.writemsg("%s %s\n" % \
- ((pkg.type_name + ":").rjust(10), pkg.cpv), noiselevel=-1)
-
- # Filter out any old-style virtual matches if they are
- # mixed with new-style virtual matches.
- cp = portage.dep_getkey(atom)
- if len(matched_packages) > 1 and \
- "virtual" == portage.catsplit(cp)[0]:
- for pkg in matched_packages:
- if pkg.cp != cp:
- continue
- # Got a new-style virtual, so filter
- # out any old-style virtuals.
- matched_packages = [pkg for pkg in matched_packages \
- if pkg.cp == cp]
- break
-
- if len(matched_packages) > 1:
- bestmatch = portage.best(
- [pkg.cpv for pkg in matched_packages])
- matched_packages = [pkg for pkg in matched_packages \
- if portage.dep.cpvequal(pkg.cpv, bestmatch)]
-
- # ordered by type preference ("ebuild" type is the last resort)
- return matched_packages[-1], existing_node
-
- def _select_pkg_from_graph(self, root, atom, onlydeps=False):
- """
- Select packages that have already been added to the graph or
- those that are installed and have not been scheduled for
- replacement.
- """
- graph_db = self._graph_trees[root]["porttree"].dbapi
- matches = graph_db.match_pkgs(atom)
- if not matches:
- return None, None
- pkg = matches[-1] # highest match
- in_graph = self._slot_pkg_map[root].get(pkg.slot_atom)
- return pkg, in_graph
-
- def _complete_graph(self):
- """
- Add any deep dependencies of required sets (args, system, world) that
- have not been pulled into the graph yet. This ensures that the graph
- is consistent such that initially satisfied deep dependencies are not
- broken in the new graph. Initially unsatisfied dependencies are
- irrelevant since we only want to avoid breaking dependencies that are
- intially satisfied.
-
- Since this method can consume enough time to disturb users, it is
- currently only enabled by the --complete-graph option.
- """
- if "--buildpkgonly" in self.myopts or \
- "recurse" not in self.myparams:
- return 1
-
- if "complete" not in self.myparams:
- # Skip this to avoid consuming enough time to disturb users.
- return 1
-
- # Put the depgraph into a mode that causes it to only
- # select packages that have already been added to the
- # graph or those that are installed and have not been
- # scheduled for replacement. Also, toggle the "deep"
- # parameter so that all dependencies are traversed and
- # accounted for.
- self._select_atoms = self._select_atoms_from_graph
- self._select_package = self._select_pkg_from_graph
- already_deep = "deep" in self.myparams
- if not already_deep:
- self.myparams.add("deep")
-
- for root in self.roots:
- required_set_names = self._required_set_names.copy()
- if root == self.target_root and \
- (already_deep or "empty" in self.myparams):
- required_set_names.difference_update(self._sets)
- if not required_set_names and not self._ignored_deps:
- continue
- root_config = self.roots[root]
- setconfig = root_config.setconfig
- args = []
- # Reuse existing SetArg instances when available.
- for arg in self.digraph.root_nodes():
- if not isinstance(arg, SetArg):
- continue
- if arg.root_config != root_config:
- continue
- if arg.name in required_set_names:
- args.append(arg)
- required_set_names.remove(arg.name)
- # Create new SetArg instances only when necessary.
- for s in required_set_names:
- expanded_set = InternalPackageSet(
- initial_atoms=setconfig.getSetAtoms(s))
- atom = SETPREFIX + s
- args.append(SetArg(arg=atom, set=expanded_set,
- root_config=root_config))
- vardb = root_config.trees["vartree"].dbapi
- for arg in args:
- for atom in arg.set:
- self._dep_stack.append(
- Dependency(atom=atom, root=root, parent=arg))
- if self._ignored_deps:
- self._dep_stack.extend(self._ignored_deps)
- self._ignored_deps = []
- if not self._create_graph(allow_unsatisfied=True):
- return 0
- # Check the unsatisfied deps to see if any initially satisfied deps
- # will become unsatisfied due to an upgrade. Initially unsatisfied
- # deps are irrelevant since we only want to avoid breaking deps
- # that are initially satisfied.
- while self._unsatisfied_deps:
- dep = self._unsatisfied_deps.pop()
- matches = vardb.match_pkgs(dep.atom)
- if not matches:
- self._initially_unsatisfied_deps.append(dep)
- continue
- # An scheduled installation broke a deep dependency.
- # Add the installed package to the graph so that it
- # will be appropriately reported as a slot collision
- # (possibly solvable via backtracking).
- pkg = matches[-1] # highest match
- if not self._add_pkg(pkg, dep):
- return 0
- if not self._create_graph(allow_unsatisfied=True):
- return 0
- return 1
-
- def _pkg(self, cpv, type_name, root_config, installed=False):
- """
- Get a package instance from the cache, or create a new
- one if necessary. Raises KeyError from aux_get if it
- failures for some reason (package does not exist or is
- corrupt).
- """
- operation = "merge"
- if installed:
- operation = "nomerge"
- pkg = self._pkg_cache.get(
- (type_name, root_config.root, cpv, operation))
- if pkg is None:
- tree_type = self.pkg_tree_map[type_name]
- db = root_config.trees[tree_type].dbapi
- db_keys = list(self._trees_orig[root_config.root][
- tree_type].dbapi._aux_cache_keys)
- try:
- metadata = izip(db_keys, db.aux_get(cpv, db_keys))
- except KeyError:
- raise portage.exception.PackageNotFound(cpv)
- pkg = Package(cpv=cpv, metadata=metadata,
- root_config=root_config, installed=installed)
- if type_name == "ebuild":
- settings = self.pkgsettings[root_config.root]
- settings.setcpv(pkg)
- pkg.metadata["USE"] = settings["PORTAGE_USE"]
- pkg.metadata['CHOST'] = settings.get('CHOST', '')
- self._pkg_cache[pkg] = pkg
- return pkg
-
- def validate_blockers(self):
- """Remove any blockers from the digraph that do not match any of the
- packages within the graph. If necessary, create hard deps to ensure
- correct merge order such that mutually blocking packages are never
- installed simultaneously."""
-
- if "--buildpkgonly" in self.myopts or \
- "--nodeps" in self.myopts:
- return True
-
- #if "deep" in self.myparams:
- if True:
- # Pull in blockers from all installed packages that haven't already
- # been pulled into the depgraph. This is not enabled by default
- # due to the performance penalty that is incurred by all the
- # additional dep_check calls that are required.
-
- dep_keys = ["DEPEND","RDEPEND","PDEPEND"]
- for myroot in self.trees:
- vardb = self.trees[myroot]["vartree"].dbapi
- portdb = self.trees[myroot]["porttree"].dbapi
- pkgsettings = self.pkgsettings[myroot]
- final_db = self.mydbapi[myroot]
-
- blocker_cache = BlockerCache(myroot, vardb)
- stale_cache = set(blocker_cache)
- for pkg in vardb:
- cpv = pkg.cpv
- stale_cache.discard(cpv)
- pkg_in_graph = self.digraph.contains(pkg)
-
- # Check for masked installed packages. Only warn about
- # packages that are in the graph in order to avoid warning
- # about those that will be automatically uninstalled during
- # the merge process or by --depclean.
- if pkg in final_db:
- if pkg_in_graph and not visible(pkgsettings, pkg):
- self._masked_installed.add(pkg)
-
- blocker_atoms = None
- blockers = None
- if pkg_in_graph:
- blockers = []
- try:
- blockers.extend(
- self._blocker_parents.child_nodes(pkg))
- except KeyError:
- pass
- try:
- blockers.extend(
- self._irrelevant_blockers.child_nodes(pkg))
- except KeyError:
- pass
- if blockers is not None:
- blockers = set(str(blocker.atom) \
- for blocker in blockers)
-
- # If this node has any blockers, create a "nomerge"
- # node for it so that they can be enforced.
- self.spinner.update()
- blocker_data = blocker_cache.get(cpv)
- if blocker_data is not None and \
- blocker_data.counter != long(pkg.metadata["COUNTER"]):
- blocker_data = None
-
- # If blocker data from the graph is available, use
- # it to validate the cache and update the cache if
- # it seems invalid.
- if blocker_data is not None and \
- blockers is not None:
- if not blockers.symmetric_difference(
- blocker_data.atoms):
- continue
- blocker_data = None
-
- if blocker_data is None and \
- blockers is not None:
- # Re-use the blockers from the graph.
- blocker_atoms = sorted(blockers)
- counter = long(pkg.metadata["COUNTER"])
- blocker_data = \
- blocker_cache.BlockerData(counter, blocker_atoms)
- blocker_cache[pkg.cpv] = blocker_data
- continue
-
- if blocker_data:
- blocker_atoms = blocker_data.atoms
- else:
- # Use aux_get() to trigger FakeVartree global
- # updates on *DEPEND when appropriate.
- depstr = " ".join(vardb.aux_get(pkg.cpv, dep_keys))
- # It is crucial to pass in final_db here in order to
- # optimize dep_check calls by eliminating atoms via
- # dep_wordreduce and dep_eval calls.
- try:
- portage.dep._dep_check_strict = False
- try:
- success, atoms = portage.dep_check(depstr,
- final_db, pkgsettings, myuse=pkg.use.enabled,
- trees=self._graph_trees, myroot=myroot)
- except Exception, e:
- if isinstance(e, SystemExit):
- raise
- # This is helpful, for example, if a ValueError
- # is thrown from cpv_expand due to multiple
- # matches (this can happen if an atom lacks a
- # category).
- show_invalid_depstring_notice(
- pkg, depstr, str(e))
- del e
- raise
- finally:
- portage.dep._dep_check_strict = True
- if not success:
- replacement_pkg = final_db.match_pkgs(pkg.slot_atom)
- if replacement_pkg and \
- replacement_pkg[0].operation == "merge":
- # This package is being replaced anyway, so
- # ignore invalid dependencies so as not to
- # annoy the user too much (otherwise they'd be
- # forced to manually unmerge it first).
- continue
- show_invalid_depstring_notice(pkg, depstr, atoms)
- return False
- blocker_atoms = [myatom for myatom in atoms \
- if myatom.startswith("!")]
- blocker_atoms.sort()
- counter = long(pkg.metadata["COUNTER"])
- blocker_cache[cpv] = \
- blocker_cache.BlockerData(counter, blocker_atoms)
- if blocker_atoms:
- try:
- for atom in blocker_atoms:
- blocker = Blocker(atom=portage.dep.Atom(atom),
- eapi=pkg.metadata["EAPI"], root=myroot)
- self._blocker_parents.add(blocker, pkg)
- except portage.exception.InvalidAtom, e:
- depstr = " ".join(vardb.aux_get(pkg.cpv, dep_keys))
- show_invalid_depstring_notice(
- pkg, depstr, "Invalid Atom: %s" % (e,))
- return False
- for cpv in stale_cache:
- del blocker_cache[cpv]
- blocker_cache.flush()
- del blocker_cache
-
- # Discard any "uninstall" tasks scheduled by previous calls
- # to this method, since those tasks may not make sense given
- # the current graph state.
- previous_uninstall_tasks = self._blocker_uninstalls.leaf_nodes()
- if previous_uninstall_tasks:
- self._blocker_uninstalls = digraph()
- self.digraph.difference_update(previous_uninstall_tasks)
-
- for blocker in self._blocker_parents.leaf_nodes():
- self.spinner.update()
- root_config = self.roots[blocker.root]
- virtuals = root_config.settings.getvirtuals()
- myroot = blocker.root
- initial_db = self.trees[myroot]["vartree"].dbapi
- final_db = self.mydbapi[myroot]
-
- provider_virtual = False
- if blocker.cp in virtuals and \
- not self._have_new_virt(blocker.root, blocker.cp):
- provider_virtual = True
-
- # Use this to check PROVIDE for each matched package
- # when necessary.
- atom_set = InternalPackageSet(
- initial_atoms=[blocker.atom])
-
- if provider_virtual:
- atoms = []
- for provider_entry in virtuals[blocker.cp]:
- provider_cp = \
- portage.dep_getkey(provider_entry)
- atoms.append(blocker.atom.replace(
- blocker.cp, provider_cp))
- else:
- atoms = [blocker.atom]
-
- blocked_initial = set()
- for atom in atoms:
- for pkg in initial_db.match_pkgs(atom):
- if atom_set.findAtomForPackage(pkg):
- blocked_initial.add(pkg)
-
- blocked_final = set()
- for atom in atoms:
- for pkg in final_db.match_pkgs(atom):
- if atom_set.findAtomForPackage(pkg):
- blocked_final.add(pkg)
-
- if not blocked_initial and not blocked_final:
- parent_pkgs = self._blocker_parents.parent_nodes(blocker)
- self._blocker_parents.remove(blocker)
- # Discard any parents that don't have any more blockers.
- for pkg in parent_pkgs:
- self._irrelevant_blockers.add(blocker, pkg)
- if not self._blocker_parents.child_nodes(pkg):
- self._blocker_parents.remove(pkg)
- continue
- for parent in self._blocker_parents.parent_nodes(blocker):
- unresolved_blocks = False
- depends_on_order = set()
- for pkg in blocked_initial:
- if pkg.slot_atom == parent.slot_atom:
- # TODO: Support blocks within slots in cases where it
- # might make sense. For example, a new version might
- # require that the old version be uninstalled at build
- # time.
- continue
- if parent.installed:
- # Two currently installed packages conflict with
- # eachother. Ignore this case since the damage
- # is already done and this would be likely to
- # confuse users if displayed like a normal blocker.
- continue
-
- self._blocked_pkgs.add(pkg, blocker)
-
- if parent.operation == "merge":
- # Maybe the blocked package can be replaced or simply
- # unmerged to resolve this block.
- depends_on_order.add((pkg, parent))
- continue
- # None of the above blocker resolutions techniques apply,
- # so apparently this one is unresolvable.
- unresolved_blocks = True
- for pkg in blocked_final:
- if pkg.slot_atom == parent.slot_atom:
- # TODO: Support blocks within slots.
- continue
- if parent.operation == "nomerge" and \
- pkg.operation == "nomerge":
- # This blocker will be handled the next time that a
- # merge of either package is triggered.
- continue
-
- self._blocked_pkgs.add(pkg, blocker)
-
- # Maybe the blocking package can be
- # unmerged to resolve this block.
- if parent.operation == "merge" and pkg.installed:
- depends_on_order.add((pkg, parent))
- continue
- elif parent.operation == "nomerge":
- depends_on_order.add((parent, pkg))
- continue
- # None of the above blocker resolutions techniques apply,
- # so apparently this one is unresolvable.
- unresolved_blocks = True
-
- # Make sure we don't unmerge any package that have been pulled
- # into the graph.
- if not unresolved_blocks and depends_on_order:
- for inst_pkg, inst_task in depends_on_order:
- if self.digraph.contains(inst_pkg) and \
- self.digraph.parent_nodes(inst_pkg):
- unresolved_blocks = True
- break
-
- if not unresolved_blocks and depends_on_order:
- for inst_pkg, inst_task in depends_on_order:
- uninst_task = Package(built=inst_pkg.built,
- cpv=inst_pkg.cpv, installed=inst_pkg.installed,
- metadata=inst_pkg.metadata,
- operation="uninstall",
- root_config=inst_pkg.root_config,
- type_name=inst_pkg.type_name)
- self._pkg_cache[uninst_task] = uninst_task
- # Enforce correct merge order with a hard dep.
- self.digraph.addnode(uninst_task, inst_task,
- priority=BlockerDepPriority.instance)
- # Count references to this blocker so that it can be
- # invalidated after nodes referencing it have been
- # merged.
- self._blocker_uninstalls.addnode(uninst_task, blocker)
- if not unresolved_blocks and not depends_on_order:
- self._irrelevant_blockers.add(blocker, parent)
- self._blocker_parents.remove_edge(blocker, parent)
- if not self._blocker_parents.parent_nodes(blocker):
- self._blocker_parents.remove(blocker)
- if not self._blocker_parents.child_nodes(parent):
- self._blocker_parents.remove(parent)
- if unresolved_blocks:
- self._unsolvable_blockers.add(blocker, parent)
-
- return True
-
- def _accept_blocker_conflicts(self):
- acceptable = False
- for x in ("--buildpkgonly", "--fetchonly",
- "--fetch-all-uri", "--nodeps"):
- if x in self.myopts:
- acceptable = True
- break
- return acceptable
-
- def _merge_order_bias(self, mygraph):
- """
- For optimal leaf node selection, promote deep system runtime deps and
- order nodes from highest to lowest overall reference count.
- """
-
- node_info = {}
- for node in mygraph.order:
- node_info[node] = len(mygraph.parent_nodes(node))
- deep_system_deps = _find_deep_system_runtime_deps(mygraph)
-
- def cmp_merge_preference(node1, node2):
-
- if node1.operation == 'uninstall':
- if node2.operation == 'uninstall':
- return 0
- return 1
-
- if node2.operation == 'uninstall':
- if node1.operation == 'uninstall':
- return 0
- return -1
-
- node1_sys = node1 in deep_system_deps
- node2_sys = node2 in deep_system_deps
- if node1_sys != node2_sys:
- if node1_sys:
- return -1
- return 1
-
- return node_info[node2] - node_info[node1]
-
- mygraph.order.sort(key=cmp_sort_key(cmp_merge_preference))
-
- def altlist(self, reversed=False):
-
- while self._serialized_tasks_cache is None:
- self._resolve_conflicts()
- try:
- self._serialized_tasks_cache, self._scheduler_graph = \
- self._serialize_tasks()
- except self._serialize_tasks_retry:
- pass
-
- retlist = self._serialized_tasks_cache[:]
- if reversed:
- retlist.reverse()
- return retlist
-
- def schedulerGraph(self):
- """
- The scheduler graph is identical to the normal one except that
- uninstall edges are reversed in specific cases that require
- conflicting packages to be temporarily installed simultaneously.
- This is intended for use by the Scheduler in it's parallelization
- logic. It ensures that temporary simultaneous installation of
- conflicting packages is avoided when appropriate (especially for
- !!atom blockers), but allowed in specific cases that require it.
-
- Note that this method calls break_refs() which alters the state of
- internal Package instances such that this depgraph instance should
- not be used to perform any more calculations.
- """
- if self._scheduler_graph is None:
- self.altlist()
- self.break_refs(self._scheduler_graph.order)
- return self._scheduler_graph
-
- def break_refs(self, nodes):
- """
- Take a mergelist like that returned from self.altlist() and
- break any references that lead back to the depgraph. This is
- useful if you want to hold references to packages without
- also holding the depgraph on the heap.
- """
- for node in nodes:
- if hasattr(node, "root_config"):
- # The FakeVartree references the _package_cache which
- # references the depgraph. So that Package instances don't
- # hold the depgraph and FakeVartree on the heap, replace
- # the RootConfig that references the FakeVartree with the
- # original RootConfig instance which references the actual
- # vartree.
- node.root_config = \
- self._trees_orig[node.root_config.root]["root_config"]
-
- def _resolve_conflicts(self):
- if not self._complete_graph():
- raise self._unknown_internal_error()
-
- if not self.validate_blockers():
- raise self._unknown_internal_error()
-
- if self._slot_collision_info:
- self._process_slot_conflicts()
-
- def _serialize_tasks(self):
-
- if "--debug" in self.myopts:
- writemsg("\ndigraph:\n\n", noiselevel=-1)
- self.digraph.debug_print()
- writemsg("\n", noiselevel=-1)
-
- scheduler_graph = self.digraph.copy()
-
- if '--nodeps' in self.myopts:
- # Preserve the package order given on the command line.
- return ([node for node in scheduler_graph \
- if isinstance(node, Package) \
- and node.operation == 'merge'], scheduler_graph)
-
- mygraph=self.digraph.copy()
- # Prune "nomerge" root nodes if nothing depends on them, since
- # otherwise they slow down merge order calculation. Don't remove
- # non-root nodes since they help optimize merge order in some cases
- # such as revdep-rebuild.
- removed_nodes = set()
- while True:
- for node in mygraph.root_nodes():
- if not isinstance(node, Package) or \
- node.installed or node.onlydeps:
- removed_nodes.add(node)
- if removed_nodes:
- self.spinner.update()
- mygraph.difference_update(removed_nodes)
- if not removed_nodes:
- break
- removed_nodes.clear()
- self._merge_order_bias(mygraph)
- def cmp_circular_bias(n1, n2):
- """
- RDEPEND is stronger than PDEPEND and this function
- measures such a strength bias within a circular
- dependency relationship.
- """
- n1_n2_medium = n2 in mygraph.child_nodes(n1,
- ignore_priority=priority_range.ignore_medium_soft)
- n2_n1_medium = n1 in mygraph.child_nodes(n2,
- ignore_priority=priority_range.ignore_medium_soft)
- if n1_n2_medium == n2_n1_medium:
- return 0
- elif n1_n2_medium:
- return 1
- return -1
- myblocker_uninstalls = self._blocker_uninstalls.copy()
- retlist=[]
- # Contains uninstall tasks that have been scheduled to
- # occur after overlapping blockers have been installed.
- scheduled_uninstalls = set()
- # Contains any Uninstall tasks that have been ignored
- # in order to avoid the circular deps code path. These
- # correspond to blocker conflicts that could not be
- # resolved.
- ignored_uninstall_tasks = set()
- have_uninstall_task = False
- complete = "complete" in self.myparams
- asap_nodes = []
-
- def get_nodes(**kwargs):
- """
- Returns leaf nodes excluding Uninstall instances
- since those should be executed as late as possible.
- """
- return [node for node in mygraph.leaf_nodes(**kwargs) \
- if isinstance(node, Package) and \
- (node.operation != "uninstall" or \
- node in scheduled_uninstalls)]
-
- # sys-apps/portage needs special treatment if ROOT="/"
- running_root = self._running_root.root
- from portage.const import PORTAGE_PACKAGE_ATOM
- runtime_deps = InternalPackageSet(
- initial_atoms=[PORTAGE_PACKAGE_ATOM])
- running_portage = self.trees[running_root]["vartree"].dbapi.match_pkgs(
- PORTAGE_PACKAGE_ATOM)
- replacement_portage = self.mydbapi[running_root].match_pkgs(
- PORTAGE_PACKAGE_ATOM)
-
- if running_portage:
- running_portage = running_portage[0]
- else:
- running_portage = None
-
- if replacement_portage:
- replacement_portage = replacement_portage[0]
- else:
- replacement_portage = None
-
- if replacement_portage == running_portage:
- replacement_portage = None
-
- if replacement_portage is not None:
- # update from running_portage to replacement_portage asap
- asap_nodes.append(replacement_portage)
-
- if running_portage is not None:
- try:
- portage_rdepend = self._select_atoms_highest_available(
- running_root, running_portage.metadata["RDEPEND"],
- myuse=running_portage.use.enabled,
- parent=running_portage, strict=False)
- except portage.exception.InvalidDependString, e:
- portage.writemsg("!!! Invalid RDEPEND in " + \
- "'%svar/db/pkg/%s/RDEPEND': %s\n" % \
- (running_root, running_portage.cpv, e), noiselevel=-1)
- del e
- portage_rdepend = []
- runtime_deps.update(atom for atom in portage_rdepend \
- if not atom.startswith("!"))
-
- def gather_deps(ignore_priority, mergeable_nodes,
- selected_nodes, node):
- """
- Recursively gather a group of nodes that RDEPEND on
- eachother. This ensures that they are merged as a group
- and get their RDEPENDs satisfied as soon as possible.
- """
- if node in selected_nodes:
- return True
- if node not in mergeable_nodes:
- return False
- if node == replacement_portage and \
- mygraph.child_nodes(node,
- ignore_priority=priority_range.ignore_medium_soft):
- # Make sure that portage always has all of it's
- # RDEPENDs installed first.
- return False
- selected_nodes.add(node)
- for child in mygraph.child_nodes(node,
- ignore_priority=ignore_priority):
- if not gather_deps(ignore_priority,
- mergeable_nodes, selected_nodes, child):
- return False
- return True
-
- def ignore_uninst_or_med(priority):
- if priority is BlockerDepPriority.instance:
- return True
- return priority_range.ignore_medium(priority)
-
- def ignore_uninst_or_med_soft(priority):
- if priority is BlockerDepPriority.instance:
- return True
- return priority_range.ignore_medium_soft(priority)
-
- tree_mode = "--tree" in self.myopts
- # Tracks whether or not the current iteration should prefer asap_nodes
- # if available. This is set to False when the previous iteration
- # failed to select any nodes. It is reset whenever nodes are
- # successfully selected.
- prefer_asap = True
-
- # Controls whether or not the current iteration should drop edges that
- # are "satisfied" by installed packages, in order to solve circular
- # dependencies. The deep runtime dependencies of installed packages are
- # not checked in this case (bug #199856), so it must be avoided
- # whenever possible.
- drop_satisfied = False
-
- # State of variables for successive iterations that loosen the
- # criteria for node selection.
- #
- # iteration prefer_asap drop_satisfied
- # 1 True False
- # 2 False False
- # 3 False True
- #
- # If no nodes are selected on the last iteration, it is due to
- # unresolved blockers or circular dependencies.
-
- while not mygraph.empty():
- self.spinner.update()
- selected_nodes = None
- ignore_priority = None
- if drop_satisfied or (prefer_asap and asap_nodes):
- priority_range = DepPrioritySatisfiedRange
- else:
- priority_range = DepPriorityNormalRange
- if prefer_asap and asap_nodes:
- # ASAP nodes are merged before their soft deps. Go ahead and
- # select root nodes here if necessary, since it's typical for
- # the parent to have been removed from the graph already.
- asap_nodes = [node for node in asap_nodes \
- if mygraph.contains(node)]
- for node in asap_nodes:
- if not mygraph.child_nodes(node,
- ignore_priority=priority_range.ignore_soft):
- selected_nodes = [node]
- asap_nodes.remove(node)
- break
- if not selected_nodes and \
- not (prefer_asap and asap_nodes):
- for i in xrange(priority_range.NONE,
- priority_range.MEDIUM_SOFT + 1):
- ignore_priority = priority_range.ignore_priority[i]
- nodes = get_nodes(ignore_priority=ignore_priority)
- if nodes:
- # If there is a mix of uninstall nodes with other
- # types, save the uninstall nodes for later since
- # sometimes a merge node will render an uninstall
- # node unnecessary (due to occupying the same slot),
- # and we want to avoid executing a separate uninstall
- # task in that case.
- if len(nodes) > 1:
- good_uninstalls = []
- with_some_uninstalls_excluded = []
- for node in nodes:
- if node.operation == "uninstall":
- slot_node = self.mydbapi[node.root
- ].match_pkgs(node.slot_atom)
- if slot_node and \
- slot_node[0].operation == "merge":
- continue
- good_uninstalls.append(node)
- with_some_uninstalls_excluded.append(node)
- if good_uninstalls:
- nodes = good_uninstalls
- elif with_some_uninstalls_excluded:
- nodes = with_some_uninstalls_excluded
- else:
- nodes = nodes
-
- if ignore_priority is None and not tree_mode:
- # Greedily pop all of these nodes since no
- # relationship has been ignored. This optimization
- # destroys --tree output, so it's disabled in tree
- # mode.
- selected_nodes = nodes
- else:
- # For optimal merge order:
- # * Only pop one node.
- # * Removing a root node (node without a parent)
- # will not produce a leaf node, so avoid it.
- # * It's normal for a selected uninstall to be a
- # root node, so don't check them for parents.
- for node in nodes:
- if node.operation == "uninstall" or \
- mygraph.parent_nodes(node):
- selected_nodes = [node]
- break
-
- if selected_nodes:
- break
-
- if not selected_nodes:
- nodes = get_nodes(ignore_priority=priority_range.ignore_medium)
- if nodes:
- mergeable_nodes = set(nodes)
- if prefer_asap and asap_nodes:
- nodes = asap_nodes
- for i in xrange(priority_range.SOFT,
- priority_range.MEDIUM_SOFT + 1):
- ignore_priority = priority_range.ignore_priority[i]
- for node in nodes:
- if not mygraph.parent_nodes(node):
- continue
- selected_nodes = set()
- if gather_deps(ignore_priority,
- mergeable_nodes, selected_nodes, node):
- break
- else:
- selected_nodes = None
- if selected_nodes:
- break
-
- if prefer_asap and asap_nodes and not selected_nodes:
- # We failed to find any asap nodes to merge, so ignore
- # them for the next iteration.
- prefer_asap = False
- continue
-
- if selected_nodes and ignore_priority is not None:
- # Try to merge ignored medium_soft deps as soon as possible
- # if they're not satisfied by installed packages.
- for node in selected_nodes:
- children = set(mygraph.child_nodes(node))
- soft = children.difference(
- mygraph.child_nodes(node,
- ignore_priority=DepPrioritySatisfiedRange.ignore_soft))
- medium_soft = children.difference(
- mygraph.child_nodes(node,
- ignore_priority = \
- DepPrioritySatisfiedRange.ignore_medium_soft))
- medium_soft.difference_update(soft)
- for child in medium_soft:
- if child in selected_nodes:
- continue
- if child in asap_nodes:
- continue
- asap_nodes.append(child)
-
- if selected_nodes and len(selected_nodes) > 1:
- if not isinstance(selected_nodes, list):
- selected_nodes = list(selected_nodes)
- selected_nodes.sort(key=cmp_sort_key(cmp_circular_bias))
-
- if not selected_nodes and not myblocker_uninstalls.is_empty():
- # An Uninstall task needs to be executed in order to
- # avoid conflict if possible.
-
- if drop_satisfied:
- priority_range = DepPrioritySatisfiedRange
- else:
- priority_range = DepPriorityNormalRange
-
- mergeable_nodes = get_nodes(
- ignore_priority=ignore_uninst_or_med)
-
- min_parent_deps = None
- uninst_task = None
- for task in myblocker_uninstalls.leaf_nodes():
- # Do some sanity checks so that system or world packages
- # don't get uninstalled inappropriately here (only really
- # necessary when --complete-graph has not been enabled).
-
- if task in ignored_uninstall_tasks:
- continue
-
- if task in scheduled_uninstalls:
- # It's been scheduled but it hasn't
- # been executed yet due to dependence
- # on installation of blocking packages.
- continue
-
- root_config = self.roots[task.root]
- inst_pkg = self._pkg_cache[
- ("installed", task.root, task.cpv, "nomerge")]
-
- if self.digraph.contains(inst_pkg):
- continue
-
- forbid_overlap = False
- heuristic_overlap = False
- for blocker in myblocker_uninstalls.parent_nodes(task):
- if blocker.eapi in ("0", "1"):
- heuristic_overlap = True
- elif blocker.atom.blocker.overlap.forbid:
- forbid_overlap = True
- break
- if forbid_overlap and running_root == task.root:
- continue
-
- if heuristic_overlap and running_root == task.root:
- # Never uninstall sys-apps/portage or it's essential
- # dependencies, except through replacement.
- try:
- runtime_dep_atoms = \
- list(runtime_deps.iterAtomsForPackage(task))
- except portage.exception.InvalidDependString, e:
- portage.writemsg("!!! Invalid PROVIDE in " + \
- "'%svar/db/pkg/%s/PROVIDE': %s\n" % \
- (task.root, task.cpv, e), noiselevel=-1)
- del e
- continue
-
- # Don't uninstall a runtime dep if it appears
- # to be the only suitable one installed.
- skip = False
- vardb = root_config.trees["vartree"].dbapi
- for atom in runtime_dep_atoms:
- other_version = None
- for pkg in vardb.match_pkgs(atom):
- if pkg.cpv == task.cpv and \
- pkg.metadata["COUNTER"] == \
- task.metadata["COUNTER"]:
- continue
- other_version = pkg
- break
- if other_version is None:
- skip = True
- break
- if skip:
- continue
-
- # For packages in the system set, don't take
- # any chances. If the conflict can't be resolved
- # by a normal replacement operation then abort.
- skip = False
- try:
- for atom in root_config.sets[
- "system"].iterAtomsForPackage(task):
- skip = True
- break
- except portage.exception.InvalidDependString, e:
- portage.writemsg("!!! Invalid PROVIDE in " + \
- "'%svar/db/pkg/%s/PROVIDE': %s\n" % \
- (task.root, task.cpv, e), noiselevel=-1)
- del e
- skip = True
- if skip:
- continue
-
- # Note that the world check isn't always
- # necessary since self._complete_graph() will
- # add all packages from the system and world sets to the
- # graph. This just allows unresolved conflicts to be
- # detected as early as possible, which makes it possible
- # to avoid calling self._complete_graph() when it is
- # unnecessary due to blockers triggering an abortion.
- if not complete:
- # For packages in the world set, go ahead an uninstall
- # when necessary, as long as the atom will be satisfied
- # in the final state.
- graph_db = self.mydbapi[task.root]
- skip = False
- try:
- for atom in root_config.sets[
- "world"].iterAtomsForPackage(task):
- satisfied = False
- for pkg in graph_db.match_pkgs(atom):
- if pkg == inst_pkg:
- continue
- satisfied = True
- break
- if not satisfied:
- skip = True
- self._blocked_world_pkgs[inst_pkg] = atom
- break
- except portage.exception.InvalidDependString, e:
- portage.writemsg("!!! Invalid PROVIDE in " + \
- "'%svar/db/pkg/%s/PROVIDE': %s\n" % \
- (task.root, task.cpv, e), noiselevel=-1)
- del e
- skip = True
- if skip:
- continue
-
- # Check the deps of parent nodes to ensure that
- # the chosen task produces a leaf node. Maybe
- # this can be optimized some more to make the
- # best possible choice, but the current algorithm
- # is simple and should be near optimal for most
- # common cases.
- mergeable_parent = False
- parent_deps = set()
- for parent in mygraph.parent_nodes(task):
- parent_deps.update(mygraph.child_nodes(parent,
- ignore_priority=priority_range.ignore_medium_soft))
- if parent in mergeable_nodes and \
- gather_deps(ignore_uninst_or_med_soft,
- mergeable_nodes, set(), parent):
- mergeable_parent = True
-
- if not mergeable_parent:
- continue
-
- parent_deps.remove(task)
- if min_parent_deps is None or \
- len(parent_deps) < min_parent_deps:
- min_parent_deps = len(parent_deps)
- uninst_task = task
-
- if uninst_task is not None:
- # The uninstall is performed only after blocking
- # packages have been merged on top of it. File
- # collisions between blocking packages are detected
- # and removed from the list of files to be uninstalled.
- scheduled_uninstalls.add(uninst_task)
- parent_nodes = mygraph.parent_nodes(uninst_task)
-
- # Reverse the parent -> uninstall edges since we want
- # to do the uninstall after blocking packages have
- # been merged on top of it.
- mygraph.remove(uninst_task)
- for blocked_pkg in parent_nodes:
- mygraph.add(blocked_pkg, uninst_task,
- priority=BlockerDepPriority.instance)
- scheduler_graph.remove_edge(uninst_task, blocked_pkg)
- scheduler_graph.add(blocked_pkg, uninst_task,
- priority=BlockerDepPriority.instance)
-
- # Reset the state variables for leaf node selection and
- # continue trying to select leaf nodes.
- prefer_asap = True
- drop_satisfied = False
- continue
-
- if not selected_nodes:
- # Only select root nodes as a last resort. This case should
- # only trigger when the graph is nearly empty and the only
- # remaining nodes are isolated (no parents or children). Since
- # the nodes must be isolated, ignore_priority is not needed.
- selected_nodes = get_nodes()
-
- if not selected_nodes and not drop_satisfied:
- drop_satisfied = True
- continue
-
- if not selected_nodes and not myblocker_uninstalls.is_empty():
- # If possible, drop an uninstall task here in order to avoid
- # the circular deps code path. The corresponding blocker will
- # still be counted as an unresolved conflict.
- uninst_task = None
- for node in myblocker_uninstalls.leaf_nodes():
- try:
- mygraph.remove(node)
- except KeyError:
- pass
- else:
- uninst_task = node
- ignored_uninstall_tasks.add(node)
- break
-
- if uninst_task is not None:
- # Reset the state variables for leaf node selection and
- # continue trying to select leaf nodes.
- prefer_asap = True
- drop_satisfied = False
- continue
-
- if not selected_nodes:
- self._circular_deps_for_display = mygraph
- raise self._unknown_internal_error()
-
- # At this point, we've succeeded in selecting one or more nodes, so
- # reset state variables for leaf node selection.
- prefer_asap = True
- drop_satisfied = False
-
- mygraph.difference_update(selected_nodes)
-
- for node in selected_nodes:
- if isinstance(node, Package) and \
- node.operation == "nomerge":
- continue
-
- # Handle interactions between blockers
- # and uninstallation tasks.
- solved_blockers = set()
- uninst_task = None
- if isinstance(node, Package) and \
- "uninstall" == node.operation:
- have_uninstall_task = True
- uninst_task = node
- else:
- vardb = self.trees[node.root]["vartree"].dbapi
- previous_cpv = vardb.match(node.slot_atom)
- if previous_cpv:
- # The package will be replaced by this one, so remove
- # the corresponding Uninstall task if necessary.
- previous_cpv = previous_cpv[0]
- uninst_task = \
- ("installed", node.root, previous_cpv, "uninstall")
- try:
- mygraph.remove(uninst_task)
- except KeyError:
- pass
-
- if uninst_task is not None and \
- uninst_task not in ignored_uninstall_tasks and \
- myblocker_uninstalls.contains(uninst_task):
- blocker_nodes = myblocker_uninstalls.parent_nodes(uninst_task)
- myblocker_uninstalls.remove(uninst_task)
- # Discard any blockers that this Uninstall solves.
- for blocker in blocker_nodes:
- if not myblocker_uninstalls.child_nodes(blocker):
- myblocker_uninstalls.remove(blocker)
- solved_blockers.add(blocker)
-
- retlist.append(node)
-
- if (isinstance(node, Package) and \
- "uninstall" == node.operation) or \
- (uninst_task is not None and \
- uninst_task in scheduled_uninstalls):
- # Include satisfied blockers in the merge list
- # since the user might be interested and also
- # it serves as an indicator that blocking packages
- # will be temporarily installed simultaneously.
- for blocker in solved_blockers:
- retlist.append(Blocker(atom=blocker.atom,
- root=blocker.root, eapi=blocker.eapi,
- satisfied=True))
-
- unsolvable_blockers = set(self._unsolvable_blockers.leaf_nodes())
- for node in myblocker_uninstalls.root_nodes():
- unsolvable_blockers.add(node)
-
- for blocker in unsolvable_blockers:
- retlist.append(blocker)
-
- # If any Uninstall tasks need to be executed in order
- # to avoid a conflict, complete the graph with any
- # dependencies that may have been initially
- # neglected (to ensure that unsafe Uninstall tasks
- # are properly identified and blocked from execution).
- if have_uninstall_task and \
- not complete and \
- not unsolvable_blockers:
- self.myparams.add("complete")
- raise self._serialize_tasks_retry("")
-
- if unsolvable_blockers and \
- not self._accept_blocker_conflicts():
- self._unsatisfied_blockers_for_display = unsolvable_blockers
- self._serialized_tasks_cache = retlist[:]
- self._scheduler_graph = scheduler_graph
- raise self._unknown_internal_error()
-
- if self._slot_collision_info and \
- not self._accept_blocker_conflicts():
- self._serialized_tasks_cache = retlist[:]
- self._scheduler_graph = scheduler_graph
- raise self._unknown_internal_error()
-
- return retlist, scheduler_graph
-
- def _show_circular_deps(self, mygraph):
- # No leaf nodes are available, so we have a circular
- # dependency panic situation. Reduce the noise level to a
- # minimum via repeated elimination of root nodes since they
- # have no parents and thus can not be part of a cycle.
- while True:
- root_nodes = mygraph.root_nodes(
- ignore_priority=DepPrioritySatisfiedRange.ignore_medium_soft)
- if not root_nodes:
- break
- mygraph.difference_update(root_nodes)
- # Display the USE flags that are enabled on nodes that are part
- # of dependency cycles in case that helps the user decide to
- # disable some of them.
- display_order = []
- tempgraph = mygraph.copy()
- while not tempgraph.empty():
- nodes = tempgraph.leaf_nodes()
- if not nodes:
- node = tempgraph.order[0]
- else:
- node = nodes[0]
- display_order.append(node)
- tempgraph.remove(node)
- display_order.reverse()
- self.myopts.pop("--quiet", None)
- self.myopts.pop("--verbose", None)
- self.myopts["--tree"] = True
- portage.writemsg("\n\n", noiselevel=-1)
- self.display(display_order)
- prefix = colorize("BAD", " * ")
- portage.writemsg("\n", noiselevel=-1)
- portage.writemsg(prefix + "Error: circular dependencies:\n",
- noiselevel=-1)
- portage.writemsg("\n", noiselevel=-1)
- mygraph.debug_print()
- portage.writemsg("\n", noiselevel=-1)
- portage.writemsg(prefix + "Note that circular dependencies " + \
- "can often be avoided by temporarily\n", noiselevel=-1)
- portage.writemsg(prefix + "disabling USE flags that trigger " + \
- "optional dependencies.\n", noiselevel=-1)
-
- def _show_merge_list(self):
- if self._serialized_tasks_cache is not None and \
- not (self._displayed_list and \
- (self._displayed_list == self._serialized_tasks_cache or \
- self._displayed_list == \
- list(reversed(self._serialized_tasks_cache)))):
- display_list = self._serialized_tasks_cache[:]
- if "--tree" in self.myopts:
- display_list.reverse()
- self.display(display_list)
-
- def _show_unsatisfied_blockers(self, blockers):
- self._show_merge_list()
- msg = "Error: The above package list contains " + \
- "packages which cannot be installed " + \
- "at the same time on the same system."
- prefix = colorize("BAD", " * ")
- from textwrap import wrap
- portage.writemsg("\n", noiselevel=-1)
- for line in wrap(msg, 70):
- portage.writemsg(prefix + line + "\n", noiselevel=-1)
-
- # Display the conflicting packages along with the packages
- # that pulled them in. This is helpful for troubleshooting
- # cases in which blockers don't solve automatically and
- # the reasons are not apparent from the normal merge list
- # display.
-
- conflict_pkgs = {}
- for blocker in blockers:
- for pkg in chain(self._blocked_pkgs.child_nodes(blocker), \
- self._blocker_parents.parent_nodes(blocker)):
- parent_atoms = self._parent_atoms.get(pkg)
- if not parent_atoms:
- atom = self._blocked_world_pkgs.get(pkg)
- if atom is not None:
- parent_atoms = set([("@world", atom)])
- if parent_atoms:
- conflict_pkgs[pkg] = parent_atoms
-
- if conflict_pkgs:
- # Reduce noise by pruning packages that are only
- # pulled in by other conflict packages.
- pruned_pkgs = set()
- for pkg, parent_atoms in conflict_pkgs.iteritems():
- relevant_parent = False
- for parent, atom in parent_atoms:
- if parent not in conflict_pkgs:
- relevant_parent = True
- break
- if not relevant_parent:
- pruned_pkgs.add(pkg)
- for pkg in pruned_pkgs:
- del conflict_pkgs[pkg]
-
- if conflict_pkgs:
- msg = []
- msg.append("\n")
- indent = " "
- # Max number of parents shown, to avoid flooding the display.
- max_parents = 3
- for pkg, parent_atoms in conflict_pkgs.iteritems():
-
- pruned_list = set()
-
- # Prefer packages that are not directly involved in a conflict.
- for parent_atom in parent_atoms:
- if len(pruned_list) >= max_parents:
- break
- parent, atom = parent_atom
- if parent not in conflict_pkgs:
- pruned_list.add(parent_atom)
-
- for parent_atom in parent_atoms:
- if len(pruned_list) >= max_parents:
- break
- pruned_list.add(parent_atom)
-
- omitted_parents = len(parent_atoms) - len(pruned_list)
- msg.append(indent + "%s pulled in by\n" % pkg)
-
- for parent_atom in pruned_list:
- parent, atom = parent_atom
- msg.append(2*indent)
- if isinstance(parent,
- (PackageArg, AtomArg)):
- # For PackageArg and AtomArg types, it's
- # redundant to display the atom attribute.
- msg.append(str(parent))
- else:
- # Display the specific atom from SetArg or
- # Package types.
- msg.append("%s required by %s" % (atom, parent))
- msg.append("\n")
-
- if omitted_parents:
- msg.append(2*indent)
- msg.append("(and %d more)\n" % omitted_parents)
-
- msg.append("\n")
-
- sys.stderr.write("".join(msg))
- sys.stderr.flush()
-
- if "--quiet" not in self.myopts:
- show_blocker_docs_link()
-
- def display(self, mylist, favorites=[], verbosity=None):
-
- # This is used to prevent display_problems() from
- # redundantly displaying this exact same merge list
- # again via _show_merge_list().
- self._displayed_list = mylist
-
- if verbosity is None:
- verbosity = ("--quiet" in self.myopts and 1 or \
- "--verbose" in self.myopts and 3 or 2)
- favorites_set = InternalPackageSet(favorites)
- oneshot = "--oneshot" in self.myopts or \
- "--onlydeps" in self.myopts
- columns = "--columns" in self.myopts
- changelogs=[]
- p=[]
- blockers = []
-
- counters = PackageCounters()
-
- if verbosity == 1 and "--verbose" not in self.myopts:
- def create_use_string(*args):
- return ""
- else:
- def create_use_string(name, cur_iuse, iuse_forced, cur_use,
- old_iuse, old_use,
- is_new, reinst_flags,
- all_flags=(verbosity == 3 or "--quiet" in self.myopts),
- alphabetical=("--alphabetical" in self.myopts)):
- enabled = []
- if alphabetical:
- disabled = enabled
- removed = enabled
- else:
- disabled = []
- removed = []
- cur_iuse = set(cur_iuse)
- enabled_flags = cur_iuse.intersection(cur_use)
- removed_iuse = set(old_iuse).difference(cur_iuse)
- any_iuse = cur_iuse.union(old_iuse)
- any_iuse = list(any_iuse)
- any_iuse.sort()
- for flag in any_iuse:
- flag_str = None
- isEnabled = False
- reinst_flag = reinst_flags and flag in reinst_flags
- if flag in enabled_flags:
- isEnabled = True
- if is_new or flag in old_use and \
- (all_flags or reinst_flag):
- flag_str = red(flag)
- elif flag not in old_iuse:
- flag_str = yellow(flag) + "%*"
- elif flag not in old_use:
- flag_str = green(flag) + "*"
- elif flag in removed_iuse:
- if all_flags or reinst_flag:
- flag_str = yellow("-" + flag) + "%"
- if flag in old_use:
- flag_str += "*"
- flag_str = "(" + flag_str + ")"
- removed.append(flag_str)
- continue
- else:
- if is_new or flag in old_iuse and \
- flag not in old_use and \
- (all_flags or reinst_flag):
- flag_str = blue("-" + flag)
- elif flag not in old_iuse:
- flag_str = yellow("-" + flag)
- if flag not in iuse_forced:
- flag_str += "%"
- elif flag in old_use:
- flag_str = green("-" + flag) + "*"
- if flag_str:
- if flag in iuse_forced:
- flag_str = "(" + flag_str + ")"
- if isEnabled:
- enabled.append(flag_str)
- else:
- disabled.append(flag_str)
-
- if alphabetical:
- ret = " ".join(enabled)
- else:
- ret = " ".join(enabled + disabled + removed)
- if ret:
- ret = '%s="%s" ' % (name, ret)
- return ret
-
- repo_display = RepoDisplay(self.roots)
-
- tree_nodes = []
- display_list = []
- mygraph = self.digraph.copy()
-
- # If there are any Uninstall instances, add the corresponding
- # blockers to the digraph (useful for --tree display).
-
- executed_uninstalls = set(node for node in mylist \
- if isinstance(node, Package) and node.operation == "unmerge")
-
- for uninstall in self._blocker_uninstalls.leaf_nodes():
- uninstall_parents = \
- self._blocker_uninstalls.parent_nodes(uninstall)
- if not uninstall_parents:
- continue
-
- # Remove the corresponding "nomerge" node and substitute
- # the Uninstall node.
- inst_pkg = self._pkg_cache[
- ("installed", uninstall.root, uninstall.cpv, "nomerge")]
- try:
- mygraph.remove(inst_pkg)
- except KeyError:
- pass
-
- try:
- inst_pkg_blockers = self._blocker_parents.child_nodes(inst_pkg)
- except KeyError:
- inst_pkg_blockers = []
-
- # Break the Package -> Uninstall edges.
- mygraph.remove(uninstall)
-
- # Resolution of a package's blockers
- # depend on it's own uninstallation.
- for blocker in inst_pkg_blockers:
- mygraph.add(uninstall, blocker)
-
- # Expand Package -> Uninstall edges into
- # Package -> Blocker -> Uninstall edges.
- for blocker in uninstall_parents:
- mygraph.add(uninstall, blocker)
- for parent in self._blocker_parents.parent_nodes(blocker):
- if parent != inst_pkg:
- mygraph.add(blocker, parent)
-
- # If the uninstall task did not need to be executed because
- # of an upgrade, display Blocker -> Upgrade edges since the
- # corresponding Blocker -> Uninstall edges will not be shown.
- upgrade_node = \
- self._slot_pkg_map[uninstall.root].get(uninstall.slot_atom)
- if upgrade_node is not None and \
- uninstall not in executed_uninstalls:
- for blocker in uninstall_parents:
- mygraph.add(upgrade_node, blocker)
-
- unsatisfied_blockers = []
- i = 0
- depth = 0
- shown_edges = set()
- for x in mylist:
- if isinstance(x, Blocker) and not x.satisfied:
- unsatisfied_blockers.append(x)
- continue
- graph_key = x
- if "--tree" in self.myopts:
- depth = len(tree_nodes)
- while depth and graph_key not in \
- mygraph.child_nodes(tree_nodes[depth-1]):
- depth -= 1
- if depth:
- tree_nodes = tree_nodes[:depth]
- tree_nodes.append(graph_key)
- display_list.append((x, depth, True))
- shown_edges.add((graph_key, tree_nodes[depth-1]))
- else:
- traversed_nodes = set() # prevent endless circles
- traversed_nodes.add(graph_key)
- def add_parents(current_node, ordered):
- parent_nodes = None
- # Do not traverse to parents if this node is an
- # an argument or a direct member of a set that has
- # been specified as an argument (system or world).
- if current_node not in self._set_nodes:
- parent_nodes = mygraph.parent_nodes(current_node)
- if parent_nodes:
- child_nodes = set(mygraph.child_nodes(current_node))
- selected_parent = None
- # First, try to avoid a direct cycle.
- for node in parent_nodes:
- if not isinstance(node, (Blocker, Package)):
- continue
- if node not in traversed_nodes and \
- node not in child_nodes:
- edge = (current_node, node)
- if edge in shown_edges:
- continue
- selected_parent = node
- break
- if not selected_parent:
- # A direct cycle is unavoidable.
- for node in parent_nodes:
- if not isinstance(node, (Blocker, Package)):
- continue
- if node not in traversed_nodes:
- edge = (current_node, node)
- if edge in shown_edges:
- continue
- selected_parent = node
- break
- if selected_parent:
- shown_edges.add((current_node, selected_parent))
- traversed_nodes.add(selected_parent)
- add_parents(selected_parent, False)
- display_list.append((current_node,
- len(tree_nodes), ordered))
- tree_nodes.append(current_node)
- tree_nodes = []
- add_parents(graph_key, True)
- else:
- display_list.append((x, depth, True))
- mylist = display_list
- for x in unsatisfied_blockers:
- mylist.append((x, 0, True))
-
- last_merge_depth = 0
- for i in xrange(len(mylist)-1,-1,-1):
- graph_key, depth, ordered = mylist[i]
- if not ordered and depth == 0 and i > 0 \
- and graph_key == mylist[i-1][0] and \
- mylist[i-1][1] == 0:
- # An ordered node got a consecutive duplicate when the tree was
- # being filled in.
- del mylist[i]
- continue
- if ordered and graph_key[-1] != "nomerge":
- last_merge_depth = depth
- continue
- if depth >= last_merge_depth or \
- i < len(mylist) - 1 and \
- depth >= mylist[i+1][1]:
- del mylist[i]
-
- from portage import flatten
- from portage.dep import use_reduce, paren_reduce
- # files to fetch list - avoids counting a same file twice
- # in size display (verbose mode)
- myfetchlist=[]
-
- # Use this set to detect when all the "repoadd" strings are "[0]"
- # and disable the entire repo display in this case.
- repoadd_set = set()
-
- for mylist_index in xrange(len(mylist)):
- x, depth, ordered = mylist[mylist_index]
- pkg_type = x[0]
- myroot = x[1]
- pkg_key = x[2]
- portdb = self.trees[myroot]["porttree"].dbapi
- bindb = self.trees[myroot]["bintree"].dbapi
- vardb = self.trees[myroot]["vartree"].dbapi
- vartree = self.trees[myroot]["vartree"]
- pkgsettings = self.pkgsettings[myroot]
-
- fetch=" "
- indent = " " * depth
-
- if isinstance(x, Blocker):
- if x.satisfied:
- blocker_style = "PKG_BLOCKER_SATISFIED"
- addl = "%s %s " % (colorize(blocker_style, "b"), fetch)
- else:
- blocker_style = "PKG_BLOCKER"
- addl = "%s %s " % (colorize(blocker_style, "B"), fetch)
- if ordered:
- counters.blocks += 1
- if x.satisfied:
- counters.blocks_satisfied += 1
- resolved = portage.key_expand(
- str(x.atom).lstrip("!"), mydb=vardb, settings=pkgsettings)
- if "--columns" in self.myopts and "--quiet" in self.myopts:
- addl += " " + colorize(blocker_style, resolved)
- else:
- addl = "[%s %s] %s%s" % \
- (colorize(blocker_style, "blocks"),
- addl, indent, colorize(blocker_style, resolved))
- block_parents = self._blocker_parents.parent_nodes(x)
- block_parents = set([pnode[2] for pnode in block_parents])
- block_parents = ", ".join(block_parents)
- if resolved!=x[2]:
- addl += colorize(blocker_style,
- " (\"%s\" is blocking %s)") % \
- (str(x.atom).lstrip("!"), block_parents)
- else:
- addl += colorize(blocker_style,
- " (is blocking %s)") % block_parents
- if isinstance(x, Blocker) and x.satisfied:
- if columns:
- continue
- p.append(addl)
- else:
- blockers.append(addl)
- else:
- pkg_status = x[3]
- pkg_merge = ordered and pkg_status == "merge"
- if not pkg_merge and pkg_status == "merge":
- pkg_status = "nomerge"
- built = pkg_type != "ebuild"
- installed = pkg_type == "installed"
- pkg = x
- metadata = pkg.metadata
- ebuild_path = None
- repo_name = metadata["repository"]
- if pkg_type == "ebuild":
- ebuild_path = portdb.findname(pkg_key)
- if not ebuild_path: # shouldn't happen
- raise portage.exception.PackageNotFound(pkg_key)
- repo_path_real = os.path.dirname(os.path.dirname(
- os.path.dirname(ebuild_path)))
- else:
- repo_path_real = portdb.getRepositoryPath(repo_name)
- pkg_use = list(pkg.use.enabled)
- try:
- restrict = flatten(use_reduce(paren_reduce(
- pkg.metadata["RESTRICT"]), uselist=pkg_use))
- except portage.exception.InvalidDependString, e:
- if not pkg.installed:
- show_invalid_depstring_notice(x,
- pkg.metadata["RESTRICT"], str(e))
- del e
- return 1
- restrict = []
- if "ebuild" == pkg_type and x[3] != "nomerge" and \
- "fetch" in restrict:
- fetch = red("F")
- if ordered:
- counters.restrict_fetch += 1
- if portdb.fetch_check(pkg_key, pkg_use):
- fetch = green("f")
- if ordered:
- counters.restrict_fetch_satisfied += 1
-
- #we need to use "--emptrytree" testing here rather than "empty" param testing because "empty"
- #param is used for -u, where you still *do* want to see when something is being upgraded.
- myoldbest = []
- myinslotlist = None
- installed_versions = vardb.match(portage.cpv_getkey(pkg_key))
- if vardb.cpv_exists(pkg_key):
- addl=" "+yellow("R")+fetch+" "
- if ordered:
- if pkg_merge:
- counters.reinst += 1
- elif pkg_status == "uninstall":
- counters.uninst += 1
- # filter out old-style virtual matches
- elif installed_versions and \
- portage.cpv_getkey(installed_versions[0]) == \
- portage.cpv_getkey(pkg_key):
- myinslotlist = vardb.match(pkg.slot_atom)
- # If this is the first install of a new-style virtual, we
- # need to filter out old-style virtual matches.
- if myinslotlist and \
- portage.cpv_getkey(myinslotlist[0]) != \
- portage.cpv_getkey(pkg_key):
- myinslotlist = None
- if myinslotlist:
- myoldbest = myinslotlist[:]
- addl = " " + fetch
- if not portage.dep.cpvequal(pkg_key,
- portage.best([pkg_key] + myoldbest)):
- # Downgrade in slot
- addl += turquoise("U")+blue("D")
- if ordered:
- counters.downgrades += 1
- else:
- # Update in slot
- addl += turquoise("U") + " "
- if ordered:
- counters.upgrades += 1
- else:
- # New slot, mark it new.
- addl = " " + green("NS") + fetch + " "
- myoldbest = vardb.match(portage.cpv_getkey(pkg_key))
- if ordered:
- counters.newslot += 1
-
- if "--changelog" in self.myopts:
- inst_matches = vardb.match(pkg.slot_atom)
- if inst_matches:
- changelogs.extend(self.calc_changelog(
- portdb.findname(pkg_key),
- inst_matches[0], pkg_key))
- else:
- addl = " " + green("N") + " " + fetch + " "
- if ordered:
- counters.new += 1
-
- verboseadd = ""
- repoadd = None
-
- if True:
- # USE flag display
- forced_flags = set()
- pkgsettings.setcpv(pkg) # for package.use.{mask,force}
- forced_flags.update(pkgsettings.useforce)
- forced_flags.update(pkgsettings.usemask)
-
- cur_use = [flag for flag in pkg.use.enabled \
- if flag in pkg.iuse.all]
- cur_iuse = sorted(pkg.iuse.all)
-
- if myoldbest and myinslotlist:
- previous_cpv = myoldbest[0]
- else:
- previous_cpv = pkg.cpv
- if vardb.cpv_exists(previous_cpv):
- old_iuse, old_use = vardb.aux_get(
- previous_cpv, ["IUSE", "USE"])
- old_iuse = list(set(
- filter_iuse_defaults(old_iuse.split())))
- old_iuse.sort()
- old_use = old_use.split()
- is_new = False
- else:
- old_iuse = []
- old_use = []
- is_new = True
-
- old_use = [flag for flag in old_use if flag in old_iuse]
-
- use_expand = pkgsettings["USE_EXPAND"].lower().split()
- use_expand.sort()
- use_expand.reverse()
- use_expand_hidden = \
- pkgsettings["USE_EXPAND_HIDDEN"].lower().split()
-
- def map_to_use_expand(myvals, forcedFlags=False,
- removeHidden=True):
- ret = {}
- forced = {}
- for exp in use_expand:
- ret[exp] = []
- forced[exp] = set()
- for val in myvals[:]:
- if val.startswith(exp.lower()+"_"):
- if val in forced_flags:
- forced[exp].add(val[len(exp)+1:])
- ret[exp].append(val[len(exp)+1:])
- myvals.remove(val)
- ret["USE"] = myvals
- forced["USE"] = [val for val in myvals \
- if val in forced_flags]
- if removeHidden:
- for exp in use_expand_hidden:
- ret.pop(exp, None)
- if forcedFlags:
- return ret, forced
- return ret
-
- # Prevent USE_EXPAND_HIDDEN flags from being hidden if they
- # are the only thing that triggered reinstallation.
- reinst_flags_map = {}
- reinstall_for_flags = self._reinstall_nodes.get(pkg)
- reinst_expand_map = None
- if reinstall_for_flags:
- reinst_flags_map = map_to_use_expand(
- list(reinstall_for_flags), removeHidden=False)
- for k in list(reinst_flags_map):
- if not reinst_flags_map[k]:
- del reinst_flags_map[k]
- if not reinst_flags_map.get("USE"):
- reinst_expand_map = reinst_flags_map.copy()
- reinst_expand_map.pop("USE", None)
- if reinst_expand_map and \
- not set(reinst_expand_map).difference(
- use_expand_hidden):
- use_expand_hidden = \
- set(use_expand_hidden).difference(
- reinst_expand_map)
-
- cur_iuse_map, iuse_forced = \
- map_to_use_expand(cur_iuse, forcedFlags=True)
- cur_use_map = map_to_use_expand(cur_use)
- old_iuse_map = map_to_use_expand(old_iuse)
- old_use_map = map_to_use_expand(old_use)
-
- use_expand.sort()
- use_expand.insert(0, "USE")
-
- for key in use_expand:
- if key in use_expand_hidden:
- continue
- verboseadd += create_use_string(key.upper(),
- cur_iuse_map[key], iuse_forced[key],
- cur_use_map[key], old_iuse_map[key],
- old_use_map[key], is_new,
- reinst_flags_map.get(key))
-
- if verbosity == 3:
- # size verbose
- mysize=0
- if pkg_type == "ebuild" and pkg_merge:
- try:
- myfilesdict = portdb.getfetchsizes(pkg_key,
- useflags=pkg_use, debug=self.edebug)
- except portage.exception.InvalidDependString, e:
- src_uri = portdb.aux_get(pkg_key, ["SRC_URI"])[0]
- show_invalid_depstring_notice(x, src_uri, str(e))
- del e
- return 1
- if myfilesdict is None:
- myfilesdict="[empty/missing/bad digest]"
- else:
- for myfetchfile in myfilesdict:
- if myfetchfile not in myfetchlist:
- mysize+=myfilesdict[myfetchfile]
- myfetchlist.append(myfetchfile)
- if ordered:
- counters.totalsize += mysize
- verboseadd += format_size(mysize)
-
- # overlay verbose
- # assign index for a previous version in the same slot
- has_previous = False
- repo_name_prev = None
- slot_atom = "%s:%s" % (portage.dep_getkey(pkg_key),
- metadata["SLOT"])
- slot_matches = vardb.match(slot_atom)
- if slot_matches:
- has_previous = True
- repo_name_prev = vardb.aux_get(slot_matches[0],
- ["repository"])[0]
-
- # now use the data to generate output
- if pkg.installed or not has_previous:
- repoadd = repo_display.repoStr(repo_path_real)
- else:
- repo_path_prev = None
- if repo_name_prev:
- repo_path_prev = portdb.getRepositoryPath(
- repo_name_prev)
- if repo_path_prev == repo_path_real:
- repoadd = repo_display.repoStr(repo_path_real)
- else:
- repoadd = "%s=>%s" % (
- repo_display.repoStr(repo_path_prev),
- repo_display.repoStr(repo_path_real))
- if repoadd:
- repoadd_set.add(repoadd)
-
- xs = [portage.cpv_getkey(pkg_key)] + \
- list(portage.catpkgsplit(pkg_key)[2:])
- if xs[2] == "r0":
- xs[2] = ""
- else:
- xs[2] = "-" + xs[2]
-
- mywidth = 130
- if "COLUMNWIDTH" in self.settings:
- try:
- mywidth = int(self.settings["COLUMNWIDTH"])
- except ValueError, e:
- portage.writemsg("!!! %s\n" % str(e), noiselevel=-1)
- portage.writemsg(
- "!!! Unable to parse COLUMNWIDTH='%s'\n" % \
- self.settings["COLUMNWIDTH"], noiselevel=-1)
- del e
- oldlp = mywidth - 30
- newlp = oldlp - 30
-
- # Convert myoldbest from a list to a string.
- if not myoldbest:
- myoldbest = ""
- else:
- for pos, key in enumerate(myoldbest):
- key = portage.catpkgsplit(key)[2] + \
- "-" + portage.catpkgsplit(key)[3]
- if key[-3:] == "-r0":
- key = key[:-3]
- myoldbest[pos] = key
- myoldbest = blue("["+", ".join(myoldbest)+"]")
-
- pkg_cp = xs[0]
- root_config = self.roots[myroot]
- system_set = root_config.sets["system"]
- world_set = root_config.sets["world"]
-
- pkg_system = False
- pkg_world = False
- try:
- pkg_system = system_set.findAtomForPackage(pkg)
- pkg_world = world_set.findAtomForPackage(pkg)
- if not (oneshot or pkg_world) and \
- myroot == self.target_root and \
- favorites_set.findAtomForPackage(pkg):
- # Maybe it will be added to world now.
- if create_world_atom(pkg, favorites_set, root_config):
- pkg_world = True
- except portage.exception.InvalidDependString:
- # This is reported elsewhere if relevant.
- pass
-
- def pkgprint(pkg_str):
- if pkg_merge:
- if pkg_system:
- return colorize("PKG_MERGE_SYSTEM", pkg_str)
- elif pkg_world:
- return colorize("PKG_MERGE_WORLD", pkg_str)
- else:
- return colorize("PKG_MERGE", pkg_str)
- elif pkg_status == "uninstall":
- return colorize("PKG_UNINSTALL", pkg_str)
- else:
- if pkg_system:
- return colorize("PKG_NOMERGE_SYSTEM", pkg_str)
- elif pkg_world:
- return colorize("PKG_NOMERGE_WORLD", pkg_str)
- else:
- return colorize("PKG_NOMERGE", pkg_str)
-
- try:
- properties = flatten(use_reduce(paren_reduce(
- pkg.metadata["PROPERTIES"]), uselist=pkg.use.enabled))
- except portage.exception.InvalidDependString, e:
- if not pkg.installed:
- show_invalid_depstring_notice(pkg,
- pkg.metadata["PROPERTIES"], str(e))
- del e
- return 1
- properties = []
- interactive = "interactive" in properties
- if interactive and pkg.operation == "merge":
- addl = colorize("WARN", "I") + addl[1:]
- if ordered:
- counters.interactive += 1
-
- if x[1]!="/":
- if myoldbest:
- myoldbest +=" "
- if "--columns" in self.myopts:
- if "--quiet" in self.myopts:
- myprint=addl+" "+indent+pkgprint(pkg_cp)
- myprint=myprint+darkblue(" "+xs[1]+xs[2])+" "
- myprint=myprint+myoldbest
- myprint=myprint+darkgreen("to "+x[1])
- verboseadd = None
- else:
- if not pkg_merge:
- myprint = "[%s] %s%s" % \
- (pkgprint(pkg_status.ljust(13)),
- indent, pkgprint(pkg.cp))
- else:
- myprint = "[%s %s] %s%s" % \
- (pkgprint(pkg.type_name), addl,
- indent, pkgprint(pkg.cp))
- if (newlp-nc_len(myprint)) > 0:
- myprint=myprint+(" "*(newlp-nc_len(myprint)))
- myprint=myprint+"["+darkblue(xs[1]+xs[2])+"] "
- if (oldlp-nc_len(myprint)) > 0:
- myprint=myprint+" "*(oldlp-nc_len(myprint))
- myprint=myprint+myoldbest
- myprint += darkgreen("to " + pkg.root)
- else:
- if not pkg_merge:
- myprint = "[%s] " % pkgprint(pkg_status.ljust(13))
- else:
- myprint = "[%s %s] " % (pkgprint(pkg_type), addl)
- myprint += indent + pkgprint(pkg_key) + " " + \
- myoldbest + darkgreen("to " + myroot)
- else:
- if "--columns" in self.myopts:
- if "--quiet" in self.myopts:
- myprint=addl+" "+indent+pkgprint(pkg_cp)
- myprint=myprint+" "+green(xs[1]+xs[2])+" "
- myprint=myprint+myoldbest
- verboseadd = None
- else:
- if not pkg_merge:
- myprint = "[%s] %s%s" % \
- (pkgprint(pkg_status.ljust(13)),
- indent, pkgprint(pkg.cp))
- else:
- myprint = "[%s %s] %s%s" % \
- (pkgprint(pkg.type_name), addl,
- indent, pkgprint(pkg.cp))
- if (newlp-nc_len(myprint)) > 0:
- myprint=myprint+(" "*(newlp-nc_len(myprint)))
- myprint=myprint+green(" ["+xs[1]+xs[2]+"] ")
- if (oldlp-nc_len(myprint)) > 0:
- myprint=myprint+(" "*(oldlp-nc_len(myprint)))
- myprint += myoldbest
- else:
- if not pkg_merge:
- myprint = "[%s] %s%s %s" % \
- (pkgprint(pkg_status.ljust(13)),
- indent, pkgprint(pkg.cpv),
- myoldbest)
- else:
- myprint = "[%s %s] %s%s %s" % \
- (pkgprint(pkg_type), addl, indent,
- pkgprint(pkg.cpv), myoldbest)
-
- if columns and pkg.operation == "uninstall":
- continue
- p.append((myprint, verboseadd, repoadd))
-
- if "--tree" not in self.myopts and \
- "--quiet" not in self.myopts and \
- not self._opts_no_restart.intersection(self.myopts) and \
- pkg.root == self._running_root.root and \
- portage.match_from_list(
- portage.const.PORTAGE_PACKAGE_ATOM, [pkg]) and \
- not vardb.cpv_exists(pkg.cpv) and \
- "--quiet" not in self.myopts:
- if mylist_index < len(mylist) - 1:
- p.append(colorize("WARN", "*** Portage will stop merging at this point and reload itself,"))
- p.append(colorize("WARN", " then resume the merge."))
-
- out = sys.stdout
- show_repos = repoadd_set and repoadd_set != set(["0"])
-
- for x in p:
- if isinstance(x, basestring):
- out.write("%s\n" % (x,))
- continue
-
- myprint, verboseadd, repoadd = x
-
- if verboseadd:
- myprint += " " + verboseadd
-
- if show_repos and repoadd:
- myprint += " " + teal("[%s]" % repoadd)
-
- out.write("%s\n" % (myprint,))
-
- for x in blockers:
- print x
-
- if verbosity == 3:
- print
- print counters
- if show_repos:
- sys.stdout.write(str(repo_display))
-
- if "--changelog" in self.myopts:
- print
- for revision,text in changelogs:
- print bold('*'+revision)
- sys.stdout.write(text)
-
- sys.stdout.flush()
- return os.EX_OK
-
- def display_problems(self):
- """
- Display problems with the dependency graph such as slot collisions.
- This is called internally by display() to show the problems _after_
- the merge list where it is most likely to be seen, but if display()
- is not going to be called then this method should be called explicitly
- to ensure that the user is notified of problems with the graph.
-
- All output goes to stderr, except for unsatisfied dependencies which
- go to stdout for parsing by programs such as autounmask.
- """
-
- # Note that show_masked_packages() sends it's output to
- # stdout, and some programs such as autounmask parse the
- # output in cases when emerge bails out. However, when
- # show_masked_packages() is called for installed packages
- # here, the message is a warning that is more appropriate
- # to send to stderr, so temporarily redirect stdout to
- # stderr. TODO: Fix output code so there's a cleaner way
- # to redirect everything to stderr.
- sys.stdout.flush()
- sys.stderr.flush()
- stdout = sys.stdout
- try:
- sys.stdout = sys.stderr
- self._display_problems()
- finally:
- sys.stdout = stdout
- sys.stdout.flush()
- sys.stderr.flush()
-
- # This goes to stdout for parsing by programs like autounmask.
- for pargs, kwargs in self._unsatisfied_deps_for_display:
- self._show_unsatisfied_dep(*pargs, **kwargs)
-
- def _display_problems(self):
- if self._circular_deps_for_display is not None:
- self._show_circular_deps(
- self._circular_deps_for_display)
-
- # The user is only notified of a slot conflict if
- # there are no unresolvable blocker conflicts.
- if self._unsatisfied_blockers_for_display is not None:
- self._show_unsatisfied_blockers(
- self._unsatisfied_blockers_for_display)
- else:
- self._show_slot_collision_notice()
-
- # TODO: Add generic support for "set problem" handlers so that
- # the below warnings aren't special cases for world only.
-
- if self._missing_args:
- world_problems = False
- if "world" in self._sets:
- # Filter out indirect members of world (from nested sets)
- # since only direct members of world are desired here.
- world_set = self.roots[self.target_root].sets["world"]
- for arg, atom in self._missing_args:
- if arg.name == "world" and atom in world_set:
- world_problems = True
- break
-
- if world_problems:
- sys.stderr.write("\n!!! Problems have been " + \
- "detected with your world file\n")
- sys.stderr.write("!!! Please run " + \
- green("emaint --check world")+"\n\n")
-
- if self._missing_args:
- sys.stderr.write("\n" + colorize("BAD", "!!!") + \
- " Ebuilds for the following packages are either all\n")
- sys.stderr.write(colorize("BAD", "!!!") + \
- " masked or don't exist:\n")
- sys.stderr.write(" ".join(str(atom) for arg, atom in \
- self._missing_args) + "\n")
-
- if self._pprovided_args:
- arg_refs = {}
- for arg, atom in self._pprovided_args:
- if isinstance(arg, SetArg):
- parent = arg.name
- arg_atom = (atom, atom)
- else:
- parent = "args"
- arg_atom = (arg.arg, atom)
- refs = arg_refs.setdefault(arg_atom, [])
- if parent not in refs:
- refs.append(parent)
- msg = []
- msg.append(bad("\nWARNING: "))
- if len(self._pprovided_args) > 1:
- msg.append("Requested packages will not be " + \
- "merged because they are listed in\n")
- else:
- msg.append("A requested package will not be " + \
- "merged because it is listed in\n")
- msg.append("package.provided:\n\n")
- problems_sets = set()
- for (arg, atom), refs in arg_refs.iteritems():
- ref_string = ""
- if refs:
- problems_sets.update(refs)
- refs.sort()
- ref_string = ", ".join(["'%s'" % name for name in refs])
- ref_string = " pulled in by " + ref_string
- msg.append(" %s%s\n" % (colorize("INFORM", str(arg)), ref_string))
- msg.append("\n")
- if "world" in problems_sets:
- msg.append("This problem can be solved in one of the following ways:\n\n")
- msg.append(" A) Use emaint to clean offending packages from world (if not installed).\n")
- msg.append(" B) Uninstall offending packages (cleans them from world).\n")
- msg.append(" C) Remove offending entries from package.provided.\n\n")
- msg.append("The best course of action depends on the reason that an offending\n")
- msg.append("package.provided entry exists.\n\n")
- sys.stderr.write("".join(msg))
-
- masked_packages = []
- for pkg in self._masked_installed:
- root_config = pkg.root_config
- pkgsettings = self.pkgsettings[pkg.root]
- mreasons = get_masking_status(pkg, pkgsettings, root_config)
- masked_packages.append((root_config, pkgsettings,
- pkg.cpv, pkg.metadata, mreasons))
- if masked_packages:
- sys.stderr.write("\n" + colorize("BAD", "!!!") + \
- " The following installed packages are masked:\n")
- show_masked_packages(masked_packages)
- show_mask_docs()
- print
-
- def calc_changelog(self,ebuildpath,current,next):
- if ebuildpath == None or not os.path.exists(ebuildpath):
- return []
- current = '-'.join(portage.catpkgsplit(current)[1:])
- if current.endswith('-r0'):
- current = current[:-3]
- next = '-'.join(portage.catpkgsplit(next)[1:])
- if next.endswith('-r0'):
- next = next[:-3]
- changelogpath = os.path.join(os.path.split(ebuildpath)[0],'ChangeLog')
- try:
- changelog = open(changelogpath).read()
- except SystemExit, e:
- raise # Needed else can't exit
- except:
- return []
- divisions = self.find_changelog_tags(changelog)
- #print 'XX from',current,'to',next
- #for div,text in divisions: print 'XX',div
- # skip entries for all revisions above the one we are about to emerge
- for i in range(len(divisions)):
- if divisions[i][0]==next:
- divisions = divisions[i:]
- break
- # find out how many entries we are going to display
- for i in range(len(divisions)):
- if divisions[i][0]==current:
- divisions = divisions[:i]
- break
- else:
- # couldnt find the current revision in the list. display nothing
- return []
- return divisions
-
- def find_changelog_tags(self,changelog):
- divs = []
- release = None
- while 1:
- match = re.search(r'^\*\ ?([-a-zA-Z0-9_.+]*)(?:\ .*)?\n',changelog,re.M)
- if match is None:
- if release is not None:
- divs.append((release,changelog))
- return divs
- if release is not None:
- divs.append((release,changelog[:match.start()]))
- changelog = changelog[match.end():]
- release = match.group(1)
- if release.endswith('.ebuild'):
- release = release[:-7]
- if release.endswith('-r0'):
- release = release[:-3]
-
- def saveNomergeFavorites(self):
- """Find atoms in favorites that are not in the mergelist and add them
- to the world file if necessary."""
- for x in ("--buildpkgonly", "--fetchonly", "--fetch-all-uri",
- "--oneshot", "--onlydeps", "--pretend"):
- if x in self.myopts:
- return
- root_config = self.roots[self.target_root]
- world_set = root_config.sets["world"]
-
- world_locked = False
- if hasattr(world_set, "lock"):
- world_set.lock()
- world_locked = True
-
- if hasattr(world_set, "load"):
- world_set.load() # maybe it's changed on disk
-
- args_set = self._sets["args"]
- portdb = self.trees[self.target_root]["porttree"].dbapi
- added_favorites = set()
- for x in self._set_nodes:
- pkg_type, root, pkg_key, pkg_status = x
- if pkg_status != "nomerge":
- continue
-
- try:
- myfavkey = create_world_atom(x, args_set, root_config)
- if myfavkey:
- if myfavkey in added_favorites:
- continue
- added_favorites.add(myfavkey)
- except portage.exception.InvalidDependString, e:
- writemsg("\n\n!!! '%s' has invalid PROVIDE: %s\n" % \
- (pkg_key, str(e)), noiselevel=-1)
- writemsg("!!! see '%s'\n\n" % os.path.join(
- root, portage.VDB_PATH, pkg_key, "PROVIDE"), noiselevel=-1)
- del e
- all_added = []
- for k in self._sets:
- if k in ("args", "world") or not root_config.sets[k].world_candidate:
- continue
- s = SETPREFIX + k
- if s in world_set:
- continue
- all_added.append(SETPREFIX + k)
- all_added.extend(added_favorites)
- all_added.sort()
- for a in all_added:
- print ">>> Recording %s in \"world\" favorites file..." % \
- colorize("INFORM", str(a))
- if all_added:
- world_set.update(all_added)
-
- if world_locked:
- world_set.unlock()
-
- def loadResumeCommand(self, resume_data, skip_masked=True,
- skip_missing=True):
- """
- Add a resume command to the graph and validate it in the process. This
- will raise a PackageNotFound exception if a package is not available.
- """
-
- if not isinstance(resume_data, dict):
- return False
-
- mergelist = resume_data.get("mergelist")
- if not isinstance(mergelist, list):
- mergelist = []
-
- fakedb = self.mydbapi
- trees = self.trees
- serialized_tasks = []
- masked_tasks = []
- for x in mergelist:
- if not (isinstance(x, list) and len(x) == 4):
- continue
- pkg_type, myroot, pkg_key, action = x
- if pkg_type not in self.pkg_tree_map:
- continue
- if action != "merge":
- continue
- tree_type = self.pkg_tree_map[pkg_type]
- mydb = trees[myroot][tree_type].dbapi
- db_keys = list(self._trees_orig[myroot][
- tree_type].dbapi._aux_cache_keys)
- try:
- metadata = izip(db_keys, mydb.aux_get(pkg_key, db_keys))
- except KeyError:
- # It does no exist or it is corrupt.
- if action == "uninstall":
- continue
- if skip_missing:
- # TODO: log these somewhere
- continue
- raise portage.exception.PackageNotFound(pkg_key)
- installed = action == "uninstall"
- built = pkg_type != "ebuild"
- root_config = self.roots[myroot]
- pkg = Package(built=built, cpv=pkg_key,
- installed=installed, metadata=metadata,
- operation=action, root_config=root_config,
- type_name=pkg_type)
- if pkg_type == "ebuild":
- pkgsettings = self.pkgsettings[myroot]
- pkgsettings.setcpv(pkg)
- pkg.metadata["USE"] = pkgsettings["PORTAGE_USE"]
- pkg.metadata['CHOST'] = pkgsettings.get('CHOST', '')
- self._pkg_cache[pkg] = pkg
-
- root_config = self.roots[pkg.root]
- if "merge" == pkg.operation and \
- not visible(root_config.settings, pkg):
- if skip_masked:
- masked_tasks.append(Dependency(root=pkg.root, parent=pkg))
- else:
- self._unsatisfied_deps_for_display.append(
- ((pkg.root, "="+pkg.cpv), {"myparent":None}))
-
- fakedb[myroot].cpv_inject(pkg)
- serialized_tasks.append(pkg)
- self.spinner.update()
-
- if self._unsatisfied_deps_for_display:
- return False
-
- if not serialized_tasks or "--nodeps" in self.myopts:
- self._serialized_tasks_cache = serialized_tasks
- self._scheduler_graph = self.digraph
- else:
- self._select_package = self._select_pkg_from_graph
- self.myparams.add("selective")
- # Always traverse deep dependencies in order to account for
- # potentially unsatisfied dependencies of installed packages.
- # This is necessary for correct --keep-going or --resume operation
- # in case a package from a group of circularly dependent packages
- # fails. In this case, a package which has recently been installed
- # may have an unsatisfied circular dependency (pulled in by
- # PDEPEND, for example). So, even though a package is already
- # installed, it may not have all of it's dependencies satisfied, so
- # it may not be usable. If such a package is in the subgraph of
- # deep depenedencies of a scheduled build, that build needs to
- # be cancelled. In order for this type of situation to be
- # recognized, deep traversal of dependencies is required.
- self.myparams.add("deep")
-
- favorites = resume_data.get("favorites")
- args_set = self._sets["args"]
- if isinstance(favorites, list):
- args = self._load_favorites(favorites)
- else:
- args = []
-
- for task in serialized_tasks:
- if isinstance(task, Package) and \
- task.operation == "merge":
- if not self._add_pkg(task, None):
- return False
-
- # Packages for argument atoms need to be explicitly
- # added via _add_pkg() so that they are included in the
- # digraph (needed at least for --tree display).
- for arg in args:
- for atom in arg.set:
- pkg, existing_node = self._select_package(
- arg.root_config.root, atom)
- if existing_node is None and \
- pkg is not None:
- if not self._add_pkg(pkg, Dependency(atom=atom,
- root=pkg.root, parent=arg)):
- return False
-
- # Allow unsatisfied deps here to avoid showing a masking
- # message for an unsatisfied dep that isn't necessarily
- # masked.
- if not self._create_graph(allow_unsatisfied=True):
- return False
-
- unsatisfied_deps = []
- for dep in self._unsatisfied_deps:
- if not isinstance(dep.parent, Package):
- continue
- if dep.parent.operation == "merge":
- unsatisfied_deps.append(dep)
- continue
-
- # For unsatisfied deps of installed packages, only account for
- # them if they are in the subgraph of dependencies of a package
- # which is scheduled to be installed.
- unsatisfied_install = False
- traversed = set()
- dep_stack = self.digraph.parent_nodes(dep.parent)
- while dep_stack:
- node = dep_stack.pop()
- if not isinstance(node, Package):
- continue
- if node.operation == "merge":
- unsatisfied_install = True
- break
- if node in traversed:
- continue
- traversed.add(node)
- dep_stack.extend(self.digraph.parent_nodes(node))
-
- if unsatisfied_install:
- unsatisfied_deps.append(dep)
-
- if masked_tasks or unsatisfied_deps:
- # This probably means that a required package
- # was dropped via --skipfirst. It makes the
- # resume list invalid, so convert it to a
- # UnsatisfiedResumeDep exception.
- raise self.UnsatisfiedResumeDep(self,
- masked_tasks + unsatisfied_deps)
- self._serialized_tasks_cache = None
- try:
- self.altlist()
- except self._unknown_internal_error:
- return False
-
- return True
-
- def _load_favorites(self, favorites):
- """
- Use a list of favorites to resume state from a
- previous select_files() call. This creates similar
- DependencyArg instances to those that would have
- been created by the original select_files() call.
- This allows Package instances to be matched with
- DependencyArg instances during graph creation.
- """
- root_config = self.roots[self.target_root]
- getSetAtoms = root_config.setconfig.getSetAtoms
- sets = root_config.sets
- args = []
- for x in favorites:
- if not isinstance(x, basestring):
- continue
- if x in ("system", "world"):
- x = SETPREFIX + x
- if x.startswith(SETPREFIX):
- s = x[len(SETPREFIX):]
- if s not in sets:
- continue
- if s in self._sets:
- continue
- # Recursively expand sets so that containment tests in
- # self._get_parent_sets() properly match atoms in nested
- # sets (like if world contains system).
- expanded_set = InternalPackageSet(
- initial_atoms=getSetAtoms(s))
- self._sets[s] = expanded_set
- args.append(SetArg(arg=x, set=expanded_set,
- root_config=root_config))
- else:
- if not portage.isvalidatom(x):
- continue
- args.append(AtomArg(arg=x, atom=x,
- root_config=root_config))
-
- self._set_args(args)
- return args
-
- class UnsatisfiedResumeDep(portage.exception.PortageException):
- """
- A dependency of a resume list is not installed. This
- can occur when a required package is dropped from the
- merge list via --skipfirst.
- """
- def __init__(self, depgraph, value):
- portage.exception.PortageException.__init__(self, value)
- self.depgraph = depgraph
-
- class _internal_exception(portage.exception.PortageException):
- def __init__(self, value=""):
- portage.exception.PortageException.__init__(self, value)
-
- class _unknown_internal_error(_internal_exception):
- """
- Used by the depgraph internally to terminate graph creation.
- The specific reason for the failure should have been dumped
- to stderr, unfortunately, the exact reason for the failure
- may not be known.
- """
-
- class _serialize_tasks_retry(_internal_exception):
- """
- This is raised by the _serialize_tasks() method when it needs to
- be called again for some reason. The only case that it's currently
- used for is when neglected dependencies need to be added to the
- graph in order to avoid making a potentially unsafe decision.
- """
-
- class _dep_check_composite_db(portage.dbapi):
- """
- A dbapi-like interface that is optimized for use in dep_check() calls.
- This is built on top of the existing depgraph package selection logic.
- Some packages that have been added to the graph may be masked from this
- view in order to influence the atom preference selection that occurs
- via dep_check().
- """
- def __init__(self, depgraph, root):
- portage.dbapi.__init__(self)
- self._depgraph = depgraph
- self._root = root
- self._match_cache = {}
- self._cpv_pkg_map = {}
-
- def _clear_cache(self):
- self._match_cache.clear()
- self._cpv_pkg_map.clear()
-
- def match(self, atom):
- ret = self._match_cache.get(atom)
- if ret is not None:
- return ret[:]
- orig_atom = atom
- if "/" not in atom:
- atom = self._dep_expand(atom)
- pkg, existing = self._depgraph._select_package(self._root, atom)
- if not pkg:
- ret = []
- else:
- # Return the highest available from select_package() as well as
- # any matching slots in the graph db.
- slots = set()
- slots.add(pkg.metadata["SLOT"])
- atom_cp = portage.dep_getkey(atom)
- if pkg.cp.startswith("virtual/"):
- # For new-style virtual lookahead that occurs inside
- # dep_check(), examine all slots. This is needed
- # so that newer slots will not unnecessarily be pulled in
- # when a satisfying lower slot is already installed. For
- # example, if virtual/jdk-1.4 is satisfied via kaffe then
- # there's no need to pull in a newer slot to satisfy a
- # virtual/jdk dependency.
- for db, pkg_type, built, installed, db_keys in \
- self._depgraph._filtered_trees[self._root]["dbs"]:
- for cpv in db.match(atom):
- if portage.cpv_getkey(cpv) != pkg.cp:
- continue
- slots.add(db.aux_get(cpv, ["SLOT"])[0])
- ret = []
- if self._visible(pkg):
- self._cpv_pkg_map[pkg.cpv] = pkg
- ret.append(pkg.cpv)
- slots.remove(pkg.metadata["SLOT"])
- while slots:
- slot_atom = "%s:%s" % (atom_cp, slots.pop())
- pkg, existing = self._depgraph._select_package(
- self._root, slot_atom)
- if not pkg:
- continue
- if not self._visible(pkg):
- continue
- self._cpv_pkg_map[pkg.cpv] = pkg
- ret.append(pkg.cpv)
- if ret:
- self._cpv_sort_ascending(ret)
- self._match_cache[orig_atom] = ret
- return ret[:]
-
- def _visible(self, pkg):
- if pkg.installed and "selective" not in self._depgraph.myparams:
- try:
- arg = self._depgraph._iter_atoms_for_pkg(pkg).next()
- except (StopIteration, portage.exception.InvalidDependString):
- arg = None
- if arg:
- return False
- if pkg.installed:
- try:
- if not visible(
- self._depgraph.pkgsettings[pkg.root], pkg):
- return False
- except portage.exception.InvalidDependString:
- pass
- in_graph = self._depgraph._slot_pkg_map[
- self._root].get(pkg.slot_atom)
- if in_graph is None:
- # Mask choices for packages which are not the highest visible
- # version within their slot (since they usually trigger slot
- # conflicts).
- highest_visible, in_graph = self._depgraph._select_package(
- self._root, pkg.slot_atom)
- if pkg != highest_visible:
- return False
- elif in_graph != pkg:
- # Mask choices for packages that would trigger a slot
- # conflict with a previously selected package.
- return False
- return True
-
- def _dep_expand(self, atom):
- """
- This is only needed for old installed packages that may
- contain atoms that are not fully qualified with a specific
- category. Emulate the cpv_expand() function that's used by
- dbapi.match() in cases like this. If there are multiple
- matches, it's often due to a new-style virtual that has
- been added, so try to filter those out to avoid raising
- a ValueError.
- """
- root_config = self._depgraph.roots[self._root]
- orig_atom = atom
- expanded_atoms = self._depgraph._dep_expand(root_config, atom)
- if len(expanded_atoms) > 1:
- non_virtual_atoms = []
- for x in expanded_atoms:
- if not portage.dep_getkey(x).startswith("virtual/"):
- non_virtual_atoms.append(x)
- if len(non_virtual_atoms) == 1:
- expanded_atoms = non_virtual_atoms
- if len(expanded_atoms) > 1:
- # compatible with portage.cpv_expand()
- raise portage.exception.AmbiguousPackageName(
- [portage.dep_getkey(x) for x in expanded_atoms])
- if expanded_atoms:
- atom = expanded_atoms[0]
- else:
- null_atom = insert_category_into_atom(atom, "null")
- null_cp = portage.dep_getkey(null_atom)
- cat, atom_pn = portage.catsplit(null_cp)
- virts_p = root_config.settings.get_virts_p().get(atom_pn)
- if virts_p:
- # Allow the resolver to choose which virtual.
- atom = insert_category_into_atom(atom, "virtual")
- else:
- atom = insert_category_into_atom(atom, "null")
- return atom
-
- def aux_get(self, cpv, wants):
- metadata = self._cpv_pkg_map[cpv].metadata
- return [metadata.get(x, "") for x in wants]
-
-class Scheduler(PollScheduler):
-
- _opts_ignore_blockers = \
- frozenset(["--buildpkgonly",
- "--fetchonly", "--fetch-all-uri",
- "--nodeps", "--pretend"])
-
- _opts_no_background = \
- frozenset(["--pretend",
- "--fetchonly", "--fetch-all-uri"])
-
- _opts_no_restart = frozenset(["--buildpkgonly",
- "--fetchonly", "--fetch-all-uri", "--pretend"])
-
- _bad_resume_opts = set(["--ask", "--changelog",
- "--resume", "--skipfirst"])
-
- _fetch_log = os.path.join(_emerge_log_dir, 'emerge-fetch.log')
-
- class _iface_class(SlotObject):
- __slots__ = ("dblinkEbuildPhase", "dblinkDisplayMerge",
- "dblinkElog", "dblinkEmergeLog", "fetch", "register", "schedule",
- "scheduleSetup", "scheduleUnpack", "scheduleYield",
- "unregister")
-
- class _fetch_iface_class(SlotObject):
- __slots__ = ("log_file", "schedule")
-
- _task_queues_class = slot_dict_class(
- ("merge", "jobs", "fetch", "unpack"), prefix="")
-
- class _build_opts_class(SlotObject):
- __slots__ = ("buildpkg", "buildpkgonly",
- "fetch_all_uri", "fetchonly", "pretend")
-
- class _binpkg_opts_class(SlotObject):
- __slots__ = ("fetchonly", "getbinpkg", "pretend")
-
- class _pkg_count_class(SlotObject):
- __slots__ = ("curval", "maxval")
-
- class _emerge_log_class(SlotObject):
- __slots__ = ("xterm_titles",)
-
- def log(self, *pargs, **kwargs):
- if not self.xterm_titles:
- # Avoid interference with the scheduler's status display.
- kwargs.pop("short_msg", None)
- emergelog(self.xterm_titles, *pargs, **kwargs)
-
- class _failed_pkg(SlotObject):
- __slots__ = ("build_dir", "build_log", "pkg", "returncode")
-
- class _ConfigPool(object):
- """Interface for a task to temporarily allocate a config
- instance from a pool. This allows a task to be constructed
- long before the config instance actually becomes needed, like
- when prefetchers are constructed for the whole merge list."""
- __slots__ = ("_root", "_allocate", "_deallocate")
- def __init__(self, root, allocate, deallocate):
- self._root = root
- self._allocate = allocate
- self._deallocate = deallocate
- def allocate(self):
- return self._allocate(self._root)
- def deallocate(self, settings):
- self._deallocate(settings)
-
- class _unknown_internal_error(portage.exception.PortageException):
- """
- Used internally to terminate scheduling. The specific reason for
- the failure should have been dumped to stderr.
- """
- def __init__(self, value=""):
- portage.exception.PortageException.__init__(self, value)
-
- def __init__(self, settings, trees, mtimedb, myopts,
- spinner, mergelist, favorites, digraph):
- PollScheduler.__init__(self)
- self.settings = settings
- self.target_root = settings["ROOT"]
- self.trees = trees
- self.myopts = myopts
- self._spinner = spinner
- self._mtimedb = mtimedb
- self._mergelist = mergelist
- self._favorites = favorites
- self._args_set = InternalPackageSet(favorites)
- self._build_opts = self._build_opts_class()
- for k in self._build_opts.__slots__:
- setattr(self._build_opts, k, "--" + k.replace("_", "-") in myopts)
- self._binpkg_opts = self._binpkg_opts_class()
- for k in self._binpkg_opts.__slots__:
- setattr(self._binpkg_opts, k, "--" + k.replace("_", "-") in myopts)
-
- self.curval = 0
- self._logger = self._emerge_log_class()
- self._task_queues = self._task_queues_class()
- for k in self._task_queues.allowed_keys:
- setattr(self._task_queues, k,
- SequentialTaskQueue())
-
- # Holds merges that will wait to be executed when no builds are
- # executing. This is useful for system packages since dependencies
- # on system packages are frequently unspecified.
- self._merge_wait_queue = []
- # Holds merges that have been transfered from the merge_wait_queue to
- # the actual merge queue. They are removed from this list upon
- # completion. Other packages can start building only when this list is
- # empty.
- self._merge_wait_scheduled = []
-
- # Holds system packages and their deep runtime dependencies. Before
- # being merged, these packages go to merge_wait_queue, to be merged
- # when no other packages are building.
- self._deep_system_deps = set()
-
- # Holds packages to merge which will satisfy currently unsatisfied
- # deep runtime dependencies of system packages. If this is not empty
- # then no parallel builds will be spawned until it is empty. This
- # minimizes the possibility that a build will fail due to the system
- # being in a fragile state. For example, see bug #259954.
- self._unsatisfied_system_deps = set()
-
- self._status_display = JobStatusDisplay(
- xterm_titles=('notitles' not in settings.features))
- self._max_load = myopts.get("--load-average")
- max_jobs = myopts.get("--jobs")
- if max_jobs is None:
- max_jobs = 1
- self._set_max_jobs(max_jobs)
-
- # The root where the currently running
- # portage instance is installed.
- self._running_root = trees["/"]["root_config"]
- self.edebug = 0
- if settings.get("PORTAGE_DEBUG", "") == "1":
- self.edebug = 1
- self.pkgsettings = {}
- self._config_pool = {}
- self._blocker_db = {}
- for root in trees:
- self._config_pool[root] = []
- self._blocker_db[root] = BlockerDB(trees[root]["root_config"])
-
- fetch_iface = self._fetch_iface_class(log_file=self._fetch_log,
- schedule=self._schedule_fetch)
- self._sched_iface = self._iface_class(
- dblinkEbuildPhase=self._dblink_ebuild_phase,
- dblinkDisplayMerge=self._dblink_display_merge,
- dblinkElog=self._dblink_elog,
- dblinkEmergeLog=self._dblink_emerge_log,
- fetch=fetch_iface, register=self._register,
- schedule=self._schedule_wait,
- scheduleSetup=self._schedule_setup,
- scheduleUnpack=self._schedule_unpack,
- scheduleYield=self._schedule_yield,
- unregister=self._unregister)
-
- self._prefetchers = weakref.WeakValueDictionary()
- self._pkg_queue = []
- self._completed_tasks = set()
-
- self._failed_pkgs = []
- self._failed_pkgs_all = []
- self._failed_pkgs_die_msgs = []
- self._post_mod_echo_msgs = []
- self._parallel_fetch = False
- merge_count = len([x for x in mergelist \
- if isinstance(x, Package) and x.operation == "merge"])
- self._pkg_count = self._pkg_count_class(
- curval=0, maxval=merge_count)
- self._status_display.maxval = self._pkg_count.maxval
-
- # The load average takes some time to respond when new
- # jobs are added, so we need to limit the rate of adding
- # new jobs.
- self._job_delay_max = 10
- self._job_delay_factor = 1.0
- self._job_delay_exp = 1.5
- self._previous_job_start_time = None
-
- self._set_digraph(digraph)
-
- # This is used to memoize the _choose_pkg() result when
- # no packages can be chosen until one of the existing
- # jobs completes.
- self._choose_pkg_return_early = False
-
- features = self.settings.features
- if "parallel-fetch" in features and \
- not ("--pretend" in self.myopts or \
- "--fetch-all-uri" in self.myopts or \
- "--fetchonly" in self.myopts):
- if "distlocks" not in features:
- portage.writemsg(red("!!!")+"\n", noiselevel=-1)
- portage.writemsg(red("!!!")+" parallel-fetching " + \
- "requires the distlocks feature enabled"+"\n",
- noiselevel=-1)
- portage.writemsg(red("!!!")+" you have it disabled, " + \
- "thus parallel-fetching is being disabled"+"\n",
- noiselevel=-1)
- portage.writemsg(red("!!!")+"\n", noiselevel=-1)
- elif len(mergelist) > 1:
- self._parallel_fetch = True
-
- if self._parallel_fetch:
- # clear out existing fetch log if it exists
- try:
- open(self._fetch_log, 'w')
- except EnvironmentError:
- pass
-
- self._running_portage = None
- portage_match = self._running_root.trees["vartree"].dbapi.match(
- portage.const.PORTAGE_PACKAGE_ATOM)
- if portage_match:
- cpv = portage_match.pop()
- self._running_portage = self._pkg(cpv, "installed",
- self._running_root, installed=True)
-
- def _poll(self, timeout=None):
- self._schedule()
- PollScheduler._poll(self, timeout=timeout)
-
- def _set_max_jobs(self, max_jobs):
- self._max_jobs = max_jobs
- self._task_queues.jobs.max_jobs = max_jobs
-
- def _background_mode(self):
- """
- Check if background mode is enabled and adjust states as necessary.
-
- @rtype: bool
- @returns: True if background mode is enabled, False otherwise.
- """
- background = (self._max_jobs is True or \
- self._max_jobs > 1 or "--quiet" in self.myopts) and \
- not bool(self._opts_no_background.intersection(self.myopts))
-
- if background:
- interactive_tasks = self._get_interactive_tasks()
- if interactive_tasks:
- background = False
- writemsg_level(">>> Sending package output to stdio due " + \
- "to interactive package(s):\n",
- level=logging.INFO, noiselevel=-1)
- msg = [""]
- for pkg in interactive_tasks:
- pkg_str = " " + colorize("INFORM", str(pkg.cpv))
- if pkg.root != "/":
- pkg_str += " for " + pkg.root
- msg.append(pkg_str)
- msg.append("")
- writemsg_level("".join("%s\n" % (l,) for l in msg),
- level=logging.INFO, noiselevel=-1)
- if self._max_jobs is True or self._max_jobs > 1:
- self._set_max_jobs(1)
- writemsg_level(">>> Setting --jobs=1 due " + \
- "to the above interactive package(s)\n",
- level=logging.INFO, noiselevel=-1)
-
- self._status_display.quiet = \
- not background or \
- ("--quiet" in self.myopts and \
- "--verbose" not in self.myopts)
-
- self._logger.xterm_titles = \
- "notitles" not in self.settings.features and \
- self._status_display.quiet
-
- return background
-
- def _get_interactive_tasks(self):
- from portage import flatten
- from portage.dep import use_reduce, paren_reduce
- interactive_tasks = []
- for task in self._mergelist:
- if not (isinstance(task, Package) and \
- task.operation == "merge"):
- continue
- try:
- properties = flatten(use_reduce(paren_reduce(
- task.metadata["PROPERTIES"]), uselist=task.use.enabled))
- except portage.exception.InvalidDependString, e:
- show_invalid_depstring_notice(task,
- task.metadata["PROPERTIES"], str(e))
- raise self._unknown_internal_error()
- if "interactive" in properties:
- interactive_tasks.append(task)
- return interactive_tasks
-
- def _set_digraph(self, digraph):
- if "--nodeps" in self.myopts or \
- (self._max_jobs is not True and self._max_jobs < 2):
- # save some memory
- self._digraph = None
- return
-
- self._digraph = digraph
- self._find_system_deps()
- self._prune_digraph()
- self._prevent_builddir_collisions()
-
- def _find_system_deps(self):
- """
- Find system packages and their deep runtime dependencies. Before being
- merged, these packages go to merge_wait_queue, to be merged when no
- other packages are building.
- """
- deep_system_deps = self._deep_system_deps
- deep_system_deps.clear()
- deep_system_deps.update(
- _find_deep_system_runtime_deps(self._digraph))
- deep_system_deps.difference_update([pkg for pkg in \
- deep_system_deps if pkg.operation != "merge"])
-
- def _prune_digraph(self):
- """
- Prune any root nodes that are irrelevant.
- """
-
- graph = self._digraph
- completed_tasks = self._completed_tasks
- removed_nodes = set()
- while True:
- for node in graph.root_nodes():
- if not isinstance(node, Package) or \
- (node.installed and node.operation == "nomerge") or \
- node.onlydeps or \
- node in completed_tasks:
- removed_nodes.add(node)
- if removed_nodes:
- graph.difference_update(removed_nodes)
- if not removed_nodes:
- break
- removed_nodes.clear()
-
- def _prevent_builddir_collisions(self):
- """
- When building stages, sometimes the same exact cpv needs to be merged
- to both $ROOTs. Add edges to the digraph in order to avoid collisions
- in the builddir. Currently, normal file locks would be inappropriate
- for this purpose since emerge holds all of it's build dir locks from
- the main process.
- """
- cpv_map = {}
- for pkg in self._mergelist:
- if not isinstance(pkg, Package):
- # a satisfied blocker
- continue
- if pkg.installed:
- continue
- if pkg.cpv not in cpv_map:
- cpv_map[pkg.cpv] = [pkg]
- continue
- for earlier_pkg in cpv_map[pkg.cpv]:
- self._digraph.add(earlier_pkg, pkg,
- priority=DepPriority(buildtime=True))
- cpv_map[pkg.cpv].append(pkg)
-
- class _pkg_failure(portage.exception.PortageException):
- """
- An instance of this class is raised by unmerge() when
- an uninstallation fails.
- """
- status = 1
- def __init__(self, *pargs):
- portage.exception.PortageException.__init__(self, pargs)
- if pargs:
- self.status = pargs[0]
-
- def _schedule_fetch(self, fetcher):
- """
- Schedule a fetcher on the fetch queue, in order to
- serialize access to the fetch log.
- """
- self._task_queues.fetch.addFront(fetcher)
-
- def _schedule_setup(self, setup_phase):
- """
- Schedule a setup phase on the merge queue, in order to
- serialize unsandboxed access to the live filesystem.
- """
- self._task_queues.merge.addFront(setup_phase)
- self._schedule()
-
- def _schedule_unpack(self, unpack_phase):
- """
- Schedule an unpack phase on the unpack queue, in order
- to serialize $DISTDIR access for live ebuilds.
- """
- self._task_queues.unpack.add(unpack_phase)
-
- def _find_blockers(self, new_pkg):
- """
- Returns a callable which should be called only when
- the vdb lock has been acquired.
- """
- def get_blockers():
- return self._find_blockers_with_lock(new_pkg, acquire_lock=0)
- return get_blockers
-
- def _find_blockers_with_lock(self, new_pkg, acquire_lock=0):
- if self._opts_ignore_blockers.intersection(self.myopts):
- return None
-
- # Call gc.collect() here to avoid heap overflow that
- # triggers 'Cannot allocate memory' errors (reported
- # with python-2.5).
- import gc
- gc.collect()
-
- blocker_db = self._blocker_db[new_pkg.root]
-
- blocker_dblinks = []
- for blocking_pkg in blocker_db.findInstalledBlockers(
- new_pkg, acquire_lock=acquire_lock):
- if new_pkg.slot_atom == blocking_pkg.slot_atom:
- continue
- if new_pkg.cpv == blocking_pkg.cpv:
- continue
- blocker_dblinks.append(portage.dblink(
- blocking_pkg.category, blocking_pkg.pf, blocking_pkg.root,
- self.pkgsettings[blocking_pkg.root], treetype="vartree",
- vartree=self.trees[blocking_pkg.root]["vartree"]))
-
- gc.collect()
-
- return blocker_dblinks
-
- def _dblink_pkg(self, pkg_dblink):
- cpv = pkg_dblink.mycpv
- type_name = RootConfig.tree_pkg_map[pkg_dblink.treetype]
- root_config = self.trees[pkg_dblink.myroot]["root_config"]
- installed = type_name == "installed"
- return self._pkg(cpv, type_name, root_config, installed=installed)
-
- def _append_to_log_path(self, log_path, msg):
- f = open(log_path, 'a')
- try:
- f.write(msg)
- finally:
- f.close()
-
- def _dblink_elog(self, pkg_dblink, phase, func, msgs):
-
- log_path = pkg_dblink.settings.get("PORTAGE_LOG_FILE")
- log_file = None
- out = sys.stdout
- background = self._background
-
- if background and log_path is not None:
- log_file = open(log_path, 'a')
- out = log_file
-
- try:
- for msg in msgs:
- func(msg, phase=phase, key=pkg_dblink.mycpv, out=out)
- finally:
- if log_file is not None:
- log_file.close()
-
- def _dblink_emerge_log(self, msg):
- self._logger.log(msg)
-
- def _dblink_display_merge(self, pkg_dblink, msg, level=0, noiselevel=0):
- log_path = pkg_dblink.settings.get("PORTAGE_LOG_FILE")
- background = self._background
-
- if log_path is None:
- if not (background and level < logging.WARN):
- portage.util.writemsg_level(msg,
- level=level, noiselevel=noiselevel)
- else:
- if not background:
- portage.util.writemsg_level(msg,
- level=level, noiselevel=noiselevel)
- self._append_to_log_path(log_path, msg)
-
- def _dblink_ebuild_phase(self,
- pkg_dblink, pkg_dbapi, ebuild_path, phase):
- """
- Using this callback for merge phases allows the scheduler
- to run while these phases execute asynchronously, and allows
- the scheduler control output handling.
- """
-
- scheduler = self._sched_iface
- settings = pkg_dblink.settings
- pkg = self._dblink_pkg(pkg_dblink)
- background = self._background
- log_path = settings.get("PORTAGE_LOG_FILE")
-
- ebuild_phase = EbuildPhase(background=background,
- pkg=pkg, phase=phase, scheduler=scheduler,
- settings=settings, tree=pkg_dblink.treetype)
- ebuild_phase.start()
- ebuild_phase.wait()
-
- return ebuild_phase.returncode
-
- def _generate_digests(self):
- """
- Generate digests if necessary for --digests or FEATURES=digest.
- In order to avoid interference, this must done before parallel
- tasks are started.
- """
-
- if '--fetchonly' in self.myopts:
- return os.EX_OK
-
- digest = '--digest' in self.myopts
- if not digest:
- for pkgsettings in self.pkgsettings.itervalues():
- if 'digest' in pkgsettings.features:
- digest = True
- break
-
- if not digest:
- return os.EX_OK
-
- for x in self._mergelist:
- if not isinstance(x, Package) or \
- x.type_name != 'ebuild' or \
- x.operation != 'merge':
- continue
- pkgsettings = self.pkgsettings[x.root]
- if '--digest' not in self.myopts and \
- 'digest' not in pkgsettings.features:
- continue
- portdb = x.root_config.trees['porttree'].dbapi
- ebuild_path = portdb.findname(x.cpv)
- if not ebuild_path:
- writemsg_level(
- "!!! Could not locate ebuild for '%s'.\n" \
- % x.cpv, level=logging.ERROR, noiselevel=-1)
- return 1
- pkgsettings['O'] = os.path.dirname(ebuild_path)
- if not portage.digestgen([], pkgsettings, myportdb=portdb):
- writemsg_level(
- "!!! Unable to generate manifest for '%s'.\n" \
- % x.cpv, level=logging.ERROR, noiselevel=-1)
- return 1
-
- return os.EX_OK
-
- def _check_manifests(self):
- # Verify all the manifests now so that the user is notified of failure
- # as soon as possible.
- if "strict" not in self.settings.features or \
- "--fetchonly" in self.myopts or \
- "--fetch-all-uri" in self.myopts:
- return os.EX_OK
-
- shown_verifying_msg = False
- quiet_settings = {}
- for myroot, pkgsettings in self.pkgsettings.iteritems():
- quiet_config = portage.config(clone=pkgsettings)
- quiet_config["PORTAGE_QUIET"] = "1"
- quiet_config.backup_changes("PORTAGE_QUIET")
- quiet_settings[myroot] = quiet_config
- del quiet_config
-
- for x in self._mergelist:
- if not isinstance(x, Package) or \
- x.type_name != "ebuild":
- continue
-
- if not shown_verifying_msg:
- shown_verifying_msg = True
- self._status_msg("Verifying ebuild manifests")
-
- root_config = x.root_config
- portdb = root_config.trees["porttree"].dbapi
- quiet_config = quiet_settings[root_config.root]
- quiet_config["O"] = os.path.dirname(portdb.findname(x.cpv))
- if not portage.digestcheck([], quiet_config, strict=True):
- return 1
-
- return os.EX_OK
-
- def _add_prefetchers(self):
-
- if not self._parallel_fetch:
- return
-
- if self._parallel_fetch:
- self._status_msg("Starting parallel fetch")
-
- prefetchers = self._prefetchers
- getbinpkg = "--getbinpkg" in self.myopts
-
- # In order to avoid "waiting for lock" messages
- # at the beginning, which annoy users, never
- # spawn a prefetcher for the first package.
- for pkg in self._mergelist[1:]:
- prefetcher = self._create_prefetcher(pkg)
- if prefetcher is not None:
- self._task_queues.fetch.add(prefetcher)
- prefetchers[pkg] = prefetcher
-
- def _create_prefetcher(self, pkg):
- """
- @return: a prefetcher, or None if not applicable
- """
- prefetcher = None
-
- if not isinstance(pkg, Package):
- pass
-
- elif pkg.type_name == "ebuild":
-
- prefetcher = EbuildFetcher(background=True,
- config_pool=self._ConfigPool(pkg.root,
- self._allocate_config, self._deallocate_config),
- fetchonly=1, logfile=self._fetch_log,
- pkg=pkg, prefetch=True, scheduler=self._sched_iface)
-
- elif pkg.type_name == "binary" and \
- "--getbinpkg" in self.myopts and \
- pkg.root_config.trees["bintree"].isremote(pkg.cpv):
-
- prefetcher = BinpkgPrefetcher(background=True,
- pkg=pkg, scheduler=self._sched_iface)
-
- return prefetcher
-
- def _is_restart_scheduled(self):
- """
- Check if the merge list contains a replacement
- for the current running instance, that will result
- in restart after merge.
- @rtype: bool
- @returns: True if a restart is scheduled, False otherwise.
- """
- if self._opts_no_restart.intersection(self.myopts):
- return False
-
- mergelist = self._mergelist
-
- for i, pkg in enumerate(mergelist):
- if self._is_restart_necessary(pkg) and \
- i != len(mergelist) - 1:
- return True
-
- return False
-
- def _is_restart_necessary(self, pkg):
- """
- @return: True if merging the given package
- requires restart, False otherwise.
- """
-
- # Figure out if we need a restart.
- if pkg.root == self._running_root.root and \
- portage.match_from_list(
- portage.const.PORTAGE_PACKAGE_ATOM, [pkg]):
- if self._running_portage:
- return pkg.cpv != self._running_portage.cpv
- return True
- return False
-
- def _restart_if_necessary(self, pkg):
- """
- Use execv() to restart emerge. This happens
- if portage upgrades itself and there are
- remaining packages in the list.
- """
-
- if self._opts_no_restart.intersection(self.myopts):
- return
-
- if not self._is_restart_necessary(pkg):
- return
-
- if pkg == self._mergelist[-1]:
- return
-
- self._main_loop_cleanup()
-
- logger = self._logger
- pkg_count = self._pkg_count
- mtimedb = self._mtimedb
- bad_resume_opts = self._bad_resume_opts
-
- logger.log(" ::: completed emerge (%s of %s) %s to %s" % \
- (pkg_count.curval, pkg_count.maxval, pkg.cpv, pkg.root))
-
- logger.log(" *** RESTARTING " + \
- "emerge via exec() after change of " + \
- "portage version.")
-
- mtimedb["resume"]["mergelist"].remove(list(pkg))
- mtimedb.commit()
- portage.run_exitfuncs()
- mynewargv = [sys.argv[0], "--resume"]
- resume_opts = self.myopts.copy()
- # For automatic resume, we need to prevent
- # any of bad_resume_opts from leaking in
- # via EMERGE_DEFAULT_OPTS.
- resume_opts["--ignore-default-opts"] = True
- for myopt, myarg in resume_opts.iteritems():
- if myopt not in bad_resume_opts:
- if myarg is True:
- mynewargv.append(myopt)
- else:
- mynewargv.append(myopt +"="+ str(myarg))
- # priority only needs to be adjusted on the first run
- os.environ["PORTAGE_NICENESS"] = "0"
- os.execv(mynewargv[0], mynewargv)
-
- def merge(self):
-
- if "--resume" in self.myopts:
- # We're resuming.
- portage.writemsg_stdout(
- colorize("GOOD", "*** Resuming merge...\n"), noiselevel=-1)
- self._logger.log(" *** Resuming merge...")
-
- self._save_resume_list()
-
- try:
- self._background = self._background_mode()
- except self._unknown_internal_error:
- return 1
-
- for root in self.trees:
- root_config = self.trees[root]["root_config"]
-
- # Even for --pretend --fetch mode, PORTAGE_TMPDIR is required
- # since it might spawn pkg_nofetch which requires PORTAGE_BUILDDIR
- # for ensuring sane $PWD (bug #239560) and storing elog messages.
- tmpdir = root_config.settings.get("PORTAGE_TMPDIR", "")
- if not tmpdir or not os.path.isdir(tmpdir):
- msg = "The directory specified in your " + \
- "PORTAGE_TMPDIR variable, '%s', " % tmpdir + \
- "does not exist. Please create this " + \
- "directory or correct your PORTAGE_TMPDIR setting."
- msg = textwrap.wrap(msg, 70)
- out = portage.output.EOutput()
- for l in msg:
- out.eerror(l)
- return 1
-
- if self._background:
- root_config.settings.unlock()
- root_config.settings["PORTAGE_BACKGROUND"] = "1"
- root_config.settings.backup_changes("PORTAGE_BACKGROUND")
- root_config.settings.lock()
-
- self.pkgsettings[root] = portage.config(
- clone=root_config.settings)
-
- rval = self._generate_digests()
- if rval != os.EX_OK:
- return rval
-
- rval = self._check_manifests()
- if rval != os.EX_OK:
- return rval
-
- keep_going = "--keep-going" in self.myopts
- fetchonly = self._build_opts.fetchonly
- mtimedb = self._mtimedb
- failed_pkgs = self._failed_pkgs
-
- while True:
- rval = self._merge()
- if rval == os.EX_OK or fetchonly or not keep_going:
- break
- if "resume" not in mtimedb:
- break
- mergelist = self._mtimedb["resume"].get("mergelist")
- if not mergelist:
- break
-
- if not failed_pkgs:
- break
-
- for failed_pkg in failed_pkgs:
- mergelist.remove(list(failed_pkg.pkg))
-
- self._failed_pkgs_all.extend(failed_pkgs)
- del failed_pkgs[:]
-
- if not mergelist:
- break
-
- if not self._calc_resume_list():
- break
-
- clear_caches(self.trees)
- if not self._mergelist:
- break
-
- self._save_resume_list()
- self._pkg_count.curval = 0
- self._pkg_count.maxval = len([x for x in self._mergelist \
- if isinstance(x, Package) and x.operation == "merge"])
- self._status_display.maxval = self._pkg_count.maxval
-
- self._logger.log(" *** Finished. Cleaning up...")
-
- if failed_pkgs:
- self._failed_pkgs_all.extend(failed_pkgs)
- del failed_pkgs[:]
-
- background = self._background
- failure_log_shown = False
- if background and len(self._failed_pkgs_all) == 1:
- # If only one package failed then just show it's
- # whole log for easy viewing.
- failed_pkg = self._failed_pkgs_all[-1]
- build_dir = failed_pkg.build_dir
- log_file = None
-
- log_paths = [failed_pkg.build_log]
-
- log_path = self._locate_failure_log(failed_pkg)
- if log_path is not None:
- try:
- log_file = open(log_path)
- except IOError:
- pass
-
- if log_file is not None:
- try:
- for line in log_file:
- writemsg_level(line, noiselevel=-1)
- finally:
- log_file.close()
- failure_log_shown = True
-
- # Dump mod_echo output now since it tends to flood the terminal.
- # This allows us to avoid having more important output, generated
- # later, from being swept away by the mod_echo output.
- mod_echo_output = _flush_elog_mod_echo()
-
- if background and not failure_log_shown and \
- self._failed_pkgs_all and \
- self._failed_pkgs_die_msgs and \
- not mod_echo_output:
-
- printer = portage.output.EOutput()
- for mysettings, key, logentries in self._failed_pkgs_die_msgs:
- root_msg = ""
- if mysettings["ROOT"] != "/":
- root_msg = " merged to %s" % mysettings["ROOT"]
- print
- printer.einfo("Error messages for package %s%s:" % \
- (colorize("INFORM", key), root_msg))
- print
- for phase in portage.const.EBUILD_PHASES:
- if phase not in logentries:
- continue
- for msgtype, msgcontent in logentries[phase]:
- if isinstance(msgcontent, basestring):
- msgcontent = [msgcontent]
- for line in msgcontent:
- printer.eerror(line.strip("\n"))
-
- if self._post_mod_echo_msgs:
- for msg in self._post_mod_echo_msgs:
- msg()
-
- if len(self._failed_pkgs_all) > 1 or \
- (self._failed_pkgs_all and "--keep-going" in self.myopts):
- if len(self._failed_pkgs_all) > 1:
- msg = "The following %d packages have " % \
- len(self._failed_pkgs_all) + \
- "failed to build or install:"
- else:
- msg = "The following package has " + \
- "failed to build or install:"
- prefix = bad(" * ")
- writemsg(prefix + "\n", noiselevel=-1)
- from textwrap import wrap
- for line in wrap(msg, 72):
- writemsg("%s%s\n" % (prefix, line), noiselevel=-1)
- writemsg(prefix + "\n", noiselevel=-1)
- for failed_pkg in self._failed_pkgs_all:
- writemsg("%s\t%s\n" % (prefix,
- colorize("INFORM", str(failed_pkg.pkg))),
- noiselevel=-1)
- writemsg(prefix + "\n", noiselevel=-1)
-
- return rval
-
- def _elog_listener(self, mysettings, key, logentries, fulltext):
- errors = portage.elog.filter_loglevels(logentries, ["ERROR"])
- if errors:
- self._failed_pkgs_die_msgs.append(
- (mysettings, key, errors))
-
- def _locate_failure_log(self, failed_pkg):
-
- build_dir = failed_pkg.build_dir
- log_file = None
-
- log_paths = [failed_pkg.build_log]
-
- for log_path in log_paths:
- if not log_path:
- continue
-
- try:
- log_size = os.stat(log_path).st_size
- except OSError:
- continue
-
- if log_size == 0:
- continue
-
- return log_path
-
- return None
-
- def _add_packages(self):
- pkg_queue = self._pkg_queue
- for pkg in self._mergelist:
- if isinstance(pkg, Package):
- pkg_queue.append(pkg)
- elif isinstance(pkg, Blocker):
- pass
-
- def _system_merge_started(self, merge):
- """
- Add any unsatisfied runtime deps to self._unsatisfied_system_deps.
- """
- graph = self._digraph
- if graph is None:
- return
- pkg = merge.merge.pkg
-
- # Skip this if $ROOT != / since it shouldn't matter if there
- # are unsatisfied system runtime deps in this case.
- if pkg.root != '/':
- return
-
- completed_tasks = self._completed_tasks
- unsatisfied = self._unsatisfied_system_deps
-
- def ignore_non_runtime_or_satisfied(priority):
- """
- Ignore non-runtime and satisfied runtime priorities.
- """
- if isinstance(priority, DepPriority) and \
- not priority.satisfied and \
- (priority.runtime or priority.runtime_post):
- return False
- return True
-
- # When checking for unsatisfied runtime deps, only check
- # direct deps since indirect deps are checked when the
- # corresponding parent is merged.
- for child in graph.child_nodes(pkg,
- ignore_priority=ignore_non_runtime_or_satisfied):
- if not isinstance(child, Package) or \
- child.operation == 'uninstall':
- continue
- if child is pkg:
- continue
- if child.operation == 'merge' and \
- child not in completed_tasks:
- unsatisfied.add(child)
-
- def _merge_wait_exit_handler(self, task):
- self._merge_wait_scheduled.remove(task)
- self._merge_exit(task)
-
- def _merge_exit(self, merge):
- self._do_merge_exit(merge)
- self._deallocate_config(merge.merge.settings)
- if merge.returncode == os.EX_OK and \
- not merge.merge.pkg.installed:
- self._status_display.curval += 1
- self._status_display.merges = len(self._task_queues.merge)
- self._schedule()
-
- def _do_merge_exit(self, merge):
- pkg = merge.merge.pkg
- if merge.returncode != os.EX_OK:
- settings = merge.merge.settings
- build_dir = settings.get("PORTAGE_BUILDDIR")
- build_log = settings.get("PORTAGE_LOG_FILE")
-
- self._failed_pkgs.append(self._failed_pkg(
- build_dir=build_dir, build_log=build_log,
- pkg=pkg,
- returncode=merge.returncode))
- self._failed_pkg_msg(self._failed_pkgs[-1], "install", "to")
-
- self._status_display.failed = len(self._failed_pkgs)
- return
-
- self._task_complete(pkg)
- pkg_to_replace = merge.merge.pkg_to_replace
- if pkg_to_replace is not None:
- # When a package is replaced, mark it's uninstall
- # task complete (if any).
- uninst_hash_key = \
- ("installed", pkg.root, pkg_to_replace.cpv, "uninstall")
- self._task_complete(uninst_hash_key)
-
- if pkg.installed:
- return
-
- self._restart_if_necessary(pkg)
-
- # Call mtimedb.commit() after each merge so that
- # --resume still works after being interrupted
- # by reboot, sigkill or similar.
- mtimedb = self._mtimedb
- mtimedb["resume"]["mergelist"].remove(list(pkg))
- if not mtimedb["resume"]["mergelist"]:
- del mtimedb["resume"]
- mtimedb.commit()
-
- def _build_exit(self, build):
- if build.returncode == os.EX_OK:
- self.curval += 1
- merge = PackageMerge(merge=build)
- if not build.build_opts.buildpkgonly and \
- build.pkg in self._deep_system_deps:
- # Since dependencies on system packages are frequently
- # unspecified, merge them only when no builds are executing.
- self._merge_wait_queue.append(merge)
- merge.addStartListener(self._system_merge_started)
- else:
- merge.addExitListener(self._merge_exit)
- self._task_queues.merge.add(merge)
- self._status_display.merges = len(self._task_queues.merge)
- else:
- settings = build.settings
- build_dir = settings.get("PORTAGE_BUILDDIR")
- build_log = settings.get("PORTAGE_LOG_FILE")
-
- self._failed_pkgs.append(self._failed_pkg(
- build_dir=build_dir, build_log=build_log,
- pkg=build.pkg,
- returncode=build.returncode))
- self._failed_pkg_msg(self._failed_pkgs[-1], "emerge", "for")
-
- self._status_display.failed = len(self._failed_pkgs)
- self._deallocate_config(build.settings)
- self._jobs -= 1
- self._status_display.running = self._jobs
- self._schedule()
-
- def _extract_exit(self, build):
- self._build_exit(build)
-
- def _task_complete(self, pkg):
- self._completed_tasks.add(pkg)
- self._unsatisfied_system_deps.discard(pkg)
- self._choose_pkg_return_early = False
-
- def _merge(self):
-
- self._add_prefetchers()
- self._add_packages()
- pkg_queue = self._pkg_queue
- failed_pkgs = self._failed_pkgs
- portage.locks._quiet = self._background
- portage.elog._emerge_elog_listener = self._elog_listener
- rval = os.EX_OK
-
- try:
- self._main_loop()
- finally:
- self._main_loop_cleanup()
- portage.locks._quiet = False
- portage.elog._emerge_elog_listener = None
- if failed_pkgs:
- rval = failed_pkgs[-1].returncode
-
- return rval
-
- def _main_loop_cleanup(self):
- del self._pkg_queue[:]
- self._completed_tasks.clear()
- self._deep_system_deps.clear()
- self._unsatisfied_system_deps.clear()
- self._choose_pkg_return_early = False
- self._status_display.reset()
- self._digraph = None
- self._task_queues.fetch.clear()
-
- def _choose_pkg(self):
- """
- Choose a task that has all it's dependencies satisfied.
- """
-
- if self._choose_pkg_return_early:
- return None
-
- if self._digraph is None:
- if (self._jobs or self._task_queues.merge) and \
- not ("--nodeps" in self.myopts and \
- (self._max_jobs is True or self._max_jobs > 1)):
- self._choose_pkg_return_early = True
- return None
- return self._pkg_queue.pop(0)
-
- if not (self._jobs or self._task_queues.merge):
- return self._pkg_queue.pop(0)
-
- self._prune_digraph()
-
- chosen_pkg = None
- later = set(self._pkg_queue)
- for pkg in self._pkg_queue:
- later.remove(pkg)
- if not self._dependent_on_scheduled_merges(pkg, later):
- chosen_pkg = pkg
- break
-
- if chosen_pkg is not None:
- self._pkg_queue.remove(chosen_pkg)
-
- if chosen_pkg is None:
- # There's no point in searching for a package to
- # choose until at least one of the existing jobs
- # completes.
- self._choose_pkg_return_early = True
-
- return chosen_pkg
-
- def _dependent_on_scheduled_merges(self, pkg, later):
- """
- Traverse the subgraph of the given packages deep dependencies
- to see if it contains any scheduled merges.
- @param pkg: a package to check dependencies for
- @type pkg: Package
- @param later: packages for which dependence should be ignored
- since they will be merged later than pkg anyway and therefore
- delaying the merge of pkg will not result in a more optimal
- merge order
- @type later: set
- @rtype: bool
- @returns: True if the package is dependent, False otherwise.
- """
-
- graph = self._digraph
- completed_tasks = self._completed_tasks
-
- dependent = False
- traversed_nodes = set([pkg])
- direct_deps = graph.child_nodes(pkg)
- node_stack = direct_deps
- direct_deps = frozenset(direct_deps)
- while node_stack:
- node = node_stack.pop()
- if node in traversed_nodes:
- continue
- traversed_nodes.add(node)
- if not ((node.installed and node.operation == "nomerge") or \
- (node.operation == "uninstall" and \
- node not in direct_deps) or \
- node in completed_tasks or \
- node in later):
- dependent = True
- break
- node_stack.extend(graph.child_nodes(node))
-
- return dependent
-
- def _allocate_config(self, root):
- """
- Allocate a unique config instance for a task in order
- to prevent interference between parallel tasks.
- """
- if self._config_pool[root]:
- temp_settings = self._config_pool[root].pop()
- else:
- temp_settings = portage.config(clone=self.pkgsettings[root])
- # Since config.setcpv() isn't guaranteed to call config.reset() due to
- # performance reasons, call it here to make sure all settings from the
- # previous package get flushed out (such as PORTAGE_LOG_FILE).
- temp_settings.reload()
- temp_settings.reset()
- return temp_settings
-
- def _deallocate_config(self, settings):
- self._config_pool[settings["ROOT"]].append(settings)
-
- def _main_loop(self):
-
- # Only allow 1 job max if a restart is scheduled
- # due to portage update.
- if self._is_restart_scheduled() or \
- self._opts_no_background.intersection(self.myopts):
- self._set_max_jobs(1)
-
- merge_queue = self._task_queues.merge
-
- while self._schedule():
- if self._poll_event_handlers:
- self._poll_loop()
-
- while True:
- self._schedule()
- if not (self._jobs or merge_queue):
- break
- if self._poll_event_handlers:
- self._poll_loop()
-
- def _keep_scheduling(self):
- return bool(self._pkg_queue and \
- not (self._failed_pkgs and not self._build_opts.fetchonly))
-
- def _schedule_tasks(self):
-
- # When the number of jobs drops to zero, process all waiting merges.
- if not self._jobs and self._merge_wait_queue:
- for task in self._merge_wait_queue:
- task.addExitListener(self._merge_wait_exit_handler)
- self._task_queues.merge.add(task)
- self._status_display.merges = len(self._task_queues.merge)
- self._merge_wait_scheduled.extend(self._merge_wait_queue)
- del self._merge_wait_queue[:]
-
- self._schedule_tasks_imp()
- self._status_display.display()
-
- state_change = 0
- for q in self._task_queues.values():
- if q.schedule():
- state_change += 1
-
- # Cancel prefetchers if they're the only reason
- # the main poll loop is still running.
- if self._failed_pkgs and not self._build_opts.fetchonly and \
- not (self._jobs or self._task_queues.merge) and \
- self._task_queues.fetch:
- self._task_queues.fetch.clear()
- state_change += 1
-
- if state_change:
- self._schedule_tasks_imp()
- self._status_display.display()
-
- return self._keep_scheduling()
-
- def _job_delay(self):
- """
- @rtype: bool
- @returns: True if job scheduling should be delayed, False otherwise.
- """
-
- if self._jobs and self._max_load is not None:
-
- current_time = time.time()
-
- delay = self._job_delay_factor * self._jobs ** self._job_delay_exp
- if delay > self._job_delay_max:
- delay = self._job_delay_max
- if (current_time - self._previous_job_start_time) < delay:
- return True
-
- return False
-
- def _schedule_tasks_imp(self):
- """
- @rtype: bool
- @returns: True if state changed, False otherwise.
- """
-
- state_change = 0
-
- while True:
-
- if not self._keep_scheduling():
- return bool(state_change)
-
- if self._choose_pkg_return_early or \
- self._merge_wait_scheduled or \
- (self._jobs and self._unsatisfied_system_deps) or \
- not self._can_add_job() or \
- self._job_delay():
- return bool(state_change)
-
- pkg = self._choose_pkg()
- if pkg is None:
- return bool(state_change)
-
- state_change += 1
-
- if not pkg.installed:
- self._pkg_count.curval += 1
-
- task = self._task(pkg)
-
- if pkg.installed:
- merge = PackageMerge(merge=task)
- merge.addExitListener(self._merge_exit)
- self._task_queues.merge.add(merge)
-
- elif pkg.built:
- self._jobs += 1
- self._previous_job_start_time = time.time()
- self._status_display.running = self._jobs
- task.addExitListener(self._extract_exit)
- self._task_queues.jobs.add(task)
-
- else:
- self._jobs += 1
- self._previous_job_start_time = time.time()
- self._status_display.running = self._jobs
- task.addExitListener(self._build_exit)
- self._task_queues.jobs.add(task)
-
- return bool(state_change)
-
- def _task(self, pkg):
-
- pkg_to_replace = None
- if pkg.operation != "uninstall":
- vardb = pkg.root_config.trees["vartree"].dbapi
- previous_cpv = vardb.match(pkg.slot_atom)
- if previous_cpv:
- previous_cpv = previous_cpv.pop()
- pkg_to_replace = self._pkg(previous_cpv,
- "installed", pkg.root_config, installed=True)
-
- task = MergeListItem(args_set=self._args_set,
- background=self._background, binpkg_opts=self._binpkg_opts,
- build_opts=self._build_opts,
- config_pool=self._ConfigPool(pkg.root,
- self._allocate_config, self._deallocate_config),
- emerge_opts=self.myopts,
- find_blockers=self._find_blockers(pkg), logger=self._logger,
- mtimedb=self._mtimedb, pkg=pkg, pkg_count=self._pkg_count.copy(),
- pkg_to_replace=pkg_to_replace,
- prefetcher=self._prefetchers.get(pkg),
- scheduler=self._sched_iface,
- settings=self._allocate_config(pkg.root),
- statusMessage=self._status_msg,
- world_atom=self._world_atom)
-
- return task
-
- def _failed_pkg_msg(self, failed_pkg, action, preposition):
- pkg = failed_pkg.pkg
- msg = "%s to %s %s" % \
- (bad("Failed"), action, colorize("INFORM", pkg.cpv))
- if pkg.root != "/":
- msg += " %s %s" % (preposition, pkg.root)
-
- log_path = self._locate_failure_log(failed_pkg)
- if log_path is not None:
- msg += ", Log file:"
- self._status_msg(msg)
-
- if log_path is not None:
- self._status_msg(" '%s'" % (colorize("INFORM", log_path),))
-
- def _status_msg(self, msg):
- """
- Display a brief status message (no newlines) in the status display.
- This is called by tasks to provide feedback to the user. This
- delegates the resposibility of generating \r and \n control characters,
- to guarantee that lines are created or erased when necessary and
- appropriate.
-
- @type msg: str
- @param msg: a brief status message (no newlines allowed)
- """
- if not self._background:
- writemsg_level("\n")
- self._status_display.displayMessage(msg)
-
- def _save_resume_list(self):
- """
- Do this before verifying the ebuild Manifests since it might
- be possible for the user to use --resume --skipfirst get past
- a non-essential package with a broken digest.
- """
- mtimedb = self._mtimedb
- mtimedb["resume"]["mergelist"] = [list(x) \
- for x in self._mergelist \
- if isinstance(x, Package) and x.operation == "merge"]
-
- mtimedb.commit()
-
- def _calc_resume_list(self):
- """
- Use the current resume list to calculate a new one,
- dropping any packages with unsatisfied deps.
- @rtype: bool
- @returns: True if successful, False otherwise.
- """
- print colorize("GOOD", "*** Resuming merge...")
-
- if self._show_list():
- if "--tree" in self.myopts:
- portage.writemsg_stdout("\n" + \
- darkgreen("These are the packages that " + \
- "would be merged, in reverse order:\n\n"))
-
- else:
- portage.writemsg_stdout("\n" + \
- darkgreen("These are the packages that " + \
- "would be merged, in order:\n\n"))
-
- show_spinner = "--quiet" not in self.myopts and \
- "--nodeps" not in self.myopts
-
- if show_spinner:
- print "Calculating dependencies ",
-
- myparams = create_depgraph_params(self.myopts, None)
- success = False
- e = None
- try:
- success, mydepgraph, dropped_tasks = resume_depgraph(
- self.settings, self.trees, self._mtimedb, self.myopts,
- myparams, self._spinner)
- except depgraph.UnsatisfiedResumeDep, exc:
- # rename variable to avoid python-3.0 error:
- # SyntaxError: can not delete variable 'e' referenced in nested
- # scope
- e = exc
- mydepgraph = e.depgraph
- dropped_tasks = set()
-
- if show_spinner:
- print "\b\b... done!"
-
- if e is not None:
- def unsatisfied_resume_dep_msg():
- mydepgraph.display_problems()
- out = portage.output.EOutput()
- out.eerror("One or more packages are either masked or " + \
- "have missing dependencies:")
- out.eerror("")
- indent = " "
- show_parents = set()
- for dep in e.value:
- if dep.parent in show_parents:
- continue
- show_parents.add(dep.parent)
- if dep.atom is None:
- out.eerror(indent + "Masked package:")
- out.eerror(2 * indent + str(dep.parent))
- out.eerror("")
- else:
- out.eerror(indent + str(dep.atom) + " pulled in by:")
- out.eerror(2 * indent + str(dep.parent))
- out.eerror("")
- msg = "The resume list contains packages " + \
- "that are either masked or have " + \
- "unsatisfied dependencies. " + \
- "Please restart/continue " + \
- "the operation manually, or use --skipfirst " + \
- "to skip the first package in the list and " + \
- "any other packages that may be " + \
- "masked or have missing dependencies."
- for line in textwrap.wrap(msg, 72):
- out.eerror(line)
- self._post_mod_echo_msgs.append(unsatisfied_resume_dep_msg)
- return False
-
- if success and self._show_list():
- mylist = mydepgraph.altlist()
- if mylist:
- if "--tree" in self.myopts:
- mylist.reverse()
- mydepgraph.display(mylist, favorites=self._favorites)
-
- if not success:
- self._post_mod_echo_msgs.append(mydepgraph.display_problems)
- return False
- mydepgraph.display_problems()
-
- mylist = mydepgraph.altlist()
- mydepgraph.break_refs(mylist)
- mydepgraph.break_refs(dropped_tasks)
- self._mergelist = mylist
- self._set_digraph(mydepgraph.schedulerGraph())
-
- msg_width = 75
- for task in dropped_tasks:
- if not (isinstance(task, Package) and task.operation == "merge"):
- continue
- pkg = task
- msg = "emerge --keep-going:" + \
- " %s" % (pkg.cpv,)
- if pkg.root != "/":
- msg += " for %s" % (pkg.root,)
- msg += " dropped due to unsatisfied dependency."
- for line in textwrap.wrap(msg, msg_width):
- eerror(line, phase="other", key=pkg.cpv)
- settings = self.pkgsettings[pkg.root]
- # Ensure that log collection from $T is disabled inside
- # elog_process(), since any logs that might exist are
- # not valid here.
- settings.pop("T", None)
- portage.elog.elog_process(pkg.cpv, settings)
- self._failed_pkgs_all.append(self._failed_pkg(pkg=pkg))
-
- return True
-
- def _show_list(self):
- myopts = self.myopts
- if "--quiet" not in myopts and \
- ("--ask" in myopts or "--tree" in myopts or \
- "--verbose" in myopts):
- return True
- return False
-
- def _world_atom(self, pkg):
- """
- Add the package to the world file, but only if
- it's supposed to be added. Otherwise, do nothing.
- """
-
- if set(("--buildpkgonly", "--fetchonly",
- "--fetch-all-uri",
- "--oneshot", "--onlydeps",
- "--pretend")).intersection(self.myopts):
- return
-
- if pkg.root != self.target_root:
- return
-
- args_set = self._args_set
- if not args_set.findAtomForPackage(pkg):
- return
-
- logger = self._logger
- pkg_count = self._pkg_count
- root_config = pkg.root_config
- world_set = root_config.sets["world"]
- world_locked = False
- if hasattr(world_set, "lock"):
- world_set.lock()
- world_locked = True
-
- try:
- if hasattr(world_set, "load"):
- world_set.load() # maybe it's changed on disk
-
- atom = create_world_atom(pkg, args_set, root_config)
- if atom:
- if hasattr(world_set, "add"):
- self._status_msg(('Recording %s in "world" ' + \
- 'favorites file...') % atom)
- logger.log(" === (%s of %s) Updating world file (%s)" % \
- (pkg_count.curval, pkg_count.maxval, pkg.cpv))
- world_set.add(atom)
- else:
- writemsg_level('\n!!! Unable to record %s in "world"\n' % \
- (atom,), level=logging.WARN, noiselevel=-1)
- finally:
- if world_locked:
- world_set.unlock()
-
- def _pkg(self, cpv, type_name, root_config, installed=False):
- """
- Get a package instance from the cache, or create a new
- one if necessary. Raises KeyError from aux_get if it
- failures for some reason (package does not exist or is
- corrupt).
- """
- operation = "merge"
- if installed:
- operation = "nomerge"
-
- if self._digraph is not None:
- # Reuse existing instance when available.
- pkg = self._digraph.get(
- (type_name, root_config.root, cpv, operation))
- if pkg is not None:
- return pkg
-
- tree_type = depgraph.pkg_tree_map[type_name]
- db = root_config.trees[tree_type].dbapi
- db_keys = list(self.trees[root_config.root][
- tree_type].dbapi._aux_cache_keys)
- metadata = izip(db_keys, db.aux_get(cpv, db_keys))
- pkg = Package(cpv=cpv, metadata=metadata,
- root_config=root_config, installed=installed)
- if type_name == "ebuild":
- settings = self.pkgsettings[root_config.root]
- settings.setcpv(pkg)
- pkg.metadata["USE"] = settings["PORTAGE_USE"]
- pkg.metadata['CHOST'] = settings.get('CHOST', '')
-
- return pkg
-
def chk_updated_info_files(root, infodirs, prev_mtimes, retval):
if os.path.exists("/usr/bin/install-info"):
@@ -6958,23 +395,6 @@ def display_preserved_libs(vardbapi):
print "Use " + colorize("GOOD", "emerge @preserved-rebuild") + " to rebuild packages using these libraries"
-def _flush_elog_mod_echo():
- """
- Dump the mod_echo output now so that our other
- notifications are shown last.
- @rtype: bool
- @returns: True if messages were shown, False otherwise.
- """
- messages_shown = False
- try:
- from portage.elog import mod_echo
- except ImportError:
- pass # happens during downgrade to a version without the module
- else:
- messages_shown = bool(mod_echo._items)
- mod_echo.finalize()
- return messages_shown
-
def post_emerge(root_config, myopts, mtimedb, retval):
"""
Misc. things to run at the end of a merge session.
@@ -7134,34 +554,6 @@ def checkUpdatedNewsItems(portdb, vardb, NEWS_PATH, UNREAD_PATH, repo_id,
manager = NewsManager(portdb, vardb, NEWS_PATH, UNREAD_PATH)
return manager.getUnreadItems( repo_id, update=update )
-def insert_category_into_atom(atom, category):
- alphanum = re.search(r'\w', atom)
- if alphanum:
- ret = atom[:alphanum.start()] + "%s/" % category + \
- atom[alphanum.start():]
- else:
- ret = None
- return ret
-
-def is_valid_package_atom(x):
- if "/" not in x:
- alphanum = re.search(r'\w', x)
- if alphanum:
- x = x[:alphanum.start()] + "cat/" + x[alphanum.start():]
- return portage.isvalidatom(x)
-
-def show_blocker_docs_link():
- print
- print "For more information about " + bad("Blocked Packages") + ", please refer to the following"
- print "section of the Gentoo Linux x86 Handbook (architecture is irrelevant):"
- print
- print "http://www.gentoo.org/doc/en/handbook/handbook-x86.xml?full=1#blocked"
- print
-
-def show_mask_docs():
- print "For more information, see the MASKED PACKAGES section in the emerge"
- print "man page or refer to the Gentoo Handbook."
-
def action_sync(settings, trees, mtimedb, myopts, myaction):
xterm_titles = "notitles" not in settings.features
emergelog(xterm_titles, " === sync")
@@ -9108,80 +2500,6 @@ def action_depclean(settings, trees, ldpath_mtimes,
else:
print "Number removed: "+str(len(cleanlist))
-def resume_depgraph(settings, trees, mtimedb, myopts, myparams, spinner):
- """
- Construct a depgraph for the given resume list. This will raise
- PackageNotFound or depgraph.UnsatisfiedResumeDep when necessary.
- @rtype: tuple
- @returns: (success, depgraph, dropped_tasks)
- """
- skip_masked = True
- skip_unsatisfied = True
- mergelist = mtimedb["resume"]["mergelist"]
- dropped_tasks = set()
- while True:
- mydepgraph = depgraph(settings, trees,
- myopts, myparams, spinner)
- try:
- success = mydepgraph.loadResumeCommand(mtimedb["resume"],
- skip_masked=skip_masked)
- except depgraph.UnsatisfiedResumeDep, e:
- if not skip_unsatisfied:
- raise
-
- graph = mydepgraph.digraph
- unsatisfied_parents = dict((dep.parent, dep.parent) \
- for dep in e.value)
- traversed_nodes = set()
- unsatisfied_stack = list(unsatisfied_parents)
- while unsatisfied_stack:
- pkg = unsatisfied_stack.pop()
- if pkg in traversed_nodes:
- continue
- traversed_nodes.add(pkg)
-
- # If this package was pulled in by a parent
- # package scheduled for merge, removing this
- # package may cause the the parent package's
- # dependency to become unsatisfied.
- for parent_node in graph.parent_nodes(pkg):
- if not isinstance(parent_node, Package) \
- or parent_node.operation not in ("merge", "nomerge"):
- continue
- unsatisfied = \
- graph.child_nodes(parent_node,
- ignore_priority=DepPrioritySatisfiedRange.ignore_soft)
- if pkg in unsatisfied:
- unsatisfied_parents[parent_node] = parent_node
- unsatisfied_stack.append(parent_node)
-
- pruned_mergelist = []
- for x in mergelist:
- if isinstance(x, list) and \
- tuple(x) not in unsatisfied_parents:
- pruned_mergelist.append(x)
-
- # If the mergelist doesn't shrink then this loop is infinite.
- if len(pruned_mergelist) == len(mergelist):
- # This happens if a package can't be dropped because
- # it's already installed, but it has unsatisfied PDEPEND.
- raise
- mergelist[:] = pruned_mergelist
-
- # Exclude installed packages that have been removed from the graph due
- # to failure to build/install runtime dependencies after the dependent
- # package has already been installed.
- dropped_tasks.update(pkg for pkg in \
- unsatisfied_parents if pkg.operation != "nomerge")
- mydepgraph.break_refs(unsatisfied_parents)
-
- del e, graph, traversed_nodes, \
- unsatisfied_parents, unsatisfied_stack
- continue
- else:
- break
- return (success, mydepgraph, dropped_tasks)
-
def action_build(settings, trees, mtimedb,
myopts, myaction, myfiles, spinner):
@@ -9847,16 +3165,6 @@ def validate_ebuild_environment(trees):
settings = trees[myroot]["vartree"].settings
settings.validate()
-def clear_caches(trees):
- for d in trees.itervalues():
- d["porttree"].dbapi.melt()
- d["porttree"].dbapi._aux_cache.clear()
- d["bintree"].dbapi._aux_cache.clear()
- d["bintree"].dbapi._clear_cache()
- d["vartree"].dbapi.linkmap._clear_cache()
- portage.dircache.clear()
- gc.collect()
-
def load_emerge_config(trees=None):
kwargs = {}
for k, envvar in (("config_root", "PORTAGE_CONFIGROOT"), ("target_root", "ROOT")):
@@ -10255,28 +3563,6 @@ def config_protect_check(trees):
msg += " for '%s'" % root
writemsg_level(msg, level=logging.WARN, noiselevel=-1)
-def ambiguous_package_name(arg, atoms, root_config, spinner, myopts):
-
- if "--quiet" in myopts:
- print "!!! The short ebuild name \"%s\" is ambiguous. Please specify" % arg
- print "!!! one of the following fully-qualified ebuild names instead:\n"
- for cp in sorted(set(portage.dep_getkey(atom) for atom in atoms)):
- print " " + colorize("INFORM", cp)
- return
-
- s = search(root_config, spinner, "--searchdesc" in myopts,
- "--quiet" not in myopts, "--usepkg" in myopts,
- "--usepkgonly" in myopts)
- null_cp = portage.dep_getkey(insert_category_into_atom(
- arg, "null"))
- cat, atom_pn = portage.catsplit(null_cp)
- s.searchkey = atom_pn
- for cp in sorted(set(portage.dep_getkey(atom) for atom in atoms)):
- s.addCP(cp)
- s.output()
- print "!!! The short ebuild name \"%s\" is ambiguous. Please specify" % arg
- print "!!! one of the above fully-qualified ebuild names instead.\n"
-
def profile_check(trees, myaction, myopts):
if myaction in ("info", "sync"):
return os.EX_OK
diff --git a/pym/_emerge/_find_deep_system_runtime_deps.py b/pym/_emerge/_find_deep_system_runtime_deps.py
new file mode 100644
index 000000000..3249168f2
--- /dev/null
+++ b/pym/_emerge/_find_deep_system_runtime_deps.py
@@ -0,0 +1,35 @@
+from _emerge.DepPriority import DepPriority
+from _emerge.Package import Package
+
+def _find_deep_system_runtime_deps(graph):
+ deep_system_deps = set()
+ node_stack = []
+ for node in graph:
+ if not isinstance(node, Package) or \
+ node.operation == 'uninstall':
+ continue
+ if node.root_config.sets['system'].findAtomForPackage(node):
+ node_stack.append(node)
+
+ def ignore_priority(priority):
+ """
+ Ignore non-runtime priorities.
+ """
+ if isinstance(priority, DepPriority) and \
+ (priority.runtime or priority.runtime_post):
+ return False
+ return True
+
+ while node_stack:
+ node = node_stack.pop()
+ if node in deep_system_deps:
+ continue
+ deep_system_deps.add(node)
+ for child in graph.child_nodes(node, ignore_priority=ignore_priority):
+ if not isinstance(child, Package) or \
+ child.operation == 'uninstall':
+ continue
+ node_stack.append(child)
+
+ return deep_system_deps
+
diff --git a/pym/_emerge/_flush_elog_mod_echo.py b/pym/_emerge/_flush_elog_mod_echo.py
new file mode 100644
index 000000000..61eeb29f7
--- /dev/null
+++ b/pym/_emerge/_flush_elog_mod_echo.py
@@ -0,0 +1,17 @@
+def _flush_elog_mod_echo():
+ """
+ Dump the mod_echo output now so that our other
+ notifications are shown last.
+ @rtype: bool
+ @returns: True if messages were shown, False otherwise.
+ """
+ messages_shown = False
+ try:
+ from portage.elog import mod_echo
+ except ImportError:
+ pass # happens during downgrade to a version without the module
+ else:
+ messages_shown = bool(mod_echo._items)
+ mod_echo.finalize()
+ return messages_shown
+
diff --git a/pym/_emerge/clear_caches.py b/pym/_emerge/clear_caches.py
new file mode 100644
index 000000000..ce903ba08
--- /dev/null
+++ b/pym/_emerge/clear_caches.py
@@ -0,0 +1,19 @@
+import gc
+
+try:
+ import portage
+except ImportError:
+ from os import path as osp
+ import sys
+ sys.path.insert(0, osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), "pym"))
+ import portage
+
+def clear_caches(trees):
+ for d in trees.itervalues():
+ d["porttree"].dbapi.melt()
+ d["porttree"].dbapi._aux_cache.clear()
+ d["bintree"].dbapi._aux_cache.clear()
+ d["bintree"].dbapi._clear_cache()
+ d["vartree"].dbapi.linkmap._clear_cache()
+ portage.dircache.clear()
+ gc.collect()
diff --git a/pym/_emerge/create_depgraph_params.py b/pym/_emerge/create_depgraph_params.py
new file mode 100644
index 000000000..fab741be5
--- /dev/null
+++ b/pym/_emerge/create_depgraph_params.py
@@ -0,0 +1,33 @@
+def create_depgraph_params(myopts, myaction):
+ #configure emerge engine parameters
+ #
+ # self: include _this_ package regardless of if it is merged.
+ # selective: exclude the package if it is merged
+ # recurse: go into the dependencies
+ # deep: go into the dependencies of already merged packages
+ # empty: pretend nothing is merged
+ # complete: completely account for all known dependencies
+ # remove: build graph for use in removing packages
+ myparams = set(["recurse"])
+
+ if myaction == "remove":
+ myparams.add("remove")
+ myparams.add("complete")
+ return myparams
+
+ if "--update" in myopts or \
+ "--newuse" in myopts or \
+ "--reinstall" in myopts or \
+ "--noreplace" in myopts:
+ myparams.add("selective")
+ if "--emptytree" in myopts:
+ myparams.add("empty")
+ myparams.discard("selective")
+ if "--nodeps" in myopts:
+ myparams.discard("recurse")
+ if "--deep" in myopts:
+ myparams.add("deep")
+ if "--complete-graph" in myopts:
+ myparams.add("complete")
+ return myparams
+
diff --git a/pym/_emerge/create_world_atom.py b/pym/_emerge/create_world_atom.py
new file mode 100644
index 000000000..1b13c4058
--- /dev/null
+++ b/pym/_emerge/create_world_atom.py
@@ -0,0 +1,91 @@
+try:
+ import portage
+except ImportError:
+ from os import path as osp
+ import sys
+ sys.path.insert(0, osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), "pym"))
+ import portage
+
+def create_world_atom(pkg, args_set, root_config):
+ """Create a new atom for the world file if one does not exist. If the
+ argument atom is precise enough to identify a specific slot then a slot
+ atom will be returned. Atoms that are in the system set may also be stored
+ in world since system atoms can only match one slot while world atoms can
+ be greedy with respect to slots. Unslotted system packages will not be
+ stored in world."""
+
+ arg_atom = args_set.findAtomForPackage(pkg)
+ if not arg_atom:
+ return None
+ cp = portage.dep_getkey(arg_atom)
+ new_world_atom = cp
+ sets = root_config.sets
+ portdb = root_config.trees["porttree"].dbapi
+ vardb = root_config.trees["vartree"].dbapi
+ available_slots = set(portdb.aux_get(cpv, ["SLOT"])[0] \
+ for cpv in portdb.match(cp))
+ slotted = len(available_slots) > 1 or \
+ (len(available_slots) == 1 and "0" not in available_slots)
+ if not slotted:
+ # check the vdb in case this is multislot
+ available_slots = set(vardb.aux_get(cpv, ["SLOT"])[0] \
+ for cpv in vardb.match(cp))
+ slotted = len(available_slots) > 1 or \
+ (len(available_slots) == 1 and "0" not in available_slots)
+ if slotted and arg_atom != cp:
+ # If the user gave a specific atom, store it as a
+ # slot atom in the world file.
+ slot_atom = pkg.slot_atom
+
+ # For USE=multislot, there are a couple of cases to
+ # handle here:
+ #
+ # 1) SLOT="0", but the real SLOT spontaneously changed to some
+ # unknown value, so just record an unslotted atom.
+ #
+ # 2) SLOT comes from an installed package and there is no
+ # matching SLOT in the portage tree.
+ #
+ # Make sure that the slot atom is available in either the
+ # portdb or the vardb, since otherwise the user certainly
+ # doesn't want the SLOT atom recorded in the world file
+ # (case 1 above). If it's only available in the vardb,
+ # the user may be trying to prevent a USE=multislot
+ # package from being removed by --depclean (case 2 above).
+
+ mydb = portdb
+ if not portdb.match(slot_atom):
+ # SLOT seems to come from an installed multislot package
+ mydb = vardb
+ # If there is no installed package matching the SLOT atom,
+ # it probably changed SLOT spontaneously due to USE=multislot,
+ # so just record an unslotted atom.
+ if vardb.match(slot_atom):
+ # Now verify that the argument is precise
+ # enough to identify a specific slot.
+ matches = mydb.match(arg_atom)
+ matched_slots = set()
+ for cpv in matches:
+ matched_slots.add(mydb.aux_get(cpv, ["SLOT"])[0])
+ if len(matched_slots) == 1:
+ new_world_atom = slot_atom
+
+ if new_world_atom == sets["world"].findAtomForPackage(pkg):
+ # Both atoms would be identical, so there's nothing to add.
+ return None
+ if not slotted:
+ # Unlike world atoms, system atoms are not greedy for slots, so they
+ # can't be safely excluded from world if they are slotted.
+ system_atom = sets["system"].findAtomForPackage(pkg)
+ if system_atom:
+ if not portage.dep_getkey(system_atom).startswith("virtual/"):
+ return None
+ # System virtuals aren't safe to exclude from world since they can
+ # match multiple old-style virtuals but only one of them will be
+ # pulled in by update or depclean.
+ providers = portdb.mysettings.getvirtuals().get(
+ portage.dep_getkey(system_atom))
+ if providers and len(providers) == 1 and providers[0] == cp:
+ return None
+ return new_world_atom
+
diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py
new file mode 100644
index 000000000..b5901285b
--- /dev/null
+++ b/pym/_emerge/depgraph.py
@@ -0,0 +1,4973 @@
+import gc
+import os
+import re
+import sys
+import textwrap
+from itertools import chain, izip
+
+try:
+ import portage
+except ImportError:
+ from os import path as osp
+ sys.path.insert(0, osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), "pym"))
+ import portage
+
+from portage import digraph
+from portage.output import bold, blue, colorize, create_color_func, darkblue, \
+ darkgreen, green, nc_len, red, teal, turquoise, yellow
+bad = create_color_func("BAD")
+from portage.sets import SETPREFIX
+from portage.sets.base import InternalPackageSet
+from portage.util import cmp_sort_key, writemsg
+
+from _emerge.AtomArg import AtomArg
+from _emerge.Blocker import Blocker
+from _emerge.BlockerCache import BlockerCache
+from _emerge.BlockerDepPriority import BlockerDepPriority
+from _emerge.countdown import countdown
+from _emerge.create_world_atom import create_world_atom
+from _emerge.Dependency import Dependency
+from _emerge.DependencyArg import DependencyArg
+from _emerge.DepPriority import DepPriority
+from _emerge.DepPriorityNormalRange import DepPriorityNormalRange
+from _emerge.DepPrioritySatisfiedRange import DepPrioritySatisfiedRange
+from _emerge.FakeVartree import FakeVartree
+from _emerge._find_deep_system_runtime_deps import _find_deep_system_runtime_deps
+from _emerge.format_size import format_size
+from _emerge.is_valid_package_atom import is_valid_package_atom
+from _emerge.Package import Package
+from _emerge.PackageArg import PackageArg
+from _emerge.PackageCounters import PackageCounters
+from _emerge.PackageVirtualDbapi import PackageVirtualDbapi
+from _emerge.RepoDisplay import RepoDisplay
+from _emerge.RootConfig import RootConfig
+from _emerge.search import search
+from _emerge.SetArg import SetArg
+from _emerge.show_invalid_depstring_notice import show_invalid_depstring_notice
+from _emerge.UnmergeDepPriority import UnmergeDepPriority
+from _emerge.visible import visible
+
+import portage.proxy.lazyimport
+import portage.proxy as proxy
+proxy.lazyimport.lazyimport(globals(),
+ '_emerge.Scheduler:Scheduler',
+)
+#from _emerge.Scheduler import Scheduler
+class depgraph(object):
+
+ pkg_tree_map = RootConfig.pkg_tree_map
+
+ _dep_keys = ["DEPEND", "RDEPEND", "PDEPEND"]
+
+ def __init__(self, settings, trees, myopts, myparams, spinner):
+ self.settings = settings
+ self.target_root = settings["ROOT"]
+ self.myopts = myopts
+ self.myparams = myparams
+ self.edebug = 0
+ if settings.get("PORTAGE_DEBUG", "") == "1":
+ self.edebug = 1
+ self.spinner = spinner
+ self._running_root = trees["/"]["root_config"]
+ self._opts_no_restart = Scheduler._opts_no_restart
+ self.pkgsettings = {}
+ # Maps slot atom to package for each Package added to the graph.
+ self._slot_pkg_map = {}
+ # Maps nodes to the reasons they were selected for reinstallation.
+ self._reinstall_nodes = {}
+ self.mydbapi = {}
+ self.trees = {}
+ self._trees_orig = trees
+ self.roots = {}
+ # Contains a filtered view of preferred packages that are selected
+ # from available repositories.
+ self._filtered_trees = {}
+ # Contains installed packages and new packages that have been added
+ # to the graph.
+ self._graph_trees = {}
+ # All Package instances
+ self._pkg_cache = {}
+ for myroot in trees:
+ self.trees[myroot] = {}
+ # Create a RootConfig instance that references
+ # the FakeVartree instead of the real one.
+ self.roots[myroot] = RootConfig(
+ trees[myroot]["vartree"].settings,
+ self.trees[myroot],
+ trees[myroot]["root_config"].setconfig)
+ for tree in ("porttree", "bintree"):
+ self.trees[myroot][tree] = trees[myroot][tree]
+ self.trees[myroot]["vartree"] = \
+ FakeVartree(trees[myroot]["root_config"],
+ pkg_cache=self._pkg_cache)
+ self.pkgsettings[myroot] = portage.config(
+ clone=self.trees[myroot]["vartree"].settings)
+ self._slot_pkg_map[myroot] = {}
+ vardb = self.trees[myroot]["vartree"].dbapi
+ preload_installed_pkgs = "--nodeps" not in self.myopts and \
+ "--buildpkgonly" not in self.myopts
+ # This fakedbapi instance will model the state that the vdb will
+ # have after new packages have been installed.
+ fakedb = PackageVirtualDbapi(vardb.settings)
+ if preload_installed_pkgs:
+ for pkg in vardb:
+ self.spinner.update()
+ # This triggers metadata updates via FakeVartree.
+ vardb.aux_get(pkg.cpv, [])
+ fakedb.cpv_inject(pkg)
+
+ # Now that the vardb state is cached in our FakeVartree,
+ # we won't be needing the real vartree cache for awhile.
+ # To make some room on the heap, clear the vardbapi
+ # caches.
+ trees[myroot]["vartree"].dbapi._clear_cache()
+ gc.collect()
+
+ self.mydbapi[myroot] = fakedb
+ def graph_tree():
+ pass
+ graph_tree.dbapi = fakedb
+ self._graph_trees[myroot] = {}
+ self._filtered_trees[myroot] = {}
+ # Substitute the graph tree for the vartree in dep_check() since we
+ # want atom selections to be consistent with package selections
+ # have already been made.
+ self._graph_trees[myroot]["porttree"] = graph_tree
+ self._graph_trees[myroot]["vartree"] = graph_tree
+ def filtered_tree():
+ pass
+ filtered_tree.dbapi = self._dep_check_composite_db(self, myroot)
+ self._filtered_trees[myroot]["porttree"] = filtered_tree
+
+ # Passing in graph_tree as the vartree here could lead to better
+ # atom selections in some cases by causing atoms for packages that
+ # have been added to the graph to be preferred over other choices.
+ # However, it can trigger atom selections that result in
+ # unresolvable direct circular dependencies. For example, this
+ # happens with gwydion-dylan which depends on either itself or
+ # gwydion-dylan-bin. In case gwydion-dylan is not yet installed,
+ # gwydion-dylan-bin needs to be selected in order to avoid a
+ # an unresolvable direct circular dependency.
+ #
+ # To solve the problem described above, pass in "graph_db" so that
+ # packages that have been added to the graph are distinguishable
+ # from other available packages and installed packages. Also, pass
+ # the parent package into self._select_atoms() calls so that
+ # unresolvable direct circular dependencies can be detected and
+ # avoided when possible.
+ self._filtered_trees[myroot]["graph_db"] = graph_tree.dbapi
+ self._filtered_trees[myroot]["vartree"] = self.trees[myroot]["vartree"]
+
+ dbs = []
+ portdb = self.trees[myroot]["porttree"].dbapi
+ bindb = self.trees[myroot]["bintree"].dbapi
+ vardb = self.trees[myroot]["vartree"].dbapi
+ # (db, pkg_type, built, installed, db_keys)
+ if "--usepkgonly" not in self.myopts:
+ db_keys = list(portdb._aux_cache_keys)
+ dbs.append((portdb, "ebuild", False, False, db_keys))
+ if "--usepkg" in self.myopts:
+ db_keys = list(bindb._aux_cache_keys)
+ dbs.append((bindb, "binary", True, False, db_keys))
+ db_keys = list(trees[myroot]["vartree"].dbapi._aux_cache_keys)
+ dbs.append((vardb, "installed", True, True, db_keys))
+ self._filtered_trees[myroot]["dbs"] = dbs
+ if "--usepkg" in self.myopts:
+ self.trees[myroot]["bintree"].populate(
+ "--getbinpkg" in self.myopts,
+ "--getbinpkgonly" in self.myopts)
+ del trees
+
+ self.digraph=portage.digraph()
+ # contains all sets added to the graph
+ self._sets = {}
+ # contains atoms given as arguments
+ self._sets["args"] = InternalPackageSet()
+ # contains all atoms from all sets added to the graph, including
+ # atoms given as arguments
+ self._set_atoms = InternalPackageSet()
+ self._atom_arg_map = {}
+ # contains all nodes pulled in by self._set_atoms
+ self._set_nodes = set()
+ # Contains only Blocker -> Uninstall edges
+ self._blocker_uninstalls = digraph()
+ # Contains only Package -> Blocker edges
+ self._blocker_parents = digraph()
+ # Contains only irrelevant Package -> Blocker edges
+ self._irrelevant_blockers = digraph()
+ # Contains only unsolvable Package -> Blocker edges
+ self._unsolvable_blockers = digraph()
+ # Contains all Blocker -> Blocked Package edges
+ self._blocked_pkgs = digraph()
+ # Contains world packages that have been protected from
+ # uninstallation but may not have been added to the graph
+ # if the graph is not complete yet.
+ self._blocked_world_pkgs = {}
+ self._slot_collision_info = {}
+ # Slot collision nodes are not allowed to block other packages since
+ # blocker validation is only able to account for one package per slot.
+ self._slot_collision_nodes = set()
+ self._parent_atoms = {}
+ self._slot_conflict_parent_atoms = set()
+ self._serialized_tasks_cache = None
+ self._scheduler_graph = None
+ self._displayed_list = None
+ self._pprovided_args = []
+ self._missing_args = []
+ self._masked_installed = set()
+ self._unsatisfied_deps_for_display = []
+ self._unsatisfied_blockers_for_display = None
+ self._circular_deps_for_display = None
+ self._dep_stack = []
+ self._dep_disjunctive_stack = []
+ self._unsatisfied_deps = []
+ self._initially_unsatisfied_deps = []
+ self._ignored_deps = []
+ self._required_set_names = set(["system", "world"])
+ self._select_atoms = self._select_atoms_highest_available
+ self._select_package = self._select_pkg_highest_available
+ self._highest_pkg_cache = {}
+
+ def _show_slot_collision_notice(self):
+ """Show an informational message advising the user to mask one of the
+ the packages. In some cases it may be possible to resolve this
+ automatically, but support for backtracking (removal nodes that have
+ already been selected) will be required in order to handle all possible
+ cases.
+ """
+
+ if not self._slot_collision_info:
+ return
+
+ self._show_merge_list()
+
+ msg = []
+ msg.append("\n!!! Multiple package instances within a single " + \
+ "package slot have been pulled\n")
+ msg.append("!!! into the dependency graph, resulting" + \
+ " in a slot conflict:\n\n")
+ indent = " "
+ # Max number of parents shown, to avoid flooding the display.
+ max_parents = 3
+ explanation_columns = 70
+ explanations = 0
+ for (slot_atom, root), slot_nodes \
+ in self._slot_collision_info.iteritems():
+ msg.append(str(slot_atom))
+ msg.append("\n\n")
+
+ for node in slot_nodes:
+ msg.append(indent)
+ msg.append(str(node))
+ parent_atoms = self._parent_atoms.get(node)
+ if parent_atoms:
+ pruned_list = set()
+ # Prefer conflict atoms over others.
+ for parent_atom in parent_atoms:
+ if len(pruned_list) >= max_parents:
+ break
+ if parent_atom in self._slot_conflict_parent_atoms:
+ pruned_list.add(parent_atom)
+
+ # If this package was pulled in by conflict atoms then
+ # show those alone since those are the most interesting.
+ if not pruned_list:
+ # When generating the pruned list, prefer instances
+ # of DependencyArg over instances of Package.
+ for parent_atom in parent_atoms:
+ if len(pruned_list) >= max_parents:
+ break
+ parent, atom = parent_atom
+ if isinstance(parent, DependencyArg):
+ pruned_list.add(parent_atom)
+ # Prefer Packages instances that themselves have been
+ # pulled into collision slots.
+ for parent_atom in parent_atoms:
+ if len(pruned_list) >= max_parents:
+ break
+ parent, atom = parent_atom
+ if isinstance(parent, Package) and \
+ (parent.slot_atom, parent.root) \
+ in self._slot_collision_info:
+ pruned_list.add(parent_atom)
+ for parent_atom in parent_atoms:
+ if len(pruned_list) >= max_parents:
+ break
+ pruned_list.add(parent_atom)
+ omitted_parents = len(parent_atoms) - len(pruned_list)
+ parent_atoms = pruned_list
+ msg.append(" pulled in by\n")
+ for parent_atom in parent_atoms:
+ parent, atom = parent_atom
+ msg.append(2*indent)
+ if isinstance(parent,
+ (PackageArg, AtomArg)):
+ # For PackageArg and AtomArg types, it's
+ # redundant to display the atom attribute.
+ msg.append(str(parent))
+ else:
+ # Display the specific atom from SetArg or
+ # Package types.
+ msg.append("%s required by %s" % (atom, parent))
+ msg.append("\n")
+ if omitted_parents:
+ msg.append(2*indent)
+ msg.append("(and %d more)\n" % omitted_parents)
+ else:
+ msg.append(" (no parents)\n")
+ msg.append("\n")
+ explanation = self._slot_conflict_explanation(slot_nodes)
+ if explanation:
+ explanations += 1
+ msg.append(indent + "Explanation:\n\n")
+ for line in textwrap.wrap(explanation, explanation_columns):
+ msg.append(2*indent + line + "\n")
+ msg.append("\n")
+ msg.append("\n")
+ sys.stderr.write("".join(msg))
+ sys.stderr.flush()
+
+ explanations_for_all = explanations == len(self._slot_collision_info)
+
+ if explanations_for_all or "--quiet" in self.myopts:
+ return
+
+ msg = []
+ msg.append("It may be possible to solve this problem ")
+ msg.append("by using package.mask to prevent one of ")
+ msg.append("those packages from being selected. ")
+ msg.append("However, it is also possible that conflicting ")
+ msg.append("dependencies exist such that they are impossible to ")
+ msg.append("satisfy simultaneously. If such a conflict exists in ")
+ msg.append("the dependencies of two different packages, then those ")
+ msg.append("packages can not be installed simultaneously.")
+
+ from formatter import AbstractFormatter, DumbWriter
+ f = AbstractFormatter(DumbWriter(sys.stderr, maxcol=72))
+ for x in msg:
+ f.add_flowing_data(x)
+ f.end_paragraph(1)
+
+ msg = []
+ msg.append("For more information, see MASKED PACKAGES ")
+ msg.append("section in the emerge man page or refer ")
+ msg.append("to the Gentoo Handbook.")
+ for x in msg:
+ f.add_flowing_data(x)
+ f.end_paragraph(1)
+ f.writer.flush()
+
+ def _slot_conflict_explanation(self, slot_nodes):
+ """
+ When a slot conflict occurs due to USE deps, there are a few
+ different cases to consider:
+
+ 1) New USE are correctly set but --newuse wasn't requested so an
+ installed package with incorrect USE happened to get pulled
+ into graph before the new one.
+
+ 2) New USE are incorrectly set but an installed package has correct
+ USE so it got pulled into the graph, and a new instance also got
+ pulled in due to --newuse or an upgrade.
+
+ 3) Multiple USE deps exist that can't be satisfied simultaneously,
+ and multiple package instances got pulled into the same slot to
+ satisfy the conflicting deps.
+
+ Currently, explanations and suggested courses of action are generated
+ for cases 1 and 2. Case 3 is too complex to give a useful suggestion.
+ """
+
+ if len(slot_nodes) != 2:
+ # Suggestions are only implemented for
+ # conflicts between two packages.
+ return None
+
+ all_conflict_atoms = self._slot_conflict_parent_atoms
+ matched_node = None
+ matched_atoms = None
+ unmatched_node = None
+ for node in slot_nodes:
+ parent_atoms = self._parent_atoms.get(node)
+ if not parent_atoms:
+ # Normally, there are always parent atoms. If there are
+ # none then something unexpected is happening and there's
+ # currently no suggestion for this case.
+ return None
+ conflict_atoms = all_conflict_atoms.intersection(parent_atoms)
+ for parent_atom in conflict_atoms:
+ parent, atom = parent_atom
+ if not atom.use:
+ # Suggestions are currently only implemented for cases
+ # in which all conflict atoms have USE deps.
+ return None
+ if conflict_atoms:
+ if matched_node is not None:
+ # If conflict atoms match multiple nodes
+ # then there's no suggestion.
+ return None
+ matched_node = node
+ matched_atoms = conflict_atoms
+ else:
+ if unmatched_node is not None:
+ # Neither node is matched by conflict atoms, and
+ # there is no suggestion for this case.
+ return None
+ unmatched_node = node
+
+ if matched_node is None or unmatched_node is None:
+ # This shouldn't happen.
+ return None
+
+ if unmatched_node.installed and not matched_node.installed and \
+ unmatched_node.cpv == matched_node.cpv:
+ # If the conflicting packages are the same version then
+ # --newuse should be all that's needed. If they are different
+ # versions then there's some other problem.
+ return "New USE are correctly set, but --newuse wasn't" + \
+ " requested, so an installed package with incorrect USE " + \
+ "happened to get pulled into the dependency graph. " + \
+ "In order to solve " + \
+ "this, either specify the --newuse option or explicitly " + \
+ " reinstall '%s'." % matched_node.slot_atom
+
+ if matched_node.installed and not unmatched_node.installed:
+ atoms = sorted(set(atom for parent, atom in matched_atoms))
+ explanation = ("New USE for '%s' are incorrectly set. " + \
+ "In order to solve this, adjust USE to satisfy '%s'") % \
+ (matched_node.slot_atom, atoms[0])
+ if len(atoms) > 1:
+ for atom in atoms[1:-1]:
+ explanation += ", '%s'" % (atom,)
+ if len(atoms) > 2:
+ explanation += ","
+ explanation += " and '%s'" % (atoms[-1],)
+ explanation += "."
+ return explanation
+
+ return None
+
+ def _process_slot_conflicts(self):
+ """
+ Process slot conflict data to identify specific atoms which
+ lead to conflict. These atoms only match a subset of the
+ packages that have been pulled into a given slot.
+ """
+ for (slot_atom, root), slot_nodes \
+ in self._slot_collision_info.iteritems():
+
+ all_parent_atoms = set()
+ for pkg in slot_nodes:
+ parent_atoms = self._parent_atoms.get(pkg)
+ if not parent_atoms:
+ continue
+ all_parent_atoms.update(parent_atoms)
+
+ for pkg in slot_nodes:
+ parent_atoms = self._parent_atoms.get(pkg)
+ if parent_atoms is None:
+ parent_atoms = set()
+ self._parent_atoms[pkg] = parent_atoms
+ for parent_atom in all_parent_atoms:
+ if parent_atom in parent_atoms:
+ continue
+ # Use package set for matching since it will match via
+ # PROVIDE when necessary, while match_from_list does not.
+ parent, atom = parent_atom
+ atom_set = InternalPackageSet(
+ initial_atoms=(atom,))
+ if atom_set.findAtomForPackage(pkg):
+ parent_atoms.add(parent_atom)
+ else:
+ self._slot_conflict_parent_atoms.add(parent_atom)
+
+ def _reinstall_for_flags(self, forced_flags,
+ orig_use, orig_iuse, cur_use, cur_iuse):
+ """Return a set of flags that trigger reinstallation, or None if there
+ are no such flags."""
+ if "--newuse" in self.myopts:
+ flags = set(orig_iuse.symmetric_difference(
+ cur_iuse).difference(forced_flags))
+ flags.update(orig_iuse.intersection(orig_use).symmetric_difference(
+ cur_iuse.intersection(cur_use)))
+ if flags:
+ return flags
+ elif "changed-use" == self.myopts.get("--reinstall"):
+ flags = orig_iuse.intersection(orig_use).symmetric_difference(
+ cur_iuse.intersection(cur_use))
+ if flags:
+ return flags
+ return None
+
+ def _create_graph(self, allow_unsatisfied=False):
+ dep_stack = self._dep_stack
+ dep_disjunctive_stack = self._dep_disjunctive_stack
+ while dep_stack or dep_disjunctive_stack:
+ self.spinner.update()
+ while dep_stack:
+ dep = dep_stack.pop()
+ if isinstance(dep, Package):
+ if not self._add_pkg_deps(dep,
+ allow_unsatisfied=allow_unsatisfied):
+ return 0
+ continue
+ if not self._add_dep(dep, allow_unsatisfied=allow_unsatisfied):
+ return 0
+ if dep_disjunctive_stack:
+ if not self._pop_disjunction(allow_unsatisfied):
+ return 0
+ return 1
+
+ def _add_dep(self, dep, allow_unsatisfied=False):
+ debug = "--debug" in self.myopts
+ buildpkgonly = "--buildpkgonly" in self.myopts
+ nodeps = "--nodeps" in self.myopts
+ empty = "empty" in self.myparams
+ deep = "deep" in self.myparams
+ update = "--update" in self.myopts and dep.depth <= 1
+ if dep.blocker:
+ if not buildpkgonly and \
+ not nodeps and \
+ dep.parent not in self._slot_collision_nodes:
+ if dep.parent.onlydeps:
+ # It's safe to ignore blockers if the
+ # parent is an --onlydeps node.
+ return 1
+ # The blocker applies to the root where
+ # the parent is or will be installed.
+ blocker = Blocker(atom=dep.atom,
+ eapi=dep.parent.metadata["EAPI"],
+ root=dep.parent.root)
+ self._blocker_parents.add(blocker, dep.parent)
+ return 1
+ dep_pkg, existing_node = self._select_package(dep.root, dep.atom,
+ onlydeps=dep.onlydeps)
+ if not dep_pkg:
+ if dep.priority.optional:
+ # This could be an unecessary build-time dep
+ # pulled in by --with-bdeps=y.
+ return 1
+ if allow_unsatisfied:
+ self._unsatisfied_deps.append(dep)
+ return 1
+ self._unsatisfied_deps_for_display.append(
+ ((dep.root, dep.atom), {"myparent":dep.parent}))
+ return 0
+ # In some cases, dep_check will return deps that shouldn't
+ # be proccessed any further, so they are identified and
+ # discarded here. Try to discard as few as possible since
+ # discarded dependencies reduce the amount of information
+ # available for optimization of merge order.
+ if dep.priority.satisfied and \
+ not dep_pkg.installed and \
+ not (existing_node or empty or deep or update):
+ myarg = None
+ if dep.root == self.target_root:
+ try:
+ myarg = self._iter_atoms_for_pkg(dep_pkg).next()
+ except StopIteration:
+ pass
+ except portage.exception.InvalidDependString:
+ if not dep_pkg.installed:
+ # This shouldn't happen since the package
+ # should have been masked.
+ raise
+ if not myarg:
+ self._ignored_deps.append(dep)
+ return 1
+
+ if not self._add_pkg(dep_pkg, dep):
+ return 0
+ return 1
+
+ def _add_pkg(self, pkg, dep):
+ myparent = None
+ priority = None
+ depth = 0
+ if dep is None:
+ dep = Dependency()
+ else:
+ myparent = dep.parent
+ priority = dep.priority
+ depth = dep.depth
+ if priority is None:
+ priority = DepPriority()
+ """
+ Fills the digraph with nodes comprised of packages to merge.
+ mybigkey is the package spec of the package to merge.
+ myparent is the package depending on mybigkey ( or None )
+ addme = Should we add this package to the digraph or are we just looking at it's deps?
+ Think --onlydeps, we need to ignore packages in that case.
+ #stuff to add:
+ #SLOT-aware emerge
+ #IUSE-aware emerge -> USE DEP aware depgraph
+ #"no downgrade" emerge
+ """
+ # Ensure that the dependencies of the same package
+ # are never processed more than once.
+ previously_added = pkg in self.digraph
+
+ # select the correct /var database that we'll be checking against
+ vardbapi = self.trees[pkg.root]["vartree"].dbapi
+ pkgsettings = self.pkgsettings[pkg.root]
+
+ arg_atoms = None
+ if True:
+ try:
+ arg_atoms = list(self._iter_atoms_for_pkg(pkg))
+ except portage.exception.InvalidDependString, e:
+ if not pkg.installed:
+ show_invalid_depstring_notice(
+ pkg, pkg.metadata["PROVIDE"], str(e))
+ return 0
+ del e
+
+ if not pkg.onlydeps:
+ if not pkg.installed and \
+ "empty" not in self.myparams and \
+ vardbapi.match(pkg.slot_atom):
+ # Increase the priority of dependencies on packages that
+ # are being rebuilt. This optimizes merge order so that
+ # dependencies are rebuilt/updated as soon as possible,
+ # which is needed especially when emerge is called by
+ # revdep-rebuild since dependencies may be affected by ABI
+ # breakage that has rendered them useless. Don't adjust
+ # priority here when in "empty" mode since all packages
+ # are being merged in that case.
+ priority.rebuild = True
+
+ existing_node = self._slot_pkg_map[pkg.root].get(pkg.slot_atom)
+ slot_collision = False
+ if existing_node:
+ existing_node_matches = pkg.cpv == existing_node.cpv
+ if existing_node_matches and \
+ pkg != existing_node and \
+ dep.atom is not None:
+ # Use package set for matching since it will match via
+ # PROVIDE when necessary, while match_from_list does not.
+ atom_set = InternalPackageSet(initial_atoms=[dep.atom])
+ if not atom_set.findAtomForPackage(existing_node):
+ existing_node_matches = False
+ if existing_node_matches:
+ # The existing node can be reused.
+ if arg_atoms:
+ for parent_atom in arg_atoms:
+ parent, atom = parent_atom
+ self.digraph.add(existing_node, parent,
+ priority=priority)
+ self._add_parent_atom(existing_node, parent_atom)
+ # If a direct circular dependency is not an unsatisfied
+ # buildtime dependency then drop it here since otherwise
+ # it can skew the merge order calculation in an unwanted
+ # way.
+ if existing_node != myparent or \
+ (priority.buildtime and not priority.satisfied):
+ self.digraph.addnode(existing_node, myparent,
+ priority=priority)
+ if dep.atom is not None and dep.parent is not None:
+ self._add_parent_atom(existing_node,
+ (dep.parent, dep.atom))
+ return 1
+ else:
+
+ # A slot collision has occurred. Sometimes this coincides
+ # with unresolvable blockers, so the slot collision will be
+ # shown later if there are no unresolvable blockers.
+ self._add_slot_conflict(pkg)
+ slot_collision = True
+
+ if slot_collision:
+ # Now add this node to the graph so that self.display()
+ # can show use flags and --tree portage.output. This node is
+ # only being partially added to the graph. It must not be
+ # allowed to interfere with the other nodes that have been
+ # added. Do not overwrite data for existing nodes in
+ # self.mydbapi since that data will be used for blocker
+ # validation.
+ # Even though the graph is now invalid, continue to process
+ # dependencies so that things like --fetchonly can still
+ # function despite collisions.
+ pass
+ elif not previously_added:
+ self._slot_pkg_map[pkg.root][pkg.slot_atom] = pkg
+ self.mydbapi[pkg.root].cpv_inject(pkg)
+ self._filtered_trees[pkg.root]["porttree"].dbapi._clear_cache()
+
+ if not pkg.installed:
+ # Allow this package to satisfy old-style virtuals in case it
+ # doesn't already. Any pre-existing providers will be preferred
+ # over this one.
+ try:
+ pkgsettings.setinst(pkg.cpv, pkg.metadata)
+ # For consistency, also update the global virtuals.
+ settings = self.roots[pkg.root].settings
+ settings.unlock()
+ settings.setinst(pkg.cpv, pkg.metadata)
+ settings.lock()
+ except portage.exception.InvalidDependString, e:
+ show_invalid_depstring_notice(
+ pkg, pkg.metadata["PROVIDE"], str(e))
+ del e
+ return 0
+
+ if arg_atoms:
+ self._set_nodes.add(pkg)
+
+ # Do this even when addme is False (--onlydeps) so that the
+ # parent/child relationship is always known in case
+ # self._show_slot_collision_notice() needs to be called later.
+ self.digraph.add(pkg, myparent, priority=priority)
+ if dep.atom is not None and dep.parent is not None:
+ self._add_parent_atom(pkg, (dep.parent, dep.atom))
+
+ if arg_atoms:
+ for parent_atom in arg_atoms:
+ parent, atom = parent_atom
+ self.digraph.add(pkg, parent, priority=priority)
+ self._add_parent_atom(pkg, parent_atom)
+
+ """ This section determines whether we go deeper into dependencies or not.
+ We want to go deeper on a few occasions:
+ Installing package A, we need to make sure package A's deps are met.
+ emerge --deep <pkgspec>; we need to recursively check dependencies of pkgspec
+ If we are in --nodeps (no recursion) mode, we obviously only check 1 level of dependencies.
+ """
+ dep_stack = self._dep_stack
+ if "recurse" not in self.myparams:
+ return 1
+ elif pkg.installed and \
+ "deep" not in self.myparams:
+ dep_stack = self._ignored_deps
+
+ self.spinner.update()
+
+ if arg_atoms:
+ depth = 0
+ pkg.depth = depth
+ if not previously_added:
+ dep_stack.append(pkg)
+ return 1
+
+ def _add_parent_atom(self, pkg, parent_atom):
+ parent_atoms = self._parent_atoms.get(pkg)
+ if parent_atoms is None:
+ parent_atoms = set()
+ self._parent_atoms[pkg] = parent_atoms
+ parent_atoms.add(parent_atom)
+
+ def _add_slot_conflict(self, pkg):
+ self._slot_collision_nodes.add(pkg)
+ slot_key = (pkg.slot_atom, pkg.root)
+ slot_nodes = self._slot_collision_info.get(slot_key)
+ if slot_nodes is None:
+ slot_nodes = set()
+ slot_nodes.add(self._slot_pkg_map[pkg.root][pkg.slot_atom])
+ self._slot_collision_info[slot_key] = slot_nodes
+ slot_nodes.add(pkg)
+
+ def _add_pkg_deps(self, pkg, allow_unsatisfied=False):
+
+ mytype = pkg.type_name
+ myroot = pkg.root
+ mykey = pkg.cpv
+ metadata = pkg.metadata
+ myuse = pkg.use.enabled
+ jbigkey = pkg
+ depth = pkg.depth + 1
+ removal_action = "remove" in self.myparams
+
+ edepend={}
+ depkeys = ["DEPEND","RDEPEND","PDEPEND"]
+ for k in depkeys:
+ edepend[k] = metadata[k]
+
+ if not pkg.built and \
+ "--buildpkgonly" in self.myopts and \
+ "deep" not in self.myparams and \
+ "empty" not in self.myparams:
+ edepend["RDEPEND"] = ""
+ edepend["PDEPEND"] = ""
+ bdeps_optional = False
+
+ if pkg.built and not removal_action:
+ if self.myopts.get("--with-bdeps", "n") == "y":
+ # Pull in build time deps as requested, but marked them as
+ # "optional" since they are not strictly required. This allows
+ # more freedom in the merge order calculation for solving
+ # circular dependencies. Don't convert to PDEPEND since that
+ # could make --with-bdeps=y less effective if it is used to
+ # adjust merge order to prevent built_with_use() calls from
+ # failing.
+ bdeps_optional = True
+ else:
+ # built packages do not have build time dependencies.
+ edepend["DEPEND"] = ""
+
+ if removal_action and self.myopts.get("--with-bdeps", "y") == "n":
+ edepend["DEPEND"] = ""
+
+ bdeps_root = "/"
+ root_deps = self.myopts.get("--root-deps")
+ if root_deps is not None:
+ if root_deps is True:
+ bdeps_root = myroot
+ elif root_deps == "rdeps":
+ edepend["DEPEND"] = ""
+
+ deps = (
+ (bdeps_root, edepend["DEPEND"],
+ self._priority(buildtime=(not bdeps_optional),
+ optional=bdeps_optional)),
+ (myroot, edepend["RDEPEND"], self._priority(runtime=True)),
+ (myroot, edepend["PDEPEND"], self._priority(runtime_post=True))
+ )
+
+ debug = "--debug" in self.myopts
+ strict = mytype != "installed"
+ try:
+ if not strict:
+ portage.dep._dep_check_strict = False
+
+ for dep_root, dep_string, dep_priority in deps:
+ if not dep_string:
+ continue
+ if debug:
+ print
+ print "Parent: ", jbigkey
+ print "Depstring:", dep_string
+ print "Priority:", dep_priority
+
+ try:
+
+ dep_string = portage.dep.paren_normalize(
+ portage.dep.use_reduce(
+ portage.dep.paren_reduce(dep_string),
+ uselist=pkg.use.enabled))
+
+ dep_string = list(self._queue_disjunctive_deps(
+ pkg, dep_root, dep_priority, dep_string))
+
+ except portage.exception.InvalidDependString, e:
+ if pkg.installed:
+ del e
+ continue
+ show_invalid_depstring_notice(pkg, dep_string, str(e))
+ return 0
+
+ if not dep_string:
+ continue
+
+ dep_string = portage.dep.paren_enclose(dep_string)
+
+ if not self._add_pkg_dep_string(
+ pkg, dep_root, dep_priority, dep_string,
+ allow_unsatisfied):
+ return 0
+
+ except portage.exception.AmbiguousPackageName, e:
+ pkgs = e.args[0]
+ portage.writemsg("\n\n!!! An atom in the dependencies " + \
+ "is not fully-qualified. Multiple matches:\n\n", noiselevel=-1)
+ for cpv in pkgs:
+ portage.writemsg(" %s\n" % cpv, noiselevel=-1)
+ portage.writemsg("\n", noiselevel=-1)
+ if mytype == "binary":
+ portage.writemsg(
+ "!!! This binary package cannot be installed: '%s'\n" % \
+ mykey, noiselevel=-1)
+ elif mytype == "ebuild":
+ portdb = self.roots[myroot].trees["porttree"].dbapi
+ myebuild, mylocation = portdb.findname2(mykey)
+ portage.writemsg("!!! This ebuild cannot be installed: " + \
+ "'%s'\n" % myebuild, noiselevel=-1)
+ portage.writemsg("!!! Please notify the package maintainer " + \
+ "that atoms must be fully-qualified.\n", noiselevel=-1)
+ return 0
+ finally:
+ portage.dep._dep_check_strict = True
+ return 1
+
+ def _add_pkg_dep_string(self, pkg, dep_root, dep_priority, dep_string,
+ allow_unsatisfied):
+ depth = pkg.depth + 1
+ debug = "--debug" in self.myopts
+ strict = pkg.type_name != "installed"
+
+ if debug:
+ print
+ print "Parent: ", pkg
+ print "Depstring:", dep_string
+ print "Priority:", dep_priority
+
+ try:
+ selected_atoms = self._select_atoms(dep_root,
+ dep_string, myuse=pkg.use.enabled, parent=pkg,
+ strict=strict, priority=dep_priority)
+ except portage.exception.InvalidDependString, e:
+ show_invalid_depstring_notice(pkg, dep_string, str(e))
+ del e
+ if pkg.installed:
+ return 1
+ return 0
+
+ if debug:
+ print "Candidates:", selected_atoms
+
+ vardb = self.roots[dep_root].trees["vartree"].dbapi
+
+ for atom in selected_atoms:
+ try:
+
+ atom = portage.dep.Atom(atom)
+
+ mypriority = dep_priority.copy()
+ if not atom.blocker and vardb.match(atom):
+ mypriority.satisfied = True
+
+ if not self._add_dep(Dependency(atom=atom,
+ blocker=atom.blocker, depth=depth, parent=pkg,
+ priority=mypriority, root=dep_root),
+ allow_unsatisfied=allow_unsatisfied):
+ return 0
+
+ except portage.exception.InvalidAtom, e:
+ show_invalid_depstring_notice(
+ pkg, dep_string, str(e))
+ del e
+ if not pkg.installed:
+ return 0
+
+ if debug:
+ print "Exiting...", pkg
+
+ return 1
+
+ def _queue_disjunctive_deps(self, pkg, dep_root, dep_priority, dep_struct):
+ """
+ Queue disjunctive (virtual and ||) deps in self._dep_disjunctive_stack.
+ Yields non-disjunctive deps. Raises InvalidDependString when
+ necessary.
+ """
+ i = 0
+ while i < len(dep_struct):
+ x = dep_struct[i]
+ if isinstance(x, list):
+ for y in self._queue_disjunctive_deps(
+ pkg, dep_root, dep_priority, x):
+ yield y
+ elif x == "||":
+ self._queue_disjunction(pkg, dep_root, dep_priority,
+ [ x, dep_struct[ i + 1 ] ] )
+ i += 1
+ else:
+ try:
+ x = portage.dep.Atom(x)
+ except portage.exception.InvalidAtom:
+ if not pkg.installed:
+ raise portage.exception.InvalidDependString(
+ "invalid atom: '%s'" % x)
+ else:
+ # Note: Eventually this will check for PROPERTIES=virtual
+ # or whatever other metadata gets implemented for this
+ # purpose.
+ if x.cp.startswith('virtual/'):
+ self._queue_disjunction( pkg, dep_root,
+ dep_priority, [ str(x) ] )
+ else:
+ yield str(x)
+ i += 1
+
+ def _queue_disjunction(self, pkg, dep_root, dep_priority, dep_struct):
+ self._dep_disjunctive_stack.append(
+ (pkg, dep_root, dep_priority, dep_struct))
+
+ def _pop_disjunction(self, allow_unsatisfied):
+ """
+ Pop one disjunctive dep from self._dep_disjunctive_stack, and use it to
+ populate self._dep_stack.
+ """
+ pkg, dep_root, dep_priority, dep_struct = \
+ self._dep_disjunctive_stack.pop()
+ dep_string = portage.dep.paren_enclose(dep_struct)
+ if not self._add_pkg_dep_string(
+ pkg, dep_root, dep_priority, dep_string, allow_unsatisfied):
+ return 0
+ return 1
+
+ def _priority(self, **kwargs):
+ if "remove" in self.myparams:
+ priority_constructor = UnmergeDepPriority
+ else:
+ priority_constructor = DepPriority
+ return priority_constructor(**kwargs)
+
+ def _dep_expand(self, root_config, atom_without_category):
+ """
+ @param root_config: a root config instance
+ @type root_config: RootConfig
+ @param atom_without_category: an atom without a category component
+ @type atom_without_category: String
+ @rtype: list
+ @returns: a list of atoms containing categories (possibly empty)
+ """
+ null_cp = portage.dep_getkey(insert_category_into_atom(
+ atom_without_category, "null"))
+ cat, atom_pn = portage.catsplit(null_cp)
+
+ dbs = self._filtered_trees[root_config.root]["dbs"]
+ categories = set()
+ for db, pkg_type, built, installed, db_keys in dbs:
+ for cat in db.categories:
+ if db.cp_list("%s/%s" % (cat, atom_pn)):
+ categories.add(cat)
+
+ deps = []
+ for cat in categories:
+ deps.append(insert_category_into_atom(
+ atom_without_category, cat))
+ return deps
+
+ def _have_new_virt(self, root, atom_cp):
+ ret = False
+ for db, pkg_type, built, installed, db_keys in \
+ self._filtered_trees[root]["dbs"]:
+ if db.cp_list(atom_cp):
+ ret = True
+ break
+ return ret
+
+ def _iter_atoms_for_pkg(self, pkg):
+ # TODO: add multiple $ROOT support
+ if pkg.root != self.target_root:
+ return
+ atom_arg_map = self._atom_arg_map
+ root_config = self.roots[pkg.root]
+ for atom in self._set_atoms.iterAtomsForPackage(pkg):
+ atom_cp = portage.dep_getkey(atom)
+ if atom_cp != pkg.cp and \
+ self._have_new_virt(pkg.root, atom_cp):
+ continue
+ visible_pkgs = root_config.visible_pkgs.match_pkgs(atom)
+ visible_pkgs.reverse() # descending order
+ higher_slot = None
+ for visible_pkg in visible_pkgs:
+ if visible_pkg.cp != atom_cp:
+ continue
+ if pkg >= visible_pkg:
+ # This is descending order, and we're not
+ # interested in any versions <= pkg given.
+ break
+ if pkg.slot_atom != visible_pkg.slot_atom:
+ higher_slot = visible_pkg
+ break
+ if higher_slot is not None:
+ continue
+ for arg in atom_arg_map[(atom, pkg.root)]:
+ if isinstance(arg, PackageArg) and \
+ arg.package != pkg:
+ continue
+ yield arg, atom
+
+ def select_files(self, myfiles):
+ """Given a list of .tbz2s, .ebuilds sets, and deps, create the
+ appropriate depgraph and return a favorite list."""
+ debug = "--debug" in self.myopts
+ root_config = self.roots[self.target_root]
+ sets = root_config.sets
+ getSetAtoms = root_config.setconfig.getSetAtoms
+ myfavorites=[]
+ myroot = self.target_root
+ dbs = self._filtered_trees[myroot]["dbs"]
+ vardb = self.trees[myroot]["vartree"].dbapi
+ real_vardb = self._trees_orig[myroot]["vartree"].dbapi
+ portdb = self.trees[myroot]["porttree"].dbapi
+ bindb = self.trees[myroot]["bintree"].dbapi
+ pkgsettings = self.pkgsettings[myroot]
+ args = []
+ onlydeps = "--onlydeps" in self.myopts
+ lookup_owners = []
+ for x in myfiles:
+ ext = os.path.splitext(x)[1]
+ if ext==".tbz2":
+ if not os.path.exists(x):
+ if os.path.exists(
+ os.path.join(pkgsettings["PKGDIR"], "All", x)):
+ x = os.path.join(pkgsettings["PKGDIR"], "All", x)
+ elif os.path.exists(
+ os.path.join(pkgsettings["PKGDIR"], x)):
+ x = os.path.join(pkgsettings["PKGDIR"], x)
+ else:
+ print "\n\n!!! Binary package '"+str(x)+"' does not exist."
+ print "!!! Please ensure the tbz2 exists as specified.\n"
+ return 0, myfavorites
+ mytbz2=portage.xpak.tbz2(x)
+ mykey=mytbz2.getelements("CATEGORY")[0]+"/"+os.path.splitext(os.path.basename(x))[0]
+ if os.path.realpath(x) != \
+ os.path.realpath(self.trees[myroot]["bintree"].getname(mykey)):
+ print colorize("BAD", "\n*** You need to adjust PKGDIR to emerge this package.\n")
+ return 0, myfavorites
+ db_keys = list(bindb._aux_cache_keys)
+ metadata = izip(db_keys, bindb.aux_get(mykey, db_keys))
+ pkg = Package(type_name="binary", root_config=root_config,
+ cpv=mykey, built=True, metadata=metadata,
+ onlydeps=onlydeps)
+ self._pkg_cache[pkg] = pkg
+ args.append(PackageArg(arg=x, package=pkg,
+ root_config=root_config))
+ elif ext==".ebuild":
+ ebuild_path = portage.util.normalize_path(os.path.abspath(x))
+ pkgdir = os.path.dirname(ebuild_path)
+ tree_root = os.path.dirname(os.path.dirname(pkgdir))
+ cp = pkgdir[len(tree_root)+1:]
+ e = portage.exception.PackageNotFound(
+ ("%s is not in a valid portage tree " + \
+ "hierarchy or does not exist") % x)
+ if not portage.isvalidatom(cp):
+ raise e
+ cat = portage.catsplit(cp)[0]
+ mykey = cat + "/" + os.path.basename(ebuild_path[:-7])
+ if not portage.isvalidatom("="+mykey):
+ raise e
+ ebuild_path = portdb.findname(mykey)
+ if ebuild_path:
+ if ebuild_path != os.path.join(os.path.realpath(tree_root),
+ cp, os.path.basename(ebuild_path)):
+ print colorize("BAD", "\n*** You need to adjust PORTDIR or PORTDIR_OVERLAY to emerge this package.\n")
+ return 0, myfavorites
+ if mykey not in portdb.xmatch(
+ "match-visible", portage.dep_getkey(mykey)):
+ print colorize("BAD", "\n*** You are emerging a masked package. It is MUCH better to use")
+ print colorize("BAD", "*** /etc/portage/package.* to accomplish this. See portage(5) man")
+ print colorize("BAD", "*** page for details.")
+ countdown(int(self.settings["EMERGE_WARNING_DELAY"]),
+ "Continuing...")
+ else:
+ raise portage.exception.PackageNotFound(
+ "%s is not in a valid portage tree hierarchy or does not exist" % x)
+ db_keys = list(portdb._aux_cache_keys)
+ metadata = izip(db_keys, portdb.aux_get(mykey, db_keys))
+ pkg = Package(type_name="ebuild", root_config=root_config,
+ cpv=mykey, metadata=metadata, onlydeps=onlydeps)
+ pkgsettings.setcpv(pkg)
+ pkg.metadata["USE"] = pkgsettings["PORTAGE_USE"]
+ pkg.metadata['CHOST'] = pkgsettings.get('CHOST', '')
+ self._pkg_cache[pkg] = pkg
+ args.append(PackageArg(arg=x, package=pkg,
+ root_config=root_config))
+ elif x.startswith(os.path.sep):
+ if not x.startswith(myroot):
+ portage.writemsg(("\n\n!!! '%s' does not start with" + \
+ " $ROOT.\n") % x, noiselevel=-1)
+ return 0, []
+ # Queue these up since it's most efficient to handle
+ # multiple files in a single iter_owners() call.
+ lookup_owners.append(x)
+ else:
+ if x in ("system", "world"):
+ x = SETPREFIX + x
+ if x.startswith(SETPREFIX):
+ s = x[len(SETPREFIX):]
+ if s not in sets:
+ raise portage.exception.PackageSetNotFound(s)
+ if s in self._sets:
+ continue
+ # Recursively expand sets so that containment tests in
+ # self._get_parent_sets() properly match atoms in nested
+ # sets (like if world contains system).
+ expanded_set = InternalPackageSet(
+ initial_atoms=getSetAtoms(s))
+ self._sets[s] = expanded_set
+ args.append(SetArg(arg=x, set=expanded_set,
+ root_config=root_config))
+ continue
+ if not is_valid_package_atom(x):
+ portage.writemsg("\n\n!!! '%s' is not a valid package atom.\n" % x,
+ noiselevel=-1)
+ portage.writemsg("!!! Please check ebuild(5) for full details.\n")
+ portage.writemsg("!!! (Did you specify a version but forget to prefix with '='?)\n")
+ return (0,[])
+ # Don't expand categories or old-style virtuals here unless
+ # necessary. Expansion of old-style virtuals here causes at
+ # least the following problems:
+ # 1) It's more difficult to determine which set(s) an atom
+ # came from, if any.
+ # 2) It takes away freedom from the resolver to choose other
+ # possible expansions when necessary.
+ if "/" in x:
+ args.append(AtomArg(arg=x, atom=x,
+ root_config=root_config))
+ continue
+ expanded_atoms = self._dep_expand(root_config, x)
+ installed_cp_set = set()
+ for atom in expanded_atoms:
+ atom_cp = portage.dep_getkey(atom)
+ if vardb.cp_list(atom_cp):
+ installed_cp_set.add(atom_cp)
+
+ if len(installed_cp_set) > 1:
+ non_virtual_cps = set()
+ for atom_cp in installed_cp_set:
+ if not atom_cp.startswith("virtual/"):
+ non_virtual_cps.add(atom_cp)
+ if len(non_virtual_cps) == 1:
+ installed_cp_set = non_virtual_cps
+
+ if len(expanded_atoms) > 1 and len(installed_cp_set) == 1:
+ installed_cp = iter(installed_cp_set).next()
+ expanded_atoms = [atom for atom in expanded_atoms \
+ if portage.dep_getkey(atom) == installed_cp]
+
+ if len(expanded_atoms) > 1:
+ print
+ print
+ ambiguous_package_name(x, expanded_atoms, root_config,
+ self.spinner, self.myopts)
+ return False, myfavorites
+ if expanded_atoms:
+ atom = expanded_atoms[0]
+ else:
+ null_atom = insert_category_into_atom(x, "null")
+ null_cp = portage.dep_getkey(null_atom)
+ cat, atom_pn = portage.catsplit(null_cp)
+ virts_p = root_config.settings.get_virts_p().get(atom_pn)
+ if virts_p:
+ # Allow the depgraph to choose which virtual.
+ atom = insert_category_into_atom(x, "virtual")
+ else:
+ atom = insert_category_into_atom(x, "null")
+
+ args.append(AtomArg(arg=x, atom=atom,
+ root_config=root_config))
+
+ if lookup_owners:
+ relative_paths = []
+ search_for_multiple = False
+ if len(lookup_owners) > 1:
+ search_for_multiple = True
+
+ for x in lookup_owners:
+ if not search_for_multiple and os.path.isdir(x):
+ search_for_multiple = True
+ relative_paths.append(x[len(myroot):])
+
+ owners = set()
+ for pkg, relative_path in \
+ real_vardb._owners.iter_owners(relative_paths):
+ owners.add(pkg.mycpv)
+ if not search_for_multiple:
+ break
+
+ if not owners:
+ portage.writemsg(("\n\n!!! '%s' is not claimed " + \
+ "by any package.\n") % lookup_owners[0], noiselevel=-1)
+ return 0, []
+
+ for cpv in owners:
+ slot = vardb.aux_get(cpv, ["SLOT"])[0]
+ if not slot:
+ # portage now masks packages with missing slot, but it's
+ # possible that one was installed by an older version
+ atom = portage.cpv_getkey(cpv)
+ else:
+ atom = "%s:%s" % (portage.cpv_getkey(cpv), slot)
+ args.append(AtomArg(arg=atom, atom=atom,
+ root_config=root_config))
+
+ if "--update" in self.myopts:
+ # In some cases, the greedy slots behavior can pull in a slot that
+ # the user would want to uninstall due to it being blocked by a
+ # newer version in a different slot. Therefore, it's necessary to
+ # detect and discard any that should be uninstalled. Each time
+ # that arguments are updated, package selections are repeated in
+ # order to ensure consistency with the current arguments:
+ #
+ # 1) Initialize args
+ # 2) Select packages and generate initial greedy atoms
+ # 3) Update args with greedy atoms
+ # 4) Select packages and generate greedy atoms again, while
+ # accounting for any blockers between selected packages
+ # 5) Update args with revised greedy atoms
+
+ self._set_args(args)
+ greedy_args = []
+ for arg in args:
+ greedy_args.append(arg)
+ if not isinstance(arg, AtomArg):
+ continue
+ for atom in self._greedy_slots(arg.root_config, arg.atom):
+ greedy_args.append(
+ AtomArg(arg=arg.arg, atom=atom,
+ root_config=arg.root_config))
+
+ self._set_args(greedy_args)
+ del greedy_args
+
+ # Revise greedy atoms, accounting for any blockers
+ # between selected packages.
+ revised_greedy_args = []
+ for arg in args:
+ revised_greedy_args.append(arg)
+ if not isinstance(arg, AtomArg):
+ continue
+ for atom in self._greedy_slots(arg.root_config, arg.atom,
+ blocker_lookahead=True):
+ revised_greedy_args.append(
+ AtomArg(arg=arg.arg, atom=atom,
+ root_config=arg.root_config))
+ args = revised_greedy_args
+ del revised_greedy_args
+
+ self._set_args(args)
+
+ myfavorites = set(myfavorites)
+ for arg in args:
+ if isinstance(arg, (AtomArg, PackageArg)):
+ myfavorites.add(arg.atom)
+ elif isinstance(arg, SetArg):
+ myfavorites.add(arg.arg)
+ myfavorites = list(myfavorites)
+
+ pprovideddict = pkgsettings.pprovideddict
+ if debug:
+ portage.writemsg("\n", noiselevel=-1)
+ # Order needs to be preserved since a feature of --nodeps
+ # is to allow the user to force a specific merge order.
+ args.reverse()
+ while args:
+ arg = args.pop()
+ for atom in arg.set:
+ self.spinner.update()
+ dep = Dependency(atom=atom, onlydeps=onlydeps,
+ root=myroot, parent=arg)
+ atom_cp = portage.dep_getkey(atom)
+ try:
+ pprovided = pprovideddict.get(portage.dep_getkey(atom))
+ if pprovided and portage.match_from_list(atom, pprovided):
+ # A provided package has been specified on the command line.
+ self._pprovided_args.append((arg, atom))
+ continue
+ if isinstance(arg, PackageArg):
+ if not self._add_pkg(arg.package, dep) or \
+ not self._create_graph():
+ sys.stderr.write(("\n\n!!! Problem resolving " + \
+ "dependencies for %s\n") % arg.arg)
+ return 0, myfavorites
+ continue
+ if debug:
+ portage.writemsg(" Arg: %s\n Atom: %s\n" % \
+ (arg, atom), noiselevel=-1)
+ pkg, existing_node = self._select_package(
+ myroot, atom, onlydeps=onlydeps)
+ if not pkg:
+ if not (isinstance(arg, SetArg) and \
+ arg.name in ("system", "world")):
+ self._unsatisfied_deps_for_display.append(
+ ((myroot, atom), {}))
+ return 0, myfavorites
+ self._missing_args.append((arg, atom))
+ continue
+ if atom_cp != pkg.cp:
+ # For old-style virtuals, we need to repeat the
+ # package.provided check against the selected package.
+ expanded_atom = atom.replace(atom_cp, pkg.cp)
+ pprovided = pprovideddict.get(pkg.cp)
+ if pprovided and \
+ portage.match_from_list(expanded_atom, pprovided):
+ # A provided package has been
+ # specified on the command line.
+ self._pprovided_args.append((arg, atom))
+ continue
+ if pkg.installed and "selective" not in self.myparams:
+ self._unsatisfied_deps_for_display.append(
+ ((myroot, atom), {}))
+ # Previous behavior was to bail out in this case, but
+ # since the dep is satisfied by the installed package,
+ # it's more friendly to continue building the graph
+ # and just show a warning message. Therefore, only bail
+ # out here if the atom is not from either the system or
+ # world set.
+ if not (isinstance(arg, SetArg) and \
+ arg.name in ("system", "world")):
+ return 0, myfavorites
+
+ # Add the selected package to the graph as soon as possible
+ # so that later dep_check() calls can use it as feedback
+ # for making more consistent atom selections.
+ if not self._add_pkg(pkg, dep):
+ if isinstance(arg, SetArg):
+ sys.stderr.write(("\n\n!!! Problem resolving " + \
+ "dependencies for %s from %s\n") % \
+ (atom, arg.arg))
+ else:
+ sys.stderr.write(("\n\n!!! Problem resolving " + \
+ "dependencies for %s\n") % atom)
+ return 0, myfavorites
+
+ except portage.exception.MissingSignature, e:
+ portage.writemsg("\n\n!!! A missing gpg signature is preventing portage from calculating the\n")
+ portage.writemsg("!!! required dependencies. This is a security feature enabled by the admin\n")
+ portage.writemsg("!!! to aid in the detection of malicious intent.\n\n")
+ portage.writemsg("!!! THIS IS A POSSIBLE INDICATION OF TAMPERED FILES -- CHECK CAREFULLY.\n")
+ portage.writemsg("!!! Affected file: %s\n" % (e), noiselevel=-1)
+ return 0, myfavorites
+ except portage.exception.InvalidSignature, e:
+ portage.writemsg("\n\n!!! An invalid gpg signature is preventing portage from calculating the\n")
+ portage.writemsg("!!! required dependencies. This is a security feature enabled by the admin\n")
+ portage.writemsg("!!! to aid in the detection of malicious intent.\n\n")
+ portage.writemsg("!!! THIS IS A POSSIBLE INDICATION OF TAMPERED FILES -- CHECK CAREFULLY.\n")
+ portage.writemsg("!!! Affected file: %s\n" % (e), noiselevel=-1)
+ return 0, myfavorites
+ except SystemExit, e:
+ raise # Needed else can't exit
+ except Exception, e:
+ print >> sys.stderr, "\n\n!!! Problem in '%s' dependencies." % atom
+ print >> sys.stderr, "!!!", str(e), getattr(e, "__module__", None)
+ raise
+
+ # Now that the root packages have been added to the graph,
+ # process the dependencies.
+ if not self._create_graph():
+ return 0, myfavorites
+
+ missing=0
+ if "--usepkgonly" in self.myopts:
+ for xs in self.digraph.all_nodes():
+ if not isinstance(xs, Package):
+ continue
+ if len(xs) >= 4 and xs[0] != "binary" and xs[3] == "merge":
+ if missing == 0:
+ print
+ missing += 1
+ print "Missing binary for:",xs[2]
+
+ try:
+ self.altlist()
+ except self._unknown_internal_error:
+ return False, myfavorites
+
+ # We're true here unless we are missing binaries.
+ return (not missing,myfavorites)
+
+ def _set_args(self, args):
+ """
+ Create the "args" package set from atoms and packages given as
+ arguments. This method can be called multiple times if necessary.
+ The package selection cache is automatically invalidated, since
+ arguments influence package selections.
+ """
+ args_set = self._sets["args"]
+ args_set.clear()
+ for arg in args:
+ if not isinstance(arg, (AtomArg, PackageArg)):
+ continue
+ atom = arg.atom
+ if atom in args_set:
+ continue
+ args_set.add(atom)
+
+ self._set_atoms.clear()
+ self._set_atoms.update(chain(*self._sets.itervalues()))
+ atom_arg_map = self._atom_arg_map
+ atom_arg_map.clear()
+ for arg in args:
+ for atom in arg.set:
+ atom_key = (atom, arg.root_config.root)
+ refs = atom_arg_map.get(atom_key)
+ if refs is None:
+ refs = []
+ atom_arg_map[atom_key] = refs
+ if arg not in refs:
+ refs.append(arg)
+
+ # Invalidate the package selection cache, since
+ # arguments influence package selections.
+ self._highest_pkg_cache.clear()
+ for trees in self._filtered_trees.itervalues():
+ trees["porttree"].dbapi._clear_cache()
+
+ def _greedy_slots(self, root_config, atom, blocker_lookahead=False):
+ """
+ Return a list of slot atoms corresponding to installed slots that
+ differ from the slot of the highest visible match. When
+ blocker_lookahead is True, slot atoms that would trigger a blocker
+ conflict are automatically discarded, potentially allowing automatic
+ uninstallation of older slots when appropriate.
+ """
+ highest_pkg, in_graph = self._select_package(root_config.root, atom)
+ if highest_pkg is None:
+ return []
+ vardb = root_config.trees["vartree"].dbapi
+ slots = set()
+ for cpv in vardb.match(atom):
+ # don't mix new virtuals with old virtuals
+ if portage.cpv_getkey(cpv) == highest_pkg.cp:
+ slots.add(vardb.aux_get(cpv, ["SLOT"])[0])
+
+ slots.add(highest_pkg.metadata["SLOT"])
+ if len(slots) == 1:
+ return []
+ greedy_pkgs = []
+ slots.remove(highest_pkg.metadata["SLOT"])
+ while slots:
+ slot = slots.pop()
+ slot_atom = portage.dep.Atom("%s:%s" % (highest_pkg.cp, slot))
+ pkg, in_graph = self._select_package(root_config.root, slot_atom)
+ if pkg is not None and \
+ pkg.cp == highest_pkg.cp and pkg < highest_pkg:
+ greedy_pkgs.append(pkg)
+ if not greedy_pkgs:
+ return []
+ if not blocker_lookahead:
+ return [pkg.slot_atom for pkg in greedy_pkgs]
+
+ blockers = {}
+ blocker_dep_keys = ["DEPEND", "PDEPEND", "RDEPEND"]
+ for pkg in greedy_pkgs + [highest_pkg]:
+ dep_str = " ".join(pkg.metadata[k] for k in blocker_dep_keys)
+ try:
+ atoms = self._select_atoms(
+ pkg.root, dep_str, pkg.use.enabled,
+ parent=pkg, strict=True)
+ except portage.exception.InvalidDependString:
+ continue
+ blocker_atoms = (x for x in atoms if x.blocker)
+ blockers[pkg] = InternalPackageSet(initial_atoms=blocker_atoms)
+
+ if highest_pkg not in blockers:
+ return []
+
+ # filter packages with invalid deps
+ greedy_pkgs = [pkg for pkg in greedy_pkgs if pkg in blockers]
+
+ # filter packages that conflict with highest_pkg
+ greedy_pkgs = [pkg for pkg in greedy_pkgs if not \
+ (blockers[highest_pkg].findAtomForPackage(pkg) or \
+ blockers[pkg].findAtomForPackage(highest_pkg))]
+
+ if not greedy_pkgs:
+ return []
+
+ # If two packages conflict, discard the lower version.
+ discard_pkgs = set()
+ greedy_pkgs.sort(reverse=True)
+ for i in xrange(len(greedy_pkgs) - 1):
+ pkg1 = greedy_pkgs[i]
+ if pkg1 in discard_pkgs:
+ continue
+ for j in xrange(i + 1, len(greedy_pkgs)):
+ pkg2 = greedy_pkgs[j]
+ if pkg2 in discard_pkgs:
+ continue
+ if blockers[pkg1].findAtomForPackage(pkg2) or \
+ blockers[pkg2].findAtomForPackage(pkg1):
+ # pkg1 > pkg2
+ discard_pkgs.add(pkg2)
+
+ return [pkg.slot_atom for pkg in greedy_pkgs \
+ if pkg not in discard_pkgs]
+
+ def _select_atoms_from_graph(self, *pargs, **kwargs):
+ """
+ Prefer atoms matching packages that have already been
+ added to the graph or those that are installed and have
+ not been scheduled for replacement.
+ """
+ kwargs["trees"] = self._graph_trees
+ return self._select_atoms_highest_available(*pargs, **kwargs)
+
+ def _select_atoms_highest_available(self, root, depstring,
+ myuse=None, parent=None, strict=True, trees=None, priority=None):
+ """This will raise InvalidDependString if necessary. If trees is
+ None then self._filtered_trees is used."""
+ pkgsettings = self.pkgsettings[root]
+ if trees is None:
+ trees = self._filtered_trees
+ if not getattr(priority, "buildtime", False):
+ # The parent should only be passed to dep_check() for buildtime
+ # dependencies since that's the only case when it's appropriate
+ # to trigger the circular dependency avoidance code which uses it.
+ # It's important not to trigger the same circular dependency
+ # avoidance code for runtime dependencies since it's not needed
+ # and it can promote an incorrect package choice.
+ parent = None
+ if True:
+ try:
+ if parent is not None:
+ trees[root]["parent"] = parent
+ if not strict:
+ portage.dep._dep_check_strict = False
+ mycheck = portage.dep_check(depstring, None,
+ pkgsettings, myuse=myuse,
+ myroot=root, trees=trees)
+ finally:
+ if parent is not None:
+ trees[root].pop("parent")
+ portage.dep._dep_check_strict = True
+ if not mycheck[0]:
+ raise portage.exception.InvalidDependString(mycheck[1])
+ selected_atoms = mycheck[1]
+ return selected_atoms
+
+ def _show_unsatisfied_dep(self, root, atom, myparent=None, arg=None):
+ atom = portage.dep.Atom(atom)
+ atom_set = InternalPackageSet(initial_atoms=(atom,))
+ atom_without_use = atom
+ if atom.use:
+ atom_without_use = portage.dep.remove_slot(atom)
+ if atom.slot:
+ atom_without_use += ":" + atom.slot
+ atom_without_use = portage.dep.Atom(atom_without_use)
+ xinfo = '"%s"' % atom
+ if arg:
+ xinfo='"%s"' % arg
+ # Discard null/ from failed cpv_expand category expansion.
+ xinfo = xinfo.replace("null/", "")
+ masked_packages = []
+ missing_use = []
+ masked_pkg_instances = set()
+ missing_licenses = []
+ have_eapi_mask = False
+ pkgsettings = self.pkgsettings[root]
+ implicit_iuse = pkgsettings._get_implicit_iuse()
+ root_config = self.roots[root]
+ portdb = self.roots[root].trees["porttree"].dbapi
+ dbs = self._filtered_trees[root]["dbs"]
+ for db, pkg_type, built, installed, db_keys in dbs:
+ if installed:
+ continue
+ match = db.match
+ if hasattr(db, "xmatch"):
+ cpv_list = db.xmatch("match-all", atom_without_use)
+ else:
+ cpv_list = db.match(atom_without_use)
+ # descending order
+ cpv_list.reverse()
+ for cpv in cpv_list:
+ metadata, mreasons = get_mask_info(root_config, cpv,
+ pkgsettings, db, pkg_type, built, installed, db_keys)
+ if metadata is not None:
+ pkg = Package(built=built, cpv=cpv,
+ installed=installed, metadata=metadata,
+ root_config=root_config)
+ if pkg.cp != atom.cp:
+ # A cpv can be returned from dbapi.match() as an
+ # old-style virtual match even in cases when the
+ # package does not actually PROVIDE the virtual.
+ # Filter out any such false matches here.
+ if not atom_set.findAtomForPackage(pkg):
+ continue
+ if mreasons:
+ masked_pkg_instances.add(pkg)
+ if atom.use:
+ missing_use.append(pkg)
+ if not mreasons:
+ continue
+ masked_packages.append(
+ (root_config, pkgsettings, cpv, metadata, mreasons))
+
+ missing_use_reasons = []
+ missing_iuse_reasons = []
+ for pkg in missing_use:
+ use = pkg.use.enabled
+ iuse = implicit_iuse.union(re.escape(x) for x in pkg.iuse.all)
+ iuse_re = re.compile("^(%s)$" % "|".join(iuse))
+ missing_iuse = []
+ for x in atom.use.required:
+ if iuse_re.match(x) is None:
+ missing_iuse.append(x)
+ mreasons = []
+ if missing_iuse:
+ mreasons.append("Missing IUSE: %s" % " ".join(missing_iuse))
+ missing_iuse_reasons.append((pkg, mreasons))
+ else:
+ need_enable = sorted(atom.use.enabled.difference(use))
+ need_disable = sorted(atom.use.disabled.intersection(use))
+ if need_enable or need_disable:
+ changes = []
+ changes.extend(colorize("red", "+" + x) \
+ for x in need_enable)
+ changes.extend(colorize("blue", "-" + x) \
+ for x in need_disable)
+ mreasons.append("Change USE: %s" % " ".join(changes))
+ missing_use_reasons.append((pkg, mreasons))
+
+ unmasked_use_reasons = [(pkg, mreasons) for (pkg, mreasons) \
+ in missing_use_reasons if pkg not in masked_pkg_instances]
+
+ unmasked_iuse_reasons = [(pkg, mreasons) for (pkg, mreasons) \
+ in missing_iuse_reasons if pkg not in masked_pkg_instances]
+
+ show_missing_use = False
+ if unmasked_use_reasons:
+ # Only show the latest version.
+ show_missing_use = unmasked_use_reasons[:1]
+ elif unmasked_iuse_reasons:
+ if missing_use_reasons:
+ # All packages with required IUSE are masked,
+ # so display a normal masking message.
+ pass
+ else:
+ show_missing_use = unmasked_iuse_reasons
+
+ if show_missing_use:
+ print "\nemerge: there are no ebuilds built with USE flags to satisfy "+green(xinfo)+"."
+ print "!!! One of the following packages is required to complete your request:"
+ for pkg, mreasons in show_missing_use:
+ print "- "+pkg.cpv+" ("+", ".join(mreasons)+")"
+
+ elif masked_packages:
+ print "\n!!! " + \
+ colorize("BAD", "All ebuilds that could satisfy ") + \
+ colorize("INFORM", xinfo) + \
+ colorize("BAD", " have been masked.")
+ print "!!! One of the following masked packages is required to complete your request:"
+ have_eapi_mask = show_masked_packages(masked_packages)
+ if have_eapi_mask:
+ print
+ msg = ("The current version of portage supports " + \
+ "EAPI '%s'. You must upgrade to a newer version" + \
+ " of portage before EAPI masked packages can" + \
+ " be installed.") % portage.const.EAPI
+ from textwrap import wrap
+ for line in wrap(msg, 75):
+ print line
+ print
+ show_mask_docs()
+ else:
+ print "\nemerge: there are no ebuilds to satisfy "+green(xinfo)+"."
+
+ # Show parent nodes and the argument that pulled them in.
+ traversed_nodes = set()
+ node = myparent
+ msg = []
+ while node is not None:
+ traversed_nodes.add(node)
+ msg.append('(dependency required by "%s" [%s])' % \
+ (colorize('INFORM', str(node.cpv)), node.type_name))
+ # When traversing to parents, prefer arguments over packages
+ # since arguments are root nodes. Never traverse the same
+ # package twice, in order to prevent an infinite loop.
+ selected_parent = None
+ for parent in self.digraph.parent_nodes(node):
+ if isinstance(parent, DependencyArg):
+ msg.append('(dependency required by "%s" [argument])' % \
+ (colorize('INFORM', str(parent))))
+ selected_parent = None
+ break
+ if parent not in traversed_nodes:
+ selected_parent = parent
+ node = selected_parent
+ for line in msg:
+ print line
+
+ print
+
+ def _select_pkg_highest_available(self, root, atom, onlydeps=False):
+ cache_key = (root, atom, onlydeps)
+ ret = self._highest_pkg_cache.get(cache_key)
+ if ret is not None:
+ pkg, existing = ret
+ if pkg and not existing:
+ existing = self._slot_pkg_map[root].get(pkg.slot_atom)
+ if existing and existing == pkg:
+ # Update the cache to reflect that the
+ # package has been added to the graph.
+ ret = pkg, pkg
+ self._highest_pkg_cache[cache_key] = ret
+ return ret
+ ret = self._select_pkg_highest_available_imp(root, atom, onlydeps=onlydeps)
+ self._highest_pkg_cache[cache_key] = ret
+ pkg, existing = ret
+ if pkg is not None:
+ settings = pkg.root_config.settings
+ if visible(settings, pkg) and not (pkg.installed and \
+ settings._getMissingKeywords(pkg.cpv, pkg.metadata)):
+ pkg.root_config.visible_pkgs.cpv_inject(pkg)
+ return ret
+
+ def _select_pkg_highest_available_imp(self, root, atom, onlydeps=False):
+ root_config = self.roots[root]
+ pkgsettings = self.pkgsettings[root]
+ dbs = self._filtered_trees[root]["dbs"]
+ vardb = self.roots[root].trees["vartree"].dbapi
+ portdb = self.roots[root].trees["porttree"].dbapi
+ # List of acceptable packages, ordered by type preference.
+ matched_packages = []
+ highest_version = None
+ if not isinstance(atom, portage.dep.Atom):
+ atom = portage.dep.Atom(atom)
+ atom_cp = atom.cp
+ atom_set = InternalPackageSet(initial_atoms=(atom,))
+ existing_node = None
+ myeb = None
+ usepkgonly = "--usepkgonly" in self.myopts
+ empty = "empty" in self.myparams
+ selective = "selective" in self.myparams
+ reinstall = False
+ noreplace = "--noreplace" in self.myopts
+ # Behavior of the "selective" parameter depends on
+ # whether or not a package matches an argument atom.
+ # If an installed package provides an old-style
+ # virtual that is no longer provided by an available
+ # package, the installed package may match an argument
+ # atom even though none of the available packages do.
+ # Therefore, "selective" logic does not consider
+ # whether or not an installed package matches an
+ # argument atom. It only considers whether or not
+ # available packages match argument atoms, which is
+ # represented by the found_available_arg flag.
+ found_available_arg = False
+ for find_existing_node in True, False:
+ if existing_node:
+ break
+ for db, pkg_type, built, installed, db_keys in dbs:
+ if existing_node:
+ break
+ if installed and not find_existing_node:
+ want_reinstall = reinstall or empty or \
+ (found_available_arg and not selective)
+ if want_reinstall and matched_packages:
+ continue
+ if hasattr(db, "xmatch"):
+ cpv_list = db.xmatch("match-all", atom)
+ else:
+ cpv_list = db.match(atom)
+
+ # USE=multislot can make an installed package appear as if
+ # it doesn't satisfy a slot dependency. Rebuilding the ebuild
+ # won't do any good as long as USE=multislot is enabled since
+ # the newly built package still won't have the expected slot.
+ # Therefore, assume that such SLOT dependencies are already
+ # satisfied rather than forcing a rebuild.
+ if installed and not cpv_list and atom.slot:
+ for cpv in db.match(atom.cp):
+ slot_available = False
+ for other_db, other_type, other_built, \
+ other_installed, other_keys in dbs:
+ try:
+ if atom.slot == \
+ other_db.aux_get(cpv, ["SLOT"])[0]:
+ slot_available = True
+ break
+ except KeyError:
+ pass
+ if not slot_available:
+ continue
+ inst_pkg = self._pkg(cpv, "installed",
+ root_config, installed=installed)
+ # Remove the slot from the atom and verify that
+ # the package matches the resulting atom.
+ atom_without_slot = portage.dep.remove_slot(atom)
+ if atom.use:
+ atom_without_slot += str(atom.use)
+ atom_without_slot = portage.dep.Atom(atom_without_slot)
+ if portage.match_from_list(
+ atom_without_slot, [inst_pkg]):
+ cpv_list = [inst_pkg.cpv]
+ break
+
+ if not cpv_list:
+ continue
+ pkg_status = "merge"
+ if installed or onlydeps:
+ pkg_status = "nomerge"
+ # descending order
+ cpv_list.reverse()
+ for cpv in cpv_list:
+ # Make --noreplace take precedence over --newuse.
+ if not installed and noreplace and \
+ cpv in vardb.match(atom):
+ # If the installed version is masked, it may
+ # be necessary to look at lower versions,
+ # in case there is a visible downgrade.
+ continue
+ reinstall_for_flags = None
+ cache_key = (pkg_type, root, cpv, pkg_status)
+ calculated_use = True
+ pkg = self._pkg_cache.get(cache_key)
+ if pkg is None:
+ calculated_use = False
+ try:
+ metadata = izip(db_keys, db.aux_get(cpv, db_keys))
+ except KeyError:
+ continue
+ pkg = Package(built=built, cpv=cpv,
+ installed=installed, metadata=metadata,
+ onlydeps=onlydeps, root_config=root_config,
+ type_name=pkg_type)
+ metadata = pkg.metadata
+ if not built:
+ metadata['CHOST'] = pkgsettings.get('CHOST', '')
+ if not built and ("?" in metadata["LICENSE"] or \
+ "?" in metadata["PROVIDE"]):
+ # This is avoided whenever possible because
+ # it's expensive. It only needs to be done here
+ # if it has an effect on visibility.
+ pkgsettings.setcpv(pkg)
+ metadata["USE"] = pkgsettings["PORTAGE_USE"]
+ calculated_use = True
+ self._pkg_cache[pkg] = pkg
+
+ if not installed or (built and matched_packages):
+ # Only enforce visibility on installed packages
+ # if there is at least one other visible package
+ # available. By filtering installed masked packages
+ # here, packages that have been masked since they
+ # were installed can be automatically downgraded
+ # to an unmasked version.
+ try:
+ if not visible(pkgsettings, pkg):
+ continue
+ except portage.exception.InvalidDependString:
+ if not installed:
+ continue
+
+ # Enable upgrade or downgrade to a version
+ # with visible KEYWORDS when the installed
+ # version is masked by KEYWORDS, but never
+ # reinstall the same exact version only due
+ # to a KEYWORDS mask.
+ if built and matched_packages:
+
+ different_version = None
+ for avail_pkg in matched_packages:
+ if not portage.dep.cpvequal(
+ pkg.cpv, avail_pkg.cpv):
+ different_version = avail_pkg
+ break
+ if different_version is not None:
+
+ if installed and \
+ pkgsettings._getMissingKeywords(
+ pkg.cpv, pkg.metadata):
+ continue
+
+ # If the ebuild no longer exists or it's
+ # keywords have been dropped, reject built
+ # instances (installed or binary).
+ # If --usepkgonly is enabled, assume that
+ # the ebuild status should be ignored.
+ if not usepkgonly:
+ try:
+ pkg_eb = self._pkg(
+ pkg.cpv, "ebuild", root_config)
+ except portage.exception.PackageNotFound:
+ continue
+ else:
+ if not visible(pkgsettings, pkg_eb):
+ continue
+
+ if not pkg.built and not calculated_use:
+ # This is avoided whenever possible because
+ # it's expensive.
+ pkgsettings.setcpv(pkg)
+ pkg.metadata["USE"] = pkgsettings["PORTAGE_USE"]
+
+ if pkg.cp != atom.cp:
+ # A cpv can be returned from dbapi.match() as an
+ # old-style virtual match even in cases when the
+ # package does not actually PROVIDE the virtual.
+ # Filter out any such false matches here.
+ if not atom_set.findAtomForPackage(pkg):
+ continue
+
+ myarg = None
+ if root == self.target_root:
+ try:
+ # Ebuild USE must have been calculated prior
+ # to this point, in case atoms have USE deps.
+ myarg = self._iter_atoms_for_pkg(pkg).next()
+ except StopIteration:
+ pass
+ except portage.exception.InvalidDependString:
+ if not installed:
+ # masked by corruption
+ continue
+ if not installed and myarg:
+ found_available_arg = True
+
+ if atom.use and not pkg.built:
+ use = pkg.use.enabled
+ if atom.use.enabled.difference(use):
+ continue
+ if atom.use.disabled.intersection(use):
+ continue
+ if pkg.cp == atom_cp:
+ if highest_version is None:
+ highest_version = pkg
+ elif pkg > highest_version:
+ highest_version = pkg
+ # At this point, we've found the highest visible
+ # match from the current repo. Any lower versions
+ # from this repo are ignored, so this so the loop
+ # will always end with a break statement below
+ # this point.
+ if find_existing_node:
+ e_pkg = self._slot_pkg_map[root].get(pkg.slot_atom)
+ if not e_pkg:
+ break
+ if portage.dep.match_from_list(atom, [e_pkg]):
+ if highest_version and \
+ e_pkg.cp == atom_cp and \
+ e_pkg < highest_version and \
+ e_pkg.slot_atom != highest_version.slot_atom:
+ # There is a higher version available in a
+ # different slot, so this existing node is
+ # irrelevant.
+ pass
+ else:
+ matched_packages.append(e_pkg)
+ existing_node = e_pkg
+ break
+ # Compare built package to current config and
+ # reject the built package if necessary.
+ if built and not installed and \
+ ("--newuse" in self.myopts or \
+ "--reinstall" in self.myopts):
+ iuses = pkg.iuse.all
+ old_use = pkg.use.enabled
+ if myeb:
+ pkgsettings.setcpv(myeb)
+ else:
+ pkgsettings.setcpv(pkg)
+ now_use = pkgsettings["PORTAGE_USE"].split()
+ forced_flags = set()
+ forced_flags.update(pkgsettings.useforce)
+ forced_flags.update(pkgsettings.usemask)
+ cur_iuse = iuses
+ if myeb and not usepkgonly:
+ cur_iuse = myeb.iuse.all
+ if self._reinstall_for_flags(forced_flags,
+ old_use, iuses,
+ now_use, cur_iuse):
+ break
+ # Compare current config to installed package
+ # and do not reinstall if possible.
+ if not installed and \
+ ("--newuse" in self.myopts or \
+ "--reinstall" in self.myopts) and \
+ cpv in vardb.match(atom):
+ pkgsettings.setcpv(pkg)
+ forced_flags = set()
+ forced_flags.update(pkgsettings.useforce)
+ forced_flags.update(pkgsettings.usemask)
+ old_use = vardb.aux_get(cpv, ["USE"])[0].split()
+ old_iuse = set(filter_iuse_defaults(
+ vardb.aux_get(cpv, ["IUSE"])[0].split()))
+ cur_use = pkg.use.enabled
+ cur_iuse = pkg.iuse.all
+ reinstall_for_flags = \
+ self._reinstall_for_flags(
+ forced_flags, old_use, old_iuse,
+ cur_use, cur_iuse)
+ if reinstall_for_flags:
+ reinstall = True
+ if not built:
+ myeb = pkg
+ matched_packages.append(pkg)
+ if reinstall_for_flags:
+ self._reinstall_nodes[pkg] = \
+ reinstall_for_flags
+ break
+
+ if not matched_packages:
+ return None, None
+
+ if "--debug" in self.myopts:
+ for pkg in matched_packages:
+ portage.writemsg("%s %s\n" % \
+ ((pkg.type_name + ":").rjust(10), pkg.cpv), noiselevel=-1)
+
+ # Filter out any old-style virtual matches if they are
+ # mixed with new-style virtual matches.
+ cp = portage.dep_getkey(atom)
+ if len(matched_packages) > 1 and \
+ "virtual" == portage.catsplit(cp)[0]:
+ for pkg in matched_packages:
+ if pkg.cp != cp:
+ continue
+ # Got a new-style virtual, so filter
+ # out any old-style virtuals.
+ matched_packages = [pkg for pkg in matched_packages \
+ if pkg.cp == cp]
+ break
+
+ if len(matched_packages) > 1:
+ bestmatch = portage.best(
+ [pkg.cpv for pkg in matched_packages])
+ matched_packages = [pkg for pkg in matched_packages \
+ if portage.dep.cpvequal(pkg.cpv, bestmatch)]
+
+ # ordered by type preference ("ebuild" type is the last resort)
+ return matched_packages[-1], existing_node
+
+ def _select_pkg_from_graph(self, root, atom, onlydeps=False):
+ """
+ Select packages that have already been added to the graph or
+ those that are installed and have not been scheduled for
+ replacement.
+ """
+ graph_db = self._graph_trees[root]["porttree"].dbapi
+ matches = graph_db.match_pkgs(atom)
+ if not matches:
+ return None, None
+ pkg = matches[-1] # highest match
+ in_graph = self._slot_pkg_map[root].get(pkg.slot_atom)
+ return pkg, in_graph
+
+ def _complete_graph(self):
+ """
+ Add any deep dependencies of required sets (args, system, world) that
+ have not been pulled into the graph yet. This ensures that the graph
+ is consistent such that initially satisfied deep dependencies are not
+ broken in the new graph. Initially unsatisfied dependencies are
+ irrelevant since we only want to avoid breaking dependencies that are
+ intially satisfied.
+
+ Since this method can consume enough time to disturb users, it is
+ currently only enabled by the --complete-graph option.
+ """
+ if "--buildpkgonly" in self.myopts or \
+ "recurse" not in self.myparams:
+ return 1
+
+ if "complete" not in self.myparams:
+ # Skip this to avoid consuming enough time to disturb users.
+ return 1
+
+ # Put the depgraph into a mode that causes it to only
+ # select packages that have already been added to the
+ # graph or those that are installed and have not been
+ # scheduled for replacement. Also, toggle the "deep"
+ # parameter so that all dependencies are traversed and
+ # accounted for.
+ self._select_atoms = self._select_atoms_from_graph
+ self._select_package = self._select_pkg_from_graph
+ already_deep = "deep" in self.myparams
+ if not already_deep:
+ self.myparams.add("deep")
+
+ for root in self.roots:
+ required_set_names = self._required_set_names.copy()
+ if root == self.target_root and \
+ (already_deep or "empty" in self.myparams):
+ required_set_names.difference_update(self._sets)
+ if not required_set_names and not self._ignored_deps:
+ continue
+ root_config = self.roots[root]
+ setconfig = root_config.setconfig
+ args = []
+ # Reuse existing SetArg instances when available.
+ for arg in self.digraph.root_nodes():
+ if not isinstance(arg, SetArg):
+ continue
+ if arg.root_config != root_config:
+ continue
+ if arg.name in required_set_names:
+ args.append(arg)
+ required_set_names.remove(arg.name)
+ # Create new SetArg instances only when necessary.
+ for s in required_set_names:
+ expanded_set = InternalPackageSet(
+ initial_atoms=setconfig.getSetAtoms(s))
+ atom = SETPREFIX + s
+ args.append(SetArg(arg=atom, set=expanded_set,
+ root_config=root_config))
+ vardb = root_config.trees["vartree"].dbapi
+ for arg in args:
+ for atom in arg.set:
+ self._dep_stack.append(
+ Dependency(atom=atom, root=root, parent=arg))
+ if self._ignored_deps:
+ self._dep_stack.extend(self._ignored_deps)
+ self._ignored_deps = []
+ if not self._create_graph(allow_unsatisfied=True):
+ return 0
+ # Check the unsatisfied deps to see if any initially satisfied deps
+ # will become unsatisfied due to an upgrade. Initially unsatisfied
+ # deps are irrelevant since we only want to avoid breaking deps
+ # that are initially satisfied.
+ while self._unsatisfied_deps:
+ dep = self._unsatisfied_deps.pop()
+ matches = vardb.match_pkgs(dep.atom)
+ if not matches:
+ self._initially_unsatisfied_deps.append(dep)
+ continue
+ # An scheduled installation broke a deep dependency.
+ # Add the installed package to the graph so that it
+ # will be appropriately reported as a slot collision
+ # (possibly solvable via backtracking).
+ pkg = matches[-1] # highest match
+ if not self._add_pkg(pkg, dep):
+ return 0
+ if not self._create_graph(allow_unsatisfied=True):
+ return 0
+ return 1
+
+ def _pkg(self, cpv, type_name, root_config, installed=False):
+ """
+ Get a package instance from the cache, or create a new
+ one if necessary. Raises KeyError from aux_get if it
+ failures for some reason (package does not exist or is
+ corrupt).
+ """
+ operation = "merge"
+ if installed:
+ operation = "nomerge"
+ pkg = self._pkg_cache.get(
+ (type_name, root_config.root, cpv, operation))
+ if pkg is None:
+ tree_type = self.pkg_tree_map[type_name]
+ db = root_config.trees[tree_type].dbapi
+ db_keys = list(self._trees_orig[root_config.root][
+ tree_type].dbapi._aux_cache_keys)
+ try:
+ metadata = izip(db_keys, db.aux_get(cpv, db_keys))
+ except KeyError:
+ raise portage.exception.PackageNotFound(cpv)
+ pkg = Package(cpv=cpv, metadata=metadata,
+ root_config=root_config, installed=installed)
+ if type_name == "ebuild":
+ settings = self.pkgsettings[root_config.root]
+ settings.setcpv(pkg)
+ pkg.metadata["USE"] = settings["PORTAGE_USE"]
+ pkg.metadata['CHOST'] = settings.get('CHOST', '')
+ self._pkg_cache[pkg] = pkg
+ return pkg
+
+ def validate_blockers(self):
+ """Remove any blockers from the digraph that do not match any of the
+ packages within the graph. If necessary, create hard deps to ensure
+ correct merge order such that mutually blocking packages are never
+ installed simultaneously."""
+
+ if "--buildpkgonly" in self.myopts or \
+ "--nodeps" in self.myopts:
+ return True
+
+ #if "deep" in self.myparams:
+ if True:
+ # Pull in blockers from all installed packages that haven't already
+ # been pulled into the depgraph. This is not enabled by default
+ # due to the performance penalty that is incurred by all the
+ # additional dep_check calls that are required.
+
+ dep_keys = ["DEPEND","RDEPEND","PDEPEND"]
+ for myroot in self.trees:
+ vardb = self.trees[myroot]["vartree"].dbapi
+ portdb = self.trees[myroot]["porttree"].dbapi
+ pkgsettings = self.pkgsettings[myroot]
+ final_db = self.mydbapi[myroot]
+
+ blocker_cache = BlockerCache(myroot, vardb)
+ stale_cache = set(blocker_cache)
+ for pkg in vardb:
+ cpv = pkg.cpv
+ stale_cache.discard(cpv)
+ pkg_in_graph = self.digraph.contains(pkg)
+
+ # Check for masked installed packages. Only warn about
+ # packages that are in the graph in order to avoid warning
+ # about those that will be automatically uninstalled during
+ # the merge process or by --depclean.
+ if pkg in final_db:
+ if pkg_in_graph and not visible(pkgsettings, pkg):
+ self._masked_installed.add(pkg)
+
+ blocker_atoms = None
+ blockers = None
+ if pkg_in_graph:
+ blockers = []
+ try:
+ blockers.extend(
+ self._blocker_parents.child_nodes(pkg))
+ except KeyError:
+ pass
+ try:
+ blockers.extend(
+ self._irrelevant_blockers.child_nodes(pkg))
+ except KeyError:
+ pass
+ if blockers is not None:
+ blockers = set(str(blocker.atom) \
+ for blocker in blockers)
+
+ # If this node has any blockers, create a "nomerge"
+ # node for it so that they can be enforced.
+ self.spinner.update()
+ blocker_data = blocker_cache.get(cpv)
+ if blocker_data is not None and \
+ blocker_data.counter != long(pkg.metadata["COUNTER"]):
+ blocker_data = None
+
+ # If blocker data from the graph is available, use
+ # it to validate the cache and update the cache if
+ # it seems invalid.
+ if blocker_data is not None and \
+ blockers is not None:
+ if not blockers.symmetric_difference(
+ blocker_data.atoms):
+ continue
+ blocker_data = None
+
+ if blocker_data is None and \
+ blockers is not None:
+ # Re-use the blockers from the graph.
+ blocker_atoms = sorted(blockers)
+ counter = long(pkg.metadata["COUNTER"])
+ blocker_data = \
+ blocker_cache.BlockerData(counter, blocker_atoms)
+ blocker_cache[pkg.cpv] = blocker_data
+ continue
+
+ if blocker_data:
+ blocker_atoms = blocker_data.atoms
+ else:
+ # Use aux_get() to trigger FakeVartree global
+ # updates on *DEPEND when appropriate.
+ depstr = " ".join(vardb.aux_get(pkg.cpv, dep_keys))
+ # It is crucial to pass in final_db here in order to
+ # optimize dep_check calls by eliminating atoms via
+ # dep_wordreduce and dep_eval calls.
+ try:
+ portage.dep._dep_check_strict = False
+ try:
+ success, atoms = portage.dep_check(depstr,
+ final_db, pkgsettings, myuse=pkg.use.enabled,
+ trees=self._graph_trees, myroot=myroot)
+ except Exception, e:
+ if isinstance(e, SystemExit):
+ raise
+ # This is helpful, for example, if a ValueError
+ # is thrown from cpv_expand due to multiple
+ # matches (this can happen if an atom lacks a
+ # category).
+ show_invalid_depstring_notice(
+ pkg, depstr, str(e))
+ del e
+ raise
+ finally:
+ portage.dep._dep_check_strict = True
+ if not success:
+ replacement_pkg = final_db.match_pkgs(pkg.slot_atom)
+ if replacement_pkg and \
+ replacement_pkg[0].operation == "merge":
+ # This package is being replaced anyway, so
+ # ignore invalid dependencies so as not to
+ # annoy the user too much (otherwise they'd be
+ # forced to manually unmerge it first).
+ continue
+ show_invalid_depstring_notice(pkg, depstr, atoms)
+ return False
+ blocker_atoms = [myatom for myatom in atoms \
+ if myatom.startswith("!")]
+ blocker_atoms.sort()
+ counter = long(pkg.metadata["COUNTER"])
+ blocker_cache[cpv] = \
+ blocker_cache.BlockerData(counter, blocker_atoms)
+ if blocker_atoms:
+ try:
+ for atom in blocker_atoms:
+ blocker = Blocker(atom=portage.dep.Atom(atom),
+ eapi=pkg.metadata["EAPI"], root=myroot)
+ self._blocker_parents.add(blocker, pkg)
+ except portage.exception.InvalidAtom, e:
+ depstr = " ".join(vardb.aux_get(pkg.cpv, dep_keys))
+ show_invalid_depstring_notice(
+ pkg, depstr, "Invalid Atom: %s" % (e,))
+ return False
+ for cpv in stale_cache:
+ del blocker_cache[cpv]
+ blocker_cache.flush()
+ del blocker_cache
+
+ # Discard any "uninstall" tasks scheduled by previous calls
+ # to this method, since those tasks may not make sense given
+ # the current graph state.
+ previous_uninstall_tasks = self._blocker_uninstalls.leaf_nodes()
+ if previous_uninstall_tasks:
+ self._blocker_uninstalls = digraph()
+ self.digraph.difference_update(previous_uninstall_tasks)
+
+ for blocker in self._blocker_parents.leaf_nodes():
+ self.spinner.update()
+ root_config = self.roots[blocker.root]
+ virtuals = root_config.settings.getvirtuals()
+ myroot = blocker.root
+ initial_db = self.trees[myroot]["vartree"].dbapi
+ final_db = self.mydbapi[myroot]
+
+ provider_virtual = False
+ if blocker.cp in virtuals and \
+ not self._have_new_virt(blocker.root, blocker.cp):
+ provider_virtual = True
+
+ # Use this to check PROVIDE for each matched package
+ # when necessary.
+ atom_set = InternalPackageSet(
+ initial_atoms=[blocker.atom])
+
+ if provider_virtual:
+ atoms = []
+ for provider_entry in virtuals[blocker.cp]:
+ provider_cp = \
+ portage.dep_getkey(provider_entry)
+ atoms.append(blocker.atom.replace(
+ blocker.cp, provider_cp))
+ else:
+ atoms = [blocker.atom]
+
+ blocked_initial = set()
+ for atom in atoms:
+ for pkg in initial_db.match_pkgs(atom):
+ if atom_set.findAtomForPackage(pkg):
+ blocked_initial.add(pkg)
+
+ blocked_final = set()
+ for atom in atoms:
+ for pkg in final_db.match_pkgs(atom):
+ if atom_set.findAtomForPackage(pkg):
+ blocked_final.add(pkg)
+
+ if not blocked_initial and not blocked_final:
+ parent_pkgs = self._blocker_parents.parent_nodes(blocker)
+ self._blocker_parents.remove(blocker)
+ # Discard any parents that don't have any more blockers.
+ for pkg in parent_pkgs:
+ self._irrelevant_blockers.add(blocker, pkg)
+ if not self._blocker_parents.child_nodes(pkg):
+ self._blocker_parents.remove(pkg)
+ continue
+ for parent in self._blocker_parents.parent_nodes(blocker):
+ unresolved_blocks = False
+ depends_on_order = set()
+ for pkg in blocked_initial:
+ if pkg.slot_atom == parent.slot_atom:
+ # TODO: Support blocks within slots in cases where it
+ # might make sense. For example, a new version might
+ # require that the old version be uninstalled at build
+ # time.
+ continue
+ if parent.installed:
+ # Two currently installed packages conflict with
+ # eachother. Ignore this case since the damage
+ # is already done and this would be likely to
+ # confuse users if displayed like a normal blocker.
+ continue
+
+ self._blocked_pkgs.add(pkg, blocker)
+
+ if parent.operation == "merge":
+ # Maybe the blocked package can be replaced or simply
+ # unmerged to resolve this block.
+ depends_on_order.add((pkg, parent))
+ continue
+ # None of the above blocker resolutions techniques apply,
+ # so apparently this one is unresolvable.
+ unresolved_blocks = True
+ for pkg in blocked_final:
+ if pkg.slot_atom == parent.slot_atom:
+ # TODO: Support blocks within slots.
+ continue
+ if parent.operation == "nomerge" and \
+ pkg.operation == "nomerge":
+ # This blocker will be handled the next time that a
+ # merge of either package is triggered.
+ continue
+
+ self._blocked_pkgs.add(pkg, blocker)
+
+ # Maybe the blocking package can be
+ # unmerged to resolve this block.
+ if parent.operation == "merge" and pkg.installed:
+ depends_on_order.add((pkg, parent))
+ continue
+ elif parent.operation == "nomerge":
+ depends_on_order.add((parent, pkg))
+ continue
+ # None of the above blocker resolutions techniques apply,
+ # so apparently this one is unresolvable.
+ unresolved_blocks = True
+
+ # Make sure we don't unmerge any package that have been pulled
+ # into the graph.
+ if not unresolved_blocks and depends_on_order:
+ for inst_pkg, inst_task in depends_on_order:
+ if self.digraph.contains(inst_pkg) and \
+ self.digraph.parent_nodes(inst_pkg):
+ unresolved_blocks = True
+ break
+
+ if not unresolved_blocks and depends_on_order:
+ for inst_pkg, inst_task in depends_on_order:
+ uninst_task = Package(built=inst_pkg.built,
+ cpv=inst_pkg.cpv, installed=inst_pkg.installed,
+ metadata=inst_pkg.metadata,
+ operation="uninstall",
+ root_config=inst_pkg.root_config,
+ type_name=inst_pkg.type_name)
+ self._pkg_cache[uninst_task] = uninst_task
+ # Enforce correct merge order with a hard dep.
+ self.digraph.addnode(uninst_task, inst_task,
+ priority=BlockerDepPriority.instance)
+ # Count references to this blocker so that it can be
+ # invalidated after nodes referencing it have been
+ # merged.
+ self._blocker_uninstalls.addnode(uninst_task, blocker)
+ if not unresolved_blocks and not depends_on_order:
+ self._irrelevant_blockers.add(blocker, parent)
+ self._blocker_parents.remove_edge(blocker, parent)
+ if not self._blocker_parents.parent_nodes(blocker):
+ self._blocker_parents.remove(blocker)
+ if not self._blocker_parents.child_nodes(parent):
+ self._blocker_parents.remove(parent)
+ if unresolved_blocks:
+ self._unsolvable_blockers.add(blocker, parent)
+
+ return True
+
+ def _accept_blocker_conflicts(self):
+ acceptable = False
+ for x in ("--buildpkgonly", "--fetchonly",
+ "--fetch-all-uri", "--nodeps"):
+ if x in self.myopts:
+ acceptable = True
+ break
+ return acceptable
+
+ def _merge_order_bias(self, mygraph):
+ """
+ For optimal leaf node selection, promote deep system runtime deps and
+ order nodes from highest to lowest overall reference count.
+ """
+
+ node_info = {}
+ for node in mygraph.order:
+ node_info[node] = len(mygraph.parent_nodes(node))
+ deep_system_deps = _find_deep_system_runtime_deps(mygraph)
+
+ def cmp_merge_preference(node1, node2):
+
+ if node1.operation == 'uninstall':
+ if node2.operation == 'uninstall':
+ return 0
+ return 1
+
+ if node2.operation == 'uninstall':
+ if node1.operation == 'uninstall':
+ return 0
+ return -1
+
+ node1_sys = node1 in deep_system_deps
+ node2_sys = node2 in deep_system_deps
+ if node1_sys != node2_sys:
+ if node1_sys:
+ return -1
+ return 1
+
+ return node_info[node2] - node_info[node1]
+
+ mygraph.order.sort(key=cmp_sort_key(cmp_merge_preference))
+
+ def altlist(self, reversed=False):
+
+ while self._serialized_tasks_cache is None:
+ self._resolve_conflicts()
+ try:
+ self._serialized_tasks_cache, self._scheduler_graph = \
+ self._serialize_tasks()
+ except self._serialize_tasks_retry:
+ pass
+
+ retlist = self._serialized_tasks_cache[:]
+ if reversed:
+ retlist.reverse()
+ return retlist
+
+ def schedulerGraph(self):
+ """
+ The scheduler graph is identical to the normal one except that
+ uninstall edges are reversed in specific cases that require
+ conflicting packages to be temporarily installed simultaneously.
+ This is intended for use by the Scheduler in it's parallelization
+ logic. It ensures that temporary simultaneous installation of
+ conflicting packages is avoided when appropriate (especially for
+ !!atom blockers), but allowed in specific cases that require it.
+
+ Note that this method calls break_refs() which alters the state of
+ internal Package instances such that this depgraph instance should
+ not be used to perform any more calculations.
+ """
+ if self._scheduler_graph is None:
+ self.altlist()
+ self.break_refs(self._scheduler_graph.order)
+ return self._scheduler_graph
+
+ def break_refs(self, nodes):
+ """
+ Take a mergelist like that returned from self.altlist() and
+ break any references that lead back to the depgraph. This is
+ useful if you want to hold references to packages without
+ also holding the depgraph on the heap.
+ """
+ for node in nodes:
+ if hasattr(node, "root_config"):
+ # The FakeVartree references the _package_cache which
+ # references the depgraph. So that Package instances don't
+ # hold the depgraph and FakeVartree on the heap, replace
+ # the RootConfig that references the FakeVartree with the
+ # original RootConfig instance which references the actual
+ # vartree.
+ node.root_config = \
+ self._trees_orig[node.root_config.root]["root_config"]
+
+ def _resolve_conflicts(self):
+ if not self._complete_graph():
+ raise self._unknown_internal_error()
+
+ if not self.validate_blockers():
+ raise self._unknown_internal_error()
+
+ if self._slot_collision_info:
+ self._process_slot_conflicts()
+
+ def _serialize_tasks(self):
+
+ if "--debug" in self.myopts:
+ writemsg("\ndigraph:\n\n", noiselevel=-1)
+ self.digraph.debug_print()
+ writemsg("\n", noiselevel=-1)
+
+ scheduler_graph = self.digraph.copy()
+
+ if '--nodeps' in self.myopts:
+ # Preserve the package order given on the command line.
+ return ([node for node in scheduler_graph \
+ if isinstance(node, Package) \
+ and node.operation == 'merge'], scheduler_graph)
+
+ mygraph=self.digraph.copy()
+ # Prune "nomerge" root nodes if nothing depends on them, since
+ # otherwise they slow down merge order calculation. Don't remove
+ # non-root nodes since they help optimize merge order in some cases
+ # such as revdep-rebuild.
+ removed_nodes = set()
+ while True:
+ for node in mygraph.root_nodes():
+ if not isinstance(node, Package) or \
+ node.installed or node.onlydeps:
+ removed_nodes.add(node)
+ if removed_nodes:
+ self.spinner.update()
+ mygraph.difference_update(removed_nodes)
+ if not removed_nodes:
+ break
+ removed_nodes.clear()
+ self._merge_order_bias(mygraph)
+ def cmp_circular_bias(n1, n2):
+ """
+ RDEPEND is stronger than PDEPEND and this function
+ measures such a strength bias within a circular
+ dependency relationship.
+ """
+ n1_n2_medium = n2 in mygraph.child_nodes(n1,
+ ignore_priority=priority_range.ignore_medium_soft)
+ n2_n1_medium = n1 in mygraph.child_nodes(n2,
+ ignore_priority=priority_range.ignore_medium_soft)
+ if n1_n2_medium == n2_n1_medium:
+ return 0
+ elif n1_n2_medium:
+ return 1
+ return -1
+ myblocker_uninstalls = self._blocker_uninstalls.copy()
+ retlist=[]
+ # Contains uninstall tasks that have been scheduled to
+ # occur after overlapping blockers have been installed.
+ scheduled_uninstalls = set()
+ # Contains any Uninstall tasks that have been ignored
+ # in order to avoid the circular deps code path. These
+ # correspond to blocker conflicts that could not be
+ # resolved.
+ ignored_uninstall_tasks = set()
+ have_uninstall_task = False
+ complete = "complete" in self.myparams
+ asap_nodes = []
+
+ def get_nodes(**kwargs):
+ """
+ Returns leaf nodes excluding Uninstall instances
+ since those should be executed as late as possible.
+ """
+ return [node for node in mygraph.leaf_nodes(**kwargs) \
+ if isinstance(node, Package) and \
+ (node.operation != "uninstall" or \
+ node in scheduled_uninstalls)]
+
+ # sys-apps/portage needs special treatment if ROOT="/"
+ running_root = self._running_root.root
+ from portage.const import PORTAGE_PACKAGE_ATOM
+ runtime_deps = InternalPackageSet(
+ initial_atoms=[PORTAGE_PACKAGE_ATOM])
+ running_portage = self.trees[running_root]["vartree"].dbapi.match_pkgs(
+ PORTAGE_PACKAGE_ATOM)
+ replacement_portage = self.mydbapi[running_root].match_pkgs(
+ PORTAGE_PACKAGE_ATOM)
+
+ if running_portage:
+ running_portage = running_portage[0]
+ else:
+ running_portage = None
+
+ if replacement_portage:
+ replacement_portage = replacement_portage[0]
+ else:
+ replacement_portage = None
+
+ if replacement_portage == running_portage:
+ replacement_portage = None
+
+ if replacement_portage is not None:
+ # update from running_portage to replacement_portage asap
+ asap_nodes.append(replacement_portage)
+
+ if running_portage is not None:
+ try:
+ portage_rdepend = self._select_atoms_highest_available(
+ running_root, running_portage.metadata["RDEPEND"],
+ myuse=running_portage.use.enabled,
+ parent=running_portage, strict=False)
+ except portage.exception.InvalidDependString, e:
+ portage.writemsg("!!! Invalid RDEPEND in " + \
+ "'%svar/db/pkg/%s/RDEPEND': %s\n" % \
+ (running_root, running_portage.cpv, e), noiselevel=-1)
+ del e
+ portage_rdepend = []
+ runtime_deps.update(atom for atom in portage_rdepend \
+ if not atom.startswith("!"))
+
+ def gather_deps(ignore_priority, mergeable_nodes,
+ selected_nodes, node):
+ """
+ Recursively gather a group of nodes that RDEPEND on
+ eachother. This ensures that they are merged as a group
+ and get their RDEPENDs satisfied as soon as possible.
+ """
+ if node in selected_nodes:
+ return True
+ if node not in mergeable_nodes:
+ return False
+ if node == replacement_portage and \
+ mygraph.child_nodes(node,
+ ignore_priority=priority_range.ignore_medium_soft):
+ # Make sure that portage always has all of it's
+ # RDEPENDs installed first.
+ return False
+ selected_nodes.add(node)
+ for child in mygraph.child_nodes(node,
+ ignore_priority=ignore_priority):
+ if not gather_deps(ignore_priority,
+ mergeable_nodes, selected_nodes, child):
+ return False
+ return True
+
+ def ignore_uninst_or_med(priority):
+ if priority is BlockerDepPriority.instance:
+ return True
+ return priority_range.ignore_medium(priority)
+
+ def ignore_uninst_or_med_soft(priority):
+ if priority is BlockerDepPriority.instance:
+ return True
+ return priority_range.ignore_medium_soft(priority)
+
+ tree_mode = "--tree" in self.myopts
+ # Tracks whether or not the current iteration should prefer asap_nodes
+ # if available. This is set to False when the previous iteration
+ # failed to select any nodes. It is reset whenever nodes are
+ # successfully selected.
+ prefer_asap = True
+
+ # Controls whether or not the current iteration should drop edges that
+ # are "satisfied" by installed packages, in order to solve circular
+ # dependencies. The deep runtime dependencies of installed packages are
+ # not checked in this case (bug #199856), so it must be avoided
+ # whenever possible.
+ drop_satisfied = False
+
+ # State of variables for successive iterations that loosen the
+ # criteria for node selection.
+ #
+ # iteration prefer_asap drop_satisfied
+ # 1 True False
+ # 2 False False
+ # 3 False True
+ #
+ # If no nodes are selected on the last iteration, it is due to
+ # unresolved blockers or circular dependencies.
+
+ while not mygraph.empty():
+ self.spinner.update()
+ selected_nodes = None
+ ignore_priority = None
+ if drop_satisfied or (prefer_asap and asap_nodes):
+ priority_range = DepPrioritySatisfiedRange
+ else:
+ priority_range = DepPriorityNormalRange
+ if prefer_asap and asap_nodes:
+ # ASAP nodes are merged before their soft deps. Go ahead and
+ # select root nodes here if necessary, since it's typical for
+ # the parent to have been removed from the graph already.
+ asap_nodes = [node for node in asap_nodes \
+ if mygraph.contains(node)]
+ for node in asap_nodes:
+ if not mygraph.child_nodes(node,
+ ignore_priority=priority_range.ignore_soft):
+ selected_nodes = [node]
+ asap_nodes.remove(node)
+ break
+ if not selected_nodes and \
+ not (prefer_asap and asap_nodes):
+ for i in xrange(priority_range.NONE,
+ priority_range.MEDIUM_SOFT + 1):
+ ignore_priority = priority_range.ignore_priority[i]
+ nodes = get_nodes(ignore_priority=ignore_priority)
+ if nodes:
+ # If there is a mix of uninstall nodes with other
+ # types, save the uninstall nodes for later since
+ # sometimes a merge node will render an uninstall
+ # node unnecessary (due to occupying the same slot),
+ # and we want to avoid executing a separate uninstall
+ # task in that case.
+ if len(nodes) > 1:
+ good_uninstalls = []
+ with_some_uninstalls_excluded = []
+ for node in nodes:
+ if node.operation == "uninstall":
+ slot_node = self.mydbapi[node.root
+ ].match_pkgs(node.slot_atom)
+ if slot_node and \
+ slot_node[0].operation == "merge":
+ continue
+ good_uninstalls.append(node)
+ with_some_uninstalls_excluded.append(node)
+ if good_uninstalls:
+ nodes = good_uninstalls
+ elif with_some_uninstalls_excluded:
+ nodes = with_some_uninstalls_excluded
+ else:
+ nodes = nodes
+
+ if ignore_priority is None and not tree_mode:
+ # Greedily pop all of these nodes since no
+ # relationship has been ignored. This optimization
+ # destroys --tree output, so it's disabled in tree
+ # mode.
+ selected_nodes = nodes
+ else:
+ # For optimal merge order:
+ # * Only pop one node.
+ # * Removing a root node (node without a parent)
+ # will not produce a leaf node, so avoid it.
+ # * It's normal for a selected uninstall to be a
+ # root node, so don't check them for parents.
+ for node in nodes:
+ if node.operation == "uninstall" or \
+ mygraph.parent_nodes(node):
+ selected_nodes = [node]
+ break
+
+ if selected_nodes:
+ break
+
+ if not selected_nodes:
+ nodes = get_nodes(ignore_priority=priority_range.ignore_medium)
+ if nodes:
+ mergeable_nodes = set(nodes)
+ if prefer_asap and asap_nodes:
+ nodes = asap_nodes
+ for i in xrange(priority_range.SOFT,
+ priority_range.MEDIUM_SOFT + 1):
+ ignore_priority = priority_range.ignore_priority[i]
+ for node in nodes:
+ if not mygraph.parent_nodes(node):
+ continue
+ selected_nodes = set()
+ if gather_deps(ignore_priority,
+ mergeable_nodes, selected_nodes, node):
+ break
+ else:
+ selected_nodes = None
+ if selected_nodes:
+ break
+
+ if prefer_asap and asap_nodes and not selected_nodes:
+ # We failed to find any asap nodes to merge, so ignore
+ # them for the next iteration.
+ prefer_asap = False
+ continue
+
+ if selected_nodes and ignore_priority is not None:
+ # Try to merge ignored medium_soft deps as soon as possible
+ # if they're not satisfied by installed packages.
+ for node in selected_nodes:
+ children = set(mygraph.child_nodes(node))
+ soft = children.difference(
+ mygraph.child_nodes(node,
+ ignore_priority=DepPrioritySatisfiedRange.ignore_soft))
+ medium_soft = children.difference(
+ mygraph.child_nodes(node,
+ ignore_priority = \
+ DepPrioritySatisfiedRange.ignore_medium_soft))
+ medium_soft.difference_update(soft)
+ for child in medium_soft:
+ if child in selected_nodes:
+ continue
+ if child in asap_nodes:
+ continue
+ asap_nodes.append(child)
+
+ if selected_nodes and len(selected_nodes) > 1:
+ if not isinstance(selected_nodes, list):
+ selected_nodes = list(selected_nodes)
+ selected_nodes.sort(key=cmp_sort_key(cmp_circular_bias))
+
+ if not selected_nodes and not myblocker_uninstalls.is_empty():
+ # An Uninstall task needs to be executed in order to
+ # avoid conflict if possible.
+
+ if drop_satisfied:
+ priority_range = DepPrioritySatisfiedRange
+ else:
+ priority_range = DepPriorityNormalRange
+
+ mergeable_nodes = get_nodes(
+ ignore_priority=ignore_uninst_or_med)
+
+ min_parent_deps = None
+ uninst_task = None
+ for task in myblocker_uninstalls.leaf_nodes():
+ # Do some sanity checks so that system or world packages
+ # don't get uninstalled inappropriately here (only really
+ # necessary when --complete-graph has not been enabled).
+
+ if task in ignored_uninstall_tasks:
+ continue
+
+ if task in scheduled_uninstalls:
+ # It's been scheduled but it hasn't
+ # been executed yet due to dependence
+ # on installation of blocking packages.
+ continue
+
+ root_config = self.roots[task.root]
+ inst_pkg = self._pkg_cache[
+ ("installed", task.root, task.cpv, "nomerge")]
+
+ if self.digraph.contains(inst_pkg):
+ continue
+
+ forbid_overlap = False
+ heuristic_overlap = False
+ for blocker in myblocker_uninstalls.parent_nodes(task):
+ if blocker.eapi in ("0", "1"):
+ heuristic_overlap = True
+ elif blocker.atom.blocker.overlap.forbid:
+ forbid_overlap = True
+ break
+ if forbid_overlap and running_root == task.root:
+ continue
+
+ if heuristic_overlap and running_root == task.root:
+ # Never uninstall sys-apps/portage or it's essential
+ # dependencies, except through replacement.
+ try:
+ runtime_dep_atoms = \
+ list(runtime_deps.iterAtomsForPackage(task))
+ except portage.exception.InvalidDependString, e:
+ portage.writemsg("!!! Invalid PROVIDE in " + \
+ "'%svar/db/pkg/%s/PROVIDE': %s\n" % \
+ (task.root, task.cpv, e), noiselevel=-1)
+ del e
+ continue
+
+ # Don't uninstall a runtime dep if it appears
+ # to be the only suitable one installed.
+ skip = False
+ vardb = root_config.trees["vartree"].dbapi
+ for atom in runtime_dep_atoms:
+ other_version = None
+ for pkg in vardb.match_pkgs(atom):
+ if pkg.cpv == task.cpv and \
+ pkg.metadata["COUNTER"] == \
+ task.metadata["COUNTER"]:
+ continue
+ other_version = pkg
+ break
+ if other_version is None:
+ skip = True
+ break
+ if skip:
+ continue
+
+ # For packages in the system set, don't take
+ # any chances. If the conflict can't be resolved
+ # by a normal replacement operation then abort.
+ skip = False
+ try:
+ for atom in root_config.sets[
+ "system"].iterAtomsForPackage(task):
+ skip = True
+ break
+ except portage.exception.InvalidDependString, e:
+ portage.writemsg("!!! Invalid PROVIDE in " + \
+ "'%svar/db/pkg/%s/PROVIDE': %s\n" % \
+ (task.root, task.cpv, e), noiselevel=-1)
+ del e
+ skip = True
+ if skip:
+ continue
+
+ # Note that the world check isn't always
+ # necessary since self._complete_graph() will
+ # add all packages from the system and world sets to the
+ # graph. This just allows unresolved conflicts to be
+ # detected as early as possible, which makes it possible
+ # to avoid calling self._complete_graph() when it is
+ # unnecessary due to blockers triggering an abortion.
+ if not complete:
+ # For packages in the world set, go ahead an uninstall
+ # when necessary, as long as the atom will be satisfied
+ # in the final state.
+ graph_db = self.mydbapi[task.root]
+ skip = False
+ try:
+ for atom in root_config.sets[
+ "world"].iterAtomsForPackage(task):
+ satisfied = False
+ for pkg in graph_db.match_pkgs(atom):
+ if pkg == inst_pkg:
+ continue
+ satisfied = True
+ break
+ if not satisfied:
+ skip = True
+ self._blocked_world_pkgs[inst_pkg] = atom
+ break
+ except portage.exception.InvalidDependString, e:
+ portage.writemsg("!!! Invalid PROVIDE in " + \
+ "'%svar/db/pkg/%s/PROVIDE': %s\n" % \
+ (task.root, task.cpv, e), noiselevel=-1)
+ del e
+ skip = True
+ if skip:
+ continue
+
+ # Check the deps of parent nodes to ensure that
+ # the chosen task produces a leaf node. Maybe
+ # this can be optimized some more to make the
+ # best possible choice, but the current algorithm
+ # is simple and should be near optimal for most
+ # common cases.
+ mergeable_parent = False
+ parent_deps = set()
+ for parent in mygraph.parent_nodes(task):
+ parent_deps.update(mygraph.child_nodes(parent,
+ ignore_priority=priority_range.ignore_medium_soft))
+ if parent in mergeable_nodes and \
+ gather_deps(ignore_uninst_or_med_soft,
+ mergeable_nodes, set(), parent):
+ mergeable_parent = True
+
+ if not mergeable_parent:
+ continue
+
+ parent_deps.remove(task)
+ if min_parent_deps is None or \
+ len(parent_deps) < min_parent_deps:
+ min_parent_deps = len(parent_deps)
+ uninst_task = task
+
+ if uninst_task is not None:
+ # The uninstall is performed only after blocking
+ # packages have been merged on top of it. File
+ # collisions between blocking packages are detected
+ # and removed from the list of files to be uninstalled.
+ scheduled_uninstalls.add(uninst_task)
+ parent_nodes = mygraph.parent_nodes(uninst_task)
+
+ # Reverse the parent -> uninstall edges since we want
+ # to do the uninstall after blocking packages have
+ # been merged on top of it.
+ mygraph.remove(uninst_task)
+ for blocked_pkg in parent_nodes:
+ mygraph.add(blocked_pkg, uninst_task,
+ priority=BlockerDepPriority.instance)
+ scheduler_graph.remove_edge(uninst_task, blocked_pkg)
+ scheduler_graph.add(blocked_pkg, uninst_task,
+ priority=BlockerDepPriority.instance)
+
+ # Reset the state variables for leaf node selection and
+ # continue trying to select leaf nodes.
+ prefer_asap = True
+ drop_satisfied = False
+ continue
+
+ if not selected_nodes:
+ # Only select root nodes as a last resort. This case should
+ # only trigger when the graph is nearly empty and the only
+ # remaining nodes are isolated (no parents or children). Since
+ # the nodes must be isolated, ignore_priority is not needed.
+ selected_nodes = get_nodes()
+
+ if not selected_nodes and not drop_satisfied:
+ drop_satisfied = True
+ continue
+
+ if not selected_nodes and not myblocker_uninstalls.is_empty():
+ # If possible, drop an uninstall task here in order to avoid
+ # the circular deps code path. The corresponding blocker will
+ # still be counted as an unresolved conflict.
+ uninst_task = None
+ for node in myblocker_uninstalls.leaf_nodes():
+ try:
+ mygraph.remove(node)
+ except KeyError:
+ pass
+ else:
+ uninst_task = node
+ ignored_uninstall_tasks.add(node)
+ break
+
+ if uninst_task is not None:
+ # Reset the state variables for leaf node selection and
+ # continue trying to select leaf nodes.
+ prefer_asap = True
+ drop_satisfied = False
+ continue
+
+ if not selected_nodes:
+ self._circular_deps_for_display = mygraph
+ raise self._unknown_internal_error()
+
+ # At this point, we've succeeded in selecting one or more nodes, so
+ # reset state variables for leaf node selection.
+ prefer_asap = True
+ drop_satisfied = False
+
+ mygraph.difference_update(selected_nodes)
+
+ for node in selected_nodes:
+ if isinstance(node, Package) and \
+ node.operation == "nomerge":
+ continue
+
+ # Handle interactions between blockers
+ # and uninstallation tasks.
+ solved_blockers = set()
+ uninst_task = None
+ if isinstance(node, Package) and \
+ "uninstall" == node.operation:
+ have_uninstall_task = True
+ uninst_task = node
+ else:
+ vardb = self.trees[node.root]["vartree"].dbapi
+ previous_cpv = vardb.match(node.slot_atom)
+ if previous_cpv:
+ # The package will be replaced by this one, so remove
+ # the corresponding Uninstall task if necessary.
+ previous_cpv = previous_cpv[0]
+ uninst_task = \
+ ("installed", node.root, previous_cpv, "uninstall")
+ try:
+ mygraph.remove(uninst_task)
+ except KeyError:
+ pass
+
+ if uninst_task is not None and \
+ uninst_task not in ignored_uninstall_tasks and \
+ myblocker_uninstalls.contains(uninst_task):
+ blocker_nodes = myblocker_uninstalls.parent_nodes(uninst_task)
+ myblocker_uninstalls.remove(uninst_task)
+ # Discard any blockers that this Uninstall solves.
+ for blocker in blocker_nodes:
+ if not myblocker_uninstalls.child_nodes(blocker):
+ myblocker_uninstalls.remove(blocker)
+ solved_blockers.add(blocker)
+
+ retlist.append(node)
+
+ if (isinstance(node, Package) and \
+ "uninstall" == node.operation) or \
+ (uninst_task is not None and \
+ uninst_task in scheduled_uninstalls):
+ # Include satisfied blockers in the merge list
+ # since the user might be interested and also
+ # it serves as an indicator that blocking packages
+ # will be temporarily installed simultaneously.
+ for blocker in solved_blockers:
+ retlist.append(Blocker(atom=blocker.atom,
+ root=blocker.root, eapi=blocker.eapi,
+ satisfied=True))
+
+ unsolvable_blockers = set(self._unsolvable_blockers.leaf_nodes())
+ for node in myblocker_uninstalls.root_nodes():
+ unsolvable_blockers.add(node)
+
+ for blocker in unsolvable_blockers:
+ retlist.append(blocker)
+
+ # If any Uninstall tasks need to be executed in order
+ # to avoid a conflict, complete the graph with any
+ # dependencies that may have been initially
+ # neglected (to ensure that unsafe Uninstall tasks
+ # are properly identified and blocked from execution).
+ if have_uninstall_task and \
+ not complete and \
+ not unsolvable_blockers:
+ self.myparams.add("complete")
+ raise self._serialize_tasks_retry("")
+
+ if unsolvable_blockers and \
+ not self._accept_blocker_conflicts():
+ self._unsatisfied_blockers_for_display = unsolvable_blockers
+ self._serialized_tasks_cache = retlist[:]
+ self._scheduler_graph = scheduler_graph
+ raise self._unknown_internal_error()
+
+ if self._slot_collision_info and \
+ not self._accept_blocker_conflicts():
+ self._serialized_tasks_cache = retlist[:]
+ self._scheduler_graph = scheduler_graph
+ raise self._unknown_internal_error()
+
+ return retlist, scheduler_graph
+
+ def _show_circular_deps(self, mygraph):
+ # No leaf nodes are available, so we have a circular
+ # dependency panic situation. Reduce the noise level to a
+ # minimum via repeated elimination of root nodes since they
+ # have no parents and thus can not be part of a cycle.
+ while True:
+ root_nodes = mygraph.root_nodes(
+ ignore_priority=DepPrioritySatisfiedRange.ignore_medium_soft)
+ if not root_nodes:
+ break
+ mygraph.difference_update(root_nodes)
+ # Display the USE flags that are enabled on nodes that are part
+ # of dependency cycles in case that helps the user decide to
+ # disable some of them.
+ display_order = []
+ tempgraph = mygraph.copy()
+ while not tempgraph.empty():
+ nodes = tempgraph.leaf_nodes()
+ if not nodes:
+ node = tempgraph.order[0]
+ else:
+ node = nodes[0]
+ display_order.append(node)
+ tempgraph.remove(node)
+ display_order.reverse()
+ self.myopts.pop("--quiet", None)
+ self.myopts.pop("--verbose", None)
+ self.myopts["--tree"] = True
+ portage.writemsg("\n\n", noiselevel=-1)
+ self.display(display_order)
+ prefix = colorize("BAD", " * ")
+ portage.writemsg("\n", noiselevel=-1)
+ portage.writemsg(prefix + "Error: circular dependencies:\n",
+ noiselevel=-1)
+ portage.writemsg("\n", noiselevel=-1)
+ mygraph.debug_print()
+ portage.writemsg("\n", noiselevel=-1)
+ portage.writemsg(prefix + "Note that circular dependencies " + \
+ "can often be avoided by temporarily\n", noiselevel=-1)
+ portage.writemsg(prefix + "disabling USE flags that trigger " + \
+ "optional dependencies.\n", noiselevel=-1)
+
+ def _show_merge_list(self):
+ if self._serialized_tasks_cache is not None and \
+ not (self._displayed_list and \
+ (self._displayed_list == self._serialized_tasks_cache or \
+ self._displayed_list == \
+ list(reversed(self._serialized_tasks_cache)))):
+ display_list = self._serialized_tasks_cache[:]
+ if "--tree" in self.myopts:
+ display_list.reverse()
+ self.display(display_list)
+
+ def _show_unsatisfied_blockers(self, blockers):
+ self._show_merge_list()
+ msg = "Error: The above package list contains " + \
+ "packages which cannot be installed " + \
+ "at the same time on the same system."
+ prefix = colorize("BAD", " * ")
+ from textwrap import wrap
+ portage.writemsg("\n", noiselevel=-1)
+ for line in wrap(msg, 70):
+ portage.writemsg(prefix + line + "\n", noiselevel=-1)
+
+ # Display the conflicting packages along with the packages
+ # that pulled them in. This is helpful for troubleshooting
+ # cases in which blockers don't solve automatically and
+ # the reasons are not apparent from the normal merge list
+ # display.
+
+ conflict_pkgs = {}
+ for blocker in blockers:
+ for pkg in chain(self._blocked_pkgs.child_nodes(blocker), \
+ self._blocker_parents.parent_nodes(blocker)):
+ parent_atoms = self._parent_atoms.get(pkg)
+ if not parent_atoms:
+ atom = self._blocked_world_pkgs.get(pkg)
+ if atom is not None:
+ parent_atoms = set([("@world", atom)])
+ if parent_atoms:
+ conflict_pkgs[pkg] = parent_atoms
+
+ if conflict_pkgs:
+ # Reduce noise by pruning packages that are only
+ # pulled in by other conflict packages.
+ pruned_pkgs = set()
+ for pkg, parent_atoms in conflict_pkgs.iteritems():
+ relevant_parent = False
+ for parent, atom in parent_atoms:
+ if parent not in conflict_pkgs:
+ relevant_parent = True
+ break
+ if not relevant_parent:
+ pruned_pkgs.add(pkg)
+ for pkg in pruned_pkgs:
+ del conflict_pkgs[pkg]
+
+ if conflict_pkgs:
+ msg = []
+ msg.append("\n")
+ indent = " "
+ # Max number of parents shown, to avoid flooding the display.
+ max_parents = 3
+ for pkg, parent_atoms in conflict_pkgs.iteritems():
+
+ pruned_list = set()
+
+ # Prefer packages that are not directly involved in a conflict.
+ for parent_atom in parent_atoms:
+ if len(pruned_list) >= max_parents:
+ break
+ parent, atom = parent_atom
+ if parent not in conflict_pkgs:
+ pruned_list.add(parent_atom)
+
+ for parent_atom in parent_atoms:
+ if len(pruned_list) >= max_parents:
+ break
+ pruned_list.add(parent_atom)
+
+ omitted_parents = len(parent_atoms) - len(pruned_list)
+ msg.append(indent + "%s pulled in by\n" % pkg)
+
+ for parent_atom in pruned_list:
+ parent, atom = parent_atom
+ msg.append(2*indent)
+ if isinstance(parent,
+ (PackageArg, AtomArg)):
+ # For PackageArg and AtomArg types, it's
+ # redundant to display the atom attribute.
+ msg.append(str(parent))
+ else:
+ # Display the specific atom from SetArg or
+ # Package types.
+ msg.append("%s required by %s" % (atom, parent))
+ msg.append("\n")
+
+ if omitted_parents:
+ msg.append(2*indent)
+ msg.append("(and %d more)\n" % omitted_parents)
+
+ msg.append("\n")
+
+ sys.stderr.write("".join(msg))
+ sys.stderr.flush()
+
+ if "--quiet" not in self.myopts:
+ show_blocker_docs_link()
+
+ def display(self, mylist, favorites=[], verbosity=None):
+
+ # This is used to prevent display_problems() from
+ # redundantly displaying this exact same merge list
+ # again via _show_merge_list().
+ self._displayed_list = mylist
+
+ if verbosity is None:
+ verbosity = ("--quiet" in self.myopts and 1 or \
+ "--verbose" in self.myopts and 3 or 2)
+ favorites_set = InternalPackageSet(favorites)
+ oneshot = "--oneshot" in self.myopts or \
+ "--onlydeps" in self.myopts
+ columns = "--columns" in self.myopts
+ changelogs=[]
+ p=[]
+ blockers = []
+
+ counters = PackageCounters()
+
+ if verbosity == 1 and "--verbose" not in self.myopts:
+ def create_use_string(*args):
+ return ""
+ else:
+ def create_use_string(name, cur_iuse, iuse_forced, cur_use,
+ old_iuse, old_use,
+ is_new, reinst_flags,
+ all_flags=(verbosity == 3 or "--quiet" in self.myopts),
+ alphabetical=("--alphabetical" in self.myopts)):
+ enabled = []
+ if alphabetical:
+ disabled = enabled
+ removed = enabled
+ else:
+ disabled = []
+ removed = []
+ cur_iuse = set(cur_iuse)
+ enabled_flags = cur_iuse.intersection(cur_use)
+ removed_iuse = set(old_iuse).difference(cur_iuse)
+ any_iuse = cur_iuse.union(old_iuse)
+ any_iuse = list(any_iuse)
+ any_iuse.sort()
+ for flag in any_iuse:
+ flag_str = None
+ isEnabled = False
+ reinst_flag = reinst_flags and flag in reinst_flags
+ if flag in enabled_flags:
+ isEnabled = True
+ if is_new or flag in old_use and \
+ (all_flags or reinst_flag):
+ flag_str = red(flag)
+ elif flag not in old_iuse:
+ flag_str = yellow(flag) + "%*"
+ elif flag not in old_use:
+ flag_str = green(flag) + "*"
+ elif flag in removed_iuse:
+ if all_flags or reinst_flag:
+ flag_str = yellow("-" + flag) + "%"
+ if flag in old_use:
+ flag_str += "*"
+ flag_str = "(" + flag_str + ")"
+ removed.append(flag_str)
+ continue
+ else:
+ if is_new or flag in old_iuse and \
+ flag not in old_use and \
+ (all_flags or reinst_flag):
+ flag_str = blue("-" + flag)
+ elif flag not in old_iuse:
+ flag_str = yellow("-" + flag)
+ if flag not in iuse_forced:
+ flag_str += "%"
+ elif flag in old_use:
+ flag_str = green("-" + flag) + "*"
+ if flag_str:
+ if flag in iuse_forced:
+ flag_str = "(" + flag_str + ")"
+ if isEnabled:
+ enabled.append(flag_str)
+ else:
+ disabled.append(flag_str)
+
+ if alphabetical:
+ ret = " ".join(enabled)
+ else:
+ ret = " ".join(enabled + disabled + removed)
+ if ret:
+ ret = '%s="%s" ' % (name, ret)
+ return ret
+
+ repo_display = RepoDisplay(self.roots)
+
+ tree_nodes = []
+ display_list = []
+ mygraph = self.digraph.copy()
+
+ # If there are any Uninstall instances, add the corresponding
+ # blockers to the digraph (useful for --tree display).
+
+ executed_uninstalls = set(node for node in mylist \
+ if isinstance(node, Package) and node.operation == "unmerge")
+
+ for uninstall in self._blocker_uninstalls.leaf_nodes():
+ uninstall_parents = \
+ self._blocker_uninstalls.parent_nodes(uninstall)
+ if not uninstall_parents:
+ continue
+
+ # Remove the corresponding "nomerge" node and substitute
+ # the Uninstall node.
+ inst_pkg = self._pkg_cache[
+ ("installed", uninstall.root, uninstall.cpv, "nomerge")]
+ try:
+ mygraph.remove(inst_pkg)
+ except KeyError:
+ pass
+
+ try:
+ inst_pkg_blockers = self._blocker_parents.child_nodes(inst_pkg)
+ except KeyError:
+ inst_pkg_blockers = []
+
+ # Break the Package -> Uninstall edges.
+ mygraph.remove(uninstall)
+
+ # Resolution of a package's blockers
+ # depend on it's own uninstallation.
+ for blocker in inst_pkg_blockers:
+ mygraph.add(uninstall, blocker)
+
+ # Expand Package -> Uninstall edges into
+ # Package -> Blocker -> Uninstall edges.
+ for blocker in uninstall_parents:
+ mygraph.add(uninstall, blocker)
+ for parent in self._blocker_parents.parent_nodes(blocker):
+ if parent != inst_pkg:
+ mygraph.add(blocker, parent)
+
+ # If the uninstall task did not need to be executed because
+ # of an upgrade, display Blocker -> Upgrade edges since the
+ # corresponding Blocker -> Uninstall edges will not be shown.
+ upgrade_node = \
+ self._slot_pkg_map[uninstall.root].get(uninstall.slot_atom)
+ if upgrade_node is not None and \
+ uninstall not in executed_uninstalls:
+ for blocker in uninstall_parents:
+ mygraph.add(upgrade_node, blocker)
+
+ unsatisfied_blockers = []
+ i = 0
+ depth = 0
+ shown_edges = set()
+ for x in mylist:
+ if isinstance(x, Blocker) and not x.satisfied:
+ unsatisfied_blockers.append(x)
+ continue
+ graph_key = x
+ if "--tree" in self.myopts:
+ depth = len(tree_nodes)
+ while depth and graph_key not in \
+ mygraph.child_nodes(tree_nodes[depth-1]):
+ depth -= 1
+ if depth:
+ tree_nodes = tree_nodes[:depth]
+ tree_nodes.append(graph_key)
+ display_list.append((x, depth, True))
+ shown_edges.add((graph_key, tree_nodes[depth-1]))
+ else:
+ traversed_nodes = set() # prevent endless circles
+ traversed_nodes.add(graph_key)
+ def add_parents(current_node, ordered):
+ parent_nodes = None
+ # Do not traverse to parents if this node is an
+ # an argument or a direct member of a set that has
+ # been specified as an argument (system or world).
+ if current_node not in self._set_nodes:
+ parent_nodes = mygraph.parent_nodes(current_node)
+ if parent_nodes:
+ child_nodes = set(mygraph.child_nodes(current_node))
+ selected_parent = None
+ # First, try to avoid a direct cycle.
+ for node in parent_nodes:
+ if not isinstance(node, (Blocker, Package)):
+ continue
+ if node not in traversed_nodes and \
+ node not in child_nodes:
+ edge = (current_node, node)
+ if edge in shown_edges:
+ continue
+ selected_parent = node
+ break
+ if not selected_parent:
+ # A direct cycle is unavoidable.
+ for node in parent_nodes:
+ if not isinstance(node, (Blocker, Package)):
+ continue
+ if node not in traversed_nodes:
+ edge = (current_node, node)
+ if edge in shown_edges:
+ continue
+ selected_parent = node
+ break
+ if selected_parent:
+ shown_edges.add((current_node, selected_parent))
+ traversed_nodes.add(selected_parent)
+ add_parents(selected_parent, False)
+ display_list.append((current_node,
+ len(tree_nodes), ordered))
+ tree_nodes.append(current_node)
+ tree_nodes = []
+ add_parents(graph_key, True)
+ else:
+ display_list.append((x, depth, True))
+ mylist = display_list
+ for x in unsatisfied_blockers:
+ mylist.append((x, 0, True))
+
+ last_merge_depth = 0
+ for i in xrange(len(mylist)-1,-1,-1):
+ graph_key, depth, ordered = mylist[i]
+ if not ordered and depth == 0 and i > 0 \
+ and graph_key == mylist[i-1][0] and \
+ mylist[i-1][1] == 0:
+ # An ordered node got a consecutive duplicate when the tree was
+ # being filled in.
+ del mylist[i]
+ continue
+ if ordered and graph_key[-1] != "nomerge":
+ last_merge_depth = depth
+ continue
+ if depth >= last_merge_depth or \
+ i < len(mylist) - 1 and \
+ depth >= mylist[i+1][1]:
+ del mylist[i]
+
+ from portage import flatten
+ from portage.dep import use_reduce, paren_reduce
+ # files to fetch list - avoids counting a same file twice
+ # in size display (verbose mode)
+ myfetchlist=[]
+
+ # Use this set to detect when all the "repoadd" strings are "[0]"
+ # and disable the entire repo display in this case.
+ repoadd_set = set()
+
+ for mylist_index in xrange(len(mylist)):
+ x, depth, ordered = mylist[mylist_index]
+ pkg_type = x[0]
+ myroot = x[1]
+ pkg_key = x[2]
+ portdb = self.trees[myroot]["porttree"].dbapi
+ bindb = self.trees[myroot]["bintree"].dbapi
+ vardb = self.trees[myroot]["vartree"].dbapi
+ vartree = self.trees[myroot]["vartree"]
+ pkgsettings = self.pkgsettings[myroot]
+
+ fetch=" "
+ indent = " " * depth
+
+ if isinstance(x, Blocker):
+ if x.satisfied:
+ blocker_style = "PKG_BLOCKER_SATISFIED"
+ addl = "%s %s " % (colorize(blocker_style, "b"), fetch)
+ else:
+ blocker_style = "PKG_BLOCKER"
+ addl = "%s %s " % (colorize(blocker_style, "B"), fetch)
+ if ordered:
+ counters.blocks += 1
+ if x.satisfied:
+ counters.blocks_satisfied += 1
+ resolved = portage.key_expand(
+ str(x.atom).lstrip("!"), mydb=vardb, settings=pkgsettings)
+ if "--columns" in self.myopts and "--quiet" in self.myopts:
+ addl += " " + colorize(blocker_style, resolved)
+ else:
+ addl = "[%s %s] %s%s" % \
+ (colorize(blocker_style, "blocks"),
+ addl, indent, colorize(blocker_style, resolved))
+ block_parents = self._blocker_parents.parent_nodes(x)
+ block_parents = set([pnode[2] for pnode in block_parents])
+ block_parents = ", ".join(block_parents)
+ if resolved!=x[2]:
+ addl += colorize(blocker_style,
+ " (\"%s\" is blocking %s)") % \
+ (str(x.atom).lstrip("!"), block_parents)
+ else:
+ addl += colorize(blocker_style,
+ " (is blocking %s)") % block_parents
+ if isinstance(x, Blocker) and x.satisfied:
+ if columns:
+ continue
+ p.append(addl)
+ else:
+ blockers.append(addl)
+ else:
+ pkg_status = x[3]
+ pkg_merge = ordered and pkg_status == "merge"
+ if not pkg_merge and pkg_status == "merge":
+ pkg_status = "nomerge"
+ built = pkg_type != "ebuild"
+ installed = pkg_type == "installed"
+ pkg = x
+ metadata = pkg.metadata
+ ebuild_path = None
+ repo_name = metadata["repository"]
+ if pkg_type == "ebuild":
+ ebuild_path = portdb.findname(pkg_key)
+ if not ebuild_path: # shouldn't happen
+ raise portage.exception.PackageNotFound(pkg_key)
+ repo_path_real = os.path.dirname(os.path.dirname(
+ os.path.dirname(ebuild_path)))
+ else:
+ repo_path_real = portdb.getRepositoryPath(repo_name)
+ pkg_use = list(pkg.use.enabled)
+ try:
+ restrict = flatten(use_reduce(paren_reduce(
+ pkg.metadata["RESTRICT"]), uselist=pkg_use))
+ except portage.exception.InvalidDependString, e:
+ if not pkg.installed:
+ show_invalid_depstring_notice(x,
+ pkg.metadata["RESTRICT"], str(e))
+ del e
+ return 1
+ restrict = []
+ if "ebuild" == pkg_type and x[3] != "nomerge" and \
+ "fetch" in restrict:
+ fetch = red("F")
+ if ordered:
+ counters.restrict_fetch += 1
+ if portdb.fetch_check(pkg_key, pkg_use):
+ fetch = green("f")
+ if ordered:
+ counters.restrict_fetch_satisfied += 1
+
+ #we need to use "--emptrytree" testing here rather than "empty" param testing because "empty"
+ #param is used for -u, where you still *do* want to see when something is being upgraded.
+ myoldbest = []
+ myinslotlist = None
+ installed_versions = vardb.match(portage.cpv_getkey(pkg_key))
+ if vardb.cpv_exists(pkg_key):
+ addl=" "+yellow("R")+fetch+" "
+ if ordered:
+ if pkg_merge:
+ counters.reinst += 1
+ elif pkg_status == "uninstall":
+ counters.uninst += 1
+ # filter out old-style virtual matches
+ elif installed_versions and \
+ portage.cpv_getkey(installed_versions[0]) == \
+ portage.cpv_getkey(pkg_key):
+ myinslotlist = vardb.match(pkg.slot_atom)
+ # If this is the first install of a new-style virtual, we
+ # need to filter out old-style virtual matches.
+ if myinslotlist and \
+ portage.cpv_getkey(myinslotlist[0]) != \
+ portage.cpv_getkey(pkg_key):
+ myinslotlist = None
+ if myinslotlist:
+ myoldbest = myinslotlist[:]
+ addl = " " + fetch
+ if not portage.dep.cpvequal(pkg_key,
+ portage.best([pkg_key] + myoldbest)):
+ # Downgrade in slot
+ addl += turquoise("U")+blue("D")
+ if ordered:
+ counters.downgrades += 1
+ else:
+ # Update in slot
+ addl += turquoise("U") + " "
+ if ordered:
+ counters.upgrades += 1
+ else:
+ # New slot, mark it new.
+ addl = " " + green("NS") + fetch + " "
+ myoldbest = vardb.match(portage.cpv_getkey(pkg_key))
+ if ordered:
+ counters.newslot += 1
+
+ if "--changelog" in self.myopts:
+ inst_matches = vardb.match(pkg.slot_atom)
+ if inst_matches:
+ changelogs.extend(self.calc_changelog(
+ portdb.findname(pkg_key),
+ inst_matches[0], pkg_key))
+ else:
+ addl = " " + green("N") + " " + fetch + " "
+ if ordered:
+ counters.new += 1
+
+ verboseadd = ""
+ repoadd = None
+
+ if True:
+ # USE flag display
+ forced_flags = set()
+ pkgsettings.setcpv(pkg) # for package.use.{mask,force}
+ forced_flags.update(pkgsettings.useforce)
+ forced_flags.update(pkgsettings.usemask)
+
+ cur_use = [flag for flag in pkg.use.enabled \
+ if flag in pkg.iuse.all]
+ cur_iuse = sorted(pkg.iuse.all)
+
+ if myoldbest and myinslotlist:
+ previous_cpv = myoldbest[0]
+ else:
+ previous_cpv = pkg.cpv
+ if vardb.cpv_exists(previous_cpv):
+ old_iuse, old_use = vardb.aux_get(
+ previous_cpv, ["IUSE", "USE"])
+ old_iuse = list(set(
+ filter_iuse_defaults(old_iuse.split())))
+ old_iuse.sort()
+ old_use = old_use.split()
+ is_new = False
+ else:
+ old_iuse = []
+ old_use = []
+ is_new = True
+
+ old_use = [flag for flag in old_use if flag in old_iuse]
+
+ use_expand = pkgsettings["USE_EXPAND"].lower().split()
+ use_expand.sort()
+ use_expand.reverse()
+ use_expand_hidden = \
+ pkgsettings["USE_EXPAND_HIDDEN"].lower().split()
+
+ def map_to_use_expand(myvals, forcedFlags=False,
+ removeHidden=True):
+ ret = {}
+ forced = {}
+ for exp in use_expand:
+ ret[exp] = []
+ forced[exp] = set()
+ for val in myvals[:]:
+ if val.startswith(exp.lower()+"_"):
+ if val in forced_flags:
+ forced[exp].add(val[len(exp)+1:])
+ ret[exp].append(val[len(exp)+1:])
+ myvals.remove(val)
+ ret["USE"] = myvals
+ forced["USE"] = [val for val in myvals \
+ if val in forced_flags]
+ if removeHidden:
+ for exp in use_expand_hidden:
+ ret.pop(exp, None)
+ if forcedFlags:
+ return ret, forced
+ return ret
+
+ # Prevent USE_EXPAND_HIDDEN flags from being hidden if they
+ # are the only thing that triggered reinstallation.
+ reinst_flags_map = {}
+ reinstall_for_flags = self._reinstall_nodes.get(pkg)
+ reinst_expand_map = None
+ if reinstall_for_flags:
+ reinst_flags_map = map_to_use_expand(
+ list(reinstall_for_flags), removeHidden=False)
+ for k in list(reinst_flags_map):
+ if not reinst_flags_map[k]:
+ del reinst_flags_map[k]
+ if not reinst_flags_map.get("USE"):
+ reinst_expand_map = reinst_flags_map.copy()
+ reinst_expand_map.pop("USE", None)
+ if reinst_expand_map and \
+ not set(reinst_expand_map).difference(
+ use_expand_hidden):
+ use_expand_hidden = \
+ set(use_expand_hidden).difference(
+ reinst_expand_map)
+
+ cur_iuse_map, iuse_forced = \
+ map_to_use_expand(cur_iuse, forcedFlags=True)
+ cur_use_map = map_to_use_expand(cur_use)
+ old_iuse_map = map_to_use_expand(old_iuse)
+ old_use_map = map_to_use_expand(old_use)
+
+ use_expand.sort()
+ use_expand.insert(0, "USE")
+
+ for key in use_expand:
+ if key in use_expand_hidden:
+ continue
+ verboseadd += create_use_string(key.upper(),
+ cur_iuse_map[key], iuse_forced[key],
+ cur_use_map[key], old_iuse_map[key],
+ old_use_map[key], is_new,
+ reinst_flags_map.get(key))
+
+ if verbosity == 3:
+ # size verbose
+ mysize=0
+ if pkg_type == "ebuild" and pkg_merge:
+ try:
+ myfilesdict = portdb.getfetchsizes(pkg_key,
+ useflags=pkg_use, debug=self.edebug)
+ except portage.exception.InvalidDependString, e:
+ src_uri = portdb.aux_get(pkg_key, ["SRC_URI"])[0]
+ show_invalid_depstring_notice(x, src_uri, str(e))
+ del e
+ return 1
+ if myfilesdict is None:
+ myfilesdict="[empty/missing/bad digest]"
+ else:
+ for myfetchfile in myfilesdict:
+ if myfetchfile not in myfetchlist:
+ mysize+=myfilesdict[myfetchfile]
+ myfetchlist.append(myfetchfile)
+ if ordered:
+ counters.totalsize += mysize
+ verboseadd += format_size(mysize)
+
+ # overlay verbose
+ # assign index for a previous version in the same slot
+ has_previous = False
+ repo_name_prev = None
+ slot_atom = "%s:%s" % (portage.dep_getkey(pkg_key),
+ metadata["SLOT"])
+ slot_matches = vardb.match(slot_atom)
+ if slot_matches:
+ has_previous = True
+ repo_name_prev = vardb.aux_get(slot_matches[0],
+ ["repository"])[0]
+
+ # now use the data to generate output
+ if pkg.installed or not has_previous:
+ repoadd = repo_display.repoStr(repo_path_real)
+ else:
+ repo_path_prev = None
+ if repo_name_prev:
+ repo_path_prev = portdb.getRepositoryPath(
+ repo_name_prev)
+ if repo_path_prev == repo_path_real:
+ repoadd = repo_display.repoStr(repo_path_real)
+ else:
+ repoadd = "%s=>%s" % (
+ repo_display.repoStr(repo_path_prev),
+ repo_display.repoStr(repo_path_real))
+ if repoadd:
+ repoadd_set.add(repoadd)
+
+ xs = [portage.cpv_getkey(pkg_key)] + \
+ list(portage.catpkgsplit(pkg_key)[2:])
+ if xs[2] == "r0":
+ xs[2] = ""
+ else:
+ xs[2] = "-" + xs[2]
+
+ mywidth = 130
+ if "COLUMNWIDTH" in self.settings:
+ try:
+ mywidth = int(self.settings["COLUMNWIDTH"])
+ except ValueError, e:
+ portage.writemsg("!!! %s\n" % str(e), noiselevel=-1)
+ portage.writemsg(
+ "!!! Unable to parse COLUMNWIDTH='%s'\n" % \
+ self.settings["COLUMNWIDTH"], noiselevel=-1)
+ del e
+ oldlp = mywidth - 30
+ newlp = oldlp - 30
+
+ # Convert myoldbest from a list to a string.
+ if not myoldbest:
+ myoldbest = ""
+ else:
+ for pos, key in enumerate(myoldbest):
+ key = portage.catpkgsplit(key)[2] + \
+ "-" + portage.catpkgsplit(key)[3]
+ if key[-3:] == "-r0":
+ key = key[:-3]
+ myoldbest[pos] = key
+ myoldbest = blue("["+", ".join(myoldbest)+"]")
+
+ pkg_cp = xs[0]
+ root_config = self.roots[myroot]
+ system_set = root_config.sets["system"]
+ world_set = root_config.sets["world"]
+
+ pkg_system = False
+ pkg_world = False
+ try:
+ pkg_system = system_set.findAtomForPackage(pkg)
+ pkg_world = world_set.findAtomForPackage(pkg)
+ if not (oneshot or pkg_world) and \
+ myroot == self.target_root and \
+ favorites_set.findAtomForPackage(pkg):
+ # Maybe it will be added to world now.
+ if create_world_atom(pkg, favorites_set, root_config):
+ pkg_world = True
+ except portage.exception.InvalidDependString:
+ # This is reported elsewhere if relevant.
+ pass
+
+ def pkgprint(pkg_str):
+ if pkg_merge:
+ if pkg_system:
+ return colorize("PKG_MERGE_SYSTEM", pkg_str)
+ elif pkg_world:
+ return colorize("PKG_MERGE_WORLD", pkg_str)
+ else:
+ return colorize("PKG_MERGE", pkg_str)
+ elif pkg_status == "uninstall":
+ return colorize("PKG_UNINSTALL", pkg_str)
+ else:
+ if pkg_system:
+ return colorize("PKG_NOMERGE_SYSTEM", pkg_str)
+ elif pkg_world:
+ return colorize("PKG_NOMERGE_WORLD", pkg_str)
+ else:
+ return colorize("PKG_NOMERGE", pkg_str)
+
+ try:
+ properties = flatten(use_reduce(paren_reduce(
+ pkg.metadata["PROPERTIES"]), uselist=pkg.use.enabled))
+ except portage.exception.InvalidDependString, e:
+ if not pkg.installed:
+ show_invalid_depstring_notice(pkg,
+ pkg.metadata["PROPERTIES"], str(e))
+ del e
+ return 1
+ properties = []
+ interactive = "interactive" in properties
+ if interactive and pkg.operation == "merge":
+ addl = colorize("WARN", "I") + addl[1:]
+ if ordered:
+ counters.interactive += 1
+
+ if x[1]!="/":
+ if myoldbest:
+ myoldbest +=" "
+ if "--columns" in self.myopts:
+ if "--quiet" in self.myopts:
+ myprint=addl+" "+indent+pkgprint(pkg_cp)
+ myprint=myprint+darkblue(" "+xs[1]+xs[2])+" "
+ myprint=myprint+myoldbest
+ myprint=myprint+darkgreen("to "+x[1])
+ verboseadd = None
+ else:
+ if not pkg_merge:
+ myprint = "[%s] %s%s" % \
+ (pkgprint(pkg_status.ljust(13)),
+ indent, pkgprint(pkg.cp))
+ else:
+ myprint = "[%s %s] %s%s" % \
+ (pkgprint(pkg.type_name), addl,
+ indent, pkgprint(pkg.cp))
+ if (newlp-nc_len(myprint)) > 0:
+ myprint=myprint+(" "*(newlp-nc_len(myprint)))
+ myprint=myprint+"["+darkblue(xs[1]+xs[2])+"] "
+ if (oldlp-nc_len(myprint)) > 0:
+ myprint=myprint+" "*(oldlp-nc_len(myprint))
+ myprint=myprint+myoldbest
+ myprint += darkgreen("to " + pkg.root)
+ else:
+ if not pkg_merge:
+ myprint = "[%s] " % pkgprint(pkg_status.ljust(13))
+ else:
+ myprint = "[%s %s] " % (pkgprint(pkg_type), addl)
+ myprint += indent + pkgprint(pkg_key) + " " + \
+ myoldbest + darkgreen("to " + myroot)
+ else:
+ if "--columns" in self.myopts:
+ if "--quiet" in self.myopts:
+ myprint=addl+" "+indent+pkgprint(pkg_cp)
+ myprint=myprint+" "+green(xs[1]+xs[2])+" "
+ myprint=myprint+myoldbest
+ verboseadd = None
+ else:
+ if not pkg_merge:
+ myprint = "[%s] %s%s" % \
+ (pkgprint(pkg_status.ljust(13)),
+ indent, pkgprint(pkg.cp))
+ else:
+ myprint = "[%s %s] %s%s" % \
+ (pkgprint(pkg.type_name), addl,
+ indent, pkgprint(pkg.cp))
+ if (newlp-nc_len(myprint)) > 0:
+ myprint=myprint+(" "*(newlp-nc_len(myprint)))
+ myprint=myprint+green(" ["+xs[1]+xs[2]+"] ")
+ if (oldlp-nc_len(myprint)) > 0:
+ myprint=myprint+(" "*(oldlp-nc_len(myprint)))
+ myprint += myoldbest
+ else:
+ if not pkg_merge:
+ myprint = "[%s] %s%s %s" % \
+ (pkgprint(pkg_status.ljust(13)),
+ indent, pkgprint(pkg.cpv),
+ myoldbest)
+ else:
+ myprint = "[%s %s] %s%s %s" % \
+ (pkgprint(pkg_type), addl, indent,
+ pkgprint(pkg.cpv), myoldbest)
+
+ if columns and pkg.operation == "uninstall":
+ continue
+ p.append((myprint, verboseadd, repoadd))
+
+ if "--tree" not in self.myopts and \
+ "--quiet" not in self.myopts and \
+ not self._opts_no_restart.intersection(self.myopts) and \
+ pkg.root == self._running_root.root and \
+ portage.match_from_list(
+ portage.const.PORTAGE_PACKAGE_ATOM, [pkg]) and \
+ not vardb.cpv_exists(pkg.cpv) and \
+ "--quiet" not in self.myopts:
+ if mylist_index < len(mylist) - 1:
+ p.append(colorize("WARN", "*** Portage will stop merging at this point and reload itself,"))
+ p.append(colorize("WARN", " then resume the merge."))
+
+ out = sys.stdout
+ show_repos = repoadd_set and repoadd_set != set(["0"])
+
+ for x in p:
+ if isinstance(x, basestring):
+ out.write("%s\n" % (x,))
+ continue
+
+ myprint, verboseadd, repoadd = x
+
+ if verboseadd:
+ myprint += " " + verboseadd
+
+ if show_repos and repoadd:
+ myprint += " " + teal("[%s]" % repoadd)
+
+ out.write("%s\n" % (myprint,))
+
+ for x in blockers:
+ print x
+
+ if verbosity == 3:
+ print
+ print counters
+ if show_repos:
+ sys.stdout.write(str(repo_display))
+
+ if "--changelog" in self.myopts:
+ print
+ for revision,text in changelogs:
+ print bold('*'+revision)
+ sys.stdout.write(text)
+
+ sys.stdout.flush()
+ return os.EX_OK
+
+ def display_problems(self):
+ """
+ Display problems with the dependency graph such as slot collisions.
+ This is called internally by display() to show the problems _after_
+ the merge list where it is most likely to be seen, but if display()
+ is not going to be called then this method should be called explicitly
+ to ensure that the user is notified of problems with the graph.
+
+ All output goes to stderr, except for unsatisfied dependencies which
+ go to stdout for parsing by programs such as autounmask.
+ """
+
+ # Note that show_masked_packages() sends it's output to
+ # stdout, and some programs such as autounmask parse the
+ # output in cases when emerge bails out. However, when
+ # show_masked_packages() is called for installed packages
+ # here, the message is a warning that is more appropriate
+ # to send to stderr, so temporarily redirect stdout to
+ # stderr. TODO: Fix output code so there's a cleaner way
+ # to redirect everything to stderr.
+ sys.stdout.flush()
+ sys.stderr.flush()
+ stdout = sys.stdout
+ try:
+ sys.stdout = sys.stderr
+ self._display_problems()
+ finally:
+ sys.stdout = stdout
+ sys.stdout.flush()
+ sys.stderr.flush()
+
+ # This goes to stdout for parsing by programs like autounmask.
+ for pargs, kwargs in self._unsatisfied_deps_for_display:
+ self._show_unsatisfied_dep(*pargs, **kwargs)
+
+ def _display_problems(self):
+ if self._circular_deps_for_display is not None:
+ self._show_circular_deps(
+ self._circular_deps_for_display)
+
+ # The user is only notified of a slot conflict if
+ # there are no unresolvable blocker conflicts.
+ if self._unsatisfied_blockers_for_display is not None:
+ self._show_unsatisfied_blockers(
+ self._unsatisfied_blockers_for_display)
+ else:
+ self._show_slot_collision_notice()
+
+ # TODO: Add generic support for "set problem" handlers so that
+ # the below warnings aren't special cases for world only.
+
+ if self._missing_args:
+ world_problems = False
+ if "world" in self._sets:
+ # Filter out indirect members of world (from nested sets)
+ # since only direct members of world are desired here.
+ world_set = self.roots[self.target_root].sets["world"]
+ for arg, atom in self._missing_args:
+ if arg.name == "world" and atom in world_set:
+ world_problems = True
+ break
+
+ if world_problems:
+ sys.stderr.write("\n!!! Problems have been " + \
+ "detected with your world file\n")
+ sys.stderr.write("!!! Please run " + \
+ green("emaint --check world")+"\n\n")
+
+ if self._missing_args:
+ sys.stderr.write("\n" + colorize("BAD", "!!!") + \
+ " Ebuilds for the following packages are either all\n")
+ sys.stderr.write(colorize("BAD", "!!!") + \
+ " masked or don't exist:\n")
+ sys.stderr.write(" ".join(str(atom) for arg, atom in \
+ self._missing_args) + "\n")
+
+ if self._pprovided_args:
+ arg_refs = {}
+ for arg, atom in self._pprovided_args:
+ if isinstance(arg, SetArg):
+ parent = arg.name
+ arg_atom = (atom, atom)
+ else:
+ parent = "args"
+ arg_atom = (arg.arg, atom)
+ refs = arg_refs.setdefault(arg_atom, [])
+ if parent not in refs:
+ refs.append(parent)
+ msg = []
+ msg.append(bad("\nWARNING: "))
+ if len(self._pprovided_args) > 1:
+ msg.append("Requested packages will not be " + \
+ "merged because they are listed in\n")
+ else:
+ msg.append("A requested package will not be " + \
+ "merged because it is listed in\n")
+ msg.append("package.provided:\n\n")
+ problems_sets = set()
+ for (arg, atom), refs in arg_refs.iteritems():
+ ref_string = ""
+ if refs:
+ problems_sets.update(refs)
+ refs.sort()
+ ref_string = ", ".join(["'%s'" % name for name in refs])
+ ref_string = " pulled in by " + ref_string
+ msg.append(" %s%s\n" % (colorize("INFORM", str(arg)), ref_string))
+ msg.append("\n")
+ if "world" in problems_sets:
+ msg.append("This problem can be solved in one of the following ways:\n\n")
+ msg.append(" A) Use emaint to clean offending packages from world (if not installed).\n")
+ msg.append(" B) Uninstall offending packages (cleans them from world).\n")
+ msg.append(" C) Remove offending entries from package.provided.\n\n")
+ msg.append("The best course of action depends on the reason that an offending\n")
+ msg.append("package.provided entry exists.\n\n")
+ sys.stderr.write("".join(msg))
+
+ masked_packages = []
+ for pkg in self._masked_installed:
+ root_config = pkg.root_config
+ pkgsettings = self.pkgsettings[pkg.root]
+ mreasons = get_masking_status(pkg, pkgsettings, root_config)
+ masked_packages.append((root_config, pkgsettings,
+ pkg.cpv, pkg.metadata, mreasons))
+ if masked_packages:
+ sys.stderr.write("\n" + colorize("BAD", "!!!") + \
+ " The following installed packages are masked:\n")
+ show_masked_packages(masked_packages)
+ show_mask_docs()
+ print
+
+ def calc_changelog(self,ebuildpath,current,next):
+ if ebuildpath == None or not os.path.exists(ebuildpath):
+ return []
+ current = '-'.join(portage.catpkgsplit(current)[1:])
+ if current.endswith('-r0'):
+ current = current[:-3]
+ next = '-'.join(portage.catpkgsplit(next)[1:])
+ if next.endswith('-r0'):
+ next = next[:-3]
+ changelogpath = os.path.join(os.path.split(ebuildpath)[0],'ChangeLog')
+ try:
+ changelog = open(changelogpath).read()
+ except SystemExit, e:
+ raise # Needed else can't exit
+ except:
+ return []
+ divisions = self.find_changelog_tags(changelog)
+ #print 'XX from',current,'to',next
+ #for div,text in divisions: print 'XX',div
+ # skip entries for all revisions above the one we are about to emerge
+ for i in range(len(divisions)):
+ if divisions[i][0]==next:
+ divisions = divisions[i:]
+ break
+ # find out how many entries we are going to display
+ for i in range(len(divisions)):
+ if divisions[i][0]==current:
+ divisions = divisions[:i]
+ break
+ else:
+ # couldnt find the current revision in the list. display nothing
+ return []
+ return divisions
+
+ def find_changelog_tags(self,changelog):
+ divs = []
+ release = None
+ while 1:
+ match = re.search(r'^\*\ ?([-a-zA-Z0-9_.+]*)(?:\ .*)?\n',changelog,re.M)
+ if match is None:
+ if release is not None:
+ divs.append((release,changelog))
+ return divs
+ if release is not None:
+ divs.append((release,changelog[:match.start()]))
+ changelog = changelog[match.end():]
+ release = match.group(1)
+ if release.endswith('.ebuild'):
+ release = release[:-7]
+ if release.endswith('-r0'):
+ release = release[:-3]
+
+ def saveNomergeFavorites(self):
+ """Find atoms in favorites that are not in the mergelist and add them
+ to the world file if necessary."""
+ for x in ("--buildpkgonly", "--fetchonly", "--fetch-all-uri",
+ "--oneshot", "--onlydeps", "--pretend"):
+ if x in self.myopts:
+ return
+ root_config = self.roots[self.target_root]
+ world_set = root_config.sets["world"]
+
+ world_locked = False
+ if hasattr(world_set, "lock"):
+ world_set.lock()
+ world_locked = True
+
+ if hasattr(world_set, "load"):
+ world_set.load() # maybe it's changed on disk
+
+ args_set = self._sets["args"]
+ portdb = self.trees[self.target_root]["porttree"].dbapi
+ added_favorites = set()
+ for x in self._set_nodes:
+ pkg_type, root, pkg_key, pkg_status = x
+ if pkg_status != "nomerge":
+ continue
+
+ try:
+ myfavkey = create_world_atom(x, args_set, root_config)
+ if myfavkey:
+ if myfavkey in added_favorites:
+ continue
+ added_favorites.add(myfavkey)
+ except portage.exception.InvalidDependString, e:
+ writemsg("\n\n!!! '%s' has invalid PROVIDE: %s\n" % \
+ (pkg_key, str(e)), noiselevel=-1)
+ writemsg("!!! see '%s'\n\n" % os.path.join(
+ root, portage.VDB_PATH, pkg_key, "PROVIDE"), noiselevel=-1)
+ del e
+ all_added = []
+ for k in self._sets:
+ if k in ("args", "world") or not root_config.sets[k].world_candidate:
+ continue
+ s = SETPREFIX + k
+ if s in world_set:
+ continue
+ all_added.append(SETPREFIX + k)
+ all_added.extend(added_favorites)
+ all_added.sort()
+ for a in all_added:
+ print ">>> Recording %s in \"world\" favorites file..." % \
+ colorize("INFORM", str(a))
+ if all_added:
+ world_set.update(all_added)
+
+ if world_locked:
+ world_set.unlock()
+
+ def loadResumeCommand(self, resume_data, skip_masked=True,
+ skip_missing=True):
+ """
+ Add a resume command to the graph and validate it in the process. This
+ will raise a PackageNotFound exception if a package is not available.
+ """
+
+ if not isinstance(resume_data, dict):
+ return False
+
+ mergelist = resume_data.get("mergelist")
+ if not isinstance(mergelist, list):
+ mergelist = []
+
+ fakedb = self.mydbapi
+ trees = self.trees
+ serialized_tasks = []
+ masked_tasks = []
+ for x in mergelist:
+ if not (isinstance(x, list) and len(x) == 4):
+ continue
+ pkg_type, myroot, pkg_key, action = x
+ if pkg_type not in self.pkg_tree_map:
+ continue
+ if action != "merge":
+ continue
+ tree_type = self.pkg_tree_map[pkg_type]
+ mydb = trees[myroot][tree_type].dbapi
+ db_keys = list(self._trees_orig[myroot][
+ tree_type].dbapi._aux_cache_keys)
+ try:
+ metadata = izip(db_keys, mydb.aux_get(pkg_key, db_keys))
+ except KeyError:
+ # It does no exist or it is corrupt.
+ if action == "uninstall":
+ continue
+ if skip_missing:
+ # TODO: log these somewhere
+ continue
+ raise portage.exception.PackageNotFound(pkg_key)
+ installed = action == "uninstall"
+ built = pkg_type != "ebuild"
+ root_config = self.roots[myroot]
+ pkg = Package(built=built, cpv=pkg_key,
+ installed=installed, metadata=metadata,
+ operation=action, root_config=root_config,
+ type_name=pkg_type)
+ if pkg_type == "ebuild":
+ pkgsettings = self.pkgsettings[myroot]
+ pkgsettings.setcpv(pkg)
+ pkg.metadata["USE"] = pkgsettings["PORTAGE_USE"]
+ pkg.metadata['CHOST'] = pkgsettings.get('CHOST', '')
+ self._pkg_cache[pkg] = pkg
+
+ root_config = self.roots[pkg.root]
+ if "merge" == pkg.operation and \
+ not visible(root_config.settings, pkg):
+ if skip_masked:
+ masked_tasks.append(Dependency(root=pkg.root, parent=pkg))
+ else:
+ self._unsatisfied_deps_for_display.append(
+ ((pkg.root, "="+pkg.cpv), {"myparent":None}))
+
+ fakedb[myroot].cpv_inject(pkg)
+ serialized_tasks.append(pkg)
+ self.spinner.update()
+
+ if self._unsatisfied_deps_for_display:
+ return False
+
+ if not serialized_tasks or "--nodeps" in self.myopts:
+ self._serialized_tasks_cache = serialized_tasks
+ self._scheduler_graph = self.digraph
+ else:
+ self._select_package = self._select_pkg_from_graph
+ self.myparams.add("selective")
+ # Always traverse deep dependencies in order to account for
+ # potentially unsatisfied dependencies of installed packages.
+ # This is necessary for correct --keep-going or --resume operation
+ # in case a package from a group of circularly dependent packages
+ # fails. In this case, a package which has recently been installed
+ # may have an unsatisfied circular dependency (pulled in by
+ # PDEPEND, for example). So, even though a package is already
+ # installed, it may not have all of it's dependencies satisfied, so
+ # it may not be usable. If such a package is in the subgraph of
+ # deep depenedencies of a scheduled build, that build needs to
+ # be cancelled. In order for this type of situation to be
+ # recognized, deep traversal of dependencies is required.
+ self.myparams.add("deep")
+
+ favorites = resume_data.get("favorites")
+ args_set = self._sets["args"]
+ if isinstance(favorites, list):
+ args = self._load_favorites(favorites)
+ else:
+ args = []
+
+ for task in serialized_tasks:
+ if isinstance(task, Package) and \
+ task.operation == "merge":
+ if not self._add_pkg(task, None):
+ return False
+
+ # Packages for argument atoms need to be explicitly
+ # added via _add_pkg() so that they are included in the
+ # digraph (needed at least for --tree display).
+ for arg in args:
+ for atom in arg.set:
+ pkg, existing_node = self._select_package(
+ arg.root_config.root, atom)
+ if existing_node is None and \
+ pkg is not None:
+ if not self._add_pkg(pkg, Dependency(atom=atom,
+ root=pkg.root, parent=arg)):
+ return False
+
+ # Allow unsatisfied deps here to avoid showing a masking
+ # message for an unsatisfied dep that isn't necessarily
+ # masked.
+ if not self._create_graph(allow_unsatisfied=True):
+ return False
+
+ unsatisfied_deps = []
+ for dep in self._unsatisfied_deps:
+ if not isinstance(dep.parent, Package):
+ continue
+ if dep.parent.operation == "merge":
+ unsatisfied_deps.append(dep)
+ continue
+
+ # For unsatisfied deps of installed packages, only account for
+ # them if they are in the subgraph of dependencies of a package
+ # which is scheduled to be installed.
+ unsatisfied_install = False
+ traversed = set()
+ dep_stack = self.digraph.parent_nodes(dep.parent)
+ while dep_stack:
+ node = dep_stack.pop()
+ if not isinstance(node, Package):
+ continue
+ if node.operation == "merge":
+ unsatisfied_install = True
+ break
+ if node in traversed:
+ continue
+ traversed.add(node)
+ dep_stack.extend(self.digraph.parent_nodes(node))
+
+ if unsatisfied_install:
+ unsatisfied_deps.append(dep)
+
+ if masked_tasks or unsatisfied_deps:
+ # This probably means that a required package
+ # was dropped via --skipfirst. It makes the
+ # resume list invalid, so convert it to a
+ # UnsatisfiedResumeDep exception.
+ raise self.UnsatisfiedResumeDep(self,
+ masked_tasks + unsatisfied_deps)
+ self._serialized_tasks_cache = None
+ try:
+ self.altlist()
+ except self._unknown_internal_error:
+ return False
+
+ return True
+
+ def _load_favorites(self, favorites):
+ """
+ Use a list of favorites to resume state from a
+ previous select_files() call. This creates similar
+ DependencyArg instances to those that would have
+ been created by the original select_files() call.
+ This allows Package instances to be matched with
+ DependencyArg instances during graph creation.
+ """
+ root_config = self.roots[self.target_root]
+ getSetAtoms = root_config.setconfig.getSetAtoms
+ sets = root_config.sets
+ args = []
+ for x in favorites:
+ if not isinstance(x, basestring):
+ continue
+ if x in ("system", "world"):
+ x = SETPREFIX + x
+ if x.startswith(SETPREFIX):
+ s = x[len(SETPREFIX):]
+ if s not in sets:
+ continue
+ if s in self._sets:
+ continue
+ # Recursively expand sets so that containment tests in
+ # self._get_parent_sets() properly match atoms in nested
+ # sets (like if world contains system).
+ expanded_set = InternalPackageSet(
+ initial_atoms=getSetAtoms(s))
+ self._sets[s] = expanded_set
+ args.append(SetArg(arg=x, set=expanded_set,
+ root_config=root_config))
+ else:
+ if not portage.isvalidatom(x):
+ continue
+ args.append(AtomArg(arg=x, atom=x,
+ root_config=root_config))
+
+ self._set_args(args)
+ return args
+
+ class UnsatisfiedResumeDep(portage.exception.PortageException):
+ """
+ A dependency of a resume list is not installed. This
+ can occur when a required package is dropped from the
+ merge list via --skipfirst.
+ """
+ def __init__(self, depgraph, value):
+ portage.exception.PortageException.__init__(self, value)
+ self.depgraph = depgraph
+
+ class _internal_exception(portage.exception.PortageException):
+ def __init__(self, value=""):
+ portage.exception.PortageException.__init__(self, value)
+
+ class _unknown_internal_error(_internal_exception):
+ """
+ Used by the depgraph internally to terminate graph creation.
+ The specific reason for the failure should have been dumped
+ to stderr, unfortunately, the exact reason for the failure
+ may not be known.
+ """
+
+ class _serialize_tasks_retry(_internal_exception):
+ """
+ This is raised by the _serialize_tasks() method when it needs to
+ be called again for some reason. The only case that it's currently
+ used for is when neglected dependencies need to be added to the
+ graph in order to avoid making a potentially unsafe decision.
+ """
+
+ class _dep_check_composite_db(portage.dbapi):
+ """
+ A dbapi-like interface that is optimized for use in dep_check() calls.
+ This is built on top of the existing depgraph package selection logic.
+ Some packages that have been added to the graph may be masked from this
+ view in order to influence the atom preference selection that occurs
+ via dep_check().
+ """
+ def __init__(self, depgraph, root):
+ portage.dbapi.__init__(self)
+ self._depgraph = depgraph
+ self._root = root
+ self._match_cache = {}
+ self._cpv_pkg_map = {}
+
+ def _clear_cache(self):
+ self._match_cache.clear()
+ self._cpv_pkg_map.clear()
+
+ def match(self, atom):
+ ret = self._match_cache.get(atom)
+ if ret is not None:
+ return ret[:]
+ orig_atom = atom
+ if "/" not in atom:
+ atom = self._dep_expand(atom)
+ pkg, existing = self._depgraph._select_package(self._root, atom)
+ if not pkg:
+ ret = []
+ else:
+ # Return the highest available from select_package() as well as
+ # any matching slots in the graph db.
+ slots = set()
+ slots.add(pkg.metadata["SLOT"])
+ atom_cp = portage.dep_getkey(atom)
+ if pkg.cp.startswith("virtual/"):
+ # For new-style virtual lookahead that occurs inside
+ # dep_check(), examine all slots. This is needed
+ # so that newer slots will not unnecessarily be pulled in
+ # when a satisfying lower slot is already installed. For
+ # example, if virtual/jdk-1.4 is satisfied via kaffe then
+ # there's no need to pull in a newer slot to satisfy a
+ # virtual/jdk dependency.
+ for db, pkg_type, built, installed, db_keys in \
+ self._depgraph._filtered_trees[self._root]["dbs"]:
+ for cpv in db.match(atom):
+ if portage.cpv_getkey(cpv) != pkg.cp:
+ continue
+ slots.add(db.aux_get(cpv, ["SLOT"])[0])
+ ret = []
+ if self._visible(pkg):
+ self._cpv_pkg_map[pkg.cpv] = pkg
+ ret.append(pkg.cpv)
+ slots.remove(pkg.metadata["SLOT"])
+ while slots:
+ slot_atom = "%s:%s" % (atom_cp, slots.pop())
+ pkg, existing = self._depgraph._select_package(
+ self._root, slot_atom)
+ if not pkg:
+ continue
+ if not self._visible(pkg):
+ continue
+ self._cpv_pkg_map[pkg.cpv] = pkg
+ ret.append(pkg.cpv)
+ if ret:
+ self._cpv_sort_ascending(ret)
+ self._match_cache[orig_atom] = ret
+ return ret[:]
+
+ def _visible(self, pkg):
+ if pkg.installed and "selective" not in self._depgraph.myparams:
+ try:
+ arg = self._depgraph._iter_atoms_for_pkg(pkg).next()
+ except (StopIteration, portage.exception.InvalidDependString):
+ arg = None
+ if arg:
+ return False
+ if pkg.installed:
+ try:
+ if not visible(
+ self._depgraph.pkgsettings[pkg.root], pkg):
+ return False
+ except portage.exception.InvalidDependString:
+ pass
+ in_graph = self._depgraph._slot_pkg_map[
+ self._root].get(pkg.slot_atom)
+ if in_graph is None:
+ # Mask choices for packages which are not the highest visible
+ # version within their slot (since they usually trigger slot
+ # conflicts).
+ highest_visible, in_graph = self._depgraph._select_package(
+ self._root, pkg.slot_atom)
+ if pkg != highest_visible:
+ return False
+ elif in_graph != pkg:
+ # Mask choices for packages that would trigger a slot
+ # conflict with a previously selected package.
+ return False
+ return True
+
+ def _dep_expand(self, atom):
+ """
+ This is only needed for old installed packages that may
+ contain atoms that are not fully qualified with a specific
+ category. Emulate the cpv_expand() function that's used by
+ dbapi.match() in cases like this. If there are multiple
+ matches, it's often due to a new-style virtual that has
+ been added, so try to filter those out to avoid raising
+ a ValueError.
+ """
+ root_config = self._depgraph.roots[self._root]
+ orig_atom = atom
+ expanded_atoms = self._depgraph._dep_expand(root_config, atom)
+ if len(expanded_atoms) > 1:
+ non_virtual_atoms = []
+ for x in expanded_atoms:
+ if not portage.dep_getkey(x).startswith("virtual/"):
+ non_virtual_atoms.append(x)
+ if len(non_virtual_atoms) == 1:
+ expanded_atoms = non_virtual_atoms
+ if len(expanded_atoms) > 1:
+ # compatible with portage.cpv_expand()
+ raise portage.exception.AmbiguousPackageName(
+ [portage.dep_getkey(x) for x in expanded_atoms])
+ if expanded_atoms:
+ atom = expanded_atoms[0]
+ else:
+ null_atom = insert_category_into_atom(atom, "null")
+ null_cp = portage.dep_getkey(null_atom)
+ cat, atom_pn = portage.catsplit(null_cp)
+ virts_p = root_config.settings.get_virts_p().get(atom_pn)
+ if virts_p:
+ # Allow the resolver to choose which virtual.
+ atom = insert_category_into_atom(atom, "virtual")
+ else:
+ atom = insert_category_into_atom(atom, "null")
+ return atom
+
+ def aux_get(self, cpv, wants):
+ metadata = self._cpv_pkg_map[cpv].metadata
+ return [metadata.get(x, "") for x in wants]
+
+
+def ambiguous_package_name(arg, atoms, root_config, spinner, myopts):
+
+ if "--quiet" in myopts:
+ print "!!! The short ebuild name \"%s\" is ambiguous. Please specify" % arg
+ print "!!! one of the following fully-qualified ebuild names instead:\n"
+ for cp in sorted(set(portage.dep_getkey(atom) for atom in atoms)):
+ print " " + colorize("INFORM", cp)
+ return
+
+ s = search(root_config, spinner, "--searchdesc" in myopts,
+ "--quiet" not in myopts, "--usepkg" in myopts,
+ "--usepkgonly" in myopts)
+ null_cp = portage.dep_getkey(insert_category_into_atom(
+ arg, "null"))
+ cat, atom_pn = portage.catsplit(null_cp)
+ s.searchkey = atom_pn
+ for cp in sorted(set(portage.dep_getkey(atom) for atom in atoms)):
+ s.addCP(cp)
+ s.output()
+ print "!!! The short ebuild name \"%s\" is ambiguous. Please specify" % arg
+ print "!!! one of the above fully-qualified ebuild names instead.\n"
+
+def insert_category_into_atom(atom, category):
+ alphanum = re.search(r'\w', atom)
+ if alphanum:
+ ret = atom[:alphanum.start()] + "%s/" % category + \
+ atom[alphanum.start():]
+ else:
+ ret = None
+ return ret
+
+def resume_depgraph(settings, trees, mtimedb, myopts, myparams, spinner):
+ """
+ Construct a depgraph for the given resume list. This will raise
+ PackageNotFound or depgraph.UnsatisfiedResumeDep when necessary.
+ @rtype: tuple
+ @returns: (success, depgraph, dropped_tasks)
+ """
+ skip_masked = True
+ skip_unsatisfied = True
+ mergelist = mtimedb["resume"]["mergelist"]
+ dropped_tasks = set()
+ while True:
+ mydepgraph = depgraph(settings, trees,
+ myopts, myparams, spinner)
+ try:
+ success = mydepgraph.loadResumeCommand(mtimedb["resume"],
+ skip_masked=skip_masked)
+ except depgraph.UnsatisfiedResumeDep, e:
+ if not skip_unsatisfied:
+ raise
+
+ graph = mydepgraph.digraph
+ unsatisfied_parents = dict((dep.parent, dep.parent) \
+ for dep in e.value)
+ traversed_nodes = set()
+ unsatisfied_stack = list(unsatisfied_parents)
+ while unsatisfied_stack:
+ pkg = unsatisfied_stack.pop()
+ if pkg in traversed_nodes:
+ continue
+ traversed_nodes.add(pkg)
+
+ # If this package was pulled in by a parent
+ # package scheduled for merge, removing this
+ # package may cause the the parent package's
+ # dependency to become unsatisfied.
+ for parent_node in graph.parent_nodes(pkg):
+ if not isinstance(parent_node, Package) \
+ or parent_node.operation not in ("merge", "nomerge"):
+ continue
+ unsatisfied = \
+ graph.child_nodes(parent_node,
+ ignore_priority=DepPrioritySatisfiedRange.ignore_soft)
+ if pkg in unsatisfied:
+ unsatisfied_parents[parent_node] = parent_node
+ unsatisfied_stack.append(parent_node)
+
+ pruned_mergelist = []
+ for x in mergelist:
+ if isinstance(x, list) and \
+ tuple(x) not in unsatisfied_parents:
+ pruned_mergelist.append(x)
+
+ # If the mergelist doesn't shrink then this loop is infinite.
+ if len(pruned_mergelist) == len(mergelist):
+ # This happens if a package can't be dropped because
+ # it's already installed, but it has unsatisfied PDEPEND.
+ raise
+ mergelist[:] = pruned_mergelist
+
+ # Exclude installed packages that have been removed from the graph due
+ # to failure to build/install runtime dependencies after the dependent
+ # package has already been installed.
+ dropped_tasks.update(pkg for pkg in \
+ unsatisfied_parents if pkg.operation != "nomerge")
+ mydepgraph.break_refs(unsatisfied_parents)
+
+ del e, graph, traversed_nodes, \
+ unsatisfied_parents, unsatisfied_stack
+ continue
+ else:
+ break
+ return (success, mydepgraph, dropped_tasks)
+
+def get_mask_info(root_config, cpv, pkgsettings,
+ db, pkg_type, built, installed, db_keys):
+ eapi_masked = False
+ try:
+ metadata = dict(izip(db_keys,
+ db.aux_get(cpv, db_keys)))
+ except KeyError:
+ metadata = None
+ if metadata and not built:
+ pkgsettings.setcpv(cpv, mydb=metadata)
+ metadata["USE"] = pkgsettings["PORTAGE_USE"]
+ metadata['CHOST'] = pkgsettings.get('CHOST', '')
+ if metadata is None:
+ mreasons = ["corruption"]
+ else:
+ eapi = metadata['EAPI']
+ if eapi[:1] == '-':
+ eapi = eapi[1:]
+ if not portage.eapi_is_supported(eapi):
+ mreasons = ['EAPI %s' % eapi]
+ else:
+ pkg = Package(type_name=pkg_type, root_config=root_config,
+ cpv=cpv, built=built, installed=installed, metadata=metadata)
+ mreasons = get_masking_status(pkg, pkgsettings, root_config)
+ return metadata, mreasons
+
+def show_masked_packages(masked_packages):
+ shown_licenses = set()
+ shown_comments = set()
+ # Maybe there is both an ebuild and a binary. Only
+ # show one of them to avoid redundant appearance.
+ shown_cpvs = set()
+ have_eapi_mask = False
+ for (root_config, pkgsettings, cpv,
+ metadata, mreasons) in masked_packages:
+ if cpv in shown_cpvs:
+ continue
+ shown_cpvs.add(cpv)
+ comment, filename = None, None
+ if "package.mask" in mreasons:
+ comment, filename = \
+ portage.getmaskingreason(
+ cpv, metadata=metadata,
+ settings=pkgsettings,
+ portdb=root_config.trees["porttree"].dbapi,
+ return_location=True)
+ missing_licenses = []
+ if metadata:
+ if not portage.eapi_is_supported(metadata["EAPI"]):
+ have_eapi_mask = True
+ try:
+ missing_licenses = \
+ pkgsettings._getMissingLicenses(
+ cpv, metadata)
+ except portage.exception.InvalidDependString:
+ # This will have already been reported
+ # above via mreasons.
+ pass
+
+ print "- "+cpv+" (masked by: "+", ".join(mreasons)+")"
+ if comment and comment not in shown_comments:
+ print filename+":"
+ print comment
+ shown_comments.add(comment)
+ portdb = root_config.trees["porttree"].dbapi
+ for l in missing_licenses:
+ l_path = portdb.findLicensePath(l)
+ if l in shown_licenses:
+ continue
+ msg = ("A copy of the '%s' license" + \
+ " is located at '%s'.") % (l, l_path)
+ print msg
+ print
+ shown_licenses.add(l)
+ return have_eapi_mask
+
+def show_mask_docs():
+ print "For more information, see the MASKED PACKAGES section in the emerge"
+ print "man page or refer to the Gentoo Handbook."
+
+def filter_iuse_defaults(iuse):
+ for flag in iuse:
+ if flag.startswith("+") or flag.startswith("-"):
+ yield flag[1:]
+ else:
+ yield flag
+
+def show_blocker_docs_link():
+ print
+ print "For more information about " + bad("Blocked Packages") + ", please refer to the following"
+ print "section of the Gentoo Linux x86 Handbook (architecture is irrelevant):"
+ print
+ print "http://www.gentoo.org/doc/en/handbook/handbook-x86.xml?full=1#blocked"
+ print
+
+def get_masking_status(pkg, pkgsettings, root_config):
+
+ mreasons = portage.getmaskingstatus(
+ pkg, settings=pkgsettings,
+ portdb=root_config.trees["porttree"].dbapi)
+
+ if not pkg.installed:
+ if not pkgsettings._accept_chost(pkg.cpv, pkg.metadata):
+ mreasons.append("CHOST: %s" % \
+ pkg.metadata["CHOST"])
+
+ if not pkg.metadata["SLOT"]:
+ mreasons.append("invalid: SLOT is undefined")
+
+ return mreasons
diff --git a/pym/_emerge/is_valid_package_atom.py b/pym/_emerge/is_valid_package_atom.py
new file mode 100644
index 000000000..bdfedc95b
--- /dev/null
+++ b/pym/_emerge/is_valid_package_atom.py
@@ -0,0 +1,16 @@
+import re
+
+try:
+ import portage
+except ImportError:
+ from os import path as osp
+ import sys
+ sys.path.insert(0, osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), "pym"))
+ import portage
+
+def is_valid_package_atom(x):
+ if "/" not in x:
+ alphanum = re.search(r'\w', x)
+ if alphanum:
+ x = x[:alphanum.start()] + "cat/" + x[alphanum.start():]
+ return portage.isvalidatom(x)