Michael Schaffner | 8ac6c4c | 2020-03-03 15:00:20 -0800 | [diff] [blame] | 1 | # Copyright lowRISC contributors. |
| 2 | # Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| 3 | # SPDX-License-Identifier: Apache-2.0 |
| 4 | r""" |
| 5 | Class describing lint configuration object |
| 6 | """ |
| 7 | |
| 8 | import logging as log |
| 9 | import sys |
| 10 | from pathlib import Path |
| 11 | |
| 12 | from tabulate import tabulate |
| 13 | |
Udi Jonnalagadda | df49fb8 | 2020-03-17 11:05:17 -0700 | [diff] [blame] | 14 | from Deploy import * |
| 15 | from Modes import * |
| 16 | from OneShotCfg import OneShotCfg |
| 17 | from utils import * |
Michael Schaffner | 8ac6c4c | 2020-03-03 15:00:20 -0800 | [diff] [blame] | 18 | |
| 19 | |
| 20 | # helper function for printing messages |
| 21 | def _print_msg_list(msg_list_name, msg_list): |
| 22 | md_results = "" |
| 23 | if msg_list: |
| 24 | md_results += "### %s\n" % msg_list_name |
| 25 | md_results += "```\n" |
| 26 | for msg in msg_list: |
| 27 | msg_parts = msg.split() |
| 28 | md_results += msg + "\n\n" |
| 29 | md_results += "```\n" |
| 30 | return md_results |
| 31 | |
| 32 | |
| 33 | class LintCfg(OneShotCfg): |
| 34 | """Derivative class for linting purposes. |
| 35 | """ |
| 36 | def __init__(self, flow_cfg_file, proj_root, args): |
| 37 | super().__init__(flow_cfg_file, proj_root, args) |
| 38 | |
| 39 | def __post_init__(self): |
| 40 | super().__post_init__() |
| 41 | # Set the title for lint results. |
| 42 | self.results_title = self.name.upper() + " Lint Results" |
| 43 | |
Michael Schaffner | 8ac6c4c | 2020-03-03 15:00:20 -0800 | [diff] [blame] | 44 | @staticmethod |
| 45 | def create_instance(flow_cfg_file, proj_root, args): |
| 46 | '''Create a new instance of this class as with given parameters. |
| 47 | ''' |
| 48 | return LintCfg(flow_cfg_file, proj_root, args) |
| 49 | |
| 50 | def gen_results_summary(self): |
| 51 | ''' |
| 52 | Gathers the aggregated results from all sub configs |
| 53 | ''' |
| 54 | |
Michael Schaffner | b60aaa5 | 2020-03-31 18:38:46 -0700 | [diff] [blame^] | 55 | # Generate results table for runs. |
Michael Schaffner | 8ac6c4c | 2020-03-03 15:00:20 -0800 | [diff] [blame] | 56 | log.info("Create summary of lint results") |
| 57 | |
| 58 | results_str = "## " + self.results_title + " (Summary)\n\n" |
| 59 | results_str += "### " + self.timestamp_long + "\n\n" |
| 60 | |
| 61 | header = [ |
| 62 | "Name", "Tool Warnings", "Tool Errors", "Lint Warnings", |
| 63 | "Lint Errors" |
| 64 | ] |
| 65 | colalign = ("center", ) * len(header) |
| 66 | table = [header] |
| 67 | |
| 68 | for cfg in self.cfgs: |
| 69 | |
| 70 | results_page = cfg.results_server_dir + '/results.html' |
| 71 | results_page_url = results_page.replace( |
| 72 | cfg.results_server_prefix, cfg.results_server_url_prefix) |
| 73 | name_with_link = "[" + cfg.name.upper( |
| 74 | ) + "](" + results_page_url + ")" |
| 75 | table.append([ |
| 76 | name_with_link, |
| 77 | str(len(cfg.result_summary["warnings"])) + " W", |
| 78 | str(len(cfg.result_summary["errors"])) + " E", |
| 79 | str(len(cfg.result_summary["lint_warnings"])) + " W", |
| 80 | str(len(cfg.result_summary["lint_errors"])) + " E" |
| 81 | ]) |
| 82 | |
| 83 | if len(table) > 1: |
| 84 | self.results_summary_md = results_str + tabulate( |
| 85 | table, headers="firstrow", tablefmt="pipe", |
| 86 | colalign=colalign) + "\n" |
| 87 | else: |
| 88 | self.results_summary_md = results_str + "\nNo results to display.\n" |
| 89 | |
| 90 | print(self.results_summary_md) |
| 91 | |
| 92 | # Return only the tables |
| 93 | return self.results_summary_md |
| 94 | |
| 95 | def _gen_results(self): |
| 96 | # ''' |
| 97 | # The function is called after the regression has completed. It looks |
| 98 | # for a regr_results.hjson file with aggregated results from the lint run. |
| 99 | # The hjson needs to have the following (potentially empty) fields |
| 100 | # |
| 101 | # { |
| 102 | # tool: "" |
| 103 | # errors: [] |
| 104 | # warnings: [] |
| 105 | # lint_errors: [] |
| 106 | # lint_warning: [] |
| 107 | # lint_infos: [] |
| 108 | # } |
| 109 | # |
| 110 | # where each entry is a string representing a lint message. This allows |
| 111 | # to reuse the same LintCfg class with different tools since just the |
| 112 | # parsing script that transforms the tool output into the hjson above |
| 113 | # needs to be adapted. |
| 114 | # |
| 115 | # note that if this is a master config, the results will |
| 116 | # be generated using the _gen_results_summary function |
| 117 | # ''' |
| 118 | |
| 119 | # Generate results table for runs. |
Michael Schaffner | 8ac6c4c | 2020-03-03 15:00:20 -0800 | [diff] [blame] | 120 | results_str = "## " + self.results_title + "\n\n" |
| 121 | results_str += "### " + self.timestamp_long + "\n" |
| 122 | results_str += "### Lint Tool: " + self.tool.upper() + "\n\n" |
| 123 | |
Michael Schaffner | 8ac6c4c | 2020-03-03 15:00:20 -0800 | [diff] [blame] | 124 | header = [ |
| 125 | "Build Mode", "Tool Warnings", "Tool Errors", "Lint Warnings", |
| 126 | "Lint Errors" |
| 127 | ] |
| 128 | colalign = ("center", ) * len(header) |
| 129 | table = [header] |
| 130 | |
| 131 | # aggregated counts |
| 132 | self.result_summary["warnings"] = [] |
| 133 | self.result_summary["errors"] = [] |
| 134 | self.result_summary["lint_warnings"] = [] |
| 135 | self.result_summary["lint_errors"] = [] |
| 136 | |
| 137 | fail_msgs = "" |
| 138 | for mode in self.build_modes: |
| 139 | |
| 140 | result_data = Path( |
| 141 | subst_wildcards(self.build_dir, {"build_mode": mode.name}) + |
| 142 | '/results.hjson') |
| 143 | log.info("looking for result data file at %s", result_data) |
| 144 | |
| 145 | try: |
| 146 | with open(result_data, "r") as results_file: |
| 147 | self.result = hjson.load(results_file, use_decimal=True) |
| 148 | except IOError as err: |
| 149 | log.warning("%s", err) |
| 150 | self.result = { |
| 151 | "tool": "", |
| 152 | "errors": ["IOError: %s" % err], |
| 153 | "warnings": [], |
| 154 | "lint_errors": [], |
| 155 | "lint_warnings": [], |
| 156 | "lint_infos": [] |
| 157 | } |
| 158 | if self.result: |
| 159 | table.append([ |
| 160 | mode.name, |
| 161 | str(len(self.result["warnings"])) + " W ", |
| 162 | str(len(self.result["errors"])) + " E", |
| 163 | # We currently do not publish these infos at |
| 164 | # the moment len(self.result["lint_infos"]), |
| 165 | str(len(self.result["lint_warnings"])) + " W", |
| 166 | str(len(self.result["lint_errors"])) + " E" |
| 167 | ]) |
| 168 | else: |
| 169 | self.result = { |
| 170 | "tool": "", |
| 171 | "errors": [], |
| 172 | "warnings": [], |
| 173 | "lint_errors": [], |
| 174 | "lint_warnings": [], |
| 175 | "lint_infos": [] |
| 176 | } |
| 177 | |
| 178 | self.result_summary["warnings"] += self.result["warnings"] |
| 179 | self.result_summary["errors"] += self.result["errors"] |
| 180 | self.result_summary["lint_warnings"] += self.result[ |
| 181 | "lint_warnings"] |
| 182 | self.result_summary["lint_errors"] += self.result["lint_errors"] |
| 183 | |
| 184 | # Append detailed messages if they exist |
| 185 | if sum([ |
| 186 | len(self.result["warnings"]), |
| 187 | len(self.result["errors"]), |
| 188 | len(self.result["lint_warnings"]), |
| 189 | len(self.result["lint_errors"]) |
| 190 | ]): |
| 191 | fail_msgs += "\n## Errors and Warnings for Build Mode `'" + mode.name + "'`\n" |
| 192 | fail_msgs += _print_msg_list("Tool Errors", |
| 193 | self.result["errors"]) |
| 194 | fail_msgs += _print_msg_list("Tool Warnings", |
| 195 | self.result["warnings"]) |
| 196 | fail_msgs += _print_msg_list("Lint Errors", |
| 197 | self.result["lint_errors"]) |
| 198 | fail_msgs += _print_msg_list("Lint Warnings", |
| 199 | self.result["lint_warnings"]) |
| 200 | #fail_msgs += _print_msg_list("Lint Infos", results["lint_infos"]) |
| 201 | |
| 202 | if len(table) > 1: |
| 203 | self.results_md = results_str + tabulate( |
| 204 | table, headers="firstrow", tablefmt="pipe", |
| 205 | colalign=colalign) + "\n" + fail_msgs |
| 206 | else: |
| 207 | self.results_md = results_str + "\nNo results to display.\n" |
| 208 | |
| 209 | # Write results to the scratch area |
| 210 | self.results_file = self.scratch_path + "/results_" + self.timestamp + ".md" |
Michael Schaffner | 8ac6c4c | 2020-03-03 15:00:20 -0800 | [diff] [blame] | 211 | with open(self.results_file, 'w') as f: |
| 212 | f.write(self.results_md) |
| 213 | |
Srikrishna Iyer | fbaa01a | 2020-03-19 15:32:23 -0700 | [diff] [blame] | 214 | log.info("[results page]: [%s] [%s]", self.name, results_file) |
Michael Schaffner | 8ac6c4c | 2020-03-03 15:00:20 -0800 | [diff] [blame] | 215 | return self.results_md |