diff options
| -rwxr-xr-x | tools/warn.py | 153 |
1 files changed, 81 insertions, 72 deletions
diff --git a/tools/warn.py b/tools/warn.py index c710164ff3..9389b7de48 100755 --- a/tools/warn.py +++ b/tools/warn.py @@ -1,5 +1,5 @@ #!/usr/bin/python -# This file uses the following encoding: utf-8 +# Prefer python3 but work also with python2. """Grep warnings messages and output HTML tables or warning counts in CSV. @@ -74,9 +74,11 @@ Use option --gencsv to output warning counts in CSV format. # 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 import re @@ -540,7 +542,7 @@ warn_patterns = [ {'category': 'java', 'severity': Severity.LOW, 'description': - 'Java: Use Java\'s utility functional interfaces instead of Function\u003cA, B> for primitive types.', + u'Java: Use Java\'s utility functional interfaces instead of Function\u003cA, B> for primitive types.', 'patterns': [r".*: warning: \[LambdaFunctionalInterface\] .+"]}, {'category': 'java', 'severity': Severity.LOW, @@ -1270,7 +1272,7 @@ warn_patterns = [ {'category': 'java', 'severity': Severity.MEDIUM, 'description': - 'Java: Prefer the short-circuiting boolean operators \u0026\u0026 and || to \u0026 and |.', + u'Java: Prefer the short-circuiting boolean operators \u0026\u0026 and || to \u0026 and |.', 'patterns': [r".*: warning: \[ShortCircuitBoolean\] .+"]}, {'category': 'java', 'severity': Severity.MEDIUM, @@ -1535,7 +1537,7 @@ warn_patterns = [ {'category': 'java', 'severity': Severity.HIGH, 'description': - 'Java: Implementing \'Comparable\u003cT>\' where T is not compatible with the implementing class.', + u'Java: Implementing \'Comparable\u003cT>\' where T is not compatible with the implementing class.', 'patterns': [r".*: warning: \[ComparableType\] .+"]}, {'category': 'java', 'severity': Severity.HIGH, @@ -1790,7 +1792,7 @@ warn_patterns = [ {'category': 'java', 'severity': Severity.HIGH, 'description': - 'Java: Path implements Iterable\u003cPath>; prefer Collection\u003cPath> for clarity', + u'Java: Path implements Iterable\u003cPath>; prefer Collection\u003cPath> for clarity', 'patterns': [r".*: warning: \[IterablePathParameter\] .+"]}, {'category': 'java', 'severity': Severity.HIGH, @@ -2922,17 +2924,17 @@ def html_big(param): def dump_html_prologue(title): - print '<html>\n<head>' - print '<title>' + title + '</title>' - print html_head_scripts + print('<html>\n<head>') + print('<title>' + title + '</title>') + print(html_head_scripts) emit_stats_by_project() - print '</head>\n<body>' - print html_big(title) - print '<p>' + print('</head>\n<body>') + print(html_big(title)) + print('<p>') def dump_html_epilogue(): - print '</body>\n</head>\n</html>' + print('</body>\n</head>\n</html>') def sort_warnings(): @@ -2943,6 +2945,7 @@ def sort_warnings(): def emit_stats_by_project(): """Dump a google chart table of warnings per project and severity.""" # warnings[p][s] is number of warnings in project p of severity s. + # pylint:disable=g-complex-comprehension warnings = {p: {s: 0 for s in Severity.range} for p in project_names} for i in warn_patterns: s = i['severity'] @@ -2988,11 +2991,11 @@ def emit_stats_by_project(): total_all_severities += total_by_severity[s] one_row.append(total_all_projects) stats_rows.append(one_row) - print '<script>' + print('<script>') emit_const_string_array('StatsHeader', stats_header) emit_const_object_array('StatsRows', stats_rows) - print draw_table_javascript - print '</script>' + print(draw_table_javascript) + print('</script>') def dump_stats(): @@ -3008,14 +3011,14 @@ def dump_stats(): skipped += len(i['members']) else: known += len(i['members']) - print 'Number of classified warnings: <b>' + str(known) + '</b><br>' - print 'Number of skipped warnings: <b>' + str(skipped) + '</b><br>' - print 'Number of unclassified warnings: <b>' + str(unknown) + '</b><br>' + print('Number of classified warnings: <b>' + str(known) + '</b><br>') + print('Number of skipped warnings: <b>' + str(skipped) + '</b><br>') + print('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)' - print 'Total number of warnings: <b>' + str(total) + '</b>' + extra_msg + print('Total number of warnings: <b>' + str(total) + '</b>' + extra_msg) # New base table of warnings, [severity, warn_id, project, warning_message] @@ -3029,14 +3032,14 @@ def dump_stats(): # id for each warning pattern # sort by project, severity, warn_id, warning_message def emit_buttons(): - print ('<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>') + print('<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): @@ -3051,14 +3054,14 @@ def dump_fixed(): """Show which warnings no longer occur.""" anchor = 'fixed_warnings' mark = anchor + '_mark' - print ('\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>') - print '<blockquote>' + print('\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>') + print('<blockquote>') fixed_patterns = [] for i in warn_patterns: if not i['members']: @@ -3066,16 +3069,16 @@ def dump_fixed(): all_patterns(i) + ')') if i['option']: fixed_patterns.append(' ' + i['option']) - fixed_patterns.sort() - print '<div id="' + anchor + '" style="display:none;"><table>' + fixed_patterns = sorted(fixed_patterns) + print('<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 - print '<tr><td class="c' + str(cur_row_class) + '">' + t + '</td></tr>' - print '</table></div>' - print '</blockquote>' + print('<tr><td class="c' + str(cur_row_class) + '">' + t + '</td></tr>') + print('</table></div>') + print('</blockquote>') def find_project_index(line): @@ -3187,8 +3190,9 @@ def normalize_path(path): def normalize_warning_line(line): """Normalize file path relative to android_root in a warning line.""" # replace fancy quotes with plain ol' quotes - line = line.replace('‘', "'") - line = line.replace('’', "'") + line = re.sub(u'[\u2018\u2019]', '\'', line) + # replace non-ASCII chars to spaces + line = re.sub(u'[^\x00-\x7f]', ' ', line) line = line.strip() first_column = line.find(':') if first_column > 0: @@ -3246,21 +3250,22 @@ def strip_escape_string(s): def emit_warning_array(name): - print 'var warning_{} = ['.format(name) + print('var warning_{} = ['.format(name)) for i in range(len(warn_patterns)): - print '{},'.format(warn_patterns[i][name]) - print '];' + print('{},'.format(warn_patterns[i][name])) + print('];') def emit_warning_arrays(): emit_warning_array('severity') - print 'var warning_description = [' + print('var warning_description = [') for i in range(len(warn_patterns)): if warn_patterns[i]['members']: - print '"{}",'.format(escape_string(warn_patterns[i]['description'])) + print('"{}",'.format(escape_string(warn_patterns[i]['description']))) else: - print '"",' # no such warning - print '];' + print('"",') # no such warning + print('];') + scripts_for_warning_groups = """ function compareMessages(x1, x2) { // of the same warning type @@ -3393,39 +3398,42 @@ scripts_for_warning_groups = """ # Emit a JavaScript const string def emit_const_string(name, value): - print 'const ' + name + ' = "' + escape_string(value) + '";' + print('const ' + name + ' = "' + escape_string(value) + '";') # Emit a JavaScript const integer array. def emit_const_int_array(name, array): - print 'const ' + name + ' = [' + print('const ' + name + ' = [') for n in array: - print str(n) + ',' - print '];' + print(str(n) + ',') + print('];') # Emit a JavaScript const string array. def emit_const_string_array(name, array): - print 'const ' + name + ' = [' + print('const ' + name + ' = [') for s in array: - print '"' + strip_escape_string(s) + '",' - print '];' + print('"' + strip_escape_string(s) + '",') + print('];') # Emit a JavaScript const string array for HTML. def emit_const_html_string_array(name, array): - print 'const ' + name + ' = [' + print('const ' + name + ' = [') for s in array: - print '"' + cgi.escape(strip_escape_string(s)) + '",' - print '];' + # Not using html.escape yet, to work for both python 2 and 3, + # until all users switch to python 3. + # pylint:disable=deprecated-method + print('"' + cgi.escape(strip_escape_string(s)) + '",') + print('];') # Emit a JavaScript const object array. def emit_const_object_array(name, array): - print 'const ' + name + ' = [' + print('const ' + name + ' = [') for x in array: - print str(x) + ',' - print '];' + print(str(x) + ',') + print('];') def emit_js_data(): @@ -3471,18 +3479,18 @@ def dump_html(): dump_html_prologue('Warnings for ' + platform_version + ' - ' + target_product + ' - ' + target_variant) dump_stats() - print '<br><div id="stats_table"></div><br>' - print '\n<script>' + print('<br><div id="stats_table"></div><br>') + print('\n<script>') emit_js_data() - print scripts_for_warning_groups - print '</script>' + print(scripts_for_warning_groups) + print('</script>') emit_buttons() # Warning messages are grouped by severities or project names. - print '<br><div id="warning_groups"></div>' + print('<br><div id="warning_groups"></div>') if args.byproject: - print '<script>groupByProject();</script>' + print('<script>groupByProject();</script>') else: - print '<script>groupBySeverity();</script>' + print('<script>groupBySeverity();</script>') dump_fixed() dump_html_epilogue() @@ -3506,8 +3514,7 @@ def count_severity(writer, sev, kind): warning = kind + ': ' + description_for_csv(i) writer.writerow([n, '', warning]) # print number of warnings for each project, ordered by project name. - projects = i['projects'].keys() - projects.sort() + projects = sorted(i['projects'].keys()) for p in projects: writer.writerow([i['projects'][p], p, warning]) writer.writerow([total, '', kind + ' warnings']) @@ -3526,7 +3533,9 @@ def dump_csv(writer): def main(): - warning_lines = parse_input_file(open(args.buildlog, 'r')) + # We must use 'utf-8' codec to parse some non-ASCII code in warnings. + warning_lines = parse_input_file( + io.open(args.buildlog, mode='r', encoding='utf-8')) parallel_classify_warnings(warning_lines) # If a user pases a csv path, save the fileoutput to the path # If the user also passed gencsv write the output to stdout |