| Index: build/android/pylib/cmd_helper.py
|
| diff --git a/build/android/pylib/cmd_helper.py b/build/android/pylib/cmd_helper.py
|
| index e3abab74ffda3087eb239cddd850bbb874383161..f8815531a56922b9db6734bef01392f7c2bea90f 100644
|
| --- a/build/android/pylib/cmd_helper.py
|
| +++ b/build/android/pylib/cmd_helper.py
|
| @@ -115,14 +115,20 @@ def GetCmdOutput(args, cwd=None, shell=False):
|
| return output
|
|
|
|
|
| -def _LogCommand(args, cwd=None):
|
| - if not isinstance(args, basestring):
|
| +def _ValidateAndLogCommand(args, cwd, shell):
|
| + if isinstance(args, basestring):
|
| + if not shell:
|
| + raise Exception('string args must be run with shell=True')
|
| + else:
|
| + if shell:
|
| + raise Exception('array args must be run with shell=False')
|
| args = ' '.join(SingleQuote(c) for c in args)
|
| if cwd is None:
|
| cwd = ''
|
| else:
|
| cwd = ':' + cwd
|
| logging.info('[host]%s> %s', cwd, args)
|
| + return args
|
|
|
|
|
| def GetCmdStatusAndOutput(args, cwd=None, shell=False):
|
| @@ -133,18 +139,13 @@ def GetCmdStatusAndOutput(args, cwd=None, shell=False):
|
| the string or the first item in the args sequence.
|
| 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.
|
| + shell: Whether to execute args as a shell command. Must be True if args
|
| + is a string and False if args is a sequence.
|
|
|
| Returns:
|
| The 2-tuple (exit code, output).
|
| """
|
| - if isinstance(args, basestring):
|
| - if not shell:
|
| - raise Exception('string args must be run with shell=True')
|
| - elif shell:
|
| - raise Exception('array args must be run with shell=False')
|
| -
|
| - _LogCommand(args, cwd)
|
| + _ValidateAndLogCommand(args, cwd, shell)
|
| pipe = Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
|
| shell=shell, cwd=cwd)
|
| stdout, stderr = pipe.communicate()
|
| @@ -162,44 +163,16 @@ class TimeoutError(Exception):
|
| pass
|
|
|
|
|
| -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 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).
|
| - """
|
| +def _IterProcessStdout(process, timeout=None, buffer_size=4096,
|
| + poll_interval=1):
|
| assert fcntl, 'fcntl module is required'
|
| - if isinstance(args, basestring):
|
| - if not shell:
|
| - raise Exception('string args must be run with shell=True')
|
| - elif shell:
|
| - raise Exception('array args must be run with shell=False')
|
| -
|
| - _LogCommand(args, cwd)
|
| - 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.
|
| + child_fd = process.stdout.fileno()
|
| fl = fcntl.fcntl(child_fd, fcntl.F_GETFL)
|
| fcntl.fcntl(child_fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
|
|
|
| + end_time = (time.time() + timeout) if timeout else None
|
| while True:
|
| if end_time and time.time() > end_time:
|
| raise TimeoutError
|
| @@ -208,9 +181,7 @@ def GetCmdStatusAndOutputWithTimeout(args, timeout, cwd=None, shell=False,
|
| data = os.read(child_fd, buffer_size)
|
| if not data:
|
| break
|
| - if logfile:
|
| - logfile.write(data)
|
| - output.write(data)
|
| + yield data
|
| if process.poll() is not None:
|
| break
|
| finally:
|
| @@ -221,4 +192,70 @@ def GetCmdStatusAndOutputWithTimeout(args, timeout, cwd=None, shell=False,
|
| except OSError:
|
| pass
|
| process.wait()
|
| +
|
| +
|
| +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 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. Must be True if args
|
| + is a string and False if args is a sequence.
|
| + logfile: Optional file-like object that will receive output from the
|
| + command as it is running.
|
| +
|
| + Returns:
|
| + The 2-tuple (exit code, output).
|
| + """
|
| + _ValidateAndLogCommand(args, cwd, shell)
|
| + output = StringIO.StringIO()
|
| + process = Popen(args, cwd=cwd, shell=shell, stdout=subprocess.PIPE,
|
| + stderr=subprocess.STDOUT)
|
| + for data in _IterProcessStdout(process, timeout=timeout):
|
| + if logfile:
|
| + logfile.write(data)
|
| + output.write(data)
|
| return process.returncode, output.getvalue()
|
| +
|
| +
|
| +def IterCmdOutputLines(args, timeout=None, cwd=None, shell=False,
|
| + check_status=True):
|
| + """Executes a subprocess and continuously yields lines from its output.
|
| +
|
| + Args:
|
| + args: List of arguments to the program, the program to execute is the first
|
| + element.
|
| + 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. Must be True if args
|
| + is a string and False if args is a sequence.
|
| + check_status: A boolean indicating whether to check the exit status of the
|
| + process after all output has been read.
|
| +
|
| + Yields:
|
| + The output of the subprocess, line by line.
|
| +
|
| + Raises:
|
| + CalledProcessError if check_status is True and the process exited with a
|
| + non-zero exit status.
|
| + """
|
| + cmd = _ValidateAndLogCommand(args, cwd, shell)
|
| + process = Popen(args, cwd=cwd, shell=shell, stdout=subprocess.PIPE,
|
| + stderr=subprocess.STDOUT)
|
| + buffer_output = ''
|
| + for data in _IterProcessStdout(process, timeout=timeout):
|
| + buffer_output += data
|
| + has_incomplete_line = buffer_output[-1] not in '\r\n'
|
| + lines = buffer_output.splitlines()
|
| + buffer_output = lines.pop() if has_incomplete_line else ''
|
| + for line in lines:
|
| + yield line
|
| + if buffer_output:
|
| + yield buffer_output
|
| + if check_status and process.returncode:
|
| + raise subprocess.CalledProcessError(process.returncode, cmd)
|
|
|