blob: 46ba25318e51952f2151fcbf7d00f62b78126713 [file] [log] [blame]
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -08001# Lint as: python3
2# 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"""Emit warning messages to html or csv files."""
17
Chih-Hung Hsieh98b285d2021-04-28 14:49:32 -070018# Many functions in this module have too many arguments to be refactored.
19# pylint:disable=too-many-arguments,missing-function-docstring
20
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -080021# To emit html page of warning messages:
22# flags: --byproject, --url, --separator
23# Old stuff for static html components:
24# html_script_style: static html scripts and styles
25# htmlbig:
26# dump_stats, dump_html_prologue, dump_html_epilogue:
27# emit_buttons:
28# dump_fixed
29# sort_warnings:
30# emit_stats_by_project:
31# all_patterns,
32# findproject, classify_warning
33# dump_html
34#
35# New dynamic HTML page's static JavaScript data:
36# Some data are copied from Python to JavaScript, to generate HTML elements.
37# FlagPlatform flags.platform
38# FlagURL flags.url, used by 'android'
39# FlagSeparator flags.separator, used by 'android'
40# SeverityColors: list of colors for all severity levels
41# SeverityHeaders: list of headers for all severity levels
42# SeverityColumnHeaders: list of column_headers for all severity levels
43# ProjectNames: project_names, or project_list[*][0]
44# WarnPatternsSeverity: warn_patterns[*]['severity']
45# WarnPatternsDescription: warn_patterns[*]['description']
46# WarningMessages: warning_messages
47# Warnings: warning_records
48# StatsHeader: warning count table header row
49# StatsRows: array of warning count table rows
50#
51# New dynamic HTML page's dynamic JavaScript data:
52#
53# New dynamic HTML related function to emit data:
54# escape_string, strip_escape_string, emit_warning_arrays
55# emit_js_data():
56
57from __future__ import print_function
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -080058import csv
Chih-Hung Hsiehdd58ab02022-05-18 23:16:37 -070059import datetime
Chih-Hung Hsieh7cc0e152021-04-26 17:09:36 -070060import html
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -080061import sys
62
63# pylint:disable=relative-beyond-top-level
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -080064from .severity import Severity
65
66
Chih-Hung Hsiehd4691ec2021-12-27 17:33:36 -080067# Report files with this number of warnings or more.
68LIMIT_WARNINGS_PER_FILE = 100
69# Report files/directories with this percentage of total warnings or more.
70LIMIT_PERCENT_WARNINGS = 1
71
Chih-Hung Hsieh98b285d2021-04-28 14:49:32 -070072HTML_HEAD_SCRIPTS = """\
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -080073 <script type="text/javascript">
74 function expand(id) {
75 var e = document.getElementById(id);
76 var f = document.getElementById(id + "_mark");
77 if (e.style.display == 'block') {
78 e.style.display = 'none';
79 f.innerHTML = '&#x2295';
80 }
81 else {
82 e.style.display = 'block';
83 f.innerHTML = '&#x2296';
84 }
85 };
86 function expandCollapse(show) {
87 for (var id = 1; ; id++) {
88 var e = document.getElementById(id + "");
89 var f = document.getElementById(id + "_mark");
90 if (!e || !f) break;
91 e.style.display = (show ? 'block' : 'none');
92 f.innerHTML = (show ? '&#x2296' : '&#x2295');
93 }
94 };
95 </script>
96 <style type="text/css">
97 th,td{border-collapse:collapse; border:1px solid black;}
Chih-Hung Hsiehd4691ec2021-12-27 17:33:36 -080098 .button{color:blue;font-size:100%;font-weight:bolder;}
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -080099 .bt{color:black;background-color:transparent;border:none;outline:none;
100 font-size:140%;font-weight:bolder;}
101 .c0{background-color:#e0e0e0;}
102 .c1{background-color:#d0d0d0;}
103 .t1{border-collapse:collapse; width:100%; border:1px solid black;}
Chih-Hung Hsiehd4691ec2021-12-27 17:33:36 -0800104 .box{margin:5pt; padding:5pt; border:1px solid;}
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800105 </style>
106 <script src="https://www.gstatic.com/charts/loader.js"></script>
107"""
108
109
110def make_writer(output_stream):
111
112 def writer(text):
113 return output_stream.write(text + '\n')
114
115 return writer
116
117
118def html_big(param):
119 return '<font size="+2">' + param + '</font>'
120
121
122def dump_html_prologue(title, writer, warn_patterns, project_names):
123 writer('<html>\n<head>')
124 writer('<title>' + title + '</title>')
Chih-Hung Hsieh98b285d2021-04-28 14:49:32 -0700125 writer(HTML_HEAD_SCRIPTS)
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800126 emit_stats_by_project(writer, warn_patterns, project_names)
127 writer('</head>\n<body>')
128 writer(html_big(title))
129 writer('<p>')
130
131
132def dump_html_epilogue(writer):
133 writer('</body>\n</head>\n</html>')
134
135
136def sort_warnings(warn_patterns):
137 for i in warn_patterns:
138 i['members'] = sorted(set(i['members']))
139
140
141def create_warnings(warn_patterns, project_names):
142 """Creates warnings s.t.
143
144 warnings[p][s] is as specified in above docs.
145
146 Args:
147 warn_patterns: list of warning patterns for specified platform
148 project_names: list of project names
149
150 Returns:
151 2D warnings array where warnings[p][s] is # of warnings in project name p of
152 severity level s
153 """
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800154 warnings = {p: {s.value: 0 for s in Severity.levels} for p in project_names}
Chih-Hung Hsieha6068222021-04-30 14:30:58 -0700155 for pattern in warn_patterns:
156 value = pattern['severity'].value
157 for project in pattern['projects']:
158 warnings[project][value] += pattern['projects'][project]
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800159 return warnings
160
161
162def get_total_by_project(warnings, project_names):
163 """Returns dict, project as key and # warnings for that project as value."""
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800164 return {
165 p: sum(warnings[p][s.value] for s in Severity.levels)
166 for p in project_names
167 }
168
169
170def get_total_by_severity(warnings, project_names):
171 """Returns dict, severity as key and # warnings of that severity as value."""
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800172 return {
173 s.value: sum(warnings[p][s.value] for p in project_names)
174 for s in Severity.levels
175 }
176
177
178def emit_table_header(total_by_severity):
179 """Returns list of HTML-formatted content for severity stats."""
180
181 stats_header = ['Project']
Chih-Hung Hsieha6068222021-04-30 14:30:58 -0700182 for severity in Severity.levels:
183 if total_by_severity[severity.value]:
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800184 stats_header.append(
185 '<span style=\'background-color:{}\'>{}</span>'.format(
Chih-Hung Hsieha6068222021-04-30 14:30:58 -0700186 severity.color, severity.column_header))
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800187 stats_header.append('TOTAL')
188 return stats_header
189
190
191def emit_row_counts_per_project(warnings, total_by_project, total_by_severity,
192 project_names):
193 """Returns total project warnings and row of stats for each project.
194
195 Args:
196 warnings: output of create_warnings(warn_patterns, project_names)
197 total_by_project: output of get_total_by_project(project_names)
198 total_by_severity: output of get_total_by_severity(project_names)
199 project_names: list of project names
200
201 Returns:
202 total_all_projects, the total number of warnings over all projects
203 stats_rows, a 2d list where each row is [Project Name, <severity counts>,
204 total # warnings for this project]
205 """
206
207 total_all_projects = 0
208 stats_rows = []
Chih-Hung Hsieha6068222021-04-30 14:30:58 -0700209 for p_name in project_names:
210 if total_by_project[p_name]:
211 one_row = [p_name]
212 for severity in Severity.levels:
213 if total_by_severity[severity.value]:
214 one_row.append(warnings[p_name][severity.value])
215 one_row.append(total_by_project[p_name])
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800216 stats_rows.append(one_row)
Chih-Hung Hsieha6068222021-04-30 14:30:58 -0700217 total_all_projects += total_by_project[p_name]
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800218 return total_all_projects, stats_rows
219
220
221def emit_row_counts_per_severity(total_by_severity, stats_header, stats_rows,
222 total_all_projects, writer):
223 """Emits stats_header and stats_rows as specified above.
224
225 Args:
226 total_by_severity: output of get_total_by_severity()
227 stats_header: output of emit_table_header()
228 stats_rows: output of emit_row_counts_per_project()
229 total_all_projects: output of emit_row_counts_per_project()
230 writer: writer returned by make_writer(output_stream)
231 """
232
233 total_all_severities = 0
234 one_row = ['<b>TOTAL</b>']
Chih-Hung Hsieha6068222021-04-30 14:30:58 -0700235 for severity in Severity.levels:
236 if total_by_severity[severity.value]:
237 one_row.append(total_by_severity[severity.value])
238 total_all_severities += total_by_severity[severity.value]
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800239 one_row.append(total_all_projects)
240 stats_rows.append(one_row)
241 writer('<script>')
242 emit_const_string_array('StatsHeader', stats_header, writer)
243 emit_const_object_array('StatsRows', stats_rows, writer)
Chih-Hung Hsieh98b285d2021-04-28 14:49:32 -0700244 writer(DRAW_TABLE_JAVASCRIPT)
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800245 writer('</script>')
246
247
248def emit_stats_by_project(writer, warn_patterns, project_names):
249 """Dump a google chart table of warnings per project and severity."""
250
251 warnings = create_warnings(warn_patterns, project_names)
252 total_by_project = get_total_by_project(warnings, project_names)
253 total_by_severity = get_total_by_severity(warnings, project_names)
254 stats_header = emit_table_header(total_by_severity)
Chih-Hung Hsieh98b285d2021-04-28 14:49:32 -0700255 total_all_projects, stats_rows = emit_row_counts_per_project(
256 warnings, total_by_project, total_by_severity, project_names)
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800257 emit_row_counts_per_severity(total_by_severity, stats_header, stats_rows,
258 total_all_projects, writer)
259
260
261def dump_stats(writer, warn_patterns):
Chih-Hung Hsiehdd58ab02022-05-18 23:16:37 -0700262 """Dump some stats about total number of warnings and date."""
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800263
264 known = 0
265 skipped = 0
266 unknown = 0
267 sort_warnings(warn_patterns)
268 for i in warn_patterns:
269 if i['severity'] == Severity.UNMATCHED:
270 unknown += len(i['members'])
271 elif i['severity'] == Severity.SKIP:
272 skipped += len(i['members'])
273 else:
274 known += len(i['members'])
275 writer('Number of classified warnings: <b>' + str(known) + '</b><br>')
276 writer('Number of skipped warnings: <b>' + str(skipped) + '</b><br>')
277 writer('Number of unclassified warnings: <b>' + str(unknown) + '</b><br>')
278 total = unknown + known + skipped
279 extra_msg = ''
280 if total < 1000:
281 extra_msg = ' (low count may indicate incremental build)'
282 writer('Total number of warnings: <b>' + str(total) + '</b>' + extra_msg)
Chih-Hung Hsiehdd58ab02022-05-18 23:16:37 -0700283 date_time_str = datetime.datetime.now().strftime('%Y/%m/%d %H:%M:%S')
284 writer('<p>(generated on ' + date_time_str + ')')
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800285
286
287# New base table of warnings, [severity, warn_id, project, warning_message]
288# Need buttons to show warnings in different grouping options.
289# (1) Current, group by severity, id for each warning pattern
290# sort by severity, warn_id, warning_message
291# (2) Current --byproject, group by severity,
292# id for each warning pattern + project name
293# sort by severity, warn_id, project, warning_message
294# (3) New, group by project + severity,
295# id for each warning pattern
296# sort by project, severity, warn_id, warning_message
297def emit_buttons(writer):
Chih-Hung Hsieh98b285d2021-04-28 14:49:32 -0700298 """Write the button elements in HTML."""
Chih-Hung Hsiehd4691ec2021-12-27 17:33:36 -0800299 writer('<p><button class="button" onclick="expandCollapse(1);">'
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800300 'Expand all warnings</button>\n'
301 '<button class="button" onclick="expandCollapse(0);">'
302 'Collapse all warnings</button>\n'
Chih-Hung Hsiehd4691ec2021-12-27 17:33:36 -0800303 '<p><button class="button" onclick="groupBySeverity();">'
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800304 'Group warnings by severity</button>\n'
305 '<button class="button" onclick="groupByProject();">'
Chih-Hung Hsiehd4691ec2021-12-27 17:33:36 -0800306 'Group warnings by project</button>')
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800307
308
309def all_patterns(category):
310 patterns = ''
311 for i in category['patterns']:
312 patterns += i
313 patterns += ' / '
314 return patterns
315
316
317def dump_fixed(writer, warn_patterns):
318 """Show which warnings no longer occur."""
319 anchor = 'fixed_warnings'
320 mark = anchor + '_mark'
321 writer('\n<br><p style="background-color:lightblue"><b>'
322 '<button id="' + mark + '" '
323 'class="bt" onclick="expand(\'' + anchor + '\');">'
324 '&#x2295</button> Fixed warnings. '
325 'No more occurrences. Please consider turning these into '
326 'errors if possible, before they are reintroduced in to the build'
327 ':</b></p>')
328 writer('<blockquote>')
329 fixed_patterns = []
330 for i in warn_patterns:
331 if not i['members']:
332 fixed_patterns.append(i['description'] + ' (' + all_patterns(i) + ')')
333 fixed_patterns = sorted(fixed_patterns)
334 writer('<div id="' + anchor + '" style="display:none;"><table>')
335 cur_row_class = 0
336 for text in fixed_patterns:
337 cur_row_class = 1 - cur_row_class
338 # remove last '\n'
Chih-Hung Hsieha6068222021-04-30 14:30:58 -0700339 out_text = text[:-1] if text[-1] == '\n' else text
Chih-Hung Hsieh5d9ee042021-06-01 16:03:22 -0700340 writer('<tr><td class="c' + str(cur_row_class) + '">'
341 + out_text + '</td></tr>')
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800342 writer('</table></div>')
343 writer('</blockquote>')
344
345
346def write_severity(csvwriter, sev, kind, warn_patterns):
347 """Count warnings of given severity and write CSV entries to writer."""
348 total = 0
349 for pattern in warn_patterns:
350 if pattern['severity'] == sev and pattern['members']:
Chih-Hung Hsieha6068222021-04-30 14:30:58 -0700351 num_members = len(pattern['members'])
352 total += num_members
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800353 warning = kind + ': ' + (pattern['description'] or '?')
Chih-Hung Hsieha6068222021-04-30 14:30:58 -0700354 csvwriter.writerow([num_members, '', warning])
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800355 # print number of warnings for each project, ordered by project name
356 projects = sorted(pattern['projects'].keys())
357 for project in projects:
358 csvwriter.writerow([pattern['projects'][project], project, warning])
359 csvwriter.writerow([total, '', kind + ' warnings'])
360 return total
361
362
363def dump_csv(csvwriter, warn_patterns):
364 """Dump number of warnings in CSV format to writer."""
365 sort_warnings(warn_patterns)
366 total = 0
Chih-Hung Hsieha6068222021-04-30 14:30:58 -0700367 for severity in Severity.levels:
Chih-Hung Hsieh5d9ee042021-06-01 16:03:22 -0700368 total += write_severity(
369 csvwriter, severity, severity.column_header, warn_patterns)
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800370 csvwriter.writerow([total, '', 'All warnings'])
371
372
Saeid Farivar Asanjan75dc8d22020-11-18 00:29:43 +0000373def dump_csv_with_description(csvwriter, warning_records, warning_messages,
374 warn_patterns, project_names):
375 """Outputs all the warning messages by project."""
376 csv_output = []
377 for record in warning_records:
378 project_name = project_names[record[1]]
379 pattern = warn_patterns[record[0]]
380 severity = pattern['severity'].header
381 category = pattern['category']
382 description = pattern['description']
383 warning = warning_messages[record[2]]
384 csv_output.append([project_name, severity,
385 category, description,
386 warning])
387 csv_output = sorted(csv_output)
388 for output in csv_output:
389 csvwriter.writerow(output)
390
391
Chih-Hung Hsieha6068222021-04-30 14:30:58 -0700392# Return line with escaped backslash and quotation characters.
393def escape_string(line):
394 return line.replace('\\', '\\\\').replace('"', '\\"')
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800395
396
Chih-Hung Hsieha6068222021-04-30 14:30:58 -0700397# Return line without trailing '\n' and escape the quotation characters.
398def strip_escape_string(line):
399 if not line:
400 return line
401 line = line[:-1] if line[-1] == '\n' else line
402 return escape_string(line)
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800403
404
405def emit_warning_array(name, writer, warn_patterns):
406 writer('var warning_{} = ['.format(name))
Chih-Hung Hsieha6068222021-04-30 14:30:58 -0700407 for pattern in warn_patterns:
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800408 if name == 'severity':
Chih-Hung Hsieha6068222021-04-30 14:30:58 -0700409 writer('{},'.format(pattern[name].value))
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800410 else:
Chih-Hung Hsieha6068222021-04-30 14:30:58 -0700411 writer('{},'.format(pattern[name]))
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800412 writer('];')
413
414
415def emit_warning_arrays(writer, warn_patterns):
416 emit_warning_array('severity', writer, warn_patterns)
417 writer('var warning_description = [')
Chih-Hung Hsieha6068222021-04-30 14:30:58 -0700418 for pattern in warn_patterns:
419 if pattern['members']:
420 writer('"{}",'.format(escape_string(pattern['description'])))
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800421 else:
422 writer('"",') # no such warning
423 writer('];')
424
425
Chih-Hung Hsieh98b285d2021-04-28 14:49:32 -0700426SCRIPTS_FOR_WARNING_GROUPS = """
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800427 function compareMessages(x1, x2) { // of the same warning type
428 return (WarningMessages[x1[2]] <= WarningMessages[x2[2]]) ? -1 : 1;
429 }
430 function byMessageCount(x1, x2) {
431 return x2[2] - x1[2]; // reversed order
432 }
433 function bySeverityMessageCount(x1, x2) {
434 // orer by severity first
435 if (x1[1] != x2[1])
436 return x1[1] - x2[1];
437 return byMessageCount(x1, x2);
438 }
439 const ParseLinePattern = /^([^ :]+):(\\d+):(.+)/;
440 function addURL(line) { // used by Android
441 if (FlagURL == "") return line;
442 if (FlagSeparator == "") {
443 return line.replace(ParseLinePattern,
444 "<a target='_blank' href='" + FlagURL + "/$1'>$1</a>:$2:$3");
445 }
446 return line.replace(ParseLinePattern,
447 "<a target='_blank' href='" + FlagURL + "/$1" + FlagSeparator +
448 "$2'>$1:$2</a>:$3");
449 }
450 function addURLToLine(line, link) { // used by Chrome
451 let line_split = line.split(":");
452 let path = line_split.slice(0,3).join(":");
453 let msg = line_split.slice(3).join(":");
454 let html_link = `<a target="_blank" href="${link}">${path}</a>${msg}`;
455 return html_link;
456 }
457 function createArrayOfDictionaries(n) {
458 var result = [];
459 for (var i=0; i<n; i++) result.push({});
460 return result;
461 }
462 function groupWarningsBySeverity() {
463 // groups is an array of dictionaries,
464 // each dictionary maps from warning type to array of warning messages.
465 var groups = createArrayOfDictionaries(SeverityColors.length);
466 for (var i=0; i<Warnings.length; i++) {
467 var w = Warnings[i][0];
468 var s = WarnPatternsSeverity[w];
469 var k = w.toString();
470 if (!(k in groups[s]))
471 groups[s][k] = [];
472 groups[s][k].push(Warnings[i]);
473 }
474 return groups;
475 }
476 function groupWarningsByProject() {
477 var groups = createArrayOfDictionaries(ProjectNames.length);
478 for (var i=0; i<Warnings.length; i++) {
479 var w = Warnings[i][0];
480 var p = Warnings[i][1];
481 var k = w.toString();
482 if (!(k in groups[p]))
483 groups[p][k] = [];
484 groups[p][k].push(Warnings[i]);
485 }
486 return groups;
487 }
488 var GlobalAnchor = 0;
489 function createWarningSection(header, color, group) {
490 var result = "";
491 var groupKeys = [];
492 var totalMessages = 0;
493 for (var k in group) {
494 totalMessages += group[k].length;
495 groupKeys.push([k, WarnPatternsSeverity[parseInt(k)], group[k].length]);
496 }
497 groupKeys.sort(bySeverityMessageCount);
498 for (var idx=0; idx<groupKeys.length; idx++) {
499 var k = groupKeys[idx][0];
500 var messages = group[k];
501 var w = parseInt(k);
502 var wcolor = SeverityColors[WarnPatternsSeverity[w]];
503 var description = WarnPatternsDescription[w];
504 if (description.length == 0)
505 description = "???";
506 GlobalAnchor += 1;
507 result += "<table class='t1'><tr bgcolor='" + wcolor + "'><td>" +
508 "<button class='bt' id='" + GlobalAnchor + "_mark" +
509 "' onclick='expand(\\"" + GlobalAnchor + "\\");'>" +
510 "&#x2295</button> " +
511 description + " (" + messages.length + ")</td></tr></table>";
512 result += "<div id='" + GlobalAnchor +
513 "' style='display:none;'><table class='t1'>";
514 var c = 0;
515 messages.sort(compareMessages);
516 if (FlagPlatform == "chrome") {
517 for (var i=0; i<messages.length; i++) {
518 result += "<tr><td class='c" + c + "'>" +
519 addURLToLine(WarningMessages[messages[i][2]], WarningLinks[messages[i][3]]) + "</td></tr>";
520 c = 1 - c;
521 }
522 } else {
523 for (var i=0; i<messages.length; i++) {
524 result += "<tr><td class='c" + c + "'>" +
525 addURL(WarningMessages[messages[i][2]]) + "</td></tr>";
526 c = 1 - c;
527 }
528 }
529 result += "</table></div>";
530 }
531 if (result.length > 0) {
532 return "<br><span style='background-color:" + color + "'><b>" +
533 header + ": " + totalMessages +
534 "</b></span><blockquote><table class='t1'>" +
535 result + "</table></blockquote>";
536
537 }
538 return ""; // empty section
539 }
540 function generateSectionsBySeverity() {
541 var result = "";
542 var groups = groupWarningsBySeverity();
543 for (s=0; s<SeverityColors.length; s++) {
544 result += createWarningSection(SeverityHeaders[s], SeverityColors[s],
545 groups[s]);
546 }
547 return result;
548 }
549 function generateSectionsByProject() {
550 var result = "";
551 var groups = groupWarningsByProject();
552 for (i=0; i<groups.length; i++) {
553 result += createWarningSection(ProjectNames[i], 'lightgrey', groups[i]);
554 }
555 return result;
556 }
557 function groupWarnings(generator) {
558 GlobalAnchor = 0;
559 var e = document.getElementById("warning_groups");
560 e.innerHTML = generator();
561 }
562 function groupBySeverity() {
563 groupWarnings(generateSectionsBySeverity);
564 }
565 function groupByProject() {
566 groupWarnings(generateSectionsByProject);
567 }
568"""
569
570
Chih-Hung Hsiehd4691ec2021-12-27 17:33:36 -0800571# Emit a JavaScript const number
572def emit_const_number(name, value, writer):
573 writer('const ' + name + ' = ' + str(value) + ';')
574
575
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800576# Emit a JavaScript const string
577def emit_const_string(name, value, writer):
578 writer('const ' + name + ' = "' + escape_string(value) + '";')
579
580
581# Emit a JavaScript const integer array.
582def emit_const_int_array(name, array, writer):
583 writer('const ' + name + ' = [')
Chih-Hung Hsieha6068222021-04-30 14:30:58 -0700584 for item in array:
585 writer(str(item) + ',')
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800586 writer('];')
587
588
589# Emit a JavaScript const string array.
590def emit_const_string_array(name, array, writer):
591 writer('const ' + name + ' = [')
Chih-Hung Hsieha6068222021-04-30 14:30:58 -0700592 for item in array:
593 writer('"' + strip_escape_string(item) + '",')
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800594 writer('];')
595
596
597# Emit a JavaScript const string array for HTML.
598def emit_const_html_string_array(name, array, writer):
599 writer('const ' + name + ' = [')
Chih-Hung Hsieha6068222021-04-30 14:30:58 -0700600 for item in array:
601 writer('"' + html.escape(strip_escape_string(item)) + '",')
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800602 writer('];')
603
604
605# Emit a JavaScript const object array.
606def emit_const_object_array(name, array, writer):
607 writer('const ' + name + ' = [')
Chih-Hung Hsieha6068222021-04-30 14:30:58 -0700608 for item in array:
609 writer(str(item) + ',')
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800610 writer('];')
611
612
613def emit_js_data(writer, flags, warning_messages, warning_links,
614 warning_records, warn_patterns, project_names):
615 """Dump dynamic HTML page's static JavaScript data."""
616 emit_const_string('FlagPlatform', flags.platform, writer)
617 emit_const_string('FlagURL', flags.url, writer)
618 emit_const_string('FlagSeparator', flags.separator, writer)
Chih-Hung Hsiehd4691ec2021-12-27 17:33:36 -0800619 emit_const_number('LimitWarningsPerFile', LIMIT_WARNINGS_PER_FILE, writer)
620 emit_const_number('LimitPercentWarnings', LIMIT_PERCENT_WARNINGS, writer)
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800621 emit_const_string_array('SeverityColors', [s.color for s in Severity.levels],
622 writer)
623 emit_const_string_array('SeverityHeaders',
624 [s.header for s in Severity.levels], writer)
625 emit_const_string_array('SeverityColumnHeaders',
626 [s.column_header for s in Severity.levels], writer)
627 emit_const_string_array('ProjectNames', project_names, writer)
628 # pytype: disable=attribute-error
629 emit_const_int_array('WarnPatternsSeverity',
630 [w['severity'].value for w in warn_patterns], writer)
631 # pytype: enable=attribute-error
632 emit_const_html_string_array('WarnPatternsDescription',
633 [w['description'] for w in warn_patterns],
634 writer)
635 emit_const_html_string_array('WarningMessages', warning_messages, writer)
636 emit_const_object_array('Warnings', warning_records, writer)
637 if flags.platform == 'chrome':
638 emit_const_html_string_array('WarningLinks', warning_links, writer)
639
640
Chih-Hung Hsieh98b285d2021-04-28 14:49:32 -0700641DRAW_TABLE_JAVASCRIPT = """
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800642google.charts.load('current', {'packages':['table']});
Chih-Hung Hsiehd4691ec2021-12-27 17:33:36 -0800643google.charts.setOnLoadCallback(genTables);
644function genSelectedProjectsTable() {
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800645 var data = new google.visualization.DataTable();
646 data.addColumn('string', StatsHeader[0]);
647 for (var i=1; i<StatsHeader.length; i++) {
648 data.addColumn('number', StatsHeader[i]);
649 }
650 data.addRows(StatsRows);
651 for (var i=0; i<StatsRows.length; i++) {
652 for (var j=0; j<StatsHeader.length; j++) {
653 data.setProperty(i, j, 'style', 'border:1px solid black;');
654 }
655 }
656 var table = new google.visualization.Table(
Chih-Hung Hsiehd4691ec2021-12-27 17:33:36 -0800657 document.getElementById('selected_projects_section'));
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800658 table.draw(data, {allowHtml: true, alternatingRowStyle: true});
659}
Chih-Hung Hsiehd4691ec2021-12-27 17:33:36 -0800660// Global TopDirs and TopFiles are computed later by genTables.
661window.TopDirs = [];
662window.TopFiles = [];
663function computeTopDirsFiles() {
664 var numWarnings = WarningMessages.length;
665 var warningsOfFiles = {};
666 var warningsOfDirs = {};
667 var subDirs = {};
Chih-Hung Hsieh4b020cd2022-05-17 11:42:25 -0700668 function addOneWarning(map, key, type, unique) {
669 function increaseCounter(idx) {
670 map[idx] = 1 + ((idx in map) ? map[idx] : 0);
671 }
672 increaseCounter(key)
673 if (type != "") {
674 increaseCounter(type + " " + key)
675 if (unique) {
676 increaseCounter(type + " *")
677 }
678 }
Chih-Hung Hsiehd4691ec2021-12-27 17:33:36 -0800679 }
680 for (var i = 0; i < numWarnings; i++) {
Chih-Hung Hsieh4b020cd2022-05-17 11:42:25 -0700681 var message = WarningMessages[i]
682 var file = message.replace(/:.*/, "");
683 var warningType = message.endsWith("]") ? message.replace(/.*\[/, "[") : "";
684 addOneWarning(warningsOfFiles, file, warningType, true);
Chih-Hung Hsiehd4691ec2021-12-27 17:33:36 -0800685 var dirs = file.split("/");
686 var dir = dirs[0];
Chih-Hung Hsieh4b020cd2022-05-17 11:42:25 -0700687 addOneWarning(warningsOfDirs, dir, warningType, true);
Chih-Hung Hsiehd4691ec2021-12-27 17:33:36 -0800688 for (var d = 1; d < dirs.length - 1; d++) {
689 var subDir = dir + "/" + dirs[d];
690 if (!(dir in subDirs)) {
691 subDirs[dir] = {};
692 }
693 subDirs[dir][subDir] = 1;
694 dir = subDir;
Chih-Hung Hsieh4b020cd2022-05-17 11:42:25 -0700695 addOneWarning(warningsOfDirs, dir, warningType, false);
Chih-Hung Hsiehd4691ec2021-12-27 17:33:36 -0800696 }
697 }
698 var minDirWarnings = numWarnings*(LimitPercentWarnings/100);
699 var minFileWarnings = Math.min(LimitWarningsPerFile, minDirWarnings);
700 // Each row in TopDirs and TopFiles has
701 // [index, {v:<num_of_warnings>, f:<percent>}, file_or_dir_name]
702 function countWarnings(minWarnings, warningsOf, isDir) {
703 var rows = [];
704 for (var name in warningsOf) {
705 if (isDir && name in subDirs && Object.keys(subDirs[name]).length < 2) {
706 continue; // skip a directory if it has only one subdir
707 }
708 var count = warningsOf[name];
709 if (count >= minWarnings) {
710 name = isDir ? (name + "/...") : name;
711 var percent = (100*count/numWarnings).toFixed(1);
712 var countFormat = count + ' (' + percent + '%)';
713 rows.push([0, {v:count, f:countFormat}, name]);
714 }
715 }
716 rows.sort((a,b) => b[1].v - a[1].v);
717 for (var i=0; i<rows.length; i++) {
718 rows[i][0] = i;
719 }
720 return rows;
721 }
722 TopDirs = countWarnings(minDirWarnings, warningsOfDirs, true);
723 TopFiles = countWarnings(minFileWarnings, warningsOfFiles, false);
724}
725function genTopDirsFilesTables() {
726 computeTopDirsFiles();
727 function addTable(name, divName, rows, clickFunction) {
728 var data = new google.visualization.DataTable();
729 data.addColumn("number", "index"); // not shown in view
730 data.addColumn("number", "# of warnings");
731 data.addColumn("string", name);
732 data.addRows(rows);
733 var formatter = new google.visualization.PatternFormat(
734 '<p onclick="' + clickFunction + '({0})">{2}</p>');
735 formatter.format(data, [0, 1, 2], 2);
736 var view = new google.visualization.DataView(data);
737 view.setColumns([1,2]); // hide the index column
738 var table = new google.visualization.Table(
739 document.getElementById(divName));
740 table.draw(view, {allowHtml: true, alternatingRowStyle: true});
741 }
Chih-Hung Hsieh4b020cd2022-05-17 11:42:25 -0700742 addTable("[Warning Type] Directory", "top_dirs_table", TopDirs, "selectDir");
743 addTable("[Warning Type] File", "top_files_table", TopFiles, "selectFile");
Chih-Hung Hsiehd4691ec2021-12-27 17:33:36 -0800744}
745function selectDirFile(idx, rows, dirFile) {
746 if (rows.length <= idx) {
747 return;
748 }
749 var name = rows[idx][2];
Chih-Hung Hsieh4b020cd2022-05-17 11:42:25 -0700750 var type = "";
751 if (name.startsWith("[")) {
752 type = " " + name.replace(/ .*/, "");
753 name = name.replace(/.* /, "");
754 }
Chih-Hung Hsiehd4691ec2021-12-27 17:33:36 -0800755 var spanName = "selected_" + dirFile + "_name";
Chih-Hung Hsieh4b020cd2022-05-17 11:42:25 -0700756 document.getElementById(spanName).innerHTML = name + type;
Chih-Hung Hsiehd4691ec2021-12-27 17:33:36 -0800757 var divName = "selected_" + dirFile + "_warnings";
758 var numWarnings = rows[idx][1].v;
759 var prefix = name.replace(/\\.\\.\\.$/, "");
760 var data = new google.visualization.DataTable();
Chih-Hung Hsieh4b020cd2022-05-17 11:42:25 -0700761 data.addColumn('string', numWarnings + type + ' warnings in ' + name);
Chih-Hung Hsiehd4691ec2021-12-27 17:33:36 -0800762 var getWarningMessage = (FlagPlatform == "chrome")
763 ? ((x) => addURLToLine(WarningMessages[Warnings[x][2]],
764 WarningLinks[Warnings[x][3]]))
765 : ((x) => addURL(WarningMessages[Warnings[x][2]]));
766 for (var i = 0; i < Warnings.length; i++) {
Chih-Hung Hsieh4b020cd2022-05-17 11:42:25 -0700767 if ((prefix.startsWith("*") || WarningMessages[Warnings[i][2]].startsWith(prefix)) &&
768 (type == "" || WarningMessages[Warnings[i][2]].endsWith(type))) {
Chih-Hung Hsiehd4691ec2021-12-27 17:33:36 -0800769 data.addRow([getWarningMessage(i)]);
770 }
771 }
772 var table = new google.visualization.Table(
773 document.getElementById(divName));
774 table.draw(data, {allowHtml: true, alternatingRowStyle: true});
775}
776function selectDir(idx) {
777 selectDirFile(idx, TopDirs, "directory")
778}
779function selectFile(idx) {
780 selectDirFile(idx, TopFiles, "file");
781}
782function genTables() {
783 genSelectedProjectsTable();
784 if (WarningMessages.length > 1) {
785 genTopDirsFilesTables();
786 }
787}
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800788"""
789
790
Chih-Hung Hsiehd4691ec2021-12-27 17:33:36 -0800791def dump_boxed_section(writer, func):
792 writer('<div class="box">')
793 func()
794 writer('</div>')
795
796
797def dump_section_header(writer, table_name, section_title):
798 writer('<h3><b><button id="' + table_name + '_mark" class="bt"\n' +
799 ' onclick="expand(\'' + table_name + '\');">&#x2295</button></b>\n' +
800 section_title + '</h3>')
801
802
803def dump_table_section(writer, table_name, section_title):
804 dump_section_header(writer, table_name, section_title)
805 writer('<div id="' + table_name + '" style="display:none;"></div>')
806
807
808def dump_dir_file_section(writer, dir_file, table_name, section_title):
809 section_name = 'top_' + dir_file + '_section'
810 dump_section_header(writer, section_name, section_title)
811 writer('<div id="' + section_name + '" style="display:none;">')
812 writer('<div id="' + table_name + '"></div>')
813 def subsection():
814 subsection_name = 'selected_' + dir_file + '_warnings'
815 subsection_title = ('Warnings in <span id="selected_' + dir_file +
816 '_name">(click a ' + dir_file +
817 ' in the above table)</span>')
818 dump_section_header(writer, subsection_name, subsection_title)
819 writer('<div id="' + subsection_name + '" style="display:none;"></div>')
820 dump_boxed_section(writer, subsection)
821 writer('</div>')
822
823
824# HTML output has the following major div elements:
825# selected_projects_section
826# top_directory_section
827# top_dirs_table
828# selected_directory_warnings
829# top_file_section
830# top_files_table
831# selected_file_warnings
832# all_warnings_section
833# warning_groups
834# fixed_warnings
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800835def dump_html(flags, output_stream, warning_messages, warning_links,
836 warning_records, header_str, warn_patterns, project_names):
837 """Dump the flags output to output_stream."""
838 writer = make_writer(output_stream)
839 dump_html_prologue('Warnings for ' + header_str, writer, warn_patterns,
840 project_names)
841 dump_stats(writer, warn_patterns)
Chih-Hung Hsiehd4691ec2021-12-27 17:33:36 -0800842 writer('<br><br>Press &#x2295 to show section content,'
843 ' and &#x2296 to hide the content.')
844 def section1():
845 dump_table_section(writer, 'selected_projects_section',
846 'Number of warnings in preselected project directories')
847 def section2():
848 dump_dir_file_section(
849 writer, 'directory', 'top_dirs_table',
Chih-Hung Hsieh4b020cd2022-05-17 11:42:25 -0700850 'Directories/Warnings with at least ' +
851 str(LIMIT_PERCENT_WARNINGS) + '% of all cases')
Chih-Hung Hsiehd4691ec2021-12-27 17:33:36 -0800852 def section3():
853 dump_dir_file_section(
854 writer, 'file', 'top_files_table',
Chih-Hung Hsieh4b020cd2022-05-17 11:42:25 -0700855 'Files/Warnings with at least ' +
856 str(LIMIT_PERCENT_WARNINGS) + '% of all or ' +
857 str(LIMIT_WARNINGS_PER_FILE) + ' cases')
Chih-Hung Hsiehd4691ec2021-12-27 17:33:36 -0800858 def section4():
859 writer('<script>')
860 emit_js_data(writer, flags, warning_messages, warning_links,
861 warning_records, warn_patterns, project_names)
862 writer(SCRIPTS_FOR_WARNING_GROUPS)
863 writer('</script>')
864 dump_section_header(writer, 'all_warnings_section',
865 'All warnings grouped by severities or projects')
866 writer('<div id="all_warnings_section" style="display:none;">')
867 emit_buttons(writer)
868 # Warning messages are grouped by severities or project names.
869 writer('<br><div id="warning_groups"></div>')
870 if flags.byproject:
871 writer('<script>groupByProject();</script>')
872 else:
873 writer('<script>groupBySeverity();</script>')
874 dump_fixed(writer, warn_patterns)
875 writer('</div>')
876 dump_boxed_section(writer, section1)
877 dump_boxed_section(writer, section2)
878 dump_boxed_section(writer, section3)
879 dump_boxed_section(writer, section4)
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800880 dump_html_epilogue(writer)
881
882
883def write_html(flags, project_names, warn_patterns, html_path, warning_messages,
884 warning_links, warning_records, header_str):
885 """Write warnings html file."""
886 if html_path:
Chih-Hung Hsieha6068222021-04-30 14:30:58 -0700887 with open(html_path, 'w') as outf:
888 dump_html(flags, outf, warning_messages, warning_links, warning_records,
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800889 header_str, warn_patterns, project_names)
890
891
892def write_out_csv(flags, warn_patterns, warning_messages, warning_links,
893 warning_records, header_str, project_names):
894 """Write warnings csv file."""
895 if flags.csvpath:
Chih-Hung Hsieha6068222021-04-30 14:30:58 -0700896 with open(flags.csvpath, 'w') as outf:
897 dump_csv(csv.writer(outf, lineterminator='\n'), warn_patterns)
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800898
Saeid Farivar Asanjan75dc8d22020-11-18 00:29:43 +0000899 if flags.csvwithdescription:
Chih-Hung Hsieha6068222021-04-30 14:30:58 -0700900 with open(flags.csvwithdescription, 'w') as outf:
901 dump_csv_with_description(csv.writer(outf, lineterminator='\n'),
Saeid Farivar Asanjan75dc8d22020-11-18 00:29:43 +0000902 warning_records, warning_messages,
903 warn_patterns, project_names)
904
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800905 if flags.gencsv:
906 dump_csv(csv.writer(sys.stdout, lineterminator='\n'), warn_patterns)
907 else:
908 dump_html(flags, sys.stdout, warning_messages, warning_links,
909 warning_records, header_str, warn_patterns, project_names)