diff options
Diffstat (limited to 'tools/warn/warn_common.py')
-rwxr-xr-x | tools/warn/warn_common.py | 666 |
1 files changed, 8 insertions, 658 deletions
diff --git a/tools/warn/warn_common.py b/tools/warn/warn_common.py index 329f1e5524..68ed995c00 100755 --- a/tools/warn/warn_common.py +++ b/tools/warn/warn_common.py @@ -45,46 +45,7 @@ Default input file is build.log, which can be changed with the --log flag. # idx to warning_links] # parse_input_file # -# To emit html page of warning messages: -# flags: --byproject, --url, --separator -# Old stuff for static html components: -# html_script_style: static html scripts and styles -# htmlbig: -# dump_stats, dump_html_prologue, dump_html_epilogue: -# emit_buttons: -# dump_fixed -# sort_warnings: -# emit_stats_by_project: -# all_patterns, -# findproject, classify_warning -# dump_html -# -# New dynamic HTML page's static JavaScript data: -# Some data are copied from Python to JavaScript, to generate HTML elements. -# FlagPlatform flags.platform -# FlagURL flags.url, used by 'android' -# FlagSeparator flags.separator, used by 'android' -# SeverityColors: list of colors for all severity levels -# SeverityHeaders: list of headers for all severity levels -# SeverityColumnHeaders: list of column_headers for all severity levels -# ProjectNames: project_names, or project_list[*][0] -# WarnPatternsSeverity: warn_patterns[*]['severity'] -# WarnPatternsDescription: warn_patterns[*]['description'] -# WarningMessages: warning_messages -# Warnings: warning_records -# StatsHeader: warning count table header row -# StatsRows: array of warning count table rows -# -# New dynamic HTML page's dynamic JavaScript data: -# -# New dynamic HTML related function to emit data: -# escape_string, strip_escape_string, emit_warning_arrays -# emit_js_data(): - -from __future__ import print_function import argparse -import cgi -import csv import io import multiprocessing import os @@ -92,15 +53,15 @@ import re import sys # pylint:disable=relative-beyond-top-level +# pylint:disable=g-importing-member from . import android_project_list from . import chrome_project_list from . import cpp_warn_patterns as cpp_patterns +from . import html_writer from . import java_warn_patterns as java_patterns from . import make_warn_patterns as make_patterns from . import other_warn_patterns as other_patterns from . import tidy_warn_patterns as tidy_patterns -# pylint:disable=g-importing-member -from .severity import Severity def parse_args(use_google3): @@ -150,304 +111,6 @@ def get_project_names(project_list): return [p[0] for p in project_list] -html_head_scripts = """\ - <script type="text/javascript"> - function expand(id) { - var e = document.getElementById(id); - var f = document.getElementById(id + "_mark"); - if (e.style.display == 'block') { - e.style.display = 'none'; - f.innerHTML = '⊕'; - } - else { - e.style.display = 'block'; - f.innerHTML = '⊖'; - } - }; - function expandCollapse(show) { - for (var id = 1; ; id++) { - var e = document.getElementById(id + ""); - var f = document.getElementById(id + "_mark"); - if (!e || !f) break; - e.style.display = (show ? 'block' : 'none'); - f.innerHTML = (show ? '⊖' : '⊕'); - } - }; - </script> - <style type="text/css"> - th,td{border-collapse:collapse; border:1px solid black;} - .button{color:blue;font-size:110%;font-weight:bolder;} - .bt{color:black;background-color:transparent;border:none;outline:none; - font-size:140%;font-weight:bolder;} - .c0{background-color:#e0e0e0;} - .c1{background-color:#d0d0d0;} - .t1{border-collapse:collapse; width:100%; border:1px solid black;} - </style> - <script src="https://www.gstatic.com/charts/loader.js"></script> -""" - - -def make_writer(output_stream): - - def writer(text): - return output_stream.write(text + '\n') - - return writer - - -def html_big(param): - return '<font size="+2">' + param + '</font>' - - -def dump_html_prologue(title, writer, warn_patterns, project_names): - writer('<html>\n<head>') - writer('<title>' + title + '</title>') - writer(html_head_scripts) - emit_stats_by_project(writer, warn_patterns, project_names) - writer('</head>\n<body>') - writer(html_big(title)) - writer('<p>') - - -def dump_html_epilogue(writer): - writer('</body>\n</head>\n</html>') - - -def sort_warnings(warn_patterns): - for i in warn_patterns: - i['members'] = sorted(set(i['members'])) - - -def create_warnings(warn_patterns, project_names): - """Creates warnings s.t. - - warnings[p][s] is as specified in above docs. - - Args: - warn_patterns: list of warning patterns for specified platform - project_names: list of project names - - Returns: - 2D warnings array where warnings[p][s] is # of warnings in project name p of - severity level s - """ - # pylint:disable=g-complex-comprehension - warnings = {p: {s.value: 0 for s in Severity.levels} for p in project_names} - for i in warn_patterns: - s = i['severity'].value - for p in i['projects']: - warnings[p][s] += i['projects'][p] - return warnings - - -def get_total_by_project(warnings, project_names): - """Returns dict, project as key and # warnings for that project as value.""" - # pylint:disable=g-complex-comprehension - return { - p: sum(warnings[p][s.value] for s in Severity.levels) - for p in project_names - } - - -def get_total_by_severity(warnings, project_names): - """Returns dict, severity as key and # warnings of that severity as value.""" - # pylint:disable=g-complex-comprehension - return { - s.value: sum(warnings[p][s.value] for p in project_names) - for s in Severity.levels - } - - -def emit_table_header(total_by_severity): - """Returns list of HTML-formatted content for severity stats.""" - - stats_header = ['Project'] - for s in Severity.levels: - if total_by_severity[s.value]: - stats_header.append( - '<span style=\'background-color:{}\'>{}</span>'.format( - s.color, s.column_header)) - stats_header.append('TOTAL') - return stats_header - - -def emit_row_counts_per_project(warnings, total_by_project, total_by_severity, - project_names): - """Returns total project warnings and row of stats for each project. - - Args: - warnings: output of create_warnings(warn_patterns, project_names) - total_by_project: output of get_total_by_project(project_names) - total_by_severity: output of get_total_by_severity(project_names) - project_names: list of project names - - Returns: - total_all_projects, the total number of warnings over all projects - stats_rows, a 2d list where each row is [Project Name, <severity counts>, - total # warnings for this project] - """ - - total_all_projects = 0 - stats_rows = [] - for p in project_names: - if total_by_project[p]: - one_row = [p] - for s in Severity.levels: - if total_by_severity[s.value]: - one_row.append(warnings[p][s.value]) - one_row.append(total_by_project[p]) - stats_rows.append(one_row) - total_all_projects += total_by_project[p] - return total_all_projects, stats_rows - - -def emit_row_counts_per_severity(total_by_severity, stats_header, stats_rows, - total_all_projects, writer): - """Emits stats_header and stats_rows as specified above. - - Args: - total_by_severity: output of get_total_by_severity() - stats_header: output of emit_table_header() - stats_rows: output of emit_row_counts_per_project() - total_all_projects: output of emit_row_counts_per_project() - writer: writer returned by make_writer(output_stream) - """ - - total_all_severities = 0 - one_row = ['<b>TOTAL</b>'] - for s in Severity.levels: - if total_by_severity[s.value]: - one_row.append(total_by_severity[s.value]) - total_all_severities += total_by_severity[s.value] - one_row.append(total_all_projects) - stats_rows.append(one_row) - writer('<script>') - emit_const_string_array('StatsHeader', stats_header, writer) - emit_const_object_array('StatsRows', stats_rows, writer) - writer(draw_table_javascript) - writer('</script>') - - -def emit_stats_by_project(writer, warn_patterns, project_names): - """Dump a google chart table of warnings per project and severity.""" - - warnings = create_warnings(warn_patterns, project_names) - total_by_project = get_total_by_project(warnings, project_names) - total_by_severity = get_total_by_severity(warnings, project_names) - stats_header = emit_table_header(total_by_severity) - total_all_projects, stats_rows = \ - emit_row_counts_per_project(warnings, total_by_project, total_by_severity, project_names) - emit_row_counts_per_severity(total_by_severity, stats_header, stats_rows, - total_all_projects, writer) - - -def dump_stats(writer, warn_patterns): - """Dump some stats about total number of warnings and such.""" - - known = 0 - skipped = 0 - unknown = 0 - sort_warnings(warn_patterns) - for i in warn_patterns: - if i['severity'] == Severity.UNMATCHED: - unknown += len(i['members']) - elif i['severity'] == Severity.SKIP: - skipped += len(i['members']) - else: - known += len(i['members']) - writer('Number of classified warnings: <b>' + str(known) + '</b><br>') - writer('Number of skipped warnings: <b>' + str(skipped) + '</b><br>') - writer('Number of unclassified warnings: <b>' + str(unknown) + '</b><br>') - total = unknown + known + skipped - extra_msg = '' - if total < 1000: - extra_msg = ' (low count may indicate incremental build)' - writer('Total number of warnings: <b>' + str(total) + '</b>' + extra_msg) - - -# New base table of warnings, [severity, warn_id, project, warning_message] -# Need buttons to show warnings in different grouping options. -# (1) Current, group by severity, id for each warning pattern -# sort by severity, warn_id, warning_message -# (2) Current --byproject, group by severity, -# id for each warning pattern + project name -# sort by severity, warn_id, project, warning_message -# (3) New, group by project + severity, -# id for each warning pattern -# sort by project, severity, warn_id, warning_message -def emit_buttons(writer): - writer('<button class="button" onclick="expandCollapse(1);">' - 'Expand all warnings</button>\n' - '<button class="button" onclick="expandCollapse(0);">' - 'Collapse all warnings</button>\n' - '<button class="button" onclick="groupBySeverity();">' - 'Group warnings by severity</button>\n' - '<button class="button" onclick="groupByProject();">' - 'Group warnings by project</button><br>') - - -def all_patterns(category): - patterns = '' - for i in category['patterns']: - patterns += i - patterns += ' / ' - return patterns - - -def dump_fixed(writer, warn_patterns): - """Show which warnings no longer occur.""" - anchor = 'fixed_warnings' - mark = anchor + '_mark' - writer('\n<br><p style="background-color:lightblue"><b>' - '<button id="' + mark + '" ' - 'class="bt" onclick="expand(\'' + anchor + '\');">' - '⊕</button> Fixed warnings. ' - 'No more occurrences. Please consider turning these into ' - 'errors if possible, before they are reintroduced in to the build' - ':</b></p>') - writer('<blockquote>') - fixed_patterns = [] - for i in warn_patterns: - if not i['members']: - fixed_patterns.append(i['description'] + ' (' + all_patterns(i) + ')') - fixed_patterns = sorted(fixed_patterns) - writer('<div id="' + anchor + '" style="display:none;"><table>') - cur_row_class = 0 - for text in fixed_patterns: - cur_row_class = 1 - cur_row_class - # remove last '\n' - t = text[:-1] if text[-1] == '\n' else text - writer('<tr><td class="c' + str(cur_row_class) + '">' + t + '</td></tr>') - writer('</table></div>') - writer('</blockquote>') - - -def write_severity(csvwriter, sev, kind, warn_patterns): - """Count warnings of given severity and write CSV entries to writer.""" - total = 0 - for pattern in warn_patterns: - if pattern['severity'] == sev and pattern['members']: - n = len(pattern['members']) - total += n - warning = kind + ': ' + (pattern['description'] or '?') - csvwriter.writerow([n, '', warning]) - # print number of warnings for each project, ordered by project name - projects = sorted(pattern['projects'].keys()) - for project in projects: - csvwriter.writerow([pattern['projects'][project], project, warning]) - csvwriter.writerow([total, '', kind + ' warnings']) - return total - - -def dump_csv(csvwriter, warn_patterns): - """Dump number of warnings in CSV format to writer.""" - sort_warnings(warn_patterns) - total = 0 - for s in Severity.levels: - total += write_severity(csvwriter, s, s.column_header, warn_patterns) - csvwriter.writerow([total, '', 'All warnings']) - - def find_project_index(line, project_patterns): for i, p in enumerate(project_patterns): if p.match(line): @@ -766,297 +429,6 @@ def parse_input_file(infile, flags): flags.platform) -# Return s with escaped backslash and quotation characters. -def escape_string(s): - return s.replace('\\', '\\\\').replace('"', '\\"') - - -# Return s without trailing '\n' and escape the quotation characters. -def strip_escape_string(s): - if not s: - return s - s = s[:-1] if s[-1] == '\n' else s - return escape_string(s) - - -def emit_warning_array(name, writer, warn_patterns): - writer('var warning_{} = ['.format(name)) - for w in warn_patterns: - if name == 'severity': - writer('{},'.format(w[name].value)) - else: - writer('{},'.format(w[name])) - writer('];') - - -def emit_warning_arrays(writer, warn_patterns): - emit_warning_array('severity', writer, warn_patterns) - writer('var warning_description = [') - for w in warn_patterns: - if w['members']: - writer('"{}",'.format(escape_string(w['description']))) - else: - writer('"",') # no such warning - writer('];') - - -scripts_for_warning_groups = """ - function compareMessages(x1, x2) { // of the same warning type - return (WarningMessages[x1[2]] <= WarningMessages[x2[2]]) ? -1 : 1; - } - function byMessageCount(x1, x2) { - return x2[2] - x1[2]; // reversed order - } - function bySeverityMessageCount(x1, x2) { - // orer by severity first - if (x1[1] != x2[1]) - return x1[1] - x2[1]; - return byMessageCount(x1, x2); - } - const ParseLinePattern = /^([^ :]+):(\\d+):(.+)/; - function addURL(line) { // used by Android - if (FlagURL == "") return line; - if (FlagSeparator == "") { - return line.replace(ParseLinePattern, - "<a target='_blank' href='" + FlagURL + "/$1'>$1</a>:$2:$3"); - } - return line.replace(ParseLinePattern, - "<a target='_blank' href='" + FlagURL + "/$1" + FlagSeparator + - "$2'>$1:$2</a>:$3"); - } - function addURLToLine(line, link) { // used by Chrome - let line_split = line.split(":"); - let path = line_split.slice(0,3).join(":"); - let msg = line_split.slice(3).join(":"); - let html_link = `<a target="_blank" href="${link}">${path}</a>${msg}`; - return html_link; - } - function createArrayOfDictionaries(n) { - var result = []; - for (var i=0; i<n; i++) result.push({}); - return result; - } - function groupWarningsBySeverity() { - // groups is an array of dictionaries, - // each dictionary maps from warning type to array of warning messages. - var groups = createArrayOfDictionaries(SeverityColors.length); - for (var i=0; i<Warnings.length; i++) { - var w = Warnings[i][0]; - var s = WarnPatternsSeverity[w]; - var k = w.toString(); - if (!(k in groups[s])) - groups[s][k] = []; - groups[s][k].push(Warnings[i]); - } - return groups; - } - function groupWarningsByProject() { - var groups = createArrayOfDictionaries(ProjectNames.length); - for (var i=0; i<Warnings.length; i++) { - var w = Warnings[i][0]; - var p = Warnings[i][1]; - var k = w.toString(); - if (!(k in groups[p])) - groups[p][k] = []; - groups[p][k].push(Warnings[i]); - } - return groups; - } - var GlobalAnchor = 0; - function createWarningSection(header, color, group) { - var result = ""; - var groupKeys = []; - var totalMessages = 0; - for (var k in group) { - totalMessages += group[k].length; - groupKeys.push([k, WarnPatternsSeverity[parseInt(k)], group[k].length]); - } - groupKeys.sort(bySeverityMessageCount); - for (var idx=0; idx<groupKeys.length; idx++) { - var k = groupKeys[idx][0]; - var messages = group[k]; - var w = parseInt(k); - var wcolor = SeverityColors[WarnPatternsSeverity[w]]; - var description = WarnPatternsDescription[w]; - if (description.length == 0) - description = "???"; - GlobalAnchor += 1; - result += "<table class='t1'><tr bgcolor='" + wcolor + "'><td>" + - "<button class='bt' id='" + GlobalAnchor + "_mark" + - "' onclick='expand(\\"" + GlobalAnchor + "\\");'>" + - "⊕</button> " + - description + " (" + messages.length + ")</td></tr></table>"; - result += "<div id='" + GlobalAnchor + - "' style='display:none;'><table class='t1'>"; - var c = 0; - messages.sort(compareMessages); - if (FlagPlatform == "chrome") { - for (var i=0; i<messages.length; i++) { - result += "<tr><td class='c" + c + "'>" + - addURLToLine(WarningMessages[messages[i][2]], WarningLinks[messages[i][3]]) + "</td></tr>"; - c = 1 - c; - } - } else { - for (var i=0; i<messages.length; i++) { - result += "<tr><td class='c" + c + "'>" + - addURL(WarningMessages[messages[i][2]]) + "</td></tr>"; - c = 1 - c; - } - } - result += "</table></div>"; - } - if (result.length > 0) { - return "<br><span style='background-color:" + color + "'><b>" + - header + ": " + totalMessages + - "</b></span><blockquote><table class='t1'>" + - result + "</table></blockquote>"; - - } - return ""; // empty section - } - function generateSectionsBySeverity() { - var result = ""; - var groups = groupWarningsBySeverity(); - for (s=0; s<SeverityColors.length; s++) { - result += createWarningSection(SeverityHeaders[s], SeverityColors[s], - groups[s]); - } - return result; - } - function generateSectionsByProject() { - var result = ""; - var groups = groupWarningsByProject(); - for (i=0; i<groups.length; i++) { - result += createWarningSection(ProjectNames[i], 'lightgrey', groups[i]); - } - return result; - } - function groupWarnings(generator) { - GlobalAnchor = 0; - var e = document.getElementById("warning_groups"); - e.innerHTML = generator(); - } - function groupBySeverity() { - groupWarnings(generateSectionsBySeverity); - } - function groupByProject() { - groupWarnings(generateSectionsByProject); - } -""" - - -# Emit a JavaScript const string -def emit_const_string(name, value, writer): - writer('const ' + name + ' = "' + escape_string(value) + '";') - - -# Emit a JavaScript const integer array. -def emit_const_int_array(name, array, writer): - writer('const ' + name + ' = [') - for n in array: - writer(str(n) + ',') - writer('];') - - -# Emit a JavaScript const string array. -def emit_const_string_array(name, array, writer): - writer('const ' + name + ' = [') - for s in array: - writer('"' + strip_escape_string(s) + '",') - writer('];') - - -# Emit a JavaScript const string array for HTML. -def emit_const_html_string_array(name, array, writer): - writer('const ' + name + ' = [') - for s in array: - # Not using html.escape yet, to work for both python 2 and 3, - # until all users switch to python 3. - # pylint:disable=deprecated-method - writer('"' + cgi.escape(strip_escape_string(s)) + '",') - writer('];') - - -# Emit a JavaScript const object array. -def emit_const_object_array(name, array, writer): - writer('const ' + name + ' = [') - for x in array: - writer(str(x) + ',') - writer('];') - - -def emit_js_data(writer, flags, warning_messages, warning_links, - warning_records, warn_patterns, project_names): - """Dump dynamic HTML page's static JavaScript data.""" - emit_const_string('FlagPlatform', flags.platform, writer) - emit_const_string('FlagURL', flags.url, writer) - emit_const_string('FlagSeparator', flags.separator, writer) - emit_const_string_array('SeverityColors', [s.color for s in Severity.levels], - writer) - emit_const_string_array('SeverityHeaders', - [s.header for s in Severity.levels], writer) - emit_const_string_array('SeverityColumnHeaders', - [s.column_header for s in Severity.levels], writer) - emit_const_string_array('ProjectNames', project_names, writer) - # pytype: disable=attribute-error - emit_const_int_array('WarnPatternsSeverity', - [w['severity'].value for w in warn_patterns], writer) - # pytype: enable=attribute-error - emit_const_html_string_array('WarnPatternsDescription', - [w['description'] for w in warn_patterns], - writer) - emit_const_html_string_array('WarningMessages', warning_messages, writer) - emit_const_object_array('Warnings', warning_records, writer) - if flags.platform == 'chrome': - emit_const_html_string_array('WarningLinks', warning_links, writer) - - -draw_table_javascript = """ -google.charts.load('current', {'packages':['table']}); -google.charts.setOnLoadCallback(drawTable); -function drawTable() { - var data = new google.visualization.DataTable(); - data.addColumn('string', StatsHeader[0]); - for (var i=1; i<StatsHeader.length; i++) { - data.addColumn('number', StatsHeader[i]); - } - data.addRows(StatsRows); - for (var i=0; i<StatsRows.length; i++) { - for (var j=0; j<StatsHeader.length; j++) { - data.setProperty(i, j, 'style', 'border:1px solid black;'); - } - } - var table = new google.visualization.Table( - document.getElementById('stats_table')); - table.draw(data, {allowHtml: true, alternatingRowStyle: true}); -} -""" - - -def dump_html(flags, output_stream, warning_messages, warning_links, - warning_records, header_str, warn_patterns, project_names): - """Dump the flags output to output_stream.""" - writer = make_writer(output_stream) - dump_html_prologue('Warnings for ' + header_str, writer, warn_patterns, - project_names) - dump_stats(writer, warn_patterns) - writer('<br><div id="stats_table"></div><br>') - writer('\n<script>') - emit_js_data(writer, flags, warning_messages, warning_links, warning_records, - warn_patterns, project_names) - writer(scripts_for_warning_groups) - writer('</script>') - emit_buttons(writer) - # Warning messages are grouped by severities or project names. - writer('<br><div id="warning_groups"></div>') - if flags.byproject: - writer('<script>groupByProject();</script>') - else: - writer('<script>groupBySeverity();</script>') - dump_fixed(writer, warn_patterns) - dump_html_epilogue(writer) - - def parse_compiler_output(compiler_output): """Parse compiler output for relevant info.""" split_output = compiler_output.split(':', 3) # 3 = max splits @@ -1153,29 +525,6 @@ def parallel_classify_warnings(warning_data, args, project_names, return warning_messages, warning_links, warning_records -def write_html(flags, project_names, warn_patterns, html_path, warning_messages, - warning_links, warning_records, header_str): - """Write warnings html file.""" - if html_path: - with open(html_path, 'w') as f: - dump_html(flags, f, warning_messages, warning_links, warning_records, - header_str, warn_patterns, project_names) - - -def write_out_csv(flags, warn_patterns, warning_messages, warning_links, - warning_records, header_str, project_names): - """Write warnings csv file.""" - if flags.csvpath: - with open(flags.csvpath, 'w') as f: - dump_csv(csv.writer(f, lineterminator='\n'), warn_patterns) - - if flags.gencsv: - dump_csv(csv.writer(sys.stdout, lineterminator='\n'), warn_patterns) - else: - dump_html(flags, sys.stdout, warning_messages, warning_links, - warning_records, header_str, warn_patterns, project_names) - - def process_log(logfile, flags, project_names, project_patterns, warn_patterns, html_path, use_google3, create_launch_subprocs_fn, classify_warnings_fn, logfile_object): @@ -1200,9 +549,9 @@ def process_log(logfile, flags, project_names, project_patterns, warn_patterns, warn_patterns, use_google3, create_launch_subprocs_fn, classify_warnings_fn) - write_html(flags, project_names, warn_patterns, html_path, - warning_messages, warning_links, warning_records, - header_str) + html_writer.write_html(flags, project_names, warn_patterns, html_path, + warning_messages, warning_links, warning_records, + header_str) return warning_messages, warning_links, warning_records, header_str @@ -1226,8 +575,9 @@ def common_main(use_google3, create_launch_subprocs_fn, classify_warnings_fn, classify_warnings_fn=classify_warnings_fn, logfile_object=logfile_object) - write_out_csv(flags, warn_patterns, warning_messages, warning_links, - warning_records, header_str, project_names) + html_writer.write_out_csv(flags, warn_patterns, warning_messages, + warning_links, warning_records, header_str, + project_names) # Return these values, so that caller can use them, if desired. return flags, warning_messages, warning_records, warn_patterns |