blob: 3fa822a9b2b8f659e28be6ee0d64e8443532b003 [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 Hsieh7cc0e152021-04-26 17:09:36 -070059import html
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -080060import sys
61
62# pylint:disable=relative-beyond-top-level
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -080063from .severity import Severity
64
65
Chih-Hung Hsiehd4691ec2021-12-27 17:33:36 -080066# Report files with this number of warnings or more.
67LIMIT_WARNINGS_PER_FILE = 100
68# Report files/directories with this percentage of total warnings or more.
69LIMIT_PERCENT_WARNINGS = 1
70
Chih-Hung Hsieh98b285d2021-04-28 14:49:32 -070071HTML_HEAD_SCRIPTS = """\
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -080072 <script type="text/javascript">
73 function expand(id) {
74 var e = document.getElementById(id);
75 var f = document.getElementById(id + "_mark");
76 if (e.style.display == 'block') {
77 e.style.display = 'none';
78 f.innerHTML = '&#x2295';
79 }
80 else {
81 e.style.display = 'block';
82 f.innerHTML = '&#x2296';
83 }
84 };
85 function expandCollapse(show) {
86 for (var id = 1; ; id++) {
87 var e = document.getElementById(id + "");
88 var f = document.getElementById(id + "_mark");
89 if (!e || !f) break;
90 e.style.display = (show ? 'block' : 'none');
91 f.innerHTML = (show ? '&#x2296' : '&#x2295');
92 }
93 };
94 </script>
95 <style type="text/css">
96 th,td{border-collapse:collapse; border:1px solid black;}
Chih-Hung Hsiehd4691ec2021-12-27 17:33:36 -080097 .button{color:blue;font-size:100%;font-weight:bolder;}
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -080098 .bt{color:black;background-color:transparent;border:none;outline:none;
99 font-size:140%;font-weight:bolder;}
100 .c0{background-color:#e0e0e0;}
101 .c1{background-color:#d0d0d0;}
102 .t1{border-collapse:collapse; width:100%; border:1px solid black;}
Chih-Hung Hsiehd4691ec2021-12-27 17:33:36 -0800103 .box{margin:5pt; padding:5pt; border:1px solid;}
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800104 </style>
105 <script src="https://www.gstatic.com/charts/loader.js"></script>
106"""
107
108
109def make_writer(output_stream):
110
111 def writer(text):
112 return output_stream.write(text + '\n')
113
114 return writer
115
116
117def html_big(param):
118 return '<font size="+2">' + param + '</font>'
119
120
121def dump_html_prologue(title, writer, warn_patterns, project_names):
122 writer('<html>\n<head>')
123 writer('<title>' + title + '</title>')
Chih-Hung Hsieh98b285d2021-04-28 14:49:32 -0700124 writer(HTML_HEAD_SCRIPTS)
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800125 emit_stats_by_project(writer, warn_patterns, project_names)
126 writer('</head>\n<body>')
127 writer(html_big(title))
128 writer('<p>')
129
130
131def dump_html_epilogue(writer):
132 writer('</body>\n</head>\n</html>')
133
134
135def sort_warnings(warn_patterns):
136 for i in warn_patterns:
137 i['members'] = sorted(set(i['members']))
138
139
140def create_warnings(warn_patterns, project_names):
141 """Creates warnings s.t.
142
143 warnings[p][s] is as specified in above docs.
144
145 Args:
146 warn_patterns: list of warning patterns for specified platform
147 project_names: list of project names
148
149 Returns:
150 2D warnings array where warnings[p][s] is # of warnings in project name p of
151 severity level s
152 """
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800153 warnings = {p: {s.value: 0 for s in Severity.levels} for p in project_names}
Chih-Hung Hsieha6068222021-04-30 14:30:58 -0700154 for pattern in warn_patterns:
155 value = pattern['severity'].value
156 for project in pattern['projects']:
157 warnings[project][value] += pattern['projects'][project]
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800158 return warnings
159
160
161def get_total_by_project(warnings, project_names):
162 """Returns dict, project as key and # warnings for that project as value."""
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800163 return {
164 p: sum(warnings[p][s.value] for s in Severity.levels)
165 for p in project_names
166 }
167
168
169def get_total_by_severity(warnings, project_names):
170 """Returns dict, severity as key and # warnings of that severity as value."""
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800171 return {
172 s.value: sum(warnings[p][s.value] for p in project_names)
173 for s in Severity.levels
174 }
175
176
177def emit_table_header(total_by_severity):
178 """Returns list of HTML-formatted content for severity stats."""
179
180 stats_header = ['Project']
Chih-Hung Hsieha6068222021-04-30 14:30:58 -0700181 for severity in Severity.levels:
182 if total_by_severity[severity.value]:
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800183 stats_header.append(
184 '<span style=\'background-color:{}\'>{}</span>'.format(
Chih-Hung Hsieha6068222021-04-30 14:30:58 -0700185 severity.color, severity.column_header))
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800186 stats_header.append('TOTAL')
187 return stats_header
188
189
190def emit_row_counts_per_project(warnings, total_by_project, total_by_severity,
191 project_names):
192 """Returns total project warnings and row of stats for each project.
193
194 Args:
195 warnings: output of create_warnings(warn_patterns, project_names)
196 total_by_project: output of get_total_by_project(project_names)
197 total_by_severity: output of get_total_by_severity(project_names)
198 project_names: list of project names
199
200 Returns:
201 total_all_projects, the total number of warnings over all projects
202 stats_rows, a 2d list where each row is [Project Name, <severity counts>,
203 total # warnings for this project]
204 """
205
206 total_all_projects = 0
207 stats_rows = []
Chih-Hung Hsieha6068222021-04-30 14:30:58 -0700208 for p_name in project_names:
209 if total_by_project[p_name]:
210 one_row = [p_name]
211 for severity in Severity.levels:
212 if total_by_severity[severity.value]:
213 one_row.append(warnings[p_name][severity.value])
214 one_row.append(total_by_project[p_name])
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800215 stats_rows.append(one_row)
Chih-Hung Hsieha6068222021-04-30 14:30:58 -0700216 total_all_projects += total_by_project[p_name]
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800217 return total_all_projects, stats_rows
218
219
220def emit_row_counts_per_severity(total_by_severity, stats_header, stats_rows,
221 total_all_projects, writer):
222 """Emits stats_header and stats_rows as specified above.
223
224 Args:
225 total_by_severity: output of get_total_by_severity()
226 stats_header: output of emit_table_header()
227 stats_rows: output of emit_row_counts_per_project()
228 total_all_projects: output of emit_row_counts_per_project()
229 writer: writer returned by make_writer(output_stream)
230 """
231
232 total_all_severities = 0
233 one_row = ['<b>TOTAL</b>']
Chih-Hung Hsieha6068222021-04-30 14:30:58 -0700234 for severity in Severity.levels:
235 if total_by_severity[severity.value]:
236 one_row.append(total_by_severity[severity.value])
237 total_all_severities += total_by_severity[severity.value]
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800238 one_row.append(total_all_projects)
239 stats_rows.append(one_row)
240 writer('<script>')
241 emit_const_string_array('StatsHeader', stats_header, writer)
242 emit_const_object_array('StatsRows', stats_rows, writer)
Chih-Hung Hsieh98b285d2021-04-28 14:49:32 -0700243 writer(DRAW_TABLE_JAVASCRIPT)
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800244 writer('</script>')
245
246
247def emit_stats_by_project(writer, warn_patterns, project_names):
248 """Dump a google chart table of warnings per project and severity."""
249
250 warnings = create_warnings(warn_patterns, project_names)
251 total_by_project = get_total_by_project(warnings, project_names)
252 total_by_severity = get_total_by_severity(warnings, project_names)
253 stats_header = emit_table_header(total_by_severity)
Chih-Hung Hsieh98b285d2021-04-28 14:49:32 -0700254 total_all_projects, stats_rows = emit_row_counts_per_project(
255 warnings, total_by_project, total_by_severity, project_names)
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800256 emit_row_counts_per_severity(total_by_severity, stats_header, stats_rows,
257 total_all_projects, writer)
258
259
260def dump_stats(writer, warn_patterns):
261 """Dump some stats about total number of warnings and such."""
262
263 known = 0
264 skipped = 0
265 unknown = 0
266 sort_warnings(warn_patterns)
267 for i in warn_patterns:
268 if i['severity'] == Severity.UNMATCHED:
269 unknown += len(i['members'])
270 elif i['severity'] == Severity.SKIP:
271 skipped += len(i['members'])
272 else:
273 known += len(i['members'])
274 writer('Number of classified warnings: <b>' + str(known) + '</b><br>')
275 writer('Number of skipped warnings: <b>' + str(skipped) + '</b><br>')
276 writer('Number of unclassified warnings: <b>' + str(unknown) + '</b><br>')
277 total = unknown + known + skipped
278 extra_msg = ''
279 if total < 1000:
280 extra_msg = ' (low count may indicate incremental build)'
281 writer('Total number of warnings: <b>' + str(total) + '</b>' + extra_msg)
282
283
284# New base table of warnings, [severity, warn_id, project, warning_message]
285# Need buttons to show warnings in different grouping options.
286# (1) Current, group by severity, id for each warning pattern
287# sort by severity, warn_id, warning_message
288# (2) Current --byproject, group by severity,
289# id for each warning pattern + project name
290# sort by severity, warn_id, project, warning_message
291# (3) New, group by project + severity,
292# id for each warning pattern
293# sort by project, severity, warn_id, warning_message
294def emit_buttons(writer):
Chih-Hung Hsieh98b285d2021-04-28 14:49:32 -0700295 """Write the button elements in HTML."""
Chih-Hung Hsiehd4691ec2021-12-27 17:33:36 -0800296 writer('<p><button class="button" onclick="expandCollapse(1);">'
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800297 'Expand all warnings</button>\n'
298 '<button class="button" onclick="expandCollapse(0);">'
299 'Collapse all warnings</button>\n'
Chih-Hung Hsiehd4691ec2021-12-27 17:33:36 -0800300 '<p><button class="button" onclick="groupBySeverity();">'
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800301 'Group warnings by severity</button>\n'
302 '<button class="button" onclick="groupByProject();">'
Chih-Hung Hsiehd4691ec2021-12-27 17:33:36 -0800303 'Group warnings by project</button>')
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800304
305
306def all_patterns(category):
307 patterns = ''
308 for i in category['patterns']:
309 patterns += i
310 patterns += ' / '
311 return patterns
312
313
314def dump_fixed(writer, warn_patterns):
315 """Show which warnings no longer occur."""
316 anchor = 'fixed_warnings'
317 mark = anchor + '_mark'
318 writer('\n<br><p style="background-color:lightblue"><b>'
319 '<button id="' + mark + '" '
320 'class="bt" onclick="expand(\'' + anchor + '\');">'
321 '&#x2295</button> Fixed warnings. '
322 'No more occurrences. Please consider turning these into '
323 'errors if possible, before they are reintroduced in to the build'
324 ':</b></p>')
325 writer('<blockquote>')
326 fixed_patterns = []
327 for i in warn_patterns:
328 if not i['members']:
329 fixed_patterns.append(i['description'] + ' (' + all_patterns(i) + ')')
330 fixed_patterns = sorted(fixed_patterns)
331 writer('<div id="' + anchor + '" style="display:none;"><table>')
332 cur_row_class = 0
333 for text in fixed_patterns:
334 cur_row_class = 1 - cur_row_class
335 # remove last '\n'
Chih-Hung Hsieha6068222021-04-30 14:30:58 -0700336 out_text = text[:-1] if text[-1] == '\n' else text
Chih-Hung Hsieh5d9ee042021-06-01 16:03:22 -0700337 writer('<tr><td class="c' + str(cur_row_class) + '">'
338 + out_text + '</td></tr>')
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800339 writer('</table></div>')
340 writer('</blockquote>')
341
342
343def write_severity(csvwriter, sev, kind, warn_patterns):
344 """Count warnings of given severity and write CSV entries to writer."""
345 total = 0
346 for pattern in warn_patterns:
347 if pattern['severity'] == sev and pattern['members']:
Chih-Hung Hsieha6068222021-04-30 14:30:58 -0700348 num_members = len(pattern['members'])
349 total += num_members
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800350 warning = kind + ': ' + (pattern['description'] or '?')
Chih-Hung Hsieha6068222021-04-30 14:30:58 -0700351 csvwriter.writerow([num_members, '', warning])
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800352 # print number of warnings for each project, ordered by project name
353 projects = sorted(pattern['projects'].keys())
354 for project in projects:
355 csvwriter.writerow([pattern['projects'][project], project, warning])
356 csvwriter.writerow([total, '', kind + ' warnings'])
357 return total
358
359
360def dump_csv(csvwriter, warn_patterns):
361 """Dump number of warnings in CSV format to writer."""
362 sort_warnings(warn_patterns)
363 total = 0
Chih-Hung Hsieha6068222021-04-30 14:30:58 -0700364 for severity in Severity.levels:
Chih-Hung Hsieh5d9ee042021-06-01 16:03:22 -0700365 total += write_severity(
366 csvwriter, severity, severity.column_header, warn_patterns)
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800367 csvwriter.writerow([total, '', 'All warnings'])
368
369
Saeid Farivar Asanjan75dc8d22020-11-18 00:29:43 +0000370def dump_csv_with_description(csvwriter, warning_records, warning_messages,
371 warn_patterns, project_names):
372 """Outputs all the warning messages by project."""
373 csv_output = []
374 for record in warning_records:
375 project_name = project_names[record[1]]
376 pattern = warn_patterns[record[0]]
377 severity = pattern['severity'].header
378 category = pattern['category']
379 description = pattern['description']
380 warning = warning_messages[record[2]]
381 csv_output.append([project_name, severity,
382 category, description,
383 warning])
384 csv_output = sorted(csv_output)
385 for output in csv_output:
386 csvwriter.writerow(output)
387
388
Chih-Hung Hsieha6068222021-04-30 14:30:58 -0700389# Return line with escaped backslash and quotation characters.
390def escape_string(line):
391 return line.replace('\\', '\\\\').replace('"', '\\"')
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800392
393
Chih-Hung Hsieha6068222021-04-30 14:30:58 -0700394# Return line without trailing '\n' and escape the quotation characters.
395def strip_escape_string(line):
396 if not line:
397 return line
398 line = line[:-1] if line[-1] == '\n' else line
399 return escape_string(line)
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800400
401
402def emit_warning_array(name, writer, warn_patterns):
403 writer('var warning_{} = ['.format(name))
Chih-Hung Hsieha6068222021-04-30 14:30:58 -0700404 for pattern in warn_patterns:
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800405 if name == 'severity':
Chih-Hung Hsieha6068222021-04-30 14:30:58 -0700406 writer('{},'.format(pattern[name].value))
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800407 else:
Chih-Hung Hsieha6068222021-04-30 14:30:58 -0700408 writer('{},'.format(pattern[name]))
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800409 writer('];')
410
411
412def emit_warning_arrays(writer, warn_patterns):
413 emit_warning_array('severity', writer, warn_patterns)
414 writer('var warning_description = [')
Chih-Hung Hsieha6068222021-04-30 14:30:58 -0700415 for pattern in warn_patterns:
416 if pattern['members']:
417 writer('"{}",'.format(escape_string(pattern['description'])))
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800418 else:
419 writer('"",') # no such warning
420 writer('];')
421
422
Chih-Hung Hsieh98b285d2021-04-28 14:49:32 -0700423SCRIPTS_FOR_WARNING_GROUPS = """
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800424 function compareMessages(x1, x2) { // of the same warning type
425 return (WarningMessages[x1[2]] <= WarningMessages[x2[2]]) ? -1 : 1;
426 }
427 function byMessageCount(x1, x2) {
428 return x2[2] - x1[2]; // reversed order
429 }
430 function bySeverityMessageCount(x1, x2) {
431 // orer by severity first
432 if (x1[1] != x2[1])
433 return x1[1] - x2[1];
434 return byMessageCount(x1, x2);
435 }
436 const ParseLinePattern = /^([^ :]+):(\\d+):(.+)/;
437 function addURL(line) { // used by Android
438 if (FlagURL == "") return line;
439 if (FlagSeparator == "") {
440 return line.replace(ParseLinePattern,
441 "<a target='_blank' href='" + FlagURL + "/$1'>$1</a>:$2:$3");
442 }
443 return line.replace(ParseLinePattern,
444 "<a target='_blank' href='" + FlagURL + "/$1" + FlagSeparator +
445 "$2'>$1:$2</a>:$3");
446 }
447 function addURLToLine(line, link) { // used by Chrome
448 let line_split = line.split(":");
449 let path = line_split.slice(0,3).join(":");
450 let msg = line_split.slice(3).join(":");
451 let html_link = `<a target="_blank" href="${link}">${path}</a>${msg}`;
452 return html_link;
453 }
454 function createArrayOfDictionaries(n) {
455 var result = [];
456 for (var i=0; i<n; i++) result.push({});
457 return result;
458 }
459 function groupWarningsBySeverity() {
460 // groups is an array of dictionaries,
461 // each dictionary maps from warning type to array of warning messages.
462 var groups = createArrayOfDictionaries(SeverityColors.length);
463 for (var i=0; i<Warnings.length; i++) {
464 var w = Warnings[i][0];
465 var s = WarnPatternsSeverity[w];
466 var k = w.toString();
467 if (!(k in groups[s]))
468 groups[s][k] = [];
469 groups[s][k].push(Warnings[i]);
470 }
471 return groups;
472 }
473 function groupWarningsByProject() {
474 var groups = createArrayOfDictionaries(ProjectNames.length);
475 for (var i=0; i<Warnings.length; i++) {
476 var w = Warnings[i][0];
477 var p = Warnings[i][1];
478 var k = w.toString();
479 if (!(k in groups[p]))
480 groups[p][k] = [];
481 groups[p][k].push(Warnings[i]);
482 }
483 return groups;
484 }
485 var GlobalAnchor = 0;
486 function createWarningSection(header, color, group) {
487 var result = "";
488 var groupKeys = [];
489 var totalMessages = 0;
490 for (var k in group) {
491 totalMessages += group[k].length;
492 groupKeys.push([k, WarnPatternsSeverity[parseInt(k)], group[k].length]);
493 }
494 groupKeys.sort(bySeverityMessageCount);
495 for (var idx=0; idx<groupKeys.length; idx++) {
496 var k = groupKeys[idx][0];
497 var messages = group[k];
498 var w = parseInt(k);
499 var wcolor = SeverityColors[WarnPatternsSeverity[w]];
500 var description = WarnPatternsDescription[w];
501 if (description.length == 0)
502 description = "???";
503 GlobalAnchor += 1;
504 result += "<table class='t1'><tr bgcolor='" + wcolor + "'><td>" +
505 "<button class='bt' id='" + GlobalAnchor + "_mark" +
506 "' onclick='expand(\\"" + GlobalAnchor + "\\");'>" +
507 "&#x2295</button> " +
508 description + " (" + messages.length + ")</td></tr></table>";
509 result += "<div id='" + GlobalAnchor +
510 "' style='display:none;'><table class='t1'>";
511 var c = 0;
512 messages.sort(compareMessages);
513 if (FlagPlatform == "chrome") {
514 for (var i=0; i<messages.length; i++) {
515 result += "<tr><td class='c" + c + "'>" +
516 addURLToLine(WarningMessages[messages[i][2]], WarningLinks[messages[i][3]]) + "</td></tr>";
517 c = 1 - c;
518 }
519 } else {
520 for (var i=0; i<messages.length; i++) {
521 result += "<tr><td class='c" + c + "'>" +
522 addURL(WarningMessages[messages[i][2]]) + "</td></tr>";
523 c = 1 - c;
524 }
525 }
526 result += "</table></div>";
527 }
528 if (result.length > 0) {
529 return "<br><span style='background-color:" + color + "'><b>" +
530 header + ": " + totalMessages +
531 "</b></span><blockquote><table class='t1'>" +
532 result + "</table></blockquote>";
533
534 }
535 return ""; // empty section
536 }
537 function generateSectionsBySeverity() {
538 var result = "";
539 var groups = groupWarningsBySeverity();
540 for (s=0; s<SeverityColors.length; s++) {
541 result += createWarningSection(SeverityHeaders[s], SeverityColors[s],
542 groups[s]);
543 }
544 return result;
545 }
546 function generateSectionsByProject() {
547 var result = "";
548 var groups = groupWarningsByProject();
549 for (i=0; i<groups.length; i++) {
550 result += createWarningSection(ProjectNames[i], 'lightgrey', groups[i]);
551 }
552 return result;
553 }
554 function groupWarnings(generator) {
555 GlobalAnchor = 0;
556 var e = document.getElementById("warning_groups");
557 e.innerHTML = generator();
558 }
559 function groupBySeverity() {
560 groupWarnings(generateSectionsBySeverity);
561 }
562 function groupByProject() {
563 groupWarnings(generateSectionsByProject);
564 }
565"""
566
567
Chih-Hung Hsiehd4691ec2021-12-27 17:33:36 -0800568# Emit a JavaScript const number
569def emit_const_number(name, value, writer):
570 writer('const ' + name + ' = ' + str(value) + ';')
571
572
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800573# Emit a JavaScript const string
574def emit_const_string(name, value, writer):
575 writer('const ' + name + ' = "' + escape_string(value) + '";')
576
577
578# Emit a JavaScript const integer array.
579def emit_const_int_array(name, array, writer):
580 writer('const ' + name + ' = [')
Chih-Hung Hsieha6068222021-04-30 14:30:58 -0700581 for item in array:
582 writer(str(item) + ',')
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800583 writer('];')
584
585
586# Emit a JavaScript const string array.
587def emit_const_string_array(name, array, writer):
588 writer('const ' + name + ' = [')
Chih-Hung Hsieha6068222021-04-30 14:30:58 -0700589 for item in array:
590 writer('"' + strip_escape_string(item) + '",')
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800591 writer('];')
592
593
594# Emit a JavaScript const string array for HTML.
595def emit_const_html_string_array(name, array, writer):
596 writer('const ' + name + ' = [')
Chih-Hung Hsieha6068222021-04-30 14:30:58 -0700597 for item in array:
598 writer('"' + html.escape(strip_escape_string(item)) + '",')
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800599 writer('];')
600
601
602# Emit a JavaScript const object array.
603def emit_const_object_array(name, array, writer):
604 writer('const ' + name + ' = [')
Chih-Hung Hsieha6068222021-04-30 14:30:58 -0700605 for item in array:
606 writer(str(item) + ',')
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800607 writer('];')
608
609
610def emit_js_data(writer, flags, warning_messages, warning_links,
611 warning_records, warn_patterns, project_names):
612 """Dump dynamic HTML page's static JavaScript data."""
613 emit_const_string('FlagPlatform', flags.platform, writer)
614 emit_const_string('FlagURL', flags.url, writer)
615 emit_const_string('FlagSeparator', flags.separator, writer)
Chih-Hung Hsiehd4691ec2021-12-27 17:33:36 -0800616 emit_const_number('LimitWarningsPerFile', LIMIT_WARNINGS_PER_FILE, writer)
617 emit_const_number('LimitPercentWarnings', LIMIT_PERCENT_WARNINGS, writer)
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800618 emit_const_string_array('SeverityColors', [s.color for s in Severity.levels],
619 writer)
620 emit_const_string_array('SeverityHeaders',
621 [s.header for s in Severity.levels], writer)
622 emit_const_string_array('SeverityColumnHeaders',
623 [s.column_header for s in Severity.levels], writer)
624 emit_const_string_array('ProjectNames', project_names, writer)
625 # pytype: disable=attribute-error
626 emit_const_int_array('WarnPatternsSeverity',
627 [w['severity'].value for w in warn_patterns], writer)
628 # pytype: enable=attribute-error
629 emit_const_html_string_array('WarnPatternsDescription',
630 [w['description'] for w in warn_patterns],
631 writer)
632 emit_const_html_string_array('WarningMessages', warning_messages, writer)
633 emit_const_object_array('Warnings', warning_records, writer)
634 if flags.platform == 'chrome':
635 emit_const_html_string_array('WarningLinks', warning_links, writer)
636
637
Chih-Hung Hsieh98b285d2021-04-28 14:49:32 -0700638DRAW_TABLE_JAVASCRIPT = """
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800639google.charts.load('current', {'packages':['table']});
Chih-Hung Hsiehd4691ec2021-12-27 17:33:36 -0800640google.charts.setOnLoadCallback(genTables);
641function genSelectedProjectsTable() {
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800642 var data = new google.visualization.DataTable();
643 data.addColumn('string', StatsHeader[0]);
644 for (var i=1; i<StatsHeader.length; i++) {
645 data.addColumn('number', StatsHeader[i]);
646 }
647 data.addRows(StatsRows);
648 for (var i=0; i<StatsRows.length; i++) {
649 for (var j=0; j<StatsHeader.length; j++) {
650 data.setProperty(i, j, 'style', 'border:1px solid black;');
651 }
652 }
653 var table = new google.visualization.Table(
Chih-Hung Hsiehd4691ec2021-12-27 17:33:36 -0800654 document.getElementById('selected_projects_section'));
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800655 table.draw(data, {allowHtml: true, alternatingRowStyle: true});
656}
Chih-Hung Hsiehd4691ec2021-12-27 17:33:36 -0800657// Global TopDirs and TopFiles are computed later by genTables.
658window.TopDirs = [];
659window.TopFiles = [];
660function computeTopDirsFiles() {
661 var numWarnings = WarningMessages.length;
662 var warningsOfFiles = {};
663 var warningsOfDirs = {};
664 var subDirs = {};
665 function addOneWarning(map, key) {
666 map[key] = 1 + ((key in map) ? map[key] : 0);
667 }
668 for (var i = 0; i < numWarnings; i++) {
669 var file = WarningMessages[i].replace(/:.*/, "");
670 addOneWarning(warningsOfFiles, file);
671 var dirs = file.split("/");
672 var dir = dirs[0];
673 addOneWarning(warningsOfDirs, dir);
674 for (var d = 1; d < dirs.length - 1; d++) {
675 var subDir = dir + "/" + dirs[d];
676 if (!(dir in subDirs)) {
677 subDirs[dir] = {};
678 }
679 subDirs[dir][subDir] = 1;
680 dir = subDir;
681 addOneWarning(warningsOfDirs, dir);
682 }
683 }
684 var minDirWarnings = numWarnings*(LimitPercentWarnings/100);
685 var minFileWarnings = Math.min(LimitWarningsPerFile, minDirWarnings);
686 // Each row in TopDirs and TopFiles has
687 // [index, {v:<num_of_warnings>, f:<percent>}, file_or_dir_name]
688 function countWarnings(minWarnings, warningsOf, isDir) {
689 var rows = [];
690 for (var name in warningsOf) {
691 if (isDir && name in subDirs && Object.keys(subDirs[name]).length < 2) {
692 continue; // skip a directory if it has only one subdir
693 }
694 var count = warningsOf[name];
695 if (count >= minWarnings) {
696 name = isDir ? (name + "/...") : name;
697 var percent = (100*count/numWarnings).toFixed(1);
698 var countFormat = count + ' (' + percent + '%)';
699 rows.push([0, {v:count, f:countFormat}, name]);
700 }
701 }
702 rows.sort((a,b) => b[1].v - a[1].v);
703 for (var i=0; i<rows.length; i++) {
704 rows[i][0] = i;
705 }
706 return rows;
707 }
708 TopDirs = countWarnings(minDirWarnings, warningsOfDirs, true);
709 TopFiles = countWarnings(minFileWarnings, warningsOfFiles, false);
710}
711function genTopDirsFilesTables() {
712 computeTopDirsFiles();
713 function addTable(name, divName, rows, clickFunction) {
714 var data = new google.visualization.DataTable();
715 data.addColumn("number", "index"); // not shown in view
716 data.addColumn("number", "# of warnings");
717 data.addColumn("string", name);
718 data.addRows(rows);
719 var formatter = new google.visualization.PatternFormat(
720 '<p onclick="' + clickFunction + '({0})">{2}</p>');
721 formatter.format(data, [0, 1, 2], 2);
722 var view = new google.visualization.DataView(data);
723 view.setColumns([1,2]); // hide the index column
724 var table = new google.visualization.Table(
725 document.getElementById(divName));
726 table.draw(view, {allowHtml: true, alternatingRowStyle: true});
727 }
728 addTable("Directory", "top_dirs_table", TopDirs, "selectDir");
729 addTable("File", "top_files_table", TopFiles, "selectFile");
730}
731function selectDirFile(idx, rows, dirFile) {
732 if (rows.length <= idx) {
733 return;
734 }
735 var name = rows[idx][2];
736 var spanName = "selected_" + dirFile + "_name";
737 document.getElementById(spanName).innerHTML = name;
738 var divName = "selected_" + dirFile + "_warnings";
739 var numWarnings = rows[idx][1].v;
740 var prefix = name.replace(/\\.\\.\\.$/, "");
741 var data = new google.visualization.DataTable();
742 data.addColumn('string', numWarnings + ' warnings in ' + name);
743 var getWarningMessage = (FlagPlatform == "chrome")
744 ? ((x) => addURLToLine(WarningMessages[Warnings[x][2]],
745 WarningLinks[Warnings[x][3]]))
746 : ((x) => addURL(WarningMessages[Warnings[x][2]]));
747 for (var i = 0; i < Warnings.length; i++) {
748 if (WarningMessages[Warnings[i][2]].startsWith(prefix)) {
749 data.addRow([getWarningMessage(i)]);
750 }
751 }
752 var table = new google.visualization.Table(
753 document.getElementById(divName));
754 table.draw(data, {allowHtml: true, alternatingRowStyle: true});
755}
756function selectDir(idx) {
757 selectDirFile(idx, TopDirs, "directory")
758}
759function selectFile(idx) {
760 selectDirFile(idx, TopFiles, "file");
761}
762function genTables() {
763 genSelectedProjectsTable();
764 if (WarningMessages.length > 1) {
765 genTopDirsFilesTables();
766 }
767}
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800768"""
769
770
Chih-Hung Hsiehd4691ec2021-12-27 17:33:36 -0800771def dump_boxed_section(writer, func):
772 writer('<div class="box">')
773 func()
774 writer('</div>')
775
776
777def dump_section_header(writer, table_name, section_title):
778 writer('<h3><b><button id="' + table_name + '_mark" class="bt"\n' +
779 ' onclick="expand(\'' + table_name + '\');">&#x2295</button></b>\n' +
780 section_title + '</h3>')
781
782
783def dump_table_section(writer, table_name, section_title):
784 dump_section_header(writer, table_name, section_title)
785 writer('<div id="' + table_name + '" style="display:none;"></div>')
786
787
788def dump_dir_file_section(writer, dir_file, table_name, section_title):
789 section_name = 'top_' + dir_file + '_section'
790 dump_section_header(writer, section_name, section_title)
791 writer('<div id="' + section_name + '" style="display:none;">')
792 writer('<div id="' + table_name + '"></div>')
793 def subsection():
794 subsection_name = 'selected_' + dir_file + '_warnings'
795 subsection_title = ('Warnings in <span id="selected_' + dir_file +
796 '_name">(click a ' + dir_file +
797 ' in the above table)</span>')
798 dump_section_header(writer, subsection_name, subsection_title)
799 writer('<div id="' + subsection_name + '" style="display:none;"></div>')
800 dump_boxed_section(writer, subsection)
801 writer('</div>')
802
803
804# HTML output has the following major div elements:
805# selected_projects_section
806# top_directory_section
807# top_dirs_table
808# selected_directory_warnings
809# top_file_section
810# top_files_table
811# selected_file_warnings
812# all_warnings_section
813# warning_groups
814# fixed_warnings
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800815def dump_html(flags, output_stream, warning_messages, warning_links,
816 warning_records, header_str, warn_patterns, project_names):
817 """Dump the flags output to output_stream."""
818 writer = make_writer(output_stream)
819 dump_html_prologue('Warnings for ' + header_str, writer, warn_patterns,
820 project_names)
821 dump_stats(writer, warn_patterns)
Chih-Hung Hsiehd4691ec2021-12-27 17:33:36 -0800822 writer('<br><br>Press &#x2295 to show section content,'
823 ' and &#x2296 to hide the content.')
824 def section1():
825 dump_table_section(writer, 'selected_projects_section',
826 'Number of warnings in preselected project directories')
827 def section2():
828 dump_dir_file_section(
829 writer, 'directory', 'top_dirs_table',
830 'Directories with at least ' +
831 str(LIMIT_PERCENT_WARNINGS) + '% warnings')
832 def section3():
833 dump_dir_file_section(
834 writer, 'file', 'top_files_table',
835 'Files with at least ' +
836 str(LIMIT_PERCENT_WARNINGS) + '% or ' +
837 str(LIMIT_WARNINGS_PER_FILE) + ' warnings')
838 def section4():
839 writer('<script>')
840 emit_js_data(writer, flags, warning_messages, warning_links,
841 warning_records, warn_patterns, project_names)
842 writer(SCRIPTS_FOR_WARNING_GROUPS)
843 writer('</script>')
844 dump_section_header(writer, 'all_warnings_section',
845 'All warnings grouped by severities or projects')
846 writer('<div id="all_warnings_section" style="display:none;">')
847 emit_buttons(writer)
848 # Warning messages are grouped by severities or project names.
849 writer('<br><div id="warning_groups"></div>')
850 if flags.byproject:
851 writer('<script>groupByProject();</script>')
852 else:
853 writer('<script>groupBySeverity();</script>')
854 dump_fixed(writer, warn_patterns)
855 writer('</div>')
856 dump_boxed_section(writer, section1)
857 dump_boxed_section(writer, section2)
858 dump_boxed_section(writer, section3)
859 dump_boxed_section(writer, section4)
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800860 dump_html_epilogue(writer)
861
862
863def write_html(flags, project_names, warn_patterns, html_path, warning_messages,
864 warning_links, warning_records, header_str):
865 """Write warnings html file."""
866 if html_path:
Chih-Hung Hsieha6068222021-04-30 14:30:58 -0700867 with open(html_path, 'w') as outf:
868 dump_html(flags, outf, warning_messages, warning_links, warning_records,
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800869 header_str, warn_patterns, project_names)
870
871
872def write_out_csv(flags, warn_patterns, warning_messages, warning_links,
873 warning_records, header_str, project_names):
874 """Write warnings csv file."""
875 if flags.csvpath:
Chih-Hung Hsieha6068222021-04-30 14:30:58 -0700876 with open(flags.csvpath, 'w') as outf:
877 dump_csv(csv.writer(outf, lineterminator='\n'), warn_patterns)
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800878
Saeid Farivar Asanjan75dc8d22020-11-18 00:29:43 +0000879 if flags.csvwithdescription:
Chih-Hung Hsieha6068222021-04-30 14:30:58 -0700880 with open(flags.csvwithdescription, 'w') as outf:
881 dump_csv_with_description(csv.writer(outf, lineterminator='\n'),
Saeid Farivar Asanjan75dc8d22020-11-18 00:29:43 +0000882 warning_records, warning_messages,
883 warn_patterns, project_names)
884
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800885 if flags.gencsv:
886 dump_csv(csv.writer(sys.stdout, lineterminator='\n'), warn_patterns)
887 else:
888 dump_html(flags, sys.stdout, warning_messages, warning_links,
889 warning_records, header_str, warn_patterns, project_names)