| 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)
 | 
| 
 |