diff options
Diffstat (limited to 'third_party/libwebrtc/build/android/gyp/javac_output_processor.py')
-rwxr-xr-x | third_party/libwebrtc/build/android/gyp/javac_output_processor.py | 198 |
1 files changed, 198 insertions, 0 deletions
diff --git a/third_party/libwebrtc/build/android/gyp/javac_output_processor.py b/third_party/libwebrtc/build/android/gyp/javac_output_processor.py new file mode 100755 index 0000000000..298c12573b --- /dev/null +++ b/third_party/libwebrtc/build/android/gyp/javac_output_processor.py @@ -0,0 +1,198 @@ +#!/usr/bin/env python3 +# +# Copyright 2021 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +"""Contains helper class for processing javac output.""" + +import os +import pathlib +import re +import sys + +from util import build_utils + +sys.path.insert( + 0, + os.path.join(build_utils.DIR_SOURCE_ROOT, 'third_party', 'colorama', 'src')) +import colorama +sys.path.insert( + 0, + os.path.join(build_utils.DIR_SOURCE_ROOT, 'tools', 'android', + 'modularization', 'convenience')) +import lookup_dep + + +class JavacOutputProcessor: + def __init__(self, target_name): + self._target_name = target_name + + # Example: ../../ui/android/java/src/org/chromium/ui/base/Clipboard.java:45: + fileline_prefix = ( + r'(?P<fileline>(?P<file>[-.\w/\\]+.java):(?P<line>[0-9]+):)') + + self._warning_re = re.compile( + fileline_prefix + r'(?P<full_message> warning: (?P<message>.*))$') + self._error_re = re.compile(fileline_prefix + + r'(?P<full_message> (?P<message>.*))$') + self._marker_re = re.compile(r'\s*(?P<marker>\^)\s*$') + + # Matches output modification performed by _ElaborateLineForUnknownSymbol() + # so that it can be colorized. + # Example: org.chromium.base.Log found in dep //base:base_java. + self._please_add_dep_re = re.compile( + r'(?P<full_message>Please add //[\w/:]+ dep to //[\w/:]+.*)$') + + # First element in pair is bool which indicates whether the missing + # class/package is part of the error message. + self._symbol_not_found_re_list = [ + # Example: + # error: package org.chromium.components.url_formatter does not exist + (True, + re.compile(fileline_prefix + + r'( error: package [\w.]+ does not exist)$')), + # Example: error: cannot find symbol + (False, re.compile(fileline_prefix + r'( error: cannot find symbol)$')), + # Example: error: symbol not found org.chromium.url.GURL + (True, + re.compile(fileline_prefix + r'( error: symbol not found [\w.]+)$')), + ] + + # Example: import org.chromium.url.GURL; + self._import_re = re.compile(r'\s*import (?P<imported_class>[\w\.]+);$') + + self._warning_color = [ + 'full_message', colorama.Fore.YELLOW + colorama.Style.DIM + ] + self._error_color = [ + 'full_message', colorama.Fore.MAGENTA + colorama.Style.BRIGHT + ] + self._marker_color = ['marker', colorama.Fore.BLUE + colorama.Style.BRIGHT] + + self._class_lookup_index = None + + colorama.init() + + def Process(self, lines): + """ Processes javac output. + + - Applies colors to output. + - Suggests GN dep to add for 'unresolved symbol in Java import' errors. + """ + lines = self._ElaborateLinesForUnknownSymbol(iter(lines)) + return (self._ApplyColors(l) for l in lines) + + def _ElaborateLinesForUnknownSymbol(self, lines): + """ Elaborates passed-in javac output for unresolved symbols. + + Looks for unresolved symbols in imports. + Adds: + - Line with GN target which cannot compile. + - Mention of unresolved class if not present in error message. + - Line with suggestion of GN dep to add. + + Args: + lines: Generator with javac input. + Returns: + Generator with processed output. + """ + previous_line = next(lines, None) + line = next(lines, None) + while previous_line != None: + elaborated_lines = self._ElaborateLineForUnknownSymbol( + previous_line, line) + for elaborated_line in elaborated_lines: + yield elaborated_line + + previous_line = line + line = next(lines, None) + + def _ApplyColors(self, line): + """Adds colors to passed-in line and returns processed line.""" + if self._warning_re.match(line): + line = self._Colorize(line, self._warning_re, self._warning_color) + elif self._error_re.match(line): + line = self._Colorize(line, self._error_re, self._error_color) + elif self._please_add_dep_re.match(line): + line = self._Colorize(line, self._please_add_dep_re, self._error_color) + elif self._marker_re.match(line): + line = self._Colorize(line, self._marker_re, self._marker_color) + return line + + def _ElaborateLineForUnknownSymbol(self, line, next_line): + if not next_line: + return [line] + + import_re_match = self._import_re.match(next_line) + if not import_re_match: + return [line] + + symbol_missing = False + has_missing_symbol_in_error_msg = False + for symbol_in_error_msg, regex in self._symbol_not_found_re_list: + if regex.match(line): + symbol_missing = True + has_missing_symbol_in_error_msg = symbol_in_error_msg + break + + if not symbol_missing: + return [line] + + class_to_lookup = import_re_match.group('imported_class') + if self._class_lookup_index == None: + self._class_lookup_index = lookup_dep.ClassLookupIndex(pathlib.Path( + os.getcwd()), + should_build=False) + suggested_deps = self._class_lookup_index.match(class_to_lookup) + + if len(suggested_deps) != 1: + suggested_deps = self._FindFactoryDep(suggested_deps) + if len(suggested_deps) != 1: + return [line] + + suggested_target = suggested_deps[0].target + + target_name = self._RemoveSuffixesIfPresent( + ["__compile_java", "__errorprone", "__header"], self._target_name) + if not has_missing_symbol_in_error_msg: + line = "{} {}".format(line, class_to_lookup) + + return [ + line, + "Please add {} dep to {}. ".format(suggested_target, target_name) + + "File a crbug if this suggestion is incorrect.", + ] + + @staticmethod + def _FindFactoryDep(class_entries): + """Find the android_library_factory() GN target.""" + if len(class_entries) != 2: + return [] + + # android_library_factory() targets set low_classpath_priority=true. + # This logic is correct if GN targets other than android_library_factory() + # set low_classpath_priority=true. low_classpath_priority=true indicates + # that the target is depended on (and overridden) by other targets which + # contain the same class. We want to recommend the leaf target. + if class_entries[0].low_classpath_priority == class_entries[ + 1].low_classpath_priority: + return [] + + if class_entries[0].low_classpath_priority: + return [class_entries[0]] + return [class_entries[1]] + + @staticmethod + def _RemoveSuffixesIfPresent(suffixes, text): + for suffix in suffixes: + if text.endswith(suffix): + return text[:-len(suffix)] + return text + + @staticmethod + def _Colorize(line, regex, color): + match = regex.match(line) + start = match.start(color[0]) + end = match.end(color[0]) + return (line[:start] + color[1] + line[start:end] + colorama.Fore.RESET + + colorama.Style.RESET_ALL + line[end:]) |