1 |
Author: zmedico |
2 |
Date: 2008-06-29 14:51:05 +0000 (Sun, 29 Jun 2008) |
3 |
New Revision: 10849 |
4 |
|
5 |
Modified: |
6 |
main/trunk/pym/_emerge/__init__.py |
7 |
main/trunk/pym/portage/__init__.py |
8 |
Log: |
9 |
* Add "fd_pipes" and "returnpid" parameters to doebuild() and pass |
10 |
these into spawn calls, enabling ebuild processes to execute |
11 |
asynchronously. |
12 |
|
13 |
* Add a EbuildPhase class that's derived from the pty logging code |
14 |
inside portage.spawn(). |
15 |
|
16 |
* Integrate post-phase code from spawnebuild() into EbuildBuild.execute() |
17 |
so that it still gets called even though doebuild() calls execute |
18 |
asynchronously. |
19 |
|
20 |
|
21 |
Modified: main/trunk/pym/_emerge/__init__.py |
22 |
=================================================================== |
23 |
--- main/trunk/pym/_emerge/__init__.py 2008-06-29 09:38:16 UTC (rev 10848) |
24 |
+++ main/trunk/pym/_emerge/__init__.py 2008-06-29 14:51:05 UTC (rev 10849) |
25 |
@@ -1541,22 +1541,184 @@ |
26 |
root_config = self.pkg.root_config |
27 |
portdb = root_config.trees["porttree"].dbapi |
28 |
ebuild_path = portdb.findname(self.pkg.cpv) |
29 |
- debug = self.settings.get("PORTAGE_DEBUG") == "1" |
30 |
+ settings = self.settings |
31 |
+ debug = settings.get("PORTAGE_DEBUG") == "1" |
32 |
+ cleanup = 1 |
33 |
|
34 |
retval = portage.doebuild(ebuild_path, "clean", |
35 |
- root_config.root, self.settings, debug, cleanup=1, |
36 |
+ root_config.root, settings, debug, cleanup=cleanup, |
37 |
mydbapi=portdb, tree="porttree") |
38 |
if retval != os.EX_OK: |
39 |
return retval |
40 |
|
41 |
+ # This initializes PORTAGE_LOG_FILE. |
42 |
+ portage.prepare_build_dirs(root_config.root, settings, cleanup) |
43 |
+ |
44 |
+ fd_pipes = { |
45 |
+ 0 : sys.stdin.fileno(), |
46 |
+ 1 : sys.stdout.fileno(), |
47 |
+ 2 : sys.stderr.fileno(), |
48 |
+ } |
49 |
+ |
50 |
for mydo in self._phases: |
51 |
- retval = portage.doebuild(ebuild_path, mydo, |
52 |
- root_config.root, self.settings, debug, |
53 |
- mydbapi=portdb, tree="porttree") |
54 |
+ ebuild_phase = EbuildPhase(fd_pipes=fd_pipes, |
55 |
+ pkg=self.pkg, phase=mydo, settings=settings) |
56 |
+ ebuild_phase.start() |
57 |
+ ebuild_phase._output_handler() |
58 |
+ retval = ebuild_phase.wait() |
59 |
+ |
60 |
+ portage._post_phase_userpriv_perms(settings) |
61 |
+ if mydo == "install": |
62 |
+ portage._check_build_log(settings) |
63 |
+ if retval == os.EX_OK: |
64 |
+ retval = portage._post_src_install_checks(settings) |
65 |
+ |
66 |
if retval != os.EX_OK: |
67 |
return retval |
68 |
+ |
69 |
return os.EX_OK |
70 |
|
71 |
+class EbuildPhase(SlotObject): |
72 |
+ |
73 |
+ __slots__ = ("fd_pipes", "phase", "pkg", "settings", |
74 |
+ "pid", "returncode", "files") |
75 |
+ |
76 |
+ _file_names = ("log", "stdout", "ebuild") |
77 |
+ _files_dict = slot_dict_class(_file_names) |
78 |
+ _bufsize = 4096 |
79 |
+ |
80 |
+ def start(self): |
81 |
+ root_config = self.pkg.root_config |
82 |
+ portdb = root_config.trees["porttree"].dbapi |
83 |
+ ebuild_path = portdb.findname(self.pkg.cpv) |
84 |
+ settings = self.settings |
85 |
+ debug = settings.get("PORTAGE_DEBUG") == "1" |
86 |
+ logfile = settings.get("PORTAGE_LOG_FILE") |
87 |
+ master_fd = None |
88 |
+ slave_fd = None |
89 |
+ fd_pipes = self.fd_pipes.copy() |
90 |
+ |
91 |
+ # flush any pending output |
92 |
+ for fd in fd_pipes.itervalues(): |
93 |
+ if fd == sys.stdout.fileno(): |
94 |
+ sys.stdout.flush() |
95 |
+ if fd == sys.stderr.fileno(): |
96 |
+ sys.stderr.flush() |
97 |
+ |
98 |
+ fd_pipes_orig = None |
99 |
+ self.files = self._files_dict() |
100 |
+ files = self.files |
101 |
+ got_pty = False |
102 |
+ |
103 |
+ portage._doebuild_exit_status_unlink( |
104 |
+ settings.get("EBUILD_EXIT_STATUS_FILE")) |
105 |
+ |
106 |
+ if logfile: |
107 |
+ if portage._disable_openpty: |
108 |
+ master_fd, slave_fd = os.pipe() |
109 |
+ else: |
110 |
+ from pty import openpty |
111 |
+ try: |
112 |
+ master_fd, slave_fd = openpty() |
113 |
+ got_pty = True |
114 |
+ except EnvironmentError, e: |
115 |
+ portage._disable_openpty = True |
116 |
+ portage.writemsg("openpty failed: '%s'\n" % str(e), |
117 |
+ noiselevel=-1) |
118 |
+ del e |
119 |
+ master_fd, slave_fd = os.pipe() |
120 |
+ |
121 |
+ if got_pty: |
122 |
+ # Disable post-processing of output since otherwise weird |
123 |
+ # things like \n -> \r\n transformations may occur. |
124 |
+ import termios |
125 |
+ mode = termios.tcgetattr(slave_fd) |
126 |
+ mode[1] &= ~termios.OPOST |
127 |
+ termios.tcsetattr(slave_fd, termios.TCSANOW, mode) |
128 |
+ |
129 |
+ import fcntl |
130 |
+ fcntl.fcntl(master_fd, fcntl.F_SETFL, |
131 |
+ fcntl.fcntl(master_fd, fcntl.F_GETFL) | os.O_NONBLOCK) |
132 |
+ |
133 |
+ fd_pipes.setdefault(0, sys.stdin.fileno()) |
134 |
+ fd_pipes_orig = fd_pipes.copy() |
135 |
+ if got_pty and os.isatty(fd_pipes_orig[1]): |
136 |
+ from portage.output import get_term_size, set_term_size |
137 |
+ rows, columns = get_term_size() |
138 |
+ set_term_size(rows, columns, slave_fd) |
139 |
+ fd_pipes[0] = fd_pipes_orig[0] |
140 |
+ fd_pipes[1] = slave_fd |
141 |
+ fd_pipes[2] = slave_fd |
142 |
+ |
143 |
+ retval = portage.doebuild(ebuild_path, self.phase, |
144 |
+ root_config.root, settings, debug, |
145 |
+ mydbapi=portdb, tree="porttree", |
146 |
+ fd_pipes=fd_pipes, returnpid=True) |
147 |
+ |
148 |
+ self.pid = retval[0] |
149 |
+ |
150 |
+ if logfile: |
151 |
+ os.close(slave_fd) |
152 |
+ files["log"] = open(logfile, 'a') |
153 |
+ files["stdout"] = os.fdopen(os.dup(fd_pipes_orig[1]), 'w') |
154 |
+ files["ebuild"] = os.fdopen(master_fd, 'r') |
155 |
+ |
156 |
+ def _output_handler(self): |
157 |
+ log_file = self.files.get("log") |
158 |
+ if log_file is None: |
159 |
+ return |
160 |
+ ebuild_file = self.files["ebuild"] |
161 |
+ stdout_file = self.files["stdout"] |
162 |
+ iwtd = [ebuild_file] |
163 |
+ owtd = [] |
164 |
+ ewtd = [] |
165 |
+ import array, select |
166 |
+ buffsize = self._bufsize |
167 |
+ eof = False |
168 |
+ while not eof: |
169 |
+ events = select.select(iwtd, owtd, ewtd) |
170 |
+ for f in events[0]: |
171 |
+ # Use non-blocking mode to prevent read |
172 |
+ # calls from blocking indefinitely. |
173 |
+ buf = array.array('B') |
174 |
+ try: |
175 |
+ buf.fromfile(f, buffsize) |
176 |
+ except EOFError: |
177 |
+ pass |
178 |
+ if not buf: |
179 |
+ eof = True |
180 |
+ break |
181 |
+ if f is ebuild_file: |
182 |
+ buf.tofile(stdout_file) |
183 |
+ stdout_file.flush() |
184 |
+ buf.tofile(log_file) |
185 |
+ log_file.flush() |
186 |
+ log_file.close() |
187 |
+ stdout_file.close() |
188 |
+ ebuild_file.close() |
189 |
+ |
190 |
+ def wait(self): |
191 |
+ pid = self.pid |
192 |
+ retval = os.waitpid(pid, 0)[1] |
193 |
+ portage.process.spawned_pids.remove(pid) |
194 |
+ if retval != os.EX_OK: |
195 |
+ if retval & 0xff: |
196 |
+ retval = (retval & 0xff) << 8 |
197 |
+ else: |
198 |
+ retval = retval >> 8 |
199 |
+ |
200 |
+ msg = portage._doebuild_exit_status_check( |
201 |
+ self.phase, self.settings) |
202 |
+ if msg: |
203 |
+ retval = 1 |
204 |
+ from textwrap import wrap |
205 |
+ from portage.elog.messages import eerror |
206 |
+ for l in wrap(msg, 72): |
207 |
+ eerror(l, phase=self.phase, key=self.pkg.cpv) |
208 |
+ |
209 |
+ self.returncode = retval |
210 |
+ return self.returncode |
211 |
+ |
212 |
class EbuildBinpkg(Task): |
213 |
""" |
214 |
This assumes that src_install() has successfully completed. |
215 |
|
216 |
Modified: main/trunk/pym/portage/__init__.py |
217 |
=================================================================== |
218 |
--- main/trunk/pym/portage/__init__.py 2008-06-29 09:38:16 UTC (rev 10848) |
219 |
+++ main/trunk/pym/portage/__init__.py 2008-06-29 14:51:05 UTC (rev 10849) |
220 |
@@ -2940,6 +2940,10 @@ |
221 |
env=mysettings.environ() |
222 |
keywords["opt_name"]="[%s]" % mysettings["PF"] |
223 |
|
224 |
+ if "EMERGE_FROM" in mysettings: |
225 |
+ # emerge handles logging externally |
226 |
+ keywords.pop("logfile", None) |
227 |
+ |
228 |
fd_pipes = keywords.get("fd_pipes") |
229 |
if fd_pipes is None: |
230 |
fd_pipes = { |
231 |
@@ -4160,12 +4164,15 @@ |
232 |
return 1 |
233 |
|
234 |
# parse actionmap to spawn ebuild with the appropriate args |
235 |
-def spawnebuild(mydo,actionmap,mysettings,debug,alwaysdep=0,logfile=None): |
236 |
+def spawnebuild(mydo, actionmap, mysettings, debug, alwaysdep=0, |
237 |
+ logfile=None, fd_pipes=None, returnpid=False): |
238 |
if "EMERGE_FROM" not in mysettings and \ |
239 |
(alwaysdep or "noauto" not in mysettings.features): |
240 |
# process dependency first |
241 |
if "dep" in actionmap[mydo]: |
242 |
- retval=spawnebuild(actionmap[mydo]["dep"],actionmap,mysettings,debug,alwaysdep=alwaysdep,logfile=logfile) |
243 |
+ retval = spawnebuild(actionmap[mydo]["dep"], actionmap, |
244 |
+ mysettings, debug, alwaysdep=alwaysdep, logfile=logfile, |
245 |
+ fd_pipes=fd_pipes, returnpid=returnpid) |
246 |
if retval: |
247 |
return retval |
248 |
kwargs = actionmap[mydo]["args"] |
249 |
@@ -4177,10 +4184,13 @@ |
250 |
mysettings._filter_calling_env = True |
251 |
try: |
252 |
phase_retval = spawn(actionmap[mydo]["cmd"] % mydo, |
253 |
- mysettings, debug=debug, logfile=logfile, **kwargs) |
254 |
+ mysettings, debug=debug, logfile=logfile, |
255 |
+ fd_pipes=fd_pipes, returnpid=returnpid, **kwargs) |
256 |
finally: |
257 |
mysettings["EBUILD_PHASE"] = "" |
258 |
mysettings._filter_calling_env = filter_calling_env_state |
259 |
+ if returnpid: |
260 |
+ return phase_retval |
261 |
msg = _doebuild_exit_status_check(mydo, mysettings) |
262 |
if msg: |
263 |
phase_retval = 1 |
264 |
@@ -4189,28 +4199,30 @@ |
265 |
for l in wrap(msg, 72): |
266 |
eerror(l, phase=mydo, key=mysettings.mycpv) |
267 |
|
268 |
- if "userpriv" in mysettings.features and \ |
269 |
- not kwargs["droppriv"] and secpass >= 2: |
270 |
+ _post_phase_userpriv_perms(mysettings) |
271 |
+ if mydo == "install": |
272 |
+ _check_build_log(mysettings) |
273 |
+ if phase_retval == os.EX_OK: |
274 |
+ phase_retval = _post_src_install_checks(mysettings) |
275 |
+ return phase_retval |
276 |
+ |
277 |
+def _post_phase_userpriv_perms(mysettings): |
278 |
+ if "userpriv" in mysettings.features and secpass >= 2: |
279 |
""" Privileged phases may have left files that need to be made |
280 |
writable to a less privileged user.""" |
281 |
apply_recursive_permissions(mysettings["T"], |
282 |
uid=portage_uid, gid=portage_gid, dirmode=070, dirmask=0, |
283 |
filemode=060, filemask=0) |
284 |
|
285 |
- if phase_retval == os.EX_OK: |
286 |
- if mydo == "install" and logfile: |
287 |
- _check_build_log(mysettings) |
288 |
+def _post_src_install_checks(mysettings): |
289 |
+ _post_src_install_uid_fix(mysettings) |
290 |
+ retval = _spawn_misc_sh(mysettings, ["install_qa_check", |
291 |
+ "install_symlink_html_docs"]) |
292 |
+ if retval != os.EX_OK: |
293 |
+ writemsg("!!! install_qa_check failed; exiting.\n", |
294 |
+ noiselevel=-1) |
295 |
+ return retval |
296 |
|
297 |
- if mydo == "install": |
298 |
- _post_src_install_uid_fix(mysettings) |
299 |
- qa_retval = _spawn_misc_sh(mysettings, ["install_qa_check", |
300 |
- "install_symlink_html_docs"], **kwargs) |
301 |
- if qa_retval != os.EX_OK: |
302 |
- writemsg("!!! install_qa_check failed; exiting.\n", |
303 |
- noiselevel=-1) |
304 |
- return qa_retval |
305 |
- return phase_retval |
306 |
- |
307 |
def _check_build_log(mysettings): |
308 |
""" |
309 |
Search the content of $PORTAGE_LOG_FILE if it exists |
310 |
@@ -4220,7 +4232,9 @@ |
311 |
* command not found |
312 |
* Unrecognized configure options |
313 |
""" |
314 |
- logfile = mysettings.get("PORTAGE_LOG_FILE", None) |
315 |
+ logfile = mysettings.get("PORTAGE_LOG_FILE") |
316 |
+ if logfile is None: |
317 |
+ return |
318 |
try: |
319 |
f = open(logfile, 'rb') |
320 |
except EnvironmentError: |
321 |
@@ -4777,8 +4791,9 @@ |
322 |
|
323 |
def doebuild(myebuild, mydo, myroot, mysettings, debug=0, listonly=0, |
324 |
fetchonly=0, cleanup=0, dbkey=None, use_cache=1, fetchall=0, tree=None, |
325 |
- mydbapi=None, vartree=None, prev_mtimes=None): |
326 |
- |
327 |
+ mydbapi=None, vartree=None, prev_mtimes=None, |
328 |
+ fd_pipes=None, returnpid=False): |
329 |
+ |
330 |
""" |
331 |
Wrapper function that invokes specific ebuild phases through the spawning |
332 |
of ebuild.sh |
333 |
@@ -5190,7 +5205,10 @@ |
334 |
elif mydo == "setup": |
335 |
retval = spawn( |
336 |
_shell_quote(ebuild_sh_binary) + " " + mydo, mysettings, |
337 |
- debug=debug, free=1, logfile=logfile) |
338 |
+ debug=debug, free=1, logfile=logfile, fd_pipes=fd_pipes, |
339 |
+ returnpid=returnpid) |
340 |
+ if returnpid: |
341 |
+ return retval |
342 |
retval = exit_status_check(retval) |
343 |
if secpass >= 2: |
344 |
""" Privileged phases may have left files that need to be made |
345 |
@@ -5418,7 +5436,8 @@ |
346 |
raise portage.exception.PermissionDenied( |
347 |
"access('%s', os.W_OK)" % parent_dir) |
348 |
retval = spawnebuild(mydo, |
349 |
- actionmap, mysettings, debug, logfile=logfile) |
350 |
+ actionmap, mysettings, debug, logfile=logfile, |
351 |
+ fd_pipes=fd_pipes, returnpid=returnpid) |
352 |
elif mydo=="qmerge": |
353 |
# check to ensure install was run. this *only* pops up when users |
354 |
# forget it and are using ebuild |
355 |
@@ -5438,7 +5457,8 @@ |
356 |
mydbapi=mydbapi, vartree=vartree, prev_mtimes=prev_mtimes) |
357 |
elif mydo=="merge": |
358 |
retval = spawnebuild("install", actionmap, mysettings, debug, |
359 |
- alwaysdep=1, logfile=logfile) |
360 |
+ alwaysdep=1, logfile=logfile, fd_pipes=fd_pipes, |
361 |
+ returnpid=returnpid) |
362 |
retval = exit_status_check(retval) |
363 |
if retval != os.EX_OK: |
364 |
# The merge phase handles this already. Callers don't know how |
365 |
|
366 |
-- |
367 |
gentoo-commits@l.g.o mailing list |