aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZac Medico <zmedico@gentoo.org>2017-04-02 17:40:55 -0700
committerZac Medico <zmedico@gentoo.org>2017-04-03 20:19:29 -0700
commit285d5d038d8bb8a17d853816e156147c8c59f248 (patch)
treee33863e4c73708df1eb9b6f708957e458d7cc561
parentEbuildBuild: eliminate call to digestgen (bug 614116) (diff)
downloadportage-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.py18
-rw-r--r--pym/portage/package/ebuild/_spawn_nofetch.py106
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()