| # Copyright 1999-2020 Gentoo Authors |
| # Distributed under the terms of the GNU General Public License v2 |
| |
| from _emerge.EbuildMetadataPhase import EbuildMetadataPhase |
| |
| import portage |
| from portage import os |
| from portage.cache.cache_errors import CacheError |
| from portage.dep import _repo_separator |
| 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) |