| # Copyright 2010-2019 Gentoo Authors |
| # Distributed under the terms of the GNU General Public License v2 |
| |
| import tempfile |
| |
| import portage |
| from portage import os |
| from portage import shutil |
| from portage.const import EBUILD_PHASES |
| from portage.elog import elog_process |
| from portage.package.ebuild.config import config |
| from portage.package.ebuild.doebuild import doebuild_environment |
| from portage.package.ebuild.prepare_build_dirs import prepare_build_dirs |
| from portage.util.futures import asyncio |
| from portage.util._async.SchedulerInterface import SchedulerInterface |
| from portage.util._eventloop.EventLoop import EventLoop |
| from portage.util._eventloop.global_event_loop import global_event_loop |
| from _emerge.CompositeTask import CompositeTask |
| from _emerge.EbuildPhase import EbuildPhase |
| |
| |
| class SpawnNofetchWithoutBuilddir(CompositeTask): |
| """ |
| This spawns pkg_nofetch if appropriate, while avoiding the |
| need to lock a global build directory. The settings parameter |
| is useful only if setcpv has already been called in order |
| to cache metadata. It will be cloned internally, in order to |
| prevent any changes from interfering with the calling code. |
| If settings is None then a suitable config instance will be |
| acquired from the given portdbapi instance. Do not use the |
| settings parameter unless setcpv has been called on the given |
| instance, since otherwise it's possible to trigger issues like |
| bug #408817 due to fragile assumptions involving the config |
| state inside doebuild_environment(). |
| |
| A private PORTAGE_BUILDDIR will be created and cleaned up, in |
| order to avoid any interference with any other processes. |
| If PORTAGE_TMPDIR is writable, that will be used, otherwise |
| the default directory for the tempfile module will be used. |
| |
| We only call the pkg_nofetch phase if either RESTRICT=fetch |
| is set or the package has explicitly overridden the default |
| pkg_nofetch implementation. This allows specialized messages |
| to be displayed for problematic packages even though they do |
| not set RESTRICT=fetch (bug #336499). |
| |
| This class does nothing if the PORTAGE_PARALLEL_FETCHONLY |
| variable is set in the config instance. |
| """ |
| __slots__ = ('ebuild_path', 'fd_pipes', 'portdb', 'settings', |
| '_private_tmpdir') |
| |
| def _start(self): |
| settings = self.settings |
| if settings is None: |
| settings = self.portdb.settings |
| |
| if 'PORTAGE_PARALLEL_FETCHONLY' in settings: |
| # parallel-fetch mode |
| self.returncode = os.EX_OK |
| self._async_wait() |
| return |
| |
| # Prevent temporary config changes from interfering |
| # with config instances that are reused. |
| settings = self.settings = config(clone=settings) |
| |
| # We must create our private PORTAGE_TMPDIR before calling |
| # doebuild_environment(), since lots of variables such |
| # as PORTAGE_BUILDDIR refer to paths inside PORTAGE_TMPDIR. |
| portage_tmpdir = settings.get('PORTAGE_TMPDIR') |
| if not portage_tmpdir or not os.access(portage_tmpdir, os.W_OK): |
| portage_tmpdir = None |
| private_tmpdir = self._private_tmpdir = tempfile.mkdtemp( |
| dir=portage_tmpdir) |
| settings['PORTAGE_TMPDIR'] = private_tmpdir |
| settings.backup_changes('PORTAGE_TMPDIR') |
| # private temp dir was just created, so it's not locked yet |
| settings.pop('PORTAGE_BUILDDIR_LOCKED', None) |
| |
| doebuild_environment(self.ebuild_path, 'nofetch', |
| settings=settings, db=self.portdb) |
| restrict = settings['PORTAGE_RESTRICT'].split() |
| defined_phases = settings['DEFINED_PHASES'].split() |
| if not defined_phases: |
| # When DEFINED_PHASES is undefined, assume all |
| # phases are defined. |
| defined_phases = EBUILD_PHASES |
| |
| if 'fetch' not in restrict and \ |
| 'nofetch' not in defined_phases: |
| self.returncode = os.EX_OK |
| self._async_wait() |
| return |
| |
| prepare_build_dirs(settings=settings) |
| |
| ebuild_phase = EbuildPhase(background=self.background, |
| phase='nofetch', |
| scheduler=self.scheduler, |
| fd_pipes=self.fd_pipes, settings=settings) |
| |
| self._start_task(ebuild_phase, self._nofetch_exit) |
| |
| def _nofetch_exit(self, ebuild_phase): |
| self._final_exit(ebuild_phase) |
| elog_process(self.settings.mycpv, self.settings) |
| shutil.rmtree(self._private_tmpdir) |
| self._async_wait() |
| |
| |
| def spawn_nofetch(portdb, ebuild_path, settings=None, fd_pipes=None): |
| """ |
| Create a NofetchPrivateTmpdir instance, and execute it synchronously. |
| This function must not be called from asynchronous code, since it will |
| trigger event loop recursion which is incompatible with asyncio. |
| """ |
| nofetch = SpawnNofetchWithoutBuilddir(background=False, |
| portdb=portdb, |
| ebuild_path=ebuild_path, |
| scheduler=SchedulerInterface(asyncio._safe_loop()), |
| fd_pipes=fd_pipes, settings=settings) |
| |
| nofetch.start() |
| return nofetch.wait() |