blob: 329f1e5524611897f5fc56be8fecc230df19c4fa [file] [log] [blame]
Chih-Hung Hsieh949205a2020-01-10 10:33:40 -08001# python3
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -08002# Copyright (C) 2019 The Android Open Source Project
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16"""Grep warnings messages and output HTML tables or warning counts in CSV.
17
18Default is to output warnings in HTML tables grouped by warning severity.
19Use option --byproject to output tables grouped by source file projects.
20Use option --gencsv to output warning counts in CSV format.
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -080021
22Default input file is build.log, which can be changed with the --log flag.
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -080023"""
24
25# List of important data structures and functions in this script.
26#
27# To parse and keep warning message in the input file:
28# severity: classification of message severity
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -080029# warn_patterns:
30# warn_patterns[w]['category'] tool that issued the warning, not used now
31# warn_patterns[w]['description'] table heading
32# warn_patterns[w]['members'] matched warnings from input
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -080033# warn_patterns[w]['patterns'] regular expressions to match warnings
34# warn_patterns[w]['projects'][p] number of warnings of pattern w in p
Chih-Hung Hsieh949205a2020-01-10 10:33:40 -080035# warn_patterns[w]['severity'] severity tuple
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -080036# project_list[p][0] project name
37# project_list[p][1] regular expression to match a project path
38# project_patterns[p] re.compile(project_list[p][1])
39# project_names[p] project_list[p][0]
40# warning_messages array of each warning message, without source url
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -080041# warning_links array of each warning code search link; for 'chrome'
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -080042# warning_records array of [idx to warn_patterns,
43# idx to project_names,
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -080044# idx to warning_messages,
45# idx to warning_links]
Chih-Hung Hsieh949205a2020-01-10 10:33:40 -080046# parse_input_file
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -080047#
48# To emit html page of warning messages:
49# flags: --byproject, --url, --separator
50# Old stuff for static html components:
51# html_script_style: static html scripts and styles
52# htmlbig:
53# dump_stats, dump_html_prologue, dump_html_epilogue:
54# emit_buttons:
55# dump_fixed
56# sort_warnings:
57# emit_stats_by_project:
58# all_patterns,
59# findproject, classify_warning
60# dump_html
61#
62# New dynamic HTML page's static JavaScript data:
63# Some data are copied from Python to JavaScript, to generate HTML elements.
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -080064# FlagPlatform flags.platform
65# FlagURL flags.url, used by 'android'
66# FlagSeparator flags.separator, used by 'android'
Chih-Hung Hsieh949205a2020-01-10 10:33:40 -080067# SeverityColors: list of colors for all severity levels
68# SeverityHeaders: list of headers for all severity levels
69# SeverityColumnHeaders: list of column_headers for all severity levels
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -080070# ProjectNames: project_names, or project_list[*][0]
71# WarnPatternsSeverity: warn_patterns[*]['severity']
72# WarnPatternsDescription: warn_patterns[*]['description']
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -080073# WarningMessages: warning_messages
74# Warnings: warning_records
75# StatsHeader: warning count table header row
76# StatsRows: array of warning count table rows
77#
78# New dynamic HTML page's dynamic JavaScript data:
79#
80# New dynamic HTML related function to emit data:
81# escape_string, strip_escape_string, emit_warning_arrays
82# emit_js_data():
83
84from __future__ import print_function
85import argparse
86import cgi
87import csv
88import io
89import multiprocessing
90import os
91import re
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -080092import sys
93
94# pylint:disable=relative-beyond-top-level
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -080095from . import android_project_list
96from . import chrome_project_list
97from . import cpp_warn_patterns as cpp_patterns
98from . import java_warn_patterns as java_patterns
99from . import make_warn_patterns as make_patterns
100from . import other_warn_patterns as other_patterns
101from . import tidy_warn_patterns as tidy_patterns
Chih-Hung Hsieh949205a2020-01-10 10:33:40 -0800102# pylint:disable=g-importing-member
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800103from .severity import Severity
104
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800105
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800106def parse_args(use_google3):
107 """Define and parse the args. Return the parse_args() result."""
108 parser = argparse.ArgumentParser(
109 description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
110 parser.add_argument('--capacitor_path', default='',
111 help='Save capacitor warning file to the passed absolute'
112 ' path')
113 # csvpath has a different naming than the above path because historically the
114 # original Android script used csvpath, so other scripts rely on it
115 parser.add_argument('--csvpath', default='',
116 help='Save CSV warning file to the passed path')
117 parser.add_argument('--gencsv', action='store_true',
118 help='Generate CSV file with number of various warnings')
119 parser.add_argument('--byproject', action='store_true',
120 help='Separate warnings in HTML output by project names')
121 parser.add_argument('--url', default='',
122 help='Root URL of an Android source code tree prefixed '
123 'before files in warnings')
124 parser.add_argument('--separator', default='?l=',
125 help='Separator between the end of a URL and the line '
126 'number argument. e.g. #')
127 parser.add_argument('--processes', default=multiprocessing.cpu_count(),
128 type=int,
129 help='Number of parallel processes to process warnings')
130 # Old Android build scripts call warn.py without --platform,
131 # so the default platform is set to 'android'.
132 parser.add_argument('--platform', default='android',
133 choices=['chrome', 'android'],
134 help='Platform of the build log')
135 # Old Android build scripts call warn.py with only a build.log file path.
136 parser.add_argument('--log', help='Path to build log file')
137 parser.add_argument(dest='buildlog', metavar='build.log',
138 default='build.log', nargs='?',
139 help='Path to build.log file')
140 flags = parser.parse_args()
141 if not flags.log:
142 flags.log = flags.buildlog
143 if not use_google3 and not os.path.exists(flags.log):
144 sys.exit('Cannot find log file: ' + flags.log)
145 return flags
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800146
147
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800148def get_project_names(project_list):
149 """Get project_names from project_list."""
150 return [p[0] for p in project_list]
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800151
152
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800153html_head_scripts = """\
154 <script type="text/javascript">
155 function expand(id) {
156 var e = document.getElementById(id);
157 var f = document.getElementById(id + "_mark");
158 if (e.style.display == 'block') {
159 e.style.display = 'none';
160 f.innerHTML = '&#x2295';
161 }
162 else {
163 e.style.display = 'block';
164 f.innerHTML = '&#x2296';
165 }
166 };
167 function expandCollapse(show) {
168 for (var id = 1; ; id++) {
169 var e = document.getElementById(id + "");
170 var f = document.getElementById(id + "_mark");
171 if (!e || !f) break;
172 e.style.display = (show ? 'block' : 'none');
173 f.innerHTML = (show ? '&#x2296' : '&#x2295');
174 }
175 };
176 </script>
177 <style type="text/css">
178 th,td{border-collapse:collapse; border:1px solid black;}
179 .button{color:blue;font-size:110%;font-weight:bolder;}
180 .bt{color:black;background-color:transparent;border:none;outline:none;
181 font-size:140%;font-weight:bolder;}
182 .c0{background-color:#e0e0e0;}
183 .c1{background-color:#d0d0d0;}
184 .t1{border-collapse:collapse; width:100%; border:1px solid black;}
185 </style>
186 <script src="https://www.gstatic.com/charts/loader.js"></script>
187"""
188
189
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800190def make_writer(output_stream):
191
192 def writer(text):
193 return output_stream.write(text + '\n')
194
195 return writer
196
197
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800198def html_big(param):
199 return '<font size="+2">' + param + '</font>'
200
201
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800202def dump_html_prologue(title, writer, warn_patterns, project_names):
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800203 writer('<html>\n<head>')
204 writer('<title>' + title + '</title>')
205 writer(html_head_scripts)
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800206 emit_stats_by_project(writer, warn_patterns, project_names)
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800207 writer('</head>\n<body>')
208 writer(html_big(title))
209 writer('<p>')
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800210
211
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800212def dump_html_epilogue(writer):
213 writer('</body>\n</head>\n</html>')
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800214
215
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800216def sort_warnings(warn_patterns):
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800217 for i in warn_patterns:
218 i['members'] = sorted(set(i['members']))
219
220
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800221def create_warnings(warn_patterns, project_names):
222 """Creates warnings s.t.
223
224 warnings[p][s] is as specified in above docs.
225
226 Args:
227 warn_patterns: list of warning patterns for specified platform
228 project_names: list of project names
229
230 Returns:
231 2D warnings array where warnings[p][s] is # of warnings in project name p of
232 severity level s
233 """
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800234 # pylint:disable=g-complex-comprehension
Chih-Hung Hsieh949205a2020-01-10 10:33:40 -0800235 warnings = {p: {s.value: 0 for s in Severity.levels} for p in project_names}
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800236 for i in warn_patterns:
Chih-Hung Hsieh949205a2020-01-10 10:33:40 -0800237 s = i['severity'].value
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800238 for p in i['projects']:
239 warnings[p][s] += i['projects'][p]
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800240 return warnings
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800241
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800242
243def get_total_by_project(warnings, project_names):
244 """Returns dict, project as key and # warnings for that project as value."""
245 # pylint:disable=g-complex-comprehension
246 return {
Chih-Hung Hsieh949205a2020-01-10 10:33:40 -0800247 p: sum(warnings[p][s.value] for s in Severity.levels)
248 for p in project_names
249 }
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800250
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800251
252def get_total_by_severity(warnings, project_names):
253 """Returns dict, severity as key and # warnings of that severity as value."""
254 # pylint:disable=g-complex-comprehension
255 return {
Chih-Hung Hsieh949205a2020-01-10 10:33:40 -0800256 s.value: sum(warnings[p][s.value] for p in project_names)
257 for s in Severity.levels
258 }
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800259
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800260
261def emit_table_header(total_by_severity):
262 """Returns list of HTML-formatted content for severity stats."""
263
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800264 stats_header = ['Project']
Chih-Hung Hsieh949205a2020-01-10 10:33:40 -0800265 for s in Severity.levels:
266 if total_by_severity[s.value]:
267 stats_header.append(
268 '<span style=\'background-color:{}\'>{}</span>'.format(
269 s.color, s.column_header))
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800270 stats_header.append('TOTAL')
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800271 return stats_header
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800272
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800273
274def emit_row_counts_per_project(warnings, total_by_project, total_by_severity,
275 project_names):
276 """Returns total project warnings and row of stats for each project.
277
278 Args:
279 warnings: output of create_warnings(warn_patterns, project_names)
280 total_by_project: output of get_total_by_project(project_names)
281 total_by_severity: output of get_total_by_severity(project_names)
282 project_names: list of project names
283
284 Returns:
285 total_all_projects, the total number of warnings over all projects
286 stats_rows, a 2d list where each row is [Project Name, <severity counts>,
287 total # warnings for this project]
288 """
289
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800290 total_all_projects = 0
291 stats_rows = []
292 for p in project_names:
293 if total_by_project[p]:
294 one_row = [p]
Chih-Hung Hsieh949205a2020-01-10 10:33:40 -0800295 for s in Severity.levels:
296 if total_by_severity[s.value]:
297 one_row.append(warnings[p][s.value])
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800298 one_row.append(total_by_project[p])
299 stats_rows.append(one_row)
300 total_all_projects += total_by_project[p]
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800301 return total_all_projects, stats_rows
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800302
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800303
304def emit_row_counts_per_severity(total_by_severity, stats_header, stats_rows,
305 total_all_projects, writer):
306 """Emits stats_header and stats_rows as specified above.
307
308 Args:
309 total_by_severity: output of get_total_by_severity()
310 stats_header: output of emit_table_header()
311 stats_rows: output of emit_row_counts_per_project()
312 total_all_projects: output of emit_row_counts_per_project()
313 writer: writer returned by make_writer(output_stream)
314 """
315
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800316 total_all_severities = 0
317 one_row = ['<b>TOTAL</b>']
Chih-Hung Hsieh949205a2020-01-10 10:33:40 -0800318 for s in Severity.levels:
319 if total_by_severity[s.value]:
320 one_row.append(total_by_severity[s.value])
321 total_all_severities += total_by_severity[s.value]
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800322 one_row.append(total_all_projects)
323 stats_rows.append(one_row)
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800324 writer('<script>')
325 emit_const_string_array('StatsHeader', stats_header, writer)
326 emit_const_object_array('StatsRows', stats_rows, writer)
327 writer(draw_table_javascript)
328 writer('</script>')
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800329
330
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800331def emit_stats_by_project(writer, warn_patterns, project_names):
332 """Dump a google chart table of warnings per project and severity."""
333
334 warnings = create_warnings(warn_patterns, project_names)
335 total_by_project = get_total_by_project(warnings, project_names)
336 total_by_severity = get_total_by_severity(warnings, project_names)
337 stats_header = emit_table_header(total_by_severity)
338 total_all_projects, stats_rows = \
339 emit_row_counts_per_project(warnings, total_by_project, total_by_severity, project_names)
340 emit_row_counts_per_severity(total_by_severity, stats_header, stats_rows,
341 total_all_projects, writer)
342
343
344def dump_stats(writer, warn_patterns):
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800345 """Dump some stats about total number of warnings and such."""
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800346
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800347 known = 0
348 skipped = 0
349 unknown = 0
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800350 sort_warnings(warn_patterns)
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800351 for i in warn_patterns:
Chih-Hung Hsieh949205a2020-01-10 10:33:40 -0800352 if i['severity'] == Severity.UNMATCHED:
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800353 unknown += len(i['members'])
354 elif i['severity'] == Severity.SKIP:
355 skipped += len(i['members'])
356 else:
357 known += len(i['members'])
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800358 writer('Number of classified warnings: <b>' + str(known) + '</b><br>')
359 writer('Number of skipped warnings: <b>' + str(skipped) + '</b><br>')
360 writer('Number of unclassified warnings: <b>' + str(unknown) + '</b><br>')
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800361 total = unknown + known + skipped
362 extra_msg = ''
363 if total < 1000:
364 extra_msg = ' (low count may indicate incremental build)'
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800365 writer('Total number of warnings: <b>' + str(total) + '</b>' + extra_msg)
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800366
367
368# New base table of warnings, [severity, warn_id, project, warning_message]
369# Need buttons to show warnings in different grouping options.
370# (1) Current, group by severity, id for each warning pattern
371# sort by severity, warn_id, warning_message
372# (2) Current --byproject, group by severity,
373# id for each warning pattern + project name
374# sort by severity, warn_id, project, warning_message
375# (3) New, group by project + severity,
376# id for each warning pattern
377# sort by project, severity, warn_id, warning_message
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800378def emit_buttons(writer):
379 writer('<button class="button" onclick="expandCollapse(1);">'
380 'Expand all warnings</button>\n'
381 '<button class="button" onclick="expandCollapse(0);">'
382 'Collapse all warnings</button>\n'
383 '<button class="button" onclick="groupBySeverity();">'
384 'Group warnings by severity</button>\n'
385 '<button class="button" onclick="groupByProject();">'
386 'Group warnings by project</button><br>')
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800387
388
389def all_patterns(category):
390 patterns = ''
391 for i in category['patterns']:
392 patterns += i
393 patterns += ' / '
394 return patterns
395
396
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800397def dump_fixed(writer, warn_patterns):
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800398 """Show which warnings no longer occur."""
399 anchor = 'fixed_warnings'
400 mark = anchor + '_mark'
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800401 writer('\n<br><p style="background-color:lightblue"><b>'
402 '<button id="' + mark + '" '
403 'class="bt" onclick="expand(\'' + anchor + '\');">'
404 '&#x2295</button> Fixed warnings. '
405 'No more occurrences. Please consider turning these into '
406 'errors if possible, before they are reintroduced in to the build'
407 ':</b></p>')
408 writer('<blockquote>')
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800409 fixed_patterns = []
410 for i in warn_patterns:
411 if not i['members']:
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800412 fixed_patterns.append(i['description'] + ' (' + all_patterns(i) + ')')
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800413 fixed_patterns = sorted(fixed_patterns)
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800414 writer('<div id="' + anchor + '" style="display:none;"><table>')
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800415 cur_row_class = 0
416 for text in fixed_patterns:
417 cur_row_class = 1 - cur_row_class
418 # remove last '\n'
419 t = text[:-1] if text[-1] == '\n' else text
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800420 writer('<tr><td class="c' + str(cur_row_class) + '">' + t + '</td></tr>')
421 writer('</table></div>')
422 writer('</blockquote>')
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800423
424
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800425def write_severity(csvwriter, sev, kind, warn_patterns):
426 """Count warnings of given severity and write CSV entries to writer."""
427 total = 0
428 for pattern in warn_patterns:
429 if pattern['severity'] == sev and pattern['members']:
430 n = len(pattern['members'])
431 total += n
432 warning = kind + ': ' + (pattern['description'] or '?')
433 csvwriter.writerow([n, '', warning])
434 # print number of warnings for each project, ordered by project name
435 projects = sorted(pattern['projects'].keys())
436 for project in projects:
437 csvwriter.writerow([pattern['projects'][project], project, warning])
438 csvwriter.writerow([total, '', kind + ' warnings'])
439 return total
440
441
442def dump_csv(csvwriter, warn_patterns):
443 """Dump number of warnings in CSV format to writer."""
444 sort_warnings(warn_patterns)
445 total = 0
446 for s in Severity.levels:
447 total += write_severity(csvwriter, s, s.column_header, warn_patterns)
448 csvwriter.writerow([total, '', 'All warnings'])
449
450
451def find_project_index(line, project_patterns):
452 for i, p in enumerate(project_patterns):
453 if p.match(line):
454 return i
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800455 return -1
456
457
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800458def classify_one_warning(warning, link, results, project_patterns,
459 warn_patterns):
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800460 """Classify one warning line."""
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800461 for i, w in enumerate(warn_patterns):
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800462 for cpat in w['compiled_patterns']:
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800463 if cpat.match(warning):
464 p = find_project_index(warning, project_patterns)
465 results.append([warning, link, i, p])
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800466 return
467 else:
468 # If we end up here, there was a problem parsing the log
469 # probably caused by 'make -j' mixing the output from
470 # 2 or more concurrent compiles
471 pass
472
473
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800474def remove_prefix(s, sub):
475 """Remove everything before last occurrence of substring sub in string s."""
476 if sub in s:
477 inc_sub = s.rfind(sub)
478 return s[inc_sub:]
479 return s
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800480
481
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800482# TODO(emmavukelj): Don't have any generate_*_cs_link functions call
483# normalize_path a second time (the first time being in parse_input_file)
484def generate_cs_link(warning_line, flags, android_root=None):
485 if flags.platform == 'chrome':
486 return generate_chrome_cs_link(warning_line, flags)
487 if flags.platform == 'android':
488 return generate_android_cs_link(warning_line, flags, android_root)
489 return 'https://cs.corp.google.com/'
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800490
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800491
492def generate_android_cs_link(warning_line, flags, android_root):
493 """Generate the code search link for a warning line in Android."""
494 # max_splits=2 -> only 3 items
495 raw_path, line_number_str, _ = warning_line.split(':', 2)
496 normalized_path = normalize_path(raw_path, flags, android_root)
497 if not flags.url:
498 return normalized_path
499 link_path = flags.url + '/' + normalized_path
500 if line_number_str.isdigit():
501 link_path += flags.separator + line_number_str
502 return link_path
503
504
505def generate_chrome_cs_link(warning_line, flags):
506 """Generate the code search link for a warning line in Chrome."""
507 split_line = warning_line.split(':')
508 raw_path = split_line[0]
509 normalized_path = normalize_path(raw_path, flags)
510 link_base = 'https://cs.chromium.org/'
511 link_add = 'chromium'
512 link_path = None
513
514 # Basically just going through a few specific directory cases and specifying
515 # the proper behavior for that case. This list of cases was accumulated
516 # through trial and error manually going through the warnings.
517 #
518 # This code pattern of using case-specific "if"s instead of "elif"s looks
519 # possibly accidental and mistaken but it is intentional because some paths
520 # fall under several cases (e.g. third_party/lib/nghttp2_frame.c) and for
521 # those we want the most specific case to be applied. If there is reliable
522 # knowledge of exactly where these occur, this could be changed to "elif"s
523 # but there is no reliable set of paths falling under multiple cases at the
524 # moment.
525 if '/src/third_party' in raw_path:
526 link_path = remove_prefix(raw_path, '/src/third_party/')
527 if '/chrome_root/src_internal/' in raw_path:
528 link_path = remove_prefix(raw_path, '/chrome_root/src_internal/')
529 link_path = link_path[len('/chrome_root'):] # remove chrome_root
530 if '/chrome_root/src/' in raw_path:
531 link_path = remove_prefix(raw_path, '/chrome_root/src/')
532 link_path = link_path[len('/chrome_root'):] # remove chrome_root
533 if '/libassistant/' in raw_path:
534 link_add = 'eureka_internal/chromium/src'
535 link_base = 'https://cs.corp.google.com/' # internal data
536 link_path = remove_prefix(normalized_path, '/libassistant/')
537 if raw_path.startswith('gen/'):
538 link_path = '/src/out/Debug/gen/' + normalized_path
539 if '/gen/' in raw_path:
540 return '%s?q=file:%s' % (link_base, remove_prefix(normalized_path, '/gen/'))
541
542 if not link_path and (raw_path.startswith('src/') or
543 raw_path.startswith('src_internal/')):
544 link_path = '/%s' % raw_path
545
546 if not link_path: # can't find specific link, send a query
547 return '%s?q=file:%s' % (link_base, normalized_path)
548
549 line_number = int(split_line[1])
550 link = '%s%s%s?l=%d' % (link_base, link_add, link_path, line_number)
551 return link
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800552
553
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800554def find_warn_py_and_android_root(path):
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800555 """Return android source root path if warn.py is found."""
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800556 parts = path.split('/')
557 for idx in reversed(range(2, len(parts))):
558 root_path = '/'.join(parts[:idx])
559 # Android root directory should contain this script.
560 if os.path.exists(root_path + '/build/make/tools/warn.py'):
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800561 return root_path
562 return ''
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800563
564
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800565def find_android_root(buildlog):
566 """Guess android source root from common prefix of file paths."""
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800567 # Use the longest common prefix of the absolute file paths
568 # of the first 10000 warning messages as the android_root.
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800569 warning_lines = []
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800570 warning_pattern = re.compile('^/[^ ]*/[^ ]*: warning: .*')
571 count = 0
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800572 for line in buildlog:
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800573 if warning_pattern.match(line):
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800574 warning_lines.append(line)
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800575 count += 1
576 if count > 9999:
577 break
578 # Try to find warn.py and use its location to find
579 # the source tree root.
580 if count < 100:
581 path = os.path.normpath(re.sub(':.*$', '', line))
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800582 android_root = find_warn_py_and_android_root(path)
583 if android_root:
584 return android_root
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800585 # Do not use common prefix of a small number of paths.
586 if count > 10:
Chih-Hung Hsieh949205a2020-01-10 10:33:40 -0800587 # pytype: disable=wrong-arg-types
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800588 root_path = os.path.commonprefix(warning_lines)
Chih-Hung Hsieh949205a2020-01-10 10:33:40 -0800589 # pytype: enable=wrong-arg-types
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800590 if len(root_path) > 2 and root_path[len(root_path) - 1] == '/':
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800591 return root_path[:-1]
592 return ''
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800593
594
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800595def remove_android_root_prefix(path, android_root):
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800596 """Remove android_root prefix from path if it is found."""
597 if path.startswith(android_root):
598 return path[1 + len(android_root):]
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800599 return path
600
601
602def normalize_path(path, flags, android_root=None):
603 """Normalize file path relative to src/ or src-internal/ directory."""
604 path = os.path.normpath(path)
605
606 if flags.platform == 'android':
607 if android_root:
608 return remove_android_root_prefix(path, android_root)
609 return path
610
611 # Remove known prefix of root path and normalize the suffix.
612 idx = path.find('chrome_root/')
613 if idx >= 0:
614 # remove chrome_root/, we want path relative to that
615 return path[idx + len('chrome_root/'):]
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800616 else:
617 return path
618
619
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800620def normalize_warning_line(line, flags, android_root=None):
621 """Normalize file path relative to src directory in a warning line."""
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800622 line = re.sub(u'[\u2018\u2019]', '\'', line)
623 # replace non-ASCII chars to spaces
624 line = re.sub(u'[^\x00-\x7f]', ' ', line)
625 line = line.strip()
626 first_column = line.find(':')
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800627 return normalize_path(line[:first_column], flags,
628 android_root) + line[first_column:]
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800629
630
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800631def parse_input_file_chrome(infile, flags):
632 """Parse Chrome input file, collect parameters and warning lines."""
633 platform_version = 'unknown'
634 board_name = 'unknown'
635 architecture = 'unknown'
636
637 # only handle warning lines of format 'file_path:line_no:col_no: warning: ...'
638 chrome_warning_pattern = r'^[^ ]*/[^ ]*:[0-9]+:[0-9]+: warning: .*'
639
640 warning_pattern = re.compile(chrome_warning_pattern)
641
642 # Collect all unique warning lines
643 # Remove the duplicated warnings save ~8% of time when parsing
644 # one typical build log than before
645 unique_warnings = dict()
646 for line in infile:
647 if warning_pattern.match(line):
648 normalized_line = normalize_warning_line(line, flags)
649 if normalized_line not in unique_warnings:
650 unique_warnings[normalized_line] = generate_cs_link(line, flags)
651 elif (platform_version == 'unknown' or board_name == 'unknown' or
652 architecture == 'unknown'):
653 m = re.match(r'.+Package:.+chromeos-base/chromeos-chrome-', line)
654 if m is not None:
655 platform_version = 'R' + line.split('chrome-')[1].split('_')[0]
656 continue
657 m = re.match(r'.+Source\sunpacked\sin\s(.+)', line)
658 if m is not None:
659 board_name = m.group(1).split('/')[2]
660 continue
661 m = re.match(r'.+USE:\s*([^\s]*).*', line)
662 if m is not None:
663 architecture = m.group(1)
664 continue
665
666 header_str = '%s - %s - %s' % (platform_version, board_name, architecture)
667 return unique_warnings, header_str
668
669
670def add_normalized_line_to_warnings(line, flags, android_root, unique_warnings):
671 """Parse/normalize path, updating warning line and add to warnings dict."""
672 normalized_line = normalize_warning_line(line, flags, android_root)
673 if normalized_line not in unique_warnings:
674 unique_warnings[normalized_line] = generate_cs_link(line, flags,
675 android_root)
676 return unique_warnings
677
678
679def parse_input_file_android(infile, flags):
680 """Parse Android input file, collect parameters and warning lines."""
681 platform_version = 'unknown'
682 target_product = 'unknown'
683 target_variant = 'unknown'
684 android_root = find_android_root(infile)
685 infile.seek(0)
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800686
687 # rustc warning messages have two lines that should be combined:
688 # warning: description
689 # --> file_path:line_number:column_number
690 # Some warning messages have no file name:
691 # warning: macro replacement list ... [bugprone-macro-parentheses]
692 # Some makefile warning messages have no line number:
693 # some/path/file.mk: warning: description
694 # C/C++ compiler warning messages have line and column numbers:
695 # some/path/file.c:line_number:column_number: warning: description
696 warning_pattern = re.compile('(^[^ ]*/[^ ]*: warning: .*)|(^warning: .*)')
697 warning_without_file = re.compile('^warning: .*')
698 rustc_file_position = re.compile('^[ ]+--> [^ ]*/[^ ]*:[0-9]+:[0-9]+')
699
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800700 # Collect all unique warning lines
701 # Remove the duplicated warnings save ~8% of time when parsing
702 # one typical build log than before
703 unique_warnings = dict()
704 line_counter = 0
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800705 prev_warning = ''
706 for line in infile:
707 if prev_warning:
708 if rustc_file_position.match(line):
709 # must be a rustc warning, combine 2 lines into one warning
710 line = line.strip().replace('--> ', '') + ': ' + prev_warning
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800711 unique_warnings = add_normalized_line_to_warnings(
712 line, flags, android_root, unique_warnings)
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800713 prev_warning = ''
714 continue
715 # add prev_warning, and then process the current line
716 prev_warning = 'unknown_source_file: ' + prev_warning
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800717 unique_warnings = add_normalized_line_to_warnings(
718 prev_warning, flags, android_root, unique_warnings)
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800719 prev_warning = ''
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800720
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800721 if warning_pattern.match(line):
722 if warning_without_file.match(line):
723 # save this line and combine it with the next line
724 prev_warning = line
725 else:
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800726 unique_warnings = add_normalized_line_to_warnings(
727 line, flags, android_root, unique_warnings)
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800728 continue
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800729
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800730 if line_counter < 100:
731 # save a little bit of time by only doing this for the first few lines
732 line_counter += 1
733 m = re.search('(?<=^PLATFORM_VERSION=).*', line)
734 if m is not None:
735 platform_version = m.group(0)
736 m = re.search('(?<=^TARGET_PRODUCT=).*', line)
737 if m is not None:
738 target_product = m.group(0)
739 m = re.search('(?<=^TARGET_BUILD_VARIANT=).*', line)
740 if m is not None:
741 target_variant = m.group(0)
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800742 m = re.search('(?<=^TOP=).*', line)
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800743 if m is not None:
744 android_root = m.group(1)
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800745
746 if android_root:
747 new_unique_warnings = dict()
748 for warning_line in unique_warnings:
749 normalized_line = normalize_warning_line(warning_line, flags,
750 android_root)
751 new_unique_warnings[normalized_line] = generate_android_cs_link(
752 warning_line, flags, android_root)
753 unique_warnings = new_unique_warnings
754
755 header_str = '%s - %s - %s' % (platform_version, target_product,
756 target_variant)
757 return unique_warnings, header_str
758
759
760def parse_input_file(infile, flags):
761 if flags.platform == 'chrome':
762 return parse_input_file_chrome(infile, flags)
763 if flags.platform == 'android':
764 return parse_input_file_android(infile, flags)
765 raise RuntimeError('parse_input_file not defined for platform %s' %
766 flags.platform)
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800767
768
769# Return s with escaped backslash and quotation characters.
770def escape_string(s):
771 return s.replace('\\', '\\\\').replace('"', '\\"')
772
773
774# Return s without trailing '\n' and escape the quotation characters.
775def strip_escape_string(s):
776 if not s:
777 return s
778 s = s[:-1] if s[-1] == '\n' else s
779 return escape_string(s)
780
781
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800782def emit_warning_array(name, writer, warn_patterns):
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800783 writer('var warning_{} = ['.format(name))
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800784 for w in warn_patterns:
785 if name == 'severity':
786 writer('{},'.format(w[name].value))
787 else:
788 writer('{},'.format(w[name]))
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800789 writer('];')
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800790
791
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800792def emit_warning_arrays(writer, warn_patterns):
793 emit_warning_array('severity', writer, warn_patterns)
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800794 writer('var warning_description = [')
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800795 for w in warn_patterns:
796 if w['members']:
797 writer('"{}",'.format(escape_string(w['description'])))
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800798 else:
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800799 writer('"",') # no such warning
800 writer('];')
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800801
802
803scripts_for_warning_groups = """
804 function compareMessages(x1, x2) { // of the same warning type
805 return (WarningMessages[x1[2]] <= WarningMessages[x2[2]]) ? -1 : 1;
806 }
807 function byMessageCount(x1, x2) {
808 return x2[2] - x1[2]; // reversed order
809 }
810 function bySeverityMessageCount(x1, x2) {
811 // orer by severity first
812 if (x1[1] != x2[1])
813 return x1[1] - x2[1];
814 return byMessageCount(x1, x2);
815 }
816 const ParseLinePattern = /^([^ :]+):(\\d+):(.+)/;
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800817 function addURL(line) { // used by Android
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800818 if (FlagURL == "") return line;
819 if (FlagSeparator == "") {
820 return line.replace(ParseLinePattern,
821 "<a target='_blank' href='" + FlagURL + "/$1'>$1</a>:$2:$3");
822 }
823 return line.replace(ParseLinePattern,
824 "<a target='_blank' href='" + FlagURL + "/$1" + FlagSeparator +
825 "$2'>$1:$2</a>:$3");
826 }
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800827 function addURLToLine(line, link) { // used by Chrome
828 let line_split = line.split(":");
829 let path = line_split.slice(0,3).join(":");
830 let msg = line_split.slice(3).join(":");
831 let html_link = `<a target="_blank" href="${link}">${path}</a>${msg}`;
832 return html_link;
833 }
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800834 function createArrayOfDictionaries(n) {
835 var result = [];
836 for (var i=0; i<n; i++) result.push({});
837 return result;
838 }
839 function groupWarningsBySeverity() {
840 // groups is an array of dictionaries,
841 // each dictionary maps from warning type to array of warning messages.
842 var groups = createArrayOfDictionaries(SeverityColors.length);
843 for (var i=0; i<Warnings.length; i++) {
844 var w = Warnings[i][0];
845 var s = WarnPatternsSeverity[w];
846 var k = w.toString();
847 if (!(k in groups[s]))
848 groups[s][k] = [];
849 groups[s][k].push(Warnings[i]);
850 }
851 return groups;
852 }
853 function groupWarningsByProject() {
854 var groups = createArrayOfDictionaries(ProjectNames.length);
855 for (var i=0; i<Warnings.length; i++) {
856 var w = Warnings[i][0];
857 var p = Warnings[i][1];
858 var k = w.toString();
859 if (!(k in groups[p]))
860 groups[p][k] = [];
861 groups[p][k].push(Warnings[i]);
862 }
863 return groups;
864 }
865 var GlobalAnchor = 0;
866 function createWarningSection(header, color, group) {
867 var result = "";
868 var groupKeys = [];
869 var totalMessages = 0;
870 for (var k in group) {
871 totalMessages += group[k].length;
872 groupKeys.push([k, WarnPatternsSeverity[parseInt(k)], group[k].length]);
873 }
874 groupKeys.sort(bySeverityMessageCount);
875 for (var idx=0; idx<groupKeys.length; idx++) {
876 var k = groupKeys[idx][0];
877 var messages = group[k];
878 var w = parseInt(k);
879 var wcolor = SeverityColors[WarnPatternsSeverity[w]];
880 var description = WarnPatternsDescription[w];
881 if (description.length == 0)
882 description = "???";
883 GlobalAnchor += 1;
884 result += "<table class='t1'><tr bgcolor='" + wcolor + "'><td>" +
885 "<button class='bt' id='" + GlobalAnchor + "_mark" +
886 "' onclick='expand(\\"" + GlobalAnchor + "\\");'>" +
887 "&#x2295</button> " +
888 description + " (" + messages.length + ")</td></tr></table>";
889 result += "<div id='" + GlobalAnchor +
890 "' style='display:none;'><table class='t1'>";
891 var c = 0;
892 messages.sort(compareMessages);
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800893 if (FlagPlatform == "chrome") {
894 for (var i=0; i<messages.length; i++) {
895 result += "<tr><td class='c" + c + "'>" +
896 addURLToLine(WarningMessages[messages[i][2]], WarningLinks[messages[i][3]]) + "</td></tr>";
897 c = 1 - c;
898 }
899 } else {
900 for (var i=0; i<messages.length; i++) {
901 result += "<tr><td class='c" + c + "'>" +
902 addURL(WarningMessages[messages[i][2]]) + "</td></tr>";
903 c = 1 - c;
904 }
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800905 }
906 result += "</table></div>";
907 }
908 if (result.length > 0) {
909 return "<br><span style='background-color:" + color + "'><b>" +
910 header + ": " + totalMessages +
911 "</b></span><blockquote><table class='t1'>" +
912 result + "</table></blockquote>";
913
914 }
915 return ""; // empty section
916 }
917 function generateSectionsBySeverity() {
918 var result = "";
919 var groups = groupWarningsBySeverity();
920 for (s=0; s<SeverityColors.length; s++) {
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800921 result += createWarningSection(SeverityHeaders[s], SeverityColors[s],
922 groups[s]);
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800923 }
924 return result;
925 }
926 function generateSectionsByProject() {
927 var result = "";
928 var groups = groupWarningsByProject();
929 for (i=0; i<groups.length; i++) {
930 result += createWarningSection(ProjectNames[i], 'lightgrey', groups[i]);
931 }
932 return result;
933 }
934 function groupWarnings(generator) {
935 GlobalAnchor = 0;
936 var e = document.getElementById("warning_groups");
937 e.innerHTML = generator();
938 }
939 function groupBySeverity() {
940 groupWarnings(generateSectionsBySeverity);
941 }
942 function groupByProject() {
943 groupWarnings(generateSectionsByProject);
944 }
945"""
946
947
948# Emit a JavaScript const string
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800949def emit_const_string(name, value, writer):
950 writer('const ' + name + ' = "' + escape_string(value) + '";')
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800951
952
953# Emit a JavaScript const integer array.
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800954def emit_const_int_array(name, array, writer):
955 writer('const ' + name + ' = [')
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800956 for n in array:
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800957 writer(str(n) + ',')
958 writer('];')
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800959
960
961# Emit a JavaScript const string array.
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800962def emit_const_string_array(name, array, writer):
963 writer('const ' + name + ' = [')
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800964 for s in array:
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800965 writer('"' + strip_escape_string(s) + '",')
966 writer('];')
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800967
968
969# Emit a JavaScript const string array for HTML.
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800970def emit_const_html_string_array(name, array, writer):
971 writer('const ' + name + ' = [')
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800972 for s in array:
973 # Not using html.escape yet, to work for both python 2 and 3,
974 # until all users switch to python 3.
975 # pylint:disable=deprecated-method
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800976 writer('"' + cgi.escape(strip_escape_string(s)) + '",')
977 writer('];')
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800978
979
980# Emit a JavaScript const object array.
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800981def emit_const_object_array(name, array, writer):
982 writer('const ' + name + ' = [')
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800983 for x in array:
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800984 writer(str(x) + ',')
985 writer('];')
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800986
987
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800988def emit_js_data(writer, flags, warning_messages, warning_links,
989 warning_records, warn_patterns, project_names):
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800990 """Dump dynamic HTML page's static JavaScript data."""
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800991 emit_const_string('FlagPlatform', flags.platform, writer)
992 emit_const_string('FlagURL', flags.url, writer)
993 emit_const_string('FlagSeparator', flags.separator, writer)
994 emit_const_string_array('SeverityColors', [s.color for s in Severity.levels],
995 writer)
Chih-Hung Hsieh949205a2020-01-10 10:33:40 -0800996 emit_const_string_array('SeverityHeaders',
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800997 [s.header for s in Severity.levels], writer)
Chih-Hung Hsieh949205a2020-01-10 10:33:40 -0800998 emit_const_string_array('SeverityColumnHeaders',
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800999 [s.column_header for s in Severity.levels], writer)
1000 emit_const_string_array('ProjectNames', project_names, writer)
Chih-Hung Hsieh949205a2020-01-10 10:33:40 -08001001 # pytype: disable=attribute-error
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -08001002 emit_const_int_array('WarnPatternsSeverity',
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -08001003 [w['severity'].value for w in warn_patterns], writer)
Chih-Hung Hsieh949205a2020-01-10 10:33:40 -08001004 # pytype: enable=attribute-error
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -08001005 emit_const_html_string_array('WarnPatternsDescription',
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -08001006 [w['description'] for w in warn_patterns],
1007 writer)
1008 emit_const_html_string_array('WarningMessages', warning_messages, writer)
1009 emit_const_object_array('Warnings', warning_records, writer)
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -08001010 if flags.platform == 'chrome':
1011 emit_const_html_string_array('WarningLinks', warning_links, writer)
1012
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -08001013
1014draw_table_javascript = """
1015google.charts.load('current', {'packages':['table']});
1016google.charts.setOnLoadCallback(drawTable);
1017function drawTable() {
1018 var data = new google.visualization.DataTable();
1019 data.addColumn('string', StatsHeader[0]);
1020 for (var i=1; i<StatsHeader.length; i++) {
1021 data.addColumn('number', StatsHeader[i]);
1022 }
1023 data.addRows(StatsRows);
1024 for (var i=0; i<StatsRows.length; i++) {
1025 for (var j=0; j<StatsHeader.length; j++) {
1026 data.setProperty(i, j, 'style', 'border:1px solid black;');
1027 }
1028 }
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -08001029 var table = new google.visualization.Table(
1030 document.getElementById('stats_table'));
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -08001031 table.draw(data, {allowHtml: true, alternatingRowStyle: true});
1032}
1033"""
1034
1035
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -08001036def dump_html(flags, output_stream, warning_messages, warning_links,
1037 warning_records, header_str, warn_patterns, project_names):
1038 """Dump the flags output to output_stream."""
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -08001039 writer = make_writer(output_stream)
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -08001040 dump_html_prologue('Warnings for ' + header_str, writer, warn_patterns,
1041 project_names)
1042 dump_stats(writer, warn_patterns)
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -08001043 writer('<br><div id="stats_table"></div><br>')
1044 writer('\n<script>')
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -08001045 emit_js_data(writer, flags, warning_messages, warning_links, warning_records,
1046 warn_patterns, project_names)
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -08001047 writer(scripts_for_warning_groups)
1048 writer('</script>')
1049 emit_buttons(writer)
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -08001050 # Warning messages are grouped by severities or project names.
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -08001051 writer('<br><div id="warning_groups"></div>')
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -08001052 if flags.byproject:
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -08001053 writer('<script>groupByProject();</script>')
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -08001054 else:
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -08001055 writer('<script>groupBySeverity();</script>')
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -08001056 dump_fixed(writer, warn_patterns)
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -08001057 dump_html_epilogue(writer)
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -08001058
1059
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -08001060def parse_compiler_output(compiler_output):
1061 """Parse compiler output for relevant info."""
1062 split_output = compiler_output.split(':', 3) # 3 = max splits
1063 file_path = split_output[0]
1064 line_number = int(split_output[1])
1065 col_number = int(split_output[2].split(' ')[0])
1066 warning_message = split_output[3]
1067 return file_path, line_number, col_number, warning_message
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -08001068
1069
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -08001070def get_warn_patterns(platform):
1071 """Get and initialize warn_patterns."""
1072 warn_patterns = []
1073 if platform == 'chrome':
1074 warn_patterns = cpp_patterns.warn_patterns
1075 elif platform == 'android':
1076 warn_patterns = make_patterns.warn_patterns + cpp_patterns.warn_patterns + java_patterns.warn_patterns + tidy_patterns.warn_patterns + other_patterns.warn_patterns
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -08001077 else:
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -08001078 raise Exception('platform name %s is not valid' % platform)
1079 for w in warn_patterns:
1080 w['members'] = []
1081 # Each warning pattern has a 'projects' dictionary, that
1082 # maps a project name to number of warnings in that project.
1083 w['projects'] = {}
1084 return warn_patterns
1085
1086
1087def get_project_list(platform):
1088 """Return project list for appropriate platform."""
1089 if platform == 'chrome':
1090 return chrome_project_list.project_list
1091 if platform == 'android':
1092 return android_project_list.project_list
1093 raise Exception('platform name %s is not valid' % platform)
1094
1095
1096def parallel_classify_warnings(warning_data, args, project_names,
1097 project_patterns, warn_patterns,
1098 use_google3, create_launch_subprocs_fn,
1099 classify_warnings_fn):
1100 """Classify all warning lines with num_cpu parallel processes."""
1101 num_cpu = args.processes
1102 group_results = []
1103
1104 if num_cpu > 1:
1105 # set up parallel processing for this...
1106 warning_groups = [[] for _ in range(num_cpu)]
1107 i = 0
1108 for warning, link in warning_data.items():
1109 warning_groups[i].append((warning, link))
1110 i = (i + 1) % num_cpu
1111 arg_groups = [[] for _ in range(num_cpu)]
1112 for i, group in enumerate(warning_groups):
1113 arg_groups[i] = [{
1114 'group': group,
1115 'project_patterns': project_patterns,
1116 'warn_patterns': warn_patterns,
1117 'num_processes': num_cpu
1118 }]
1119
1120 group_results = create_launch_subprocs_fn(num_cpu,
1121 classify_warnings_fn,
1122 arg_groups,
1123 group_results)
1124 else:
1125 group_results = []
1126 for warning, link in warning_data.items():
1127 classify_one_warning(warning, link, group_results,
1128 project_patterns, warn_patterns)
1129 group_results = [group_results]
1130
1131 warning_messages = []
1132 warning_links = []
1133 warning_records = []
1134 if use_google3:
1135 group_results = [group_results]
1136 for group_result in group_results:
1137 for result in group_result:
1138 for line, link, pattern_idx, project_idx in result:
1139 pattern = warn_patterns[pattern_idx]
1140 pattern['members'].append(line)
1141 message_idx = len(warning_messages)
1142 warning_messages.append(line)
1143 link_idx = len(warning_links)
1144 warning_links.append(link)
1145 warning_records.append([pattern_idx, project_idx, message_idx,
1146 link_idx])
1147 pname = '???' if project_idx < 0 else project_names[project_idx]
1148 # Count warnings by project.
1149 if pname in pattern['projects']:
1150 pattern['projects'][pname] += 1
1151 else:
1152 pattern['projects'][pname] = 1
1153 return warning_messages, warning_links, warning_records
1154
1155
1156def write_html(flags, project_names, warn_patterns, html_path, warning_messages,
1157 warning_links, warning_records, header_str):
1158 """Write warnings html file."""
1159 if html_path:
1160 with open(html_path, 'w') as f:
1161 dump_html(flags, f, warning_messages, warning_links, warning_records,
1162 header_str, warn_patterns, project_names)
1163
1164
1165def write_out_csv(flags, warn_patterns, warning_messages, warning_links,
1166 warning_records, header_str, project_names):
1167 """Write warnings csv file."""
1168 if flags.csvpath:
1169 with open(flags.csvpath, 'w') as f:
1170 dump_csv(csv.writer(f, lineterminator='\n'), warn_patterns)
1171
1172 if flags.gencsv:
1173 dump_csv(csv.writer(sys.stdout, lineterminator='\n'), warn_patterns)
1174 else:
1175 dump_html(flags, sys.stdout, warning_messages, warning_links,
1176 warning_records, header_str, warn_patterns, project_names)
1177
1178
1179def process_log(logfile, flags, project_names, project_patterns, warn_patterns,
1180 html_path, use_google3, create_launch_subprocs_fn,
1181 classify_warnings_fn, logfile_object):
1182 # pylint: disable=g-doc-args
1183 # pylint: disable=g-doc-return-or-yield
1184 """Function that handles processing of a log.
1185
1186 This is isolated into its own function (rather than just taking place in main)
1187 so that it can be used by both warn.py and the borg job process_gs_logs.py, to
1188 avoid duplication of code.
1189 Note that if the arguments to this function change, process_gs_logs.py must
1190 be updated accordingly.
1191 """
1192 if logfile_object is None:
1193 with io.open(logfile, encoding='utf-8') as log:
1194 warning_lines_and_links, header_str = parse_input_file(log, flags)
1195 else:
1196 warning_lines_and_links, header_str = parse_input_file(
1197 logfile_object, flags)
1198 warning_messages, warning_links, warning_records = parallel_classify_warnings(
1199 warning_lines_and_links, flags, project_names, project_patterns,
1200 warn_patterns, use_google3, create_launch_subprocs_fn,
1201 classify_warnings_fn)
1202
1203 write_html(flags, project_names, warn_patterns, html_path,
1204 warning_messages, warning_links, warning_records,
1205 header_str)
1206
1207 return warning_messages, warning_links, warning_records, header_str
1208
1209
1210def common_main(use_google3, create_launch_subprocs_fn, classify_warnings_fn,
1211 logfile_object=None):
1212 """Shared main function for Google3 and non-Google3 versions of warn.py."""
1213 flags = parse_args(use_google3)
1214 warn_patterns = get_warn_patterns(flags.platform)
1215 project_list = get_project_list(flags.platform)
1216
1217 project_names = get_project_names(project_list)
1218 project_patterns = [re.compile(p[1]) for p in project_list]
1219
1220 # html_path=None because we output html below if not outputting CSV
1221 warning_messages, warning_links, warning_records, header_str = process_log(
1222 logfile=flags.log, flags=flags, project_names=project_names,
1223 project_patterns=project_patterns, warn_patterns=warn_patterns,
1224 html_path=None, use_google3=use_google3,
1225 create_launch_subprocs_fn=create_launch_subprocs_fn,
1226 classify_warnings_fn=classify_warnings_fn,
1227 logfile_object=logfile_object)
1228
1229 write_out_csv(flags, warn_patterns, warning_messages, warning_links,
1230 warning_records, header_str, project_names)
1231
1232 # Return these values, so that caller can use them, if desired.
1233 return flags, warning_messages, warning_records, warn_patterns