blob: 1b3cf16081ea774d32efd0ffb90ad5aceaa9d214 [file] [log] [blame]
# Copyright lowRISC contributors.
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
r'''
Class describing lint configuration object
'''
import logging as log
from pathlib import Path
from tabulate import tabulate
from OneShotCfg import OneShotCfg
from utils import subst_wildcards, check_bool
from MsgBuckets import MsgBuckets
class LintCfg(OneShotCfg):
'''Derivative class for linting purposes.'''
flow = 'lint'
def __init__(self, flow_cfg_file, hjson_data, args, mk_config):
# TODO: check whether this can be replaced with the subflow concept.
# This determines whether the flow is for a style lint run.
# Format: bool
self.is_style_lint = ''
# Determines which message severities to print into report summaries
# Format: [str, ...]
self.report_severities = []
# Determines which message severities lead to a pass/fail
# Format: [str, ...]
self.fail_severities = []
# Message bucket format configuration
# Format: [{category: str, severity: str, label: str}, ...]
self.message_buckets = []
super().__init__(flow_cfg_file, hjson_data, args, mk_config)
if self.is_style_lint == '':
self.is_style_lint = False
else:
self.is_style_lint = check_bool(self.is_style_lint)
# Set the title for lint results.
if self.is_style_lint:
self.results_title = f'{self.name.upper()} Style Lint Results'
else:
self.results_title = f'{self.name.upper()} Lint Results'
def gen_results_summary(self):
'''
Gathers the aggregated results from all sub configs
'''
# Generate results table for runs.
log.info('Create summary of lint results')
results_str = f'## {self.results_title} (Summary)\n\n'
results_str += f'### {self.timestamp_long}\n'
if self.revision:
results_str += f'### {self.revision}\n'
results_str += f'### Branch: {self.branch}\n'
results_str += '\n'
# Aggregate with all summaries
self.totals = MsgBuckets(self.message_buckets)
for cfg in self.cfgs:
self.totals += cfg.result_summary
# Construct Header
labels = self.totals.get_labels(self.report_severities)
header = ['Name'] + labels
colalign = ('center', ) * len(header)
table = [header]
keys = self.totals.get_keys(self.report_severities)
for cfg in self.cfgs:
name_with_link = cfg._get_results_page_link(
self.results_dir)
row = [name_with_link]
row += cfg.result_summary.get_counts_md(keys)
table.append(row)
if len(table) > 1:
self.results_summary_md = results_str + tabulate(
table, headers='firstrow', tablefmt='pipe',
colalign=colalign) + '\n'
else:
self.results_summary_md = f'{results_str}\nNo results to display.\n'
print(self.results_summary_md)
# Return only the tables
return self.results_summary_md
# TODO(#9079): This way of parsing out messages into an intermediate
# results.hjson file will be replaced by a native parser mechanism.
def _gen_results(self, results):
# '''
# The function is called after the regression has completed. It looks
# for a results.hjson file with aggregated results from the lint run.
# The hjson needs to have the following format:
#
# {
# bucket_key: [str],
# // other buckets according to message_buckets configuration
# }
#
# Each bucket key points to a list of signatures (strings).
# The bucket categories and severities are defined in the
# message_buckets class variable, and can be set via Hjson Dvsim
# config files.
#
# Note that if this is a primary config, the results will
# be generated using the _gen_results_summary function
# '''
# Generate results table for runs.
results_str = f'## {self.results_title}\n\n'
results_str += f'### {self.timestamp_long}\n'
if self.revision:
results_str += f'### {self.revision}\n'
results_str += f'### Branch: {self.branch}\n'
results_str += f'### Tool: {self.tool.upper()}\n\n'
# Load all result files from all build modes and convert them to
# message buckets.
self.result = []
self.result_summary = MsgBuckets(self.message_buckets)
for mode in self.build_modes:
result_path = Path(
subst_wildcards(self.build_dir, {'build_mode': mode.name}) +
'/results.hjson')
log.info('[results:hjson]: [%s]: [%s]', self.name, result_path)
# TODO(#9079): replace this with native log parser
msgs = MsgBuckets(self.message_buckets)
msgs.load_hjson(result_path)
self.result.append(msgs)
# Aggregate with summary results
self.result_summary += msgs
# Construct Header
labels = self.result_summary.get_labels(self.report_severities)
header = ['Build Mode'] + labels
colalign = ('center', ) * len(header)
table = [header]
fail_msgs = ''
self.errors_seen = 0
keys = self.result_summary.get_keys(self.report_severities)
for mode, res in zip(self.build_modes, self.result):
row = [mode.name] + res.get_counts_md(keys)
table.append(row)
self.errors_seen += res.has_signatures(self.fail_severities)
fail_msgs += f"\n### Messages for Build Mode `'{mode.name}'`\n"
fail_msgs += res.print_signatures_md(self.report_severities,
self.max_msg_count)
if len(table) > 1:
self.results_md = results_str + tabulate(
table, headers='firstrow', tablefmt='pipe',
colalign=colalign) + '\n'
# Th published report will default to self.results_md if they are
# empty. In case it needs need to be sanitized, override it and do
# not append detailed messages.
if self.sanitize_publish_results:
self.publish_results_md = self.results_md
# Locally generated result always contains all details.
self.results_md += fail_msgs
else:
self.results_md = f'{results_str}\nNo results to display.\n'
self.publish_results_md = self.results_md
return self.results_md