blob: 0c9d9efbbddfefa0623ab8683838841b17dd0905 [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.
21"""
22
23# List of important data structures and functions in this script.
24#
25# To parse and keep warning message in the input file:
26# severity: classification of message severity
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -080027# warn_patterns:
28# warn_patterns[w]['category'] tool that issued the warning, not used now
29# warn_patterns[w]['description'] table heading
30# warn_patterns[w]['members'] matched warnings from input
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -080031# warn_patterns[w]['patterns'] regular expressions to match warnings
32# warn_patterns[w]['projects'][p] number of warnings of pattern w in p
Chih-Hung Hsieh949205a2020-01-10 10:33:40 -080033# warn_patterns[w]['severity'] severity tuple
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -080034# project_list[p][0] project name
35# project_list[p][1] regular expression to match a project path
36# project_patterns[p] re.compile(project_list[p][1])
37# project_names[p] project_list[p][0]
38# warning_messages array of each warning message, without source url
39# warning_records array of [idx to warn_patterns,
40# idx to project_names,
41# idx to warning_messages]
42# android_root
43# platform_version
44# target_product
45# target_variant
Chih-Hung Hsieh949205a2020-01-10 10:33:40 -080046# parse_input_file
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -080047#
48# To emit html page of warning messages:
49# flags: --byproject, --url, --separator
50# Old stuff for static html components:
51# html_script_style: static html scripts and styles
52# htmlbig:
53# dump_stats, dump_html_prologue, dump_html_epilogue:
54# emit_buttons:
55# dump_fixed
56# sort_warnings:
57# emit_stats_by_project:
58# all_patterns,
59# findproject, classify_warning
60# dump_html
61#
62# New dynamic HTML page's static JavaScript data:
63# Some data are copied from Python to JavaScript, to generate HTML elements.
64# FlagURL args.url
65# FlagSeparator args.separator
Chih-Hung Hsieh949205a2020-01-10 10:33:40 -080066# SeverityColors: list of colors for all severity levels
67# SeverityHeaders: list of headers for all severity levels
68# SeverityColumnHeaders: list of column_headers for all severity levels
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -080069# ProjectNames: project_names, or project_list[*][0]
70# WarnPatternsSeverity: warn_patterns[*]['severity']
71# WarnPatternsDescription: warn_patterns[*]['description']
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -080072# WarningMessages: warning_messages
73# Warnings: warning_records
74# StatsHeader: warning count table header row
75# StatsRows: array of warning count table rows
76#
77# New dynamic HTML page's dynamic JavaScript data:
78#
79# New dynamic HTML related function to emit data:
80# escape_string, strip_escape_string, emit_warning_arrays
81# emit_js_data():
82
83from __future__ import print_function
84import argparse
85import cgi
86import csv
87import io
88import multiprocessing
89import os
90import re
91import signal
92import sys
93
94# pylint:disable=relative-beyond-top-level
95from . import cpp_warn_patterns
96from . import java_warn_patterns
97from . import make_warn_patterns
98from . import other_warn_patterns
99from . import tidy_warn_patterns
Chih-Hung Hsieh949205a2020-01-10 10:33:40 -0800100# pylint:disable=g-importing-member
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800101from .android_project_list import project_list
102from .severity import Severity
103
104parser = argparse.ArgumentParser(description='Convert a build log into HTML')
105parser.add_argument('--csvpath',
106 help='Save CSV warning file to the passed absolute path',
107 default=None)
108parser.add_argument('--gencsv',
109 help='Generate a CSV file with number of various warnings',
110 action='store_true',
111 default=False)
112parser.add_argument('--byproject',
113 help='Separate warnings in HTML output by project names',
114 action='store_true',
115 default=False)
116parser.add_argument('--url',
117 help='Root URL of an Android source code tree prefixed '
118 'before files in warnings')
119parser.add_argument('--separator',
120 help='Separator between the end of a URL and the line '
121 'number argument. e.g. #')
122parser.add_argument('--processes',
123 type=int,
124 default=multiprocessing.cpu_count(),
125 help='Number of parallel processes to process warnings')
126parser.add_argument(dest='buildlog', metavar='build.log',
127 help='Path to build.log file')
128args = parser.parse_args()
129
Chih-Hung Hsieh949205a2020-01-10 10:33:40 -0800130warn_patterns = make_warn_patterns.warn_patterns
131warn_patterns.extend(cpp_warn_patterns.warn_patterns)
132warn_patterns.extend(java_warn_patterns.warn_patterns)
133warn_patterns.extend(tidy_warn_patterns.warn_patterns)
134warn_patterns.extend(other_warn_patterns.warn_patterns)
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800135
136project_patterns = []
137project_names = []
138warning_messages = []
139warning_records = []
140
141
142def initialize_arrays():
143 """Complete global arrays before they are used."""
144 global project_names, project_patterns
145 project_names = [p[0] for p in project_list]
146 project_patterns = [re.compile(p[1]) for p in project_list]
147 for w in warn_patterns:
148 w['members'] = []
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800149 # Each warning pattern has a 'projects' dictionary, that
150 # maps a project name to number of warnings in that project.
151 w['projects'] = {}
152
153
154initialize_arrays()
155
156
157android_root = ''
158platform_version = 'unknown'
159target_product = 'unknown'
160target_variant = 'unknown'
161
162
163##### Data and functions to dump html file. ##################################
164
165html_head_scripts = """\
166 <script type="text/javascript">
167 function expand(id) {
168 var e = document.getElementById(id);
169 var f = document.getElementById(id + "_mark");
170 if (e.style.display == 'block') {
171 e.style.display = 'none';
172 f.innerHTML = '&#x2295';
173 }
174 else {
175 e.style.display = 'block';
176 f.innerHTML = '&#x2296';
177 }
178 };
179 function expandCollapse(show) {
180 for (var id = 1; ; id++) {
181 var e = document.getElementById(id + "");
182 var f = document.getElementById(id + "_mark");
183 if (!e || !f) break;
184 e.style.display = (show ? 'block' : 'none');
185 f.innerHTML = (show ? '&#x2296' : '&#x2295');
186 }
187 };
188 </script>
189 <style type="text/css">
190 th,td{border-collapse:collapse; border:1px solid black;}
191 .button{color:blue;font-size:110%;font-weight:bolder;}
192 .bt{color:black;background-color:transparent;border:none;outline:none;
193 font-size:140%;font-weight:bolder;}
194 .c0{background-color:#e0e0e0;}
195 .c1{background-color:#d0d0d0;}
196 .t1{border-collapse:collapse; width:100%; border:1px solid black;}
197 </style>
198 <script src="https://www.gstatic.com/charts/loader.js"></script>
199"""
200
201
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800202def make_writer(output_stream):
203
204 def writer(text):
205 return output_stream.write(text + '\n')
206
207 return writer
208
209
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800210def html_big(param):
211 return '<font size="+2">' + param + '</font>'
212
213
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800214def dump_html_prologue(title, writer):
215 writer('<html>\n<head>')
216 writer('<title>' + title + '</title>')
217 writer(html_head_scripts)
218 emit_stats_by_project(writer)
219 writer('</head>\n<body>')
220 writer(html_big(title))
221 writer('<p>')
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800222
223
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800224def dump_html_epilogue(writer):
225 writer('</body>\n</head>\n</html>')
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800226
227
228def sort_warnings():
229 for i in warn_patterns:
230 i['members'] = sorted(set(i['members']))
231
232
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800233def emit_stats_by_project(writer):
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800234 """Dump a google chart table of warnings per project and severity."""
235 # warnings[p][s] is number of warnings in project p of severity s.
236 # pylint:disable=g-complex-comprehension
Chih-Hung Hsieh949205a2020-01-10 10:33:40 -0800237 warnings = {p: {s.value: 0 for s in Severity.levels} for p in project_names}
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800238 for i in warn_patterns:
Chih-Hung Hsieh949205a2020-01-10 10:33:40 -0800239 # pytype: disable=attribute-error
240 s = i['severity'].value
241 # pytype: enable=attribute-error
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800242 for p in i['projects']:
243 warnings[p][s] += i['projects'][p]
244
245 # total_by_project[p] is number of warnings in project p.
Chih-Hung Hsieh949205a2020-01-10 10:33:40 -0800246 total_by_project = {
247 p: sum(warnings[p][s.value] for s in Severity.levels)
248 for p in project_names
249 }
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800250
251 # total_by_severity[s] is number of warnings of severity s.
Chih-Hung Hsieh949205a2020-01-10 10:33:40 -0800252 total_by_severity = {
253 s.value: sum(warnings[p][s.value] for p in project_names)
254 for s in Severity.levels
255 }
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800256
257 # emit table header
258 stats_header = ['Project']
Chih-Hung Hsieh949205a2020-01-10 10:33:40 -0800259 for s in Severity.levels:
260 if total_by_severity[s.value]:
261 stats_header.append(
262 '<span style=\'background-color:{}\'>{}</span>'.format(
263 s.color, s.column_header))
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800264 stats_header.append('TOTAL')
265
266 # emit a row of warning counts per project, skip no-warning projects
267 total_all_projects = 0
268 stats_rows = []
269 for p in project_names:
270 if total_by_project[p]:
271 one_row = [p]
Chih-Hung Hsieh949205a2020-01-10 10:33:40 -0800272 for s in Severity.levels:
273 if total_by_severity[s.value]:
274 one_row.append(warnings[p][s.value])
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800275 one_row.append(total_by_project[p])
276 stats_rows.append(one_row)
277 total_all_projects += total_by_project[p]
278
279 # emit a row of warning counts per severity
280 total_all_severities = 0
281 one_row = ['<b>TOTAL</b>']
Chih-Hung Hsieh949205a2020-01-10 10:33:40 -0800282 for s in Severity.levels:
283 if total_by_severity[s.value]:
284 one_row.append(total_by_severity[s.value])
285 total_all_severities += total_by_severity[s.value]
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800286 one_row.append(total_all_projects)
287 stats_rows.append(one_row)
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800288 writer('<script>')
289 emit_const_string_array('StatsHeader', stats_header, writer)
290 emit_const_object_array('StatsRows', stats_rows, writer)
291 writer(draw_table_javascript)
292 writer('</script>')
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800293
294
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800295def dump_stats(writer):
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800296 """Dump some stats about total number of warnings and such."""
297 known = 0
298 skipped = 0
299 unknown = 0
300 sort_warnings()
301 for i in warn_patterns:
Chih-Hung Hsieh949205a2020-01-10 10:33:40 -0800302 if i['severity'] == Severity.UNMATCHED:
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800303 unknown += len(i['members'])
304 elif i['severity'] == Severity.SKIP:
305 skipped += len(i['members'])
306 else:
307 known += len(i['members'])
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800308 writer('Number of classified warnings: <b>' + str(known) + '</b><br>')
309 writer('Number of skipped warnings: <b>' + str(skipped) + '</b><br>')
310 writer('Number of unclassified warnings: <b>' + str(unknown) + '</b><br>')
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800311 total = unknown + known + skipped
312 extra_msg = ''
313 if total < 1000:
314 extra_msg = ' (low count may indicate incremental build)'
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800315 writer('Total number of warnings: <b>' + str(total) + '</b>' + extra_msg)
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800316
317
318# New base table of warnings, [severity, warn_id, project, warning_message]
319# Need buttons to show warnings in different grouping options.
320# (1) Current, group by severity, id for each warning pattern
321# sort by severity, warn_id, warning_message
322# (2) Current --byproject, group by severity,
323# id for each warning pattern + project name
324# sort by severity, warn_id, project, warning_message
325# (3) New, group by project + severity,
326# id for each warning pattern
327# sort by project, severity, warn_id, warning_message
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800328def emit_buttons(writer):
329 writer('<button class="button" onclick="expandCollapse(1);">'
330 'Expand all warnings</button>\n'
331 '<button class="button" onclick="expandCollapse(0);">'
332 'Collapse all warnings</button>\n'
333 '<button class="button" onclick="groupBySeverity();">'
334 'Group warnings by severity</button>\n'
335 '<button class="button" onclick="groupByProject();">'
336 'Group warnings by project</button><br>')
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800337
338
339def all_patterns(category):
340 patterns = ''
341 for i in category['patterns']:
342 patterns += i
343 patterns += ' / '
344 return patterns
345
346
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800347def dump_fixed(writer):
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800348 """Show which warnings no longer occur."""
349 anchor = 'fixed_warnings'
350 mark = anchor + '_mark'
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800351 writer('\n<br><p style="background-color:lightblue"><b>'
352 '<button id="' + mark + '" '
353 'class="bt" onclick="expand(\'' + anchor + '\');">'
354 '&#x2295</button> Fixed warnings. '
355 'No more occurrences. Please consider turning these into '
356 'errors if possible, before they are reintroduced in to the build'
357 ':</b></p>')
358 writer('<blockquote>')
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800359 fixed_patterns = []
360 for i in warn_patterns:
361 if not i['members']:
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800362 fixed_patterns.append(i['description'] + ' (' + all_patterns(i) + ')')
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800363 fixed_patterns = sorted(fixed_patterns)
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800364 writer('<div id="' + anchor + '" style="display:none;"><table>')
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800365 cur_row_class = 0
366 for text in fixed_patterns:
367 cur_row_class = 1 - cur_row_class
368 # remove last '\n'
369 t = text[:-1] if text[-1] == '\n' else text
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800370 writer('<tr><td class="c' + str(cur_row_class) + '">' + t + '</td></tr>')
371 writer('</table></div>')
372 writer('</blockquote>')
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800373
374
375def find_project_index(line):
376 for p in range(len(project_patterns)):
377 if project_patterns[p].match(line):
378 return p
379 return -1
380
381
382def classify_one_warning(line, results):
383 """Classify one warning line."""
384 for i in range(len(warn_patterns)):
385 w = warn_patterns[i]
386 for cpat in w['compiled_patterns']:
Chih-Hung Hsieh949205a2020-01-10 10:33:40 -0800387 # pytype: disable=attribute-error
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800388 if cpat.match(line):
389 p = find_project_index(line)
390 results.append([line, i, p])
391 return
392 else:
393 # If we end up here, there was a problem parsing the log
394 # probably caused by 'make -j' mixing the output from
395 # 2 or more concurrent compiles
396 pass
Chih-Hung Hsieh949205a2020-01-10 10:33:40 -0800397 # pytype: enable=attribute-error
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800398
399
400def classify_warnings(lines):
401 results = []
402 for line in lines:
403 classify_one_warning(line, results)
404 # After the main work, ignore all other signals to a child process,
405 # to avoid bad warning/error messages from the exit clean-up process.
406 if args.processes > 1:
407 signal.signal(signal.SIGTERM, lambda *args: sys.exit(-signal.SIGTERM))
408 return results
409
410
411def parallel_classify_warnings(warning_lines, parallel_process):
412 """Classify all warning lines with num_cpu parallel processes."""
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800413 num_cpu = args.processes
414 if num_cpu > 1:
415 groups = [[] for x in range(num_cpu)]
416 i = 0
417 for x in warning_lines:
418 groups[i].append(x)
419 i = (i + 1) % num_cpu
420 group_results = parallel_process(num_cpu, classify_warnings, groups)
421 else:
422 group_results = [classify_warnings(warning_lines)]
423
424 for result in group_results:
425 for line, pattern_idx, project_idx in result:
426 pattern = warn_patterns[pattern_idx]
427 pattern['members'].append(line)
428 message_idx = len(warning_messages)
429 warning_messages.append(line)
430 warning_records.append([pattern_idx, project_idx, message_idx])
431 pname = '???' if project_idx < 0 else project_names[project_idx]
432 # Count warnings by project.
433 if pname in pattern['projects']:
434 pattern['projects'][pname] += 1
435 else:
436 pattern['projects'][pname] = 1
437
438
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800439def find_warn_py_and_android_root(path):
440 """Set and return android_root path if it is found."""
441 global android_root
442 parts = path.split('/')
443 for idx in reversed(range(2, len(parts))):
444 root_path = '/'.join(parts[:idx])
445 # Android root directory should contain this script.
446 if os.path.exists(root_path + '/build/make/tools/warn.py'):
447 android_root = root_path
448 return True
449 return False
450
451
452def find_android_root():
453 """Guess android_root from common prefix of file paths."""
454 # Use the longest common prefix of the absolute file paths
455 # of the first 10000 warning messages as the android_root.
456 global android_root
457 warning_lines = set()
458 warning_pattern = re.compile('^/[^ ]*/[^ ]*: warning: .*')
459 count = 0
460 infile = io.open(args.buildlog, mode='r', encoding='utf-8')
461 for line in infile:
462 if warning_pattern.match(line):
463 warning_lines.add(line)
464 count += 1
465 if count > 9999:
466 break
467 # Try to find warn.py and use its location to find
468 # the source tree root.
469 if count < 100:
470 path = os.path.normpath(re.sub(':.*$', '', line))
471 if find_warn_py_and_android_root(path):
472 return
473 # Do not use common prefix of a small number of paths.
474 if count > 10:
Chih-Hung Hsieh949205a2020-01-10 10:33:40 -0800475 # pytype: disable=wrong-arg-types
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800476 root_path = os.path.commonprefix(warning_lines)
Chih-Hung Hsieh949205a2020-01-10 10:33:40 -0800477 # pytype: enable=wrong-arg-types
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800478 if len(root_path) > 2 and root_path[len(root_path) - 1] == '/':
479 android_root = root_path[:-1]
480
481
482def remove_android_root_prefix(path):
483 """Remove android_root prefix from path if it is found."""
484 if path.startswith(android_root):
485 return path[1 + len(android_root):]
486 else:
487 return path
488
489
490def normalize_path(path):
491 """Normalize file path relative to android_root."""
492 # If path is not an absolute path, just normalize it.
493 path = os.path.normpath(path)
494 # Remove known prefix of root path and normalize the suffix.
495 if path[0] == '/' and android_root:
496 return remove_android_root_prefix(path)
497 return path
498
499
500def normalize_warning_line(line):
501 """Normalize file path relative to android_root in a warning line."""
502 # replace fancy quotes with plain ol' quotes
503 line = re.sub(u'[\u2018\u2019]', '\'', line)
504 # replace non-ASCII chars to spaces
505 line = re.sub(u'[^\x00-\x7f]', ' ', line)
506 line = line.strip()
507 first_column = line.find(':')
508 if first_column > 0:
509 return normalize_path(line[:first_column]) + line[first_column:]
510 else:
511 return line
512
513
514def parse_input_file(infile):
515 """Parse input file, collect parameters and warning lines."""
516 global android_root
517 global platform_version
518 global target_product
519 global target_variant
520 line_counter = 0
521
522 # rustc warning messages have two lines that should be combined:
523 # warning: description
524 # --> file_path:line_number:column_number
525 # Some warning messages have no file name:
526 # warning: macro replacement list ... [bugprone-macro-parentheses]
527 # Some makefile warning messages have no line number:
528 # some/path/file.mk: warning: description
529 # C/C++ compiler warning messages have line and column numbers:
530 # some/path/file.c:line_number:column_number: warning: description
531 warning_pattern = re.compile('(^[^ ]*/[^ ]*: warning: .*)|(^warning: .*)')
532 warning_without_file = re.compile('^warning: .*')
533 rustc_file_position = re.compile('^[ ]+--> [^ ]*/[^ ]*:[0-9]+:[0-9]+')
534
535 # Collect all warnings into the warning_lines set.
536 warning_lines = set()
537 prev_warning = ''
538 for line in infile:
539 if prev_warning:
540 if rustc_file_position.match(line):
541 # must be a rustc warning, combine 2 lines into one warning
542 line = line.strip().replace('--> ', '') + ': ' + prev_warning
543 warning_lines.add(normalize_warning_line(line))
544 prev_warning = ''
545 continue
546 # add prev_warning, and then process the current line
547 prev_warning = 'unknown_source_file: ' + prev_warning
548 warning_lines.add(normalize_warning_line(prev_warning))
549 prev_warning = ''
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800550
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800551 if warning_pattern.match(line):
552 if warning_without_file.match(line):
553 # save this line and combine it with the next line
554 prev_warning = line
555 else:
556 warning_lines.add(normalize_warning_line(line))
557 continue
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800558
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800559 if line_counter < 100:
560 # save a little bit of time by only doing this for the first few lines
561 line_counter += 1
562 m = re.search('(?<=^PLATFORM_VERSION=).*', line)
563 if m is not None:
564 platform_version = m.group(0)
565 m = re.search('(?<=^TARGET_PRODUCT=).*', line)
566 if m is not None:
567 target_product = m.group(0)
568 m = re.search('(?<=^TARGET_BUILD_VARIANT=).*', line)
569 if m is not None:
570 target_variant = m.group(0)
571 m = re.search('.* TOP=([^ ]*) .*', line)
572 if m is not None:
573 android_root = m.group(1)
574 return warning_lines
575
576
577# Return s with escaped backslash and quotation characters.
578def escape_string(s):
Chih-Hung Hsieh949205a2020-01-10 10:33:40 -0800579 # pytype: disable=attribute-error
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800580 return s.replace('\\', '\\\\').replace('"', '\\"')
Chih-Hung Hsieh949205a2020-01-10 10:33:40 -0800581 # pytype: enable=attribute-error
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800582
583
584# Return s without trailing '\n' and escape the quotation characters.
585def strip_escape_string(s):
586 if not s:
587 return s
588 s = s[:-1] if s[-1] == '\n' else s
589 return escape_string(s)
590
591
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800592def emit_warning_array(name, writer):
593 writer('var warning_{} = ['.format(name))
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800594 for i in range(len(warn_patterns)):
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800595 writer('{},'.format(warn_patterns[i][name]))
596 writer('];')
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800597
598
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800599def emit_warning_arrays(writer):
600 emit_warning_array('severity', writer)
601 writer('var warning_description = [')
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800602 for i in range(len(warn_patterns)):
603 if warn_patterns[i]['members']:
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800604 writer('"{}",'.format(escape_string(warn_patterns[i]['description'])))
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800605 else:
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800606 writer('"",') # no such warning
607 writer('];')
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800608
609
610scripts_for_warning_groups = """
611 function compareMessages(x1, x2) { // of the same warning type
612 return (WarningMessages[x1[2]] <= WarningMessages[x2[2]]) ? -1 : 1;
613 }
614 function byMessageCount(x1, x2) {
615 return x2[2] - x1[2]; // reversed order
616 }
617 function bySeverityMessageCount(x1, x2) {
618 // orer by severity first
619 if (x1[1] != x2[1])
620 return x1[1] - x2[1];
621 return byMessageCount(x1, x2);
622 }
623 const ParseLinePattern = /^([^ :]+):(\\d+):(.+)/;
624 function addURL(line) {
625 if (FlagURL == "") return line;
626 if (FlagSeparator == "") {
627 return line.replace(ParseLinePattern,
628 "<a target='_blank' href='" + FlagURL + "/$1'>$1</a>:$2:$3");
629 }
630 return line.replace(ParseLinePattern,
631 "<a target='_blank' href='" + FlagURL + "/$1" + FlagSeparator +
632 "$2'>$1:$2</a>:$3");
633 }
634 function createArrayOfDictionaries(n) {
635 var result = [];
636 for (var i=0; i<n; i++) result.push({});
637 return result;
638 }
639 function groupWarningsBySeverity() {
640 // groups is an array of dictionaries,
641 // each dictionary maps from warning type to array of warning messages.
642 var groups = createArrayOfDictionaries(SeverityColors.length);
643 for (var i=0; i<Warnings.length; i++) {
644 var w = Warnings[i][0];
645 var s = WarnPatternsSeverity[w];
646 var k = w.toString();
647 if (!(k in groups[s]))
648 groups[s][k] = [];
649 groups[s][k].push(Warnings[i]);
650 }
651 return groups;
652 }
653 function groupWarningsByProject() {
654 var groups = createArrayOfDictionaries(ProjectNames.length);
655 for (var i=0; i<Warnings.length; i++) {
656 var w = Warnings[i][0];
657 var p = Warnings[i][1];
658 var k = w.toString();
659 if (!(k in groups[p]))
660 groups[p][k] = [];
661 groups[p][k].push(Warnings[i]);
662 }
663 return groups;
664 }
665 var GlobalAnchor = 0;
666 function createWarningSection(header, color, group) {
667 var result = "";
668 var groupKeys = [];
669 var totalMessages = 0;
670 for (var k in group) {
671 totalMessages += group[k].length;
672 groupKeys.push([k, WarnPatternsSeverity[parseInt(k)], group[k].length]);
673 }
674 groupKeys.sort(bySeverityMessageCount);
675 for (var idx=0; idx<groupKeys.length; idx++) {
676 var k = groupKeys[idx][0];
677 var messages = group[k];
678 var w = parseInt(k);
679 var wcolor = SeverityColors[WarnPatternsSeverity[w]];
680 var description = WarnPatternsDescription[w];
681 if (description.length == 0)
682 description = "???";
683 GlobalAnchor += 1;
684 result += "<table class='t1'><tr bgcolor='" + wcolor + "'><td>" +
685 "<button class='bt' id='" + GlobalAnchor + "_mark" +
686 "' onclick='expand(\\"" + GlobalAnchor + "\\");'>" +
687 "&#x2295</button> " +
688 description + " (" + messages.length + ")</td></tr></table>";
689 result += "<div id='" + GlobalAnchor +
690 "' style='display:none;'><table class='t1'>";
691 var c = 0;
692 messages.sort(compareMessages);
693 for (var i=0; i<messages.length; i++) {
694 result += "<tr><td class='c" + c + "'>" +
695 addURL(WarningMessages[messages[i][2]]) + "</td></tr>";
696 c = 1 - c;
697 }
698 result += "</table></div>";
699 }
700 if (result.length > 0) {
701 return "<br><span style='background-color:" + color + "'><b>" +
702 header + ": " + totalMessages +
703 "</b></span><blockquote><table class='t1'>" +
704 result + "</table></blockquote>";
705
706 }
707 return ""; // empty section
708 }
709 function generateSectionsBySeverity() {
710 var result = "";
711 var groups = groupWarningsBySeverity();
712 for (s=0; s<SeverityColors.length; s++) {
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800713 result += createWarningSection(SeverityHeaders[s], SeverityColors[s],
714 groups[s]);
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800715 }
716 return result;
717 }
718 function generateSectionsByProject() {
719 var result = "";
720 var groups = groupWarningsByProject();
721 for (i=0; i<groups.length; i++) {
722 result += createWarningSection(ProjectNames[i], 'lightgrey', groups[i]);
723 }
724 return result;
725 }
726 function groupWarnings(generator) {
727 GlobalAnchor = 0;
728 var e = document.getElementById("warning_groups");
729 e.innerHTML = generator();
730 }
731 function groupBySeverity() {
732 groupWarnings(generateSectionsBySeverity);
733 }
734 function groupByProject() {
735 groupWarnings(generateSectionsByProject);
736 }
737"""
738
739
740# Emit a JavaScript const string
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800741def emit_const_string(name, value, writer):
742 writer('const ' + name + ' = "' + escape_string(value) + '";')
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800743
744
745# Emit a JavaScript const integer array.
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800746def emit_const_int_array(name, array, writer):
747 writer('const ' + name + ' = [')
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800748 for n in array:
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800749 writer(str(n) + ',')
750 writer('];')
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800751
752
753# Emit a JavaScript const string array.
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800754def emit_const_string_array(name, array, writer):
755 writer('const ' + name + ' = [')
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800756 for s in array:
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800757 writer('"' + strip_escape_string(s) + '",')
758 writer('];')
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800759
760
761# Emit a JavaScript const string array for HTML.
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800762def emit_const_html_string_array(name, array, writer):
763 writer('const ' + name + ' = [')
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800764 for s in array:
765 # Not using html.escape yet, to work for both python 2 and 3,
766 # until all users switch to python 3.
767 # pylint:disable=deprecated-method
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800768 writer('"' + cgi.escape(strip_escape_string(s)) + '",')
769 writer('];')
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800770
771
772# Emit a JavaScript const object array.
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800773def emit_const_object_array(name, array, writer):
774 writer('const ' + name + ' = [')
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800775 for x in array:
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800776 writer(str(x) + ',')
777 writer('];')
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800778
779
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800780def emit_js_data(writer):
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800781 """Dump dynamic HTML page's static JavaScript data."""
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800782 emit_const_string('FlagURL',
783 args.url if args.url else '', writer)
784 emit_const_string('FlagSeparator',
785 args.separator if args.separator else '', writer)
786 emit_const_string_array('SeverityColors',
787 [s.color for s in Severity.levels], writer)
Chih-Hung Hsieh949205a2020-01-10 10:33:40 -0800788 emit_const_string_array('SeverityHeaders',
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800789 [s.header for s in Severity.levels], writer)
Chih-Hung Hsieh949205a2020-01-10 10:33:40 -0800790 emit_const_string_array('SeverityColumnHeaders',
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800791 [s.column_header for s in Severity.levels], writer)
792 emit_const_string_array('ProjectNames', project_names, writer)
Chih-Hung Hsieh949205a2020-01-10 10:33:40 -0800793 # pytype: disable=attribute-error
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800794 emit_const_int_array('WarnPatternsSeverity',
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800795 [w['severity'].value for w in warn_patterns], writer)
Chih-Hung Hsieh949205a2020-01-10 10:33:40 -0800796 # pytype: enable=attribute-error
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800797 emit_const_html_string_array('WarnPatternsDescription',
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800798 [w['description'] for w in warn_patterns],
799 writer)
800 emit_const_html_string_array('WarningMessages', warning_messages, writer)
801 emit_const_object_array('Warnings', warning_records, writer)
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800802
803draw_table_javascript = """
804google.charts.load('current', {'packages':['table']});
805google.charts.setOnLoadCallback(drawTable);
806function drawTable() {
807 var data = new google.visualization.DataTable();
808 data.addColumn('string', StatsHeader[0]);
809 for (var i=1; i<StatsHeader.length; i++) {
810 data.addColumn('number', StatsHeader[i]);
811 }
812 data.addRows(StatsRows);
813 for (var i=0; i<StatsRows.length; i++) {
814 for (var j=0; j<StatsHeader.length; j++) {
815 data.setProperty(i, j, 'style', 'border:1px solid black;');
816 }
817 }
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800818 var table = new google.visualization.Table(
819 document.getElementById('stats_table'));
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800820 table.draw(data, {allowHtml: true, alternatingRowStyle: true});
821}
822"""
823
824
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800825def dump_html(output_stream):
826 """Dump the html output to output_stream."""
827 writer = make_writer(output_stream)
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800828 dump_html_prologue('Warnings for ' + platform_version + ' - ' +
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800829 target_product + ' - ' + target_variant, writer)
830 dump_stats(writer)
831 writer('<br><div id="stats_table"></div><br>')
832 writer('\n<script>')
833 emit_js_data(writer)
834 writer(scripts_for_warning_groups)
835 writer('</script>')
836 emit_buttons(writer)
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800837 # Warning messages are grouped by severities or project names.
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800838 writer('<br><div id="warning_groups"></div>')
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800839 if args.byproject:
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800840 writer('<script>groupByProject();</script>')
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800841 else:
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800842 writer('<script>groupBySeverity();</script>')
843 dump_fixed(writer)
844 dump_html_epilogue(writer)
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800845
846
847##### Functions to count warnings and dump csv file. #########################
848
849
850def description_for_csv(category):
851 if not category['description']:
852 return '?'
853 return category['description']
854
855
856def count_severity(writer, sev, kind):
857 """Count warnings of given severity."""
858 total = 0
859 for i in warn_patterns:
860 if i['severity'] == sev and i['members']:
861 n = len(i['members'])
862 total += n
863 warning = kind + ': ' + description_for_csv(i)
864 writer.writerow([n, '', warning])
865 # print number of warnings for each project, ordered by project name.
Chih-Hung Hsieh949205a2020-01-10 10:33:40 -0800866 # pytype: disable=attribute-error
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800867 projects = sorted(i['projects'].keys())
Chih-Hung Hsieh949205a2020-01-10 10:33:40 -0800868 # pytype: enable=attribute-error
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800869 for p in projects:
870 writer.writerow([i['projects'][p], p, warning])
871 writer.writerow([total, '', kind + ' warnings'])
872
873 return total
874
875
876# dump number of warnings in csv format to stdout
877def dump_csv(writer):
878 """Dump number of warnings in csv format to stdout."""
879 sort_warnings()
880 total = 0
Chih-Hung Hsieh949205a2020-01-10 10:33:40 -0800881 for s in Severity.levels:
882 if s != Severity.SEVERITY_UNKNOWN:
883 total += count_severity(writer, s, s.column_header)
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800884 writer.writerow([total, '', 'All warnings'])
885
886
887def common_main(parallel_process):
888 """Real main function to classify warnings and generate .html file."""
889 find_android_root()
890 # We must use 'utf-8' codec to parse some non-ASCII code in warnings.
891 warning_lines = parse_input_file(
892 io.open(args.buildlog, mode='r', encoding='utf-8'))
893 parallel_classify_warnings(warning_lines, parallel_process)
894 # If a user pases a csv path, save the fileoutput to the path
895 # If the user also passed gencsv write the output to stdout
896 # If the user did not pass gencsv flag dump the html report to stdout.
897 if args.csvpath:
898 with open(args.csvpath, 'w') as f:
899 dump_csv(csv.writer(f, lineterminator='\n'))
900 if args.gencsv:
901 dump_csv(csv.writer(sys.stdout, lineterminator='\n'))
902 else:
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800903 dump_html(sys.stdout)