Call a writer to dump html; new warning patterns.

* HTML emit functions now take a writer parameter.
  This makes warn_common.py one step closer to the ChromeOS version.
* Add new found warning patterns from java and yacc.

Test: warn.py --url=http://cs/android --separator='?l=' build.log > warnings.html
Test: warn.py --gencsv build.log > warnings.csv
Change-Id: I5c446ca767746598f07603591fdf98f7d82cae17
This commit is contained in:
Chih-Hung Hsieh
2020-01-13 14:05:17 -08:00
parent 997661002a
commit 5392cdbeb1
3 changed files with 132 additions and 102 deletions

View File

@@ -93,6 +93,8 @@ warn_patterns = [
[r".*: warning: \[MultipleTopLevelClasses\] .+"]), [r".*: warning: \[MultipleTopLevelClasses\] .+"]),
java_low('Avoid having multiple unary operators acting on the same variable in a method call', java_low('Avoid having multiple unary operators acting on the same variable in a method call',
[r".*: warning: \[MultipleUnaryOperatorsInMethodCall\] .+"]), [r".*: warning: \[MultipleUnaryOperatorsInMethodCall\] .+"]),
java_low('OnNameExpected naming style',
[r".*\.java:.*: warning: .+ \[OnNameExpected\]$"]),
java_low('Package names should match the directory they are declared in', java_low('Package names should match the directory they are declared in',
[r".*: warning: \[PackageLocation\] .+"]), [r".*: warning: \[PackageLocation\] .+"]),
java_low('Non-standard parameter comment; prefer `/* paramName= */ arg`', java_low('Non-standard parameter comment; prefer `/* paramName= */ arg`',

View File

@@ -46,6 +46,10 @@ def kotlin(description, pattern_list):
return warn('Kotlin', Severity.MEDIUM, description, pattern_list) return warn('Kotlin', Severity.MEDIUM, description, pattern_list)
def yacc(description, pattern_list):
return warn('yacc', Severity.MEDIUM, description, pattern_list)
warn_patterns = [ warn_patterns = [
# pylint:disable=line-too-long,g-inconsistent-quotes # pylint:disable=line-too-long,g-inconsistent-quotes
# aapt warnings # aapt warnings
@@ -115,6 +119,14 @@ warn_patterns = [
kotlin('library has Kotlin runtime', kotlin('library has Kotlin runtime',
[r".*: warning: library has Kotlin runtime bundled into it", [r".*: warning: library has Kotlin runtime bundled into it",
r".*: warning: some JAR files .* have the Kotlin Runtime library"]), r".*: warning: some JAR files .* have the Kotlin Runtime library"]),
# Yacc warnings
yacc('deprecate directive',
[r".*\.yy?:.*: warning: deprecated directive: "]),
yacc('shift/reduce conflicts',
[r".*\.yy?: warning: .+ shift/reduce conflicts "]),
{'category': 'yacc', 'severity': Severity.SKIP,
'description': 'yacc: fix-its can be applied',
'patterns': [r".*\.yy?: warning: fix-its can be applied."]},
# Rust warnings # Rust warnings
{'category': 'Rust', 'severity': Severity.HIGH, {'category': 'Rust', 'severity': Severity.HIGH,
'description': 'Rust: Does not derive Copy', 'description': 'Rust: Does not derive Copy',

View File

@@ -199,22 +199,30 @@ html_head_scripts = """\
""" """
def make_writer(output_stream):
def writer(text):
return output_stream.write(text + '\n')
return writer
def html_big(param): def html_big(param):
return '<font size="+2">' + param + '</font>' return '<font size="+2">' + param + '</font>'
def dump_html_prologue(title): def dump_html_prologue(title, writer):
print('<html>\n<head>') writer('<html>\n<head>')
print('<title>' + title + '</title>') writer('<title>' + title + '</title>')
print(html_head_scripts) writer(html_head_scripts)
emit_stats_by_project() emit_stats_by_project(writer)
print('</head>\n<body>') writer('</head>\n<body>')
print(html_big(title)) writer(html_big(title))
print('<p>') writer('<p>')
def dump_html_epilogue(): def dump_html_epilogue(writer):
print('</body>\n</head>\n</html>') writer('</body>\n</head>\n</html>')
def sort_warnings(): def sort_warnings():
@@ -222,7 +230,7 @@ def sort_warnings():
i['members'] = sorted(set(i['members'])) i['members'] = sorted(set(i['members']))
def emit_stats_by_project(): def emit_stats_by_project(writer):
"""Dump a google chart table of warnings per project and severity.""" """Dump a google chart table of warnings per project and severity."""
# warnings[p][s] is number of warnings in project p of severity s. # warnings[p][s] is number of warnings in project p of severity s.
# pylint:disable=g-complex-comprehension # pylint:disable=g-complex-comprehension
@@ -277,14 +285,14 @@ def emit_stats_by_project():
total_all_severities += total_by_severity[s.value] total_all_severities += total_by_severity[s.value]
one_row.append(total_all_projects) one_row.append(total_all_projects)
stats_rows.append(one_row) stats_rows.append(one_row)
print('<script>') writer('<script>')
emit_const_string_array('StatsHeader', stats_header) emit_const_string_array('StatsHeader', stats_header, writer)
emit_const_object_array('StatsRows', stats_rows) emit_const_object_array('StatsRows', stats_rows, writer)
print(draw_table_javascript) writer(draw_table_javascript)
print('</script>') writer('</script>')
def dump_stats(): def dump_stats(writer):
"""Dump some stats about total number of warnings and such.""" """Dump some stats about total number of warnings and such."""
known = 0 known = 0
skipped = 0 skipped = 0
@@ -297,14 +305,14 @@ def dump_stats():
skipped += len(i['members']) skipped += len(i['members'])
else: else:
known += len(i['members']) known += len(i['members'])
print('Number of classified warnings: <b>' + str(known) + '</b><br>') writer('Number of classified warnings: <b>' + str(known) + '</b><br>')
print('Number of skipped warnings: <b>' + str(skipped) + '</b><br>') writer('Number of skipped warnings: <b>' + str(skipped) + '</b><br>')
print('Number of unclassified warnings: <b>' + str(unknown) + '</b><br>') writer('Number of unclassified warnings: <b>' + str(unknown) + '</b><br>')
total = unknown + known + skipped total = unknown + known + skipped
extra_msg = '' extra_msg = ''
if total < 1000: if total < 1000:
extra_msg = ' (low count may indicate incremental build)' extra_msg = ' (low count may indicate incremental build)'
print('Total number of warnings: <b>' + str(total) + '</b>' + extra_msg) writer('Total number of warnings: <b>' + str(total) + '</b>' + extra_msg)
# New base table of warnings, [severity, warn_id, project, warning_message] # New base table of warnings, [severity, warn_id, project, warning_message]
@@ -317,8 +325,8 @@ def dump_stats():
# (3) New, group by project + severity, # (3) New, group by project + severity,
# id for each warning pattern # id for each warning pattern
# sort by project, severity, warn_id, warning_message # sort by project, severity, warn_id, warning_message
def emit_buttons(): def emit_buttons(writer):
print('<button class="button" onclick="expandCollapse(1);">' writer('<button class="button" onclick="expandCollapse(1);">'
'Expand all warnings</button>\n' 'Expand all warnings</button>\n'
'<button class="button" onclick="expandCollapse(0);">' '<button class="button" onclick="expandCollapse(0);">'
'Collapse all warnings</button>\n' 'Collapse all warnings</button>\n'
@@ -336,33 +344,32 @@ def all_patterns(category):
return patterns return patterns
def dump_fixed(): def dump_fixed(writer):
"""Show which warnings no longer occur.""" """Show which warnings no longer occur."""
anchor = 'fixed_warnings' anchor = 'fixed_warnings'
mark = anchor + '_mark' mark = anchor + '_mark'
print('\n<br><p style="background-color:lightblue"><b>' writer('\n<br><p style="background-color:lightblue"><b>'
'<button id="' + mark + '" ' '<button id="' + mark + '" '
'class="bt" onclick="expand(\'' + anchor + '\');">' 'class="bt" onclick="expand(\'' + anchor + '\');">'
'&#x2295</button> Fixed warnings. ' '&#x2295</button> Fixed warnings. '
'No more occurrences. Please consider turning these into ' 'No more occurrences. Please consider turning these into '
'errors if possible, before they are reintroduced in to the build' 'errors if possible, before they are reintroduced in to the build'
':</b></p>') ':</b></p>')
print('<blockquote>') writer('<blockquote>')
fixed_patterns = [] fixed_patterns = []
for i in warn_patterns: for i in warn_patterns:
if not i['members']: if not i['members']:
fixed_patterns.append(i['description'] + ' (' + fixed_patterns.append(i['description'] + ' (' + all_patterns(i) + ')')
all_patterns(i) + ')')
fixed_patterns = sorted(fixed_patterns) fixed_patterns = sorted(fixed_patterns)
print('<div id="' + anchor + '" style="display:none;"><table>') writer('<div id="' + anchor + '" style="display:none;"><table>')
cur_row_class = 0 cur_row_class = 0
for text in fixed_patterns: for text in fixed_patterns:
cur_row_class = 1 - cur_row_class cur_row_class = 1 - cur_row_class
# remove last '\n' # remove last '\n'
t = text[:-1] if text[-1] == '\n' else text t = text[:-1] if text[-1] == '\n' else text
print('<tr><td class="c' + str(cur_row_class) + '">' + t + '</td></tr>') writer('<tr><td class="c' + str(cur_row_class) + '">' + t + '</td></tr>')
print('</table></div>') writer('</table></div>')
print('</blockquote>') writer('</blockquote>')
def find_project_index(line): def find_project_index(line):
@@ -540,6 +547,7 @@ def parse_input_file(infile):
prev_warning = 'unknown_source_file: ' + prev_warning prev_warning = 'unknown_source_file: ' + prev_warning
warning_lines.add(normalize_warning_line(prev_warning)) warning_lines.add(normalize_warning_line(prev_warning))
prev_warning = '' prev_warning = ''
if warning_pattern.match(line): if warning_pattern.match(line):
if warning_without_file.match(line): if warning_without_file.match(line):
# save this line and combine it with the next line # save this line and combine it with the next line
@@ -547,6 +555,7 @@ def parse_input_file(infile):
else: else:
warning_lines.add(normalize_warning_line(line)) warning_lines.add(normalize_warning_line(line))
continue continue
if line_counter < 100: if line_counter < 100:
# save a little bit of time by only doing this for the first few lines # save a little bit of time by only doing this for the first few lines
line_counter += 1 line_counter += 1
@@ -580,22 +589,22 @@ def strip_escape_string(s):
return escape_string(s) return escape_string(s)
def emit_warning_array(name): def emit_warning_array(name, writer):
print('var warning_{} = ['.format(name)) writer('var warning_{} = ['.format(name))
for i in range(len(warn_patterns)): for i in range(len(warn_patterns)):
print('{},'.format(warn_patterns[i][name])) writer('{},'.format(warn_patterns[i][name]))
print('];') writer('];')
def emit_warning_arrays(): def emit_warning_arrays(writer):
emit_warning_array('severity') emit_warning_array('severity', writer)
print('var warning_description = [') writer('var warning_description = [')
for i in range(len(warn_patterns)): for i in range(len(warn_patterns)):
if warn_patterns[i]['members']: if warn_patterns[i]['members']:
print('"{}",'.format(escape_string(warn_patterns[i]['description']))) writer('"{}",'.format(escape_string(warn_patterns[i]['description'])))
else: else:
print('"",') # no such warning writer('"",') # no such warning
print('];') writer('];')
scripts_for_warning_groups = """ scripts_for_warning_groups = """
@@ -701,7 +710,8 @@ scripts_for_warning_groups = """
var result = ""; var result = "";
var groups = groupWarningsBySeverity(); var groups = groupWarningsBySeverity();
for (s=0; s<SeverityColors.length; s++) { for (s=0; s<SeverityColors.length; s++) {
result += createWarningSection(SeverityHeaders[s], SeverityColors[s], groups[s]); result += createWarningSection(SeverityHeaders[s], SeverityColors[s],
groups[s]);
} }
return result; return result;
} }
@@ -728,63 +738,67 @@ scripts_for_warning_groups = """
# Emit a JavaScript const string # Emit a JavaScript const string
def emit_const_string(name, value): def emit_const_string(name, value, writer):
print('const ' + name + ' = "' + escape_string(value) + '";') writer('const ' + name + ' = "' + escape_string(value) + '";')
# Emit a JavaScript const integer array. # Emit a JavaScript const integer array.
def emit_const_int_array(name, array): def emit_const_int_array(name, array, writer):
print('const ' + name + ' = [') writer('const ' + name + ' = [')
for n in array: for n in array:
print(str(n) + ',') writer(str(n) + ',')
print('];') writer('];')
# Emit a JavaScript const string array. # Emit a JavaScript const string array.
def emit_const_string_array(name, array): def emit_const_string_array(name, array, writer):
print('const ' + name + ' = [') writer('const ' + name + ' = [')
for s in array: for s in array:
print('"' + strip_escape_string(s) + '",') writer('"' + strip_escape_string(s) + '",')
print('];') writer('];')
# Emit a JavaScript const string array for HTML. # Emit a JavaScript const string array for HTML.
def emit_const_html_string_array(name, array): def emit_const_html_string_array(name, array, writer):
print('const ' + name + ' = [') writer('const ' + name + ' = [')
for s in array: for s in array:
# Not using html.escape yet, to work for both python 2 and 3, # Not using html.escape yet, to work for both python 2 and 3,
# until all users switch to python 3. # until all users switch to python 3.
# pylint:disable=deprecated-method # pylint:disable=deprecated-method
print('"' + cgi.escape(strip_escape_string(s)) + '",') writer('"' + cgi.escape(strip_escape_string(s)) + '",')
print('];') writer('];')
# Emit a JavaScript const object array. # Emit a JavaScript const object array.
def emit_const_object_array(name, array): def emit_const_object_array(name, array, writer):
print('const ' + name + ' = [') writer('const ' + name + ' = [')
for x in array: for x in array:
print(str(x) + ',') writer(str(x) + ',')
print('];') writer('];')
def emit_js_data(): def emit_js_data(writer):
"""Dump dynamic HTML page's static JavaScript data.""" """Dump dynamic HTML page's static JavaScript data."""
emit_const_string('FlagURL', args.url if args.url else '') emit_const_string('FlagURL',
emit_const_string('FlagSeparator', args.separator if args.separator else '') args.url if args.url else '', writer)
emit_const_string_array('SeverityColors', [s.color for s in Severity.levels]) emit_const_string('FlagSeparator',
args.separator if args.separator else '', writer)
emit_const_string_array('SeverityColors',
[s.color for s in Severity.levels], writer)
emit_const_string_array('SeverityHeaders', emit_const_string_array('SeverityHeaders',
[s.header for s in Severity.levels]) [s.header for s in Severity.levels], writer)
emit_const_string_array('SeverityColumnHeaders', emit_const_string_array('SeverityColumnHeaders',
[s.column_header for s in Severity.levels]) [s.column_header for s in Severity.levels], writer)
emit_const_string_array('ProjectNames', project_names) emit_const_string_array('ProjectNames', project_names, writer)
# pytype: disable=attribute-error # pytype: disable=attribute-error
emit_const_int_array('WarnPatternsSeverity', emit_const_int_array('WarnPatternsSeverity',
[w['severity'].value for w in warn_patterns]) [w['severity'].value for w in warn_patterns], writer)
# pytype: enable=attribute-error # pytype: enable=attribute-error
emit_const_html_string_array('WarnPatternsDescription', emit_const_html_string_array('WarnPatternsDescription',
[w['description'] for w in warn_patterns]) [w['description'] for w in warn_patterns],
emit_const_html_string_array('WarningMessages', warning_messages) writer)
emit_const_object_array('Warnings', warning_records) emit_const_html_string_array('WarningMessages', warning_messages, writer)
emit_const_object_array('Warnings', warning_records, writer)
draw_table_javascript = """ draw_table_javascript = """
google.charts.load('current', {'packages':['table']}); google.charts.load('current', {'packages':['table']});
@@ -801,31 +815,33 @@ function drawTable() {
data.setProperty(i, j, 'style', 'border:1px solid black;'); data.setProperty(i, j, 'style', 'border:1px solid black;');
} }
} }
var table = new google.visualization.Table(document.getElementById('stats_table')); var table = new google.visualization.Table(
document.getElementById('stats_table'));
table.draw(data, {allowHtml: true, alternatingRowStyle: true}); table.draw(data, {allowHtml: true, alternatingRowStyle: true});
} }
""" """
def dump_html(): def dump_html(output_stream):
"""Dump the html output to stdout.""" """Dump the html output to output_stream."""
writer = make_writer(output_stream)
dump_html_prologue('Warnings for ' + platform_version + ' - ' + dump_html_prologue('Warnings for ' + platform_version + ' - ' +
target_product + ' - ' + target_variant) target_product + ' - ' + target_variant, writer)
dump_stats() dump_stats(writer)
print('<br><div id="stats_table"></div><br>') writer('<br><div id="stats_table"></div><br>')
print('\n<script>') writer('\n<script>')
emit_js_data() emit_js_data(writer)
print(scripts_for_warning_groups) writer(scripts_for_warning_groups)
print('</script>') writer('</script>')
emit_buttons() emit_buttons(writer)
# Warning messages are grouped by severities or project names. # Warning messages are grouped by severities or project names.
print('<br><div id="warning_groups"></div>') writer('<br><div id="warning_groups"></div>')
if args.byproject: if args.byproject:
print('<script>groupByProject();</script>') writer('<script>groupByProject();</script>')
else: else:
print('<script>groupBySeverity();</script>') writer('<script>groupBySeverity();</script>')
dump_fixed() dump_fixed(writer)
dump_html_epilogue() dump_html_epilogue(writer)
##### Functions to count warnings and dump csv file. ######################### ##### Functions to count warnings and dump csv file. #########################
@@ -884,4 +900,4 @@ def common_main(parallel_process):
if args.gencsv: if args.gencsv:
dump_csv(csv.writer(sys.stdout, lineterminator='\n')) dump_csv(csv.writer(sys.stdout, lineterminator='\n'))
else: else:
dump_html() dump_html(sys.stdout)