xref: /aosp_15_r20/build/make/tools/warn/warn_common.py (revision 9e94795a3d4ef5c1d47486f9a02bb378756cea8a)
1*9e94795aSAndroid Build Coastguard Worker# python3
2*9e94795aSAndroid Build Coastguard Worker# Copyright (C) 2019 The Android Open Source Project
3*9e94795aSAndroid Build Coastguard Worker#
4*9e94795aSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
5*9e94795aSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
6*9e94795aSAndroid Build Coastguard Worker# You may obtain a copy of the License at
7*9e94795aSAndroid Build Coastguard Worker#
8*9e94795aSAndroid Build Coastguard Worker#      http://www.apache.org/licenses/LICENSE-2.0
9*9e94795aSAndroid Build Coastguard Worker#
10*9e94795aSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
11*9e94795aSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
12*9e94795aSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*9e94795aSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
14*9e94795aSAndroid Build Coastguard Worker# limitations under the License.
15*9e94795aSAndroid Build Coastguard Worker
16*9e94795aSAndroid Build Coastguard Worker"""Grep warnings messages and output HTML tables or warning counts in CSV.
17*9e94795aSAndroid Build Coastguard Worker
18*9e94795aSAndroid Build Coastguard WorkerDefault is to output warnings in HTML tables grouped by warning severity.
19*9e94795aSAndroid Build Coastguard WorkerUse option --byproject to output tables grouped by source file projects.
20*9e94795aSAndroid Build Coastguard WorkerUse option --gencsv to output warning counts in CSV format.
21*9e94795aSAndroid Build Coastguard Worker
22*9e94795aSAndroid Build Coastguard WorkerDefault input file is build.log, which can be changed with the --log flag.
23*9e94795aSAndroid Build Coastguard Worker"""
24*9e94795aSAndroid Build Coastguard Worker
25*9e94795aSAndroid Build Coastguard Worker# List of important data structures and functions in this script.
26*9e94795aSAndroid Build Coastguard Worker#
27*9e94795aSAndroid Build Coastguard Worker# To parse and keep warning message in the input file:
28*9e94795aSAndroid Build Coastguard Worker#   severity:                classification of message severity
29*9e94795aSAndroid Build Coastguard Worker#   warn_patterns:
30*9e94795aSAndroid Build Coastguard Worker#   warn_patterns[w]['category']     tool that issued the warning, not used now
31*9e94795aSAndroid Build Coastguard Worker#   warn_patterns[w]['description']  table heading
32*9e94795aSAndroid Build Coastguard Worker#   warn_patterns[w]['members']      matched warnings from input
33*9e94795aSAndroid Build Coastguard Worker#   warn_patterns[w]['patterns']     regular expressions to match warnings
34*9e94795aSAndroid Build Coastguard Worker#   warn_patterns[w]['projects'][p]  number of warnings of pattern w in p
35*9e94795aSAndroid Build Coastguard Worker#   warn_patterns[w]['severity']     severity tuple
36*9e94795aSAndroid Build Coastguard Worker#   project_list[p][0]               project name
37*9e94795aSAndroid Build Coastguard Worker#   project_list[p][1]               regular expression to match a project path
38*9e94795aSAndroid Build Coastguard Worker#   project_patterns[p]              re.compile(project_list[p][1])
39*9e94795aSAndroid Build Coastguard Worker#   project_names[p]                 project_list[p][0]
40*9e94795aSAndroid Build Coastguard Worker#   warning_messages     array of each warning message, without source url
41*9e94795aSAndroid Build Coastguard Worker#   warning_links        array of each warning code search link; for 'chrome'
42*9e94795aSAndroid Build Coastguard Worker#   warning_records      array of [idx to warn_patterns,
43*9e94795aSAndroid Build Coastguard Worker#                                  idx to project_names,
44*9e94795aSAndroid Build Coastguard Worker#                                  idx to warning_messages,
45*9e94795aSAndroid Build Coastguard Worker#                                  idx to warning_links]
46*9e94795aSAndroid Build Coastguard Worker#   parse_input_file
47*9e94795aSAndroid Build Coastguard Worker#
48*9e94795aSAndroid Build Coastguard Workerimport argparse
49*9e94795aSAndroid Build Coastguard Workerimport io
50*9e94795aSAndroid Build Coastguard Workerimport multiprocessing
51*9e94795aSAndroid Build Coastguard Workerimport os
52*9e94795aSAndroid Build Coastguard Workerimport re
53*9e94795aSAndroid Build Coastguard Workerimport sys
54*9e94795aSAndroid Build Coastguard Worker
55*9e94795aSAndroid Build Coastguard Worker# pylint:disable=relative-beyond-top-level,no-name-in-module
56*9e94795aSAndroid Build Coastguard Worker# suppress false positive of no-name-in-module warnings
57*9e94795aSAndroid Build Coastguard Workerfrom . import android_project_list
58*9e94795aSAndroid Build Coastguard Workerfrom . import chrome_project_list
59*9e94795aSAndroid Build Coastguard Workerfrom . import cpp_warn_patterns as cpp_patterns
60*9e94795aSAndroid Build Coastguard Workerfrom . import html_writer
61*9e94795aSAndroid Build Coastguard Workerfrom . import java_warn_patterns as java_patterns
62*9e94795aSAndroid Build Coastguard Workerfrom . import make_warn_patterns as make_patterns
63*9e94795aSAndroid Build Coastguard Workerfrom . import other_warn_patterns as other_patterns
64*9e94795aSAndroid Build Coastguard Workerfrom . import tidy_warn_patterns as tidy_patterns
65*9e94795aSAndroid Build Coastguard Worker
66*9e94795aSAndroid Build Coastguard Worker
67*9e94795aSAndroid Build Coastguard Worker# Location of this file is used to guess the root of Android source tree.
68*9e94795aSAndroid Build Coastguard WorkerTHIS_FILE_PATH = 'build/make/tools/warn/warn_common.py'
69*9e94795aSAndroid Build Coastguard Worker
70*9e94795aSAndroid Build Coastguard Worker
71*9e94795aSAndroid Build Coastguard Workerdef parse_args(use_google3):
72*9e94795aSAndroid Build Coastguard Worker  """Define and parse the args. Return the parse_args() result."""
73*9e94795aSAndroid Build Coastguard Worker  parser = argparse.ArgumentParser(
74*9e94795aSAndroid Build Coastguard Worker      description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
75*9e94795aSAndroid Build Coastguard Worker  parser.add_argument('--capacitor_path', default='',
76*9e94795aSAndroid Build Coastguard Worker                      help='Save capacitor warning file to the passed absolute'
77*9e94795aSAndroid Build Coastguard Worker                      ' path')
78*9e94795aSAndroid Build Coastguard Worker  # csvpath has a different naming than the above path because historically the
79*9e94795aSAndroid Build Coastguard Worker  # original Android script used csvpath, so other scripts rely on it
80*9e94795aSAndroid Build Coastguard Worker  parser.add_argument('--csvpath', default='',
81*9e94795aSAndroid Build Coastguard Worker                      help='Save CSV warning file to the passed path')
82*9e94795aSAndroid Build Coastguard Worker  parser.add_argument('--gencsv', action='store_true',
83*9e94795aSAndroid Build Coastguard Worker                      help='Generate CSV file with number of various warnings')
84*9e94795aSAndroid Build Coastguard Worker  parser.add_argument('--csvwithdescription', default='',
85*9e94795aSAndroid Build Coastguard Worker                      help="""Save CSV warning file to the passed path this csv
86*9e94795aSAndroid Build Coastguard Worker                            will contain all the warning descriptions""")
87*9e94795aSAndroid Build Coastguard Worker  parser.add_argument('--byproject', action='store_true',
88*9e94795aSAndroid Build Coastguard Worker                      help='Separate warnings in HTML output by project names')
89*9e94795aSAndroid Build Coastguard Worker  parser.add_argument('--url', default='',
90*9e94795aSAndroid Build Coastguard Worker                      help='Root URL of an Android source code tree prefixed '
91*9e94795aSAndroid Build Coastguard Worker                      'before files in warnings')
92*9e94795aSAndroid Build Coastguard Worker  parser.add_argument('--separator', default='?l=',
93*9e94795aSAndroid Build Coastguard Worker                      help='Separator between the end of a URL and the line '
94*9e94795aSAndroid Build Coastguard Worker                      'number argument. e.g. #')
95*9e94795aSAndroid Build Coastguard Worker  parser.add_argument('--processes', default=multiprocessing.cpu_count(),
96*9e94795aSAndroid Build Coastguard Worker                      type=int,
97*9e94795aSAndroid Build Coastguard Worker                      help='Number of parallel processes to process warnings')
98*9e94795aSAndroid Build Coastguard Worker  # Old Android build scripts call warn.py without --platform,
99*9e94795aSAndroid Build Coastguard Worker  # so the default platform is set to 'android'.
100*9e94795aSAndroid Build Coastguard Worker  parser.add_argument('--platform', default='android',
101*9e94795aSAndroid Build Coastguard Worker                      choices=['chrome', 'android'],
102*9e94795aSAndroid Build Coastguard Worker                      help='Platform of the build log')
103*9e94795aSAndroid Build Coastguard Worker  # Old Android build scripts call warn.py with only a build.log file path.
104*9e94795aSAndroid Build Coastguard Worker  parser.add_argument('--log', help='Path to build log file')
105*9e94795aSAndroid Build Coastguard Worker  parser.add_argument(dest='buildlog', metavar='build.log',
106*9e94795aSAndroid Build Coastguard Worker                      default='build.log', nargs='?',
107*9e94795aSAndroid Build Coastguard Worker                      help='Path to build.log file')
108*9e94795aSAndroid Build Coastguard Worker  flags = parser.parse_args()
109*9e94795aSAndroid Build Coastguard Worker  if not flags.log:
110*9e94795aSAndroid Build Coastguard Worker    flags.log = flags.buildlog
111*9e94795aSAndroid Build Coastguard Worker  if not use_google3 and not os.path.exists(flags.log):
112*9e94795aSAndroid Build Coastguard Worker    sys.exit('Cannot find log file: ' + flags.log)
113*9e94795aSAndroid Build Coastguard Worker  return flags
114*9e94795aSAndroid Build Coastguard Worker
115*9e94795aSAndroid Build Coastguard Worker
116*9e94795aSAndroid Build Coastguard Workerdef get_project_names(project_list):
117*9e94795aSAndroid Build Coastguard Worker  """Get project_names from project_list."""
118*9e94795aSAndroid Build Coastguard Worker  return [p[0] for p in project_list]
119*9e94795aSAndroid Build Coastguard Worker
120*9e94795aSAndroid Build Coastguard Worker
121*9e94795aSAndroid Build Coastguard Workerdef find_project_index(line, project_patterns):
122*9e94795aSAndroid Build Coastguard Worker  """Return the index to the project pattern array."""
123*9e94795aSAndroid Build Coastguard Worker  for idx, pattern in enumerate(project_patterns):
124*9e94795aSAndroid Build Coastguard Worker    if pattern.match(line):
125*9e94795aSAndroid Build Coastguard Worker      return idx
126*9e94795aSAndroid Build Coastguard Worker  return -1
127*9e94795aSAndroid Build Coastguard Worker
128*9e94795aSAndroid Build Coastguard Worker
129*9e94795aSAndroid Build Coastguard Workerdef classify_one_warning(warning, link, results, project_patterns,
130*9e94795aSAndroid Build Coastguard Worker                         warn_patterns):
131*9e94795aSAndroid Build Coastguard Worker  """Classify one warning line."""
132*9e94795aSAndroid Build Coastguard Worker  for idx, pattern in enumerate(warn_patterns):
133*9e94795aSAndroid Build Coastguard Worker    for cpat in pattern['compiled_patterns']:
134*9e94795aSAndroid Build Coastguard Worker      if cpat.match(warning):
135*9e94795aSAndroid Build Coastguard Worker        project_idx = find_project_index(warning, project_patterns)
136*9e94795aSAndroid Build Coastguard Worker        results.append([warning, link, idx, project_idx])
137*9e94795aSAndroid Build Coastguard Worker        return
138*9e94795aSAndroid Build Coastguard Worker  # If we end up here, there was a problem parsing the log
139*9e94795aSAndroid Build Coastguard Worker  # probably caused by 'make -j' mixing the output from
140*9e94795aSAndroid Build Coastguard Worker  # 2 or more concurrent compiles
141*9e94795aSAndroid Build Coastguard Worker
142*9e94795aSAndroid Build Coastguard Worker
143*9e94795aSAndroid Build Coastguard Workerdef remove_prefix(src, sub):
144*9e94795aSAndroid Build Coastguard Worker  """Remove everything before last occurrence of substring sub in string src."""
145*9e94795aSAndroid Build Coastguard Worker  if sub in src:
146*9e94795aSAndroid Build Coastguard Worker    inc_sub = src.rfind(sub)
147*9e94795aSAndroid Build Coastguard Worker    return src[inc_sub:]
148*9e94795aSAndroid Build Coastguard Worker  return src
149*9e94795aSAndroid Build Coastguard Worker
150*9e94795aSAndroid Build Coastguard Worker
151*9e94795aSAndroid Build Coastguard Worker# TODO(emmavukelj): Don't have any generate_*_cs_link functions call
152*9e94795aSAndroid Build Coastguard Worker# normalize_path a second time (the first time being in parse_input_file)
153*9e94795aSAndroid Build Coastguard Workerdef generate_cs_link(warning_line, flags, android_root=None):
154*9e94795aSAndroid Build Coastguard Worker  """Try to add code search HTTP URL prefix."""
155*9e94795aSAndroid Build Coastguard Worker  if flags.platform == 'chrome':
156*9e94795aSAndroid Build Coastguard Worker    return generate_chrome_cs_link(warning_line, flags)
157*9e94795aSAndroid Build Coastguard Worker  if flags.platform == 'android':
158*9e94795aSAndroid Build Coastguard Worker    return generate_android_cs_link(warning_line, flags, android_root)
159*9e94795aSAndroid Build Coastguard Worker  return 'https://cs.corp.google.com/'
160*9e94795aSAndroid Build Coastguard Worker
161*9e94795aSAndroid Build Coastguard Worker
162*9e94795aSAndroid Build Coastguard Workerdef generate_android_cs_link(warning_line, flags, android_root):
163*9e94795aSAndroid Build Coastguard Worker  """Generate the code search link for a warning line in Android."""
164*9e94795aSAndroid Build Coastguard Worker  # max_splits=2 -> only 3 items
165*9e94795aSAndroid Build Coastguard Worker  raw_path, line_number_str, _ = warning_line.split(':', 2)
166*9e94795aSAndroid Build Coastguard Worker  normalized_path = normalize_path(raw_path, flags, android_root)
167*9e94795aSAndroid Build Coastguard Worker  if not flags.url:
168*9e94795aSAndroid Build Coastguard Worker    return normalized_path
169*9e94795aSAndroid Build Coastguard Worker  link_path = flags.url + '/' + normalized_path
170*9e94795aSAndroid Build Coastguard Worker  if line_number_str.isdigit():
171*9e94795aSAndroid Build Coastguard Worker    link_path += flags.separator + line_number_str
172*9e94795aSAndroid Build Coastguard Worker  return link_path
173*9e94795aSAndroid Build Coastguard Worker
174*9e94795aSAndroid Build Coastguard Worker
175*9e94795aSAndroid Build Coastguard Workerdef generate_chrome_cs_link(warning_line, flags):
176*9e94795aSAndroid Build Coastguard Worker  """Generate the code search link for a warning line in Chrome."""
177*9e94795aSAndroid Build Coastguard Worker  split_line = warning_line.split(':')
178*9e94795aSAndroid Build Coastguard Worker  raw_path = split_line[0]
179*9e94795aSAndroid Build Coastguard Worker  normalized_path = normalize_path(raw_path, flags)
180*9e94795aSAndroid Build Coastguard Worker  link_base = 'https://cs.chromium.org/'
181*9e94795aSAndroid Build Coastguard Worker  link_add = 'chromium'
182*9e94795aSAndroid Build Coastguard Worker  link_path = None
183*9e94795aSAndroid Build Coastguard Worker
184*9e94795aSAndroid Build Coastguard Worker  # Basically just going through a few specific directory cases and specifying
185*9e94795aSAndroid Build Coastguard Worker  # the proper behavior for that case. This list of cases was accumulated
186*9e94795aSAndroid Build Coastguard Worker  # through trial and error manually going through the warnings.
187*9e94795aSAndroid Build Coastguard Worker  #
188*9e94795aSAndroid Build Coastguard Worker  # This code pattern of using case-specific "if"s instead of "elif"s looks
189*9e94795aSAndroid Build Coastguard Worker  # possibly accidental and mistaken but it is intentional because some paths
190*9e94795aSAndroid Build Coastguard Worker  # fall under several cases (e.g. third_party/lib/nghttp2_frame.c) and for
191*9e94795aSAndroid Build Coastguard Worker  # those we want the most specific case to be applied. If there is reliable
192*9e94795aSAndroid Build Coastguard Worker  # knowledge of exactly where these occur, this could be changed to "elif"s
193*9e94795aSAndroid Build Coastguard Worker  # but there is no reliable set of paths falling under multiple cases at the
194*9e94795aSAndroid Build Coastguard Worker  # moment.
195*9e94795aSAndroid Build Coastguard Worker  if '/src/third_party' in raw_path:
196*9e94795aSAndroid Build Coastguard Worker    link_path = remove_prefix(raw_path, '/src/third_party/')
197*9e94795aSAndroid Build Coastguard Worker  if '/chrome_root/src_internal/' in raw_path:
198*9e94795aSAndroid Build Coastguard Worker    link_path = remove_prefix(raw_path, '/chrome_root/src_internal/')
199*9e94795aSAndroid Build Coastguard Worker    link_path = link_path[len('/chrome_root'):]  # remove chrome_root
200*9e94795aSAndroid Build Coastguard Worker  if '/chrome_root/src/' in raw_path:
201*9e94795aSAndroid Build Coastguard Worker    link_path = remove_prefix(raw_path, '/chrome_root/src/')
202*9e94795aSAndroid Build Coastguard Worker    link_path = link_path[len('/chrome_root'):]  # remove chrome_root
203*9e94795aSAndroid Build Coastguard Worker  if '/libassistant/' in raw_path:
204*9e94795aSAndroid Build Coastguard Worker    link_add = 'eureka_internal/chromium/src'
205*9e94795aSAndroid Build Coastguard Worker    link_base = 'https://cs.corp.google.com/'  # internal data
206*9e94795aSAndroid Build Coastguard Worker    link_path = remove_prefix(normalized_path, '/libassistant/')
207*9e94795aSAndroid Build Coastguard Worker  if raw_path.startswith('gen/'):
208*9e94795aSAndroid Build Coastguard Worker    link_path = '/src/out/Debug/gen/' + normalized_path
209*9e94795aSAndroid Build Coastguard Worker  if '/gen/' in raw_path:
210*9e94795aSAndroid Build Coastguard Worker    return '%s?q=file:%s' % (link_base, remove_prefix(normalized_path, '/gen/'))
211*9e94795aSAndroid Build Coastguard Worker
212*9e94795aSAndroid Build Coastguard Worker  if not link_path and (raw_path.startswith('src/') or
213*9e94795aSAndroid Build Coastguard Worker                        raw_path.startswith('src_internal/')):
214*9e94795aSAndroid Build Coastguard Worker    link_path = '/%s' % raw_path
215*9e94795aSAndroid Build Coastguard Worker
216*9e94795aSAndroid Build Coastguard Worker  if not link_path:  # can't find specific link, send a query
217*9e94795aSAndroid Build Coastguard Worker    return '%s?q=file:%s' % (link_base, normalized_path)
218*9e94795aSAndroid Build Coastguard Worker
219*9e94795aSAndroid Build Coastguard Worker  line_number = int(split_line[1])
220*9e94795aSAndroid Build Coastguard Worker  link = '%s%s%s?l=%d' % (link_base, link_add, link_path, line_number)
221*9e94795aSAndroid Build Coastguard Worker  return link
222*9e94795aSAndroid Build Coastguard Worker
223*9e94795aSAndroid Build Coastguard Worker
224*9e94795aSAndroid Build Coastguard Workerdef find_this_file_and_android_root(path):
225*9e94795aSAndroid Build Coastguard Worker  """Return android source root path if this file is found."""
226*9e94795aSAndroid Build Coastguard Worker  parts = path.split('/')
227*9e94795aSAndroid Build Coastguard Worker  for idx in reversed(range(2, len(parts))):
228*9e94795aSAndroid Build Coastguard Worker    root_path = '/'.join(parts[:idx])
229*9e94795aSAndroid Build Coastguard Worker    # Android root directory should contain this script.
230*9e94795aSAndroid Build Coastguard Worker    if os.path.exists(root_path + '/' + THIS_FILE_PATH):
231*9e94795aSAndroid Build Coastguard Worker      return root_path
232*9e94795aSAndroid Build Coastguard Worker  return ''
233*9e94795aSAndroid Build Coastguard Worker
234*9e94795aSAndroid Build Coastguard Worker
235*9e94795aSAndroid Build Coastguard Workerdef find_android_root_top_dirs(root_dir):
236*9e94795aSAndroid Build Coastguard Worker  """Return a list of directories under the root_dir, if it exists."""
237*9e94795aSAndroid Build Coastguard Worker  # Root directory should contain at least build/make and build/soong.
238*9e94795aSAndroid Build Coastguard Worker  if (not os.path.isdir(root_dir + '/build/make') or
239*9e94795aSAndroid Build Coastguard Worker      not os.path.isdir(root_dir + '/build/soong')):
240*9e94795aSAndroid Build Coastguard Worker    return None
241*9e94795aSAndroid Build Coastguard Worker  return list(filter(lambda d: os.path.isdir(root_dir + '/' + d),
242*9e94795aSAndroid Build Coastguard Worker                     os.listdir(root_dir)))
243*9e94795aSAndroid Build Coastguard Worker
244*9e94795aSAndroid Build Coastguard Worker
245*9e94795aSAndroid Build Coastguard Workerdef find_android_root(buildlog):
246*9e94795aSAndroid Build Coastguard Worker  """Guess android source root from common prefix of file paths."""
247*9e94795aSAndroid Build Coastguard Worker  # Use the longest common prefix of the absolute file paths
248*9e94795aSAndroid Build Coastguard Worker  # of the first 10000 warning messages as the android_root.
249*9e94795aSAndroid Build Coastguard Worker  warning_lines = []
250*9e94795aSAndroid Build Coastguard Worker  warning_pattern = re.compile('^/[^ ]*/[^ ]*: warning: .*')
251*9e94795aSAndroid Build Coastguard Worker  count = 0
252*9e94795aSAndroid Build Coastguard Worker  for line in buildlog:
253*9e94795aSAndroid Build Coastguard Worker    # We want to find android_root of a local build machine.
254*9e94795aSAndroid Build Coastguard Worker    # Do not use RBE warning lines, which has '/b/f/w/' path prefix.
255*9e94795aSAndroid Build Coastguard Worker    # Do not use /tmp/ file warnings.
256*9e94795aSAndroid Build Coastguard Worker    if ('/b/f/w' not in line and not line.startswith('/tmp/') and
257*9e94795aSAndroid Build Coastguard Worker        warning_pattern.match(line)):
258*9e94795aSAndroid Build Coastguard Worker      warning_lines.append(line)
259*9e94795aSAndroid Build Coastguard Worker      count += 1
260*9e94795aSAndroid Build Coastguard Worker      if count > 9999:
261*9e94795aSAndroid Build Coastguard Worker        break
262*9e94795aSAndroid Build Coastguard Worker      # Try to find warn.py and use its location to find
263*9e94795aSAndroid Build Coastguard Worker      # the source tree root.
264*9e94795aSAndroid Build Coastguard Worker      if count < 100:
265*9e94795aSAndroid Build Coastguard Worker        path = os.path.normpath(re.sub(':.*$', '', line))
266*9e94795aSAndroid Build Coastguard Worker        android_root = find_this_file_and_android_root(path)
267*9e94795aSAndroid Build Coastguard Worker        if android_root:
268*9e94795aSAndroid Build Coastguard Worker          return android_root, find_android_root_top_dirs(android_root)
269*9e94795aSAndroid Build Coastguard Worker  # Do not use common prefix of a small number of paths.
270*9e94795aSAndroid Build Coastguard Worker  android_root = ''
271*9e94795aSAndroid Build Coastguard Worker  if count > 10:
272*9e94795aSAndroid Build Coastguard Worker    # pytype: disable=wrong-arg-types
273*9e94795aSAndroid Build Coastguard Worker    root_path = os.path.commonprefix(warning_lines)
274*9e94795aSAndroid Build Coastguard Worker    # pytype: enable=wrong-arg-types
275*9e94795aSAndroid Build Coastguard Worker    if len(root_path) > 2 and root_path[len(root_path) - 1] == '/':
276*9e94795aSAndroid Build Coastguard Worker      android_root = root_path[:-1]
277*9e94795aSAndroid Build Coastguard Worker  if android_root and os.path.isdir(android_root):
278*9e94795aSAndroid Build Coastguard Worker    return android_root, find_android_root_top_dirs(android_root)
279*9e94795aSAndroid Build Coastguard Worker  # When the build.log file is moved to a different machine where
280*9e94795aSAndroid Build Coastguard Worker  # android_root is not found, use the location of this script
281*9e94795aSAndroid Build Coastguard Worker  # to find the android source tree sub directories.
282*9e94795aSAndroid Build Coastguard Worker  if __file__.endswith('/' + THIS_FILE_PATH):
283*9e94795aSAndroid Build Coastguard Worker    script_root = __file__.replace('/' + THIS_FILE_PATH, '')
284*9e94795aSAndroid Build Coastguard Worker    return android_root, find_android_root_top_dirs(script_root)
285*9e94795aSAndroid Build Coastguard Worker  return android_root, None
286*9e94795aSAndroid Build Coastguard Worker
287*9e94795aSAndroid Build Coastguard Worker
288*9e94795aSAndroid Build Coastguard Workerdef remove_android_root_prefix(path, android_root):
289*9e94795aSAndroid Build Coastguard Worker  """Remove android_root prefix from path if it is found."""
290*9e94795aSAndroid Build Coastguard Worker  if path.startswith(android_root):
291*9e94795aSAndroid Build Coastguard Worker    return path[1 + len(android_root):]
292*9e94795aSAndroid Build Coastguard Worker  return path
293*9e94795aSAndroid Build Coastguard Worker
294*9e94795aSAndroid Build Coastguard Worker
295*9e94795aSAndroid Build Coastguard Workerdef normalize_path(path, flags, android_root=None):
296*9e94795aSAndroid Build Coastguard Worker  """Normalize file path relative to src/ or src-internal/ directory."""
297*9e94795aSAndroid Build Coastguard Worker  path = os.path.normpath(path)
298*9e94795aSAndroid Build Coastguard Worker
299*9e94795aSAndroid Build Coastguard Worker  if flags.platform == 'android':
300*9e94795aSAndroid Build Coastguard Worker    if android_root:
301*9e94795aSAndroid Build Coastguard Worker      return remove_android_root_prefix(path, android_root)
302*9e94795aSAndroid Build Coastguard Worker    return path
303*9e94795aSAndroid Build Coastguard Worker
304*9e94795aSAndroid Build Coastguard Worker  # Remove known prefix of root path and normalize the suffix.
305*9e94795aSAndroid Build Coastguard Worker  idx = path.find('chrome_root/')
306*9e94795aSAndroid Build Coastguard Worker  if idx >= 0:
307*9e94795aSAndroid Build Coastguard Worker    # remove chrome_root/, we want path relative to that
308*9e94795aSAndroid Build Coastguard Worker    return path[idx + len('chrome_root/'):]
309*9e94795aSAndroid Build Coastguard Worker  return path
310*9e94795aSAndroid Build Coastguard Worker
311*9e94795aSAndroid Build Coastguard Worker
312*9e94795aSAndroid Build Coastguard Workerdef normalize_warning_line(line, flags, android_root=None):
313*9e94795aSAndroid Build Coastguard Worker  """Normalize file path relative to src directory in a warning line."""
314*9e94795aSAndroid Build Coastguard Worker  line = re.sub(u'[\u2018\u2019]', '\'', line)
315*9e94795aSAndroid Build Coastguard Worker  # replace non-ASCII chars to spaces
316*9e94795aSAndroid Build Coastguard Worker  line = re.sub(u'[^\x00-\x7f]', ' ', line)
317*9e94795aSAndroid Build Coastguard Worker  line = line.strip()
318*9e94795aSAndroid Build Coastguard Worker  first_column = line.find(':')
319*9e94795aSAndroid Build Coastguard Worker  return normalize_path(line[:first_column], flags,
320*9e94795aSAndroid Build Coastguard Worker                        android_root) + line[first_column:]
321*9e94795aSAndroid Build Coastguard Worker
322*9e94795aSAndroid Build Coastguard Worker
323*9e94795aSAndroid Build Coastguard Workerdef parse_input_file_chrome(infile, flags):
324*9e94795aSAndroid Build Coastguard Worker  """Parse Chrome input file, collect parameters and warning lines."""
325*9e94795aSAndroid Build Coastguard Worker  platform_version = 'unknown'
326*9e94795aSAndroid Build Coastguard Worker  board_name = 'unknown'
327*9e94795aSAndroid Build Coastguard Worker  architecture = 'unknown'
328*9e94795aSAndroid Build Coastguard Worker
329*9e94795aSAndroid Build Coastguard Worker  # only handle warning lines of format 'file_path:line_no:col_no: warning: ...'
330*9e94795aSAndroid Build Coastguard Worker  # Bug: http://198657613, This might need change to handle RBE output.
331*9e94795aSAndroid Build Coastguard Worker  chrome_warning_pattern = r'^[^ ]*/[^ ]*:[0-9]+:[0-9]+: warning: .*'
332*9e94795aSAndroid Build Coastguard Worker
333*9e94795aSAndroid Build Coastguard Worker  warning_pattern = re.compile(chrome_warning_pattern)
334*9e94795aSAndroid Build Coastguard Worker
335*9e94795aSAndroid Build Coastguard Worker  # Collect all unique warning lines
336*9e94795aSAndroid Build Coastguard Worker  unique_warnings = dict()
337*9e94795aSAndroid Build Coastguard Worker  for line in infile:
338*9e94795aSAndroid Build Coastguard Worker    if warning_pattern.match(line):
339*9e94795aSAndroid Build Coastguard Worker      normalized_line = normalize_warning_line(line, flags)
340*9e94795aSAndroid Build Coastguard Worker      if normalized_line not in unique_warnings:
341*9e94795aSAndroid Build Coastguard Worker        unique_warnings[normalized_line] = generate_cs_link(line, flags)
342*9e94795aSAndroid Build Coastguard Worker    elif (platform_version == 'unknown' or board_name == 'unknown' or
343*9e94795aSAndroid Build Coastguard Worker          architecture == 'unknown'):
344*9e94795aSAndroid Build Coastguard Worker      result = re.match(r'.+Package:.+chromeos-base/chromeos-chrome-', line)
345*9e94795aSAndroid Build Coastguard Worker      if result is not None:
346*9e94795aSAndroid Build Coastguard Worker        platform_version = 'R' + line.split('chrome-')[1].split('_')[0]
347*9e94795aSAndroid Build Coastguard Worker        continue
348*9e94795aSAndroid Build Coastguard Worker      result = re.match(r'.+Source\sunpacked\sin\s(.+)', line)
349*9e94795aSAndroid Build Coastguard Worker      if result is not None:
350*9e94795aSAndroid Build Coastguard Worker        board_name = result.group(1).split('/')[2]
351*9e94795aSAndroid Build Coastguard Worker        continue
352*9e94795aSAndroid Build Coastguard Worker      result = re.match(r'.+USE:\s*([^\s]*).*', line)
353*9e94795aSAndroid Build Coastguard Worker      if result is not None:
354*9e94795aSAndroid Build Coastguard Worker        architecture = result.group(1)
355*9e94795aSAndroid Build Coastguard Worker        continue
356*9e94795aSAndroid Build Coastguard Worker
357*9e94795aSAndroid Build Coastguard Worker  header_str = '%s - %s - %s' % (platform_version, board_name, architecture)
358*9e94795aSAndroid Build Coastguard Worker  return unique_warnings, header_str
359*9e94795aSAndroid Build Coastguard Worker
360*9e94795aSAndroid Build Coastguard Worker
361*9e94795aSAndroid Build Coastguard Workerdef add_normalized_line_to_warnings(line, flags, android_root, unique_warnings):
362*9e94795aSAndroid Build Coastguard Worker  """Parse/normalize path, updating warning line and add to warnings dict."""
363*9e94795aSAndroid Build Coastguard Worker  normalized_line = normalize_warning_line(line, flags, android_root)
364*9e94795aSAndroid Build Coastguard Worker  if normalized_line not in unique_warnings:
365*9e94795aSAndroid Build Coastguard Worker    unique_warnings[normalized_line] = generate_cs_link(line, flags,
366*9e94795aSAndroid Build Coastguard Worker                                                        android_root)
367*9e94795aSAndroid Build Coastguard Worker  return unique_warnings
368*9e94795aSAndroid Build Coastguard Worker
369*9e94795aSAndroid Build Coastguard Worker
370*9e94795aSAndroid Build Coastguard Workerdef parse_input_file_android(infile, flags):
371*9e94795aSAndroid Build Coastguard Worker  """Parse Android input file, collect parameters and warning lines."""
372*9e94795aSAndroid Build Coastguard Worker  # pylint:disable=too-many-locals,too-many-branches
373*9e94795aSAndroid Build Coastguard Worker  platform_version = 'unknown'
374*9e94795aSAndroid Build Coastguard Worker  target_product = 'unknown'
375*9e94795aSAndroid Build Coastguard Worker  target_variant = 'unknown'
376*9e94795aSAndroid Build Coastguard Worker  build_id = 'unknown'
377*9e94795aSAndroid Build Coastguard Worker  android_root, root_top_dirs = find_android_root(infile)
378*9e94795aSAndroid Build Coastguard Worker  infile.seek(0)
379*9e94795aSAndroid Build Coastguard Worker
380*9e94795aSAndroid Build Coastguard Worker  # rustc warning messages have two lines that should be combined:
381*9e94795aSAndroid Build Coastguard Worker  #     warning: description
382*9e94795aSAndroid Build Coastguard Worker  #        --> file_path:line_number:column_number
383*9e94795aSAndroid Build Coastguard Worker  # Some warning messages have no file name:
384*9e94795aSAndroid Build Coastguard Worker  #     warning: macro replacement list ... [bugprone-macro-parentheses]
385*9e94795aSAndroid Build Coastguard Worker  # Some makefile warning messages have no line number:
386*9e94795aSAndroid Build Coastguard Worker  #     some/path/file.mk: warning: description
387*9e94795aSAndroid Build Coastguard Worker  # C/C++ compiler warning messages have line and column numbers:
388*9e94795aSAndroid Build Coastguard Worker  #     some/path/file.c:line_number:column_number: warning: description
389*9e94795aSAndroid Build Coastguard Worker  warning_pattern = re.compile('(^[^ ]*/[^ ]*: warning: .*)|(^warning: .*)')
390*9e94795aSAndroid Build Coastguard Worker  rustc_file_position = re.compile('^[ ]+--> [^ ]*/[^ ]*:[0-9]+:[0-9]+')
391*9e94795aSAndroid Build Coastguard Worker
392*9e94795aSAndroid Build Coastguard Worker  # If RBE was used, try to reclaim some warning lines (from stdout)
393*9e94795aSAndroid Build Coastguard Worker  # that contain leading characters from stderr.
394*9e94795aSAndroid Build Coastguard Worker  # The leading characters can be any character, including digits and spaces.
395*9e94795aSAndroid Build Coastguard Worker
396*9e94795aSAndroid Build Coastguard Worker  # If a warning line's source file path contains the special RBE prefix
397*9e94795aSAndroid Build Coastguard Worker  # /b/f/w/, we can remove all leading chars up to and including the "/b/f/w/".
398*9e94795aSAndroid Build Coastguard Worker  bfw_warning_pattern = re.compile('.*/b/f/w/([^ ]*: warning: .*)')
399*9e94795aSAndroid Build Coastguard Worker
400*9e94795aSAndroid Build Coastguard Worker  # When android_root is known and available, we find its top directories
401*9e94795aSAndroid Build Coastguard Worker  # and remove all leading chars before a top directory name.
402*9e94795aSAndroid Build Coastguard Worker  # We assume that the leading chars from stderr do not contain "/".
403*9e94795aSAndroid Build Coastguard Worker  # For example,
404*9e94795aSAndroid Build Coastguard Worker  #   10external/...
405*9e94795aSAndroid Build Coastguard Worker  #   12 warningsexternal/...
406*9e94795aSAndroid Build Coastguard Worker  #   413 warningexternal/...
407*9e94795aSAndroid Build Coastguard Worker  #   5 warnings generatedexternal/...
408*9e94795aSAndroid Build Coastguard Worker  #   Suppressed 1000 warnings (packages/modules/...
409*9e94795aSAndroid Build Coastguard Worker  if root_top_dirs:
410*9e94795aSAndroid Build Coastguard Worker    extra_warning_pattern = re.compile(
411*9e94795aSAndroid Build Coastguard Worker        '^.[^/]*((' + '|'.join(root_top_dirs) +
412*9e94795aSAndroid Build Coastguard Worker        ')/[^ ]*: warning: .*)')
413*9e94795aSAndroid Build Coastguard Worker  else:
414*9e94795aSAndroid Build Coastguard Worker    extra_warning_pattern = re.compile('^[^/]* ([^ /]*/[^ ]*: warning: .*)')
415*9e94795aSAndroid Build Coastguard Worker
416*9e94795aSAndroid Build Coastguard Worker  # Collect all unique warning lines
417*9e94795aSAndroid Build Coastguard Worker  unique_warnings = dict()
418*9e94795aSAndroid Build Coastguard Worker  checked_warning_lines = dict()
419*9e94795aSAndroid Build Coastguard Worker  line_counter = 0
420*9e94795aSAndroid Build Coastguard Worker  prev_warning = ''
421*9e94795aSAndroid Build Coastguard Worker  for line in infile:
422*9e94795aSAndroid Build Coastguard Worker    line_counter += 1
423*9e94795aSAndroid Build Coastguard Worker    if prev_warning:
424*9e94795aSAndroid Build Coastguard Worker      if rustc_file_position.match(line):
425*9e94795aSAndroid Build Coastguard Worker        # must be a rustc warning, combine 2 lines into one warning
426*9e94795aSAndroid Build Coastguard Worker        line = line.strip().replace('--> ', '') + ': ' + prev_warning
427*9e94795aSAndroid Build Coastguard Worker        unique_warnings = add_normalized_line_to_warnings(
428*9e94795aSAndroid Build Coastguard Worker            line, flags, android_root, unique_warnings)
429*9e94795aSAndroid Build Coastguard Worker        prev_warning = ''
430*9e94795aSAndroid Build Coastguard Worker        continue
431*9e94795aSAndroid Build Coastguard Worker      # add prev_warning, and then process the current line
432*9e94795aSAndroid Build Coastguard Worker      prev_warning = 'unknown_source_file: ' + prev_warning
433*9e94795aSAndroid Build Coastguard Worker      unique_warnings = add_normalized_line_to_warnings(
434*9e94795aSAndroid Build Coastguard Worker          prev_warning, flags, android_root, unique_warnings)
435*9e94795aSAndroid Build Coastguard Worker      prev_warning = ''
436*9e94795aSAndroid Build Coastguard Worker
437*9e94795aSAndroid Build Coastguard Worker    # re.match is slow, with several warning line patterns and
438*9e94795aSAndroid Build Coastguard Worker    # long input lines like "TIMEOUT: ...".
439*9e94795aSAndroid Build Coastguard Worker    # We save significant time by skipping non-warning lines.
440*9e94795aSAndroid Build Coastguard Worker    # But do not skip the first 100 lines, because we want to
441*9e94795aSAndroid Build Coastguard Worker    # catch build variables.
442*9e94795aSAndroid Build Coastguard Worker    if line_counter > 100 and line.find('warning: ') < 0:
443*9e94795aSAndroid Build Coastguard Worker      continue
444*9e94795aSAndroid Build Coastguard Worker
445*9e94795aSAndroid Build Coastguard Worker    # A large clean build output can contain up to 90% of duplicated
446*9e94795aSAndroid Build Coastguard Worker    # "warning:" lines. If we can skip them quickly, we can
447*9e94795aSAndroid Build Coastguard Worker    # speed up this for-loop 3X to 5X.
448*9e94795aSAndroid Build Coastguard Worker    if line in checked_warning_lines:
449*9e94795aSAndroid Build Coastguard Worker      continue
450*9e94795aSAndroid Build Coastguard Worker    checked_warning_lines[line] = True
451*9e94795aSAndroid Build Coastguard Worker
452*9e94795aSAndroid Build Coastguard Worker    # Clean up extra prefix that could be introduced when RBE was used.
453*9e94795aSAndroid Build Coastguard Worker    if '/b/f/w/' in line:
454*9e94795aSAndroid Build Coastguard Worker      result = bfw_warning_pattern.search(line)
455*9e94795aSAndroid Build Coastguard Worker    else:
456*9e94795aSAndroid Build Coastguard Worker      result = extra_warning_pattern.search(line)
457*9e94795aSAndroid Build Coastguard Worker    if result is not None:
458*9e94795aSAndroid Build Coastguard Worker      line = result.group(1)
459*9e94795aSAndroid Build Coastguard Worker
460*9e94795aSAndroid Build Coastguard Worker    if warning_pattern.match(line):
461*9e94795aSAndroid Build Coastguard Worker      if line.startswith('warning: '):
462*9e94795aSAndroid Build Coastguard Worker        # save this line and combine it with the next line
463*9e94795aSAndroid Build Coastguard Worker        prev_warning = line
464*9e94795aSAndroid Build Coastguard Worker      else:
465*9e94795aSAndroid Build Coastguard Worker        unique_warnings = add_normalized_line_to_warnings(
466*9e94795aSAndroid Build Coastguard Worker            line, flags, android_root, unique_warnings)
467*9e94795aSAndroid Build Coastguard Worker      continue
468*9e94795aSAndroid Build Coastguard Worker
469*9e94795aSAndroid Build Coastguard Worker    if line_counter < 100:
470*9e94795aSAndroid Build Coastguard Worker      # save a little bit of time by only doing this for the first few lines
471*9e94795aSAndroid Build Coastguard Worker      result = re.search('(?<=^PLATFORM_VERSION=).*', line)
472*9e94795aSAndroid Build Coastguard Worker      if result is not None:
473*9e94795aSAndroid Build Coastguard Worker        platform_version = result.group(0)
474*9e94795aSAndroid Build Coastguard Worker        continue
475*9e94795aSAndroid Build Coastguard Worker      result = re.search('(?<=^TARGET_PRODUCT=).*', line)
476*9e94795aSAndroid Build Coastguard Worker      if result is not None:
477*9e94795aSAndroid Build Coastguard Worker        target_product = result.group(0)
478*9e94795aSAndroid Build Coastguard Worker        continue
479*9e94795aSAndroid Build Coastguard Worker      result = re.search('(?<=^TARGET_BUILD_VARIANT=).*', line)
480*9e94795aSAndroid Build Coastguard Worker      if result is not None:
481*9e94795aSAndroid Build Coastguard Worker        target_variant = result.group(0)
482*9e94795aSAndroid Build Coastguard Worker        continue
483*9e94795aSAndroid Build Coastguard Worker      result = re.search('(?<=^BUILD_ID=).*', line)
484*9e94795aSAndroid Build Coastguard Worker      if result is not None:
485*9e94795aSAndroid Build Coastguard Worker        build_id = result.group(0)
486*9e94795aSAndroid Build Coastguard Worker        continue
487*9e94795aSAndroid Build Coastguard Worker
488*9e94795aSAndroid Build Coastguard Worker  if android_root:
489*9e94795aSAndroid Build Coastguard Worker    new_unique_warnings = dict()
490*9e94795aSAndroid Build Coastguard Worker    for warning_line in unique_warnings:
491*9e94795aSAndroid Build Coastguard Worker      normalized_line = normalize_warning_line(warning_line, flags,
492*9e94795aSAndroid Build Coastguard Worker                                               android_root)
493*9e94795aSAndroid Build Coastguard Worker      new_unique_warnings[normalized_line] = generate_android_cs_link(
494*9e94795aSAndroid Build Coastguard Worker          warning_line, flags, android_root)
495*9e94795aSAndroid Build Coastguard Worker    unique_warnings = new_unique_warnings
496*9e94795aSAndroid Build Coastguard Worker
497*9e94795aSAndroid Build Coastguard Worker  header_str = '%s - %s - %s (%s)' % (
498*9e94795aSAndroid Build Coastguard Worker      platform_version, target_product, target_variant, build_id)
499*9e94795aSAndroid Build Coastguard Worker  return unique_warnings, header_str
500*9e94795aSAndroid Build Coastguard Worker
501*9e94795aSAndroid Build Coastguard Worker
502*9e94795aSAndroid Build Coastguard Workerdef parse_input_file(infile, flags):
503*9e94795aSAndroid Build Coastguard Worker  """Parse one input file for chrome or android."""
504*9e94795aSAndroid Build Coastguard Worker  if flags.platform == 'chrome':
505*9e94795aSAndroid Build Coastguard Worker    return parse_input_file_chrome(infile, flags)
506*9e94795aSAndroid Build Coastguard Worker  if flags.platform == 'android':
507*9e94795aSAndroid Build Coastguard Worker    return parse_input_file_android(infile, flags)
508*9e94795aSAndroid Build Coastguard Worker  raise RuntimeError('parse_input_file not defined for platform %s' %
509*9e94795aSAndroid Build Coastguard Worker                     flags.platform)
510*9e94795aSAndroid Build Coastguard Worker
511*9e94795aSAndroid Build Coastguard Worker
512*9e94795aSAndroid Build Coastguard Workerdef parse_compiler_output(compiler_output):
513*9e94795aSAndroid Build Coastguard Worker  """Parse compiler output for relevant info."""
514*9e94795aSAndroid Build Coastguard Worker  split_output = compiler_output.split(':', 3)  # 3 = max splits
515*9e94795aSAndroid Build Coastguard Worker  file_path = split_output[0]
516*9e94795aSAndroid Build Coastguard Worker  line_number = int(split_output[1])
517*9e94795aSAndroid Build Coastguard Worker  col_number = int(split_output[2].split(' ')[0])
518*9e94795aSAndroid Build Coastguard Worker  warning_message = split_output[3]
519*9e94795aSAndroid Build Coastguard Worker  return file_path, line_number, col_number, warning_message
520*9e94795aSAndroid Build Coastguard Worker
521*9e94795aSAndroid Build Coastguard Worker
522*9e94795aSAndroid Build Coastguard Workerdef get_warn_patterns(platform):
523*9e94795aSAndroid Build Coastguard Worker  """Get and initialize warn_patterns."""
524*9e94795aSAndroid Build Coastguard Worker  warn_patterns = []
525*9e94795aSAndroid Build Coastguard Worker  if platform == 'chrome':
526*9e94795aSAndroid Build Coastguard Worker    warn_patterns = cpp_patterns.warn_patterns
527*9e94795aSAndroid Build Coastguard Worker  elif platform == 'android':
528*9e94795aSAndroid Build Coastguard Worker    warn_patterns = (make_patterns.warn_patterns + cpp_patterns.warn_patterns +
529*9e94795aSAndroid Build Coastguard Worker                     java_patterns.warn_patterns + tidy_patterns.warn_patterns +
530*9e94795aSAndroid Build Coastguard Worker                     other_patterns.warn_patterns)
531*9e94795aSAndroid Build Coastguard Worker  else:
532*9e94795aSAndroid Build Coastguard Worker    raise Exception('platform name %s is not valid' % platform)
533*9e94795aSAndroid Build Coastguard Worker  for pattern in warn_patterns:
534*9e94795aSAndroid Build Coastguard Worker    pattern['members'] = []
535*9e94795aSAndroid Build Coastguard Worker    # Each warning pattern has a 'projects' dictionary, that
536*9e94795aSAndroid Build Coastguard Worker    # maps a project name to number of warnings in that project.
537*9e94795aSAndroid Build Coastguard Worker    pattern['projects'] = {}
538*9e94795aSAndroid Build Coastguard Worker  return warn_patterns
539*9e94795aSAndroid Build Coastguard Worker
540*9e94795aSAndroid Build Coastguard Worker
541*9e94795aSAndroid Build Coastguard Workerdef get_project_list(platform):
542*9e94795aSAndroid Build Coastguard Worker  """Return project list for appropriate platform."""
543*9e94795aSAndroid Build Coastguard Worker  if platform == 'chrome':
544*9e94795aSAndroid Build Coastguard Worker    return chrome_project_list.project_list
545*9e94795aSAndroid Build Coastguard Worker  if platform == 'android':
546*9e94795aSAndroid Build Coastguard Worker    return android_project_list.project_list
547*9e94795aSAndroid Build Coastguard Worker  raise Exception('platform name %s is not valid' % platform)
548*9e94795aSAndroid Build Coastguard Worker
549*9e94795aSAndroid Build Coastguard Worker
550*9e94795aSAndroid Build Coastguard Workerdef parallel_classify_warnings(warning_data, args, project_names,
551*9e94795aSAndroid Build Coastguard Worker                               project_patterns, warn_patterns,
552*9e94795aSAndroid Build Coastguard Worker                               use_google3, create_launch_subprocs_fn,
553*9e94795aSAndroid Build Coastguard Worker                               classify_warnings_fn):
554*9e94795aSAndroid Build Coastguard Worker  """Classify all warning lines with num_cpu parallel processes."""
555*9e94795aSAndroid Build Coastguard Worker  # pylint:disable=too-many-arguments,too-many-locals
556*9e94795aSAndroid Build Coastguard Worker  num_cpu = args.processes
557*9e94795aSAndroid Build Coastguard Worker  group_results = []
558*9e94795aSAndroid Build Coastguard Worker
559*9e94795aSAndroid Build Coastguard Worker  if num_cpu > 1:
560*9e94795aSAndroid Build Coastguard Worker    # set up parallel processing for this...
561*9e94795aSAndroid Build Coastguard Worker    warning_groups = [[] for _ in range(num_cpu)]
562*9e94795aSAndroid Build Coastguard Worker    i = 0
563*9e94795aSAndroid Build Coastguard Worker    for warning, link in warning_data.items():
564*9e94795aSAndroid Build Coastguard Worker      warning_groups[i].append((warning, link))
565*9e94795aSAndroid Build Coastguard Worker      i = (i + 1) % num_cpu
566*9e94795aSAndroid Build Coastguard Worker    arg_groups = [[] for _ in range(num_cpu)]
567*9e94795aSAndroid Build Coastguard Worker    for i, group in enumerate(warning_groups):
568*9e94795aSAndroid Build Coastguard Worker      arg_groups[i] = [{
569*9e94795aSAndroid Build Coastguard Worker          'group': group,
570*9e94795aSAndroid Build Coastguard Worker          'project_patterns': project_patterns,
571*9e94795aSAndroid Build Coastguard Worker          'warn_patterns': warn_patterns,
572*9e94795aSAndroid Build Coastguard Worker          'num_processes': num_cpu
573*9e94795aSAndroid Build Coastguard Worker      }]
574*9e94795aSAndroid Build Coastguard Worker
575*9e94795aSAndroid Build Coastguard Worker    group_results = create_launch_subprocs_fn(num_cpu,
576*9e94795aSAndroid Build Coastguard Worker                                              classify_warnings_fn,
577*9e94795aSAndroid Build Coastguard Worker                                              arg_groups,
578*9e94795aSAndroid Build Coastguard Worker                                              group_results)
579*9e94795aSAndroid Build Coastguard Worker  else:
580*9e94795aSAndroid Build Coastguard Worker    group_results = []
581*9e94795aSAndroid Build Coastguard Worker    for warning, link in warning_data.items():
582*9e94795aSAndroid Build Coastguard Worker      classify_one_warning(warning, link, group_results,
583*9e94795aSAndroid Build Coastguard Worker                           project_patterns, warn_patterns)
584*9e94795aSAndroid Build Coastguard Worker    group_results = [group_results]
585*9e94795aSAndroid Build Coastguard Worker
586*9e94795aSAndroid Build Coastguard Worker  warning_messages = []
587*9e94795aSAndroid Build Coastguard Worker  warning_links = []
588*9e94795aSAndroid Build Coastguard Worker  warning_records = []
589*9e94795aSAndroid Build Coastguard Worker  if use_google3:
590*9e94795aSAndroid Build Coastguard Worker    group_results = [group_results]
591*9e94795aSAndroid Build Coastguard Worker  for group_result in group_results:
592*9e94795aSAndroid Build Coastguard Worker    for result in group_result:
593*9e94795aSAndroid Build Coastguard Worker      for line, link, pattern_idx, project_idx in result:
594*9e94795aSAndroid Build Coastguard Worker        pattern = warn_patterns[pattern_idx]
595*9e94795aSAndroid Build Coastguard Worker        pattern['members'].append(line)
596*9e94795aSAndroid Build Coastguard Worker        message_idx = len(warning_messages)
597*9e94795aSAndroid Build Coastguard Worker        warning_messages.append(line)
598*9e94795aSAndroid Build Coastguard Worker        link_idx = len(warning_links)
599*9e94795aSAndroid Build Coastguard Worker        warning_links.append(link)
600*9e94795aSAndroid Build Coastguard Worker        warning_records.append([pattern_idx, project_idx, message_idx,
601*9e94795aSAndroid Build Coastguard Worker                                link_idx])
602*9e94795aSAndroid Build Coastguard Worker        pname = '???' if project_idx < 0 else project_names[project_idx]
603*9e94795aSAndroid Build Coastguard Worker        # Count warnings by project.
604*9e94795aSAndroid Build Coastguard Worker        if pname in pattern['projects']:
605*9e94795aSAndroid Build Coastguard Worker          pattern['projects'][pname] += 1
606*9e94795aSAndroid Build Coastguard Worker        else:
607*9e94795aSAndroid Build Coastguard Worker          pattern['projects'][pname] = 1
608*9e94795aSAndroid Build Coastguard Worker  return warning_messages, warning_links, warning_records
609*9e94795aSAndroid Build Coastguard Worker
610*9e94795aSAndroid Build Coastguard Worker
611*9e94795aSAndroid Build Coastguard Workerdef process_log(logfile, flags, project_names, project_patterns, warn_patterns,
612*9e94795aSAndroid Build Coastguard Worker                html_path, use_google3, create_launch_subprocs_fn,
613*9e94795aSAndroid Build Coastguard Worker                classify_warnings_fn, logfile_object):
614*9e94795aSAndroid Build Coastguard Worker  # pylint does not recognize g-doc-*
615*9e94795aSAndroid Build Coastguard Worker  # pylint: disable=bad-option-value,g-doc-args
616*9e94795aSAndroid Build Coastguard Worker  # pylint: disable=bad-option-value,g-doc-return-or-yield
617*9e94795aSAndroid Build Coastguard Worker  # pylint: disable=too-many-arguments,too-many-locals
618*9e94795aSAndroid Build Coastguard Worker  """Function that handles processing of a log.
619*9e94795aSAndroid Build Coastguard Worker
620*9e94795aSAndroid Build Coastguard Worker  This is isolated into its own function (rather than just taking place in main)
621*9e94795aSAndroid Build Coastguard Worker  so that it can be used by both warn.py and the borg job process_gs_logs.py, to
622*9e94795aSAndroid Build Coastguard Worker  avoid duplication of code.
623*9e94795aSAndroid Build Coastguard Worker  Note that if the arguments to this function change, process_gs_logs.py must
624*9e94795aSAndroid Build Coastguard Worker  be updated accordingly.
625*9e94795aSAndroid Build Coastguard Worker  """
626*9e94795aSAndroid Build Coastguard Worker  if logfile_object is None:
627*9e94795aSAndroid Build Coastguard Worker    with io.open(logfile, encoding='utf-8') as log:
628*9e94795aSAndroid Build Coastguard Worker      warning_lines_and_links, header_str = parse_input_file(log, flags)
629*9e94795aSAndroid Build Coastguard Worker  else:
630*9e94795aSAndroid Build Coastguard Worker    warning_lines_and_links, header_str = parse_input_file(
631*9e94795aSAndroid Build Coastguard Worker        logfile_object, flags)
632*9e94795aSAndroid Build Coastguard Worker  warning_messages, warning_links, warning_records = parallel_classify_warnings(
633*9e94795aSAndroid Build Coastguard Worker      warning_lines_and_links, flags, project_names, project_patterns,
634*9e94795aSAndroid Build Coastguard Worker      warn_patterns, use_google3, create_launch_subprocs_fn,
635*9e94795aSAndroid Build Coastguard Worker      classify_warnings_fn)
636*9e94795aSAndroid Build Coastguard Worker
637*9e94795aSAndroid Build Coastguard Worker  html_writer.write_html(flags, project_names, warn_patterns, html_path,
638*9e94795aSAndroid Build Coastguard Worker                         warning_messages, warning_links, warning_records,
639*9e94795aSAndroid Build Coastguard Worker                         header_str)
640*9e94795aSAndroid Build Coastguard Worker
641*9e94795aSAndroid Build Coastguard Worker  return warning_messages, warning_links, warning_records, header_str
642*9e94795aSAndroid Build Coastguard Worker
643*9e94795aSAndroid Build Coastguard Worker
644*9e94795aSAndroid Build Coastguard Workerdef common_main(use_google3, create_launch_subprocs_fn, classify_warnings_fn,
645*9e94795aSAndroid Build Coastguard Worker                logfile_object=None):
646*9e94795aSAndroid Build Coastguard Worker  """Shared main function for Google3 and non-Google3 versions of warn.py."""
647*9e94795aSAndroid Build Coastguard Worker  flags = parse_args(use_google3)
648*9e94795aSAndroid Build Coastguard Worker  warn_patterns = get_warn_patterns(flags.platform)
649*9e94795aSAndroid Build Coastguard Worker  project_list = get_project_list(flags.platform)
650*9e94795aSAndroid Build Coastguard Worker
651*9e94795aSAndroid Build Coastguard Worker  project_names = get_project_names(project_list)
652*9e94795aSAndroid Build Coastguard Worker  project_patterns = [re.compile(p[1]) for p in project_list]
653*9e94795aSAndroid Build Coastguard Worker
654*9e94795aSAndroid Build Coastguard Worker  # html_path=None because we output html below if not outputting CSV
655*9e94795aSAndroid Build Coastguard Worker  warning_messages, warning_links, warning_records, header_str = process_log(
656*9e94795aSAndroid Build Coastguard Worker      logfile=flags.log, flags=flags, project_names=project_names,
657*9e94795aSAndroid Build Coastguard Worker      project_patterns=project_patterns, warn_patterns=warn_patterns,
658*9e94795aSAndroid Build Coastguard Worker      html_path=None, use_google3=use_google3,
659*9e94795aSAndroid Build Coastguard Worker      create_launch_subprocs_fn=create_launch_subprocs_fn,
660*9e94795aSAndroid Build Coastguard Worker      classify_warnings_fn=classify_warnings_fn,
661*9e94795aSAndroid Build Coastguard Worker      logfile_object=logfile_object)
662*9e94795aSAndroid Build Coastguard Worker
663*9e94795aSAndroid Build Coastguard Worker  html_writer.write_out_csv(flags, warn_patterns, warning_messages,
664*9e94795aSAndroid Build Coastguard Worker                            warning_links, warning_records, header_str,
665*9e94795aSAndroid Build Coastguard Worker                            project_names)
666*9e94795aSAndroid Build Coastguard Worker
667*9e94795aSAndroid Build Coastguard Worker  # Return these values, so that caller can use them, if desired.
668*9e94795aSAndroid Build Coastguard Worker  return flags, warning_messages, warning_records, warn_patterns
669