diff options
author | Zac Medico <zmedico@gentoo.org> | 2017-04-02 17:40:55 -0700 |
---|---|---|
committer | Zac Medico <zmedico@gentoo.org> | 2017-04-03 20:19:29 -0700 |
commit | 285d5d038d8bb8a17d853816e156147c8c59f248 (patch) | |
tree | e33863e4c73708df1eb9b6f708957e458d7cc561 | |
parent | EbuildBuild: eliminate call to digestgen (bug 614116) (diff) | |
download | portage-285d5d038d8bb8a17d853816e156147c8c59f248.tar.gz portage-285d5d038d8bb8a17d853816e156147c8c59f248.tar.bz2 portage-285d5d038d8bb8a17d853816e156147c8c59f248.zip |
EbuildBuild: async spawn_nofetch in _fetchonly_exit (bug 614116)
Replace a synchronous spawn_nofetch call with an asynchronous one,
in order to avoid event loop recursion which is not compatible with
asyncio. This involves refactoring of spawn_nofetch to provide an
asynchronous interface.
X-Gentoo-bug: 614116
X-Gentoo-bug-url: https://bugs.gentoo.org/show_bug.cgi?id=614116
Acked-by: Brian Dolbec <dolsen@gentoo.org>
-rw-r--r-- | pym/_emerge/EbuildBuild.py | 18 | ||||
-rw-r--r-- | pym/portage/package/ebuild/_spawn_nofetch.py | 106 |
2 files changed, 85 insertions, 39 deletions
diff --git a/pym/_emerge/EbuildBuild.py b/pym/_emerge/EbuildBuild.py index 11eb1c93e..48f470483 100644 --- a/pym/_emerge/EbuildBuild.py +++ b/pym/_emerge/EbuildBuild.py @@ -22,7 +22,7 @@ import portage from portage import _encodings, _unicode_decode, _unicode_encode, os from portage.package.ebuild.digestcheck import digestcheck from portage.package.ebuild.doebuild import _check_temp_dir -from portage.package.ebuild._spawn_nofetch import spawn_nofetch +from portage.package.ebuild._spawn_nofetch import SpawnNofetchWithoutBuilddir class EbuildBuild(CompositeTask): @@ -165,8 +165,22 @@ class EbuildBuild(CompositeTask): def _fetchonly_exit(self, fetcher): self._final_exit(fetcher) if self.returncode != os.EX_OK: + self.returncode = None portdb = self.pkg.root_config.trees[self._tree].dbapi - spawn_nofetch(portdb, self._ebuild_path, settings=self.settings) + self._start_task(SpawnNofetchWithoutBuilddir( + background=self.background, + portdb=portdb, + ebuild_path=self._ebuild_path, + scheduler=self.scheduler, + settings=self.settings), + self._nofetch_without_builddir_exit) + return + + self.wait() + + def _nofetch_without_builddir_exit(self, nofetch): + self._final_exit(nofetch) + self.returncode = 1 self.wait() def _pre_clean_exit(self, pre_clean_phase): diff --git a/pym/portage/package/ebuild/_spawn_nofetch.py b/pym/portage/package/ebuild/_spawn_nofetch.py index 0fc53c8ca..bbfd5b72b 100644 --- a/pym/portage/package/ebuild/_spawn_nofetch.py +++ b/pym/portage/package/ebuild/_spawn_nofetch.py @@ -14,11 +14,14 @@ from portage.package.ebuild.prepare_build_dirs import prepare_build_dirs 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 -def spawn_nofetch(portdb, ebuild_path, settings=None, fd_pipes=None): + +class SpawnNofetchWithoutBuilddir(CompositeTask): """ - This spawns pkg_nofetch if appropriate. The settings parameter + 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. @@ -40,33 +43,42 @@ def spawn_nofetch(portdb, ebuild_path, settings=None, fd_pipes=None): to be displayed for problematic packages even though they do not set RESTRICT=fetch (bug #336499). - This function does nothing if the PORTAGE_PARALLEL_FETCHONLY + 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 - if settings is None: - settings = config(clone=portdb.settings) - else: - settings = config(clone=settings) - - if 'PORTAGE_PARALLEL_FETCHONLY' in settings: - return os.EX_OK - - # 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 = 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) - - try: - doebuild_environment(ebuild_path, 'nofetch', - settings=settings, db=portdb) + # 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: @@ -76,18 +88,38 @@ def spawn_nofetch(portdb, ebuild_path, settings=None, fd_pipes=None): if 'fetch' not in restrict and \ 'nofetch' not in defined_phases: - return os.EX_OK + self.returncode = os.EX_OK + self._async_wait() + return prepare_build_dirs(settings=settings) - ebuild_phase = EbuildPhase(background=False, + + ebuild_phase = EbuildPhase(background=self.background, phase='nofetch', - scheduler=SchedulerInterface(portage._internal_caller and + 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(portage._internal_caller and global_event_loop() or EventLoop(main=False)), - fd_pipes=fd_pipes, settings=settings) - ebuild_phase.start() - ebuild_phase.wait() - elog_process(settings.mycpv, settings) - finally: - shutil.rmtree(private_tmpdir) - - return ebuild_phase.returncode + fd_pipes=fd_pipes, settings=settings) + + nofetch.start() + return nofetch.wait() |