diff options
author | Zac Medico <zmedico@gentoo.org> | 2008-06-29 14:51:05 +0000 |
---|---|---|
committer | Zac Medico <zmedico@gentoo.org> | 2008-06-29 14:51:05 +0000 |
commit | 2758391fc9f3791cdd3260cb8113aad43d8063a3 (patch) | |
tree | f127b1ead856dbb7b568b5260f2be5bcdc9ab19f /pym/_emerge | |
parent | 0c277f2ca685f1058d4217b94ae9905b3098333e (diff) | |
download | portage-2758391fc9f3791cdd3260cb8113aad43d8063a3.tar.gz portage-2758391fc9f3791cdd3260cb8113aad43d8063a3.tar.bz2 portage-2758391fc9f3791cdd3260cb8113aad43d8063a3.zip |
* Add "fd_pipes" and "returnpid" parameters to doebuild() and pass
these into spawn calls, enabling ebuild processes to execute
asynchronously.
* Add a EbuildPhase class that's derived from the pty logging code
inside portage.spawn().
* Integrate post-phase code from spawnebuild() into EbuildBuild.execute()
so that it still gets called even though doebuild() calls execute
asynchronously.
svn path=/main/trunk/; revision=10849
Diffstat (limited to 'pym/_emerge')
-rw-r--r-- | pym/_emerge/__init__.py | 172 |
1 files changed, 167 insertions, 5 deletions
diff --git a/pym/_emerge/__init__.py b/pym/_emerge/__init__.py index dc266ddb0..84b5769a3 100644 --- a/pym/_emerge/__init__.py +++ b/pym/_emerge/__init__.py @@ -1541,22 +1541,184 @@ class EbuildBuild(Task): root_config = self.pkg.root_config portdb = root_config.trees["porttree"].dbapi ebuild_path = portdb.findname(self.pkg.cpv) - debug = self.settings.get("PORTAGE_DEBUG") == "1" + settings = self.settings + debug = settings.get("PORTAGE_DEBUG") == "1" + cleanup = 1 retval = portage.doebuild(ebuild_path, "clean", - root_config.root, self.settings, debug, cleanup=1, + root_config.root, settings, debug, cleanup=cleanup, mydbapi=portdb, tree="porttree") if retval != os.EX_OK: return retval + # This initializes PORTAGE_LOG_FILE. + portage.prepare_build_dirs(root_config.root, settings, cleanup) + + fd_pipes = { + 0 : sys.stdin.fileno(), + 1 : sys.stdout.fileno(), + 2 : sys.stderr.fileno(), + } + for mydo in self._phases: - retval = portage.doebuild(ebuild_path, mydo, - root_config.root, self.settings, debug, - mydbapi=portdb, tree="porttree") + ebuild_phase = EbuildPhase(fd_pipes=fd_pipes, + pkg=self.pkg, phase=mydo, settings=settings) + ebuild_phase.start() + ebuild_phase._output_handler() + retval = ebuild_phase.wait() + + portage._post_phase_userpriv_perms(settings) + if mydo == "install": + portage._check_build_log(settings) + if retval == os.EX_OK: + retval = portage._post_src_install_checks(settings) + if retval != os.EX_OK: return retval + return os.EX_OK +class EbuildPhase(SlotObject): + + __slots__ = ("fd_pipes", "phase", "pkg", "settings", + "pid", "returncode", "files") + + _file_names = ("log", "stdout", "ebuild") + _files_dict = slot_dict_class(_file_names) + _bufsize = 4096 + + def start(self): + root_config = self.pkg.root_config + portdb = root_config.trees["porttree"].dbapi + ebuild_path = portdb.findname(self.pkg.cpv) + settings = self.settings + debug = settings.get("PORTAGE_DEBUG") == "1" + logfile = settings.get("PORTAGE_LOG_FILE") + master_fd = None + slave_fd = None + fd_pipes = self.fd_pipes.copy() + + # flush any pending output + for fd in fd_pipes.itervalues(): + if fd == sys.stdout.fileno(): + sys.stdout.flush() + if fd == sys.stderr.fileno(): + sys.stderr.flush() + + fd_pipes_orig = None + self.files = self._files_dict() + files = self.files + got_pty = False + + portage._doebuild_exit_status_unlink( + settings.get("EBUILD_EXIT_STATUS_FILE")) + + if logfile: + if portage._disable_openpty: + master_fd, slave_fd = os.pipe() + else: + from pty import openpty + try: + master_fd, slave_fd = openpty() + got_pty = True + except EnvironmentError, e: + portage._disable_openpty = True + portage.writemsg("openpty failed: '%s'\n" % str(e), + noiselevel=-1) + del e + master_fd, slave_fd = os.pipe() + + if got_pty: + # Disable post-processing of output since otherwise weird + # things like \n -> \r\n transformations may occur. + import termios + mode = termios.tcgetattr(slave_fd) + mode[1] &= ~termios.OPOST + termios.tcsetattr(slave_fd, termios.TCSANOW, mode) + + import fcntl + fcntl.fcntl(master_fd, fcntl.F_SETFL, + fcntl.fcntl(master_fd, fcntl.F_GETFL) | os.O_NONBLOCK) + + fd_pipes.setdefault(0, sys.stdin.fileno()) + fd_pipes_orig = fd_pipes.copy() + if got_pty and os.isatty(fd_pipes_orig[1]): + from portage.output import get_term_size, set_term_size + rows, columns = get_term_size() + set_term_size(rows, columns, slave_fd) + fd_pipes[0] = fd_pipes_orig[0] + fd_pipes[1] = slave_fd + fd_pipes[2] = slave_fd + + retval = portage.doebuild(ebuild_path, self.phase, + root_config.root, settings, debug, + mydbapi=portdb, tree="porttree", + fd_pipes=fd_pipes, returnpid=True) + + self.pid = retval[0] + + if logfile: + os.close(slave_fd) + files["log"] = open(logfile, 'a') + files["stdout"] = os.fdopen(os.dup(fd_pipes_orig[1]), 'w') + files["ebuild"] = os.fdopen(master_fd, 'r') + + def _output_handler(self): + log_file = self.files.get("log") + if log_file is None: + return + ebuild_file = self.files["ebuild"] + stdout_file = self.files["stdout"] + iwtd = [ebuild_file] + owtd = [] + ewtd = [] + import array, select + buffsize = self._bufsize + eof = False + while not eof: + events = select.select(iwtd, owtd, ewtd) + for f in events[0]: + # Use non-blocking mode to prevent read + # calls from blocking indefinitely. + buf = array.array('B') + try: + buf.fromfile(f, buffsize) + except EOFError: + pass + if not buf: + eof = True + break + if f is ebuild_file: + buf.tofile(stdout_file) + stdout_file.flush() + buf.tofile(log_file) + log_file.flush() + log_file.close() + stdout_file.close() + ebuild_file.close() + + def wait(self): + pid = self.pid + retval = os.waitpid(pid, 0)[1] + portage.process.spawned_pids.remove(pid) + if retval != os.EX_OK: + if retval & 0xff: + retval = (retval & 0xff) << 8 + else: + retval = retval >> 8 + + msg = portage._doebuild_exit_status_check( + self.phase, self.settings) + if msg: + retval = 1 + from textwrap import wrap + from portage.elog.messages import eerror + for l in wrap(msg, 72): + eerror(l, phase=self.phase, key=self.pkg.cpv) + + self.returncode = retval + return self.returncode + class EbuildBinpkg(Task): """ This assumes that src_install() has successfully completed. |