Gentoo Archives: gentoo-commits

From: "Zac Medico (zmedico)" <zmedico@g.o>
To: gentoo-commits@l.g.o
Subject: [gentoo-commits] portage r10849 - in main/trunk/pym: _emerge portage
Date: Sun, 29 Jun 2008 14:51:13
Message-Id: E1KCyF8-0001em-Tb@stork.gentoo.org
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