blob: a2df18026a2976592dddbad5105afb628442f0c8 [file] [log] [blame]
Timothy Trippelb93cc6a2021-07-30 02:34:38 +00001#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3# Copyright lowRISC contributors.
4# Licensed under the Apache License, Version 2.0, see LICENSE for details.
5# SPDX-License-Identifier: Apache-2.0
6"""DIF Status Report Generator.
7
8This tool generates a status report for the DIFs by cross referencing the git
9commit history of each DIF with that of the HW it actuates to provide OpenTitan
10developers with information about what DIFs require updating.
11
12To display usage run:
13 ./check_dif_statuses.py --help
14"""
15
16import argparse
17import collections
Timothy Trippelb93cc6a2021-07-30 02:34:38 +000018import io
Timothy Trippel7f327d82021-10-29 02:26:39 +000019import itertools
Timothy Trippelb93cc6a2021-07-30 02:34:38 +000020import json
21import logging
Timothy Trippelb93cc6a2021-07-30 02:34:38 +000022import re
Srikrishna Iyer05fc3e12021-08-20 09:50:21 -070023import subprocess
Timothy Trippelb93cc6a2021-07-30 02:34:38 +000024import sys
25from contextlib import redirect_stdout
26from enum import Enum
Srikrishna Iyer05fc3e12021-08-20 09:50:21 -070027from pathlib import Path
Timothy Trippelec466392021-10-28 00:27:26 +000028from typing import List, Set
Timothy Trippelb93cc6a2021-07-30 02:34:38 +000029
30import enlighten
31import gitfame
32import hjson
33import pydriller
34from tabulate import tabulate
35from termcolor import colored
36
Timothy Trippel1d21f072021-10-28 23:42:59 +000037from make_new_dif.ip import IPS_USING_IPGEN
38
Srikrishna Iyer05fc3e12021-08-20 09:50:21 -070039# Maintain a list of IPs that only exist in the top-level area.
40#
41# Note that there are several templated IPs that are auto-generated in the
42# top-level area as well, but since the bulk of the code (including the
43# template) lives in the hw/ip area, we do not need to consider them.
Timothy Trippel7f327d82021-10-29 02:26:39 +000044# These IPs are slowly being migrated to use the `ipgen` tooling, and are
Timothy Trippel1d21f072021-10-28 23:42:59 +000045# defined in the IPS_USING_IPGEN list in the make_new_dif.ip module imported
46# above.
Srikrishna Iyer05fc3e12021-08-20 09:50:21 -070047_TOP_LEVEL_IPS = {"ast", "sensor_ctrl"}
48
49# Indicates that the DIF work has not yet started.
50_NOT_STARTED = colored("NOT STARTED", "red")
51
Timothy Trippel7f327d82021-10-29 02:26:39 +000052# This file is $REPO_TOP/util/check_dif_statuses.py, so it takes two parent()
Timothy Trippel76dd6462021-10-27 22:26:20 +000053# calls to get back to the top.
54REPO_TOP = Path(__file__).resolve().parent.parent
55
Timothy Trippel7f327d82021-10-29 02:26:39 +000056# Define the DIF library relative to REPO_TOP.
57DIFS_RELATIVE_PATH = Path("sw/device/lib/dif")
58
Timothy Trippelb93cc6a2021-07-30 02:34:38 +000059
60class _OTComponent(Enum):
61 """Type of OpenTitan component."""
62 DIF = 1
63 HW = 2
64
65
66class DIFStatus:
67 """Holds all DIF status information for displaying.
68
69 Attributes:
70 dif_name (str): Full name of the DIF including the IP name.
71 ip (str): Name of the IP the DIF is associated with.
Timothy Trippel7f327d82021-10-29 02:26:39 +000072 dif_path (Path): Path to the DIF code (relative to REPO_TOP).
Timothy Trippelec466392021-10-28 00:27:26 +000073 hw_path (Path): Path to the HW RTL associated with this DIF.
Timothy Trippelb93cc6a2021-07-30 02:34:38 +000074 dif_last_modified (datetime): Date and time the DIF was last modified.
75 hw_last_modified (datetime): Date and time the HW was last modified.
76 dif_main_contributors (List[str]): List of emails of DIF contributors.
77 hw_main_constributors (List[str]): List of emails of HW contributors.
78 lifecycle_state (str): Lifecycle state string (e.g., S0, S1, ...).
79 num_functions_defined (int): Number of API functions defined.
80 num_functions_implemented (int): Number of API functions implemented.
81 api_complete (bool): Indicates if DIF implements all defined functions.
82 funcs_unimplemented (Set[str]): Set of unimplemted DIF functions.
83
84 """
Timothy Trippel7f327d82021-10-29 02:26:39 +000085 def __init__(self, top_level, dif_name):
Timothy Trippelb93cc6a2021-07-30 02:34:38 +000086 """Mines metadata to populate this DIFStatus object.
87
88 Args:
Srikrishna Iyer05fc3e12021-08-20 09:50:21 -070089 top_level: Name of the top level design.
Timothy Trippelb93cc6a2021-07-30 02:34:38 +000090 dif_name: Full name of the DIF including the IP name.
91
92 Raises:
93 ValueError: Raised if DIF name does not start with "dif_".
94 """
95 # Get DIF/IP names and path.
96 if not dif_name.startswith("dif_"):
97 raise ValueError("DIF name should start with \"dif_\".")
98 self.dif_name = dif_name
99 self.ip = self.dif_name[4:]
Timothy Trippel7f327d82021-10-29 02:26:39 +0000100 self.dif_path = DIFS_RELATIVE_PATH / dif_name
101 self.dif_autogen_path = (DIFS_RELATIVE_PATH /
102 f"autogen/{dif_name}_autogen")
Timothy Trippela7cd9a22021-08-12 21:03:49 +0000103
Srikrishna Iyer05fc3e12021-08-20 09:50:21 -0700104 # Check if header file exists - if not then its not even begun.
Timothy Trippel1d21f072021-10-28 23:42:59 +0000105 has_started = self.dif_path.with_suffix(".h").is_file()
106
107 # Get (relative) HW RTL path.
108 if self.ip in IPS_USING_IPGEN:
109 self.hw_path = Path(f"hw/ip_templates/{self.ip}")
110 elif self.ip in _TOP_LEVEL_IPS:
111 self.hw_path = Path(f"hw/{top_level}/ip/{self.ip}")
112 else:
113 self.hw_path = Path(f"hw/ip/{self.ip}")
Timothy Trippela7cd9a22021-08-12 21:03:49 +0000114
Srikrishna Iyer05fc3e12021-08-20 09:50:21 -0700115 # Indicates DIF API completeness.
Timothy Trippelb93cc6a2021-07-30 02:34:38 +0000116 self.num_functions_defined = -1
117 self.num_functions_implemented = -1
118 self.api_complete = False
Srikrishna Iyer05fc3e12021-08-20 09:50:21 -0700119
120 # Determine last date HW was updated.
121 self.hw_last_modified = self._get_last_commit_date(
Timothy Trippel7f327d82021-10-29 02:26:39 +0000122 [self.hw_path / "rtl"], [""])
Timothy Trippela7cd9a22021-08-12 21:03:49 +0000123
Srikrishna Iyer05fc3e12021-08-20 09:50:21 -0700124 # Determine the main contributor of the HW.
125 self.hw_main_contributors = self._get_main_contributor_emails(
126 _OTComponent.HW)
127 if has_started:
128 # Determine last date DIF was updated.
129 self.dif_last_modified = self._get_last_commit_date(
Timothy Trippel7f327d82021-10-29 02:26:39 +0000130 [self.dif_path, self.dif_autogen_path], [".h", ".c"])
Srikrishna Iyer05fc3e12021-08-20 09:50:21 -0700131 # Determine the main contributor of the DIF.
132 self.dif_main_contributors = self._get_main_contributor_emails(
133 _OTComponent.DIF)
134 # Determine lifecycle state
135 self.lifecycle_state = self._get_dif_lifecycle_state()
136 # Determine DIF API completeness.
137 self.funcs_unimplemented = self._get_funcs_unimplemented()
138 else:
Timothy Trippel1d21f072021-10-28 23:42:59 +0000139 # Set DIF status data to indicate it has not started.
Srikrishna Iyer05fc3e12021-08-20 09:50:21 -0700140 self.dif_last_modified = "-"
141 self.dif_main_contributors = [_NOT_STARTED]
142 self.lifecycle_state = "-"
143 self.funcs_unimplemented = [_NOT_STARTED]
Timothy Trippelb93cc6a2021-07-30 02:34:38 +0000144
145 def _get_dif_lifecycle_state(self):
Timothy Trippelec466392021-10-28 00:27:26 +0000146 hjson_filename = self.hw_path / f"data/{self.ip}.prj.hjson"
Timothy Trippelb93cc6a2021-07-30 02:34:38 +0000147 with open(hjson_filename, "r") as life_f:
148 lifecycle_data = hjson.load(life_f)
149 # If there are multiple revisions, grab the latest.
150 if "revisions" in lifecycle_data:
151 lifecycle_data = lifecycle_data["revisions"][-1]
152 if "dif_stage" in lifecycle_data:
153 return lifecycle_data["dif_stage"]
154 return "-"
155
156 def _get_main_contributor_emails(self, component):
157 # Get contributor stats for HW or DIF (SW) and sort by LOC.
158 if component == _OTComponent.DIF:
Timothy Trippel7f327d82021-10-29 02:26:39 +0000159 stats = self._get_contributors(
160 [self.dif_path, self.dif_autogen_path], [".h", ".c"])
Timothy Trippelb93cc6a2021-07-30 02:34:38 +0000161 else:
Timothy Trippel7f327d82021-10-29 02:26:39 +0000162 stats = self._get_contributors([self.hw_path / "rtl"], [""])
Timothy Trippelb93cc6a2021-07-30 02:34:38 +0000163 sorted_stats = sorted(stats.items(), key=lambda x: x[1], reverse=True)
164 # If the second contributor has contributed at least 10% as much as the
165 # first contributor, include both second and first contributors.
166 contributor_1_email, contributor_1_loc = sorted_stats[0]
167 if len(sorted_stats) > 1:
168 contributor_2_email, contributor_2_loc = sorted_stats[1]
169 if (float(contributor_2_loc) / float(contributor_1_loc)) > 0.1:
170 return [contributor_1_email, contributor_2_email]
171 return [contributor_1_email]
172
Timothy Trippel7f327d82021-10-29 02:26:39 +0000173 def _get_contributors(self, file_paths, exts):
Timothy Trippelb93cc6a2021-07-30 02:34:38 +0000174 contributor_stats = collections.defaultdict(int)
Timothy Trippel7f327d82021-10-29 02:26:39 +0000175 for file_path, ext in itertools.product(file_paths, exts):
Timothy Trippel1d21f072021-10-28 23:42:59 +0000176 full_file_path = file_path.with_suffix(ext)
Timothy Trippel7f327d82021-10-29 02:26:39 +0000177 output = io.StringIO()
Timothy Trippelec466392021-10-28 00:27:26 +0000178 try:
Timothy Trippelb93cc6a2021-07-30 02:34:38 +0000179 # Use gitfame to fetch commit stats, captured from STDOUT.
Timothy Trippelb93cc6a2021-07-30 02:34:38 +0000180 with redirect_stdout(output):
181 gitfame.main(args=[
182 f"--incl={full_file_path}", "-s", "-e", "--log=ERROR",
183 "--format=json"
184 ])
Timothy Trippelec466392021-10-28 00:27:26 +0000185 except FileNotFoundError:
186 logging.error(f"(contributors) file path ({full_file_path}) "
187 "does not exist.")
Timothy Trippelb93cc6a2021-07-30 02:34:38 +0000188 sys.exit(1)
Timothy Trippel7f327d82021-10-29 02:26:39 +0000189 gitfame_commit_stats = json.loads(output.getvalue())
190 for contributor_stat in gitfame_commit_stats["data"]:
191 contributor = contributor_stat[0]
192 loc = contributor_stat[1]
193 if loc == 0:
194 break
195 contributor_stats[contributor] += loc
Timothy Trippelb93cc6a2021-07-30 02:34:38 +0000196 return contributor_stats
197
Timothy Trippel7f327d82021-10-29 02:26:39 +0000198 def _get_last_commit_date(self, file_paths, exts):
Timothy Trippelb93cc6a2021-07-30 02:34:38 +0000199 last_dif_commit_date = None
Timothy Trippel7f327d82021-10-29 02:26:39 +0000200 for file_path, ext in itertools.product(file_paths, exts):
Timothy Trippel1d21f072021-10-28 23:42:59 +0000201 full_file_path = file_path.with_suffix(ext)
Timothy Trippelec466392021-10-28 00:27:26 +0000202 try:
Timothy Trippelb93cc6a2021-07-30 02:34:38 +0000203 repo = pydriller.Repository(
Timothy Trippel76dd6462021-10-27 22:26:20 +0000204 str(REPO_TOP), filepath=full_file_path).traverse_commits()
Timothy Trippelec466392021-10-28 00:27:26 +0000205 except FileNotFoundError:
Timothy Trippelb93cc6a2021-07-30 02:34:38 +0000206 logging.error(
207 f"(date) file path ({full_file_path}) does not exist.")
208 sys.exit(1)
Timothy Trippel7f327d82021-10-29 02:26:39 +0000209 for commit in repo:
210 if last_dif_commit_date is None:
211 last_dif_commit_date = commit.author_date
212 else:
213 last_dif_commit_date = max(last_dif_commit_date,
214 commit.author_date)
Srikrishna Iyer05fc3e12021-08-20 09:50:21 -0700215 return last_dif_commit_date.strftime("%Y-%m-%d %H:%M:%S")
Timothy Trippelb93cc6a2021-07-30 02:34:38 +0000216
217 def _get_funcs_unimplemented(self):
218 defined_funcs = self._get_defined_funcs()
219 implemented_funcs = self._get_implemented_funcs()
220 self.num_functions_defined = len(defined_funcs)
221 self.num_functions_implemented = len(implemented_funcs)
222 self.api_complete = bool(defined_funcs and
223 defined_funcs == implemented_funcs)
224 return defined_funcs - implemented_funcs
225
226 def _get_defined_funcs(self):
Timothy Trippelec466392021-10-28 00:27:26 +0000227 header_file = self.dif_path.with_suffix(".h")
Timothy Trippel7f327d82021-10-29 02:26:39 +0000228 autogen_header_file = self.dif_autogen_path.with_suffix(".h")
Timothy Trippela7cd9a22021-08-12 21:03:49 +0000229 defined_funcs = self._get_funcs(header_file)
Timothy Trippel7f327d82021-10-29 02:26:39 +0000230 defined_funcs |= self._get_funcs(autogen_header_file)
Timothy Trippela7cd9a22021-08-12 21:03:49 +0000231 return defined_funcs
Timothy Trippelb93cc6a2021-07-30 02:34:38 +0000232
233 def _get_implemented_funcs(self):
Timothy Trippelec466392021-10-28 00:27:26 +0000234 c_file = self.dif_path.with_suffix(".c")
Timothy Trippel7f327d82021-10-29 02:26:39 +0000235 c_autogen_file = self.dif_autogen_path.with_suffix(".c")
236 # The autogenerated header should always exist if the DIF has been
237 # started.
238 implemented_funcs = self._get_funcs(c_autogen_file)
239 # However, the manually-implemented header may not exist yet.
Timothy Trippelb93cc6a2021-07-30 02:34:38 +0000240 # If no .c file exists --> All functions are undefined.
Timothy Trippel7f327d82021-10-29 02:26:39 +0000241 if c_file.is_file():
242 implemented_funcs |= self._get_funcs(c_file)
243 return implemented_funcs
Timothy Trippelb93cc6a2021-07-30 02:34:38 +0000244
245 def _get_funcs(self, file_path):
Timothy Trippel76dd6462021-10-27 22:26:20 +0000246 func_pattern = re.compile(r"^dif_result_t (dif_.*)\(.*")
Timothy Trippelb93cc6a2021-07-30 02:34:38 +0000247 funcs = set()
248 with open(file_path, "r") as fp:
249 for line in fp:
250 result = func_pattern.search(line)
251 if result is not None:
252 funcs.add(result.group(1))
253 return funcs
254
Timothy Trippela7cd9a22021-08-12 21:03:49 +0000255
Timothy Trippel7f327d82021-10-29 02:26:39 +0000256def get_list_of_difs(shared_headers: List[str]) -> Set[str]:
Timothy Trippelb93cc6a2021-07-30 02:34:38 +0000257 """Get a list of the root filenames of the DIFs.
258
259 Args:
Timothy Trippelb93cc6a2021-07-30 02:34:38 +0000260 shared_headers: Header file(s) shared amongst DIFs.
261
262 Returns:
Timothy Trippelec466392021-10-28 00:27:26 +0000263 difs: Set of IP DIF library names.
Timothy Trippelb93cc6a2021-07-30 02:34:38 +0000264 """
Timothy Trippel7f327d82021-10-29 02:26:39 +0000265 dif_headers = sorted(DIFS_RELATIVE_PATH.glob("*.h"))
Timothy Trippelec466392021-10-28 00:27:26 +0000266 difs = list(map(lambda s: Path(s).resolve().stem, dif_headers))
Timothy Trippelb93cc6a2021-07-30 02:34:38 +0000267 for header in shared_headers:
268 if header in difs:
269 difs.remove(header)
270 return difs
271
272
273def print_status_table(dif_statuses: List[DIFStatus],
274 table_format: str) -> None:
275 """Print a table of DIF status information to STDOUT.
276
277 Args:
278 dif_statuses: List of DIFStatus objects containing metadata about DIF
279 development states.
280
281 Returns:
282 None
283 """
284 # Build the table.
285 rows = []
286 headers = [
287 "IP", "DIF Updated", "HW Updated", "DIF Contributor*",
288 "HW Contributor*", "Functions\nDefined", "Functions\nImplemented",
289 "Stage"
290 ]
291 for dif_status in dif_statuses:
292 # Color code last modified dates.
Srikrishna Iyer05fc3e12021-08-20 09:50:21 -0700293 # Limit the last modified strings to 10 characters to only print the
294 # date (YYYY-MM-DD).
295 hw_last_modified = dif_status.hw_last_modified[:10]
296 dif_last_modified = dif_status.dif_last_modified[:10]
Timothy Trippelb93cc6a2021-07-30 02:34:38 +0000297 if dif_status.hw_last_modified > dif_status.dif_last_modified:
Srikrishna Iyer05fc3e12021-08-20 09:50:21 -0700298 hw_last_modified = colored(hw_last_modified, "yellow")
299 dif_last_modified = colored(dif_last_modified, "yellow")
Timothy Trippelb93cc6a2021-07-30 02:34:38 +0000300 # Color code API complete status.
301 if dif_status.api_complete:
302 num_funcs_defined = colored(dif_status.num_functions_defined,
303 "green")
304 num_funcs_implemented = colored(
305 dif_status.num_functions_implemented, "green")
306 else:
307 num_funcs_defined = colored(dif_status.num_functions_defined,
308 "red")
309 num_funcs_implemented = colored(
310 dif_status.num_functions_implemented, "red")
311
312 # Add row to table (printing one contributor email per line).
313 rows.append([
314 dif_status.ip, dif_last_modified, hw_last_modified,
315 "\n".join(dif_status.dif_main_contributors),
316 "\n".join(dif_status.hw_main_contributors), num_funcs_defined,
317 num_funcs_implemented, dif_status.lifecycle_state
318 ])
319
320 # Print the table and legend.
321 print("DIF Statuses:")
322 print(tabulate(rows, headers, tablefmt=table_format))
323 print("""*Only the top two contributors (by LOC) """
324 """for each component are listed.""")
325 print(colored("Yellow", "yellow"),
326 "\t= HW has been updated since the DIF.")
327 print(
328 colored("Green", "green"),
329 """\t= DIF API, as defined in the current header file, is complete. """
330 """Note, the header file may lack necessary API functionality.""")
331 print(colored("Red", "red"),
Srikrishna Iyer05fc3e12021-08-20 09:50:21 -0700332 ("\t= DIF API is incomplete, as defined in the header file or the "
333 "work has not yet begun."))
Timothy Trippelb93cc6a2021-07-30 02:34:38 +0000334
335
Timothy Trippel1d21f072021-10-28 23:42:59 +0000336def print_unimplemented_difs(dif_statuses: List[DIFStatus],
337 table_format: str) -> None:
Timothy Trippela7cd9a22021-08-12 21:03:49 +0000338 """Print a table of specific functions names DIF functions to STDOUT.
Timothy Trippelb93cc6a2021-07-30 02:34:38 +0000339
340 Args:
341 dif_statuses: List of DIFStatus objects containing metadata about DIF
342 development states.
Timothy Trippela7cd9a22021-08-12 21:03:49 +0000343 table_format: Format of output table to print. See tabulate module.
Timothy Trippelb93cc6a2021-07-30 02:34:38 +0000344
345 Returns:
346 None
347 """
Timothy Trippela7cd9a22021-08-12 21:03:49 +0000348 # Build and print table.
Timothy Trippel1d21f072021-10-28 23:42:59 +0000349 print("Unimplemented Functions:")
Timothy Trippelb93cc6a2021-07-30 02:34:38 +0000350 rows = []
351 headers = ["IP", "Function"]
352 for dif_status in dif_statuses:
Timothy Trippel1d21f072021-10-28 23:42:59 +0000353 if not dif_status.api_complete:
354 rows.append(
355 [dif_status.ip, "\n".join(dif_status.funcs_unimplemented)])
Timothy Trippelb93cc6a2021-07-30 02:34:38 +0000356 print(tabulate(rows, headers, tablefmt=table_format))
357
358
359def main(argv):
Timothy Trippela7cd9a22021-08-12 21:03:49 +0000360 # Process args and set logging level.
361 # TODO: parallelize data scraping so its much faster
Timothy Trippelb93cc6a2021-07-30 02:34:38 +0000362 parser = argparse.ArgumentParser(
363 prog="check_dif_statuses",
364 formatter_class=argparse.RawDescriptionHelpFormatter)
365 parser.add_argument(
Srikrishna Iyer05fc3e12021-08-20 09:50:21 -0700366 "--top-hjson",
Timothy Trippel76dd6462021-10-27 22:26:20 +0000367 help="""Path to the top-level HJSON configuration file relative to
368 REPO_TOP.""")
Srikrishna Iyer05fc3e12021-08-20 09:50:21 -0700369 parser.add_argument(
Timothy Trippelb93cc6a2021-07-30 02:34:38 +0000370 "--show-unimplemented",
371 action="store_true",
372 help="""Show unimplemented functions for each incomplete DIF.""")
373 parser.add_argument("--table-format",
374 type=str,
375 choices=["grid", "github", "pipe"],
376 default="grid",
377 help="""Format to print status tables in.""")
378 args = parser.parse_args(argv)
Timothy Trippela7cd9a22021-08-12 21:03:49 +0000379 logging.basicConfig(level=logging.WARNING)
Timothy Trippelb93cc6a2021-07-30 02:34:38 +0000380
Timothy Trippel7f327d82021-10-29 02:26:39 +0000381 # Make sure to call this script from REPO_TOP.
Timothy Trippel1d21f072021-10-28 23:42:59 +0000382 if Path.cwd() != REPO_TOP:
383 logging.error(f"Must call script from \"$REPO_TOP\": {REPO_TOP}")
384 sys.exit(1)
385
Srikrishna Iyer05fc3e12021-08-20 09:50:21 -0700386 if args.top_hjson:
387 # Get the list of IP blocks by invoking the topgen tool.
Timothy Trippelec466392021-10-28 00:27:26 +0000388 topgen_tool = REPO_TOP / "util/topgen.py"
389 top_hjson = REPO_TOP / args.top_hjson
390 top_level = top_hjson.stem
Srikrishna Iyer05fc3e12021-08-20 09:50:21 -0700391 # yapf: disable
392 topgen_process = subprocess.run([topgen_tool, "-t", top_hjson,
Timothy Trippel76dd6462021-10-27 22:26:20 +0000393 "--get_blocks", "-o", REPO_TOP],
Srikrishna Iyer05fc3e12021-08-20 09:50:21 -0700394 universal_newlines=True,
395 stdout=subprocess.PIPE,
396 check=True)
397 # yapf: enable
398 # All DIF names are prefixed with `dif_`.
399 difs = {f"dif_{dif.strip()}" for dif in topgen_process.stdout.split()}
400 else:
401 # Get list of all DIF basenames.
402 # TODO: automatically get the list below by cross referencing DIF names
403 # with IP block names. Hardcoded for now.
404 print("WARNING: It is recommended to pass the --top-hjson switch to "
405 "get a more accurate representation of the DIF progress. The "
406 "list of IPs for which no DIF sources exist is unknown.")
Timothy Trippele3f8a822021-09-17 06:09:28 +0000407 shared_headers = ["dif_base"]
Srikrishna Iyer05fc3e12021-08-20 09:50:21 -0700408 top_level = "top_earlgrey"
Timothy Trippel7f327d82021-10-29 02:26:39 +0000409 difs = get_list_of_difs(shared_headers)
Timothy Trippelb93cc6a2021-07-30 02:34:38 +0000410
411 # Get DIF statuses (while displaying a progress bar).
412 dif_statuses = []
413 progress_bar = enlighten.Counter(total=len(difs),
414 desc="Analyzing statuses of DIFs ...",
415 unit="DIFs")
416 for dif in difs:
Timothy Trippel7f327d82021-10-29 02:26:39 +0000417 dif_statuses.append(DIFStatus(top_level, dif))
Timothy Trippelb93cc6a2021-07-30 02:34:38 +0000418 progress_bar.update()
419
420 # Build table and print it to STDOUT.
421 print_status_table(dif_statuses, args.table_format)
422 if args.show_unimplemented:
Timothy Trippel1d21f072021-10-28 23:42:59 +0000423 print_unimplemented_difs(dif_statuses, args.table_format)
Timothy Trippelb93cc6a2021-07-30 02:34:38 +0000424
425
426if __name__ == "__main__":
427 main(sys.argv[1:])