1caa642bfSMauro Carvalho Chehab#!/usr/bin/env python3 2caa642bfSMauro Carvalho Chehab# pylint: disable=R0902,R0911,R0912,R0914,R0915 3caa642bfSMauro Carvalho Chehab# Copyright(c) 2025: Mauro Carvalho Chehab <mchehab@kernel.org>. 4caa642bfSMauro Carvalho Chehab# SPDX-License-Identifier: GPL-2.0 5caa642bfSMauro Carvalho Chehab 6caa642bfSMauro Carvalho Chehab 7caa642bfSMauro Carvalho Chehab""" 8caa642bfSMauro Carvalho ChehabLibrary to parse the Linux Feature files and produce a ReST book. 9caa642bfSMauro Carvalho Chehab""" 10caa642bfSMauro Carvalho Chehab 11caa642bfSMauro Carvalho Chehabimport os 12caa642bfSMauro Carvalho Chehabimport re 13caa642bfSMauro Carvalho Chehabimport sys 14caa642bfSMauro Carvalho Chehab 15caa642bfSMauro Carvalho Chehabfrom glob import iglob 16caa642bfSMauro Carvalho Chehab 17caa642bfSMauro Carvalho Chehab 18caa642bfSMauro Carvalho Chehabclass ParseFeature: 19caa642bfSMauro Carvalho Chehab """ 20caa642bfSMauro Carvalho Chehab Parses Documentation/features, allowing to generate ReST documentation 21caa642bfSMauro Carvalho Chehab from it. 22caa642bfSMauro Carvalho Chehab """ 23caa642bfSMauro Carvalho Chehab 24*ef6aa110SMauro Carvalho Chehab #: feature header string. 25caa642bfSMauro Carvalho Chehab h_name = "Feature" 26*ef6aa110SMauro Carvalho Chehab 27*ef6aa110SMauro Carvalho Chehab #: Kernel config header string. 28caa642bfSMauro Carvalho Chehab h_kconfig = "Kconfig" 29*ef6aa110SMauro Carvalho Chehab 30*ef6aa110SMauro Carvalho Chehab #: description header string. 31caa642bfSMauro Carvalho Chehab h_description = "Description" 32*ef6aa110SMauro Carvalho Chehab 33*ef6aa110SMauro Carvalho Chehab #: subsystem header string. 34caa642bfSMauro Carvalho Chehab h_subsys = "Subsystem" 35*ef6aa110SMauro Carvalho Chehab 36*ef6aa110SMauro Carvalho Chehab #: status header string. 37caa642bfSMauro Carvalho Chehab h_status = "Status" 38*ef6aa110SMauro Carvalho Chehab 39*ef6aa110SMauro Carvalho Chehab #: architecture header string. 40caa642bfSMauro Carvalho Chehab h_arch = "Architecture" 41caa642bfSMauro Carvalho Chehab 42*ef6aa110SMauro Carvalho Chehab #: Sort order for status. Others will be mapped at the end. 43caa642bfSMauro Carvalho Chehab status_map = { 44caa642bfSMauro Carvalho Chehab "ok": 0, 45caa642bfSMauro Carvalho Chehab "TODO": 1, 46caa642bfSMauro Carvalho Chehab "N/A": 2, 47caa642bfSMauro Carvalho Chehab # The only missing status is "..", which was mapped as "---", 48caa642bfSMauro Carvalho Chehab # as this is an special ReST cell value. Let it get the 49caa642bfSMauro Carvalho Chehab # default order (99). 50caa642bfSMauro Carvalho Chehab } 51caa642bfSMauro Carvalho Chehab 52caa642bfSMauro Carvalho Chehab def __init__(self, prefix, debug=0, enable_fname=False): 53caa642bfSMauro Carvalho Chehab """ 54*ef6aa110SMauro Carvalho Chehab Sets internal variables. 55caa642bfSMauro Carvalho Chehab """ 56caa642bfSMauro Carvalho Chehab 57caa642bfSMauro Carvalho Chehab self.prefix = prefix 58caa642bfSMauro Carvalho Chehab self.debug = debug 59caa642bfSMauro Carvalho Chehab self.enable_fname = enable_fname 60caa642bfSMauro Carvalho Chehab 61caa642bfSMauro Carvalho Chehab self.data = {} 62caa642bfSMauro Carvalho Chehab 63caa642bfSMauro Carvalho Chehab # Initial maximum values use just the headers 64caa642bfSMauro Carvalho Chehab self.max_size_name = len(self.h_name) 65caa642bfSMauro Carvalho Chehab self.max_size_kconfig = len(self.h_kconfig) 66caa642bfSMauro Carvalho Chehab self.max_size_description = len(self.h_description) 67caa642bfSMauro Carvalho Chehab self.max_size_desc_word = 0 68caa642bfSMauro Carvalho Chehab self.max_size_subsys = len(self.h_subsys) 69caa642bfSMauro Carvalho Chehab self.max_size_status = len(self.h_status) 70caa642bfSMauro Carvalho Chehab self.max_size_arch = len(self.h_arch) 71caa642bfSMauro Carvalho Chehab self.max_size_arch_with_header = self.max_size_arch + self.max_size_arch 72caa642bfSMauro Carvalho Chehab self.description_size = 1 73caa642bfSMauro Carvalho Chehab 74caa642bfSMauro Carvalho Chehab self.msg = "" 75caa642bfSMauro Carvalho Chehab 76caa642bfSMauro Carvalho Chehab def emit(self, msg="", end="\n"): 77*ef6aa110SMauro Carvalho Chehab """Helper function to append a new message for feature output.""" 78*ef6aa110SMauro Carvalho Chehab 79caa642bfSMauro Carvalho Chehab self.msg += msg + end 80caa642bfSMauro Carvalho Chehab 81caa642bfSMauro Carvalho Chehab def parse_error(self, fname, ln, msg, data=None): 82caa642bfSMauro Carvalho Chehab """ 83*ef6aa110SMauro Carvalho Chehab Displays an error message, printing file name and line. 84caa642bfSMauro Carvalho Chehab """ 85caa642bfSMauro Carvalho Chehab 86caa642bfSMauro Carvalho Chehab if ln: 87caa642bfSMauro Carvalho Chehab fname += f"#{ln}" 88caa642bfSMauro Carvalho Chehab 89caa642bfSMauro Carvalho Chehab print(f"Warning: file {fname}: {msg}", file=sys.stderr, end="") 90caa642bfSMauro Carvalho Chehab 91caa642bfSMauro Carvalho Chehab if data: 92caa642bfSMauro Carvalho Chehab data = data.rstrip() 93caa642bfSMauro Carvalho Chehab print(f":\n\t{data}", file=sys.stderr) 94caa642bfSMauro Carvalho Chehab else: 95caa642bfSMauro Carvalho Chehab print("", file=sys.stderr) 96caa642bfSMauro Carvalho Chehab 97caa642bfSMauro Carvalho Chehab def parse_feat_file(self, fname): 98*ef6aa110SMauro Carvalho Chehab """Parses a single arch-support.txt feature file.""" 99caa642bfSMauro Carvalho Chehab 100caa642bfSMauro Carvalho Chehab if os.path.isdir(fname): 101caa642bfSMauro Carvalho Chehab return 102caa642bfSMauro Carvalho Chehab 103caa642bfSMauro Carvalho Chehab base = os.path.basename(fname) 104caa642bfSMauro Carvalho Chehab 105caa642bfSMauro Carvalho Chehab if base != "arch-support.txt": 106caa642bfSMauro Carvalho Chehab if self.debug: 107caa642bfSMauro Carvalho Chehab print(f"ignoring {fname}", file=sys.stderr) 108caa642bfSMauro Carvalho Chehab return 109caa642bfSMauro Carvalho Chehab 110caa642bfSMauro Carvalho Chehab subsys = os.path.dirname(fname).split("/")[-2] 111caa642bfSMauro Carvalho Chehab self.max_size_subsys = max(self.max_size_subsys, len(subsys)) 112caa642bfSMauro Carvalho Chehab 113caa642bfSMauro Carvalho Chehab feature_name = "" 114caa642bfSMauro Carvalho Chehab kconfig = "" 115caa642bfSMauro Carvalho Chehab description = "" 116caa642bfSMauro Carvalho Chehab comments = "" 117caa642bfSMauro Carvalho Chehab arch_table = {} 118caa642bfSMauro Carvalho Chehab 119caa642bfSMauro Carvalho Chehab if self.debug > 1: 120caa642bfSMauro Carvalho Chehab print(f"Opening {fname}", file=sys.stderr) 121caa642bfSMauro Carvalho Chehab 122caa642bfSMauro Carvalho Chehab if self.enable_fname: 123caa642bfSMauro Carvalho Chehab full_fname = os.path.abspath(fname) 124caa642bfSMauro Carvalho Chehab self.emit(f".. FILE {full_fname}") 125caa642bfSMauro Carvalho Chehab 126caa642bfSMauro Carvalho Chehab with open(fname, encoding="utf-8") as f: 127caa642bfSMauro Carvalho Chehab for ln, line in enumerate(f, start=1): 128caa642bfSMauro Carvalho Chehab line = line.strip() 129caa642bfSMauro Carvalho Chehab 130caa642bfSMauro Carvalho Chehab match = re.match(r"^\#\s+Feature\s+name:\s*(.*\S)", line) 131caa642bfSMauro Carvalho Chehab if match: 132caa642bfSMauro Carvalho Chehab feature_name = match.group(1) 133caa642bfSMauro Carvalho Chehab 134caa642bfSMauro Carvalho Chehab self.max_size_name = max(self.max_size_name, 135caa642bfSMauro Carvalho Chehab len(feature_name)) 136caa642bfSMauro Carvalho Chehab continue 137caa642bfSMauro Carvalho Chehab 138caa642bfSMauro Carvalho Chehab match = re.match(r"^\#\s+Kconfig:\s*(.*\S)", line) 139caa642bfSMauro Carvalho Chehab if match: 140caa642bfSMauro Carvalho Chehab kconfig = match.group(1) 141caa642bfSMauro Carvalho Chehab 142caa642bfSMauro Carvalho Chehab self.max_size_kconfig = max(self.max_size_kconfig, 143caa642bfSMauro Carvalho Chehab len(kconfig)) 144caa642bfSMauro Carvalho Chehab continue 145caa642bfSMauro Carvalho Chehab 146caa642bfSMauro Carvalho Chehab match = re.match(r"^\#\s+description:\s*(.*\S)", line) 147caa642bfSMauro Carvalho Chehab if match: 148caa642bfSMauro Carvalho Chehab description = match.group(1) 149caa642bfSMauro Carvalho Chehab 150caa642bfSMauro Carvalho Chehab self.max_size_description = max(self.max_size_description, 151caa642bfSMauro Carvalho Chehab len(description)) 152caa642bfSMauro Carvalho Chehab 153caa642bfSMauro Carvalho Chehab words = re.split(r"\s+", line)[1:] 154caa642bfSMauro Carvalho Chehab for word in words: 155caa642bfSMauro Carvalho Chehab self.max_size_desc_word = max(self.max_size_desc_word, 156caa642bfSMauro Carvalho Chehab len(word)) 157caa642bfSMauro Carvalho Chehab 158caa642bfSMauro Carvalho Chehab continue 159caa642bfSMauro Carvalho Chehab 160caa642bfSMauro Carvalho Chehab if re.search(r"^\\s*$", line): 161caa642bfSMauro Carvalho Chehab continue 162caa642bfSMauro Carvalho Chehab 163caa642bfSMauro Carvalho Chehab if re.match(r"^\s*\-+\s*$", line): 164caa642bfSMauro Carvalho Chehab continue 165caa642bfSMauro Carvalho Chehab 166caa642bfSMauro Carvalho Chehab if re.search(r"^\s*\|\s*arch\s*\|\s*status\s*\|\s*$", line): 167caa642bfSMauro Carvalho Chehab continue 168caa642bfSMauro Carvalho Chehab 169caa642bfSMauro Carvalho Chehab match = re.match(r"^\#\s*(.*)$", line) 170caa642bfSMauro Carvalho Chehab if match: 171caa642bfSMauro Carvalho Chehab comments += match.group(1) 172caa642bfSMauro Carvalho Chehab continue 173caa642bfSMauro Carvalho Chehab 174caa642bfSMauro Carvalho Chehab match = re.match(r"^\s*\|\s*(\S+):\s*\|\s*(\S+)\s*\|\s*$", line) 175caa642bfSMauro Carvalho Chehab if match: 176caa642bfSMauro Carvalho Chehab arch = match.group(1) 177caa642bfSMauro Carvalho Chehab status = match.group(2) 178caa642bfSMauro Carvalho Chehab 179caa642bfSMauro Carvalho Chehab self.max_size_status = max(self.max_size_status, 180caa642bfSMauro Carvalho Chehab len(status)) 181caa642bfSMauro Carvalho Chehab self.max_size_arch = max(self.max_size_arch, len(arch)) 182caa642bfSMauro Carvalho Chehab 183caa642bfSMauro Carvalho Chehab if status == "..": 184caa642bfSMauro Carvalho Chehab status = "---" 185caa642bfSMauro Carvalho Chehab 186caa642bfSMauro Carvalho Chehab arch_table[arch] = status 187caa642bfSMauro Carvalho Chehab 188caa642bfSMauro Carvalho Chehab continue 189caa642bfSMauro Carvalho Chehab 190caa642bfSMauro Carvalho Chehab self.parse_error(fname, ln, "Line is invalid", line) 191caa642bfSMauro Carvalho Chehab 192caa642bfSMauro Carvalho Chehab if not feature_name: 193caa642bfSMauro Carvalho Chehab self.parse_error(fname, 0, "Feature name not found") 194caa642bfSMauro Carvalho Chehab return 195caa642bfSMauro Carvalho Chehab if not subsys: 196caa642bfSMauro Carvalho Chehab self.parse_error(fname, 0, "Subsystem not found") 197caa642bfSMauro Carvalho Chehab return 198caa642bfSMauro Carvalho Chehab if not kconfig: 199caa642bfSMauro Carvalho Chehab self.parse_error(fname, 0, "Kconfig not found") 200caa642bfSMauro Carvalho Chehab return 201caa642bfSMauro Carvalho Chehab if not description: 202caa642bfSMauro Carvalho Chehab self.parse_error(fname, 0, "Description not found") 203caa642bfSMauro Carvalho Chehab return 204caa642bfSMauro Carvalho Chehab if not arch_table: 205caa642bfSMauro Carvalho Chehab self.parse_error(fname, 0, "Architecture table not found") 206caa642bfSMauro Carvalho Chehab return 207caa642bfSMauro Carvalho Chehab 208caa642bfSMauro Carvalho Chehab self.data[feature_name] = { 209caa642bfSMauro Carvalho Chehab "where": fname, 210caa642bfSMauro Carvalho Chehab "subsys": subsys, 211caa642bfSMauro Carvalho Chehab "kconfig": kconfig, 212caa642bfSMauro Carvalho Chehab "description": description, 213caa642bfSMauro Carvalho Chehab "comments": comments, 214caa642bfSMauro Carvalho Chehab "table": arch_table, 215caa642bfSMauro Carvalho Chehab } 216caa642bfSMauro Carvalho Chehab 217caa642bfSMauro Carvalho Chehab self.max_size_arch_with_header = self.max_size_arch + len(self.h_arch) 218caa642bfSMauro Carvalho Chehab 219caa642bfSMauro Carvalho Chehab def parse(self): 220*ef6aa110SMauro Carvalho Chehab """Parses all arch-support.txt feature files inside self.prefix.""" 221caa642bfSMauro Carvalho Chehab 222caa642bfSMauro Carvalho Chehab path = os.path.expanduser(self.prefix) 223caa642bfSMauro Carvalho Chehab 224caa642bfSMauro Carvalho Chehab if self.debug > 2: 225caa642bfSMauro Carvalho Chehab print(f"Running parser for {path}") 226caa642bfSMauro Carvalho Chehab 227caa642bfSMauro Carvalho Chehab example_path = os.path.join(path, "arch-support.txt") 228caa642bfSMauro Carvalho Chehab 229caa642bfSMauro Carvalho Chehab for fname in iglob(os.path.join(path, "**"), recursive=True): 230caa642bfSMauro Carvalho Chehab if fname != example_path: 231caa642bfSMauro Carvalho Chehab self.parse_feat_file(fname) 232caa642bfSMauro Carvalho Chehab 233caa642bfSMauro Carvalho Chehab return self.data 234caa642bfSMauro Carvalho Chehab 235caa642bfSMauro Carvalho Chehab def output_arch_table(self, arch, feat=None): 236caa642bfSMauro Carvalho Chehab """ 237caa642bfSMauro Carvalho Chehab Output feature(s) for a given architecture. 238caa642bfSMauro Carvalho Chehab """ 239caa642bfSMauro Carvalho Chehab 240caa642bfSMauro Carvalho Chehab title = f"Feature status on {arch} architecture" 241caa642bfSMauro Carvalho Chehab 242caa642bfSMauro Carvalho Chehab self.emit("=" * len(title)) 243caa642bfSMauro Carvalho Chehab self.emit(title) 244caa642bfSMauro Carvalho Chehab self.emit("=" * len(title)) 245caa642bfSMauro Carvalho Chehab self.emit() 246caa642bfSMauro Carvalho Chehab 247caa642bfSMauro Carvalho Chehab self.emit("=" * self.max_size_subsys + " ", end="") 248caa642bfSMauro Carvalho Chehab self.emit("=" * self.max_size_name + " ", end="") 249caa642bfSMauro Carvalho Chehab self.emit("=" * self.max_size_kconfig + " ", end="") 250caa642bfSMauro Carvalho Chehab self.emit("=" * self.max_size_status + " ", end="") 251caa642bfSMauro Carvalho Chehab self.emit("=" * self.max_size_description) 252caa642bfSMauro Carvalho Chehab 253caa642bfSMauro Carvalho Chehab self.emit(f"{self.h_subsys:<{self.max_size_subsys}} ", end="") 254caa642bfSMauro Carvalho Chehab self.emit(f"{self.h_name:<{self.max_size_name}} ", end="") 255caa642bfSMauro Carvalho Chehab self.emit(f"{self.h_kconfig:<{self.max_size_kconfig}} ", end="") 256caa642bfSMauro Carvalho Chehab self.emit(f"{self.h_status:<{self.max_size_status}} ", end="") 257caa642bfSMauro Carvalho Chehab self.emit(f"{self.h_description:<{self.max_size_description}}") 258caa642bfSMauro Carvalho Chehab 259caa642bfSMauro Carvalho Chehab self.emit("=" * self.max_size_subsys + " ", end="") 260caa642bfSMauro Carvalho Chehab self.emit("=" * self.max_size_name + " ", end="") 261caa642bfSMauro Carvalho Chehab self.emit("=" * self.max_size_kconfig + " ", end="") 262caa642bfSMauro Carvalho Chehab self.emit("=" * self.max_size_status + " ", end="") 263caa642bfSMauro Carvalho Chehab self.emit("=" * self.max_size_description) 264caa642bfSMauro Carvalho Chehab 265caa642bfSMauro Carvalho Chehab sorted_features = sorted(self.data.keys(), 266caa642bfSMauro Carvalho Chehab key=lambda x: (self.data[x]["subsys"], 267caa642bfSMauro Carvalho Chehab x.lower())) 268caa642bfSMauro Carvalho Chehab 269caa642bfSMauro Carvalho Chehab for name in sorted_features: 270caa642bfSMauro Carvalho Chehab if feat and name != feat: 271caa642bfSMauro Carvalho Chehab continue 272caa642bfSMauro Carvalho Chehab 273caa642bfSMauro Carvalho Chehab arch_table = self.data[name]["table"] 274caa642bfSMauro Carvalho Chehab 275caa642bfSMauro Carvalho Chehab if not arch in arch_table: 276caa642bfSMauro Carvalho Chehab continue 277caa642bfSMauro Carvalho Chehab 278caa642bfSMauro Carvalho Chehab self.emit(f"{self.data[name]['subsys']:<{self.max_size_subsys}} ", 279caa642bfSMauro Carvalho Chehab end="") 280caa642bfSMauro Carvalho Chehab self.emit(f"{name:<{self.max_size_name}} ", end="") 281caa642bfSMauro Carvalho Chehab self.emit(f"{self.data[name]['kconfig']:<{self.max_size_kconfig}} ", 282caa642bfSMauro Carvalho Chehab end="") 283caa642bfSMauro Carvalho Chehab self.emit(f"{arch_table[arch]:<{self.max_size_status}} ", 284caa642bfSMauro Carvalho Chehab end="") 285caa642bfSMauro Carvalho Chehab self.emit(f"{self.data[name]['description']}") 286caa642bfSMauro Carvalho Chehab 287caa642bfSMauro Carvalho Chehab self.emit("=" * self.max_size_subsys + " ", end="") 288caa642bfSMauro Carvalho Chehab self.emit("=" * self.max_size_name + " ", end="") 289caa642bfSMauro Carvalho Chehab self.emit("=" * self.max_size_kconfig + " ", end="") 290caa642bfSMauro Carvalho Chehab self.emit("=" * self.max_size_status + " ", end="") 291caa642bfSMauro Carvalho Chehab self.emit("=" * self.max_size_description) 292caa642bfSMauro Carvalho Chehab 293caa642bfSMauro Carvalho Chehab return self.msg 294caa642bfSMauro Carvalho Chehab 295caa642bfSMauro Carvalho Chehab def output_feature(self, feat): 296caa642bfSMauro Carvalho Chehab """ 297*ef6aa110SMauro Carvalho Chehab Output a feature on all architectures. 298caa642bfSMauro Carvalho Chehab """ 299caa642bfSMauro Carvalho Chehab 300caa642bfSMauro Carvalho Chehab title = f"Feature {feat}" 301caa642bfSMauro Carvalho Chehab 302caa642bfSMauro Carvalho Chehab self.emit("=" * len(title)) 303caa642bfSMauro Carvalho Chehab self.emit(title) 304caa642bfSMauro Carvalho Chehab self.emit("=" * len(title)) 305caa642bfSMauro Carvalho Chehab self.emit() 306caa642bfSMauro Carvalho Chehab 307caa642bfSMauro Carvalho Chehab if not feat in self.data: 308caa642bfSMauro Carvalho Chehab return 309caa642bfSMauro Carvalho Chehab 310caa642bfSMauro Carvalho Chehab if self.data[feat]["subsys"]: 311caa642bfSMauro Carvalho Chehab self.emit(f":Subsystem: {self.data[feat]['subsys']}") 312caa642bfSMauro Carvalho Chehab if self.data[feat]["kconfig"]: 313caa642bfSMauro Carvalho Chehab self.emit(f":Kconfig: {self.data[feat]['kconfig']}") 314caa642bfSMauro Carvalho Chehab 315caa642bfSMauro Carvalho Chehab desc = self.data[feat]["description"] 316caa642bfSMauro Carvalho Chehab desc = desc[0].upper() + desc[1:] 317caa642bfSMauro Carvalho Chehab desc = desc.rstrip(". \t") 318caa642bfSMauro Carvalho Chehab self.emit(f"\n{desc}.\n") 319caa642bfSMauro Carvalho Chehab 320caa642bfSMauro Carvalho Chehab com = self.data[feat]["comments"].strip() 321caa642bfSMauro Carvalho Chehab if com: 322caa642bfSMauro Carvalho Chehab self.emit("Comments") 323caa642bfSMauro Carvalho Chehab self.emit("--------") 324caa642bfSMauro Carvalho Chehab self.emit(f"\n{com}\n") 325caa642bfSMauro Carvalho Chehab 326caa642bfSMauro Carvalho Chehab self.emit("=" * self.max_size_arch + " ", end="") 327caa642bfSMauro Carvalho Chehab self.emit("=" * self.max_size_status) 328caa642bfSMauro Carvalho Chehab 329caa642bfSMauro Carvalho Chehab self.emit(f"{self.h_arch:<{self.max_size_arch}} ", end="") 330caa642bfSMauro Carvalho Chehab self.emit(f"{self.h_status:<{self.max_size_status}}") 331caa642bfSMauro Carvalho Chehab 332caa642bfSMauro Carvalho Chehab self.emit("=" * self.max_size_arch + " ", end="") 333caa642bfSMauro Carvalho Chehab self.emit("=" * self.max_size_status) 334caa642bfSMauro Carvalho Chehab 335caa642bfSMauro Carvalho Chehab arch_table = self.data[feat]["table"] 336caa642bfSMauro Carvalho Chehab for arch in sorted(arch_table.keys()): 337caa642bfSMauro Carvalho Chehab self.emit(f"{arch:<{self.max_size_arch}} ", end="") 338caa642bfSMauro Carvalho Chehab self.emit(f"{arch_table[arch]:<{self.max_size_status}}") 339caa642bfSMauro Carvalho Chehab 340caa642bfSMauro Carvalho Chehab self.emit("=" * self.max_size_arch + " ", end="") 341caa642bfSMauro Carvalho Chehab self.emit("=" * self.max_size_status) 342caa642bfSMauro Carvalho Chehab 343caa642bfSMauro Carvalho Chehab return self.msg 344caa642bfSMauro Carvalho Chehab 345caa642bfSMauro Carvalho Chehab def matrix_lines(self, desc_size, max_size_status, header): 346caa642bfSMauro Carvalho Chehab """ 347*ef6aa110SMauro Carvalho Chehab Helper function to split element tables at the output matrix. 348caa642bfSMauro Carvalho Chehab """ 349caa642bfSMauro Carvalho Chehab 350caa642bfSMauro Carvalho Chehab if header: 351caa642bfSMauro Carvalho Chehab ln_marker = "=" 352caa642bfSMauro Carvalho Chehab else: 353caa642bfSMauro Carvalho Chehab ln_marker = "-" 354caa642bfSMauro Carvalho Chehab 355caa642bfSMauro Carvalho Chehab self.emit("+" + ln_marker * self.max_size_name + "+", end="") 356caa642bfSMauro Carvalho Chehab self.emit(ln_marker * desc_size, end="") 357caa642bfSMauro Carvalho Chehab self.emit("+" + ln_marker * max_size_status + "+") 358caa642bfSMauro Carvalho Chehab 359caa642bfSMauro Carvalho Chehab def output_matrix(self): 360caa642bfSMauro Carvalho Chehab """ 361caa642bfSMauro Carvalho Chehab Generates a set of tables, groped by subsystem, containing 362caa642bfSMauro Carvalho Chehab what's the feature state on each architecture. 363caa642bfSMauro Carvalho Chehab """ 364caa642bfSMauro Carvalho Chehab 365caa642bfSMauro Carvalho Chehab title = "Feature status on all architectures" 366caa642bfSMauro Carvalho Chehab 367caa642bfSMauro Carvalho Chehab self.emit("=" * len(title)) 368caa642bfSMauro Carvalho Chehab self.emit(title) 369caa642bfSMauro Carvalho Chehab self.emit("=" * len(title)) 370caa642bfSMauro Carvalho Chehab self.emit() 371caa642bfSMauro Carvalho Chehab 372caa642bfSMauro Carvalho Chehab desc_title = f"{self.h_kconfig} / {self.h_description}" 373caa642bfSMauro Carvalho Chehab 374caa642bfSMauro Carvalho Chehab desc_size = self.max_size_kconfig + 4 375caa642bfSMauro Carvalho Chehab if not self.description_size: 376caa642bfSMauro Carvalho Chehab desc_size = max(self.max_size_description, desc_size) 377caa642bfSMauro Carvalho Chehab else: 378caa642bfSMauro Carvalho Chehab desc_size = max(self.description_size, desc_size) 379caa642bfSMauro Carvalho Chehab 380caa642bfSMauro Carvalho Chehab desc_size = max(self.max_size_desc_word, desc_size, len(desc_title)) 381caa642bfSMauro Carvalho Chehab 382caa642bfSMauro Carvalho Chehab notcompat = "Not compatible" 383caa642bfSMauro Carvalho Chehab self.max_size_status = max(self.max_size_status, len(notcompat)) 384caa642bfSMauro Carvalho Chehab 385caa642bfSMauro Carvalho Chehab min_status_size = self.max_size_status + self.max_size_arch + 4 386caa642bfSMauro Carvalho Chehab max_size_status = max(min_status_size, self.max_size_status) 387caa642bfSMauro Carvalho Chehab 388caa642bfSMauro Carvalho Chehab h_status_per_arch = "Status per architecture" 389caa642bfSMauro Carvalho Chehab max_size_status = max(max_size_status, len(h_status_per_arch)) 390caa642bfSMauro Carvalho Chehab 391caa642bfSMauro Carvalho Chehab cur_subsys = None 392caa642bfSMauro Carvalho Chehab for name in sorted(self.data.keys(), 393caa642bfSMauro Carvalho Chehab key=lambda x: (self.data[x]["subsys"], x.lower())): 394caa642bfSMauro Carvalho Chehab if not cur_subsys or cur_subsys != self.data[name]["subsys"]: 395caa642bfSMauro Carvalho Chehab if cur_subsys: 396caa642bfSMauro Carvalho Chehab self.emit() 397caa642bfSMauro Carvalho Chehab 398caa642bfSMauro Carvalho Chehab cur_subsys = self.data[name]["subsys"] 399caa642bfSMauro Carvalho Chehab 400caa642bfSMauro Carvalho Chehab title = f"Subsystem: {cur_subsys}" 401caa642bfSMauro Carvalho Chehab self.emit(title) 402caa642bfSMauro Carvalho Chehab self.emit("=" * len(title)) 403caa642bfSMauro Carvalho Chehab self.emit() 404caa642bfSMauro Carvalho Chehab 405caa642bfSMauro Carvalho Chehab self.matrix_lines(desc_size, max_size_status, 0) 406caa642bfSMauro Carvalho Chehab 407caa642bfSMauro Carvalho Chehab self.emit(f"|{self.h_name:<{self.max_size_name}}", end="") 408caa642bfSMauro Carvalho Chehab self.emit(f"|{desc_title:<{desc_size}}", end="") 409caa642bfSMauro Carvalho Chehab self.emit(f"|{h_status_per_arch:<{max_size_status}}|") 410caa642bfSMauro Carvalho Chehab 411caa642bfSMauro Carvalho Chehab self.matrix_lines(desc_size, max_size_status, 1) 412caa642bfSMauro Carvalho Chehab 413caa642bfSMauro Carvalho Chehab lines = [] 414caa642bfSMauro Carvalho Chehab descs = [] 415caa642bfSMauro Carvalho Chehab cur_status = "" 416caa642bfSMauro Carvalho Chehab line = "" 417caa642bfSMauro Carvalho Chehab 418caa642bfSMauro Carvalho Chehab arch_table = sorted(self.data[name]["table"].items(), 419caa642bfSMauro Carvalho Chehab key=lambda x: (self.status_map.get(x[1], 99), 420caa642bfSMauro Carvalho Chehab x[0].lower())) 421caa642bfSMauro Carvalho Chehab 422caa642bfSMauro Carvalho Chehab for arch, status in arch_table: 423caa642bfSMauro Carvalho Chehab if status == "---": 424caa642bfSMauro Carvalho Chehab status = notcompat 425caa642bfSMauro Carvalho Chehab 426caa642bfSMauro Carvalho Chehab if status != cur_status: 427caa642bfSMauro Carvalho Chehab if line != "": 428caa642bfSMauro Carvalho Chehab lines.append(line) 429caa642bfSMauro Carvalho Chehab line = "" 430caa642bfSMauro Carvalho Chehab line = f"- **{status}**: {arch}" 431caa642bfSMauro Carvalho Chehab elif len(line) + len(arch) + 2 < max_size_status: 432caa642bfSMauro Carvalho Chehab line += f", {arch}" 433caa642bfSMauro Carvalho Chehab else: 434caa642bfSMauro Carvalho Chehab lines.append(line) 435caa642bfSMauro Carvalho Chehab line = f" {arch}" 436caa642bfSMauro Carvalho Chehab cur_status = status 437caa642bfSMauro Carvalho Chehab 438caa642bfSMauro Carvalho Chehab if line != "": 439caa642bfSMauro Carvalho Chehab lines.append(line) 440caa642bfSMauro Carvalho Chehab 441caa642bfSMauro Carvalho Chehab description = self.data[name]["description"] 442caa642bfSMauro Carvalho Chehab while len(description) > desc_size: 443caa642bfSMauro Carvalho Chehab desc_line = description[:desc_size] 444caa642bfSMauro Carvalho Chehab 445caa642bfSMauro Carvalho Chehab last_space = desc_line.rfind(" ") 446caa642bfSMauro Carvalho Chehab if last_space != -1: 447caa642bfSMauro Carvalho Chehab desc_line = desc_line[:last_space] 448caa642bfSMauro Carvalho Chehab descs.append(desc_line) 449caa642bfSMauro Carvalho Chehab description = description[last_space + 1:] 450caa642bfSMauro Carvalho Chehab else: 451caa642bfSMauro Carvalho Chehab desc_line = desc_line[:-1] 452caa642bfSMauro Carvalho Chehab descs.append(desc_line + "\\") 453caa642bfSMauro Carvalho Chehab description = description[len(desc_line):] 454caa642bfSMauro Carvalho Chehab 455caa642bfSMauro Carvalho Chehab if description: 456caa642bfSMauro Carvalho Chehab descs.append(description) 457caa642bfSMauro Carvalho Chehab 458caa642bfSMauro Carvalho Chehab while len(lines) < 2 + len(descs): 459caa642bfSMauro Carvalho Chehab lines.append("") 460caa642bfSMauro Carvalho Chehab 461caa642bfSMauro Carvalho Chehab for ln, line in enumerate(lines): 462caa642bfSMauro Carvalho Chehab col = ["", ""] 463caa642bfSMauro Carvalho Chehab 464caa642bfSMauro Carvalho Chehab if not ln: 465caa642bfSMauro Carvalho Chehab col[0] = name 466caa642bfSMauro Carvalho Chehab col[1] = f"``{self.data[name]['kconfig']}``" 467caa642bfSMauro Carvalho Chehab else: 468caa642bfSMauro Carvalho Chehab if ln >= 2 and descs: 469caa642bfSMauro Carvalho Chehab col[1] = descs.pop(0) 470caa642bfSMauro Carvalho Chehab 471caa642bfSMauro Carvalho Chehab self.emit(f"|{col[0]:<{self.max_size_name}}", end="") 472caa642bfSMauro Carvalho Chehab self.emit(f"|{col[1]:<{desc_size}}", end="") 473caa642bfSMauro Carvalho Chehab self.emit(f"|{line:<{max_size_status}}|") 474caa642bfSMauro Carvalho Chehab 475caa642bfSMauro Carvalho Chehab self.matrix_lines(desc_size, max_size_status, 0) 476caa642bfSMauro Carvalho Chehab 477caa642bfSMauro Carvalho Chehab return self.msg 478caa642bfSMauro Carvalho Chehab 479caa642bfSMauro Carvalho Chehab def list_arch_features(self, arch, feat): 480caa642bfSMauro Carvalho Chehab """ 481caa642bfSMauro Carvalho Chehab Print a matrix of kernel feature support for the chosen architecture. 482caa642bfSMauro Carvalho Chehab """ 483caa642bfSMauro Carvalho Chehab self.emit("#") 484caa642bfSMauro Carvalho Chehab self.emit(f"# Kernel feature support matrix of the '{arch}' architecture:") 485caa642bfSMauro Carvalho Chehab self.emit("#") 486caa642bfSMauro Carvalho Chehab 487caa642bfSMauro Carvalho Chehab # Sort by subsystem, then by feature name (case‑insensitive) 488caa642bfSMauro Carvalho Chehab for name in sorted(self.data.keys(), 489caa642bfSMauro Carvalho Chehab key=lambda n: (self.data[n]["subsys"].lower(), 490caa642bfSMauro Carvalho Chehab n.lower())): 491caa642bfSMauro Carvalho Chehab if feat and name != feat: 492caa642bfSMauro Carvalho Chehab continue 493caa642bfSMauro Carvalho Chehab 494caa642bfSMauro Carvalho Chehab feature = self.data[name] 495caa642bfSMauro Carvalho Chehab arch_table = feature["table"] 496caa642bfSMauro Carvalho Chehab status = arch_table.get(arch, "") 497caa642bfSMauro Carvalho Chehab status = " " * ((4 - len(status)) // 2) + status 498caa642bfSMauro Carvalho Chehab 499caa642bfSMauro Carvalho Chehab self.emit(f"{feature['subsys']:>{self.max_size_subsys + 1}}/ ", 500caa642bfSMauro Carvalho Chehab end="") 501caa642bfSMauro Carvalho Chehab self.emit(f"{name:<{self.max_size_name}}: ", end="") 502caa642bfSMauro Carvalho Chehab self.emit(f"{status:<5}| ", end="") 503caa642bfSMauro Carvalho Chehab self.emit(f"{feature['kconfig']:>{self.max_size_kconfig}} ", 504caa642bfSMauro Carvalho Chehab end="") 505caa642bfSMauro Carvalho Chehab self.emit(f"# {feature['description']}") 506caa642bfSMauro Carvalho Chehab 507caa642bfSMauro Carvalho Chehab return self.msg 508