# Copyright 1999-2018 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 import portage from portage import os from portage.dep import _repo_separator from _emerge.EbuildMetadataPhase import EbuildMetadataPhase from portage.cache.cache_errors import CacheError from portage.util._async.AsyncScheduler import AsyncScheduler class MetadataRegen(AsyncScheduler): def __init__(self, portdb, cp_iter=None, consumer=None, write_auxdb=True, **kwargs): AsyncScheduler.__init__(self, **kwargs) self._portdb = portdb self._write_auxdb = write_auxdb 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 self._valid_pkgs = set() self._cp_set = set() self._process_iter = self._iter_metadata_processes() self._running_tasks = set() def _next_task(self): return next(self._process_iter) def _iter_every_cp(self): # List categories individually, in order to start yielding quicker, # and in order to reduce latency in case of a signal interrupt. cp_all = self._portdb.cp_all for category in sorted(self._portdb.categories): for cp in cp_all(categories=(category,)): yield cp def _iter_metadata_processes(self): portdb = self._portdb valid_pkgs = self._valid_pkgs cp_set = self._cp_set consumer = self._consumer portage.writemsg_stdout("Regenerating cache entries...\n") for cp in self._cp_iter: if self._terminated.is_set(): break cp_set.add(cp) portage.writemsg_stdout("Processing %s\n" % cp) # We iterate over portdb.porttrees, since it's common to # tweak this attribute in order to adjust repo selection. for mytree in portdb.porttrees: repo = portdb.repositories.get_repo_for_location(mytree) cpv_list = portdb.cp_list(cp, mytree=[repo.location]) for cpv in cpv_list: if self._terminated.is_set(): break valid_pkgs.add(cpv) ebuild_path, repo_path = portdb.findname2(cpv, myrepo=repo.name) if ebuild_path is None: raise AssertionError("ebuild not found for '%s%s%s'" % (cpv, _repo_separator, repo.name)) metadata, ebuild_hash = portdb._pull_valid_cache( cpv, ebuild_path, repo_path) if metadata is not None: if consumer is not None: consumer(cpv, repo_path, metadata, ebuild_hash, True) continue yield EbuildMetadataPhase(cpv=cpv, ebuild_hash=ebuild_hash, portdb=portdb, repo_path=repo_path, settings=portdb.doebuild_settings, write_auxdb=self._write_auxdb) def _cleanup(self): super(MetadataRegen, self)._cleanup() portdb = self._portdb dead_nodes = {} if self._terminated.is_set(): portdb.flush_cache() return if self._global_cleanse: for mytree in portdb.porttrees: try: dead_nodes[mytree] = set(portdb.auxdb[mytree]) except CacheError as 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] \ if cpv_getkey(cpv) in cp_set) except CacheError as 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.items(): auxdb = portdb.auxdb[mytree] for y in nodes: try: del auxdb[y] except (KeyError, CacheError): pass portdb.flush_cache() def _task_exit(self, metadata_process): if metadata_process.returncode != os.EX_OK: self._valid_pkgs.discard(metadata_process.cpv) if not self._terminated_tasks: 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.repo_path, metadata_process.metadata, metadata_process.ebuild_hash, metadata_process.eapi_supported) AsyncScheduler._task_exit(self, metadata_process)