|  | # 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 |