tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

javac_output_processor.py (6277B)


      1 #!/usr/bin/env python3
      2 #
      3 # Copyright 2021 The Chromium Authors
      4 # Use of this source code is governed by a BSD-style license that can be
      5 # found in the LICENSE file.
      6 """Contains helper class for processing javac output."""
      7 
      8 import os
      9 import pathlib
     10 import re
     11 import shlex
     12 import sys
     13 import traceback
     14 
     15 from util import build_utils
     16 from util import dep_utils
     17 
     18 sys.path.insert(
     19    0,
     20    os.path.join(build_utils.DIR_SOURCE_ROOT, 'third_party', 'colorama', 'src'))
     21 import colorama
     22 
     23 
     24 class JavacOutputProcessor:
     25  def __init__(self, target_name):
     26    self._target_name = self._RemoveSuffixesIfPresent(
     27        ["__compile_java", "__errorprone", "__header"], target_name)
     28    self._suggested_targets_list = set()
     29 
     30    # Example: ../../ui/android/java/src/org/chromium/ui/base/Clipboard.java:45:
     31    fileline_prefix = (
     32        r'(?P<fileline>(?P<file>[-.\w/\\]+.java):(?P<line>[0-9]+):)')
     33 
     34    self._warning_re = re.compile(
     35        fileline_prefix + r'(?P<full_message> warning: (?P<message>.*))$')
     36    self._error_re = re.compile(fileline_prefix +
     37                                r'(?P<full_message> (?P<message>.*))$')
     38    self._marker_re = re.compile(r'\s*(?P<marker>\^)\s*$')
     39 
     40    self._symbol_not_found_re_list = [
     41        # Example:
     42        # error: package org.chromium.components.url_formatter does not exist
     43        re.compile(fileline_prefix +
     44                   r'( error: package [\w.]+ does not exist)$'),
     45        # Example: error: cannot find symbol
     46        re.compile(fileline_prefix + r'( error: cannot find symbol)$'),
     47        # Example: error: symbol not found org.chromium.url.GURL
     48        re.compile(fileline_prefix + r'( error: symbol not found [\w.]+)$'),
     49    ]
     50 
     51    # Example: import org.chromium.url.GURL;
     52    self._import_re = re.compile(r'\s*import (?P<imported_class>[\w\.]+);$')
     53 
     54    self._warning_color = [
     55        'full_message', colorama.Fore.YELLOW + colorama.Style.DIM
     56    ]
     57    self._error_color = [
     58        'full_message', colorama.Fore.MAGENTA + colorama.Style.BRIGHT
     59    ]
     60    self._marker_color = ['marker', colorama.Fore.BLUE + colorama.Style.BRIGHT]
     61 
     62    self._class_lookup_index = None
     63 
     64    colorama.init()
     65 
     66  def Process(self, lines):
     67    """ Processes javac output.
     68 
     69      - Applies colors to output.
     70      - Suggests GN dep to add for 'unresolved symbol in Java import' errors.
     71      """
     72    lines = self._ElaborateLinesForUnknownSymbol(iter(lines))
     73    for line in lines:
     74      yield self._ApplyColors(line)
     75    if self._suggested_targets_list:
     76 
     77      def yellow(text):
     78        return colorama.Fore.YELLOW + text + colorama.Fore.RESET
     79 
     80      # Show them in quotes so they can be copy/pasted into BUILD.gn files.
     81      yield yellow('Hint:') + ' One or more errors due to missing GN deps.'
     82      yield (yellow('Hint:') + ' Try adding the following to ' +
     83             yellow(self._target_name))
     84 
     85      for targets in sorted(self._suggested_targets_list):
     86        if len(targets) > 1:
     87          suggested_targets_str = 'one of: ' + ', '.join(targets)
     88        else:
     89          suggested_targets_str = targets[0]
     90        yield '    "{}",'.format(suggested_targets_str)
     91 
     92      yield ''
     93      yield yellow('Hint:') + (' Run the following command to add the missing '
     94                               'deps:')
     95      missing_targets = {targets[0] for targets in self._suggested_targets_list}
     96      cmd = dep_utils.CreateAddDepsCommand(self._target_name,
     97                                           sorted(missing_targets))
     98      yield f'    {shlex.join(cmd)}\n '  # Extra space necessary for new line.
     99 
    100  def _ElaborateLinesForUnknownSymbol(self, lines):
    101    """ Elaborates passed-in javac output for unresolved symbols.
    102 
    103    Looks for unresolved symbols in imports.
    104    Adds:
    105    - Line with GN target which cannot compile.
    106    - Mention of unresolved class if not present in error message.
    107    - Line with suggestion of GN dep to add.
    108 
    109    Args:
    110      lines: Generator with javac input.
    111    Returns:
    112      Generator with processed output.
    113    """
    114    previous_line = next(lines, None)
    115    line = next(lines, None)
    116    while previous_line != None:
    117      try:
    118        self._LookForUnknownSymbol(previous_line, line)
    119      except Exception:
    120        elaborated_lines = ['Error in _LookForUnknownSymbol ---']
    121        elaborated_lines += traceback.format_exc().splitlines()
    122        elaborated_lines += ['--- end _LookForUnknownSymbol error']
    123        for elaborated_line in elaborated_lines:
    124          yield elaborated_line
    125 
    126      yield previous_line
    127      previous_line = line
    128      line = next(lines, None)
    129 
    130  def _ApplyColors(self, line):
    131    """Adds colors to passed-in line and returns processed line."""
    132    if self._warning_re.match(line):
    133      line = self._Colorize(line, self._warning_re, self._warning_color)
    134    elif self._error_re.match(line):
    135      line = self._Colorize(line, self._error_re, self._error_color)
    136    elif self._marker_re.match(line):
    137      line = self._Colorize(line, self._marker_re, self._marker_color)
    138    return line
    139 
    140  def _LookForUnknownSymbol(self, line, next_line):
    141    if not next_line:
    142      return
    143 
    144    import_re_match = self._import_re.match(next_line)
    145    if not import_re_match:
    146      return
    147 
    148    for regex in self._symbol_not_found_re_list:
    149      if regex.match(line):
    150        break
    151    else:
    152      return
    153 
    154    if self._class_lookup_index is None:
    155      self._class_lookup_index = dep_utils.ClassLookupIndex(
    156          pathlib.Path(os.getcwd()),
    157          should_build=False,
    158      )
    159 
    160    class_to_lookup = import_re_match.group('imported_class')
    161    suggested_deps = self._class_lookup_index.match(class_to_lookup)
    162 
    163    if not suggested_deps:
    164      print(f'No suggested deps for {class_to_lookup}')
    165      return
    166 
    167    suggested_deps = dep_utils.DisambiguateDeps(suggested_deps)
    168    self._suggested_targets_list.add(tuple(d.target for d in suggested_deps))
    169 
    170  @staticmethod
    171  def _RemoveSuffixesIfPresent(suffixes, text):
    172    for suffix in suffixes:
    173      if text.endswith(suffix):
    174        return text[:-len(suffix)]
    175    return text
    176 
    177  @staticmethod
    178  def _Colorize(line, regex, color):
    179    match = regex.match(line)
    180    start = match.start(color[0])
    181    end = match.end(color[0])
    182    return (line[:start] + color[1] + line[start:end] + colorama.Fore.RESET +
    183            colorama.Style.RESET_ALL + line[end:])