blob: d69050f54f039cf096c38e001ea2eec314c023f3 [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
Chih-Hung Hsieh98b285d2021-04-28 14:49:32 -070055# pylint:disable=relative-beyond-top-level,no-name-in-module
56# suppress false positive of no-name-in-module warnings
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):
Chih-Hung Hsieh98b285d2021-04-28 14:49:32 -0700118 """Return the index to the project pattern array."""
119 # pylint:disable=invalid-name
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800120 for i, p in enumerate(project_patterns):
121 if p.match(line):
122 return i
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800123 return -1
124
125
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800126def classify_one_warning(warning, link, results, project_patterns,
127 warn_patterns):
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800128 """Classify one warning line."""
Chih-Hung Hsieh98b285d2021-04-28 14:49:32 -0700129 # pylint:disable=invalid-name
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800130 for i, w in enumerate(warn_patterns):
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800131 for cpat in w['compiled_patterns']:
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800132 if cpat.match(warning):
133 p = find_project_index(warning, project_patterns)
134 results.append([warning, link, i, p])
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800135 return
Chih-Hung Hsieh98b285d2021-04-28 14:49:32 -0700136 # If we end up here, there was a problem parsing the log
137 # probably caused by 'make -j' mixing the output from
138 # 2 or more concurrent compiles
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800139
140
Chih-Hung Hsieh98b285d2021-04-28 14:49:32 -0700141def remove_prefix(src, sub):
142 """Remove everything before last occurrence of substring sub in string src."""
143 if sub in src:
144 inc_sub = src.rfind(sub)
145 return src[inc_sub:]
146 return src
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800147
148
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800149# TODO(emmavukelj): Don't have any generate_*_cs_link functions call
150# normalize_path a second time (the first time being in parse_input_file)
151def generate_cs_link(warning_line, flags, android_root=None):
Chih-Hung Hsieh98b285d2021-04-28 14:49:32 -0700152 """Try to add code search HTTP URL prefix."""
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800153 if flags.platform == 'chrome':
154 return generate_chrome_cs_link(warning_line, flags)
155 if flags.platform == 'android':
156 return generate_android_cs_link(warning_line, flags, android_root)
157 return 'https://cs.corp.google.com/'
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800158
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800159
160def generate_android_cs_link(warning_line, flags, android_root):
161 """Generate the code search link for a warning line in Android."""
162 # max_splits=2 -> only 3 items
163 raw_path, line_number_str, _ = warning_line.split(':', 2)
164 normalized_path = normalize_path(raw_path, flags, android_root)
165 if not flags.url:
166 return normalized_path
167 link_path = flags.url + '/' + normalized_path
168 if line_number_str.isdigit():
169 link_path += flags.separator + line_number_str
170 return link_path
171
172
173def generate_chrome_cs_link(warning_line, flags):
174 """Generate the code search link for a warning line in Chrome."""
175 split_line = warning_line.split(':')
176 raw_path = split_line[0]
177 normalized_path = normalize_path(raw_path, flags)
178 link_base = 'https://cs.chromium.org/'
179 link_add = 'chromium'
180 link_path = None
181
182 # Basically just going through a few specific directory cases and specifying
183 # the proper behavior for that case. This list of cases was accumulated
184 # through trial and error manually going through the warnings.
185 #
186 # This code pattern of using case-specific "if"s instead of "elif"s looks
187 # possibly accidental and mistaken but it is intentional because some paths
188 # fall under several cases (e.g. third_party/lib/nghttp2_frame.c) and for
189 # those we want the most specific case to be applied. If there is reliable
190 # knowledge of exactly where these occur, this could be changed to "elif"s
191 # but there is no reliable set of paths falling under multiple cases at the
192 # moment.
193 if '/src/third_party' in raw_path:
194 link_path = remove_prefix(raw_path, '/src/third_party/')
195 if '/chrome_root/src_internal/' in raw_path:
196 link_path = remove_prefix(raw_path, '/chrome_root/src_internal/')
197 link_path = link_path[len('/chrome_root'):] # remove chrome_root
198 if '/chrome_root/src/' in raw_path:
199 link_path = remove_prefix(raw_path, '/chrome_root/src/')
200 link_path = link_path[len('/chrome_root'):] # remove chrome_root
201 if '/libassistant/' in raw_path:
202 link_add = 'eureka_internal/chromium/src'
203 link_base = 'https://cs.corp.google.com/' # internal data
204 link_path = remove_prefix(normalized_path, '/libassistant/')
205 if raw_path.startswith('gen/'):
206 link_path = '/src/out/Debug/gen/' + normalized_path
207 if '/gen/' in raw_path:
208 return '%s?q=file:%s' % (link_base, remove_prefix(normalized_path, '/gen/'))
209
210 if not link_path and (raw_path.startswith('src/') or
211 raw_path.startswith('src_internal/')):
212 link_path = '/%s' % raw_path
213
214 if not link_path: # can't find specific link, send a query
215 return '%s?q=file:%s' % (link_base, normalized_path)
216
217 line_number = int(split_line[1])
218 link = '%s%s%s?l=%d' % (link_base, link_add, link_path, line_number)
219 return link
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800220
221
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800222def find_warn_py_and_android_root(path):
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800223 """Return android source root path if warn.py is found."""
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800224 parts = path.split('/')
225 for idx in reversed(range(2, len(parts))):
226 root_path = '/'.join(parts[:idx])
227 # Android root directory should contain this script.
228 if os.path.exists(root_path + '/build/make/tools/warn.py'):
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800229 return root_path
230 return ''
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800231
232
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800233def find_android_root(buildlog):
234 """Guess android source root from common prefix of file paths."""
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800235 # Use the longest common prefix of the absolute file paths
236 # of the first 10000 warning messages as the android_root.
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800237 warning_lines = []
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800238 warning_pattern = re.compile('^/[^ ]*/[^ ]*: warning: .*')
239 count = 0
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800240 for line in buildlog:
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800241 if warning_pattern.match(line):
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800242 warning_lines.append(line)
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800243 count += 1
244 if count > 9999:
245 break
246 # Try to find warn.py and use its location to find
247 # the source tree root.
248 if count < 100:
249 path = os.path.normpath(re.sub(':.*$', '', line))
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800250 android_root = find_warn_py_and_android_root(path)
251 if android_root:
252 return android_root
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800253 # Do not use common prefix of a small number of paths.
254 if count > 10:
Chih-Hung Hsieh949205a2020-01-10 10:33:40 -0800255 # pytype: disable=wrong-arg-types
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800256 root_path = os.path.commonprefix(warning_lines)
Chih-Hung Hsieh949205a2020-01-10 10:33:40 -0800257 # pytype: enable=wrong-arg-types
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800258 if len(root_path) > 2 and root_path[len(root_path) - 1] == '/':
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800259 return root_path[:-1]
260 return ''
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800261
262
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800263def remove_android_root_prefix(path, android_root):
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800264 """Remove android_root prefix from path if it is found."""
265 if path.startswith(android_root):
266 return path[1 + len(android_root):]
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800267 return path
268
269
270def normalize_path(path, flags, android_root=None):
271 """Normalize file path relative to src/ or src-internal/ directory."""
272 path = os.path.normpath(path)
273
274 if flags.platform == 'android':
275 if android_root:
276 return remove_android_root_prefix(path, android_root)
277 return path
278
279 # Remove known prefix of root path and normalize the suffix.
280 idx = path.find('chrome_root/')
281 if idx >= 0:
282 # remove chrome_root/, we want path relative to that
283 return path[idx + len('chrome_root/'):]
Chih-Hung Hsieh98b285d2021-04-28 14:49:32 -0700284 return path
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800285
286
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800287def normalize_warning_line(line, flags, android_root=None):
288 """Normalize file path relative to src directory in a warning line."""
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800289 line = re.sub(u'[\u2018\u2019]', '\'', line)
290 # replace non-ASCII chars to spaces
291 line = re.sub(u'[^\x00-\x7f]', ' ', line)
292 line = line.strip()
293 first_column = line.find(':')
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800294 return normalize_path(line[:first_column], flags,
295 android_root) + line[first_column:]
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800296
297
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800298def parse_input_file_chrome(infile, flags):
299 """Parse Chrome input file, collect parameters and warning lines."""
300 platform_version = 'unknown'
301 board_name = 'unknown'
302 architecture = 'unknown'
303
304 # only handle warning lines of format 'file_path:line_no:col_no: warning: ...'
305 chrome_warning_pattern = r'^[^ ]*/[^ ]*:[0-9]+:[0-9]+: warning: .*'
306
307 warning_pattern = re.compile(chrome_warning_pattern)
308
309 # Collect all unique warning lines
310 # Remove the duplicated warnings save ~8% of time when parsing
311 # one typical build log than before
312 unique_warnings = dict()
Chih-Hung Hsieh98b285d2021-04-28 14:49:32 -0700313 # pylint:disable=invalid-name
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800314 for line in infile:
315 if warning_pattern.match(line):
316 normalized_line = normalize_warning_line(line, flags)
317 if normalized_line not in unique_warnings:
318 unique_warnings[normalized_line] = generate_cs_link(line, flags)
319 elif (platform_version == 'unknown' or board_name == 'unknown' or
320 architecture == 'unknown'):
321 m = re.match(r'.+Package:.+chromeos-base/chromeos-chrome-', line)
322 if m is not None:
323 platform_version = 'R' + line.split('chrome-')[1].split('_')[0]
324 continue
325 m = re.match(r'.+Source\sunpacked\sin\s(.+)', line)
326 if m is not None:
327 board_name = m.group(1).split('/')[2]
328 continue
329 m = re.match(r'.+USE:\s*([^\s]*).*', line)
330 if m is not None:
331 architecture = m.group(1)
332 continue
333
334 header_str = '%s - %s - %s' % (platform_version, board_name, architecture)
335 return unique_warnings, header_str
336
337
338def add_normalized_line_to_warnings(line, flags, android_root, unique_warnings):
339 """Parse/normalize path, updating warning line and add to warnings dict."""
340 normalized_line = normalize_warning_line(line, flags, android_root)
341 if normalized_line not in unique_warnings:
342 unique_warnings[normalized_line] = generate_cs_link(line, flags,
343 android_root)
344 return unique_warnings
345
346
347def parse_input_file_android(infile, flags):
348 """Parse Android input file, collect parameters and warning lines."""
Chih-Hung Hsieh98b285d2021-04-28 14:49:32 -0700349 # pylint:disable=too-many-locals,too-many-branches
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800350 platform_version = 'unknown'
351 target_product = 'unknown'
352 target_variant = 'unknown'
353 android_root = find_android_root(infile)
354 infile.seek(0)
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800355
356 # rustc warning messages have two lines that should be combined:
357 # warning: description
358 # --> file_path:line_number:column_number
359 # Some warning messages have no file name:
360 # warning: macro replacement list ... [bugprone-macro-parentheses]
361 # Some makefile warning messages have no line number:
362 # some/path/file.mk: warning: description
363 # C/C++ compiler warning messages have line and column numbers:
364 # some/path/file.c:line_number:column_number: warning: description
365 warning_pattern = re.compile('(^[^ ]*/[^ ]*: warning: .*)|(^warning: .*)')
366 warning_without_file = re.compile('^warning: .*')
367 rustc_file_position = re.compile('^[ ]+--> [^ ]*/[^ ]*:[0-9]+:[0-9]+')
368
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800369 # Collect all unique warning lines
370 # Remove the duplicated warnings save ~8% of time when parsing
371 # one typical build log than before
372 unique_warnings = dict()
373 line_counter = 0
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800374 prev_warning = ''
375 for line in infile:
376 if prev_warning:
377 if rustc_file_position.match(line):
378 # must be a rustc warning, combine 2 lines into one warning
379 line = line.strip().replace('--> ', '') + ': ' + prev_warning
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800380 unique_warnings = add_normalized_line_to_warnings(
381 line, flags, android_root, unique_warnings)
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800382 prev_warning = ''
383 continue
384 # add prev_warning, and then process the current line
385 prev_warning = 'unknown_source_file: ' + prev_warning
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800386 unique_warnings = add_normalized_line_to_warnings(
387 prev_warning, flags, android_root, unique_warnings)
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800388 prev_warning = ''
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800389
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800390 if warning_pattern.match(line):
391 if warning_without_file.match(line):
392 # save this line and combine it with the next line
393 prev_warning = line
394 else:
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800395 unique_warnings = add_normalized_line_to_warnings(
396 line, flags, android_root, unique_warnings)
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800397 continue
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800398
Chih-Hung Hsieh98b285d2021-04-28 14:49:32 -0700399 # pylint:disable=invalid-name
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800400 if line_counter < 100:
401 # save a little bit of time by only doing this for the first few lines
402 line_counter += 1
403 m = re.search('(?<=^PLATFORM_VERSION=).*', line)
404 if m is not None:
405 platform_version = m.group(0)
406 m = re.search('(?<=^TARGET_PRODUCT=).*', line)
407 if m is not None:
408 target_product = m.group(0)
409 m = re.search('(?<=^TARGET_BUILD_VARIANT=).*', line)
410 if m is not None:
411 target_variant = m.group(0)
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800412 m = re.search('(?<=^TOP=).*', line)
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800413 if m is not None:
414 android_root = m.group(1)
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800415
416 if android_root:
417 new_unique_warnings = dict()
418 for warning_line in unique_warnings:
419 normalized_line = normalize_warning_line(warning_line, flags,
420 android_root)
421 new_unique_warnings[normalized_line] = generate_android_cs_link(
422 warning_line, flags, android_root)
423 unique_warnings = new_unique_warnings
424
425 header_str = '%s - %s - %s' % (platform_version, target_product,
426 target_variant)
427 return unique_warnings, header_str
428
429
430def parse_input_file(infile, flags):
Chih-Hung Hsieh98b285d2021-04-28 14:49:32 -0700431 """Parse one input file for chrome or android."""
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800432 if flags.platform == 'chrome':
433 return parse_input_file_chrome(infile, flags)
434 if flags.platform == 'android':
435 return parse_input_file_android(infile, flags)
436 raise RuntimeError('parse_input_file not defined for platform %s' %
437 flags.platform)
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800438
439
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800440def parse_compiler_output(compiler_output):
441 """Parse compiler output for relevant info."""
442 split_output = compiler_output.split(':', 3) # 3 = max splits
443 file_path = split_output[0]
444 line_number = int(split_output[1])
445 col_number = int(split_output[2].split(' ')[0])
446 warning_message = split_output[3]
447 return file_path, line_number, col_number, warning_message
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800448
449
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800450def get_warn_patterns(platform):
451 """Get and initialize warn_patterns."""
452 warn_patterns = []
453 if platform == 'chrome':
454 warn_patterns = cpp_patterns.warn_patterns
455 elif platform == 'android':
Chih-Hung Hsieh98b285d2021-04-28 14:49:32 -0700456 warn_patterns = (make_patterns.warn_patterns + cpp_patterns.warn_patterns +
457 java_patterns.warn_patterns + tidy_patterns.warn_patterns +
458 other_patterns.warn_patterns)
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800459 else:
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800460 raise Exception('platform name %s is not valid' % platform)
Chih-Hung Hsieh98b285d2021-04-28 14:49:32 -0700461 # pylint:disable=invalid-name
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800462 for w in warn_patterns:
463 w['members'] = []
464 # Each warning pattern has a 'projects' dictionary, that
465 # maps a project name to number of warnings in that project.
466 w['projects'] = {}
467 return warn_patterns
468
469
470def get_project_list(platform):
471 """Return project list for appropriate platform."""
472 if platform == 'chrome':
473 return chrome_project_list.project_list
474 if platform == 'android':
475 return android_project_list.project_list
476 raise Exception('platform name %s is not valid' % platform)
477
478
479def parallel_classify_warnings(warning_data, args, project_names,
480 project_patterns, warn_patterns,
481 use_google3, create_launch_subprocs_fn,
482 classify_warnings_fn):
483 """Classify all warning lines with num_cpu parallel processes."""
Chih-Hung Hsieh98b285d2021-04-28 14:49:32 -0700484 # pylint:disable=too-many-arguments,too-many-locals
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800485 num_cpu = args.processes
486 group_results = []
487
488 if num_cpu > 1:
489 # set up parallel processing for this...
490 warning_groups = [[] for _ in range(num_cpu)]
491 i = 0
492 for warning, link in warning_data.items():
493 warning_groups[i].append((warning, link))
494 i = (i + 1) % num_cpu
495 arg_groups = [[] for _ in range(num_cpu)]
496 for i, group in enumerate(warning_groups):
497 arg_groups[i] = [{
498 'group': group,
499 'project_patterns': project_patterns,
500 'warn_patterns': warn_patterns,
501 'num_processes': num_cpu
502 }]
503
504 group_results = create_launch_subprocs_fn(num_cpu,
505 classify_warnings_fn,
506 arg_groups,
507 group_results)
508 else:
509 group_results = []
510 for warning, link in warning_data.items():
511 classify_one_warning(warning, link, group_results,
512 project_patterns, warn_patterns)
513 group_results = [group_results]
514
515 warning_messages = []
516 warning_links = []
517 warning_records = []
518 if use_google3:
519 group_results = [group_results]
520 for group_result in group_results:
521 for result in group_result:
522 for line, link, pattern_idx, project_idx in result:
523 pattern = warn_patterns[pattern_idx]
524 pattern['members'].append(line)
525 message_idx = len(warning_messages)
526 warning_messages.append(line)
527 link_idx = len(warning_links)
528 warning_links.append(link)
529 warning_records.append([pattern_idx, project_idx, message_idx,
530 link_idx])
531 pname = '???' if project_idx < 0 else project_names[project_idx]
532 # Count warnings by project.
533 if pname in pattern['projects']:
534 pattern['projects'][pname] += 1
535 else:
536 pattern['projects'][pname] = 1
537 return warning_messages, warning_links, warning_records
538
539
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800540def process_log(logfile, flags, project_names, project_patterns, warn_patterns,
541 html_path, use_google3, create_launch_subprocs_fn,
542 classify_warnings_fn, logfile_object):
Chih-Hung Hsieh98b285d2021-04-28 14:49:32 -0700543 # pylint does not recognize g-doc-*
544 # pylint: disable=bad-option-value,g-doc-args
545 # pylint: disable=bad-option-value,g-doc-return-or-yield
546 # pylint: disable=too-many-arguments,too-many-locals
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800547 """Function that handles processing of a log.
548
549 This is isolated into its own function (rather than just taking place in main)
550 so that it can be used by both warn.py and the borg job process_gs_logs.py, to
551 avoid duplication of code.
552 Note that if the arguments to this function change, process_gs_logs.py must
553 be updated accordingly.
554 """
555 if logfile_object is None:
556 with io.open(logfile, encoding='utf-8') as log:
557 warning_lines_and_links, header_str = parse_input_file(log, flags)
558 else:
559 warning_lines_and_links, header_str = parse_input_file(
560 logfile_object, flags)
561 warning_messages, warning_links, warning_records = parallel_classify_warnings(
562 warning_lines_and_links, flags, project_names, project_patterns,
563 warn_patterns, use_google3, create_launch_subprocs_fn,
564 classify_warnings_fn)
565
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800566 html_writer.write_html(flags, project_names, warn_patterns, html_path,
567 warning_messages, warning_links, warning_records,
568 header_str)
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800569
570 return warning_messages, warning_links, warning_records, header_str
571
572
573def common_main(use_google3, create_launch_subprocs_fn, classify_warnings_fn,
574 logfile_object=None):
575 """Shared main function for Google3 and non-Google3 versions of warn.py."""
576 flags = parse_args(use_google3)
577 warn_patterns = get_warn_patterns(flags.platform)
578 project_list = get_project_list(flags.platform)
579
580 project_names = get_project_names(project_list)
581 project_patterns = [re.compile(p[1]) for p in project_list]
582
583 # html_path=None because we output html below if not outputting CSV
584 warning_messages, warning_links, warning_records, header_str = process_log(
585 logfile=flags.log, flags=flags, project_names=project_names,
586 project_patterns=project_patterns, warn_patterns=warn_patterns,
587 html_path=None, use_google3=use_google3,
588 create_launch_subprocs_fn=create_launch_subprocs_fn,
589 classify_warnings_fn=classify_warnings_fn,
590 logfile_object=logfile_object)
591
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800592 html_writer.write_out_csv(flags, warn_patterns, warning_messages,
593 warning_links, warning_records, header_str,
594 project_names)
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800595
596 # Return these values, so that caller can use them, if desired.
597 return flags, warning_messages, warning_records, warn_patterns