Count warnings per project and dump out CSV format.
* No change to html output, except removing unused "PageTop" anchor.
* Add --gencsv flag to output a CSV file instead of html file.
CSV files are combined to a table for dashboard charts.
* Count warnings from selected source project directories.
* CSV output lines are:
num_of_warnings,project_name,warning_description
* project_name is empty for sum of warnings from all projects.
* warning_description could be:
(1) "<severity>: <warning type description>"
for one warning type, or
(2) "<severity> warnings"
for all warning types of the severity.
Change-Id: I1cf976ed0cf142091226b22665b7f28d383608fd
Test: run through current build.log files.
diff --git a/tools/warn.py b/tools/warn.py
index 8f7a638..51349af 100755
--- a/tools/warn.py
+++ b/tools/warn.py
@@ -6,6 +6,10 @@
import re
parser = argparse.ArgumentParser(description='Convert a build log into HTML')
+parser.add_argument('--gencsv',
+ help='Generate a CSV file with number of various warnings',
+ action="store_true",
+ default=False)
parser.add_argument('--url',
help='Root URL of an Android source code tree prefixed '
'before files in warnings')
@@ -1789,6 +1793,54 @@
'patterns':[r".*: warning: .+"] },
]
+# A list of [project_name, file_path_pattern].
+# project_name should not contain comma, to be used in CSV output.
+projectlist = [
+ ['art', r"(^|.*/)art/.*: warning:"],
+ ['bionic', r"(^|.*/)bionic/.*: warning:"],
+ ['bootable', r"(^|.*/)bootable/.*: warning:"],
+ ['build', r"(^|.*/)build/.*: warning:"],
+ ['cts', r"(^|.*/)cts/.*: warning:"],
+ ['dalvik', r"(^|.*/)dalvik/.*: warning:"],
+ ['developers', r"(^|.*/)developers/.*: warning:"],
+ ['development', r"(^|.*/)development/.*: warning:"],
+ ['device', r"(^|.*/)device/.*: warning:"],
+ ['doc', r"(^|.*/)doc/.*: warning:"],
+ ['external', r"(^|.*/)external/.*: warning:"],
+ ['frameworks', r"(^|.*/)frameworks/.*: warning:"],
+ ['hardware', r"(^|.*/)hardware/.*: warning:"],
+ ['kernel', r"(^|.*/)kernel/.*: warning:"],
+ ['libcore', r"(^|.*/)libcore/.*: warning:"],
+ ['libnativehelper', r"(^|.*/)libnativehelper/.*: warning:"],
+ ['ndk', r"(^|.*/)ndk/.*: warning:"],
+ ['packages', r"(^|.*/)packages/.*: warning:"],
+ ['pdk', r"(^|.*/)pdk/.*: warning:"],
+ ['prebuilts', r"(^|.*/)prebuilts/.*: warning:"],
+ ['system', r"(^|.*/)system/.*: warning:"],
+ ['toolchain', r"(^|.*/)toolchain/.*: warning:"],
+ ['test', r"(^|.*/)test/.*: warning:"],
+ ['tools', r"(^|.*/)tools/.*: warning:"],
+ ['vendor', r"(^|.*/)vendor/.*: warning:"],
+ ['out/obj', r".*/(gen|obj[^/]*)/(include|EXECUTABLES|SHARED_LIBRARIES|STATIC_LIBRARIES)/.*: warning:"],
+ ['other', r".*: warning:"],
+]
+
+projectpatterns = []
+for p in projectlist:
+ projectpatterns.append({'description':p[0], 'members':[], 'pattern':re.compile(p[1])})
+
+# Each warning pattern has a dictionary that maps
+# a project name to number of warnings in that project.
+for w in warnpatterns:
+ w['projects'] = {}
+
+platformversion = 'unknown'
+targetproduct = 'unknown'
+targetvariant = 'unknown'
+
+
+##### Data and functions to dump html file. ##################################
+
anchor = 0
cur_row_class = 0
@@ -1837,7 +1889,6 @@
output('<title>' + title + '</title>\n')
output(html_script_style)
output('</head>\n<body>\n')
- output('<a name="PageTop">')
output(htmlbig(title))
output('<p>\n')
@@ -1851,12 +1902,16 @@
output(text)
output('</td></tr>')
+def sortwarnings():
+ for i in warnpatterns:
+ i['members'] = sorted(set(i['members']))
+
# dump some stats about total number of warnings and such
def dumpstats():
known = 0
unknown = 0
+ sortwarnings()
for i in warnpatterns:
- i['members'] = sorted(set(i['members']))
if i['severity'] == severity.UNKNOWN:
unknown += len(i['members'])
elif i['severity'] != severity.SKIP:
@@ -1961,17 +2016,28 @@
output('</table></div>\n')
+def findproject(line):
+ for p in projectpatterns:
+ if p['pattern'].match(line):
+ return p['description']
+ return '???'
+
def classifywarning(line):
for i in warnpatterns:
for cpat in i['compiledpatterns']:
if cpat.match(line):
i['members'].append(line)
+ pname = findproject(line)
+ if pname in i['projects']:
+ i['projects'][pname] += 1
+ else:
+ i['projects'][pname] = 1
return
- else:
- # If we end up here, there was a problem parsing the log
- # probably caused by 'make -j' mixing the output from
- # 2 or more concurrent compiles
- pass
+ else:
+ # If we end up here, there was a problem parsing the log
+ # probably caused by 'make -j' mixing the output from
+ # 2 or more concurrent compiles
+ pass
# precompiling every pattern speeds up parsing by about 30x
def compilepatterns():
@@ -1980,54 +2046,103 @@
for pat in i['patterns']:
i['compiledpatterns'].append(re.compile(pat))
-infile = open(args.buildlog, 'r')
-warnings = []
+def parseinputfile():
+ global platformversion
+ global targetproduct
+ global targetvariant
+ infile = open(args.buildlog, 'r')
+ linecounter = 0
-platformversion = 'unknown'
-targetproduct = 'unknown'
-targetvariant = 'unknown'
-linecounter = 0
+ warningpattern = re.compile('.* warning:.*')
+ compilepatterns()
-warningpattern = re.compile('.* warning:.*')
-compilepatterns()
-
-# read the log file and classify all the warnings
-warninglines = set()
-for line in infile:
- # replace fancy quotes with plain ol' quotes
- line = line.replace("‘", "'");
- line = line.replace("’", "'");
- if warningpattern.match(line):
- if line not in warninglines:
- classifywarning(line)
- warninglines.add(line)
- else:
- # save a little bit of time by only doing this for the first few lines
- if linecounter < 50:
- linecounter +=1
- m = re.search('(?<=^PLATFORM_VERSION=).*', line)
- if m != None:
- platformversion = m.group(0)
- m = re.search('(?<=^TARGET_PRODUCT=).*', line)
- if m != None:
- targetproduct = m.group(0)
- m = re.search('(?<=^TARGET_BUILD_VARIANT=).*', line)
- if m != None:
- targetvariant = m.group(0)
+ # read the log file and classify all the warnings
+ warninglines = set()
+ for line in infile:
+ # replace fancy quotes with plain ol' quotes
+ line = line.replace("‘", "'");
+ line = line.replace("’", "'");
+ if warningpattern.match(line):
+ if line not in warninglines:
+ classifywarning(line)
+ warninglines.add(line)
+ else:
+ # save a little bit of time by only doing this for the first few lines
+ if linecounter < 50:
+ linecounter +=1
+ m = re.search('(?<=^PLATFORM_VERSION=).*', line)
+ if m != None:
+ platformversion = m.group(0)
+ m = re.search('(?<=^TARGET_PRODUCT=).*', line)
+ if m != None:
+ targetproduct = m.group(0)
+ m = re.search('(?<=^TARGET_BUILD_VARIANT=).*', line)
+ if m != None:
+ targetvariant = m.group(0)
# dump the html output to stdout
-dumphtmlprologue('Warnings for ' + platformversion + ' - ' + targetproduct + ' - ' + targetvariant)
-dumpstats()
-# 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)
-dumpfixed()
-dumphtmlepilogue()
+def dumphtml():
+ dumphtmlprologue('Warnings for ' + platformversion + ' - ' + targetproduct + ' - ' + targetvariant)
+ dumpstats()
+ # 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)
+ dumpfixed()
+ dumphtmlepilogue()
+
+
+##### Functions to count warnings and dump csv file. #########################
+
+def descriptionforcsv(cat):
+ if cat['description'] == '':
+ return '?'
+ return cat['description']
+
+def stringforcsv(s):
+ if ',' in s:
+ return '"{}"'.format(s)
+ return s
+
+def countseverity(sev, kind):
+ sum = 0
+ for i in warnpatterns:
+ if i['severity'] == sev and len(i['members']) > 0:
+ n = len(i['members'])
+ sum += n
+ warning = stringforcsv(kind + ': ' + descriptionforcsv(i))
+ print '{},,{}'.format(n, warning)
+ # print number of warnings for each project, ordered by project name.
+ projects = i['projects'].keys()
+ projects.sort()
+ for p in projects:
+ print '{},{},{}'.format(i['projects'][p], p, warning)
+ print '{},,{}'.format(sum, kind + ' warnings')
+ return sum
+
+# dump number of warnings in csv format to stdout
+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')
+ print '{},,{}'.format(total, 'All warnings')
+
+
+parseinputfile()
+if args.gencsv:
+ dumpcsv()
+else:
+ dumphtml()