diff options
author | Zac Medico <zmedico@gentoo.org> | 2009-06-22 20:02:48 +0000 |
---|---|---|
committer | Zac Medico <zmedico@gentoo.org> | 2009-06-22 20:02:48 +0000 |
commit | 4827f2de62ec604fbf3a6dafbcfbe2c180481efe (patch) | |
tree | 918bbd1b90db48effaa3ccd0790e224b66da01a8 | |
parent | bf9282b6782ad433b2ca905a5131bd0c424a2d94 (diff) | |
download | portage-4827f2de62ec604fbf3a6dafbcfbe2c180481efe.tar.gz portage-4827f2de62ec604fbf3a6dafbcfbe2c180481efe.tar.bz2 portage-4827f2de62ec604fbf3a6dafbcfbe2c180481efe.zip |
Bug #275047 - Split _emerge/__init__.py into smaller pieces (part 4).
Thanks to Sebastian Mingramm (few) <s.mingramm@gmx.de> for this patch.
svn path=/main/trunk/; revision=13669
-rw-r--r-- | pym/_emerge/BlockerDB.py | 127 | ||||
-rw-r--r-- | pym/_emerge/FakeVartree.py | 235 | ||||
-rw-r--r-- | pym/_emerge/MergeListItem.py | 146 | ||||
-rw-r--r-- | pym/_emerge/MetadataRegen.py | 170 | ||||
-rw-r--r-- | pym/_emerge/Package.py | 185 | ||||
-rw-r--r-- | pym/_emerge/PackageCounters.py | 77 | ||||
-rw-r--r-- | pym/_emerge/PackageUninstall.py | 51 | ||||
-rw-r--r-- | pym/_emerge/RootConfig.py | 28 | ||||
-rw-r--r-- | pym/_emerge/__init__.py | 1975 | ||||
-rw-r--r-- | pym/_emerge/countdown.py | 17 | ||||
-rw-r--r-- | pym/_emerge/emergelog.py | 41 | ||||
-rw-r--r-- | pym/_emerge/format_size.py | 16 | ||||
-rw-r--r-- | pym/_emerge/search.py | 378 | ||||
-rw-r--r-- | pym/_emerge/show_invalid_depstring_notice.py | 39 | ||||
-rw-r--r-- | pym/_emerge/unmerge.py | 523 | ||||
-rw-r--r-- | pym/_emerge/userquery.py | 44 | ||||
-rw-r--r-- | pym/_emerge/visible.py | 40 |
17 files changed, 2134 insertions, 1958 deletions
diff --git a/pym/_emerge/BlockerDB.py b/pym/_emerge/BlockerDB.py new file mode 100644 index 000000000..7ef78d824 --- /dev/null +++ b/pym/_emerge/BlockerDB.py @@ -0,0 +1,127 @@ +import os + +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 + +from portage import digraph +from portage.sets.base import InternalPackageSet + +from _emerge.BlockerCache import BlockerCache +from _emerge.FakeVartree import FakeVartree +from _emerge.show_invalid_depstring_notice import show_invalid_depstring_notice + +class BlockerDB(object): + + def __init__(self, root_config): + self._root_config = root_config + self._vartree = root_config.trees["vartree"] + self._portdb = root_config.trees["porttree"].dbapi + + self._dep_check_trees = None + self._fake_vartree = None + + def _get_fake_vartree(self, acquire_lock=0): + fake_vartree = self._fake_vartree + if fake_vartree is None: + fake_vartree = FakeVartree(self._root_config, + acquire_lock=acquire_lock) + self._fake_vartree = fake_vartree + self._dep_check_trees = { self._vartree.root : { + "porttree" : fake_vartree, + "vartree" : fake_vartree, + }} + else: + fake_vartree.sync(acquire_lock=acquire_lock) + return fake_vartree + + def findInstalledBlockers(self, new_pkg, acquire_lock=0): + blocker_cache = BlockerCache(self._vartree.root, self._vartree.dbapi) + dep_keys = ["DEPEND", "RDEPEND", "PDEPEND"] + settings = self._vartree.settings + stale_cache = set(blocker_cache) + fake_vartree = self._get_fake_vartree(acquire_lock=acquire_lock) + dep_check_trees = self._dep_check_trees + vardb = fake_vartree.dbapi + installed_pkgs = list(vardb) + + for inst_pkg in installed_pkgs: + stale_cache.discard(inst_pkg.cpv) + cached_blockers = blocker_cache.get(inst_pkg.cpv) + if cached_blockers is not None and \ + cached_blockers.counter != long(inst_pkg.metadata["COUNTER"]): + cached_blockers = None + if cached_blockers is not None: + blocker_atoms = cached_blockers.atoms + else: + # Use aux_get() to trigger FakeVartree global + # updates on *DEPEND when appropriate. + depstr = " ".join(vardb.aux_get(inst_pkg.cpv, dep_keys)) + try: + portage.dep._dep_check_strict = False + success, atoms = portage.dep_check(depstr, + vardb, settings, myuse=inst_pkg.use.enabled, + trees=dep_check_trees, myroot=inst_pkg.root) + finally: + portage.dep._dep_check_strict = True + if not success: + pkg_location = os.path.join(inst_pkg.root, + portage.VDB_PATH, inst_pkg.category, inst_pkg.pf) + portage.writemsg("!!! %s/*DEPEND: %s\n" % \ + (pkg_location, atoms), noiselevel=-1) + continue + + blocker_atoms = [atom for atom in atoms \ + if atom.startswith("!")] + blocker_atoms.sort() + counter = long(inst_pkg.metadata["COUNTER"]) + blocker_cache[inst_pkg.cpv] = \ + blocker_cache.BlockerData(counter, blocker_atoms) + for cpv in stale_cache: + del blocker_cache[cpv] + blocker_cache.flush() + + blocker_parents = digraph() + blocker_atoms = [] + for pkg in installed_pkgs: + for blocker_atom in blocker_cache[pkg.cpv].atoms: + blocker_atom = blocker_atom.lstrip("!") + blocker_atoms.append(blocker_atom) + blocker_parents.add(blocker_atom, pkg) + + blocker_atoms = InternalPackageSet(initial_atoms=blocker_atoms) + blocking_pkgs = set() + for atom in blocker_atoms.iterAtomsForPackage(new_pkg): + blocking_pkgs.update(blocker_parents.parent_nodes(atom)) + + # Check for blockers in the other direction. + depstr = " ".join(new_pkg.metadata[k] for k in dep_keys) + try: + portage.dep._dep_check_strict = False + success, atoms = portage.dep_check(depstr, + vardb, settings, myuse=new_pkg.use.enabled, + trees=dep_check_trees, myroot=new_pkg.root) + finally: + portage.dep._dep_check_strict = True + if not success: + # We should never get this far with invalid deps. + show_invalid_depstring_notice(new_pkg, depstr, atoms) + assert False + + blocker_atoms = [atom.lstrip("!") for atom in atoms \ + if atom[:1] == "!"] + if blocker_atoms: + blocker_atoms = InternalPackageSet(initial_atoms=blocker_atoms) + for inst_pkg in installed_pkgs: + try: + blocker_atoms.iterAtomsForPackage(inst_pkg).next() + except (portage.exception.InvalidDependString, StopIteration): + continue + blocking_pkgs.add(inst_pkg) + + return blocking_pkgs + diff --git a/pym/_emerge/FakeVartree.py b/pym/_emerge/FakeVartree.py new file mode 100644 index 000000000..2c5a0c654 --- /dev/null +++ b/pym/_emerge/FakeVartree.py @@ -0,0 +1,235 @@ +import os +from itertools import izip + +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 + +from _emerge.Package import Package +from _emerge.PackageVirtualDbapi import PackageVirtualDbapi + +class FakeVartree(portage.vartree): + """This is implements an in-memory copy of a vartree instance that provides + all the interfaces required for use by the depgraph. The vardb is locked + during the constructor call just long enough to read a copy of the + installed package information. This allows the depgraph to do it's + dependency calculations without holding a lock on the vardb. It also + allows things like vardb global updates to be done in memory so that the + user doesn't necessarily need write access to the vardb in cases where + global updates are necessary (updates are performed when necessary if there + is not a matching ebuild in the tree).""" + def __init__(self, root_config, pkg_cache=None, acquire_lock=1): + self._root_config = root_config + if pkg_cache is None: + pkg_cache = {} + real_vartree = root_config.trees["vartree"] + portdb = root_config.trees["porttree"].dbapi + self.root = real_vartree.root + self.settings = real_vartree.settings + mykeys = list(real_vartree.dbapi._aux_cache_keys) + if "_mtime_" not in mykeys: + mykeys.append("_mtime_") + self._db_keys = mykeys + self._pkg_cache = pkg_cache + self.dbapi = PackageVirtualDbapi(real_vartree.settings) + vdb_path = os.path.join(self.root, portage.VDB_PATH) + try: + # At least the parent needs to exist for the lock file. + portage.util.ensure_dirs(vdb_path) + except portage.exception.PortageException: + pass + vdb_lock = None + try: + if acquire_lock and os.access(vdb_path, os.W_OK): + vdb_lock = portage.locks.lockdir(vdb_path) + real_dbapi = real_vartree.dbapi + slot_counters = {} + for cpv in real_dbapi.cpv_all(): + cache_key = ("installed", self.root, cpv, "nomerge") + pkg = self._pkg_cache.get(cache_key) + if pkg is not None: + metadata = pkg.metadata + else: + metadata = dict(izip(mykeys, real_dbapi.aux_get(cpv, mykeys))) + myslot = metadata["SLOT"] + mycp = portage.dep_getkey(cpv) + myslot_atom = "%s:%s" % (mycp, myslot) + try: + mycounter = long(metadata["COUNTER"]) + except ValueError: + mycounter = 0 + metadata["COUNTER"] = str(mycounter) + other_counter = slot_counters.get(myslot_atom, None) + if other_counter is not None: + if other_counter > mycounter: + continue + slot_counters[myslot_atom] = mycounter + if pkg is None: + pkg = Package(built=True, cpv=cpv, + installed=True, metadata=metadata, + root_config=root_config, type_name="installed") + self._pkg_cache[pkg] = pkg + self.dbapi.cpv_inject(pkg) + real_dbapi.flush_cache() + finally: + if vdb_lock: + portage.locks.unlockdir(vdb_lock) + # Populate the old-style virtuals using the cached values. + if not self.settings.treeVirtuals: + self.settings.treeVirtuals = portage.util.map_dictlist_vals( + portage.getCPFromCPV, self.get_all_provides()) + + # Intialize variables needed for lazy cache pulls of the live ebuild + # metadata. This ensures that the vardb lock is released ASAP, without + # being delayed in case cache generation is triggered. + self._aux_get = self.dbapi.aux_get + self.dbapi.aux_get = self._aux_get_wrapper + self._match = self.dbapi.match + self.dbapi.match = self._match_wrapper + self._aux_get_history = set() + self._portdb_keys = ["EAPI", "DEPEND", "RDEPEND", "PDEPEND"] + self._portdb = portdb + self._global_updates = None + + def _match_wrapper(self, cpv, use_cache=1): + """ + Make sure the metadata in Package instances gets updated for any + cpv that is returned from a match() call, since the metadata can + be accessed directly from the Package instance instead of via + aux_get(). + """ + matches = self._match(cpv, use_cache=use_cache) + for cpv in matches: + if cpv in self._aux_get_history: + continue + self._aux_get_wrapper(cpv, []) + return matches + + def _aux_get_wrapper(self, pkg, wants): + if pkg in self._aux_get_history: + return self._aux_get(pkg, wants) + self._aux_get_history.add(pkg) + try: + # Use the live ebuild metadata if possible. + live_metadata = dict(izip(self._portdb_keys, + self._portdb.aux_get(pkg, self._portdb_keys))) + if not portage.eapi_is_supported(live_metadata["EAPI"]): + raise KeyError(pkg) + self.dbapi.aux_update(pkg, live_metadata) + except (KeyError, portage.exception.PortageException): + if self._global_updates is None: + self._global_updates = \ + grab_global_updates(self._portdb.porttree_root) + perform_global_updates( + pkg, self.dbapi, self._global_updates) + return self._aux_get(pkg, wants) + + def sync(self, acquire_lock=1): + """ + Call this method to synchronize state with the real vardb + after one or more packages may have been installed or + uninstalled. + """ + vdb_path = os.path.join(self.root, portage.VDB_PATH) + try: + # At least the parent needs to exist for the lock file. + portage.util.ensure_dirs(vdb_path) + except portage.exception.PortageException: + pass + vdb_lock = None + try: + if acquire_lock and os.access(vdb_path, os.W_OK): + vdb_lock = portage.locks.lockdir(vdb_path) + self._sync() + finally: + if vdb_lock: + portage.locks.unlockdir(vdb_lock) + + def _sync(self): + + real_vardb = self._root_config.trees["vartree"].dbapi + current_cpv_set = frozenset(real_vardb.cpv_all()) + pkg_vardb = self.dbapi + aux_get_history = self._aux_get_history + + # Remove any packages that have been uninstalled. + for pkg in list(pkg_vardb): + if pkg.cpv not in current_cpv_set: + pkg_vardb.cpv_remove(pkg) + aux_get_history.discard(pkg.cpv) + + # Validate counters and timestamps. + slot_counters = {} + root = self.root + validation_keys = ["COUNTER", "_mtime_"] + for cpv in current_cpv_set: + + pkg_hash_key = ("installed", root, cpv, "nomerge") + pkg = pkg_vardb.get(pkg_hash_key) + if pkg is not None: + counter, mtime = real_vardb.aux_get(cpv, validation_keys) + try: + counter = long(counter) + except ValueError: + counter = 0 + + if counter != pkg.counter or \ + mtime != pkg.mtime: + pkg_vardb.cpv_remove(pkg) + aux_get_history.discard(pkg.cpv) + pkg = None + + if pkg is None: + pkg = self._pkg(cpv) + + other_counter = slot_counters.get(pkg.slot_atom) + if other_counter is not None: + if other_counter > pkg.counter: + continue + + slot_counters[pkg.slot_atom] = pkg.counter + pkg_vardb.cpv_inject(pkg) + + real_vardb.flush_cache() + + def _pkg(self, cpv): + root_config = self._root_config + real_vardb = root_config.trees["vartree"].dbapi + pkg = Package(cpv=cpv, installed=True, + metadata=izip(self._db_keys, + real_vardb.aux_get(cpv, self._db_keys)), + root_config=root_config, + type_name="installed") + + try: + mycounter = long(pkg.metadata["COUNTER"]) + except ValueError: + mycounter = 0 + pkg.metadata["COUNTER"] = str(mycounter) + + return pkg + +def grab_global_updates(portdir): + from portage.update import grab_updates, parse_updates + updpath = os.path.join(portdir, "profiles", "updates") + try: + rawupdates = grab_updates(updpath) + except portage.exception.DirectoryNotFound: + rawupdates = [] + upd_commands = [] + for mykey, mystat, mycontent in rawupdates: + commands, errors = parse_updates(mycontent) + upd_commands.extend(commands) + return upd_commands + +def perform_global_updates(mycpv, mydb, mycommands): + from portage.update import update_dbentries + aux_keys = ["DEPEND", "RDEPEND", "PDEPEND"] + aux_dict = dict(izip(aux_keys, mydb.aux_get(mycpv, aux_keys))) + updates = update_dbentries(mycommands, aux_dict) + if updates: + mydb.aux_update(mycpv, updates) diff --git a/pym/_emerge/MergeListItem.py b/pym/_emerge/MergeListItem.py new file mode 100644 index 000000000..9238a7a37 --- /dev/null +++ b/pym/_emerge/MergeListItem.py @@ -0,0 +1,146 @@ +import os + +from portage.output import colorize + +from _emerge.Binpkg import Binpkg +from _emerge.CompositeTask import CompositeTask +from _emerge.EbuildBuild import EbuildBuild +from _emerge.PackageUninstall import PackageUninstall + +class MergeListItem(CompositeTask): + + """ + TODO: For parallel scheduling, everything here needs asynchronous + execution support (start, poll, and wait methods). + """ + + __slots__ = ("args_set", + "binpkg_opts", "build_opts", "config_pool", "emerge_opts", + "find_blockers", "logger", "mtimedb", "pkg", + "pkg_count", "pkg_to_replace", "prefetcher", + "settings", "statusMessage", "world_atom") + \ + ("_install_task",) + + def _start(self): + + pkg = self.pkg + build_opts = self.build_opts + + if pkg.installed: + # uninstall, executed by self.merge() + self.returncode = os.EX_OK + self.wait() + return + + args_set = self.args_set + find_blockers = self.find_blockers + logger = self.logger + mtimedb = self.mtimedb + pkg_count = self.pkg_count + scheduler = self.scheduler + settings = self.settings + world_atom = self.world_atom + ldpath_mtimes = mtimedb["ldpath"] + + action_desc = "Emerging" + preposition = "for" + if pkg.type_name == "binary": + action_desc += " binary" + + if build_opts.fetchonly: + action_desc = "Fetching" + + msg = "%s (%s of %s) %s" % \ + (action_desc, + colorize("MERGE_LIST_PROGRESS", str(pkg_count.curval)), + colorize("MERGE_LIST_PROGRESS", str(pkg_count.maxval)), + colorize("GOOD", pkg.cpv)) + + portdb = pkg.root_config.trees["porttree"].dbapi + portdir_repo_name = portdb._repository_map.get(portdb.porttree_root) + if portdir_repo_name: + pkg_repo_name = pkg.metadata.get("repository") + if pkg_repo_name != portdir_repo_name: + if not pkg_repo_name: + pkg_repo_name = "unknown repo" + msg += " from %s" % pkg_repo_name + + if pkg.root != "/": + msg += " %s %s" % (preposition, pkg.root) + + if not build_opts.pretend: + self.statusMessage(msg) + logger.log(" >>> emerge (%s of %s) %s to %s" % \ + (pkg_count.curval, pkg_count.maxval, pkg.cpv, pkg.root)) + + if pkg.type_name == "ebuild": + + build = EbuildBuild(args_set=args_set, + background=self.background, + config_pool=self.config_pool, + find_blockers=find_blockers, + ldpath_mtimes=ldpath_mtimes, logger=logger, + opts=build_opts, pkg=pkg, pkg_count=pkg_count, + prefetcher=self.prefetcher, scheduler=scheduler, + settings=settings, world_atom=world_atom) + + self._install_task = build + self._start_task(build, self._default_final_exit) + return + + elif pkg.type_name == "binary": + + binpkg = Binpkg(background=self.background, + find_blockers=find_blockers, + ldpath_mtimes=ldpath_mtimes, logger=logger, + opts=self.binpkg_opts, pkg=pkg, pkg_count=pkg_count, + prefetcher=self.prefetcher, settings=settings, + scheduler=scheduler, world_atom=world_atom) + + self._install_task = binpkg + self._start_task(binpkg, self._default_final_exit) + return + + def _poll(self): + self._install_task.poll() + return self.returncode + + def _wait(self): + self._install_task.wait() + return self.returncode + + def merge(self): + + pkg = self.pkg + build_opts = self.build_opts + find_blockers = self.find_blockers + logger = self.logger + mtimedb = self.mtimedb + pkg_count = self.pkg_count + prefetcher = self.prefetcher + scheduler = self.scheduler + settings = self.settings + world_atom = self.world_atom + ldpath_mtimes = mtimedb["ldpath"] + + if pkg.installed: + if not (build_opts.buildpkgonly or \ + build_opts.fetchonly or build_opts.pretend): + + uninstall = PackageUninstall(background=self.background, + ldpath_mtimes=ldpath_mtimes, opts=self.emerge_opts, + pkg=pkg, scheduler=scheduler, settings=settings) + + uninstall.start() + retval = uninstall.wait() + if retval != os.EX_OK: + return retval + return os.EX_OK + + if build_opts.fetchonly or \ + build_opts.buildpkgonly: + return self.returncode + + retval = self._install_task.install() + return retval + diff --git a/pym/_emerge/MetadataRegen.py b/pym/_emerge/MetadataRegen.py new file mode 100644 index 000000000..7b06650d0 --- /dev/null +++ b/pym/_emerge/MetadataRegen.py @@ -0,0 +1,170 @@ +import os + +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 + +from _emerge.EbuildMetadataPhase import EbuildMetadataPhase +from _emerge.PollScheduler import PollScheduler + +class MetadataRegen(PollScheduler): + + def __init__(self, portdb, cp_iter=None, consumer=None, + max_jobs=None, max_load=None): + PollScheduler.__init__(self) + self._portdb = portdb + self._global_cleanse = False + if cp_iter is None: + cp_iter = self._iter_every_cp() + # We can globally cleanse stale cache only if we + # iterate over every single cp. + self._global_cleanse = True + self._cp_iter = cp_iter + self._consumer = consumer + + if max_jobs is None: + max_jobs = 1 + + self._max_jobs = max_jobs + self._max_load = max_load + self._sched_iface = self._sched_iface_class( + register=self._register, + schedule=self._schedule_wait, + unregister=self._unregister) + + self._valid_pkgs = set() + self._cp_set = set() + self._process_iter = self._iter_metadata_processes() + self.returncode = os.EX_OK + self._error_count = 0 + + def _iter_every_cp(self): + every_cp = self._portdb.cp_all() + every_cp.sort(reverse=True) + try: + while True: + yield every_cp.pop() + except IndexError: + pass + + def _iter_metadata_processes(self): + portdb = self._portdb + valid_pkgs = self._valid_pkgs + cp_set = self._cp_set + consumer = self._consumer + + for cp in self._cp_iter: + cp_set.add(cp) + portage.writemsg_stdout("Processing %s\n" % cp) + cpv_list = portdb.cp_list(cp) + for cpv in cpv_list: + valid_pkgs.add(cpv) + ebuild_path, repo_path = portdb.findname2(cpv) + metadata, st, emtime = portdb._pull_valid_cache( + cpv, ebuild_path, repo_path) + if metadata is not None: + if consumer is not None: + consumer(cpv, ebuild_path, + repo_path, metadata) + continue + + yield EbuildMetadataPhase(cpv=cpv, ebuild_path=ebuild_path, + ebuild_mtime=emtime, + metadata_callback=portdb._metadata_callback, + portdb=portdb, repo_path=repo_path, + settings=portdb.doebuild_settings) + + def run(self): + + portdb = self._portdb + from portage.cache.cache_errors import CacheError + dead_nodes = {} + + while self._schedule(): + self._poll_loop() + + while self._jobs: + self._poll_loop() + + if self._global_cleanse: + for mytree in portdb.porttrees: + try: + dead_nodes[mytree] = set(portdb.auxdb[mytree].iterkeys()) + except CacheError, e: + portage.writemsg("Error listing cache entries for " + \ + "'%s': %s, continuing...\n" % (mytree, e), + noiselevel=-1) + del e + dead_nodes = None + break + else: + cp_set = self._cp_set + cpv_getkey = portage.cpv_getkey + for mytree in portdb.porttrees: + try: + dead_nodes[mytree] = set(cpv for cpv in \ + portdb.auxdb[mytree].iterkeys() \ + if cpv_getkey(cpv) in cp_set) + except CacheError, e: + portage.writemsg("Error listing cache entries for " + \ + "'%s': %s, continuing...\n" % (mytree, e), + noiselevel=-1) + del e + dead_nodes = None + break + + if dead_nodes: + for y in self._valid_pkgs: + for mytree in portdb.porttrees: + if portdb.findname2(y, mytree=mytree)[0]: + dead_nodes[mytree].discard(y) + + for mytree, nodes in dead_nodes.iteritems(): + auxdb = portdb.auxdb[mytree] + for y in nodes: + try: + del auxdb[y] + except (KeyError, CacheError): + pass + + def _schedule_tasks(self): + """ + @rtype: bool + @returns: True if there may be remaining tasks to schedule, + False otherwise. + """ + while self._can_add_job(): + try: + metadata_process = self._process_iter.next() + except StopIteration: + return False + + self._jobs += 1 + metadata_process.scheduler = self._sched_iface + metadata_process.addExitListener(self._metadata_exit) + metadata_process.start() + return True + + def _metadata_exit(self, metadata_process): + self._jobs -= 1 + if metadata_process.returncode != os.EX_OK: + self.returncode = 1 + self._error_count += 1 + self._valid_pkgs.discard(metadata_process.cpv) + portage.writemsg("Error processing %s, continuing...\n" % \ + (metadata_process.cpv,), noiselevel=-1) + + if self._consumer is not None: + # On failure, still notify the consumer (in this case the metadata + # argument is None). + self._consumer(metadata_process.cpv, + metadata_process.ebuild_path, + metadata_process.repo_path, + metadata_process.metadata) + + self._schedule() + diff --git a/pym/_emerge/Package.py b/pym/_emerge/Package.py new file mode 100644 index 000000000..4a40bf950 --- /dev/null +++ b/pym/_emerge/Package.py @@ -0,0 +1,185 @@ +import re +from itertools import chain + +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 + +from portage.cache.mappings import slot_dict_class + +from _emerge.Task import Task + +class Package(Task): + + __hash__ = Task.__hash__ + __slots__ = ("built", "cpv", "depth", + "installed", "metadata", "onlydeps", "operation", + "root_config", "type_name", + "category", "counter", "cp", "cpv_split", + "inherited", "iuse", "mtime", + "pf", "pv_split", "root", "slot", "slot_atom", "use") + + metadata_keys = [ + "CHOST", "COUNTER", "DEPEND", "EAPI", + "INHERITED", "IUSE", "KEYWORDS", + "LICENSE", "PDEPEND", "PROVIDE", "RDEPEND", + "repository", "PROPERTIES", "RESTRICT", "SLOT", "USE", "_mtime_"] + + def __init__(self, **kwargs): + Task.__init__(self, **kwargs) + self.root = self.root_config.root + self.metadata = _PackageMetadataWrapper(self, self.metadata) + self.cp = portage.cpv_getkey(self.cpv) + slot = self.slot + if not slot: + # Avoid an InvalidAtom exception when creating slot_atom. + # This package instance will be masked due to empty SLOT. + slot = '0' + self.slot_atom = portage.dep.Atom("%s:%s" % (self.cp, slot)) + self.category, self.pf = portage.catsplit(self.cpv) + self.cpv_split = portage.catpkgsplit(self.cpv) + self.pv_split = self.cpv_split[1:] + + class _use(object): + + __slots__ = ("__weakref__", "enabled") + + def __init__(self, use): + self.enabled = frozenset(use) + + class _iuse(object): + + __slots__ = ("__weakref__", "all", "enabled", "disabled", "iuse_implicit", "regex", "tokens") + + def __init__(self, tokens, iuse_implicit): + self.tokens = tuple(tokens) + self.iuse_implicit = iuse_implicit + enabled = [] + disabled = [] + other = [] + for x in tokens: + prefix = x[:1] + if prefix == "+": + enabled.append(x[1:]) + elif prefix == "-": + disabled.append(x[1:]) + else: + other.append(x) + self.enabled = frozenset(enabled) + self.disabled = frozenset(disabled) + self.all = frozenset(chain(enabled, disabled, other)) + + def __getattribute__(self, name): + if name == "regex": + try: + return object.__getattribute__(self, "regex") + except AttributeError: + all = object.__getattribute__(self, "all") + iuse_implicit = object.__getattribute__(self, "iuse_implicit") + # Escape anything except ".*" which is supposed + # to pass through from _get_implicit_iuse() + regex = (re.escape(x) for x in chain(all, iuse_implicit)) + regex = "^(%s)$" % "|".join(regex) + regex = regex.replace("\\.\\*", ".*") + self.regex = re.compile(regex) + return object.__getattribute__(self, name) + + def _get_hash_key(self): + hash_key = getattr(self, "_hash_key", None) + if hash_key is None: + if self.operation is None: + self.operation = "merge" + if self.onlydeps or self.installed: + self.operation = "nomerge" + self._hash_key = \ + (self.type_name, self.root, self.cpv, self.operation) + return self._hash_key + + def __lt__(self, other): + if other.cp != self.cp: + return False + if portage.pkgcmp(self.pv_split, other.pv_split) < 0: + return True + return False + + def __le__(self, other): + if other.cp != self.cp: + return False + if portage.pkgcmp(self.pv_split, other.pv_split) <= 0: + return True + return False + + def __gt__(self, other): + if other.cp != self.cp: + return False + if portage.pkgcmp(self.pv_split, other.pv_split) > 0: + return True + return False + + def __ge__(self, other): + if other.cp != self.cp: + return False + if portage.pkgcmp(self.pv_split, other.pv_split) >= 0: + return True + return False + +_all_metadata_keys = set(x for x in portage.auxdbkeys \ + if not x.startswith("UNUSED_")) +_all_metadata_keys.discard("CDEPEND") +_all_metadata_keys.update(Package.metadata_keys) + +_PackageMetadataWrapperBase = slot_dict_class(_all_metadata_keys) + +class _PackageMetadataWrapper(_PackageMetadataWrapperBase): + """ + Detect metadata updates and synchronize Package attributes. + """ + + __slots__ = ("_pkg",) + _wrapped_keys = frozenset( + ["COUNTER", "INHERITED", "IUSE", "SLOT", "USE", "_mtime_"]) + + def __init__(self, pkg, metadata): + _PackageMetadataWrapperBase.__init__(self) + self._pkg = pkg + self.update(metadata) + + def __setitem__(self, k, v): + _PackageMetadataWrapperBase.__setitem__(self, k, v) + if k in self._wrapped_keys: + getattr(self, "_set_" + k.lower())(k, v) + + def _set_inherited(self, k, v): + if isinstance(v, basestring): + v = frozenset(v.split()) + self._pkg.inherited = v + + def _set_iuse(self, k, v): + self._pkg.iuse = self._pkg._iuse( + v.split(), self._pkg.root_config.iuse_implicit) + + def _set_slot(self, k, v): + self._pkg.slot = v + + def _set_use(self, k, v): + self._pkg.use = self._pkg._use(v.split()) + + def _set_counter(self, k, v): + if isinstance(v, basestring): + try: + v = long(v.strip()) + except ValueError: + v = 0 + self._pkg.counter = v + + def _set__mtime_(self, k, v): + if isinstance(v, basestring): + try: + v = long(v.strip()) + except ValueError: + v = 0 + self._pkg.mtime = v diff --git a/pym/_emerge/PackageCounters.py b/pym/_emerge/PackageCounters.py new file mode 100644 index 000000000..91757f004 --- /dev/null +++ b/pym/_emerge/PackageCounters.py @@ -0,0 +1,77 @@ +from portage.output import colorize, create_color_func +bad = create_color_func("BAD") + +from _emerge.format_size import format_size + +class PackageCounters(object): + + def __init__(self): + self.upgrades = 0 + self.downgrades = 0 + self.new = 0 + self.newslot = 0 + self.reinst = 0 + self.uninst = 0 + self.blocks = 0 + self.blocks_satisfied = 0 + self.totalsize = 0 + self.restrict_fetch = 0 + self.restrict_fetch_satisfied = 0 + self.interactive = 0 + + def __str__(self): + total_installs = self.upgrades + self.downgrades + self.newslot + self.new + self.reinst + myoutput = [] + details = [] + myoutput.append("Total: %s package" % total_installs) + if total_installs != 1: + myoutput.append("s") + if total_installs != 0: + myoutput.append(" (") + if self.upgrades > 0: + details.append("%s upgrade" % self.upgrades) + if self.upgrades > 1: + details[-1] += "s" + if self.downgrades > 0: + details.append("%s downgrade" % self.downgrades) + if self.downgrades > 1: + details[-1] += "s" + if self.new > 0: + details.append("%s new" % self.new) + if self.newslot > 0: + details.append("%s in new slot" % self.newslot) + if self.newslot > 1: + details[-1] += "s" + if self.reinst > 0: + details.append("%s reinstall" % self.reinst) + if self.reinst > 1: + details[-1] += "s" + if self.uninst > 0: + details.append("%s uninstall" % self.uninst) + if self.uninst > 1: + details[-1] += "s" + if self.interactive > 0: + details.append("%s %s" % (self.interactive, + colorize("WARN", "interactive"))) + myoutput.append(", ".join(details)) + if total_installs != 0: + myoutput.append(")") + myoutput.append(", Size of downloads: %s" % format_size(self.totalsize)) + if self.restrict_fetch: + myoutput.append("\nFetch Restriction: %s package" % \ + self.restrict_fetch) + if self.restrict_fetch > 1: + myoutput.append("s") + if self.restrict_fetch_satisfied < self.restrict_fetch: + myoutput.append(bad(" (%s unsatisfied)") % \ + (self.restrict_fetch - self.restrict_fetch_satisfied)) + if self.blocks > 0: + myoutput.append("\nConflict: %s block" % \ + self.blocks) + if self.blocks > 1: + myoutput.append("s") + if self.blocks_satisfied < self.blocks: + myoutput.append(bad(" (%s unsatisfied)") % \ + (self.blocks - self.blocks_satisfied)) + return "".join(myoutput) + diff --git a/pym/_emerge/PackageUninstall.py b/pym/_emerge/PackageUninstall.py new file mode 100644 index 000000000..148dba0e7 --- /dev/null +++ b/pym/_emerge/PackageUninstall.py @@ -0,0 +1,51 @@ +import logging +import os + +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 + +from _emerge.AsynchronousTask import AsynchronousTask +from _emerge.unmerge import unmerge +from _emerge.UninstallFailure import UninstallFailure + +class PackageUninstall(AsynchronousTask): + + __slots__ = ("ldpath_mtimes", "opts", "pkg", "scheduler", "settings") + + def _start(self): + try: + unmerge(self.pkg.root_config, self.opts, "unmerge", + [self.pkg.cpv], self.ldpath_mtimes, clean_world=0, + clean_delay=0, raise_on_error=1, scheduler=self.scheduler, + writemsg_level=self._writemsg_level) + except UninstallFailure, e: + self.returncode = e.status + else: + self.returncode = os.EX_OK + self.wait() + + def _writemsg_level(self, msg, level=0, noiselevel=0): + + log_path = self.settings.get("PORTAGE_LOG_FILE") + background = self.background + + if log_path is None: + if not (background and level < logging.WARNING): + portage.util.writemsg_level(msg, + level=level, noiselevel=noiselevel) + else: + if not background: + portage.util.writemsg_level(msg, + level=level, noiselevel=noiselevel) + + f = open(log_path, 'a') + try: + f.write(msg) + finally: + f.close() + diff --git a/pym/_emerge/RootConfig.py b/pym/_emerge/RootConfig.py new file mode 100644 index 000000000..8ed11c9f2 --- /dev/null +++ b/pym/_emerge/RootConfig.py @@ -0,0 +1,28 @@ +from _emerge.PackageVirtualDbapi import PackageVirtualDbapi + +class RootConfig(object): + """This is used internally by depgraph to track information about a + particular $ROOT.""" + + pkg_tree_map = { + "ebuild" : "porttree", + "binary" : "bintree", + "installed" : "vartree" + } + + tree_pkg_map = {} + for k, v in pkg_tree_map.iteritems(): + tree_pkg_map[v] = k + + def __init__(self, settings, trees, setconfig): + self.trees = trees + self.settings = settings + self.iuse_implicit = tuple(sorted(settings._get_implicit_iuse())) + self.root = self.settings["ROOT"] + self.setconfig = setconfig + if setconfig is None: + self.sets = {} + else: + self.sets = self.setconfig.getSets() + self.visible_pkgs = PackageVirtualDbapi(self.settings) + diff --git a/pym/_emerge/__init__.py b/pym/_emerge/__init__.py index de5642da9..f0b994a4c 100644 --- a/pym/_emerge/__init__.py +++ b/pym/_emerge/__init__.py @@ -23,17 +23,16 @@ 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, xtermTitle, \ + nc_len, red, teal, turquoise, \ xtermTitleReset, yellow from portage.output import create_color_func good = create_color_func("GOOD") bad = create_color_func("BAD") -# white looks bad on terminals with white background -from portage.output import bold as white import portage.elog import portage.dep @@ -57,15 +56,11 @@ from _emerge.BlockerDepPriority import BlockerDepPriority from _emerge.UnmergeDepPriority import UnmergeDepPriority from _emerge.DepPriorityNormalRange import DepPriorityNormalRange from _emerge.DepPrioritySatisfiedRange import DepPrioritySatisfiedRange -from _emerge.Task import Task +from _emerge.Package import Package from _emerge.Blocker import Blocker -from _emerge.AsynchronousTask import AsynchronousTask -from _emerge.CompositeTask import CompositeTask +from _emerge.BlockerDB import BlockerDB from _emerge.EbuildFetcher import EbuildFetcher -from _emerge.EbuildBuild import EbuildBuild -from _emerge.EbuildMetadataPhase import EbuildMetadataPhase from _emerge.EbuildPhase import EbuildPhase -from _emerge.Binpkg import Binpkg from _emerge.BinpkgPrefetcher import BinpkgPrefetcher from _emerge.PackageMerge import PackageMerge from _emerge.DependencyArg import DependencyArg @@ -80,49 +75,21 @@ from _emerge.UseFlagDisplay import UseFlagDisplay from _emerge.SequentialTaskQueue import SequentialTaskQueue from _emerge.ProgressHandler import ProgressHandler from _emerge.stdout_spinner import stdout_spinner -from _emerge.UninstallFailure import UninstallFailure from _emerge.JobStatusDisplay import JobStatusDisplay from _emerge.PollScheduler import PollScheduler - -def userquery(prompt, responses=None, colours=None): - """Displays a prompt and a set of responses, then waits for a response - which is checked against the responses and the first to match is - returned. An empty response will match the first value in responses. The - input buffer is *not* cleared prior to the prompt! - - prompt: a String. - responses: a List of Strings. - colours: a List of Functions taking and returning a String, used to - process the responses for display. Typically these will be functions - like red() but could be e.g. lambda x: "DisplayString". - If responses is omitted, defaults to ["Yes", "No"], [green, red]. - If only colours is omitted, defaults to [bold, ...]. - - Returns a member of the List responses. (If called without optional - arguments, returns "Yes" or "No".) - KeyboardInterrupt is converted to SystemExit to avoid tracebacks being - printed.""" - if responses is None: - responses = ["Yes", "No"] - colours = [ - create_color_func("PROMPT_CHOICE_DEFAULT"), - create_color_func("PROMPT_CHOICE_OTHER") - ] - elif colours is None: - colours=[bold] - colours=(colours*len(responses))[:len(responses)] - print bold(prompt), - try: - while True: - response=raw_input("["+"/".join([colours[i](responses[i]) for i in range(len(responses))])+"] ") - for key in responses: - # An empty response will match the first value in responses. - if response.upper()==key[:len(response)].upper(): - return key - print "Sorry, response '%s' not understood." % response, - except (EOFError, KeyboardInterrupt): - print "Interrupted." - sys.exit(1) +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.show_invalid_depstring_notice import show_invalid_depstring_notice actions = frozenset([ "clean", "config", "depclean", @@ -178,63 +145,6 @@ shortmapping={ "v":"--verbose", "V":"--version" } -_emerge_log_dir = '/var/log' - -def emergelog(xterm_titles, mystr, short_msg=None): - if xterm_titles and short_msg: - if "HOSTNAME" in os.environ: - short_msg = os.environ["HOSTNAME"]+": "+short_msg - xtermTitle(short_msg) - try: - file_path = os.path.join(_emerge_log_dir, 'emerge.log') - mylogfile = open(file_path, "a") - portage.util.apply_secpass_permissions(file_path, - uid=portage.portage_uid, gid=portage.portage_gid, - mode=0660) - mylock = None - try: - mylock = portage.locks.lockfile(mylogfile) - # seek because we may have gotten held up by the lock. - # if so, we may not be positioned at the end of the file. - mylogfile.seek(0, 2) - mylogfile.write(str(time.time())[:10]+": "+mystr+"\n") - mylogfile.flush() - finally: - if mylock: - portage.locks.unlockfile(mylock) - mylogfile.close() - except (IOError,OSError,portage.exception.PortageException), e: - if secpass >= 1: - print >> sys.stderr, "emergelog():",e - -def countdown(secs=5, doing="Starting"): - if secs: - print ">>> Waiting",secs,"seconds before starting..." - print ">>> (Control-C to abort)...\n"+doing+" in: ", - ticks=range(secs) - ticks.reverse() - for sec in ticks: - sys.stdout.write(colorize("UNMERGE_WARN", str(sec+1)+" ")) - sys.stdout.flush() - time.sleep(1) - print - -# formats a size given in bytes nicely -def format_size(mysize): - if isinstance(mysize, basestring): - return mysize - if 0 != mysize % 1024: - # Always round up to the next kB so that it doesn't show 0 kB when - # some small file still needs to be fetched. - mysize += 1024 - mysize % 1024 - mystr=str(mysize/1024) - mycount=len(mystr) - while (mycount > 3): - mycount-=3 - mystr=mystr[:mycount]+","+mystr[mycount:] - return mystr+" kB" - - def getgccversion(chost): """ rtype: C{str} @@ -331,394 +241,6 @@ def create_depgraph_params(myopts, myaction): myparams.add("complete") return myparams -# search functionality -class search(object): - - # - # class constants - # - VERSION_SHORT=1 - VERSION_RELEASE=2 - - # - # public interface - # - def __init__(self, root_config, spinner, searchdesc, - verbose, usepkg, usepkgonly): - """Searches the available and installed packages for the supplied search key. - The list of available and installed packages is created at object instantiation. - This makes successive searches faster.""" - self.settings = root_config.settings - self.vartree = root_config.trees["vartree"] - self.spinner = spinner - self.verbose = verbose - self.searchdesc = searchdesc - self.root_config = root_config - self.setconfig = root_config.setconfig - self.matches = {"pkg" : []} - self.mlen = 0 - - def fake_portdb(): - pass - self.portdb = fake_portdb - for attrib in ("aux_get", "cp_all", - "xmatch", "findname", "getFetchMap"): - setattr(fake_portdb, attrib, getattr(self, "_"+attrib)) - - self._dbs = [] - - portdb = root_config.trees["porttree"].dbapi - bindb = root_config.trees["bintree"].dbapi - vardb = root_config.trees["vartree"].dbapi - - if not usepkgonly and portdb._have_root_eclass_dir: - self._dbs.append(portdb) - - if (usepkg or usepkgonly) and bindb.cp_all(): - self._dbs.append(bindb) - - self._dbs.append(vardb) - self._portdb = portdb - - def _cp_all(self): - cp_all = set() - for db in self._dbs: - cp_all.update(db.cp_all()) - return list(sorted(cp_all)) - - def _aux_get(self, *args, **kwargs): - for db in self._dbs: - try: - return db.aux_get(*args, **kwargs) - except KeyError: - pass - raise - - def _findname(self, *args, **kwargs): - for db in self._dbs: - if db is not self._portdb: - # We don't want findname to return anything - # unless it's an ebuild in a portage tree. - # Otherwise, it's already built and we don't - # care about it. - continue - func = getattr(db, "findname", None) - if func: - value = func(*args, **kwargs) - if value: - return value - return None - - def _getFetchMap(self, *args, **kwargs): - for db in self._dbs: - func = getattr(db, "getFetchMap", None) - if func: - value = func(*args, **kwargs) - if value: - return value - return {} - - def _visible(self, db, cpv, metadata): - installed = db is self.vartree.dbapi - built = installed or db is not self._portdb - pkg_type = "ebuild" - if installed: - pkg_type = "installed" - elif built: - pkg_type = "binary" - return visible(self.settings, - Package(type_name=pkg_type, root_config=self.root_config, - cpv=cpv, built=built, installed=installed, metadata=metadata)) - - def _xmatch(self, level, atom): - """ - This method does not expand old-style virtuals because it - is restricted to returning matches for a single ${CATEGORY}/${PN} - and old-style virual matches unreliable for that when querying - multiple package databases. If necessary, old-style virtuals - can be performed on atoms prior to calling this method. - """ - cp = portage.dep_getkey(atom) - if level == "match-all": - matches = set() - for db in self._dbs: - if hasattr(db, "xmatch"): - matches.update(db.xmatch(level, atom)) - else: - matches.update(db.match(atom)) - result = list(x for x in matches if portage.cpv_getkey(x) == cp) - db._cpv_sort_ascending(result) - elif level == "match-visible": - matches = set() - for db in self._dbs: - if hasattr(db, "xmatch"): - matches.update(db.xmatch(level, atom)) - else: - db_keys = list(db._aux_cache_keys) - for cpv in db.match(atom): - metadata = izip(db_keys, - db.aux_get(cpv, db_keys)) - if not self._visible(db, cpv, metadata): - continue - matches.add(cpv) - result = list(x for x in matches if portage.cpv_getkey(x) == cp) - db._cpv_sort_ascending(result) - elif level == "bestmatch-visible": - result = None - for db in self._dbs: - if hasattr(db, "xmatch"): - cpv = db.xmatch("bestmatch-visible", atom) - if not cpv or portage.cpv_getkey(cpv) != cp: - continue - if not result or cpv == portage.best([cpv, result]): - result = cpv - else: - db_keys = Package.metadata_keys - # break out of this loop with highest visible - # match, checked in descending order - for cpv in reversed(db.match(atom)): - if portage.cpv_getkey(cpv) != cp: - continue - metadata = izip(db_keys, - db.aux_get(cpv, db_keys)) - if not self._visible(db, cpv, metadata): - continue - if not result or cpv == portage.best([cpv, result]): - result = cpv - break - else: - raise NotImplementedError(level) - return result - - def execute(self,searchkey): - """Performs the search for the supplied search key""" - match_category = 0 - self.searchkey=searchkey - self.packagematches = [] - if self.searchdesc: - self.searchdesc=1 - self.matches = {"pkg":[], "desc":[], "set":[]} - else: - self.searchdesc=0 - self.matches = {"pkg":[], "set":[]} - print "Searching... ", - - regexsearch = False - if self.searchkey.startswith('%'): - regexsearch = True - self.searchkey = self.searchkey[1:] - if self.searchkey.startswith('@'): - match_category = 1 - self.searchkey = self.searchkey[1:] - if regexsearch: - self.searchre=re.compile(self.searchkey,re.I) - else: - self.searchre=re.compile(re.escape(self.searchkey), re.I) - for package in self.portdb.cp_all(): - self.spinner.update() - - if match_category: - match_string = package[:] - else: - match_string = package.split("/")[-1] - - masked=0 - if self.searchre.search(match_string): - if not self.portdb.xmatch("match-visible", package): - masked=1 - self.matches["pkg"].append([package,masked]) - elif self.searchdesc: # DESCRIPTION searching - full_package = self.portdb.xmatch("bestmatch-visible", package) - if not full_package: - #no match found; we don't want to query description - full_package = portage.best( - self.portdb.xmatch("match-all", package)) - if not full_package: - continue - else: - masked=1 - try: - full_desc = self.portdb.aux_get( - full_package, ["DESCRIPTION"])[0] - except KeyError: - print "emerge: search: aux_get() failed, skipping" - continue - if self.searchre.search(full_desc): - self.matches["desc"].append([full_package,masked]) - - self.sdict = self.setconfig.getSets() - for setname in self.sdict: - self.spinner.update() - if match_category: - match_string = setname - else: - match_string = setname.split("/")[-1] - - if self.searchre.search(match_string): - self.matches["set"].append([setname, False]) - elif self.searchdesc: - if self.searchre.search( - self.sdict[setname].getMetadata("DESCRIPTION")): - self.matches["set"].append([setname, False]) - - self.mlen=0 - for mtype in self.matches: - self.matches[mtype].sort() - self.mlen += len(self.matches[mtype]) - - def addCP(self, cp): - if not self.portdb.xmatch("match-all", cp): - return - masked = 0 - if not self.portdb.xmatch("bestmatch-visible", cp): - masked = 1 - self.matches["pkg"].append([cp, masked]) - self.mlen += 1 - - def output(self): - """Outputs the results of the search.""" - print "\b\b \n[ Results for search key : "+white(self.searchkey)+" ]" - print "[ Applications found : "+white(str(self.mlen))+" ]" - print " " - vardb = self.vartree.dbapi - for mtype in self.matches: - for match,masked in self.matches[mtype]: - full_package = None - if mtype == "pkg": - catpack = match - full_package = self.portdb.xmatch( - "bestmatch-visible", match) - if not full_package: - #no match found; we don't want to query description - masked=1 - full_package = portage.best( - self.portdb.xmatch("match-all",match)) - elif mtype == "desc": - full_package = match - match = portage.cpv_getkey(match) - elif mtype == "set": - print green("*")+" "+white(match) - print " ", darkgreen("Description:")+" ", self.sdict[match].getMetadata("DESCRIPTION") - print - if full_package: - try: - desc, homepage, license = self.portdb.aux_get( - full_package, ["DESCRIPTION","HOMEPAGE","LICENSE"]) - except KeyError: - print "emerge: search: aux_get() failed, skipping" - continue - if masked: - print green("*")+" "+white(match)+" "+red("[ Masked ]") - else: - print green("*")+" "+white(match) - myversion = self.getVersion(full_package, search.VERSION_RELEASE) - - mysum = [0,0] - file_size_str = None - mycat = match.split("/")[0] - mypkg = match.split("/")[1] - mycpv = match + "-" + myversion - myebuild = self.portdb.findname(mycpv) - if myebuild: - pkgdir = os.path.dirname(myebuild) - from portage import manifest - mf = manifest.Manifest( - pkgdir, self.settings["DISTDIR"]) - try: - uri_map = self.portdb.getFetchMap(mycpv) - except portage.exception.InvalidDependString, e: - file_size_str = "Unknown (%s)" % (e,) - del e - else: - try: - mysum[0] = mf.getDistfilesSize(uri_map) - except KeyError, e: - file_size_str = "Unknown (missing " + \ - "digest for %s)" % (e,) - del e - - available = False - for db in self._dbs: - if db is not vardb and \ - db.cpv_exists(mycpv): - available = True - if not myebuild and hasattr(db, "bintree"): - myebuild = db.bintree.getname(mycpv) - try: - mysum[0] = os.stat(myebuild).st_size - except OSError: - myebuild = None - break - - if myebuild and file_size_str is None: - mystr = str(mysum[0] / 1024) - mycount = len(mystr) - while (mycount > 3): - mycount -= 3 - mystr = mystr[:mycount] + "," + mystr[mycount:] - file_size_str = mystr + " kB" - - if self.verbose: - if available: - print " ", darkgreen("Latest version available:"),myversion - print " ", self.getInstallationStatus(mycat+'/'+mypkg) - if myebuild: - print " %s %s" % \ - (darkgreen("Size of files:"), file_size_str) - print " ", darkgreen("Homepage:")+" ",homepage - print " ", darkgreen("Description:")+" ",desc - print " ", darkgreen("License:")+" ",license - print - # - # private interface - # - def getInstallationStatus(self,package): - installed_package = self.vartree.dep_bestmatch(package) - result = "" - version = self.getVersion(installed_package,search.VERSION_RELEASE) - if len(version) > 0: - result = darkgreen("Latest version installed:")+" "+version - else: - result = darkgreen("Latest version installed:")+" [ Not Installed ]" - return result - - def getVersion(self,full_package,detail): - if len(full_package) > 1: - package_parts = portage.catpkgsplit(full_package) - if detail == search.VERSION_RELEASE and package_parts[3] != 'r0': - result = package_parts[2]+ "-" + package_parts[3] - else: - result = package_parts[2] - else: - result = "" - return result - -class RootConfig(object): - """This is used internally by depgraph to track information about a - particular $ROOT.""" - - pkg_tree_map = { - "ebuild" : "porttree", - "binary" : "bintree", - "installed" : "vartree" - } - - tree_pkg_map = {} - for k, v in pkg_tree_map.iteritems(): - tree_pkg_map[v] = k - - def __init__(self, settings, trees, setconfig): - self.trees = trees - self.settings = settings - self.iuse_implicit = tuple(sorted(settings._get_implicit_iuse())) - self.root = self.settings["ROOT"] - self.setconfig = setconfig - if setconfig is None: - self.sets = {} - else: - self.sets = self.setconfig.getSets() - self.visible_pkgs = PackageVirtualDbapi(self.settings) - 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 @@ -841,260 +363,6 @@ def _find_deep_system_runtime_deps(graph): return deep_system_deps -class FakeVartree(portage.vartree): - """This is implements an in-memory copy of a vartree instance that provides - all the interfaces required for use by the depgraph. The vardb is locked - during the constructor call just long enough to read a copy of the - installed package information. This allows the depgraph to do it's - dependency calculations without holding a lock on the vardb. It also - allows things like vardb global updates to be done in memory so that the - user doesn't necessarily need write access to the vardb in cases where - global updates are necessary (updates are performed when necessary if there - is not a matching ebuild in the tree).""" - def __init__(self, root_config, pkg_cache=None, acquire_lock=1): - self._root_config = root_config - if pkg_cache is None: - pkg_cache = {} - real_vartree = root_config.trees["vartree"] - portdb = root_config.trees["porttree"].dbapi - self.root = real_vartree.root - self.settings = real_vartree.settings - mykeys = list(real_vartree.dbapi._aux_cache_keys) - if "_mtime_" not in mykeys: - mykeys.append("_mtime_") - self._db_keys = mykeys - self._pkg_cache = pkg_cache - self.dbapi = PackageVirtualDbapi(real_vartree.settings) - vdb_path = os.path.join(self.root, portage.VDB_PATH) - try: - # At least the parent needs to exist for the lock file. - portage.util.ensure_dirs(vdb_path) - except portage.exception.PortageException: - pass - vdb_lock = None - try: - if acquire_lock and os.access(vdb_path, os.W_OK): - vdb_lock = portage.locks.lockdir(vdb_path) - real_dbapi = real_vartree.dbapi - slot_counters = {} - for cpv in real_dbapi.cpv_all(): - cache_key = ("installed", self.root, cpv, "nomerge") - pkg = self._pkg_cache.get(cache_key) - if pkg is not None: - metadata = pkg.metadata - else: - metadata = dict(izip(mykeys, real_dbapi.aux_get(cpv, mykeys))) - myslot = metadata["SLOT"] - mycp = portage.dep_getkey(cpv) - myslot_atom = "%s:%s" % (mycp, myslot) - try: - mycounter = long(metadata["COUNTER"]) - except ValueError: - mycounter = 0 - metadata["COUNTER"] = str(mycounter) - other_counter = slot_counters.get(myslot_atom, None) - if other_counter is not None: - if other_counter > mycounter: - continue - slot_counters[myslot_atom] = mycounter - if pkg is None: - pkg = Package(built=True, cpv=cpv, - installed=True, metadata=metadata, - root_config=root_config, type_name="installed") - self._pkg_cache[pkg] = pkg - self.dbapi.cpv_inject(pkg) - real_dbapi.flush_cache() - finally: - if vdb_lock: - portage.locks.unlockdir(vdb_lock) - # Populate the old-style virtuals using the cached values. - if not self.settings.treeVirtuals: - self.settings.treeVirtuals = portage.util.map_dictlist_vals( - portage.getCPFromCPV, self.get_all_provides()) - - # Intialize variables needed for lazy cache pulls of the live ebuild - # metadata. This ensures that the vardb lock is released ASAP, without - # being delayed in case cache generation is triggered. - self._aux_get = self.dbapi.aux_get - self.dbapi.aux_get = self._aux_get_wrapper - self._match = self.dbapi.match - self.dbapi.match = self._match_wrapper - self._aux_get_history = set() - self._portdb_keys = ["EAPI", "DEPEND", "RDEPEND", "PDEPEND"] - self._portdb = portdb - self._global_updates = None - - def _match_wrapper(self, cpv, use_cache=1): - """ - Make sure the metadata in Package instances gets updated for any - cpv that is returned from a match() call, since the metadata can - be accessed directly from the Package instance instead of via - aux_get(). - """ - matches = self._match(cpv, use_cache=use_cache) - for cpv in matches: - if cpv in self._aux_get_history: - continue - self._aux_get_wrapper(cpv, []) - return matches - - def _aux_get_wrapper(self, pkg, wants): - if pkg in self._aux_get_history: - return self._aux_get(pkg, wants) - self._aux_get_history.add(pkg) - try: - # Use the live ebuild metadata if possible. - live_metadata = dict(izip(self._portdb_keys, - self._portdb.aux_get(pkg, self._portdb_keys))) - if not portage.eapi_is_supported(live_metadata["EAPI"]): - raise KeyError(pkg) - self.dbapi.aux_update(pkg, live_metadata) - except (KeyError, portage.exception.PortageException): - if self._global_updates is None: - self._global_updates = \ - grab_global_updates(self._portdb.porttree_root) - perform_global_updates( - pkg, self.dbapi, self._global_updates) - return self._aux_get(pkg, wants) - - def sync(self, acquire_lock=1): - """ - Call this method to synchronize state with the real vardb - after one or more packages may have been installed or - uninstalled. - """ - vdb_path = os.path.join(self.root, portage.VDB_PATH) - try: - # At least the parent needs to exist for the lock file. - portage.util.ensure_dirs(vdb_path) - except portage.exception.PortageException: - pass - vdb_lock = None - try: - if acquire_lock and os.access(vdb_path, os.W_OK): - vdb_lock = portage.locks.lockdir(vdb_path) - self._sync() - finally: - if vdb_lock: - portage.locks.unlockdir(vdb_lock) - - def _sync(self): - - real_vardb = self._root_config.trees["vartree"].dbapi - current_cpv_set = frozenset(real_vardb.cpv_all()) - pkg_vardb = self.dbapi - aux_get_history = self._aux_get_history - - # Remove any packages that have been uninstalled. - for pkg in list(pkg_vardb): - if pkg.cpv not in current_cpv_set: - pkg_vardb.cpv_remove(pkg) - aux_get_history.discard(pkg.cpv) - - # Validate counters and timestamps. - slot_counters = {} - root = self.root - validation_keys = ["COUNTER", "_mtime_"] - for cpv in current_cpv_set: - - pkg_hash_key = ("installed", root, cpv, "nomerge") - pkg = pkg_vardb.get(pkg_hash_key) - if pkg is not None: - counter, mtime = real_vardb.aux_get(cpv, validation_keys) - try: - counter = long(counter) - except ValueError: - counter = 0 - - if counter != pkg.counter or \ - mtime != pkg.mtime: - pkg_vardb.cpv_remove(pkg) - aux_get_history.discard(pkg.cpv) - pkg = None - - if pkg is None: - pkg = self._pkg(cpv) - - other_counter = slot_counters.get(pkg.slot_atom) - if other_counter is not None: - if other_counter > pkg.counter: - continue - - slot_counters[pkg.slot_atom] = pkg.counter - pkg_vardb.cpv_inject(pkg) - - real_vardb.flush_cache() - - def _pkg(self, cpv): - root_config = self._root_config - real_vardb = root_config.trees["vartree"].dbapi - pkg = Package(cpv=cpv, installed=True, - metadata=izip(self._db_keys, - real_vardb.aux_get(cpv, self._db_keys)), - root_config=root_config, - type_name="installed") - - try: - mycounter = long(pkg.metadata["COUNTER"]) - except ValueError: - mycounter = 0 - pkg.metadata["COUNTER"] = str(mycounter) - - return pkg - -def grab_global_updates(portdir): - from portage.update import grab_updates, parse_updates - updpath = os.path.join(portdir, "profiles", "updates") - try: - rawupdates = grab_updates(updpath) - except portage.exception.DirectoryNotFound: - rawupdates = [] - upd_commands = [] - for mykey, mystat, mycontent in rawupdates: - commands, errors = parse_updates(mycontent) - upd_commands.extend(commands) - return upd_commands - -def perform_global_updates(mycpv, mydb, mycommands): - from portage.update import update_dbentries - aux_keys = ["DEPEND", "RDEPEND", "PDEPEND"] - aux_dict = dict(izip(aux_keys, mydb.aux_get(mycpv, aux_keys))) - updates = update_dbentries(mycommands, aux_dict) - if updates: - mydb.aux_update(mycpv, updates) - -def visible(pkgsettings, pkg): - """ - Check if a package is visible. This can raise an InvalidDependString - exception if LICENSE is invalid. - TODO: optionally generate a list of masking reasons - @rtype: Boolean - @returns: True if the package is visible, False otherwise. - """ - if not pkg.metadata["SLOT"]: - return False - if not pkg.installed: - if not pkgsettings._accept_chost(pkg.cpv, pkg.metadata): - return False - eapi = pkg.metadata["EAPI"] - if not portage.eapi_is_supported(eapi): - return False - if not pkg.installed: - if portage._eapi_is_deprecated(eapi): - return False - if pkgsettings._getMissingKeywords(pkg.cpv, pkg.metadata): - return False - if pkgsettings._getMaskAtom(pkg.cpv, pkg.metadata): - return False - if pkgsettings._getProfileMaskAtom(pkg.cpv, pkg.metadata): - return False - try: - if pkgsettings._getMissingLicenses(pkg.cpv, pkg.metadata): - return False - except portage.exception.InvalidDependString: - return False - return True - def get_masking_status(pkg, pkgsettings, root_config): mreasons = portage.getmaskingstatus( @@ -1187,486 +455,6 @@ def show_masked_packages(masked_packages): shown_licenses.add(l) return have_eapi_mask -class Package(Task): - - __hash__ = Task.__hash__ - __slots__ = ("built", "cpv", "depth", - "installed", "metadata", "onlydeps", "operation", - "root_config", "type_name", - "category", "counter", "cp", "cpv_split", - "inherited", "iuse", "mtime", - "pf", "pv_split", "root", "slot", "slot_atom", "use") - - metadata_keys = [ - "CHOST", "COUNTER", "DEPEND", "EAPI", - "INHERITED", "IUSE", "KEYWORDS", - "LICENSE", "PDEPEND", "PROVIDE", "RDEPEND", - "repository", "PROPERTIES", "RESTRICT", "SLOT", "USE", "_mtime_"] - - def __init__(self, **kwargs): - Task.__init__(self, **kwargs) - self.root = self.root_config.root - self.metadata = _PackageMetadataWrapper(self, self.metadata) - self.cp = portage.cpv_getkey(self.cpv) - slot = self.slot - if not slot: - # Avoid an InvalidAtom exception when creating slot_atom. - # This package instance will be masked due to empty SLOT. - slot = '0' - self.slot_atom = portage.dep.Atom("%s:%s" % (self.cp, slot)) - self.category, self.pf = portage.catsplit(self.cpv) - self.cpv_split = portage.catpkgsplit(self.cpv) - self.pv_split = self.cpv_split[1:] - - class _use(object): - - __slots__ = ("__weakref__", "enabled") - - def __init__(self, use): - self.enabled = frozenset(use) - - class _iuse(object): - - __slots__ = ("__weakref__", "all", "enabled", "disabled", "iuse_implicit", "regex", "tokens") - - def __init__(self, tokens, iuse_implicit): - self.tokens = tuple(tokens) - self.iuse_implicit = iuse_implicit - enabled = [] - disabled = [] - other = [] - for x in tokens: - prefix = x[:1] - if prefix == "+": - enabled.append(x[1:]) - elif prefix == "-": - disabled.append(x[1:]) - else: - other.append(x) - self.enabled = frozenset(enabled) - self.disabled = frozenset(disabled) - self.all = frozenset(chain(enabled, disabled, other)) - - def __getattribute__(self, name): - if name == "regex": - try: - return object.__getattribute__(self, "regex") - except AttributeError: - all = object.__getattribute__(self, "all") - iuse_implicit = object.__getattribute__(self, "iuse_implicit") - # Escape anything except ".*" which is supposed - # to pass through from _get_implicit_iuse() - regex = (re.escape(x) for x in chain(all, iuse_implicit)) - regex = "^(%s)$" % "|".join(regex) - regex = regex.replace("\\.\\*", ".*") - self.regex = re.compile(regex) - return object.__getattribute__(self, name) - - def _get_hash_key(self): - hash_key = getattr(self, "_hash_key", None) - if hash_key is None: - if self.operation is None: - self.operation = "merge" - if self.onlydeps or self.installed: - self.operation = "nomerge" - self._hash_key = \ - (self.type_name, self.root, self.cpv, self.operation) - return self._hash_key - - def __lt__(self, other): - if other.cp != self.cp: - return False - if portage.pkgcmp(self.pv_split, other.pv_split) < 0: - return True - return False - - def __le__(self, other): - if other.cp != self.cp: - return False - if portage.pkgcmp(self.pv_split, other.pv_split) <= 0: - return True - return False - - def __gt__(self, other): - if other.cp != self.cp: - return False - if portage.pkgcmp(self.pv_split, other.pv_split) > 0: - return True - return False - - def __ge__(self, other): - if other.cp != self.cp: - return False - if portage.pkgcmp(self.pv_split, other.pv_split) >= 0: - return True - return False - -_all_metadata_keys = set(x for x in portage.auxdbkeys \ - if not x.startswith("UNUSED_")) -_all_metadata_keys.discard("CDEPEND") -_all_metadata_keys.update(Package.metadata_keys) - -from portage.cache.mappings import slot_dict_class -_PackageMetadataWrapperBase = slot_dict_class(_all_metadata_keys) - -class _PackageMetadataWrapper(_PackageMetadataWrapperBase): - """ - Detect metadata updates and synchronize Package attributes. - """ - - __slots__ = ("_pkg",) - _wrapped_keys = frozenset( - ["COUNTER", "INHERITED", "IUSE", "SLOT", "USE", "_mtime_"]) - - def __init__(self, pkg, metadata): - _PackageMetadataWrapperBase.__init__(self) - self._pkg = pkg - self.update(metadata) - - def __setitem__(self, k, v): - _PackageMetadataWrapperBase.__setitem__(self, k, v) - if k in self._wrapped_keys: - getattr(self, "_set_" + k.lower())(k, v) - - def _set_inherited(self, k, v): - if isinstance(v, basestring): - v = frozenset(v.split()) - self._pkg.inherited = v - - def _set_iuse(self, k, v): - self._pkg.iuse = self._pkg._iuse( - v.split(), self._pkg.root_config.iuse_implicit) - - def _set_slot(self, k, v): - self._pkg.slot = v - - def _set_use(self, k, v): - self._pkg.use = self._pkg._use(v.split()) - - def _set_counter(self, k, v): - if isinstance(v, basestring): - try: - v = long(v.strip()) - except ValueError: - v = 0 - self._pkg.counter = v - - def _set__mtime_(self, k, v): - if isinstance(v, basestring): - try: - v = long(v.strip()) - except ValueError: - v = 0 - self._pkg.mtime = v - -class PackageUninstall(AsynchronousTask): - - __slots__ = ("ldpath_mtimes", "opts", "pkg", "scheduler", "settings") - - def _start(self): - try: - unmerge(self.pkg.root_config, self.opts, "unmerge", - [self.pkg.cpv], self.ldpath_mtimes, clean_world=0, - clean_delay=0, raise_on_error=1, scheduler=self.scheduler, - writemsg_level=self._writemsg_level) - except UninstallFailure, e: - self.returncode = e.status - else: - self.returncode = os.EX_OK - self.wait() - - def _writemsg_level(self, msg, level=0, noiselevel=0): - - log_path = self.settings.get("PORTAGE_LOG_FILE") - background = self.background - - if log_path is None: - if not (background and level < logging.WARNING): - portage.util.writemsg_level(msg, - level=level, noiselevel=noiselevel) - else: - if not background: - portage.util.writemsg_level(msg, - level=level, noiselevel=noiselevel) - - f = open(log_path, 'a') - try: - f.write(msg) - finally: - f.close() - -class MergeListItem(CompositeTask): - - """ - TODO: For parallel scheduling, everything here needs asynchronous - execution support (start, poll, and wait methods). - """ - - __slots__ = ("args_set", - "binpkg_opts", "build_opts", "config_pool", "emerge_opts", - "find_blockers", "logger", "mtimedb", "pkg", - "pkg_count", "pkg_to_replace", "prefetcher", - "settings", "statusMessage", "world_atom") + \ - ("_install_task",) - - def _start(self): - - pkg = self.pkg - build_opts = self.build_opts - - if pkg.installed: - # uninstall, executed by self.merge() - self.returncode = os.EX_OK - self.wait() - return - - args_set = self.args_set - find_blockers = self.find_blockers - logger = self.logger - mtimedb = self.mtimedb - pkg_count = self.pkg_count - scheduler = self.scheduler - settings = self.settings - world_atom = self.world_atom - ldpath_mtimes = mtimedb["ldpath"] - - action_desc = "Emerging" - preposition = "for" - if pkg.type_name == "binary": - action_desc += " binary" - - if build_opts.fetchonly: - action_desc = "Fetching" - - msg = "%s (%s of %s) %s" % \ - (action_desc, - colorize("MERGE_LIST_PROGRESS", str(pkg_count.curval)), - colorize("MERGE_LIST_PROGRESS", str(pkg_count.maxval)), - colorize("GOOD", pkg.cpv)) - - portdb = pkg.root_config.trees["porttree"].dbapi - portdir_repo_name = portdb._repository_map.get(portdb.porttree_root) - if portdir_repo_name: - pkg_repo_name = pkg.metadata.get("repository") - if pkg_repo_name != portdir_repo_name: - if not pkg_repo_name: - pkg_repo_name = "unknown repo" - msg += " from %s" % pkg_repo_name - - if pkg.root != "/": - msg += " %s %s" % (preposition, pkg.root) - - if not build_opts.pretend: - self.statusMessage(msg) - logger.log(" >>> emerge (%s of %s) %s to %s" % \ - (pkg_count.curval, pkg_count.maxval, pkg.cpv, pkg.root)) - - if pkg.type_name == "ebuild": - - build = EbuildBuild(args_set=args_set, - background=self.background, - config_pool=self.config_pool, - find_blockers=find_blockers, - ldpath_mtimes=ldpath_mtimes, logger=logger, - opts=build_opts, pkg=pkg, pkg_count=pkg_count, - prefetcher=self.prefetcher, scheduler=scheduler, - settings=settings, world_atom=world_atom) - - self._install_task = build - self._start_task(build, self._default_final_exit) - return - - elif pkg.type_name == "binary": - - binpkg = Binpkg(background=self.background, - find_blockers=find_blockers, - ldpath_mtimes=ldpath_mtimes, logger=logger, - opts=self.binpkg_opts, pkg=pkg, pkg_count=pkg_count, - prefetcher=self.prefetcher, settings=settings, - scheduler=scheduler, world_atom=world_atom) - - self._install_task = binpkg - self._start_task(binpkg, self._default_final_exit) - return - - def _poll(self): - self._install_task.poll() - return self.returncode - - def _wait(self): - self._install_task.wait() - return self.returncode - - def merge(self): - - pkg = self.pkg - build_opts = self.build_opts - find_blockers = self.find_blockers - logger = self.logger - mtimedb = self.mtimedb - pkg_count = self.pkg_count - prefetcher = self.prefetcher - scheduler = self.scheduler - settings = self.settings - world_atom = self.world_atom - ldpath_mtimes = mtimedb["ldpath"] - - if pkg.installed: - if not (build_opts.buildpkgonly or \ - build_opts.fetchonly or build_opts.pretend): - - uninstall = PackageUninstall(background=self.background, - ldpath_mtimes=ldpath_mtimes, opts=self.emerge_opts, - pkg=pkg, scheduler=scheduler, settings=settings) - - uninstall.start() - retval = uninstall.wait() - if retval != os.EX_OK: - return retval - return os.EX_OK - - if build_opts.fetchonly or \ - build_opts.buildpkgonly: - return self.returncode - - retval = self._install_task.install() - return retval - -class BlockerDB(object): - - def __init__(self, root_config): - self._root_config = root_config - self._vartree = root_config.trees["vartree"] - self._portdb = root_config.trees["porttree"].dbapi - - self._dep_check_trees = None - self._fake_vartree = None - - def _get_fake_vartree(self, acquire_lock=0): - fake_vartree = self._fake_vartree - if fake_vartree is None: - fake_vartree = FakeVartree(self._root_config, - acquire_lock=acquire_lock) - self._fake_vartree = fake_vartree - self._dep_check_trees = { self._vartree.root : { - "porttree" : fake_vartree, - "vartree" : fake_vartree, - }} - else: - fake_vartree.sync(acquire_lock=acquire_lock) - return fake_vartree - - def findInstalledBlockers(self, new_pkg, acquire_lock=0): - blocker_cache = BlockerCache(self._vartree.root, self._vartree.dbapi) - dep_keys = ["DEPEND", "RDEPEND", "PDEPEND"] - settings = self._vartree.settings - stale_cache = set(blocker_cache) - fake_vartree = self._get_fake_vartree(acquire_lock=acquire_lock) - dep_check_trees = self._dep_check_trees - vardb = fake_vartree.dbapi - installed_pkgs = list(vardb) - - for inst_pkg in installed_pkgs: - stale_cache.discard(inst_pkg.cpv) - cached_blockers = blocker_cache.get(inst_pkg.cpv) - if cached_blockers is not None and \ - cached_blockers.counter != long(inst_pkg.metadata["COUNTER"]): - cached_blockers = None - if cached_blockers is not None: - blocker_atoms = cached_blockers.atoms - else: - # Use aux_get() to trigger FakeVartree global - # updates on *DEPEND when appropriate. - depstr = " ".join(vardb.aux_get(inst_pkg.cpv, dep_keys)) - try: - portage.dep._dep_check_strict = False - success, atoms = portage.dep_check(depstr, - vardb, settings, myuse=inst_pkg.use.enabled, - trees=dep_check_trees, myroot=inst_pkg.root) - finally: - portage.dep._dep_check_strict = True - if not success: - pkg_location = os.path.join(inst_pkg.root, - portage.VDB_PATH, inst_pkg.category, inst_pkg.pf) - portage.writemsg("!!! %s/*DEPEND: %s\n" % \ - (pkg_location, atoms), noiselevel=-1) - continue - - blocker_atoms = [atom for atom in atoms \ - if atom.startswith("!")] - blocker_atoms.sort() - counter = long(inst_pkg.metadata["COUNTER"]) - blocker_cache[inst_pkg.cpv] = \ - blocker_cache.BlockerData(counter, blocker_atoms) - for cpv in stale_cache: - del blocker_cache[cpv] - blocker_cache.flush() - - blocker_parents = digraph() - blocker_atoms = [] - for pkg in installed_pkgs: - for blocker_atom in blocker_cache[pkg.cpv].atoms: - blocker_atom = blocker_atom.lstrip("!") - blocker_atoms.append(blocker_atom) - blocker_parents.add(blocker_atom, pkg) - - blocker_atoms = InternalPackageSet(initial_atoms=blocker_atoms) - blocking_pkgs = set() - for atom in blocker_atoms.iterAtomsForPackage(new_pkg): - blocking_pkgs.update(blocker_parents.parent_nodes(atom)) - - # Check for blockers in the other direction. - depstr = " ".join(new_pkg.metadata[k] for k in dep_keys) - try: - portage.dep._dep_check_strict = False - success, atoms = portage.dep_check(depstr, - vardb, settings, myuse=new_pkg.use.enabled, - trees=dep_check_trees, myroot=new_pkg.root) - finally: - portage.dep._dep_check_strict = True - if not success: - # We should never get this far with invalid deps. - show_invalid_depstring_notice(new_pkg, depstr, atoms) - assert False - - blocker_atoms = [atom.lstrip("!") for atom in atoms \ - if atom[:1] == "!"] - if blocker_atoms: - blocker_atoms = InternalPackageSet(initial_atoms=blocker_atoms) - for inst_pkg in installed_pkgs: - try: - blocker_atoms.iterAtomsForPackage(inst_pkg).next() - except (portage.exception.InvalidDependString, StopIteration): - continue - blocking_pkgs.add(inst_pkg) - - return blocking_pkgs - -def show_invalid_depstring_notice(parent_node, depstring, error_msg): - - msg1 = "\n\n!!! Invalid or corrupt dependency specification: " + \ - "\n\n%s\n\n%s\n\n%s\n\n" % (error_msg, parent_node, depstring) - p_type, p_root, p_key, p_status = parent_node - msg = [] - if p_status == "nomerge": - category, pf = portage.catsplit(p_key) - pkg_location = os.path.join(p_root, portage.VDB_PATH, category, pf) - msg.append("Portage is unable to process the dependencies of the ") - msg.append("'%s' package. " % p_key) - msg.append("In order to correct this problem, the package ") - msg.append("should be uninstalled, reinstalled, or upgraded. ") - msg.append("As a temporary workaround, the --nodeps option can ") - msg.append("be used to ignore all dependencies. For reference, ") - msg.append("the problematic dependencies can be found in the ") - msg.append("*DEPEND files located in '%s/'." % pkg_location) - else: - msg.append("This package can not be installed. ") - msg.append("Please notify the '%s' package maintainer " % p_key) - msg.append("about this problem.") - - msg2 = "".join("%s\n" % line for line in textwrap.wrap("".join(msg), 72)) - writemsg_level(msg1 + msg2, level=logging.ERROR, noiselevel=-1) - class depgraph(object): pkg_tree_map = RootConfig.pkg_tree_map @@ -6369,78 +5157,6 @@ class depgraph(object): metadata = self._cpv_pkg_map[cpv].metadata return [metadata.get(x, "") for x in wants] -class PackageCounters(object): - - def __init__(self): - self.upgrades = 0 - self.downgrades = 0 - self.new = 0 - self.newslot = 0 - self.reinst = 0 - self.uninst = 0 - self.blocks = 0 - self.blocks_satisfied = 0 - self.totalsize = 0 - self.restrict_fetch = 0 - self.restrict_fetch_satisfied = 0 - self.interactive = 0 - - def __str__(self): - total_installs = self.upgrades + self.downgrades + self.newslot + self.new + self.reinst - myoutput = [] - details = [] - myoutput.append("Total: %s package" % total_installs) - if total_installs != 1: - myoutput.append("s") - if total_installs != 0: - myoutput.append(" (") - if self.upgrades > 0: - details.append("%s upgrade" % self.upgrades) - if self.upgrades > 1: - details[-1] += "s" - if self.downgrades > 0: - details.append("%s downgrade" % self.downgrades) - if self.downgrades > 1: - details[-1] += "s" - if self.new > 0: - details.append("%s new" % self.new) - if self.newslot > 0: - details.append("%s in new slot" % self.newslot) - if self.newslot > 1: - details[-1] += "s" - if self.reinst > 0: - details.append("%s reinstall" % self.reinst) - if self.reinst > 1: - details[-1] += "s" - if self.uninst > 0: - details.append("%s uninstall" % self.uninst) - if self.uninst > 1: - details[-1] += "s" - if self.interactive > 0: - details.append("%s %s" % (self.interactive, - colorize("WARN", "interactive"))) - myoutput.append(", ".join(details)) - if total_installs != 0: - myoutput.append(")") - myoutput.append(", Size of downloads: %s" % format_size(self.totalsize)) - if self.restrict_fetch: - myoutput.append("\nFetch Restriction: %s package" % \ - self.restrict_fetch) - if self.restrict_fetch > 1: - myoutput.append("s") - if self.restrict_fetch_satisfied < self.restrict_fetch: - myoutput.append(bad(" (%s unsatisfied)") % \ - (self.restrict_fetch - self.restrict_fetch_satisfied)) - if self.blocks > 0: - myoutput.append("\nConflict: %s block" % \ - self.blocks) - if self.blocks > 1: - myoutput.append("s") - if self.blocks_satisfied < self.blocks: - myoutput.append(bad(" (%s unsatisfied)") % \ - (self.blocks - self.blocks_satisfied)) - return "".join(myoutput) - class Scheduler(PollScheduler): _opts_ignore_blockers = \ @@ -8033,663 +6749,6 @@ class Scheduler(PollScheduler): return pkg -class MetadataRegen(PollScheduler): - - def __init__(self, portdb, cp_iter=None, consumer=None, - max_jobs=None, max_load=None): - PollScheduler.__init__(self) - self._portdb = portdb - self._global_cleanse = False - if cp_iter is None: - cp_iter = self._iter_every_cp() - # We can globally cleanse stale cache only if we - # iterate over every single cp. - self._global_cleanse = True - self._cp_iter = cp_iter - self._consumer = consumer - - if max_jobs is None: - max_jobs = 1 - - self._max_jobs = max_jobs - self._max_load = max_load - self._sched_iface = self._sched_iface_class( - register=self._register, - schedule=self._schedule_wait, - unregister=self._unregister) - - self._valid_pkgs = set() - self._cp_set = set() - self._process_iter = self._iter_metadata_processes() - self.returncode = os.EX_OK - self._error_count = 0 - - def _iter_every_cp(self): - every_cp = self._portdb.cp_all() - every_cp.sort(reverse=True) - try: - while True: - yield every_cp.pop() - except IndexError: - pass - - def _iter_metadata_processes(self): - portdb = self._portdb - valid_pkgs = self._valid_pkgs - cp_set = self._cp_set - consumer = self._consumer - - for cp in self._cp_iter: - cp_set.add(cp) - portage.writemsg_stdout("Processing %s\n" % cp) - cpv_list = portdb.cp_list(cp) - for cpv in cpv_list: - valid_pkgs.add(cpv) - ebuild_path, repo_path = portdb.findname2(cpv) - metadata, st, emtime = portdb._pull_valid_cache( - cpv, ebuild_path, repo_path) - if metadata is not None: - if consumer is not None: - consumer(cpv, ebuild_path, - repo_path, metadata) - continue - - yield EbuildMetadataPhase(cpv=cpv, ebuild_path=ebuild_path, - ebuild_mtime=emtime, - metadata_callback=portdb._metadata_callback, - portdb=portdb, repo_path=repo_path, - settings=portdb.doebuild_settings) - - def run(self): - - portdb = self._portdb - from portage.cache.cache_errors import CacheError - dead_nodes = {} - - while self._schedule(): - self._poll_loop() - - while self._jobs: - self._poll_loop() - - if self._global_cleanse: - for mytree in portdb.porttrees: - try: - dead_nodes[mytree] = set(portdb.auxdb[mytree].iterkeys()) - except CacheError, e: - portage.writemsg("Error listing cache entries for " + \ - "'%s': %s, continuing...\n" % (mytree, e), - noiselevel=-1) - del e - dead_nodes = None - break - else: - cp_set = self._cp_set - cpv_getkey = portage.cpv_getkey - for mytree in portdb.porttrees: - try: - dead_nodes[mytree] = set(cpv for cpv in \ - portdb.auxdb[mytree].iterkeys() \ - if cpv_getkey(cpv) in cp_set) - except CacheError, e: - portage.writemsg("Error listing cache entries for " + \ - "'%s': %s, continuing...\n" % (mytree, e), - noiselevel=-1) - del e - dead_nodes = None - break - - if dead_nodes: - for y in self._valid_pkgs: - for mytree in portdb.porttrees: - if portdb.findname2(y, mytree=mytree)[0]: - dead_nodes[mytree].discard(y) - - for mytree, nodes in dead_nodes.iteritems(): - auxdb = portdb.auxdb[mytree] - for y in nodes: - try: - del auxdb[y] - except (KeyError, CacheError): - pass - - def _schedule_tasks(self): - """ - @rtype: bool - @returns: True if there may be remaining tasks to schedule, - False otherwise. - """ - while self._can_add_job(): - try: - metadata_process = self._process_iter.next() - except StopIteration: - return False - - self._jobs += 1 - metadata_process.scheduler = self._sched_iface - metadata_process.addExitListener(self._metadata_exit) - metadata_process.start() - return True - - def _metadata_exit(self, metadata_process): - self._jobs -= 1 - if metadata_process.returncode != os.EX_OK: - self.returncode = 1 - self._error_count += 1 - self._valid_pkgs.discard(metadata_process.cpv) - portage.writemsg("Error processing %s, continuing...\n" % \ - (metadata_process.cpv,), noiselevel=-1) - - if self._consumer is not None: - # On failure, still notify the consumer (in this case the metadata - # argument is None). - self._consumer(metadata_process.cpv, - metadata_process.ebuild_path, - metadata_process.repo_path, - metadata_process.metadata) - - self._schedule() - -def unmerge(root_config, myopts, unmerge_action, - unmerge_files, ldpath_mtimes, autoclean=0, - clean_world=1, clean_delay=1, ordered=0, raise_on_error=0, - scheduler=None, writemsg_level=portage.util.writemsg_level): - - if clean_world: - clean_world = myopts.get('--deselect') != 'n' - quiet = "--quiet" in myopts - settings = root_config.settings - sets = root_config.sets - vartree = root_config.trees["vartree"] - candidate_catpkgs=[] - global_unmerge=0 - xterm_titles = "notitles" not in settings.features - out = portage.output.EOutput() - pkg_cache = {} - db_keys = list(vartree.dbapi._aux_cache_keys) - - def _pkg(cpv): - pkg = pkg_cache.get(cpv) - if pkg is None: - pkg = Package(cpv=cpv, installed=True, - metadata=izip(db_keys, vartree.dbapi.aux_get(cpv, db_keys)), - root_config=root_config, - type_name="installed") - pkg_cache[cpv] = pkg - return pkg - - vdb_path = os.path.join(settings["ROOT"], portage.VDB_PATH) - try: - # At least the parent needs to exist for the lock file. - portage.util.ensure_dirs(vdb_path) - except portage.exception.PortageException: - pass - vdb_lock = None - try: - if os.access(vdb_path, os.W_OK): - vdb_lock = portage.locks.lockdir(vdb_path) - realsyslist = sets["system"].getAtoms() - syslist = [] - for x in realsyslist: - mycp = portage.dep_getkey(x) - if mycp in settings.getvirtuals(): - providers = [] - for provider in settings.getvirtuals()[mycp]: - if vartree.dbapi.match(provider): - providers.append(provider) - if len(providers) == 1: - syslist.extend(providers) - else: - syslist.append(mycp) - - mysettings = portage.config(clone=settings) - - if not unmerge_files: - if unmerge_action == "unmerge": - print - print bold("emerge unmerge") + " can only be used with specific package names" - print - return 0 - else: - global_unmerge = 1 - - localtree = vartree - # process all arguments and add all - # valid db entries to candidate_catpkgs - if global_unmerge: - if not unmerge_files: - candidate_catpkgs.extend(vartree.dbapi.cp_all()) - else: - #we've got command-line arguments - if not unmerge_files: - print "\nNo packages to unmerge have been provided.\n" - return 0 - for x in unmerge_files: - arg_parts = x.split('/') - if x[0] not in [".","/"] and \ - arg_parts[-1][-7:] != ".ebuild": - #possible cat/pkg or dep; treat as such - candidate_catpkgs.append(x) - elif unmerge_action in ["prune","clean"]: - print "\n!!! Prune and clean do not accept individual" + \ - " ebuilds as arguments;\n skipping.\n" - continue - else: - # it appears that the user is specifying an installed - # ebuild and we're in "unmerge" mode, so it's ok. - if not os.path.exists(x): - print "\n!!! The path '"+x+"' doesn't exist.\n" - return 0 - - absx = os.path.abspath(x) - sp_absx = absx.split("/") - if sp_absx[-1][-7:] == ".ebuild": - del sp_absx[-1] - absx = "/".join(sp_absx) - - sp_absx_len = len(sp_absx) - - vdb_path = os.path.join(settings["ROOT"], portage.VDB_PATH) - vdb_len = len(vdb_path) - - sp_vdb = vdb_path.split("/") - sp_vdb_len = len(sp_vdb) - - if not os.path.exists(absx+"/CONTENTS"): - print "!!! Not a valid db dir: "+str(absx) - return 0 - - if sp_absx_len <= sp_vdb_len: - # The Path is shorter... so it can't be inside the vdb. - print sp_absx - print absx - print "\n!!!",x,"cannot be inside "+ \ - vdb_path+"; aborting.\n" - return 0 - - for idx in range(0,sp_vdb_len): - if idx >= sp_absx_len or sp_vdb[idx] != sp_absx[idx]: - print sp_absx - print absx - print "\n!!!", x, "is not inside "+\ - vdb_path+"; aborting.\n" - return 0 - - print "="+"/".join(sp_absx[sp_vdb_len:]) - candidate_catpkgs.append( - "="+"/".join(sp_absx[sp_vdb_len:])) - - newline="" - if (not "--quiet" in myopts): - newline="\n" - if settings["ROOT"] != "/": - writemsg_level(darkgreen(newline+ \ - ">>> Using system located in ROOT tree %s\n" % \ - settings["ROOT"])) - - if (("--pretend" in myopts) or ("--ask" in myopts)) and \ - not ("--quiet" in myopts): - writemsg_level(darkgreen(newline+\ - ">>> These are the packages that would be unmerged:\n")) - - # Preservation of order is required for --depclean and --prune so - # that dependencies are respected. Use all_selected to eliminate - # duplicate packages since the same package may be selected by - # multiple atoms. - pkgmap = [] - all_selected = set() - for x in candidate_catpkgs: - # cycle through all our candidate deps and determine - # what will and will not get unmerged - try: - mymatch = vartree.dbapi.match(x) - except portage.exception.AmbiguousPackageName, errpkgs: - print "\n\n!!! The short ebuild name \"" + \ - x + "\" is ambiguous. Please specify" - print "!!! one of the following fully-qualified " + \ - "ebuild names instead:\n" - for i in errpkgs[0]: - print " " + green(i) - print - sys.exit(1) - - if not mymatch and x[0] not in "<>=~": - mymatch = localtree.dep_match(x) - if not mymatch: - portage.writemsg("\n--- Couldn't find '%s' to %s.\n" % \ - (x, unmerge_action), noiselevel=-1) - continue - - pkgmap.append( - {"protected": set(), "selected": set(), "omitted": set()}) - mykey = len(pkgmap) - 1 - if unmerge_action=="unmerge": - for y in mymatch: - if y not in all_selected: - pkgmap[mykey]["selected"].add(y) - all_selected.add(y) - elif unmerge_action == "prune": - if len(mymatch) == 1: - continue - best_version = mymatch[0] - best_slot = vartree.getslot(best_version) - best_counter = vartree.dbapi.cpv_counter(best_version) - for mypkg in mymatch[1:]: - myslot = vartree.getslot(mypkg) - mycounter = vartree.dbapi.cpv_counter(mypkg) - if (myslot == best_slot and mycounter > best_counter) or \ - mypkg == portage.best([mypkg, best_version]): - if myslot == best_slot: - if mycounter < best_counter: - # On slot collision, keep the one with the - # highest counter since it is the most - # recently installed. - continue - best_version = mypkg - best_slot = myslot - best_counter = mycounter - pkgmap[mykey]["protected"].add(best_version) - pkgmap[mykey]["selected"].update(mypkg for mypkg in mymatch \ - if mypkg != best_version and mypkg not in all_selected) - all_selected.update(pkgmap[mykey]["selected"]) - else: - # unmerge_action == "clean" - slotmap={} - for mypkg in mymatch: - if unmerge_action == "clean": - myslot = localtree.getslot(mypkg) - else: - # since we're pruning, we don't care about slots - # and put all the pkgs in together - myslot = 0 - if myslot not in slotmap: - slotmap[myslot] = {} - slotmap[myslot][localtree.dbapi.cpv_counter(mypkg)] = mypkg - - for mypkg in vartree.dbapi.cp_list( - portage.dep_getkey(mymatch[0])): - myslot = vartree.getslot(mypkg) - if myslot not in slotmap: - slotmap[myslot] = {} - slotmap[myslot][vartree.dbapi.cpv_counter(mypkg)] = mypkg - - for myslot in slotmap: - counterkeys = slotmap[myslot].keys() - if not counterkeys: - continue - counterkeys.sort() - pkgmap[mykey]["protected"].add( - slotmap[myslot][counterkeys[-1]]) - del counterkeys[-1] - - for counter in counterkeys[:]: - mypkg = slotmap[myslot][counter] - if mypkg not in mymatch: - counterkeys.remove(counter) - pkgmap[mykey]["protected"].add( - slotmap[myslot][counter]) - - #be pretty and get them in order of merge: - for ckey in counterkeys: - mypkg = slotmap[myslot][ckey] - if mypkg not in all_selected: - pkgmap[mykey]["selected"].add(mypkg) - all_selected.add(mypkg) - # ok, now the last-merged package - # is protected, and the rest are selected - numselected = len(all_selected) - if global_unmerge and not numselected: - portage.writemsg_stdout("\n>>> No outdated packages were found on your system.\n") - return 0 - - if not numselected: - portage.writemsg_stdout( - "\n>>> No packages selected for removal by " + \ - unmerge_action + "\n") - return 0 - finally: - if vdb_lock: - vartree.dbapi.flush_cache() - portage.locks.unlockdir(vdb_lock) - - from portage.sets.base import EditablePackageSet - - # generate a list of package sets that are directly or indirectly listed in "world", - # as there is no persistent list of "installed" sets - installed_sets = ["world"] - stop = False - pos = 0 - while not stop: - stop = True - pos = len(installed_sets) - for s in installed_sets[pos - 1:]: - if s not in sets: - continue - candidates = [x[len(SETPREFIX):] for x in sets[s].getNonAtoms() if x.startswith(SETPREFIX)] - if candidates: - stop = False - installed_sets += candidates - installed_sets = [x for x in installed_sets if x not in root_config.setconfig.active] - del stop, pos - - # we don't want to unmerge packages that are still listed in user-editable package sets - # listed in "world" as they would be remerged on the next update of "world" or the - # relevant package sets. - unknown_sets = set() - for cp in xrange(len(pkgmap)): - for cpv in pkgmap[cp]["selected"].copy(): - try: - pkg = _pkg(cpv) - except KeyError: - # It could have been uninstalled - # by a concurrent process. - continue - - if unmerge_action != "clean" and \ - root_config.root == "/" and \ - portage.match_from_list( - portage.const.PORTAGE_PACKAGE_ATOM, [pkg]): - msg = ("Not unmerging package %s since there is no valid " + \ - "reason for portage to unmerge itself.") % (pkg.cpv,) - for line in textwrap.wrap(msg, 75): - out.eerror(line) - # adjust pkgmap so the display output is correct - pkgmap[cp]["selected"].remove(cpv) - all_selected.remove(cpv) - pkgmap[cp]["protected"].add(cpv) - continue - - parents = [] - for s in installed_sets: - # skip sets that the user requested to unmerge, and skip world - # unless we're unmerging a package set (as the package would be - # removed from "world" later on) - if s in root_config.setconfig.active or (s == "world" and not root_config.setconfig.active): - continue - - if s not in sets: - if s in unknown_sets: - continue - unknown_sets.add(s) - out = portage.output.EOutput() - out.eerror(("Unknown set '@%s' in " + \ - "%svar/lib/portage/world_sets") % \ - (s, root_config.root)) - continue - - # only check instances of EditablePackageSet as other classes are generally used for - # special purposes and can be ignored here (and are usually generated dynamically, so the - # user can't do much about them anyway) - if isinstance(sets[s], EditablePackageSet): - - # This is derived from a snippet of code in the - # depgraph._iter_atoms_for_pkg() method. - for atom in sets[s].iterAtomsForPackage(pkg): - inst_matches = vartree.dbapi.match(atom) - inst_matches.reverse() # descending order - higher_slot = None - for inst_cpv in inst_matches: - try: - inst_pkg = _pkg(inst_cpv) - except KeyError: - # It could have been uninstalled - # by a concurrent process. - continue - - if inst_pkg.cp != atom.cp: - continue - if pkg >= inst_pkg: - # This is descending order, and we're not - # interested in any versions <= pkg given. - break - if pkg.slot_atom != inst_pkg.slot_atom: - higher_slot = inst_pkg - break - if higher_slot is None: - parents.append(s) - break - if parents: - #print colorize("WARN", "Package %s is going to be unmerged," % cpv) - #print colorize("WARN", "but still listed in the following package sets:") - #print " %s\n" % ", ".join(parents) - print colorize("WARN", "Not unmerging package %s as it is" % cpv) - print colorize("WARN", "still referenced by the following package sets:") - print " %s\n" % ", ".join(parents) - # adjust pkgmap so the display output is correct - pkgmap[cp]["selected"].remove(cpv) - all_selected.remove(cpv) - pkgmap[cp]["protected"].add(cpv) - - del installed_sets - - numselected = len(all_selected) - if not numselected: - writemsg_level( - "\n>>> No packages selected for removal by " + \ - unmerge_action + "\n") - return 0 - - # Unmerge order only matters in some cases - if not ordered: - unordered = {} - for d in pkgmap: - selected = d["selected"] - if not selected: - continue - cp = portage.cpv_getkey(iter(selected).next()) - cp_dict = unordered.get(cp) - if cp_dict is None: - cp_dict = {} - unordered[cp] = cp_dict - for k in d: - cp_dict[k] = set() - for k, v in d.iteritems(): - cp_dict[k].update(v) - pkgmap = [unordered[cp] for cp in sorted(unordered)] - - for x in xrange(len(pkgmap)): - selected = pkgmap[x]["selected"] - if not selected: - continue - for mytype, mylist in pkgmap[x].iteritems(): - if mytype == "selected": - continue - mylist.difference_update(all_selected) - cp = portage.cpv_getkey(iter(selected).next()) - for y in localtree.dep_match(cp): - if y not in pkgmap[x]["omitted"] and \ - y not in pkgmap[x]["selected"] and \ - y not in pkgmap[x]["protected"] and \ - y not in all_selected: - pkgmap[x]["omitted"].add(y) - if global_unmerge and not pkgmap[x]["selected"]: - #avoid cluttering the preview printout with stuff that isn't getting unmerged - continue - if not (pkgmap[x]["protected"] or pkgmap[x]["omitted"]) and cp in syslist: - writemsg_level(colorize("BAD","\a\n\n!!! " + \ - "'%s' is part of your system profile.\n" % cp), - level=logging.WARNING, noiselevel=-1) - writemsg_level(colorize("WARN","\a!!! Unmerging it may " + \ - "be damaging to your system.\n\n"), - level=logging.WARNING, noiselevel=-1) - if clean_delay and "--pretend" not in myopts and "--ask" not in myopts: - countdown(int(settings["EMERGE_WARNING_DELAY"]), - colorize("UNMERGE_WARN", "Press Ctrl-C to Stop")) - if not quiet: - writemsg_level("\n %s\n" % (bold(cp),), noiselevel=-1) - else: - writemsg_level(bold(cp) + ": ", noiselevel=-1) - for mytype in ["selected","protected","omitted"]: - if not quiet: - writemsg_level((mytype + ": ").rjust(14), noiselevel=-1) - if pkgmap[x][mytype]: - sorted_pkgs = [portage.catpkgsplit(mypkg)[1:] for mypkg in pkgmap[x][mytype]] - sorted_pkgs.sort(key=cmp_sort_key(portage.pkgcmp)) - for pn, ver, rev in sorted_pkgs: - if rev == "r0": - myversion = ver - else: - myversion = ver + "-" + rev - if mytype == "selected": - writemsg_level( - colorize("UNMERGE_WARN", myversion + " "), - noiselevel=-1) - else: - writemsg_level( - colorize("GOOD", myversion + " "), noiselevel=-1) - else: - writemsg_level("none ", noiselevel=-1) - if not quiet: - writemsg_level("\n", noiselevel=-1) - if quiet: - writemsg_level("\n", noiselevel=-1) - - writemsg_level("\n>>> " + colorize("UNMERGE_WARN", "'Selected'") + \ - " packages are slated for removal.\n") - writemsg_level(">>> " + colorize("GOOD", "'Protected'") + \ - " and " + colorize("GOOD", "'omitted'") + \ - " packages will not be removed.\n\n") - - if "--pretend" in myopts: - #we're done... return - return 0 - if "--ask" in myopts: - if userquery("Would you like to unmerge these packages?")=="No": - # enter pretend mode for correct formatting of results - myopts["--pretend"] = True - print - print "Quitting." - print - return 0 - #the real unmerging begins, after a short delay.... - if clean_delay and not autoclean: - countdown(int(settings["CLEAN_DELAY"]), ">>> Unmerging") - - for x in xrange(len(pkgmap)): - for y in pkgmap[x]["selected"]: - writemsg_level(">>> Unmerging "+y+"...\n", noiselevel=-1) - emergelog(xterm_titles, "=== Unmerging... ("+y+")") - mysplit = y.split("/") - #unmerge... - retval = portage.unmerge(mysplit[0], mysplit[1], settings["ROOT"], - mysettings, unmerge_action not in ["clean","prune"], - vartree=vartree, ldpath_mtimes=ldpath_mtimes, - scheduler=scheduler) - - if retval != os.EX_OK: - emergelog(xterm_titles, " !!! unmerge FAILURE: "+y) - if raise_on_error: - raise UninstallFailure(retval) - sys.exit(retval) - else: - if clean_world and hasattr(sets["world"], "cleanPackage"): - sets["world"].cleanPackage(vartree.dbapi, y) - emergelog(xterm_titles, " >>> unmerge success: "+y) - if clean_world and hasattr(sets["world"], "remove"): - for s in root_config.setconfig.active: - sets["world"].remove(SETPREFIX+s) - return 1 - def chk_updated_info_files(root, infodirs, prev_mtimes, retval): if os.path.exists("/usr/bin/install-info"): diff --git a/pym/_emerge/countdown.py b/pym/_emerge/countdown.py new file mode 100644 index 000000000..a5fcde84e --- /dev/null +++ b/pym/_emerge/countdown.py @@ -0,0 +1,17 @@ +import sys +import time + +from portage.output import colorize + +def countdown(secs=5, doing="Starting"): + if secs: + print ">>> Waiting",secs,"seconds before starting..." + print ">>> (Control-C to abort)...\n"+doing+" in: ", + ticks=range(secs) + ticks.reverse() + for sec in ticks: + sys.stdout.write(colorize("UNMERGE_WARN", str(sec+1)+" ")) + sys.stdout.flush() + time.sleep(1) + print + diff --git a/pym/_emerge/emergelog.py b/pym/_emerge/emergelog.py new file mode 100644 index 000000000..53c6abe16 --- /dev/null +++ b/pym/_emerge/emergelog.py @@ -0,0 +1,41 @@ +import os +import sys +import time +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.data import secpass +from portage.output import xtermTitle + +_emerge_log_dir = '/var/log' + +def emergelog(xterm_titles, mystr, short_msg=None): + if xterm_titles and short_msg: + if "HOSTNAME" in os.environ: + short_msg = os.environ["HOSTNAME"]+": "+short_msg + xtermTitle(short_msg) + try: + file_path = os.path.join(_emerge_log_dir, 'emerge.log') + mylogfile = open(file_path, "a") + portage.util.apply_secpass_permissions(file_path, + uid=portage.portage_uid, gid=portage.portage_gid, + mode=0660) + mylock = None + try: + mylock = portage.locks.lockfile(mylogfile) + # seek because we may have gotten held up by the lock. + # if so, we may not be positioned at the end of the file. + mylogfile.seek(0, 2) + mylogfile.write(str(time.time())[:10]+": "+mystr+"\n") + mylogfile.flush() + finally: + if mylock: + portage.locks.unlockfile(mylock) + mylogfile.close() + except (IOError,OSError,portage.exception.PortageException), e: + if secpass >= 1: + print >> sys.stderr, "emergelog():",e diff --git a/pym/_emerge/format_size.py b/pym/_emerge/format_size.py new file mode 100644 index 000000000..7638100f6 --- /dev/null +++ b/pym/_emerge/format_size.py @@ -0,0 +1,16 @@ + +# formats a size given in bytes nicely +def format_size(mysize): + if isinstance(mysize, basestring): + return mysize + if 0 != mysize % 1024: + # Always round up to the next kB so that it doesn't show 0 kB when + # some small file still needs to be fetched. + mysize += 1024 - mysize % 1024 + mystr=str(mysize/1024) + mycount=len(mystr) + while (mycount > 3): + mycount-=3 + mystr=mystr[:mycount]+","+mystr[mycount:] + return mystr+" kB" + diff --git a/pym/_emerge/search.py b/pym/_emerge/search.py new file mode 100644 index 000000000..69e13233a --- /dev/null +++ b/pym/_emerge/search.py @@ -0,0 +1,378 @@ +import os +import re +from itertools import izip + +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 + +from portage.output import bold as white, darkgreen, green, red + +from _emerge.Package import Package +from _emerge.visible import visible + +class search(object): + + # + # class constants + # + VERSION_SHORT=1 + VERSION_RELEASE=2 + + # + # public interface + # + def __init__(self, root_config, spinner, searchdesc, + verbose, usepkg, usepkgonly): + """Searches the available and installed packages for the supplied search key. + The list of available and installed packages is created at object instantiation. + This makes successive searches faster.""" + self.settings = root_config.settings + self.vartree = root_config.trees["vartree"] + self.spinner = spinner + self.verbose = verbose + self.searchdesc = searchdesc + self.root_config = root_config + self.setconfig = root_config.setconfig + self.matches = {"pkg" : []} + self.mlen = 0 + + def fake_portdb(): + pass + self.portdb = fake_portdb + for attrib in ("aux_get", "cp_all", + "xmatch", "findname", "getFetchMap"): + setattr(fake_portdb, attrib, getattr(self, "_"+attrib)) + + self._dbs = [] + + portdb = root_config.trees["porttree"].dbapi + bindb = root_config.trees["bintree"].dbapi + vardb = root_config.trees["vartree"].dbapi + + if not usepkgonly and portdb._have_root_eclass_dir: + self._dbs.append(portdb) + + if (usepkg or usepkgonly) and bindb.cp_all(): + self._dbs.append(bindb) + + self._dbs.append(vardb) + self._portdb = portdb + + def _cp_all(self): + cp_all = set() + for db in self._dbs: + cp_all.update(db.cp_all()) + return list(sorted(cp_all)) + + def _aux_get(self, *args, **kwargs): + for db in self._dbs: + try: + return db.aux_get(*args, **kwargs) + except KeyError: + pass + raise + + def _findname(self, *args, **kwargs): + for db in self._dbs: + if db is not self._portdb: + # We don't want findname to return anything + # unless it's an ebuild in a portage tree. + # Otherwise, it's already built and we don't + # care about it. + continue + func = getattr(db, "findname", None) + if func: + value = func(*args, **kwargs) + if value: + return value + return None + + def _getFetchMap(self, *args, **kwargs): + for db in self._dbs: + func = getattr(db, "getFetchMap", None) + if func: + value = func(*args, **kwargs) + if value: + return value + return {} + + def _visible(self, db, cpv, metadata): + installed = db is self.vartree.dbapi + built = installed or db is not self._portdb + pkg_type = "ebuild" + if installed: + pkg_type = "installed" + elif built: + pkg_type = "binary" + return visible(self.settings, + Package(type_name=pkg_type, root_config=self.root_config, + cpv=cpv, built=built, installed=installed, metadata=metadata)) + + def _xmatch(self, level, atom): + """ + This method does not expand old-style virtuals because it + is restricted to returning matches for a single ${CATEGORY}/${PN} + and old-style virual matches unreliable for that when querying + multiple package databases. If necessary, old-style virtuals + can be performed on atoms prior to calling this method. + """ + cp = portage.dep_getkey(atom) + if level == "match-all": + matches = set() + for db in self._dbs: + if hasattr(db, "xmatch"): + matches.update(db.xmatch(level, atom)) + else: + matches.update(db.match(atom)) + result = list(x for x in matches if portage.cpv_getkey(x) == cp) + db._cpv_sort_ascending(result) + elif level == "match-visible": + matches = set() + for db in self._dbs: + if hasattr(db, "xmatch"): + matches.update(db.xmatch(level, atom)) + else: + db_keys = list(db._aux_cache_keys) + for cpv in db.match(atom): + metadata = izip(db_keys, + db.aux_get(cpv, db_keys)) + if not self._visible(db, cpv, metadata): + continue + matches.add(cpv) + result = list(x for x in matches if portage.cpv_getkey(x) == cp) + db._cpv_sort_ascending(result) + elif level == "bestmatch-visible": + result = None + for db in self._dbs: + if hasattr(db, "xmatch"): + cpv = db.xmatch("bestmatch-visible", atom) + if not cpv or portage.cpv_getkey(cpv) != cp: + continue + if not result or cpv == portage.best([cpv, result]): + result = cpv + else: + db_keys = Package.metadata_keys + # break out of this loop with highest visible + # match, checked in descending order + for cpv in reversed(db.match(atom)): + if portage.cpv_getkey(cpv) != cp: + continue + metadata = izip(db_keys, + db.aux_get(cpv, db_keys)) + if not self._visible(db, cpv, metadata): + continue + if not result or cpv == portage.best([cpv, result]): + result = cpv + break + else: + raise NotImplementedError(level) + return result + + def execute(self,searchkey): + """Performs the search for the supplied search key""" + match_category = 0 + self.searchkey=searchkey + self.packagematches = [] + if self.searchdesc: + self.searchdesc=1 + self.matches = {"pkg":[], "desc":[], "set":[]} + else: + self.searchdesc=0 + self.matches = {"pkg":[], "set":[]} + print "Searching... ", + + regexsearch = False + if self.searchkey.startswith('%'): + regexsearch = True + self.searchkey = self.searchkey[1:] + if self.searchkey.startswith('@'): + match_category = 1 + self.searchkey = self.searchkey[1:] + if regexsearch: + self.searchre=re.compile(self.searchkey,re.I) + else: + self.searchre=re.compile(re.escape(self.searchkey), re.I) + for package in self.portdb.cp_all(): + self.spinner.update() + + if match_category: + match_string = package[:] + else: + match_string = package.split("/")[-1] + + masked=0 + if self.searchre.search(match_string): + if not self.portdb.xmatch("match-visible", package): + masked=1 + self.matches["pkg"].append([package,masked]) + elif self.searchdesc: # DESCRIPTION searching + full_package = self.portdb.xmatch("bestmatch-visible", package) + if not full_package: + #no match found; we don't want to query description + full_package = portage.best( + self.portdb.xmatch("match-all", package)) + if not full_package: + continue + else: + masked=1 + try: + full_desc = self.portdb.aux_get( + full_package, ["DESCRIPTION"])[0] + except KeyError: + print "emerge: search: aux_get() failed, skipping" + continue + if self.searchre.search(full_desc): + self.matches["desc"].append([full_package,masked]) + + self.sdict = self.setconfig.getSets() + for setname in self.sdict: + self.spinner.update() + if match_category: + match_string = setname + else: + match_string = setname.split("/")[-1] + + if self.searchre.search(match_string): + self.matches["set"].append([setname, False]) + elif self.searchdesc: + if self.searchre.search( + self.sdict[setname].getMetadata("DESCRIPTION")): + self.matches["set"].append([setname, False]) + + self.mlen=0 + for mtype in self.matches: + self.matches[mtype].sort() + self.mlen += len(self.matches[mtype]) + + def addCP(self, cp): + if not self.portdb.xmatch("match-all", cp): + return + masked = 0 + if not self.portdb.xmatch("bestmatch-visible", cp): + masked = 1 + self.matches["pkg"].append([cp, masked]) + self.mlen += 1 + + def output(self): + """Outputs the results of the search.""" + print "\b\b \n[ Results for search key : "+white(self.searchkey)+" ]" + print "[ Applications found : "+white(str(self.mlen))+" ]" + print " " + vardb = self.vartree.dbapi + for mtype in self.matches: + for match,masked in self.matches[mtype]: + full_package = None + if mtype == "pkg": + catpack = match + full_package = self.portdb.xmatch( + "bestmatch-visible", match) + if not full_package: + #no match found; we don't want to query description + masked=1 + full_package = portage.best( + self.portdb.xmatch("match-all",match)) + elif mtype == "desc": + full_package = match + match = portage.cpv_getkey(match) + elif mtype == "set": + print green("*")+" "+white(match) + print " ", darkgreen("Description:")+" ", self.sdict[match].getMetadata("DESCRIPTION") + print + if full_package: + try: + desc, homepage, license = self.portdb.aux_get( + full_package, ["DESCRIPTION","HOMEPAGE","LICENSE"]) + except KeyError: + print "emerge: search: aux_get() failed, skipping" + continue + if masked: + print green("*")+" "+white(match)+" "+red("[ Masked ]") + else: + print green("*")+" "+white(match) + myversion = self.getVersion(full_package, search.VERSION_RELEASE) + + mysum = [0,0] + file_size_str = None + mycat = match.split("/")[0] + mypkg = match.split("/")[1] + mycpv = match + "-" + myversion + myebuild = self.portdb.findname(mycpv) + if myebuild: + pkgdir = os.path.dirname(myebuild) + from portage import manifest + mf = manifest.Manifest( + pkgdir, self.settings["DISTDIR"]) + try: + uri_map = self.portdb.getFetchMap(mycpv) + except portage.exception.InvalidDependString, e: + file_size_str = "Unknown (%s)" % (e,) + del e + else: + try: + mysum[0] = mf.getDistfilesSize(uri_map) + except KeyError, e: + file_size_str = "Unknown (missing " + \ + "digest for %s)" % (e,) + del e + + available = False + for db in self._dbs: + if db is not vardb and \ + db.cpv_exists(mycpv): + available = True + if not myebuild and hasattr(db, "bintree"): + myebuild = db.bintree.getname(mycpv) + try: + mysum[0] = os.stat(myebuild).st_size + except OSError: + myebuild = None + break + + if myebuild and file_size_str is None: + mystr = str(mysum[0] / 1024) + mycount = len(mystr) + while (mycount > 3): + mycount -= 3 + mystr = mystr[:mycount] + "," + mystr[mycount:] + file_size_str = mystr + " kB" + + if self.verbose: + if available: + print " ", darkgreen("Latest version available:"),myversion + print " ", self.getInstallationStatus(mycat+'/'+mypkg) + if myebuild: + print " %s %s" % \ + (darkgreen("Size of files:"), file_size_str) + print " ", darkgreen("Homepage:")+" ",homepage + print " ", darkgreen("Description:")+" ",desc + print " ", darkgreen("License:")+" ",license + print + # + # private interface + # + def getInstallationStatus(self,package): + installed_package = self.vartree.dep_bestmatch(package) + result = "" + version = self.getVersion(installed_package,search.VERSION_RELEASE) + if len(version) > 0: + result = darkgreen("Latest version installed:")+" "+version + else: + result = darkgreen("Latest version installed:")+" [ Not Installed ]" + return result + + def getVersion(self,full_package,detail): + if len(full_package) > 1: + package_parts = portage.catpkgsplit(full_package) + if detail == search.VERSION_RELEASE and package_parts[3] != 'r0': + result = package_parts[2]+ "-" + package_parts[3] + else: + result = package_parts[2] + else: + result = "" + return result + diff --git a/pym/_emerge/show_invalid_depstring_notice.py b/pym/_emerge/show_invalid_depstring_notice.py new file mode 100644 index 000000000..4eaca0e68 --- /dev/null +++ b/pym/_emerge/show_invalid_depstring_notice.py @@ -0,0 +1,39 @@ +import logging +import os +import textwrap + +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 + +from portage.util import writemsg_level + +def show_invalid_depstring_notice(parent_node, depstring, error_msg): + + msg1 = "\n\n!!! Invalid or corrupt dependency specification: " + \ + "\n\n%s\n\n%s\n\n%s\n\n" % (error_msg, parent_node, depstring) + p_type, p_root, p_key, p_status = parent_node + msg = [] + if p_status == "nomerge": + category, pf = portage.catsplit(p_key) + pkg_location = os.path.join(p_root, portage.VDB_PATH, category, pf) + msg.append("Portage is unable to process the dependencies of the ") + msg.append("'%s' package. " % p_key) + msg.append("In order to correct this problem, the package ") + msg.append("should be uninstalled, reinstalled, or upgraded. ") + msg.append("As a temporary workaround, the --nodeps option can ") + msg.append("be used to ignore all dependencies. For reference, ") + msg.append("the problematic dependencies can be found in the ") + msg.append("*DEPEND files located in '%s/'." % pkg_location) + else: + msg.append("This package can not be installed. ") + msg.append("Please notify the '%s' package maintainer " % p_key) + msg.append("about this problem.") + + msg2 = "".join("%s\n" % line for line in textwrap.wrap("".join(msg), 72)) + writemsg_level(msg1 + msg2, level=logging.ERROR, noiselevel=-1) + diff --git a/pym/_emerge/unmerge.py b/pym/_emerge/unmerge.py new file mode 100644 index 000000000..393f938ff --- /dev/null +++ b/pym/_emerge/unmerge.py @@ -0,0 +1,523 @@ +import logging +import os +import sys +import textwrap +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.output import bold, colorize, darkgreen, green +from portage.sets import SETPREFIX +from portage.util import cmp_sort_key + +from _emerge.emergelog import emergelog +from _emerge.Package import Package +from _emerge.UninstallFailure import UninstallFailure +from _emerge.userquery import userquery +from _emerge.countdown import countdown + +def unmerge(root_config, myopts, unmerge_action, + unmerge_files, ldpath_mtimes, autoclean=0, + clean_world=1, clean_delay=1, ordered=0, raise_on_error=0, + scheduler=None, writemsg_level=portage.util.writemsg_level): + + if clean_world: + clean_world = myopts.get('--deselect') != 'n' + quiet = "--quiet" in myopts + settings = root_config.settings + sets = root_config.sets + vartree = root_config.trees["vartree"] + candidate_catpkgs=[] + global_unmerge=0 + xterm_titles = "notitles" not in settings.features + out = portage.output.EOutput() + pkg_cache = {} + db_keys = list(vartree.dbapi._aux_cache_keys) + + def _pkg(cpv): + pkg = pkg_cache.get(cpv) + if pkg is None: + pkg = Package(cpv=cpv, installed=True, + metadata=izip(db_keys, vartree.dbapi.aux_get(cpv, db_keys)), + root_config=root_config, + type_name="installed") + pkg_cache[cpv] = pkg + return pkg + + vdb_path = os.path.join(settings["ROOT"], portage.VDB_PATH) + try: + # At least the parent needs to exist for the lock file. + portage.util.ensure_dirs(vdb_path) + except portage.exception.PortageException: + pass + vdb_lock = None + try: + if os.access(vdb_path, os.W_OK): + vdb_lock = portage.locks.lockdir(vdb_path) + realsyslist = sets["system"].getAtoms() + syslist = [] + for x in realsyslist: + mycp = portage.dep_getkey(x) + if mycp in settings.getvirtuals(): + providers = [] + for provider in settings.getvirtuals()[mycp]: + if vartree.dbapi.match(provider): + providers.append(provider) + if len(providers) == 1: + syslist.extend(providers) + else: + syslist.append(mycp) + + mysettings = portage.config(clone=settings) + + if not unmerge_files: + if unmerge_action == "unmerge": + print + print bold("emerge unmerge") + " can only be used with specific package names" + print + return 0 + else: + global_unmerge = 1 + + localtree = vartree + # process all arguments and add all + # valid db entries to candidate_catpkgs + if global_unmerge: + if not unmerge_files: + candidate_catpkgs.extend(vartree.dbapi.cp_all()) + else: + #we've got command-line arguments + if not unmerge_files: + print "\nNo packages to unmerge have been provided.\n" + return 0 + for x in unmerge_files: + arg_parts = x.split('/') + if x[0] not in [".","/"] and \ + arg_parts[-1][-7:] != ".ebuild": + #possible cat/pkg or dep; treat as such + candidate_catpkgs.append(x) + elif unmerge_action in ["prune","clean"]: + print "\n!!! Prune and clean do not accept individual" + \ + " ebuilds as arguments;\n skipping.\n" + continue + else: + # it appears that the user is specifying an installed + # ebuild and we're in "unmerge" mode, so it's ok. + if not os.path.exists(x): + print "\n!!! The path '"+x+"' doesn't exist.\n" + return 0 + + absx = os.path.abspath(x) + sp_absx = absx.split("/") + if sp_absx[-1][-7:] == ".ebuild": + del sp_absx[-1] + absx = "/".join(sp_absx) + + sp_absx_len = len(sp_absx) + + vdb_path = os.path.join(settings["ROOT"], portage.VDB_PATH) + vdb_len = len(vdb_path) + + sp_vdb = vdb_path.split("/") + sp_vdb_len = len(sp_vdb) + + if not os.path.exists(absx+"/CONTENTS"): + print "!!! Not a valid db dir: "+str(absx) + return 0 + + if sp_absx_len <= sp_vdb_len: + # The Path is shorter... so it can't be inside the vdb. + print sp_absx + print absx + print "\n!!!",x,"cannot be inside "+ \ + vdb_path+"; aborting.\n" + return 0 + + for idx in range(0,sp_vdb_len): + if idx >= sp_absx_len or sp_vdb[idx] != sp_absx[idx]: + print sp_absx + print absx + print "\n!!!", x, "is not inside "+\ + vdb_path+"; aborting.\n" + return 0 + + print "="+"/".join(sp_absx[sp_vdb_len:]) + candidate_catpkgs.append( + "="+"/".join(sp_absx[sp_vdb_len:])) + + newline="" + if (not "--quiet" in myopts): + newline="\n" + if settings["ROOT"] != "/": + writemsg_level(darkgreen(newline+ \ + ">>> Using system located in ROOT tree %s\n" % \ + settings["ROOT"])) + + if (("--pretend" in myopts) or ("--ask" in myopts)) and \ + not ("--quiet" in myopts): + writemsg_level(darkgreen(newline+\ + ">>> These are the packages that would be unmerged:\n")) + + # Preservation of order is required for --depclean and --prune so + # that dependencies are respected. Use all_selected to eliminate + # duplicate packages since the same package may be selected by + # multiple atoms. + pkgmap = [] + all_selected = set() + for x in candidate_catpkgs: + # cycle through all our candidate deps and determine + # what will and will not get unmerged + try: + mymatch = vartree.dbapi.match(x) + except portage.exception.AmbiguousPackageName, errpkgs: + print "\n\n!!! The short ebuild name \"" + \ + x + "\" is ambiguous. Please specify" + print "!!! one of the following fully-qualified " + \ + "ebuild names instead:\n" + for i in errpkgs[0]: + print " " + green(i) + print + sys.exit(1) + + if not mymatch and x[0] not in "<>=~": + mymatch = localtree.dep_match(x) + if not mymatch: + portage.writemsg("\n--- Couldn't find '%s' to %s.\n" % \ + (x, unmerge_action), noiselevel=-1) + continue + + pkgmap.append( + {"protected": set(), "selected": set(), "omitted": set()}) + mykey = len(pkgmap) - 1 + if unmerge_action=="unmerge": + for y in mymatch: + if y not in all_selected: + pkgmap[mykey]["selected"].add(y) + all_selected.add(y) + elif unmerge_action == "prune": + if len(mymatch) == 1: + continue + best_version = mymatch[0] + best_slot = vartree.getslot(best_version) + best_counter = vartree.dbapi.cpv_counter(best_version) + for mypkg in mymatch[1:]: + myslot = vartree.getslot(mypkg) + mycounter = vartree.dbapi.cpv_counter(mypkg) + if (myslot == best_slot and mycounter > best_counter) or \ + mypkg == portage.best([mypkg, best_version]): + if myslot == best_slot: + if mycounter < best_counter: + # On slot collision, keep the one with the + # highest counter since it is the most + # recently installed. + continue + best_version = mypkg + best_slot = myslot + best_counter = mycounter + pkgmap[mykey]["protected"].add(best_version) + pkgmap[mykey]["selected"].update(mypkg for mypkg in mymatch \ + if mypkg != best_version and mypkg not in all_selected) + all_selected.update(pkgmap[mykey]["selected"]) + else: + # unmerge_action == "clean" + slotmap={} + for mypkg in mymatch: + if unmerge_action == "clean": + myslot = localtree.getslot(mypkg) + else: + # since we're pruning, we don't care about slots + # and put all the pkgs in together + myslot = 0 + if myslot not in slotmap: + slotmap[myslot] = {} + slotmap[myslot][localtree.dbapi.cpv_counter(mypkg)] = mypkg + + for mypkg in vartree.dbapi.cp_list( + portage.dep_getkey(mymatch[0])): + myslot = vartree.getslot(mypkg) + if myslot not in slotmap: + slotmap[myslot] = {} + slotmap[myslot][vartree.dbapi.cpv_counter(mypkg)] = mypkg + + for myslot in slotmap: + counterkeys = slotmap[myslot].keys() + if not counterkeys: + continue + counterkeys.sort() + pkgmap[mykey]["protected"].add( + slotmap[myslot][counterkeys[-1]]) + del counterkeys[-1] + + for counter in counterkeys[:]: + mypkg = slotmap[myslot][counter] + if mypkg not in mymatch: + counterkeys.remove(counter) + pkgmap[mykey]["protected"].add( + slotmap[myslot][counter]) + + #be pretty and get them in order of merge: + for ckey in counterkeys: + mypkg = slotmap[myslot][ckey] + if mypkg not in all_selected: + pkgmap[mykey]["selected"].add(mypkg) + all_selected.add(mypkg) + # ok, now the last-merged package + # is protected, and the rest are selected + numselected = len(all_selected) + if global_unmerge and not numselected: + portage.writemsg_stdout("\n>>> No outdated packages were found on your system.\n") + return 0 + + if not numselected: + portage.writemsg_stdout( + "\n>>> No packages selected for removal by " + \ + unmerge_action + "\n") + return 0 + finally: + if vdb_lock: + vartree.dbapi.flush_cache() + portage.locks.unlockdir(vdb_lock) + + from portage.sets.base import EditablePackageSet + + # generate a list of package sets that are directly or indirectly listed in "world", + # as there is no persistent list of "installed" sets + installed_sets = ["world"] + stop = False + pos = 0 + while not stop: + stop = True + pos = len(installed_sets) + for s in installed_sets[pos - 1:]: + if s not in sets: + continue + candidates = [x[len(SETPREFIX):] for x in sets[s].getNonAtoms() if x.startswith(SETPREFIX)] + if candidates: + stop = False + installed_sets += candidates + installed_sets = [x for x in installed_sets if x not in root_config.setconfig.active] + del stop, pos + + # we don't want to unmerge packages that are still listed in user-editable package sets + # listed in "world" as they would be remerged on the next update of "world" or the + # relevant package sets. + unknown_sets = set() + for cp in xrange(len(pkgmap)): + for cpv in pkgmap[cp]["selected"].copy(): + try: + pkg = _pkg(cpv) + except KeyError: + # It could have been uninstalled + # by a concurrent process. + continue + + if unmerge_action != "clean" and \ + root_config.root == "/" and \ + portage.match_from_list( + portage.const.PORTAGE_PACKAGE_ATOM, [pkg]): + msg = ("Not unmerging package %s since there is no valid " + \ + "reason for portage to unmerge itself.") % (pkg.cpv,) + for line in textwrap.wrap(msg, 75): + out.eerror(line) + # adjust pkgmap so the display output is correct + pkgmap[cp]["selected"].remove(cpv) + all_selected.remove(cpv) + pkgmap[cp]["protected"].add(cpv) + continue + + parents = [] + for s in installed_sets: + # skip sets that the user requested to unmerge, and skip world + # unless we're unmerging a package set (as the package would be + # removed from "world" later on) + if s in root_config.setconfig.active or (s == "world" and not root_config.setconfig.active): + continue + + if s not in sets: + if s in unknown_sets: + continue + unknown_sets.add(s) + out = portage.output.EOutput() + out.eerror(("Unknown set '@%s' in " + \ + "%svar/lib/portage/world_sets") % \ + (s, root_config.root)) + continue + + # only check instances of EditablePackageSet as other classes are generally used for + # special purposes and can be ignored here (and are usually generated dynamically, so the + # user can't do much about them anyway) + if isinstance(sets[s], EditablePackageSet): + + # This is derived from a snippet of code in the + # depgraph._iter_atoms_for_pkg() method. + for atom in sets[s].iterAtomsForPackage(pkg): + inst_matches = vartree.dbapi.match(atom) + inst_matches.reverse() # descending order + higher_slot = None + for inst_cpv in inst_matches: + try: + inst_pkg = _pkg(inst_cpv) + except KeyError: + # It could have been uninstalled + # by a concurrent process. + continue + + if inst_pkg.cp != atom.cp: + continue + if pkg >= inst_pkg: + # This is descending order, and we're not + # interested in any versions <= pkg given. + break + if pkg.slot_atom != inst_pkg.slot_atom: + higher_slot = inst_pkg + break + if higher_slot is None: + parents.append(s) + break + if parents: + #print colorize("WARN", "Package %s is going to be unmerged," % cpv) + #print colorize("WARN", "but still listed in the following package sets:") + #print " %s\n" % ", ".join(parents) + print colorize("WARN", "Not unmerging package %s as it is" % cpv) + print colorize("WARN", "still referenced by the following package sets:") + print " %s\n" % ", ".join(parents) + # adjust pkgmap so the display output is correct + pkgmap[cp]["selected"].remove(cpv) + all_selected.remove(cpv) + pkgmap[cp]["protected"].add(cpv) + + del installed_sets + + numselected = len(all_selected) + if not numselected: + writemsg_level( + "\n>>> No packages selected for removal by " + \ + unmerge_action + "\n") + return 0 + + # Unmerge order only matters in some cases + if not ordered: + unordered = {} + for d in pkgmap: + selected = d["selected"] + if not selected: + continue + cp = portage.cpv_getkey(iter(selected).next()) + cp_dict = unordered.get(cp) + if cp_dict is None: + cp_dict = {} + unordered[cp] = cp_dict + for k in d: + cp_dict[k] = set() + for k, v in d.iteritems(): + cp_dict[k].update(v) + pkgmap = [unordered[cp] for cp in sorted(unordered)] + + for x in xrange(len(pkgmap)): + selected = pkgmap[x]["selected"] + if not selected: + continue + for mytype, mylist in pkgmap[x].iteritems(): + if mytype == "selected": + continue + mylist.difference_update(all_selected) + cp = portage.cpv_getkey(iter(selected).next()) + for y in localtree.dep_match(cp): + if y not in pkgmap[x]["omitted"] and \ + y not in pkgmap[x]["selected"] and \ + y not in pkgmap[x]["protected"] and \ + y not in all_selected: + pkgmap[x]["omitted"].add(y) + if global_unmerge and not pkgmap[x]["selected"]: + #avoid cluttering the preview printout with stuff that isn't getting unmerged + continue + if not (pkgmap[x]["protected"] or pkgmap[x]["omitted"]) and cp in syslist: + writemsg_level(colorize("BAD","\a\n\n!!! " + \ + "'%s' is part of your system profile.\n" % cp), + level=logging.WARNING, noiselevel=-1) + writemsg_level(colorize("WARN","\a!!! Unmerging it may " + \ + "be damaging to your system.\n\n"), + level=logging.WARNING, noiselevel=-1) + if clean_delay and "--pretend" not in myopts and "--ask" not in myopts: + countdown(int(settings["EMERGE_WARNING_DELAY"]), + colorize("UNMERGE_WARN", "Press Ctrl-C to Stop")) + if not quiet: + writemsg_level("\n %s\n" % (bold(cp),), noiselevel=-1) + else: + writemsg_level(bold(cp) + ": ", noiselevel=-1) + for mytype in ["selected","protected","omitted"]: + if not quiet: + writemsg_level((mytype + ": ").rjust(14), noiselevel=-1) + if pkgmap[x][mytype]: + sorted_pkgs = [portage.catpkgsplit(mypkg)[1:] for mypkg in pkgmap[x][mytype]] + sorted_pkgs.sort(key=cmp_sort_key(portage.pkgcmp)) + for pn, ver, rev in sorted_pkgs: + if rev == "r0": + myversion = ver + else: + myversion = ver + "-" + rev + if mytype == "selected": + writemsg_level( + colorize("UNMERGE_WARN", myversion + " "), + noiselevel=-1) + else: + writemsg_level( + colorize("GOOD", myversion + " "), noiselevel=-1) + else: + writemsg_level("none ", noiselevel=-1) + if not quiet: + writemsg_level("\n", noiselevel=-1) + if quiet: + writemsg_level("\n", noiselevel=-1) + + writemsg_level("\n>>> " + colorize("UNMERGE_WARN", "'Selected'") + \ + " packages are slated for removal.\n") + writemsg_level(">>> " + colorize("GOOD", "'Protected'") + \ + " and " + colorize("GOOD", "'omitted'") + \ + " packages will not be removed.\n\n") + + if "--pretend" in myopts: + #we're done... return + return 0 + if "--ask" in myopts: + if userquery("Would you like to unmerge these packages?")=="No": + # enter pretend mode for correct formatting of results + myopts["--pretend"] = True + print + print "Quitting." + print + return 0 + #the real unmerging begins, after a short delay.... + if clean_delay and not autoclean: + countdown(int(settings["CLEAN_DELAY"]), ">>> Unmerging") + + for x in xrange(len(pkgmap)): + for y in pkgmap[x]["selected"]: + writemsg_level(">>> Unmerging "+y+"...\n", noiselevel=-1) + emergelog(xterm_titles, "=== Unmerging... ("+y+")") + mysplit = y.split("/") + #unmerge... + retval = portage.unmerge(mysplit[0], mysplit[1], settings["ROOT"], + mysettings, unmerge_action not in ["clean","prune"], + vartree=vartree, ldpath_mtimes=ldpath_mtimes, + scheduler=scheduler) + + if retval != os.EX_OK: + emergelog(xterm_titles, " !!! unmerge FAILURE: "+y) + if raise_on_error: + raise UninstallFailure(retval) + sys.exit(retval) + else: + if clean_world and hasattr(sets["world"], "cleanPackage"): + sets["world"].cleanPackage(vartree.dbapi, y) + emergelog(xterm_titles, " >>> unmerge success: "+y) + if clean_world and hasattr(sets["world"], "remove"): + for s in root_config.setconfig.active: + sets["world"].remove(SETPREFIX+s) + return 1 + diff --git a/pym/_emerge/userquery.py b/pym/_emerge/userquery.py new file mode 100644 index 000000000..64a8761e0 --- /dev/null +++ b/pym/_emerge/userquery.py @@ -0,0 +1,44 @@ +import sys + +from portage.output import bold, create_color_func + +def userquery(prompt, responses=None, colours=None): + """Displays a prompt and a set of responses, then waits for a response + which is checked against the responses and the first to match is + returned. An empty response will match the first value in responses. The + input buffer is *not* cleared prior to the prompt! + + prompt: a String. + responses: a List of Strings. + colours: a List of Functions taking and returning a String, used to + process the responses for display. Typically these will be functions + like red() but could be e.g. lambda x: "DisplayString". + If responses is omitted, defaults to ["Yes", "No"], [green, red]. + If only colours is omitted, defaults to [bold, ...]. + + Returns a member of the List responses. (If called without optional + arguments, returns "Yes" or "No".) + KeyboardInterrupt is converted to SystemExit to avoid tracebacks being + printed.""" + if responses is None: + responses = ["Yes", "No"] + colours = [ + create_color_func("PROMPT_CHOICE_DEFAULT"), + create_color_func("PROMPT_CHOICE_OTHER") + ] + elif colours is None: + colours=[bold] + colours=(colours*len(responses))[:len(responses)] + print bold(prompt), + try: + while True: + response=raw_input("["+"/".join([colours[i](responses[i]) for i in range(len(responses))])+"] ") + for key in responses: + # An empty response will match the first value in responses. + if response.upper()==key[:len(response)].upper(): + return key + print "Sorry, response '%s' not understood." % response, + except (EOFError, KeyboardInterrupt): + print "Interrupted." + sys.exit(1) + diff --git a/pym/_emerge/visible.py b/pym/_emerge/visible.py new file mode 100644 index 000000000..5ae04ce03 --- /dev/null +++ b/pym/_emerge/visible.py @@ -0,0 +1,40 @@ +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 visible(pkgsettings, pkg): + """ + Check if a package is visible. This can raise an InvalidDependString + exception if LICENSE is invalid. + TODO: optionally generate a list of masking reasons + @rtype: Boolean + @returns: True if the package is visible, False otherwise. + """ + if not pkg.metadata["SLOT"]: + return False + if not pkg.installed: + if not pkgsettings._accept_chost(pkg.cpv, pkg.metadata): + return False + eapi = pkg.metadata["EAPI"] + if not portage.eapi_is_supported(eapi): + return False + if not pkg.installed: + if portage._eapi_is_deprecated(eapi): + return False + if pkgsettings._getMissingKeywords(pkg.cpv, pkg.metadata): + return False + if pkgsettings._getMaskAtom(pkg.cpv, pkg.metadata): + return False + if pkgsettings._getProfileMaskAtom(pkg.cpv, pkg.metadata): + return False + try: + if pkgsettings._getMissingLicenses(pkg.cpv, pkg.metadata): + return False + except portage.exception.InvalidDependString: + return False + return True + |