def _RunBazelAndPatchOutput()

in src/TulsiGenerator/Scripts/bazel_build.py [0:0]


  def _RunBazelAndPatchOutput(self, command):
    """Runs subprocess command, patching output as it's received."""
    self._PrintVerbose('Running "%s", patching output for workspace root at '
                       '"%s" with project path at "%s".' %
                       (' '.join([pipes.quote(x) for x in command]),
                        self.workspace_root,
                        self.project_dir))
    # Clean up bazel output to make it look better in Xcode.
    bazel_line_regex = re.compile(
        r'(INFO|DEBUG|WARNING|ERROR|FAILED): ([^:]+:\d+:(?:\d+:)?)\s+(.+)')

    bazel_generic_regex = re.compile(r'(INFO|DEBUG|WARNING|ERROR|FAILED): (.*)')

    def PatchBazelDiagnosticStatements(output_line):
      """Make Bazel output more Xcode friendly."""

      def BazelLabelToXcodeLabel(bazel_label):
        """Map Bazel labels to xcode labels for build output."""
        xcode_labels = {
            'INFO': 'note',
            'DEBUG': 'note',
            'WARNING': 'warning',
            'ERROR': 'error',
            'FAILED': 'error'
        }
        return xcode_labels.get(bazel_label, bazel_label)

      match = bazel_line_regex.match(output_line)
      if match:
        xcode_label = BazelLabelToXcodeLabel(match.group(1))
        output_line = '%s %s: %s' % (match.group(2), xcode_label,
                                     match.group(3))
      else:
        match = bazel_generic_regex.match(output_line)
        if match:
          xcode_label = BazelLabelToXcodeLabel(match.group(1))
          output_line = '%s: %s' % (xcode_label, match.group(2))
      return output_line

    if self.workspace_root != self.project_dir:
      # Match (likely) filename:line_number: lines.
      xcode_parsable_line_regex = re.compile(r'([^/][^:]+):\d+:')

      def PatchOutputLine(output_line):
        output_line = PatchBazelDiagnosticStatements(output_line)
        if xcode_parsable_line_regex.match(output_line):
          output_line = '%s/%s' % (self.workspace_root, output_line)
        return output_line
      patch_xcode_parsable_line = PatchOutputLine
    else:
      patch_xcode_parsable_line = PatchBazelDiagnosticStatements

    def HandleOutput(output):
      for line in output.splitlines():
        _logger.log_bazel_message(patch_xcode_parsable_line(line))

    def WatcherUpdate(watcher):
      """Processes any new events in the given watcher.

      Args:
        watcher: a BazelBuildEventsWatcher object.

      Returns:
        A list of new tulsiout file names seen.
      """
      new_events = watcher.check_for_new_events()
      new_outputs = []
      for build_event in new_events:
        if build_event.stderr:
          HandleOutput(build_event.stderr)
        if build_event.stdout:
          HandleOutput(build_event.stdout)
        if build_event.files:
          outputs = [x for x in build_event.files if x.endswith('.tulsiouts')]
          new_outputs.extend(outputs)
      return new_outputs

    def ReaderThread(file_handle, out_buffer):
      out_buffer.append(file_handle.read())
      file_handle.close()

    # Make sure the BEP JSON file exists and is empty. We do this to prevent
    # any sort of race between the watcher, bazel, and the old file contents.
    open(self.build_events_file_path, 'w').close()

    # Capture the stderr and stdout from Bazel. We only display it if it we're
    # unable to read any BEP events.
    process = subprocess.Popen(
        command,
        stdout=subprocess.PIPE,
        stderr=subprocess.STDOUT,
        universal_newlines=True,
        bufsize=1)

    # Register atexit function to clean up BEP file.
    atexit.register(_BEPFileExitCleanup, self.build_events_file_path)
    global CLEANUP_BEP_FILE_AT_EXIT
    CLEANUP_BEP_FILE_AT_EXIT = True

    # Start capturing output from Bazel.
    reader_buffer = []
    reader_thread = threading.Thread(target=ReaderThread,
                                     args=(process.stdout, reader_buffer))
    reader_thread.daemon = True
    reader_thread.start()

    with io.open(self.build_events_file_path, 'r', -1, 'utf-8', 'ignore'
                ) as bep_file:
      watcher = bazel_build_events.BazelBuildEventsWatcher(bep_file,
                                                           _PrintXcodeWarning)
      output_locations = []
      while process.returncode is None:
        output_locations.extend(WatcherUpdate(watcher))
        time.sleep(0.1)
        process.poll()

      output_locations.extend(WatcherUpdate(watcher))

      # If BEP JSON parsing failed, we should display the raw stdout and
      # stderr from Bazel.
      reader_thread.join()
      if not watcher.has_read_events():
        HandleOutput(reader_buffer[0])

      if process.returncode == 0 and not output_locations:
        CLEANUP_BEP_FILE_AT_EXIT = False
        _PrintXcodeError('Unable to find location of the .tulsiouts file.'
                         'Please report this as a Tulsi bug, including the'
                         'contents of %s.' % self.build_events_file_path)
        return 1, output_locations
      return process.returncode, output_locations