# Copyright 1999-2009 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 # $Id$ from _emerge.SubProcess import SubProcess from _emerge.PollConstants import PollConstants import sys from portage.cache.mappings import slot_dict_class import portage from portage import os from portage import _encodings from portage import _unicode_decode from portage import _unicode_encode import fcntl import codecs class EbuildMetadataPhase(SubProcess): """ Asynchronous interface for the ebuild "depend" phase which is used to extract metadata from the ebuild. """ __slots__ = ("cpv", "ebuild_path", "fd_pipes", "metadata_callback", "ebuild_mtime", "metadata", "portdb", "repo_path", "settings") + \ ("_raw_metadata",) _file_names = ("ebuild",) _files_dict = slot_dict_class(_file_names, prefix="") _metadata_fd = 9 def _start(self): settings = self.settings settings.setcpv(self.cpv) ebuild_path = self.ebuild_path eapi = None if 'parse-eapi-glep-55' in settings.features: pf, eapi = portage._split_ebuild_name_glep55( os.path.basename(ebuild_path)) if eapi is None and \ 'parse-eapi-ebuild-head' in settings.features: eapi = portage._parse_eapi_ebuild_head( codecs.open(_unicode_encode(ebuild_path, encoding=_encodings['fs'], errors='strict'), mode='r', encoding=_encodings['repo.content'], errors='replace')) if eapi is not None: if not portage.eapi_is_supported(eapi): self.metadata_callback(self.cpv, self.ebuild_path, self.repo_path, {'EAPI' : eapi}, self.ebuild_mtime) self.returncode = os.EX_OK self.wait() return settings.configdict['pkg']['EAPI'] = eapi debug = settings.get("PORTAGE_DEBUG") == "1" master_fd = None slave_fd = None fd_pipes = None if self.fd_pipes is not None: fd_pipes = self.fd_pipes.copy() else: fd_pipes = {} fd_pipes.setdefault(0, sys.stdin.fileno()) fd_pipes.setdefault(1, sys.stdout.fileno()) fd_pipes.setdefault(2, sys.stderr.fileno()) # flush any pending output for fd in fd_pipes.values(): if fd == sys.stdout.fileno(): sys.stdout.flush() if fd == sys.stderr.fileno(): sys.stderr.flush() fd_pipes_orig = fd_pipes.copy() self._files = self._files_dict() files = self._files master_fd, slave_fd = os.pipe() fcntl.fcntl(master_fd, fcntl.F_SETFL, fcntl.fcntl(master_fd, fcntl.F_GETFL) | os.O_NONBLOCK) fd_pipes[self._metadata_fd] = slave_fd self._raw_metadata = [] files.ebuild = os.fdopen(master_fd, 'rb') self._reg_id = self.scheduler.register(files.ebuild.fileno(), self._registered_events, self._output_handler) self._registered = True retval = portage.doebuild(ebuild_path, "depend", settings["ROOT"], settings, debug, mydbapi=self.portdb, tree="porttree", fd_pipes=fd_pipes, returnpid=True) os.close(slave_fd) if isinstance(retval, int): # doebuild failed before spawning self._unregister() self.returncode = retval self.wait() return self.pid = retval[0] portage.process.spawned_pids.remove(self.pid) def _output_handler(self, fd, event): if event & PollConstants.POLLIN: self._raw_metadata.append(self._files.ebuild.read()) if not self._raw_metadata[-1]: self._unregister() self.wait() self._unregister_if_appropriate(event) return self._registered def _set_returncode(self, wait_retval): SubProcess._set_returncode(self, wait_retval) if self.returncode == os.EX_OK: metadata_lines = ''.join(_unicode_decode(chunk, encoding=_encodings['repo.content'], errors='replace') for chunk in self._raw_metadata).splitlines() if len(portage.auxdbkeys) != len(metadata_lines): # Don't trust bash's returncode if the # number of lines is incorrect. self.returncode = 1 else: metadata = zip(portage.auxdbkeys, metadata_lines) self.metadata = self.metadata_callback(self.cpv, self.ebuild_path, self.repo_path, metadata, self.ebuild_mtime)