From 2a38c37d575b3b330d61c12912aeda1faa5159aa Mon Sep 17 00:00:00 2001 From: Chih-Hung Hsieh Date: Thu, 22 Sep 2016 19:22:07 -0700 Subject: [PATCH] Add buttons to group warning by project or severity. * Add more project patterns. * Add more top level comments for global variables and functions. * Resequence severity numbers to match the dump order. * Emit warning messages and tables to static HTML JavaScript arrays. * Replace old static HTML table dumper functions with new dynamic HTML JavaScript to generate sections of warnings. * Warning messages are grouped into sections by severity or projects. * Better descriptions for SKIP warning patterns. * Replace output function with print. Bug: 31377083 Test: run warn.py --byproject build.log Change-Id: I7b44ef6223d5b2f1aa31655a5a47d854f9a1dedc --- tools/warn.py | 819 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 534 insertions(+), 285 deletions(-) diff --git a/tools/warn.py b/tools/warn.py index c223d9ee73..7d9ba6530b 100755 --- a/tools/warn.py +++ b/tools/warn.py @@ -8,6 +8,77 @@ Use option --byproject to output tables grouped by source file projects. Use option --gencsv to output warning counts in CSV format. """ +# List of important data structures and functions in this script. +# +# To parse and keep warning message in the input file: +# severity: classification of message severity +# severity.range [0, 1, ... last_severity_level] +# severity.colors for header background +# severity.column_headers for the warning count table +# severity.headers for warning message tables +# warn_patterns: +# warn_patterns[w]['category'] tool that issued the warning, not used now +# warn_patterns[w]['description'] table heading +# warn_patterns[w]['members'] matched warnings from input +# warn_patterns[w]['option'] compiler flag to control the warning +# warn_patterns[w]['patterns'] regular expressions to match warnings +# warn_patterns[w]['projects'][p] number of warnings of pattern w in p +# warn_patterns[w]['severity'] severity level +# project_list[p][0] project name +# project_list[p][1] regular expression to match a project path +# project_patterns[p] re.compile(project_list[p][1]) +# project_names[p] project_list[p][0] +# warning_messages array of each warning message, without source url +# warning_records array of [idx to warn_patterns, +# idx to project_names, +# idx to warning_messages] +# platform_version +# target_product +# target_variant +# compile_patterns, 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. +# FlagURL args.url +# FlagSeparator args.separator +# SeverityColors: severity.colors +# SeverityHeaders: severity.headers +# SeverityColumnHeaders: severity.column_headers +# ProjectNames: project_names, or project_list[*][0] +# WarnPatternsSeverity: warn_patterns[*]['severity'] +# WarnPatternsDescription: warn_patterns[*]['description'] +# WarnPatternsOption: warn_patterns[*]['option'] +# 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(): +# +# To emit csv files of warning message counts: +# flag --gencsv +# description_for_csv, string_for_csv: +# count_severity(sev, kind): +# dump_csv(): + import argparse import re @@ -31,33 +102,32 @@ parser.add_argument(dest='buildlog', metavar='build.log', args = parser.parse_args() -# if you add another level, don't forget to give it a color below class severity: # pylint:disable=invalid-name,old-style-class """Severity levels and attributes.""" - UNKNOWN = 0 - FIXMENOW = 1 - HIGH = 2 - MEDIUM = 3 - LOW = 4 - TIDY = 5 - HARMLESS = 6 + # numbered by dump order + FIXMENOW = 0 + HIGH = 1 + MEDIUM = 2 + LOW = 3 + TIDY = 4 + HARMLESS = 5 + UNKNOWN = 6 SKIP = 7 + range = range(8) attributes = [ # pylint:disable=bad-whitespace - ['lightblue', 'Unknown', 'Unknown warnings'], ['fuchsia', 'FixNow', 'Critical warnings, fix me now'], ['red', 'High', 'High severity warnings'], ['orange', 'Medium', 'Medium severity warnings'], ['yellow', 'Low', 'Low severity warnings'], ['peachpuff', 'Tidy', 'Clang-Tidy warnings'], ['limegreen', 'Harmless', 'Harmless warnings'], + ['lightblue', 'Unknown', 'Unknown warnings'], ['grey', 'Unhandled', 'Unhandled warnings'] ] - color = [a[0] for a in attributes] + colors = [a[0] for a in attributes] column_headers = [a[1] for a in attributes] - header = [a[2] for a in attributes] - # order to dump by severity - kinds = [FIXMENOW, HIGH, MEDIUM, LOW, TIDY, HARMLESS, UNKNOWN, SKIP] + headers = [a[2] for a in attributes] warn_patterns = [ # TODO(chh): fix pylint space and indentation warnings @@ -84,7 +154,7 @@ warn_patterns = [ 'patterns':[r".*: warning: implicit declaration of function .+", r".*: warning: implicitly declaring library function" ] }, { 'category':'C/C++', 'severity':severity.SKIP, - 'description':'', + 'description':'skip, conflicting types for ...', 'patterns':[r".*: warning: conflicting types for '.+'"] }, { 'category':'C/C++', 'severity':severity.HIGH, 'option':'-Wtype-limits', 'description':'Expression always evaluates to true or false', @@ -159,7 +229,7 @@ warn_patterns = [ 'description':'Need virtual destructor', 'patterns':[r".*: warning: delete called .* has virtual functions but non-virtual destructor"] }, { 'category':'cont.', 'severity':severity.SKIP, - 'description':'', + 'description':'skip, near initialization for ...', 'patterns':[r".*: warning: \(near initialization for '.+'\)"] }, { 'category':'C/C++', 'severity':severity.MEDIUM, 'option':'-Wdate-time', 'description':'Expansion of data or time macro', @@ -299,7 +369,7 @@ warn_patterns = [ r".*: warning: Access to .+ results in a dereference of a null pointer", r".*: warning: Null pointer argument in"] }, { 'category':'cont.', 'severity':severity.SKIP, - 'description':'', + 'description':'skip, parameter name (without types) in function declaration', 'patterns':[r".*: warning: parameter names \(without types\) in function declaration"] }, { 'category':'C/C++', 'severity':severity.MEDIUM, 'option':'-Wstrict-aliasing', 'description':'Dereferencing <foo> breaks strict aliasing rules', @@ -315,7 +385,7 @@ warn_patterns = [ 'description':'Symbol redefined', 'patterns':[r".*: warning: "".+"" redefined"] }, { 'category':'cont.', 'severity':severity.SKIP, - 'description':'', + 'description':'skip, ... location of the previous definition', 'patterns':[r".*: warning: this is the location of the previous definition"] }, { 'category':'ld', 'severity':severity.MEDIUM, 'description':'ld: type and size of dynamic symbol are not defined', @@ -351,7 +421,7 @@ warn_patterns = [ 'description':'Redundant declaration', 'patterns':[r".*: warning: redundant redeclaration of '.+'"] }, { 'category':'cont.', 'severity':severity.SKIP, - 'description':'', + 'description':'skip, previous declaration ... was here', 'patterns':[r".*: warning: previous declaration of '.+' was here"] }, { 'category':'C/C++', 'severity':severity.MEDIUM, 'option':'-Wswitch-enum', 'description':'Enum value not handled in switch', @@ -1154,13 +1224,13 @@ warn_patterns = [ 'patterns':[r".*: warning: '.+' will be initialized after", r".*: warning: field .+ will be initialized after .+Wreorder"] }, { 'category':'cont.', 'severity':severity.SKIP, - 'description':'', + 'description':'skip, ....', 'patterns':[r".*: warning: '.+'"] }, { 'category':'cont.', 'severity':severity.SKIP, - 'description':'', + 'description':'skip, base ...', 'patterns':[r".*: warning: base '.+'"] }, { 'category':'cont.', 'severity':severity.SKIP, - 'description':'', + 'description':'skip, when initialized here', 'patterns':[r".*: warning: when initialized here"] }, { 'category':'C/C++', 'severity':severity.MEDIUM, 'option':'-Wmissing-parameter-type', 'description':'Parameter type not specified', @@ -1206,7 +1276,7 @@ warn_patterns = [ 'description':'<foo> declared inside parameter list, scope limited to this definition', 'patterns':[r".*: warning: '.+' declared inside parameter list"] }, { 'category':'cont.', 'severity':severity.SKIP, - 'description':'', + 'description':'skip, its scope is only this ...', 'patterns':[r".*: warning: its scope is only this definition or declaration, which is probably not what you want"] }, { 'category':'C/C++', 'severity':severity.LOW, 'option':'-Wcomment', 'description':'Line continuation inside comment', @@ -1277,7 +1347,7 @@ warn_patterns = [ 'description':'Overload resolution chose to promote from unsigned or enum to signed type' , 'patterns':[r".*: warning: passing '.+' chooses '.+' over '.+'.*Wsign-promo"] }, { 'category':'cont.', 'severity':severity.SKIP, - 'description':'', + 'description':'skip, in call to ...', 'patterns':[r".*: warning: in call to '.+'"] }, { 'category':'C/C++', 'severity':severity.HIGH, 'option':'-Wextra', 'description':'Base should be explicitly initialized in copy constructor', @@ -1469,13 +1539,13 @@ warn_patterns = [ # these next ones are to deal with formatting problems resulting from the log being mixed up by 'make -j' { 'category':'C/C++', 'severity':severity.SKIP, - 'description':'', + 'description':'skip, ,', 'patterns':[r".*: warning: ,$"] }, { 'category':'C/C++', 'severity':severity.SKIP, - 'description':'', + 'description':'skip,', 'patterns':[r".*: warning: $"] }, { 'category':'C/C++', 'severity':severity.SKIP, - 'description':'', + 'description':'skip, In file included from ...', 'patterns':[r".*: warning: In file included from .+,"] }, # warnings from clang-tidy @@ -1610,16 +1680,56 @@ project_list = [ # match external/google* before external/ ['external/google', r"(^|.*/)external/google.*: warning:"], ['external/non-google', r"(^|.*/)external/.*: warning:"], - ['frameworks', r"(^|.*/)frameworks/.*: warning:"], - ['hardware', r"(^|.*/)hardware/.*: warning:"], + ['frameworks/av/camera',r"(^|.*/)frameworks/av/camera/.*: warning:"], + ['frameworks/av/cmds', r"(^|.*/)frameworks/av/cmds/.*: warning:"], + ['frameworks/av/drm', r"(^|.*/)frameworks/av/drm/.*: warning:"], + ['frameworks/av/include',r"(^|.*/)frameworks/av/include/.*: warning:"], + ['frameworks/av/media', r"(^|.*/)frameworks/av/media/.*: warning:"], + ['frameworks/av/radio', r"(^|.*/)frameworks/av/radio/.*: warning:"], + ['frameworks/av/services', r"(^|.*/)frameworks/av/services/.*: warning:"], + ['frameworks/av/Other', r"(^|.*/)frameworks/av/.*: warning:"], + ['frameworks/base', r"(^|.*/)frameworks/base/.*: warning:"], + ['frameworks/compile', r"(^|.*/)frameworks/compile/.*: warning:"], + ['frameworks/minikin', r"(^|.*/)frameworks/minikin/.*: warning:"], + ['frameworks/native', r"(^|.*/)frameworks/native/.*: warning:"], + ['frameworks/opt', r"(^|.*/)frameworks/opt/.*: warning:"], + ['frameworks/rs', r"(^|.*/)frameworks/rs/.*: warning:"], + ['frameworks/webview', r"(^|.*/)frameworks/webview/.*: warning:"], + ['frameworks/wilhelm', r"(^|.*/)frameworks/wilhelm/.*: warning:"], + ['frameworks/Other', r"(^|.*/)frameworks/.*: warning:"], + ['hardware/akm', r"(^|.*/)hardware/akm/.*: warning:"], + ['hardware/broadcom', r"(^|.*/)hardware/broadcom/.*: warning:"], + ['hardware/google', r"(^|.*/)hardware/google/.*: warning:"], + ['hardware/intel', r"(^|.*/)hardware/intel/.*: warning:"], + ['hardware/interfaces', r"(^|.*/)hardware/interfaces/.*: warning:"], + ['hardware/libhardware',r"(^|.*/)hardware/libhardware/.*: warning:"], + ['hardware/libhardware_legacy',r"(^|.*/)hardware/libhardware_legacy/.*: warning:"], + ['hardware/qcom', r"(^|.*/)hardware/qcom/.*: warning:"], + ['hardware/ril', r"(^|.*/)hardware/ril/.*: warning:"], + ['hardware/Other', r"(^|.*/)hardware/.*: warning:"], ['kernel', r"(^|.*/)kernel/.*: warning:"], ['libcore', r"(^|.*/)libcore/.*: warning:"], - ['libnativehelper', r"(^|.*/)libnativehelper/.*: warning:"], + ['libnativehelper', r"(^|.*/)libnativehelper/.*: warning:"], ['ndk', r"(^|.*/)ndk/.*: warning:"], + # match vendor/unbungled_google/packages before other packages + ['unbundled_google', r"(^|.*/)unbundled_google/.*: warning:"], ['packages', r"(^|.*/)packages/.*: warning:"], ['pdk', r"(^|.*/)pdk/.*: warning:"], ['prebuilts', r"(^|.*/)prebuilts/.*: warning:"], - ['system', r"(^|.*/)system/.*: warning:"], + ['system/bt', r"(^|.*/)system/bt/.*: warning:"], + ['system/connectivity', r"(^|.*/)system/connectivity/.*: warning:"], + ['system/core', r"(^|.*/)system/core/.*: warning:"], + ['system/extras', r"(^|.*/)system/extras/.*: warning:"], + ['system/gatekeeper', r"(^|.*/)system/gatekeeper/.*: warning:"], + ['system/keymaster', r"(^|.*/)system/keymaster/.*: warning:"], + ['system/libhwbinder', r"(^|.*/)system/libhwbinder/.*: warning:"], + ['system/media', r"(^|.*/)system/media/.*: warning:"], + ['system/netd', r"(^|.*/)system/netd/.*: warning:"], + ['system/security', r"(^|.*/)system/security/.*: warning:"], + ['system/sepolicy', r"(^|.*/)system/sepolicy/.*: warning:"], + ['system/tools', r"(^|.*/)system/tools/.*: warning:"], + ['system/vold', r"(^|.*/)system/vold/.*: warning:"], + ['system/Other', r"(^|.*/)system/.*: warning:"], ['toolchain', r"(^|.*/)toolchain/.*: warning:"], ['test', r"(^|.*/)test/.*: warning:"], ['tools', r"(^|.*/)tools/.*: warning:"], @@ -1627,33 +1737,28 @@ project_list = [ ['vendor/google', r"(^|.*/)vendor/google.*: warning:"], ['vendor/non-google', r"(^|.*/)vendor/.*: warning:"], # keep out/obj and other patterns at the end. - ['out/obj', r".*/(gen|obj[^/]*)/(include|EXECUTABLES|SHARED_LIBRARIES|STATIC_LIBRARIES)/.*: warning:"], + ['out/obj', r".*/(gen|obj[^/]*)/(include|EXECUTABLES|SHARED_LIBRARIES|STATIC_LIBRARIES|NATIVE_TESTS)/.*: warning:"], ['other', r".*: warning:"], ] project_patterns = [] project_names = [] +warning_messages = [] +warning_records = [] def initialize_arrays(): """Complete global arrays before they are used.""" - global project_names + global project_names, project_patterns project_names = [p[0] for p in project_list] - for p in project_list: - project_patterns.append({'description': p[0], - 'members': [], - 'pattern': re.compile(p[1])}) - # Each warning pattern has 3 dictionaries: - # (1) 'projects' maps a project name to number of warnings in that project. - # (2) 'project_anchor' maps a project name to its anchor number for HTML. - # (3) 'project_warnings' maps a project name to a list of warning messages. + project_patterns = [re.compile(p[1]) for p in project_list] for w in warn_patterns: w['members'] = [] if 'option' not in w: w['option'] = '' + # Each warning pattern has a 'projects' dictionary, that + # maps a project name to number of warnings in that project. w['projects'] = {} - w['project_anchor'] = {} - w['project_warnings'] = {} initialize_arrays() @@ -1666,46 +1771,41 @@ target_variant = 'unknown' ##### Data and functions to dump html file. ################################## -anchor = 0 -cur_row_class = 0 - -html_script_style = """\ - - \n""" - - -def output(text): - print text, + if (!e || !f) break; + e.style.display = (show ? 'block' : 'none'); + f.innerHTML = (show ? '⊖' : '⊕'); + } + }; + + + +""" def html_big(param): @@ -1713,24 +1813,17 @@ def html_big(param): def dump_html_prologue(title): - output('\n\n') - output('' + title + '\n') - output(html_script_style) - output('\n\n') - output(html_big(title)) - output('

\n') + print '\n' + print '' + title + '' + print html_head_scripts + emit_stats_by_project() + print '\n' + print html_big(title) + print '

' def dump_html_epilogue(): - output('\n\n\n') - - -def table_row(text): - global cur_row_class - cur_row_class = 1 - cur_row_class - # remove last '\n' - t = text[:-1] if text[-1] == '\n' else text - output('' + t + '\n') + print '\n\n' def sort_warnings(): @@ -1738,57 +1831,59 @@ def sort_warnings(): i['members'] = sorted(set(i['members'])) -def dump_stats_by_project(): - """Dump a table of warnings per project and severity.""" +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. - warnings = {p: {s: 0 for s in severity.kinds} for p in project_names} + warnings = {p: {s: 0 for s in severity.range} for p in project_names} for i in warn_patterns: s = i['severity'] for p in i['projects']: warnings[p][s] += i['projects'][p] # total_by_project[p] is number of warnings in project p. - total_by_project = {p: sum(warnings[p][s] for s in severity.kinds) + total_by_project = {p: sum(warnings[p][s] for s in severity.range) for p in project_names} # total_by_severity[s] is number of warnings of severity s. total_by_severity = {s: sum(warnings[p][s] for p in project_names) - for s in severity.kinds} + for s in severity.range} # emit table header - output('

\n\n') - for s in severity.kinds: + stats_header = ['Project'] + for s in severity.range: if total_by_severity[s]: - output(''. - format(severity.color[s], severity.column_headers[s])) - output('\n') + stats_header.append("{}". + format(severity.colors[s], + severity.column_headers[s])) + stats_header.append('TOTAL') # emit a row of warning counts per project, skip no-warning projects total_all_projects = 0 + stats_rows = [] for p in project_names: if total_by_project[p]: - output(''.format(p)) - for s in severity.kinds: + one_row = [p] + for s in severity.range: if total_by_severity[s]: - output(''.format(warnings[p][s])) - output(''.format(total_by_project[p])) + one_row.append(warnings[p][s]) + one_row.append(total_by_project[p]) + stats_rows.append(one_row) total_all_projects += total_by_project[p] - output('\n') # emit a row of warning counts per severity total_all_severities = 0 - output('') - for s in severity.kinds: + one_row = ['TOTAL'] + for s in severity.range: if total_by_severity[s]: - output(''.format(total_by_severity[s])) + one_row.append(total_by_severity[s]) total_all_severities += total_by_severity[s] - output('\n'.format(total_all_projects)) - - # at the end of table, verify total counts - output('
Project{}TOTAL
{}{}{}
TOTAL{}{}

\n') - if total_all_projects != total_all_severities: - output('

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

\n') + one_row.append(total_all_projects) + stats_rows.append(one_row) + print '' def dump_stats(): @@ -1804,83 +1899,57 @@ def dump_stats(): 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) + '
') + print 'Number of classified warnings: ' + str(known) + '
' + print 'Number of skipped warnings: ' + str(skipped) + '
' + print 'Number of unclassified warnings: ' + str(unknown) + '
' total = unknown + known + skipped - output('\nTotal number of warnings: ' + str(total) + '') + extra_msg = '' if total < 1000: - output('(low count may indicate incremental build)') - output('

\n') + extra_msg = ' (low count may indicate incremental build)' + print 'Total number of warnings: ' + str(total) + '' + 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(): - output('\n' - '
\n') + '\n' + '\n' + '
') -def dump_severity(sev): - """Dump everything for a given severity.""" - global anchor - total = 0 - for i in warn_patterns: - if i['severity'] == sev: - total += len(i['members']) - output('\n
' + severity.header[sev] + ': ' + str(total) + '\n') - output('
\n') - for i in warn_patterns: - if i['severity'] == sev and i['members']: - anchor += 1 - i['anchor'] = str(anchor) - if args.byproject: - dump_category_by_project(sev, i) - else: - dump_category(sev, i) - output('
\n') - - -def dump_skipped_anchors(): - """emit all skipped project anchors for expand_collapse.""" - output('
\n') # hide these fake elements - for i in warn_patterns: - if i['severity'] == severity.SKIP and i['members']: - projects = i['project_warnings'].keys() - for p in projects: - output('
' + - '
\n') - output('
\n') - - -def all_patterns(cat): - pats = '' - for i in cat['patterns']: - pats += i - pats += ' / ' - return pats - - -def description_for(cat): - if cat['description']: - return cat['description'] - return all_patterns(cat) +def all_patterns(category): + patterns = '' + for i in category['patterns']: + patterns += i + patterns += ' / ' + return patterns def dump_fixed(): """Show which warnings no longer occur.""" - global anchor - anchor += 1 - mark = str(anchor) + '_mark' - output('\n

' + anchor = 'fixed_warnings' + mark = anchor + '_mark' + print ('\n

' ' Fixed warnings. ' 'No more occurrences. Please consider turning these into ' 'errors if possible, before they are reintroduced in to the build' - ':

\n') - output('
\n') + ':

') + print '
' fixed_patterns = [] for i in warn_patterns: if not i['members']: @@ -1889,93 +1958,40 @@ def dump_fixed(): if i['option']: fixed_patterns.append(' ' + i['option']) fixed_patterns.sort() - output('\n') - output('
\n') + print '' + print '
' -def warning_with_url(line): - """Returns a warning message line with HTML link to given args.url.""" - if not args.url: - return line - m = re.search(r'^([^ :]+):(\d+):(.+)', line, re.M|re.I) - if not m: - return line - file_path = m.group(1) - line_number = m.group(2) - warning = m.group(3) - prefix = '' + file_path + - ':' + line_number + ':' + warning) - else: - return prefix + '">' + file_path + ':' + line_number + ':' + warning - - -def dump_group(sev, anchor_str, description, warnings): - """Dump warnings of given severity, anchor_str, and description.""" - mark = anchor_str + '_mark' - output('\n\n') - output('' + - '\n') - output('
' + description + '
\n') - output('\n') - - -def dump_category(sev, cat): - """Dump warnings in a category.""" - description = description_for(cat) + ' (' + str(len(cat['members'])) + ')' - dump_group(sev, cat['anchor'], description, cat['members']) - - -def dump_category_by_project(sev, cat): - """Similar to dump_category but output one table per project.""" - warning = description_for(cat) - projects = cat['project_warnings'].keys() - projects.sort() - for p in projects: - anchor_str = cat['project_anchor'][p] - project_warnings = cat['project_warnings'][p] - description = '{}, in {} ({})'.format(warning, p, len(project_warnings)) - dump_group(sev, anchor_str, description, project_warnings) - - -def find_project(line): - for p in project_patterns: - if p['pattern'].match(line): - return p['description'] - return '???' +def find_project_index(line): + for p in range(len(project_patterns)): + if project_patterns[p].match(line): + return p + return -1 def classify_warning(line): - global anchor - for i in warn_patterns: - for cpat in i['compiled_patterns']: + for i in range(len(warn_patterns)): + w = warn_patterns[i] + for cpat in w['compiled_patterns']: if cpat.match(line): - i['members'].append(line) - pname = find_project(line) + w['members'].append(line) + p = find_project_index(line) + index = len(warning_messages) + warning_messages.append(line) + warning_records.append([i, p, index]) + pname = '???' if p < 0 else project_names[p] # Count warnings by project. - if pname in i['projects']: - i['projects'][pname] += 1 + if pname in w['projects']: + w['projects'][pname] += 1 else: - i['projects'][pname] = 1 - # Collect warnings by project. - if args.byproject: - if pname in i['project_warnings']: - i['project_warnings'][pname].append(line) - else: - i['project_warnings'][pname] = [line] - if pname not in i['project_anchor']: - anchor += 1 - i['project_anchor'][pname] = str(anchor) + w['projects'][pname] = 1 return else: # If we end up here, there was a problem parsing the log @@ -2013,19 +2029,243 @@ def parse_input_file(): if line not in warning_lines: classify_warning(line) warning_lines.add(line) - else: + elif line_counter < 50: # save a little bit of time by only doing this for the first few lines - if line_counter < 50: - line_counter += 1 - m = re.search('(?<=^PLATFORM_VERSION=).*', line) - if m is not None: - platform_version = m.group(0) - m = re.search('(?<=^TARGET_PRODUCT=).*', line) - if m is not None: - target_product = m.group(0) - m = re.search('(?<=^TARGET_BUILD_VARIANT=).*', line) - if m is not None: - target_variant = m.group(0) + line_counter += 1 + m = re.search('(?<=^PLATFORM_VERSION=).*', line) + if m is not None: + platform_version = m.group(0) + m = re.search('(?<=^TARGET_PRODUCT=).*', line) + if m is not None: + target_product = m.group(0) + m = re.search('(?<=^TARGET_BUILD_VARIANT=).*', line) + if m is not None: + target_variant = m.group(0) + + +# Return s with escaped quotation characters. +def escape_string(s): + return s.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): + print 'var warning_{} = ['.format(name) + for i in range(len(warn_patterns)): + print '{},'.format(warn_patterns[i][name]) + print '];' + + +def emit_warning_arrays(): + emit_warning_array('severity') + print 'var warning_description = [' + for i in range(len(warn_patterns)): + if warn_patterns[i]['members']: + print '"{}",'.format(escape_string(warn_patterns[i]['description'])) + else: + print '"",' # no such warning + print '];' + +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) { + if (FlagURL == "") return line; + if (FlagSeparator == "") { + return line.replace(ParseLinePattern, + "$1:$2:$3"); + } + return line.replace(ParseLinePattern, + "$1:$2:$3"); + } + function createArrayOfDictionaries(n) { + var result = []; + for (var i=0; i" + + " " + + description + " (" + messages.length + ")"; + result += ""; + } + if (result.length > 0) { + return "
" + + header + ": " + totalMessages + + "
" + + result + "
"; + + } + return ""; // empty section + } + function generateSectionsBySeverity() { + var result = ""; + var groups = groupWarningsBySeverity(); + for (s=0; s

' + print '\n' emit_buttons() - # sort table based on number of members once dump_stats has de-duplicated the - # members. - warn_patterns.sort(reverse=True, key=lambda i: len(i['members'])) - # Dump warnings by severity. If severity.SKIP warnings are not dumped, - # the project anchors should be dumped through dump_skipped_anchors. - for s in severity.kinds: - dump_severity(s) + # Warning messages are grouped by severities or project names. + print '
' + if args.byproject: + print '' + else: + print '' dump_fixed() dump_html_epilogue() @@ -2049,14 +2292,17 @@ def dump_html(): ##### Functions to count warnings and dump csv file. ######################### -def description_for_csv(cat): - if not cat['description']: +def description_for_csv(category): + if not category['description']: return '?' - return cat['description'] + return category['description'] def string_for_csv(s): + # Only some Java warning desciptions have used quotation marks. + # TODO(chh): if s has double quote character, s should be quoted. if ',' in s: + # TODO(chh): replace a double quote with two double quotes in s. return '"{}"'.format(s) return s @@ -2079,15 +2325,18 @@ def count_severity(sev, kind): return total +# dump number of warnings in csv format to stdout def dump_csv(): """Dump number of warnings in csv format to stdout.""" sort_warnings() total = 0 - for s in severity.kinds: + for s in severity.range: total += count_severity(s, severity.column_headers[s]) print '{},,{}'.format(total, 'All warnings') +##### Main function starts here. ######################### + parse_input_file() if args.gencsv: dump_csv()