| Index: build/android/pylib/cmd_helper.py
|
| diff --git a/build/android/pylib/cmd_helper.py b/build/android/pylib/cmd_helper.py
|
| index 4aa17b9d10726afd14cbd14eb2227aa28894cc41..aba00be735368e9e931e321b14bef1d46ad1fbf3 100644
|
| --- a/build/android/pylib/cmd_helper.py
|
| +++ b/build/android/pylib/cmd_helper.py
|
| @@ -5,12 +5,19 @@
|
| """A wrapper for subprocess to make calling shell commands easier."""
|
|
|
| import logging
|
| +import os
|
| import pipes
|
| +import select
|
| import signal
|
| +import StringIO
|
| import subprocess
|
| -import tempfile
|
| +import time
|
|
|
| -from pylib.utils import timeout_retry
|
| +# fcntl is not available on Windows.
|
| +try:
|
| + import fcntl
|
| +except ImportError:
|
| + fcntl = None
|
|
|
|
|
| def Popen(args, stdout=None, stderr=None, shell=None, cwd=None, env=None):
|
| @@ -88,33 +95,73 @@ def GetCmdStatusAndOutput(args, cwd=None, shell=False):
|
| s += ':' + cwd
|
| s += '> ' + args_repr
|
| logging.info(s)
|
| - tmpout = tempfile.TemporaryFile(bufsize=0)
|
| - tmperr = tempfile.TemporaryFile(bufsize=0)
|
| - exit_code = Call(args, cwd=cwd, stdout=tmpout, stderr=tmperr, shell=shell)
|
| - tmperr.seek(0)
|
| - stderr = tmperr.read()
|
| - tmperr.close()
|
| + pipe = Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
|
| + shell=shell, cwd=cwd)
|
| + stdout, stderr = pipe.communicate()
|
| +
|
| if stderr:
|
| logging.critical(stderr)
|
| - tmpout.seek(0)
|
| - stdout = tmpout.read()
|
| - tmpout.close()
|
| if len(stdout) > 4096:
|
| logging.debug('Truncated output:')
|
| logging.debug(stdout[:4096])
|
| - return (exit_code, stdout)
|
| + return (pipe.returncode, stdout)
|
| +
|
| +
|
| +class TimeoutError(Exception):
|
| + """Module-specific timeout exception."""
|
| + pass
|
|
|
|
|
| -def GetCmdStatusAndOutputWithTimeoutAndRetries(args, timeout, retries):
|
| - """Executes a subprocess with a timeout and retries.
|
| +def GetCmdStatusAndOutputWithTimeout(args, timeout, cwd=None, shell=False,
|
| + logfile=None):
|
| + """Executes a subprocess with a timeout.
|
|
|
| Args:
|
| args: List of arguments to the program, the program to execute is the first
|
| element.
|
| - timeout: the timeout in seconds.
|
| - retries: the number of retries.
|
| + timeout: the timeout in seconds or None to wait forever.
|
| + cwd: If not None, the subprocess's current directory will be changed to
|
| + |cwd| before it's executed.
|
| + shell: Whether to execute args as a shell command.
|
| + logfile: Optional file-like object that will receive output from the
|
| + command as it is running.
|
|
|
| Returns:
|
| The 2-tuple (exit code, output).
|
| """
|
| - return timeout_retry.Run(GetCmdStatusAndOutput, timeout, retries, [args])
|
| + assert fcntl, 'fcntl module is required'
|
| + process = Popen(args, cwd=cwd, shell=shell, stdout=subprocess.PIPE,
|
| + stderr=subprocess.STDOUT)
|
| + try:
|
| + end_time = (time.time() + timeout) if timeout else None
|
| + poll_interval = 1
|
| + buffer_size = 4096
|
| + child_fd = process.stdout.fileno()
|
| + output = StringIO.StringIO()
|
| +
|
| + # Enable non-blocking reads from the child's stdout.
|
| + fl = fcntl.fcntl(child_fd, fcntl.F_GETFL)
|
| + fcntl.fcntl(child_fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
|
| +
|
| + while True:
|
| + if end_time and time.time() > end_time:
|
| + raise TimeoutError
|
| + read_fds, _, _ = select.select([child_fd], [], [], poll_interval)
|
| + if child_fd in read_fds:
|
| + data = os.read(child_fd, buffer_size)
|
| + if not data:
|
| + break
|
| + if logfile:
|
| + logfile.write(data)
|
| + output.write(data)
|
| + if process.poll() is not None:
|
| + break
|
| + finally:
|
| + try:
|
| + # Make sure the process doesn't stick around if we fail with an
|
| + # exception.
|
| + process.kill()
|
| + except OSError:
|
| + pass
|
| + process.wait()
|
| + return process.returncode, output.getvalue()
|
|
|