From 87b6cdf5475c6fcc2368e1d80dfaac8fdee5a453 Mon Sep 17 00:00:00 2001 From: Chris Larson Date: Thu, 9 Dec 2010 20:14:40 -0500 Subject: process: add subprocess-based bits (Bitbake rev: c63e55564a8840083dbd8634b10fe6f76d1f1354) Signed-off-by: Chris Larson Signed-off-by: Richard Purdie --- bitbake/lib/bb/process.py | 107 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 bitbake/lib/bb/process.py (limited to 'bitbake/lib/bb') diff --git a/bitbake/lib/bb/process.py b/bitbake/lib/bb/process.py new file mode 100644 index 0000000000..b022e4c8f9 --- /dev/null +++ b/bitbake/lib/bb/process.py @@ -0,0 +1,107 @@ +import subprocess +import signal +import logging + +logger = logging.getLogger('BitBake.Process') + +def subprocess_setup(): + # Python installs a SIGPIPE handler by default. This is usually not what + # non-Python subprocesses expect. + print('in preexec hook') + signal.signal(signal.SIGPIPE, signal.SIG_DFL) + +class CmdError(RuntimeError): + def __init__(self, command): + self.command = command + + def __str__(self): + if not isinstance(self.command, basestring): + cmd = subprocess.list2cmdline(self.command) + else: + cmd = self.command + + return "Execution of '%s' failed" % cmd + +class NotFoundError(CmdError): + def __str__(self): + return CmdError.__str__(self) + ": command not found" + +class ExecutionError(CmdError): + def __init__(self, command, exitcode, stdout = None, stderr = None): + CmdError.__init__(self, command) + self.exitcode = exitcode + self.stdout = stdout + self.stderr = stderr + + def __str__(self): + message = "" + if self.stderr: + message += self.stderr + if self.stdout: + message += self.stdout + if message: + message = ":\n" + message + return (CmdError.__str__(self) + + " with exit code %s" % self.exitcode + message) + +class Popen(subprocess.Popen): + defaults = { + "close_fds": True, + "preexec_fn": subprocess_setup, + "stdout": subprocess.PIPE, + "stderr": subprocess.STDOUT, + "stdin": subprocess.PIPE, + "shell": False, + } + + def __init__(self, *args, **kwargs): + options = dict(self.defaults) + options.update(kwargs) + subprocess.Popen.__init__(self, *args, **options) + +def _logged_communicate(pipe, log, input): + bufsize = 512 + hasoutput = pipe.stdout is not None or pipe.stderr is not None + if hasoutput: + outdata, errdata = [], [] + while pipe.poll() is None: + if pipe.stdout is not None: + data = pipe.stdout.read(bufsize) + if data is not None: + outdata.append(data) + log.write(data) + + if pipe.stderr is not None: + data = pipe.stderr.read(bufsize) + if data is not None: + errdata.append(data) + log.write(data) + return ''.join(outdata), ''.join(errdata) + +def run(cmd, input=None, **options): + """Convenience function to run a command and return its output, raising an + exception when the command fails""" + + if isinstance(cmd, basestring) and not "shell" in options: + options["shell"] = True + + log = options.get('log') + if log: + del options['log'] + + try: + pipe = Popen(cmd, **options) + except OSError, exc: + if exc.errno == 2: + raise NotFoundError(cmd) + else: + raise + + if log: + stdout, stderr = _logged_communicate(pipe, log, input) + else: + stdout, stderr = pipe.communicate(input) + + if pipe.returncode != 0: + raise ExecutionError(cmd, pipe.returncode, stdout, stderr) + return stdout, stderr -- cgit v1.2.3-54-g00ecf