blob: 8e80dd2e7925778bac6b193851455703b5a1940a [file] [log] [blame]
#!/usr/bin/env python3
# Copyright lowRISC contributors.
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
r"""Parses lint report and dump filtered messages in hjson format.
"""
import argparse
import logging as log
import re
import sys
from pathlib import Path
import hjson
def extract_messages(full_file, patterns, results):
"""
This extracts messages from the sting buffer full_file.
The argument patterns needs to be a list of tuples with
(<error_severity>, <pattern_to_match_for>).
"""
for severity, pattern in patterns:
results[severity] += re.findall(pattern, full_file, flags=re.MULTILINE)
return results
def get_results(resdir):
"""
Parse report and corresponding logfiles and extract error, warning
and info messages for each IP present in the result folder
"""
results = {
"tool": "ascentlint",
"fusesoc-error": [],
"errors": [],
"warnings": [],
"lint_errors": [],
"lint_warnings": [],
"lint_infos": []
}
log_files = [
'ascentlint.log', 'lint-ascentlint/ascentlint.log',
'lint-ascentlint/ascentlint.rpt'
]
log_file_contents = []
# Open all log files
for name in log_files:
try:
with Path(resdir).joinpath(name).open() as f:
log_file_contents.append(f.read())
except IOError as err:
results["errors"] += ["IOError: %s" % err]
# Define warning/error patterns for each logfile
err_warn_patterns = []
# Patterns for lint.log
err_warn_patterns.append([
# If lint warnings have been found, the lint tool will exit
# with a nonzero status code and fusesoc will always spit out
# an error like
#
# ERROR: Failed to build ip:core:name:0.1 : 'make' exited with an error code
#
# If we found any other warnings or errors, there's no point in
# listing this too. BUT we want to make sure we *do* see this
# error if there are no other errors or warnings, since that
# shows something has come unstuck. (Probably the lint tool
# spat out a warning that we don't understand)
("fusesoc-error",
r"^ERROR: Failed to build .* : 'make' exited with an error code")
])
# Patterns for ascentlint.log
err_warn_patterns.append([
("errors", r"^FlexNet Licensing error.*"),
("errors", r"^Error: .*"),
("errors", r"^ERROR.*"),
("errors", r"^ ERR .*"),
("warnings", r"^Warning: .*"),
# TODO: struct assignment labels within concatenation
# not supported. check with newer ascentlint version.
("warnings", r"^ (?!WARN \[#39024\])WARN .*")
])
# Patterns for ascentlint.rpt
err_warn_patterns.append([("lint_errors", r"^E .*"),
("lint_warnings", r"^W .*"),
("lint_infos", r"^I .*")])
# Go parse the logs
for patterns, logs in zip(err_warn_patterns, log_file_contents):
extract_messages(logs, patterns, results)
# If there are no errors or warnings, add the "fusesoc-error" field to
# "errors" (which will be reported as tooling errors). Remove the
# "fusesoc-error" field either way.
if not (results['errors'] or results['warnings']):
results['errors'] = results['fusesoc-error']
del results['fusesoc-error']
return results
def main():
parser = argparse.ArgumentParser(
description="""This script parses AscentLint log and report files from
a lint run, filters the messages and creates an aggregated result
.hjson file with the following fields:
{"tool": "ascentlint",
"errors" : [],
"warnings" : [],
"lint_errors" : [],
"lint_warnings" : [],
"lint_infos" : []}
The fields 'errors' and 'warnings' contain file IO messages or
messages output by the tool itself, whereas the fields prefixed with
'lint_' contain lint-related messages.
The script returns nonzero status if any warnings or errors are present.
""")
parser.add_argument('--repdir',
type=str,
default="./",
help="""The script searches the 'ascentlint.log' and
'ascentlint.rpt' files in this directory.
Defaults to './'""")
parser.add_argument('--outdir',
type=str,
default="./",
help="""Output directory for the 'results.hjson' file.
Defaults to './'""")
args = parser.parse_args()
results = get_results(args.repdir)
with Path(args.outdir).joinpath("results.hjson").open("w") as results_file:
hjson.dump(results,
results_file,
ensure_ascii=False,
for_json=True,
use_decimal=True)
# return nonzero status if any warnings or errors are present
# lint infos do not count as failures
n_errors = len(results["errors"]) + len(results["lint_errors"])
n_warnings = len(results["warnings"]) + len(results["lint_warnings"])
if n_errors > 0 or n_warnings > 0:
log.info("Found %d lint errors and %d lint warnings", n_errors,
n_warnings)
sys.exit(1)
log.info("Lint logfile parsed succesfully")
sys.exit(0)
if __name__ == "__main__":
main()