# Lint as: python3
# Copyright (C) 2019 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Emit warning messages to html or csv files."""
# Many functions in this module have too many arguments to be refactored.
# pylint:disable=too-many-arguments,missing-function-docstring
# 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.
# FlagPlatform flags.platform
# FlagURL flags.url, used by 'android'
# FlagSeparator flags.separator, used by 'android'
# SeverityColors: list of colors for all severity levels
# SeverityHeaders: list of headers for all severity levels
# SeverityColumnHeaders: list of column_headers for all severity levels
# ProjectNames: project_names, or project_list[*][0]
# WarnPatternsSeverity: warn_patterns[*]['severity']
# WarnPatternsDescription: warn_patterns[*]['description']
# 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():
from __future__
import print_function
import csv
import datetime
import html
import sys
# pylint:disable=relative-beyond-top-level
from .severity
import Severity
# Report files with this number of warnings or more.
LIMIT_WARNINGS_PER_FILE =
100
# Report files/directories with this percentage of total warnings or more.
LIMIT_PERCENT_WARNINGS =
1
HTML_HEAD_SCRIPTS =
"""\
<script type=
"text/javascript">
function expand(id) {
var e = document.getElementById(id);
var f = document.getElementById(id +
"_mark");
if (e.style.display ==
'block') {
e.style.display =
'none';
f.innerHTML =
'⊕';
}
else {
e.style.display =
'block';
f.innerHTML =
'⊖';
}
};
function expandCollapse(show) {
for (var id =
1; ; id++) {
var e = document.getElementById(id +
"");
var f = document.getElementById(id +
"_mark");
if (!e || !f)
break;
e.style.display = (show ?
'block' :
'none');
f.innerHTML = (show ?
'⊖' : '⊕');
}
};
</script>
<style type=
"text/css">
th,td{border-collapse:collapse; border:
1px solid black;}
.button{color:blue;font-size:
100%;font-weight:bolder;}
.bt{color:black;background-color:transparent;border:
none;outline:
none;
font-size:
140%;font-weight:bolder;}
.c0{background-color:
#e0e0e0;}
.c1{background-color:
#d0d0d0;}
.t1{border-collapse:collapse; width:
100%; border:
1px solid black;}
.box{margin:
5pt; padding:
5pt; border:
1px solid;}
</style>
<script src=
"https://www.gstatic.com/charts/loader.js"></script>
"""
def make_writer(output_stream):
def writer(text):
return output_stream.write(text +
'\n')
return writer
def html_big(param):
return '<font size="+2">' + param +
'</font>'
def dump_html_prologue(title, writer, warn_patterns, project_names):
writer(
'<html>\n<head>')
writer(
'<title>' + title +
'</title>')
writer(HTML_HEAD_SCRIPTS)
emit_stats_by_project(writer, warn_patterns, project_names)
writer(
'</head>\n<body>')
writer(html_big(title))
writer(
'<p>')
def dump_html_epilogue(writer):
writer(
'</body>\n</head>\n</html>')
def sort_warnings(warn_patterns):
for i
in warn_patterns:
i[
'members'] = sorted(set(i[
'members']))
def create_warnings(warn_patterns, project_names):
"""Creates warnings s.t.
warnings[p][s]
is as specified
in above docs.
Args:
warn_patterns: list of warning patterns
for specified platform
project_names: list of project names
Returns:
2D warnings array where warnings[p][s]
is # of warnings in project name p of
severity level s
"""
warnings = {p: {s.value:
0 for s
in Severity.levels}
for p
in project_names}
for pattern
in warn_patterns:
value = pattern[
'severity'].value
for project
in pattern[
'projects']:
warnings[project][value] += pattern[
'projects'][project]
return warnings
def get_total_by_project(warnings, project_names):
"""Returns dict, project as key and # warnings for that project as value."""
return {
p: sum(warnings[p][s.value]
for s
in Severity.levels)
for p
in project_names
}
def get_total_by_severity(warnings, project_names):
"""Returns dict, severity as key and # warnings of that severity as value."""
return {
s.value: sum(warnings[p][s.value]
for p
in project_names)
for s
in Severity.levels
}
def emit_table_header(total_by_severity):
"""Returns list of HTML-formatted content for severity stats."""
stats_header = [
'Project']
for severity
in Severity.levels:
if total_by_severity[severity.value]:
stats_header.append(
'<span style=\'background-color:{}\
'>{}</span>'.format(
severity.color, severity.column_header))
stats_header.append(
'TOTAL')
return stats_header
def emit_row_counts_per_project(warnings, total_by_project, total_by_severity,
project_names):
"""Returns total project warnings and row of stats for each project.
Args:
warnings: output of create_warnings(warn_patterns, project_names)
total_by_project: output of get_total_by_project(project_names)
total_by_severity: output of get_total_by_severity(project_names)
project_names: list of project names
Returns:
total_all_projects, the total number of warnings over all projects
stats_rows, a
2d list where each row
is [Project Name, <severity counts>,
total
# warnings for this project]
"""
total_all_projects =
0
stats_rows = []
for p_name
in project_names:
if total_by_project[p_name]:
one_row = [p_name]
for severity
in Severity.levels:
if total_by_severity[severity.value]:
one_row.append(warnings[p_name][severity.value])
one_row.append(total_by_project[p_name])
stats_rows.append(one_row)
total_all_projects += total_by_project[p_name]
return total_all_projects, stats_rows
def emit_row_counts_per_severity(total_by_severity, stats_header, stats_rows,
total_all_projects, writer):
"""Emits stats_header and stats_rows as specified above.
Args:
total_by_severity: output of get_total_by_severity()
stats_header: output of emit_table_header()
stats_rows: output of emit_row_counts_per_project()
total_all_projects: output of emit_row_counts_per_project()
writer: writer returned by make_writer(output_stream)
"""
total_all_severities =
0
one_row = [
'<b>TOTAL</b>']
for severity
in Severity.levels:
if total_by_severity[severity.value]:
one_row.append(total_by_severity[severity.value])
total_all_severities += total_by_severity[severity.value]
one_row.append(total_all_projects)
stats_rows.append(one_row)
writer(
'<script>')
emit_const_string_array(
'StatsHeader', stats_header, writer)
emit_const_object_array(
'StatsRows', stats_rows, writer)
writer(DRAW_TABLE_JAVASCRIPT)
writer(
'</script>')
def emit_stats_by_project(writer, warn_patterns, project_names):
"""Dump a google chart table of warnings per project and severity."""
warnings = create_warnings(warn_patterns, project_names)
total_by_project = get_total_by_project(warnings, project_names)
total_by_severity = get_total_by_severity(warnings, project_names)
stats_header = emit_table_header(total_by_severity)
total_all_projects, stats_rows = emit_row_counts_per_project(
warnings, total_by_project, total_by_severity, project_names)
emit_row_counts_per_severity(total_by_severity, stats_header, stats_rows,
total_all_projects, writer)
def dump_stats(writer, warn_patterns):
"""Dump some stats about total number of warnings and date."""
known =
0
skipped =
0
unknown =
0
sort_warnings(warn_patterns)
for i
in warn_patterns:
if i[
'severity'] == Severity.UNMATCHED:
unknown += len(i[
'members'])
elif i[
'severity'] == Severity.SKIP:
skipped += len(i[
'members'])
else:
known += len(i[
'members'])
writer(
'Number of classified warnings: <b>' + str(known) +
'</b><br>')
writer(
'Number of skipped warnings: <b>' + str(skipped) +
'</b><br>')
writer(
'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)'
writer(
'Total number of warnings: <b>' + str(total) +
'</b>' + extra_msg)
date_time_str = datetime.datetime.now().strftime(
'%Y/%m/%d %H:%M:%S')
writer(
'<p>(generated on ' + date_time_str +
')')
# 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(writer):
"""Write the button elements in HTML."""
writer(
'<p><button class="button" onclick="expandCollapse(1);">'
'Expand all warnings</button>\n'
'<button class="button" onclick="expandCollapse(0);">'
'Collapse all warnings</button>\n'
'<p><button class="button" onclick="groupBySeverity();">'
'Group warnings by severity</button>\n'
'<button class="button" onclick="groupByProject();">'
'Group warnings by project</button>')
def all_patterns(category):
patterns =
''
for i
in category[
'patterns']:
patterns += i
patterns +=
' / '
return patterns
def dump_fixed(writer, warn_patterns):
"""Show which warnings no longer occur."""
anchor =
'fixed_warnings'
mark = anchor +
'_mark'
writer(
'\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>')
writer(
'<blockquote>')
fixed_patterns = []
for i
in warn_patterns:
if not i[
'members']:
fixed_patterns.append(i[
'description'] +
' (' + all_patterns(i) +
')')
fixed_patterns = sorted(fixed_patterns)
writer(
'<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'
out_text = text[:-
1]
if text[-
1] ==
'\n' else text
writer(
'<tr><td class="c' + str(cur_row_class) +
'">'
+ out_text +
'</td></tr>')
writer(
'</table></div>')
writer(
'</blockquote>')
def write_severity(csvwriter, sev, kind, warn_patterns):
"""Count warnings of given severity and write CSV entries to writer."""
total =
0
for pattern
in warn_patterns:
if pattern[
'severity'] == sev
and pattern[
'members']:
num_members = len(pattern[
'members'])
total += num_members
warning = kind +
': ' + (pattern[
'description']
or '?')
csvwriter.writerow([num_members,
'', warning])
# print number of warnings for each project, ordered by project name
projects = sorted(pattern[
'projects'].keys())
for project
in projects:
csvwriter.writerow([pattern[
'projects'][project], project, warning])
csvwriter.writerow([total,
'', kind +
' warnings'])
return total
def dump_csv(csvwriter, warn_patterns):
"""Dump number of warnings in CSV format to writer."""
sort_warnings(warn_patterns)
total =
0
for severity
in Severity.levels:
total += write_severity(
csvwriter, severity, severity.column_header, warn_patterns)
csvwriter.writerow([total,
'',
'All warnings'])
def dump_csv_with_description(csvwriter, warning_records, warning_messages,
warn_patterns, project_names):
"""Outputs all the warning messages by project."""
csv_output = []
for record
in warning_records:
project_name = project_names[record[
1]]
pattern = warn_patterns[record[
0]]
severity = pattern[
'severity'].header
category = pattern[
'category']
description = pattern[
'description']
warning = warning_messages[record[
2]]
csv_output.append([project_name, severity,
category, description,
warning])
csv_output = sorted(csv_output)
for output
in csv_output:
csvwriter.writerow(output)
# Return line with escaped backslash and quotation characters.
def escape_string(line):
return line.replace(
'\\',
'\\\\').replace(
'"',
'\\"')
# Return line without trailing '\n' and escape the quotation characters.
def strip_escape_string(line):
if not line:
return line
line = line[:-
1]
if line[-
1] ==
'\n' else line
return escape_string(line)
def emit_warning_array(name, writer, warn_patterns):
writer(
'var warning_{} = ['.format(name))
for pattern
in warn_patterns:
if name ==
'severity':
writer(
'{},'.format(pattern[name].value))
else:
writer(
'{},'.format(pattern[name]))
writer(
'];')
def emit_warning_arrays(writer, warn_patterns):
emit_warning_array(
'severity', writer, warn_patterns)
writer(
'var warning_description = [')
for pattern
in warn_patterns:
if pattern[
'members']:
writer(
'"{}",'.format(escape_string(pattern[
'description'])))
else:
writer(
'"",')
# no such warning
writer(
'];')
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) { // used by Android
if (FlagURL ==
"")
return line;
if (FlagSeparator ==
"") {
return line.replace(ParseLinePattern,
"<a target='_blank' href='" + FlagURL +
"/$1'>$1</a>:$2:$3");
}
return line.replace(ParseLinePattern,
"<a target='_blank' href='" + FlagURL +
"/$1" + FlagSeparator +
"$2'>$1:$2</a>:$3");
}
function addURLToLine(line, link) { // used by Chrome
let line_split = line.split(
":");
let path = line_split.slice(
0,
3).join(
":");
let msg = line_split.slice(
3).join(
":");
let html_link = `<a target=
"_blank" href=
"${link}">${path}</a>${msg}`;
return html_link;
}
function createArrayOfDictionaries(n) {
var result = [];
for (var i=
0; i<n; i++) result.push({});
return result;
}
function groupWarningsBySeverity() {
// groups
is an array of dictionaries,
// each dictionary maps
from warning type to array of warning messages.
var groups = createArrayOfDictionaries(SeverityColors.length);
for (var i=
0; i<Warnings.length; i++) {
var w = Warnings[i][
0];
var s = WarnPatternsSeverity[w];
var k = w.toString();
if (!(k
in groups[s]))
groups[s][k] = [];
groups[s][k].push(Warnings[i]);
}
return groups;
}
function groupWarningsByProject() {
var groups = createArrayOfDictionaries(ProjectNames.length);
for (var i=
0; i<Warnings.length; i++) {
var w = Warnings[i][
0];
var p = Warnings[i][
1];
var k = w.toString();
if (!(k
in groups[p]))
groups[p][k] = [];
groups[p][k].push(Warnings[i]);
}
return groups;
}
var GlobalAnchor =
0;
function createWarningSection(header, color, group) {
var result =
"";
var groupKeys = [];
var totalMessages =
0;
for (var k
in group) {
totalMessages += group[k].length;
groupKeys.push([k, WarnPatternsSeverity[parseInt(k)], group[k].length]);
}
groupKeys.sort(bySeverityMessageCount);
for (var idx=
0; idx<groupKeys.length; idx++) {
var k = groupKeys[idx][
0];
var messages = group[k];
var w = parseInt(k);
var wcolor = SeverityColors[WarnPatternsSeverity[w]];
var description = WarnPatternsDescription[w];
if (description.length ==
0)
description =
"???";
GlobalAnchor +=
1;
result +=
"<table class='t1'><tr bgcolor='" + wcolor +
"'><td>" +
"<button class='bt' id='" + GlobalAnchor +
"_mark" +
"' onclick='expand(\\"" + GlobalAnchor + "\\
");'>" +
"⊕</button> " +
description +
" (" + messages.length +
")</td></tr></table>";
result +=
"<div id='" + GlobalAnchor +
"' style='display:none;'><table class='t1'>";
var c =
0;
messages.sort(compareMessages);
if (FlagPlatform ==
"chrome") {
for (var i=
0; i<messages.length; i++) {
result +=
"<tr><td class='c" + c +
"'>" +
addURLToLine(WarningMessages[messages[i][
2]], WarningLinks[messages[i][
3]]) +
"</td></tr>";
c =
1 - c;
}
}
else {
for (var i=
0; i<messages.length; i++) {
result +=
"<tr><td class='c" + c +
"'>" +
addURL(WarningMessages[messages[i][
2]]) +
"</td></tr>";
c =
1 - c;
}
}
result +=
"</table></div>";
}
if (result.length >
0) {
return "<br><span style='background-color:" + color +
"'><b>" +
header +
": " + totalMessages +
"</b></span><blockquote><table class='t1'>" +
result +
"</table></blockquote>";
}
return ""; // empty section
}
function generateSectionsBySeverity() {
var result =
"";
var groups = groupWarningsBySeverity();
for (s=
0; s<SeverityColors.length; s++) {
result += createWarningSection(SeverityHeaders[s], SeverityColors[s],
groups[s]);
}
return result;
}
function generateSectionsByProject() {
var result =
"";
var groups = groupWarningsByProject();
for (i=
0; i<groups.length; i++) {
result += createWarningSection(ProjectNames[i],
'lightgrey', groups[i]);
}
return result;
}
function groupWarnings(generator) {
GlobalAnchor =
0;
var e = document.getElementById(
"warning_groups");
e.innerHTML = generator();
}
function groupBySeverity() {
groupWarnings(generateSectionsBySeverity);
}
function groupByProject() {
groupWarnings(generateSectionsByProject);
}
"""
# Emit a JavaScript const number
def emit_const_number(name, value, writer):
writer(
'const ' + name +
' = ' + str(value) +
';')
# Emit a JavaScript const string
def emit_const_string(name, value, writer):
writer(
'const ' + name +
' = "' + escape_string(value) +
'";')
# Emit a JavaScript const integer array.
def emit_const_int_array(name, array, writer):
writer(
'const ' + name +
' = [')
for item
in array:
writer(str(item) +
',')
writer(
'];')
# Emit a JavaScript const string array.
def emit_const_string_array(name, array, writer):
writer(
'const ' + name +
' = [')
for item
in array:
writer(
'"' + strip_escape_string(item) +
'",')
writer(
'];')
# Emit a JavaScript const string array for HTML.
def emit_const_html_string_array(name, array, writer):
writer(
'const ' + name +
' = [')
for item
in array:
writer(
'"' + html.escape(strip_escape_string(item)) +
'",')
writer(
'];')
# Emit a JavaScript const object array.
def emit_const_object_array(name, array, writer):
writer(
'const ' + name +
' = [')
for item
in array:
writer(str(item) +
',')
writer(
'];')
def emit_js_data(writer, flags, warning_messages, warning_links,
warning_records, warn_patterns, project_names):
"""Dump dynamic HTML page's static JavaScript data."""
emit_const_string(
'FlagPlatform', flags.platform, writer)
emit_const_string(
'FlagURL', flags.url, writer)
emit_const_string(
'FlagSeparator', flags.separator, writer)
emit_const_number(
'LimitWarningsPerFile', LIMIT_WARNINGS_PER_FILE, writer)
emit_const_number(
'LimitPercentWarnings', LIMIT_PERCENT_WARNINGS, writer)
emit_const_string_array(
'SeverityColors', [s.color
for s
in Severity.levels],
writer)
emit_const_string_array(
'SeverityHeaders',
[s.header
for s
in Severity.levels], writer)
emit_const_string_array(
'SeverityColumnHeaders',
[s.column_header
for s
in Severity.levels], writer)
emit_const_string_array(
'ProjectNames', project_names, writer)
# pytype: disable=attribute-error
emit_const_int_array(
'WarnPatternsSeverity',
[w[
'severity'].value
for w
in warn_patterns], writer)
# pytype: enable=attribute-error
emit_const_html_string_array(
'WarnPatternsDescription',
[w[
'description']
for w
in warn_patterns],
writer)
emit_const_html_string_array(
'WarningMessages', warning_messages, writer)
emit_const_object_array(
'Warnings', warning_records, writer)
if flags.platform ==
'chrome':
emit_const_html_string_array(
'WarningLinks', warning_links, writer)
DRAW_TABLE_JAVASCRIPT =
"""
google.charts.load(
'current', {
'packages':[
'table']});
google.charts.setOnLoadCallback(genTables);
function genSelectedProjectsTable() {
var data = new google.visualization.DataTable();
data.addColumn(
'string', StatsHeader[
0]);
for (var i=
1; i<StatsHeader.length; i++) {
data.addColumn(
'number', StatsHeader[i]);
}
data.addRows(StatsRows);
for (var i=
0; i<StatsRows.length; i++) {
for (var j=
0; j<StatsHeader.length; j++) {
data.setProperty(i, j,
'style',
'border:1px solid black;');
}
}
var table = new google.visualization.Table(
document.getElementById(
'selected_projects_section'));
table.draw(data, {allowHtml:
true, alternatingRowStyle:
true});
}
//
Global TopDirs
and TopFiles are computed later by genTables.
window.TopDirs = [];
window.TopFiles = [];
function computeTopDirsFiles() {
var numWarnings = WarningMessages.length;
var warningsOfFiles = {};
var warningsOfDirs = {};
var subDirs = {};
function addOneWarning(map, key, type, unique) {
function increaseCounter(idx) {
map[idx] =
1 + ((idx
in map) ? map[idx] :
0);
}
increaseCounter(key)
if (type !=
"") {
increaseCounter(type +
" " + key)
if (unique) {
increaseCounter(type +
" *")
}
}
}
for (var i =
0; i < numWarnings; i++) {
var message = WarningMessages[i]
var file = message.replace(/:.*/,
"");
var warningType = message.endsWith(
"]") ? message.replace(/.*\[/,
"[") :
"";
addOneWarning(warningsOfFiles, file, warningType,
true);
var dirs = file.split(
"/");
var dir = dirs[
0];
addOneWarning(warningsOfDirs, dir, warningType,
true);
for (var d =
1; d < dirs.length -
1; d++) {
var subDir = dir +
"/" + dirs[d];
if (!(dir
in subDirs)) {
subDirs[dir] = {};
}
subDirs[dir][subDir] =
1;
dir = subDir;
addOneWarning(warningsOfDirs, dir, warningType,
false);
}
}
var minDirWarnings = numWarnings*(LimitPercentWarnings/
100);
var minFileWarnings = Math.min(LimitWarningsPerFile, minDirWarnings);
// Each row
in TopDirs
and TopFiles has
// [index, {v:<num_of_warnings>, f:<percent>}, file_or_dir_name]
function countWarnings(minWarnings, warningsOf, isDir) {
var rows = [];
for (var name
in warningsOf) {
if (isDir && name
in subDirs && Object.keys(subDirs[name]).length <
2) {
continue; // skip a directory
if it has only one subdir
}
var count = warningsOf[name];
if (count >= minWarnings) {
name = isDir ? (name +
"/...") : name;
var percent = (
100*count/numWarnings).toFixed(
1);
var countFormat = count +
' (' + percent +
'%)';
rows.push([
0, {v:count, f:countFormat}, name]);
}
}
rows.sort((a,b) => b[
1].v - a[
1].v);
for (var i=
0; i<rows.length; i++) {
rows[i][
0] = i;
}
return rows;
}
TopDirs = countWarnings(minDirWarnings, warningsOfDirs,
true);
TopFiles = countWarnings(minFileWarnings, warningsOfFiles,
false);
}
function genTopDirsFilesTables() {
computeTopDirsFiles();
function addTable(name, divName, rows, clickFunction) {
var data = new google.visualization.DataTable();
data.addColumn(
"number",
"index"); //
not shown
in view
data.addColumn(
"number",
"# of warnings");
data.addColumn(
"string", name);
data.addRows(rows);
var formatter = new google.visualization.PatternFormat(
'<p onclick="' + clickFunction +
'({0})">{2}</p>');
formatter.format(data, [
0,
1,
2],
2);
var view = new google.visualization.DataView(data);
view.setColumns([
1,
2]); // hide the index column
var table = new google.visualization.Table(
document.getElementById(divName));
table.draw(view, {allowHtml:
true, alternatingRowStyle:
true});
}
addTable(
"[Warning Type] Directory",
"top_dirs_table", TopDirs,
"selectDir");
addTable(
"[Warning Type] File",
"top_files_table", TopFiles,
"selectFile");
}
function selectDirFile(idx, rows, dirFile) {
if (rows.length <= idx) {
return;
}
var name = rows[idx][
2];
var type =
"";
if (name.startsWith(
"[")) {
type =
" " + name.replace(/ .*/,
"");
name = name.replace(/.* /,
"");
}
var spanName =
"selected_" + dirFile +
"_name";
document.getElementById(spanName).innerHTML = name + type;
var divName =
"selected_" + dirFile +
"_warnings";
var numWarnings = rows[idx][
1].v;
var prefix = name.replace(/\\.\\.\\.$/,
"");
var data = new google.visualization.DataTable();
data.addColumn(
'string', numWarnings + type +
' warnings in ' + name);
var getWarningMessage = (FlagPlatform ==
"chrome")
? ((x) => addURLToLine(WarningMessages[Warnings[x][
2]],
WarningLinks[Warnings[x][
3]]))
: ((x) => addURL(WarningMessages[Warnings[x][
2]]));
for (var i =
0; i < Warnings.length; i++) {
if ((prefix.startsWith(
"*") || WarningMessages[Warnings[i][
2]].startsWith(prefix)) &&
(type ==
"" || WarningMessages[Warnings[i][
2]].endsWith(type))) {
data.addRow([getWarningMessage(i)]);
}
}
var table = new google.visualization.Table(
document.getElementById(divName));
table.draw(data, {allowHtml:
true, alternatingRowStyle:
true});
}
function selectDir(idx) {
selectDirFile(idx, TopDirs,
"directory")
}
function selectFile(idx) {
selectDirFile(idx, TopFiles,
"file");
}
function genTables() {
genSelectedProjectsTable();
if (WarningMessages.length >
1) {
genTopDirsFilesTables();
}
}
"""
def dump_boxed_section(writer, func):
writer(
'<div class="box">')
func()
writer(
'</div>')
def dump_section_header(writer, table_name, section_title):
writer(
'<h3><b><button id="' + table_name +
'_mark" class="bt"\n' +
' onclick="expand(\'' + table_name + '\
');">⊕</button></b>\n' +
section_title +
'</h3>')
def dump_table_section(writer, table_name, section_title):
dump_section_header(writer, table_name, section_title)
writer(
'<div id="' + table_name +
'" style="display:none;"></div>')
def dump_dir_file_section(writer, dir_file, table_name, section_title):
section_name =
'top_' + dir_file +
'_section'
dump_section_header(writer, section_name, section_title)
writer(
'<div id="' + section_name +
'" style="display:none;">')
writer(
'<div id="' + table_name +
'"></div>')
def subsection():
subsection_name =
'selected_' + dir_file +
'_warnings'
subsection_title = (
'Warnings in <span id="selected_' + dir_file +
'_name">(click a ' + dir_file +
' in the above table)</span>')
dump_section_header(writer, subsection_name, subsection_title)
writer(
'<div id="' + subsection_name +
'" style="display:none;"></div>')
dump_boxed_section(writer, subsection)
writer(
'</div>')
# HTML output has the following major div elements:
# selected_projects_section
# top_directory_section
# top_dirs_table
# selected_directory_warnings
# top_file_section
# top_files_table
# selected_file_warnings
# all_warnings_section
# warning_groups
# fixed_warnings
def dump_html(flags, output_stream, warning_messages, warning_links,
warning_records, header_str, warn_patterns, project_names):
"""Dump the flags output to output_stream."""
writer = make_writer(output_stream)
dump_html_prologue(
'Warnings for ' + header_str, writer, warn_patterns,
project_names)
dump_stats(writer, warn_patterns)
writer(
'<br><br>Press ⊕ to show section content,'
' and ⊖ to hide the content.')
def section1():
dump_table_section(writer,
'selected_projects_section',
'Number of warnings in preselected project directories')
def section2():
dump_dir_file_section(
writer,
'directory',
'top_dirs_table',
'Directories/Warnings with at least ' +
str(LIMIT_PERCENT_WARNINGS) +
'% of all cases')
def section3():
dump_dir_file_section(
writer,
'file',
'top_files_table',
'Files/Warnings with at least ' +
str(LIMIT_PERCENT_WARNINGS) +
'% of all or ' +
str(LIMIT_WARNINGS_PER_FILE) +
' cases')
def section4():
writer(
'<script>')
emit_js_data(writer, flags, warning_messages, warning_links,
warning_records, warn_patterns, project_names)
writer(SCRIPTS_FOR_WARNING_GROUPS)
writer(
'</script>')
dump_section_header(writer,
'all_warnings_section',
'All warnings grouped by severities or projects')
writer(
'<div id="all_warnings_section" style="display:none;">')
emit_buttons(writer)
# Warning messages are grouped by severities or project names.
writer(
'<br><div id="warning_groups"></div>')
if flags.byproject:
writer(
'<script>groupByProject();</script>')
else:
writer(
'<script>groupBySeverity();</script>')
dump_fixed(writer, warn_patterns)
writer(
'</div>')
dump_boxed_section(writer, section1)
dump_boxed_section(writer, section2)
dump_boxed_section(writer, section3)
dump_boxed_section(writer, section4)
dump_html_epilogue(writer)
def write_html(flags, project_names, warn_patterns, html_path, warning_messages,
warning_links, warning_records, header_str):
"""Write warnings html file."""
if html_path:
with open(html_path,
'w')
as outf:
dump_html(flags, outf, warning_messages, warning_links, warning_records,
header_str, warn_patterns, project_names)
def write_out_csv(flags, warn_patterns, warning_messages, warning_links,
warning_records, header_str, project_names):
"""Write warnings csv file."""
if flags.csvpath:
with open(flags.csvpath,
'w')
as outf:
dump_csv(csv.writer(outf, lineterminator=
'\n'), warn_patterns)
if flags.csvwithdescription:
with open(flags.csvwithdescription,
'w')
as outf:
dump_csv_with_description(csv.writer(outf, lineterminator=
'\n'),
warning_records, warning_messages,
warn_patterns, project_names)
if flags.gencsv:
dump_csv(csv.writer(sys.stdout, lineterminator=
'\n'), warn_patterns)
else:
dump_html(flags, sys.stdout, warning_messages, warning_links,
warning_records, header_str, warn_patterns, project_names)