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 8import os 9import pathlib 10import re 11import shlex 12import sys 13import traceback 14 15from util import build_utils 16from util import dep_utils 17 18sys.path.insert( 19 0, 20 os.path.join(build_utils.DIR_SOURCE_ROOT, 'third_party', 'colorama', 'src')) 21import colorama 22 23 24class 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:]) 184