1 |
commit: 285d5d038d8bb8a17d853816e156147c8c59f248 |
2 |
Author: Zac Medico <zmedico <AT> gentoo <DOT> org> |
3 |
AuthorDate: Mon Apr 3 00:40:55 2017 +0000 |
4 |
Commit: Zac Medico <zmedico <AT> gentoo <DOT> org> |
5 |
CommitDate: Tue Apr 4 03:19:29 2017 +0000 |
6 |
URL: https://gitweb.gentoo.org/proj/portage.git/commit/?id=285d5d03 |
7 |
|
8 |
EbuildBuild: async spawn_nofetch in _fetchonly_exit (bug 614116) |
9 |
|
10 |
Replace a synchronous spawn_nofetch call with an asynchronous one, |
11 |
in order to avoid event loop recursion which is not compatible with |
12 |
asyncio. This involves refactoring of spawn_nofetch to provide an |
13 |
asynchronous interface. |
14 |
|
15 |
X-Gentoo-bug: 614116 |
16 |
X-Gentoo-bug-url: https://bugs.gentoo.org/show_bug.cgi?id=614116 |
17 |
Acked-by: Brian Dolbec <dolsen <AT> gentoo.org> |
18 |
|
19 |
pym/_emerge/EbuildBuild.py | 18 ++++- |
20 |
pym/portage/package/ebuild/_spawn_nofetch.py | 106 +++++++++++++++++---------- |
21 |
2 files changed, 85 insertions(+), 39 deletions(-) |
22 |
|
23 |
diff --git a/pym/_emerge/EbuildBuild.py b/pym/_emerge/EbuildBuild.py |
24 |
index 11eb1c93e..48f470483 100644 |
25 |
--- a/pym/_emerge/EbuildBuild.py |
26 |
+++ b/pym/_emerge/EbuildBuild.py |
27 |
@@ -22,7 +22,7 @@ import portage |
28 |
from portage import _encodings, _unicode_decode, _unicode_encode, os |
29 |
from portage.package.ebuild.digestcheck import digestcheck |
30 |
from portage.package.ebuild.doebuild import _check_temp_dir |
31 |
-from portage.package.ebuild._spawn_nofetch import spawn_nofetch |
32 |
+from portage.package.ebuild._spawn_nofetch import SpawnNofetchWithoutBuilddir |
33 |
|
34 |
class EbuildBuild(CompositeTask): |
35 |
|
36 |
@@ -165,8 +165,22 @@ class EbuildBuild(CompositeTask): |
37 |
def _fetchonly_exit(self, fetcher): |
38 |
self._final_exit(fetcher) |
39 |
if self.returncode != os.EX_OK: |
40 |
+ self.returncode = None |
41 |
portdb = self.pkg.root_config.trees[self._tree].dbapi |
42 |
- spawn_nofetch(portdb, self._ebuild_path, settings=self.settings) |
43 |
+ self._start_task(SpawnNofetchWithoutBuilddir( |
44 |
+ background=self.background, |
45 |
+ portdb=portdb, |
46 |
+ ebuild_path=self._ebuild_path, |
47 |
+ scheduler=self.scheduler, |
48 |
+ settings=self.settings), |
49 |
+ self._nofetch_without_builddir_exit) |
50 |
+ return |
51 |
+ |
52 |
+ self.wait() |
53 |
+ |
54 |
+ def _nofetch_without_builddir_exit(self, nofetch): |
55 |
+ self._final_exit(nofetch) |
56 |
+ self.returncode = 1 |
57 |
self.wait() |
58 |
|
59 |
def _pre_clean_exit(self, pre_clean_phase): |
60 |
|
61 |
diff --git a/pym/portage/package/ebuild/_spawn_nofetch.py b/pym/portage/package/ebuild/_spawn_nofetch.py |
62 |
index 0fc53c8ca..bbfd5b72b 100644 |
63 |
--- a/pym/portage/package/ebuild/_spawn_nofetch.py |
64 |
+++ b/pym/portage/package/ebuild/_spawn_nofetch.py |
65 |
@@ -14,11 +14,14 @@ from portage.package.ebuild.prepare_build_dirs import prepare_build_dirs |
66 |
from portage.util._async.SchedulerInterface import SchedulerInterface |
67 |
from portage.util._eventloop.EventLoop import EventLoop |
68 |
from portage.util._eventloop.global_event_loop import global_event_loop |
69 |
+from _emerge.CompositeTask import CompositeTask |
70 |
from _emerge.EbuildPhase import EbuildPhase |
71 |
|
72 |
-def spawn_nofetch(portdb, ebuild_path, settings=None, fd_pipes=None): |
73 |
+ |
74 |
+class SpawnNofetchWithoutBuilddir(CompositeTask): |
75 |
""" |
76 |
- This spawns pkg_nofetch if appropriate. The settings parameter |
77 |
+ This spawns pkg_nofetch if appropriate, while avoiding the |
78 |
+ need to lock a global build directory. The settings parameter |
79 |
is useful only if setcpv has already been called in order |
80 |
to cache metadata. It will be cloned internally, in order to |
81 |
prevent any changes from interfering with the calling code. |
82 |
@@ -40,33 +43,42 @@ def spawn_nofetch(portdb, ebuild_path, settings=None, fd_pipes=None): |
83 |
to be displayed for problematic packages even though they do |
84 |
not set RESTRICT=fetch (bug #336499). |
85 |
|
86 |
- This function does nothing if the PORTAGE_PARALLEL_FETCHONLY |
87 |
+ This class does nothing if the PORTAGE_PARALLEL_FETCHONLY |
88 |
variable is set in the config instance. |
89 |
""" |
90 |
+ __slots__ = ('ebuild_path', 'fd_pipes', 'portdb', 'settings', |
91 |
+ '_private_tmpdir') |
92 |
+ |
93 |
+ def _start(self): |
94 |
+ settings = self.settings |
95 |
+ if settings is None: |
96 |
+ settings = self.portdb.settings |
97 |
+ |
98 |
+ if 'PORTAGE_PARALLEL_FETCHONLY' in settings: |
99 |
+ # parallel-fetch mode |
100 |
+ self.returncode = os.EX_OK |
101 |
+ self._async_wait() |
102 |
+ return |
103 |
|
104 |
- if settings is None: |
105 |
- settings = config(clone=portdb.settings) |
106 |
- else: |
107 |
- settings = config(clone=settings) |
108 |
- |
109 |
- if 'PORTAGE_PARALLEL_FETCHONLY' in settings: |
110 |
- return os.EX_OK |
111 |
- |
112 |
- # We must create our private PORTAGE_TMPDIR before calling |
113 |
- # doebuild_environment(), since lots of variables such |
114 |
- # as PORTAGE_BUILDDIR refer to paths inside PORTAGE_TMPDIR. |
115 |
- portage_tmpdir = settings.get('PORTAGE_TMPDIR') |
116 |
- if not portage_tmpdir or not os.access(portage_tmpdir, os.W_OK): |
117 |
- portage_tmpdir = None |
118 |
- private_tmpdir = tempfile.mkdtemp(dir=portage_tmpdir) |
119 |
- settings['PORTAGE_TMPDIR'] = private_tmpdir |
120 |
- settings.backup_changes('PORTAGE_TMPDIR') |
121 |
- # private temp dir was just created, so it's not locked yet |
122 |
- settings.pop('PORTAGE_BUILDDIR_LOCKED', None) |
123 |
- |
124 |
- try: |
125 |
- doebuild_environment(ebuild_path, 'nofetch', |
126 |
- settings=settings, db=portdb) |
127 |
+ # Prevent temporary config changes from interfering |
128 |
+ # with config instances that are reused. |
129 |
+ settings = self.settings = config(clone=settings) |
130 |
+ |
131 |
+ # We must create our private PORTAGE_TMPDIR before calling |
132 |
+ # doebuild_environment(), since lots of variables such |
133 |
+ # as PORTAGE_BUILDDIR refer to paths inside PORTAGE_TMPDIR. |
134 |
+ portage_tmpdir = settings.get('PORTAGE_TMPDIR') |
135 |
+ if not portage_tmpdir or not os.access(portage_tmpdir, os.W_OK): |
136 |
+ portage_tmpdir = None |
137 |
+ private_tmpdir = self._private_tmpdir = tempfile.mkdtemp( |
138 |
+ dir=portage_tmpdir) |
139 |
+ settings['PORTAGE_TMPDIR'] = private_tmpdir |
140 |
+ settings.backup_changes('PORTAGE_TMPDIR') |
141 |
+ # private temp dir was just created, so it's not locked yet |
142 |
+ settings.pop('PORTAGE_BUILDDIR_LOCKED', None) |
143 |
+ |
144 |
+ doebuild_environment(self.ebuild_path, 'nofetch', |
145 |
+ settings=settings, db=self.portdb) |
146 |
restrict = settings['PORTAGE_RESTRICT'].split() |
147 |
defined_phases = settings['DEFINED_PHASES'].split() |
148 |
if not defined_phases: |
149 |
@@ -76,18 +88,38 @@ def spawn_nofetch(portdb, ebuild_path, settings=None, fd_pipes=None): |
150 |
|
151 |
if 'fetch' not in restrict and \ |
152 |
'nofetch' not in defined_phases: |
153 |
- return os.EX_OK |
154 |
+ self.returncode = os.EX_OK |
155 |
+ self._async_wait() |
156 |
+ return |
157 |
|
158 |
prepare_build_dirs(settings=settings) |
159 |
- ebuild_phase = EbuildPhase(background=False, |
160 |
+ |
161 |
+ ebuild_phase = EbuildPhase(background=self.background, |
162 |
phase='nofetch', |
163 |
- scheduler=SchedulerInterface(portage._internal_caller and |
164 |
+ scheduler=self.scheduler, |
165 |
+ fd_pipes=self.fd_pipes, settings=settings) |
166 |
+ |
167 |
+ self._start_task(ebuild_phase, self._nofetch_exit) |
168 |
+ |
169 |
+ def _nofetch_exit(self, ebuild_phase): |
170 |
+ self._final_exit(ebuild_phase) |
171 |
+ elog_process(self.settings.mycpv, self.settings) |
172 |
+ shutil.rmtree(self._private_tmpdir) |
173 |
+ self._async_wait() |
174 |
+ |
175 |
+ |
176 |
+def spawn_nofetch(portdb, ebuild_path, settings=None, fd_pipes=None): |
177 |
+ """ |
178 |
+ Create a NofetchPrivateTmpdir instance, and execute it synchronously. |
179 |
+ This function must not be called from asynchronous code, since it will |
180 |
+ trigger event loop recursion which is incompatible with asyncio. |
181 |
+ """ |
182 |
+ nofetch = SpawnNofetchWithoutBuilddir(background=False, |
183 |
+ portdb=portdb, |
184 |
+ ebuild_path=ebuild_path, |
185 |
+ scheduler=SchedulerInterface(portage._internal_caller and |
186 |
global_event_loop() or EventLoop(main=False)), |
187 |
- fd_pipes=fd_pipes, settings=settings) |
188 |
- ebuild_phase.start() |
189 |
- ebuild_phase.wait() |
190 |
- elog_process(settings.mycpv, settings) |
191 |
- finally: |
192 |
- shutil.rmtree(private_tmpdir) |
193 |
- |
194 |
- return ebuild_phase.returncode |
195 |
+ fd_pipes=fd_pipes, settings=settings) |
196 |
+ |
197 |
+ nofetch.start() |
198 |
+ return nofetch.wait() |