blob: eaa7f40b2d179ab5eb8aec3d3175a4bd80abf608 [file] [log] [blame]
# Copyright 1999-2009 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
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 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._set_returncode((self.pid, 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._set_returncode((self.pid, 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)
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)