From: Chih-Hung Hsieh Date: Mon, 12 Sep 2016 23:20:49 +0000 (-0700) Subject: Emit a table of warnings by project and severity. X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=e41c99b721145247fcee8f06a0d3bf91f398708e;p=android-x86%2Fbuild.git Emit a table of warnings by project and severity. * Use 't1' CSS class for main warning tables, and default table style for new warning count table. * Count total skipped warnings. * Append count of warnings in each severity header. * Fix some skipped warning patterns and count/dump skipped warnings. * Add dumpskippedanchors function to fix expand_collapse error when --byproject flag is used and severity.SKIP is not emitted. * Minor coding style changes: * Loop through severity.kinds. * Remove unused import. * Add spaces around assignment operators. * Adjust indentation. Bug: 31377083 Test: run warn.py with build.log Change-Id: I68949edc4601b10ea2f7ac84e48e6c3da4ab1e4f --- diff --git a/tools/warn.py b/tools/warn.py index ce78d5d66..572ac68de 100755 --- a/tools/warn.py +++ b/tools/warn.py @@ -2,7 +2,6 @@ # This file uses the following encoding: utf-8 import argparse -import sys import re parser = argparse.ArgumentParser(description='Convert a build log into HTML') @@ -26,14 +25,15 @@ args = parser.parse_args() # if you add another level, don't forget to give it a color below class severity: - UNKNOWN=0 - SKIP=100 - FIXMENOW=1 - HIGH=2 - MEDIUM=3 - LOW=4 - TIDY=5 - HARMLESS=6 + UNKNOWN = 0 + FIXMENOW = 1 + HIGH = 2 + MEDIUM = 3 + LOW = 4 + TIDY = 5 + HARMLESS = 6 + SKIP = 100 + kinds = [FIXMENOW, HIGH, MEDIUM, LOW, TIDY, HARMLESS, UNKNOWN, SKIP] def colorforseverity(sev): if sev == severity.FIXMENOW: @@ -69,6 +69,23 @@ def headerforseverity(sev): return 'Unknown warnings' return 'Unhandled warnings' +def columnheaderforseverity(sev): + if sev == severity.FIXMENOW: + return 'FixNow' + if sev == severity.HIGH: + return 'High' + if sev == severity.MEDIUM: + return 'Medium' + if sev == severity.LOW: + return 'Low' + if sev == severity.HARMLESS: + return 'Harmless' + if sev == severity.TIDY: + return 'Tidy' + if sev == severity.UNKNOWN: + return 'Unknown' + return 'Unhandled' + warnpatterns = [ { 'category':'make', 'severity':severity.MEDIUM, 'members':[], 'option':'', 'description':'make: overriding commands/ignoring old commands', @@ -240,7 +257,8 @@ warnpatterns = [ 'patterns':[r".*: warning: control reaches end of non-void function"] }, { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'-Wimplicit-int', 'description':'Implicit int type', - 'patterns':[r".*: warning: type specifier missing, defaults to 'int'"] }, + 'patterns':[r".*: warning: type specifier missing, defaults to 'int'", + r".*: warning: type defaults to 'int' in declaration of '.+'"] }, { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'-Wmain-return-type', 'description':'Main function should return int', 'patterns':[r".*: warning: return type of 'main' is not 'int'"] }, @@ -297,9 +315,6 @@ warnpatterns = [ r".*: warning: Null pointer argument in"] }, { 'category':'cont.', 'severity':severity.SKIP, 'members':[], 'option':'', 'description':'', - 'patterns':[r".*: warning: type defaults to 'int' in declaration of '.+'"] }, - { 'category':'cont.', 'severity':severity.SKIP, 'members':[], 'option':'', - 'description':'', 'patterns':[r".*: warning: parameter names \(without types\) in function declaration"] }, { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'-Wstrict-aliasing', 'description':'Dereferencing <foo> breaks strict aliasing rules', @@ -1909,6 +1924,8 @@ projectpatterns = [] for p in projectlist: projectpatterns.append({'description':p[0], 'members':[], 'pattern':re.compile(p[1])}) +projectnames = [p[0] for p in projectlist] + # Each warning pattern has 3 dictionaries: # (1) 'projects' maps a project name to number of warnings in that project. # (2) 'projectanchor' maps a project name to its anchor number for HTML. @@ -1953,12 +1970,13 @@ html_script_style = """\ }; \n""" @@ -1990,45 +2008,114 @@ def sortwarnings(): for i in warnpatterns: i['members'] = sorted(set(i['members'])) +# dump a table of warnings per project and severity +def dumpstatsbyproject(): + projects = set(projectnames) + severities = set(severity.kinds) + + # warnings[p][s] is number of warnings in project p of severity s. + warnings = {p:{s:0 for s in severity.kinds} for p in projectnames} + for i in warnpatterns: + s = i['severity'] + for p in i['projects']: + warnings[p][s] += i['projects'][p] + + # totalbyseverity[s] is number of warnings of severity s. + totalbyseverity = {s:0 for s in severity.kinds} + + # emit table header + output('
\n\n') + for s in severity.kinds: + output(''. + format(colorforseverity(s), columnheaderforseverity(s))) + output('\n') + + # emit a row of warnings per project + totalallprojects = 0 + for p in projectnames: + totalbyproject = 0 + output(''.format(p)) + for s in severity.kinds: + output(''.format(warnings[p][s])) + totalbyproject += warnings[p][s] + totalbyseverity[s] += warnings[p][s] + output(''.format(totalbyproject)) + totalallprojects += totalbyproject + output('\n') + + # emit a row of warning counts per severity + totalallseverities = 0 + output('') + for s in severity.kinds: + output(''.format(totalbyseverity[s])) + totalallseverities += totalbyseverity[s] + output('\n'.format(totalallprojects)) + + # at the end of table, verify total counts + output('
{}TOTAL
{}{}{}
TOTAL{}{}

\n') + if totalallprojects != totalallseverities: + output('

ERROR: Sum of warnings by project ' + + '!= Sum of warnings by severity.

\n') + # dump some stats about total number of warnings and such def dumpstats(): known = 0 + skipped = 0 unknown = 0 sortwarnings() for i in warnpatterns: if i['severity'] == severity.UNKNOWN: unknown += len(i['members']) - elif i['severity'] != severity.SKIP: + elif i['severity'] == severity.SKIP: + skipped += len(i['members']) + else: known += len(i['members']) output('\nNumber of classified warnings: ' + str(known) + '
' ) + output('\nNumber of skipped warnings: ' + str(skipped) + '
') output('\nNumber of unclassified warnings: ' + str(unknown) + '
') - total = unknown + known + total = unknown + known + skipped output('\nTotal number of warnings: ' + str(total) + '') if total < 1000: output('(low count may indicate incremental build)') output('

\n') + +def emitbuttons(): output(' ' + + 'Expand all warnings\n' + '') - output('
\n') + 'Collapse all warnings
\n') # dump everything for a given severity def dumpseverity(sev): global anchor + total = 0 + for i in warnpatterns: + if i['severity'] == sev: + total = total + len(i['members']) output('\n
' + - headerforseverity(sev) + ':\n') + headerforseverity(sev) + ': ' + str(total) + '\n') output('
\n') for i in warnpatterns: - if i['severity'] == sev and len(i['members']) > 0: - anchor += 1 - i['anchor'] = str(anchor) - if args.byproject: - dumpcategorybyproject(sev, i) - else: - dumpcategory(sev, i) + if i['severity'] == sev and len(i['members']) > 0: + anchor += 1 + i['anchor'] = str(anchor) + if args.byproject: + dumpcategorybyproject(sev, i) + else: + dumpcategory(sev, i) output('
\n') +# emit all skipped project anchors for expand_collapse. +def dumpskippedanchors(): + output('
\n') # hide these fake elements + for i in warnpatterns: + if i['severity'] == severity.SKIP and len(i['members']) > 0: + projects = i['projectwarning'].keys() + for p in projects: + output('
' + + '
\n') + output('
\n') + def allpatterns(cat): pats = '' for i in cat['patterns']: @@ -2057,7 +2144,7 @@ def dumpfixed(): output('
\n') fixed_patterns = [] for i in warnpatterns: - if len(i['members']) == 0 and i['severity'] != severity.SKIP: + if len(i['members']) == 0: fixed_patterns.append(i['description'] + ' (' + allpatterns(i) + ') ' + i['option']) fixed_patterns.sort() @@ -2083,14 +2170,14 @@ def warningwithurl(line): def dumpgroup(sev, anchor, description, warnings): mark = anchor + '_mark' - output('\n\n') + output('\n
\n') output('' + '\n') output('
' + description + '
\n') output('\n') @@ -2191,16 +2278,15 @@ def parseinputfile(): def dumphtml(): dumphtmlprologue('Warnings for ' + platformversion + ' - ' + targetproduct + ' - ' + targetvariant) dumpstats() + dumpstatsbyproject() + emitbuttons() # sort table based on number of members once dumpstats has deduplicated the # members. warnpatterns.sort(reverse=True, key=lambda i: len(i['members'])) - dumpseverity(severity.FIXMENOW) - dumpseverity(severity.HIGH) - dumpseverity(severity.MEDIUM) - dumpseverity(severity.LOW) - dumpseverity(severity.TIDY) - dumpseverity(severity.HARMLESS) - dumpseverity(severity.UNKNOWN) + # Dump warnings by severity. If severity.SKIP warnings are not dumpped, + # the project anchors should be dumped through dumpskippedanchors. + for s in severity.kinds: + dumpseverity(s) dumpfixed() dumphtmlepilogue() @@ -2237,13 +2323,8 @@ def countseverity(sev, kind): def dumpcsv(): sortwarnings() total = 0 - total += countseverity(severity.FIXMENOW, 'FixNow') - total += countseverity(severity.HIGH, 'High') - total += countseverity(severity.MEDIUM, 'Medium') - total += countseverity(severity.LOW, 'Low') - total += countseverity(severity.TIDY, 'Tidy') - total += countseverity(severity.HARMLESS, 'Harmless') - total += countseverity(severity.UNKNOWN, 'Unknown') + for s in severity.kinds: + total += countseverity(s, columnheaderforseverity(s)) print '{},,{}'.format(total, 'All warnings')