blob: b2dd8abffcf2ca9010d3802c28911a8df8a6fd2d [file] [log] [blame]
Chih-Hung Hsieh949205a2020-01-10 10:33:40 -08001# python3
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -08002# Copyright (C) 2019 The Android Open Source Project
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16"""Grep warnings messages and output HTML tables or warning counts in CSV.
17
18Default is to output warnings in HTML tables grouped by warning severity.
19Use option --byproject to output tables grouped by source file projects.
20Use option --gencsv to output warning counts in CSV format.
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -080021
22Default input file is build.log, which can be changed with the --log flag.
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -080023"""
24
25# List of important data structures and functions in this script.
26#
27# To parse and keep warning message in the input file:
28# severity: classification of message severity
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -080029# warn_patterns:
30# warn_patterns[w]['category'] tool that issued the warning, not used now
31# warn_patterns[w]['description'] table heading
32# warn_patterns[w]['members'] matched warnings from input
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -080033# warn_patterns[w]['patterns'] regular expressions to match warnings
34# warn_patterns[w]['projects'][p] number of warnings of pattern w in p
Chih-Hung Hsieh949205a2020-01-10 10:33:40 -080035# warn_patterns[w]['severity'] severity tuple
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -080036# project_list[p][0] project name
37# project_list[p][1] regular expression to match a project path
38# project_patterns[p] re.compile(project_list[p][1])
39# project_names[p] project_list[p][0]
40# warning_messages array of each warning message, without source url
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -080041# warning_links array of each warning code search link; for 'chrome'
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -080042# warning_records array of [idx to warn_patterns,
43# idx to project_names,
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -080044# idx to warning_messages,
45# idx to warning_links]
Chih-Hung Hsieh949205a2020-01-10 10:33:40 -080046# parse_input_file
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -080047#
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -080048import argparse
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -080049import io
50import multiprocessing
51import os
52import re
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -080053import sys
54
55# pylint:disable=relative-beyond-top-level
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -080056# pylint:disable=g-importing-member
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -080057from . import android_project_list
58from . import chrome_project_list
59from . import cpp_warn_patterns as cpp_patterns
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -080060from . import html_writer
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -080061from . import java_warn_patterns as java_patterns
62from . import make_warn_patterns as make_patterns
63from . import other_warn_patterns as other_patterns
64from . import tidy_warn_patterns as tidy_patterns
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -080065
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -080066
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -080067def parse_args(use_google3):
68 """Define and parse the args. Return the parse_args() result."""
69 parser = argparse.ArgumentParser(
70 description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
71 parser.add_argument('--capacitor_path', default='',
72 help='Save capacitor warning file to the passed absolute'
73 ' path')
74 # csvpath has a different naming than the above path because historically the
75 # original Android script used csvpath, so other scripts rely on it
76 parser.add_argument('--csvpath', default='',
77 help='Save CSV warning file to the passed path')
78 parser.add_argument('--gencsv', action='store_true',
79 help='Generate CSV file with number of various warnings')
Saeid Farivar Asanjan75dc8d22020-11-18 00:29:43 +000080 parser.add_argument('--csvwithdescription', default='',
81 help="""Save CSV warning file to the passed path this csv
82 will contain all the warning descriptions""")
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -080083 parser.add_argument('--byproject', action='store_true',
84 help='Separate warnings in HTML output by project names')
85 parser.add_argument('--url', default='',
86 help='Root URL of an Android source code tree prefixed '
87 'before files in warnings')
88 parser.add_argument('--separator', default='?l=',
89 help='Separator between the end of a URL and the line '
90 'number argument. e.g. #')
91 parser.add_argument('--processes', default=multiprocessing.cpu_count(),
92 type=int,
93 help='Number of parallel processes to process warnings')
94 # Old Android build scripts call warn.py without --platform,
95 # so the default platform is set to 'android'.
96 parser.add_argument('--platform', default='android',
97 choices=['chrome', 'android'],
98 help='Platform of the build log')
99 # Old Android build scripts call warn.py with only a build.log file path.
100 parser.add_argument('--log', help='Path to build log file')
101 parser.add_argument(dest='buildlog', metavar='build.log',
102 default='build.log', nargs='?',
103 help='Path to build.log file')
104 flags = parser.parse_args()
105 if not flags.log:
106 flags.log = flags.buildlog
107 if not use_google3 and not os.path.exists(flags.log):
108 sys.exit('Cannot find log file: ' + flags.log)
109 return flags
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800110
111
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800112def get_project_names(project_list):
113 """Get project_names from project_list."""
114 return [p[0] for p in project_list]
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800115
116
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800117def find_project_index(line, project_patterns):
118 for i, p in enumerate(project_patterns):
119 if p.match(line):
120 return i
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800121 return -1
122
123
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800124def classify_one_warning(warning, link, results, project_patterns,
125 warn_patterns):
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800126 """Classify one warning line."""
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800127 for i, w in enumerate(warn_patterns):
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800128 for cpat in w['compiled_patterns']:
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800129 if cpat.match(warning):
130 p = find_project_index(warning, project_patterns)
131 results.append([warning, link, i, p])
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800132 return
133 else:
134 # If we end up here, there was a problem parsing the log
135 # probably caused by 'make -j' mixing the output from
136 # 2 or more concurrent compiles
137 pass
138
139
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800140def remove_prefix(s, sub):
141 """Remove everything before last occurrence of substring sub in string s."""
142 if sub in s:
143 inc_sub = s.rfind(sub)
144 return s[inc_sub:]
145 return s
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800146
147
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800148# TODO(emmavukelj): Don't have any generate_*_cs_link functions call
149# normalize_path a second time (the first time being in parse_input_file)
150def generate_cs_link(warning_line, flags, android_root=None):
151 if flags.platform == 'chrome':
152 return generate_chrome_cs_link(warning_line, flags)
153 if flags.platform == 'android':
154 return generate_android_cs_link(warning_line, flags, android_root)
155 return 'https://cs.corp.google.com/'
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800156
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800157
158def generate_android_cs_link(warning_line, flags, android_root):
159 """Generate the code search link for a warning line in Android."""
160 # max_splits=2 -> only 3 items
161 raw_path, line_number_str, _ = warning_line.split(':', 2)
162 normalized_path = normalize_path(raw_path, flags, android_root)
163 if not flags.url:
164 return normalized_path
165 link_path = flags.url + '/' + normalized_path
166 if line_number_str.isdigit():
167 link_path += flags.separator + line_number_str
168 return link_path
169
170
171def generate_chrome_cs_link(warning_line, flags):
172 """Generate the code search link for a warning line in Chrome."""
173 split_line = warning_line.split(':')
174 raw_path = split_line[0]
175 normalized_path = normalize_path(raw_path, flags)
176 link_base = 'https://cs.chromium.org/'
177 link_add = 'chromium'
178 link_path = None
179
180 # Basically just going through a few specific directory cases and specifying
181 # the proper behavior for that case. This list of cases was accumulated
182 # through trial and error manually going through the warnings.
183 #
184 # This code pattern of using case-specific "if"s instead of "elif"s looks
185 # possibly accidental and mistaken but it is intentional because some paths
186 # fall under several cases (e.g. third_party/lib/nghttp2_frame.c) and for
187 # those we want the most specific case to be applied. If there is reliable
188 # knowledge of exactly where these occur, this could be changed to "elif"s
189 # but there is no reliable set of paths falling under multiple cases at the
190 # moment.
191 if '/src/third_party' in raw_path:
192 link_path = remove_prefix(raw_path, '/src/third_party/')
193 if '/chrome_root/src_internal/' in raw_path:
194 link_path = remove_prefix(raw_path, '/chrome_root/src_internal/')
195 link_path = link_path[len('/chrome_root'):] # remove chrome_root
196 if '/chrome_root/src/' in raw_path:
197 link_path = remove_prefix(raw_path, '/chrome_root/src/')
198 link_path = link_path[len('/chrome_root'):] # remove chrome_root
199 if '/libassistant/' in raw_path:
200 link_add = 'eureka_internal/chromium/src'
201 link_base = 'https://cs.corp.google.com/' # internal data
202 link_path = remove_prefix(normalized_path, '/libassistant/')
203 if raw_path.startswith('gen/'):
204 link_path = '/src/out/Debug/gen/' + normalized_path
205 if '/gen/' in raw_path:
206 return '%s?q=file:%s' % (link_base, remove_prefix(normalized_path, '/gen/'))
207
208 if not link_path and (raw_path.startswith('src/') or
209 raw_path.startswith('src_internal/')):
210 link_path = '/%s' % raw_path
211
212 if not link_path: # can't find specific link, send a query
213 return '%s?q=file:%s' % (link_base, normalized_path)
214
215 line_number = int(split_line[1])
216 link = '%s%s%s?l=%d' % (link_base, link_add, link_path, line_number)
217 return link
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800218
219
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800220def find_warn_py_and_android_root(path):
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800221 """Return android source root path if warn.py is found."""
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800222 parts = path.split('/')
223 for idx in reversed(range(2, len(parts))):
224 root_path = '/'.join(parts[:idx])
225 # Android root directory should contain this script.
226 if os.path.exists(root_path + '/build/make/tools/warn.py'):
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800227 return root_path
228 return ''
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800229
230
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800231def find_android_root(buildlog):
232 """Guess android source root from common prefix of file paths."""
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800233 # Use the longest common prefix of the absolute file paths
234 # of the first 10000 warning messages as the android_root.
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800235 warning_lines = []
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800236 warning_pattern = re.compile('^/[^ ]*/[^ ]*: warning: .*')
237 count = 0
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800238 for line in buildlog:
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800239 if warning_pattern.match(line):
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800240 warning_lines.append(line)
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800241 count += 1
242 if count > 9999:
243 break
244 # Try to find warn.py and use its location to find
245 # the source tree root.
246 if count < 100:
247 path = os.path.normpath(re.sub(':.*$', '', line))
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800248 android_root = find_warn_py_and_android_root(path)
249 if android_root:
250 return android_root
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800251 # Do not use common prefix of a small number of paths.
252 if count > 10:
Chih-Hung Hsieh949205a2020-01-10 10:33:40 -0800253 # pytype: disable=wrong-arg-types
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800254 root_path = os.path.commonprefix(warning_lines)
Chih-Hung Hsieh949205a2020-01-10 10:33:40 -0800255 # pytype: enable=wrong-arg-types
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800256 if len(root_path) > 2 and root_path[len(root_path) - 1] == '/':
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800257 return root_path[:-1]
258 return ''
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800259
260
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800261def remove_android_root_prefix(path, android_root):
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800262 """Remove android_root prefix from path if it is found."""
263 if path.startswith(android_root):
264 return path[1 + len(android_root):]
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800265 return path
266
267
268def normalize_path(path, flags, android_root=None):
269 """Normalize file path relative to src/ or src-internal/ directory."""
270 path = os.path.normpath(path)
271
272 if flags.platform == 'android':
273 if android_root:
274 return remove_android_root_prefix(path, android_root)
275 return path
276
277 # Remove known prefix of root path and normalize the suffix.
278 idx = path.find('chrome_root/')
279 if idx >= 0:
280 # remove chrome_root/, we want path relative to that
281 return path[idx + len('chrome_root/'):]
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800282 else:
283 return path
284
285
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800286def normalize_warning_line(line, flags, android_root=None):
287 """Normalize file path relative to src directory in a warning line."""
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800288 line = re.sub(u'[\u2018\u2019]', '\'', line)
289 # replace non-ASCII chars to spaces
290 line = re.sub(u'[^\x00-\x7f]', ' ', line)
291 line = line.strip()
292 first_column = line.find(':')
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800293 return normalize_path(line[:first_column], flags,
294 android_root) + line[first_column:]
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800295
296
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800297def parse_input_file_chrome(infile, flags):
298 """Parse Chrome input file, collect parameters and warning lines."""
299 platform_version = 'unknown'
300 board_name = 'unknown'
301 architecture = 'unknown'
302
303 # only handle warning lines of format 'file_path:line_no:col_no: warning: ...'
304 chrome_warning_pattern = r'^[^ ]*/[^ ]*:[0-9]+:[0-9]+: warning: .*'
305
306 warning_pattern = re.compile(chrome_warning_pattern)
307
308 # Collect all unique warning lines
309 # Remove the duplicated warnings save ~8% of time when parsing
310 # one typical build log than before
311 unique_warnings = dict()
312 for line in infile:
313 if warning_pattern.match(line):
314 normalized_line = normalize_warning_line(line, flags)
315 if normalized_line not in unique_warnings:
316 unique_warnings[normalized_line] = generate_cs_link(line, flags)
317 elif (platform_version == 'unknown' or board_name == 'unknown' or
318 architecture == 'unknown'):
319 m = re.match(r'.+Package:.+chromeos-base/chromeos-chrome-', line)
320 if m is not None:
321 platform_version = 'R' + line.split('chrome-')[1].split('_')[0]
322 continue
323 m = re.match(r'.+Source\sunpacked\sin\s(.+)', line)
324 if m is not None:
325 board_name = m.group(1).split('/')[2]
326 continue
327 m = re.match(r'.+USE:\s*([^\s]*).*', line)
328 if m is not None:
329 architecture = m.group(1)
330 continue
331
332 header_str = '%s - %s - %s' % (platform_version, board_name, architecture)
333 return unique_warnings, header_str
334
335
336def add_normalized_line_to_warnings(line, flags, android_root, unique_warnings):
337 """Parse/normalize path, updating warning line and add to warnings dict."""
338 normalized_line = normalize_warning_line(line, flags, android_root)
339 if normalized_line not in unique_warnings:
340 unique_warnings[normalized_line] = generate_cs_link(line, flags,
341 android_root)
342 return unique_warnings
343
344
345def parse_input_file_android(infile, flags):
346 """Parse Android input file, collect parameters and warning lines."""
347 platform_version = 'unknown'
348 target_product = 'unknown'
349 target_variant = 'unknown'
350 android_root = find_android_root(infile)
351 infile.seek(0)
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800352
353 # rustc warning messages have two lines that should be combined:
354 # warning: description
355 # --> file_path:line_number:column_number
356 # Some warning messages have no file name:
357 # warning: macro replacement list ... [bugprone-macro-parentheses]
358 # Some makefile warning messages have no line number:
359 # some/path/file.mk: warning: description
360 # C/C++ compiler warning messages have line and column numbers:
361 # some/path/file.c:line_number:column_number: warning: description
362 warning_pattern = re.compile('(^[^ ]*/[^ ]*: warning: .*)|(^warning: .*)')
363 warning_without_file = re.compile('^warning: .*')
364 rustc_file_position = re.compile('^[ ]+--> [^ ]*/[^ ]*:[0-9]+:[0-9]+')
365
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800366 # Collect all unique warning lines
367 # Remove the duplicated warnings save ~8% of time when parsing
368 # one typical build log than before
369 unique_warnings = dict()
370 line_counter = 0
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800371 prev_warning = ''
372 for line in infile:
373 if prev_warning:
374 if rustc_file_position.match(line):
375 # must be a rustc warning, combine 2 lines into one warning
376 line = line.strip().replace('--> ', '') + ': ' + prev_warning
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800377 unique_warnings = add_normalized_line_to_warnings(
378 line, flags, android_root, unique_warnings)
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800379 prev_warning = ''
380 continue
381 # add prev_warning, and then process the current line
382 prev_warning = 'unknown_source_file: ' + prev_warning
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800383 unique_warnings = add_normalized_line_to_warnings(
384 prev_warning, flags, android_root, unique_warnings)
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800385 prev_warning = ''
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800386
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800387 if warning_pattern.match(line):
388 if warning_without_file.match(line):
389 # save this line and combine it with the next line
390 prev_warning = line
391 else:
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800392 unique_warnings = add_normalized_line_to_warnings(
393 line, flags, android_root, unique_warnings)
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800394 continue
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800395
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800396 if line_counter < 100:
397 # save a little bit of time by only doing this for the first few lines
398 line_counter += 1
399 m = re.search('(?<=^PLATFORM_VERSION=).*', line)
400 if m is not None:
401 platform_version = m.group(0)
402 m = re.search('(?<=^TARGET_PRODUCT=).*', line)
403 if m is not None:
404 target_product = m.group(0)
405 m = re.search('(?<=^TARGET_BUILD_VARIANT=).*', line)
406 if m is not None:
407 target_variant = m.group(0)
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800408 m = re.search('(?<=^TOP=).*', line)
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800409 if m is not None:
410 android_root = m.group(1)
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800411
412 if android_root:
413 new_unique_warnings = dict()
414 for warning_line in unique_warnings:
415 normalized_line = normalize_warning_line(warning_line, flags,
416 android_root)
417 new_unique_warnings[normalized_line] = generate_android_cs_link(
418 warning_line, flags, android_root)
419 unique_warnings = new_unique_warnings
420
421 header_str = '%s - %s - %s' % (platform_version, target_product,
422 target_variant)
423 return unique_warnings, header_str
424
425
426def parse_input_file(infile, flags):
427 if flags.platform == 'chrome':
428 return parse_input_file_chrome(infile, flags)
429 if flags.platform == 'android':
430 return parse_input_file_android(infile, flags)
431 raise RuntimeError('parse_input_file not defined for platform %s' %
432 flags.platform)
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800433
434
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800435def parse_compiler_output(compiler_output):
436 """Parse compiler output for relevant info."""
437 split_output = compiler_output.split(':', 3) # 3 = max splits
438 file_path = split_output[0]
439 line_number = int(split_output[1])
440 col_number = int(split_output[2].split(' ')[0])
441 warning_message = split_output[3]
442 return file_path, line_number, col_number, warning_message
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800443
444
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800445def get_warn_patterns(platform):
446 """Get and initialize warn_patterns."""
447 warn_patterns = []
448 if platform == 'chrome':
449 warn_patterns = cpp_patterns.warn_patterns
450 elif platform == 'android':
451 warn_patterns = make_patterns.warn_patterns + cpp_patterns.warn_patterns + java_patterns.warn_patterns + tidy_patterns.warn_patterns + other_patterns.warn_patterns
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800452 else:
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800453 raise Exception('platform name %s is not valid' % platform)
454 for w in warn_patterns:
455 w['members'] = []
456 # Each warning pattern has a 'projects' dictionary, that
457 # maps a project name to number of warnings in that project.
458 w['projects'] = {}
459 return warn_patterns
460
461
462def get_project_list(platform):
463 """Return project list for appropriate platform."""
464 if platform == 'chrome':
465 return chrome_project_list.project_list
466 if platform == 'android':
467 return android_project_list.project_list
468 raise Exception('platform name %s is not valid' % platform)
469
470
471def parallel_classify_warnings(warning_data, args, project_names,
472 project_patterns, warn_patterns,
473 use_google3, create_launch_subprocs_fn,
474 classify_warnings_fn):
475 """Classify all warning lines with num_cpu parallel processes."""
476 num_cpu = args.processes
477 group_results = []
478
479 if num_cpu > 1:
480 # set up parallel processing for this...
481 warning_groups = [[] for _ in range(num_cpu)]
482 i = 0
483 for warning, link in warning_data.items():
484 warning_groups[i].append((warning, link))
485 i = (i + 1) % num_cpu
486 arg_groups = [[] for _ in range(num_cpu)]
487 for i, group in enumerate(warning_groups):
488 arg_groups[i] = [{
489 'group': group,
490 'project_patterns': project_patterns,
491 'warn_patterns': warn_patterns,
492 'num_processes': num_cpu
493 }]
494
495 group_results = create_launch_subprocs_fn(num_cpu,
496 classify_warnings_fn,
497 arg_groups,
498 group_results)
499 else:
500 group_results = []
501 for warning, link in warning_data.items():
502 classify_one_warning(warning, link, group_results,
503 project_patterns, warn_patterns)
504 group_results = [group_results]
505
506 warning_messages = []
507 warning_links = []
508 warning_records = []
509 if use_google3:
510 group_results = [group_results]
511 for group_result in group_results:
512 for result in group_result:
513 for line, link, pattern_idx, project_idx in result:
514 pattern = warn_patterns[pattern_idx]
515 pattern['members'].append(line)
516 message_idx = len(warning_messages)
517 warning_messages.append(line)
518 link_idx = len(warning_links)
519 warning_links.append(link)
520 warning_records.append([pattern_idx, project_idx, message_idx,
521 link_idx])
522 pname = '???' if project_idx < 0 else project_names[project_idx]
523 # Count warnings by project.
524 if pname in pattern['projects']:
525 pattern['projects'][pname] += 1
526 else:
527 pattern['projects'][pname] = 1
528 return warning_messages, warning_links, warning_records
529
530
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800531def process_log(logfile, flags, project_names, project_patterns, warn_patterns,
532 html_path, use_google3, create_launch_subprocs_fn,
533 classify_warnings_fn, logfile_object):
534 # pylint: disable=g-doc-args
535 # pylint: disable=g-doc-return-or-yield
536 """Function that handles processing of a log.
537
538 This is isolated into its own function (rather than just taking place in main)
539 so that it can be used by both warn.py and the borg job process_gs_logs.py, to
540 avoid duplication of code.
541 Note that if the arguments to this function change, process_gs_logs.py must
542 be updated accordingly.
543 """
544 if logfile_object is None:
545 with io.open(logfile, encoding='utf-8') as log:
546 warning_lines_and_links, header_str = parse_input_file(log, flags)
547 else:
548 warning_lines_and_links, header_str = parse_input_file(
549 logfile_object, flags)
550 warning_messages, warning_links, warning_records = parallel_classify_warnings(
551 warning_lines_and_links, flags, project_names, project_patterns,
552 warn_patterns, use_google3, create_launch_subprocs_fn,
553 classify_warnings_fn)
554
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800555 html_writer.write_html(flags, project_names, warn_patterns, html_path,
556 warning_messages, warning_links, warning_records,
557 header_str)
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800558
559 return warning_messages, warning_links, warning_records, header_str
560
561
562def common_main(use_google3, create_launch_subprocs_fn, classify_warnings_fn,
563 logfile_object=None):
564 """Shared main function for Google3 and non-Google3 versions of warn.py."""
565 flags = parse_args(use_google3)
566 warn_patterns = get_warn_patterns(flags.platform)
567 project_list = get_project_list(flags.platform)
568
569 project_names = get_project_names(project_list)
570 project_patterns = [re.compile(p[1]) for p in project_list]
571
572 # html_path=None because we output html below if not outputting CSV
573 warning_messages, warning_links, warning_records, header_str = process_log(
574 logfile=flags.log, flags=flags, project_names=project_names,
575 project_patterns=project_patterns, warn_patterns=warn_patterns,
576 html_path=None, use_google3=use_google3,
577 create_launch_subprocs_fn=create_launch_subprocs_fn,
578 classify_warnings_fn=classify_warnings_fn,
579 logfile_object=logfile_object)
580
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800581 html_writer.write_out_csv(flags, warn_patterns, warning_messages,
582 warning_links, warning_records, header_str,
583 project_names)
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800584
585 # Return these values, so that caller can use them, if desired.
586 return flags, warning_messages, warning_records, warn_patterns