From nobody Tue Dec 2 02:31:02 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E3021366DB0; Tue, 18 Nov 2025 19:09:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763492982; cv=none; b=O3wVUMGiZdOuzPSh4xilnY2FGLtOuHHqNNt+ZjFhOWmXe0uv9CoX2Ct8koXo4z34DnfU6T/SGkwbAzmhwMt4CoxFwcq0jemM1UWpEGo5otZQcFMzghqnXiLu5k4uoehOoHwsDYjjbKuax5+trsZFD6pqm0a3UnPXubkwWwCC8IA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763492982; c=relaxed/simple; bh=JSYeCHfzPfIGscZTpH7Zd15FwY4Vw9eJSwNML06KvQs=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=pWXRiQd3UelE7bR44F60eY90teWC+qU9C+kR4TWkIqVPPXZV+Vie74nPn/CHiWm7wrgto3IdrcosmUGJDStBrAglAdDXfFSdo8CMGcnOn97h/N+lPLtsbk8DqMDsd6Dz4a2dEEAu775N4Geinz3FZRxQSSy4kNC36y8vIkWXpMI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=XUjWugWA; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="XUjWugWA" Received: by smtp.kernel.org (Postfix) with ESMTPSA id E10AAC4AF0B; Tue, 18 Nov 2025 19:09:40 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1763492981; bh=JSYeCHfzPfIGscZTpH7Zd15FwY4Vw9eJSwNML06KvQs=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=XUjWugWARpB4BtzYNGI9/tEebbYxlPCrxs7RAKDV13/F3OqquAT+JcVGcKEP0Wkv+ fGWygwjdt9yQWumbM5eV8wVSj8dgquAU2rDNU1iR08NbMd7F7QR9IqolVbiUSpnRPv +joroYO66/Z0fihsT44W1EI9ZSTBlvtyJhy0cXNsPu6z9vqwzCzK+pDQSo70MXdCdU k44r+5mGkzpLUHW0qq8qgj34mu5QCwitffTA4XRvUhef/oIPNvoEnmY/CKyFXrlpNV 5XPl4PgvtIDBvmfjAimnssFvC9IwK+IGWq4DwDp2Fcgl44GFQ3z3TURiQmzm3FefK6 ljZeCUYKA/aJg== Received: from mchehab by mail.kernel.org with local (Exim 4.99) (envelope-from ) id 1vLR58-000000042hQ-18if; Tue, 18 Nov 2025 20:09:38 +0100 From: Mauro Carvalho Chehab To: "Jonathan Corbet" , Linux Doc Mailing List Cc: Mauro Carvalho Chehab , "Mauro Carvalho Chehab" , linux-kernel@vger.kernel.org Subject: [PATCH v2 1/3] tools/docs/get_feat.py: convert get_feat.pl to Python Date: Tue, 18 Nov 2025 20:09:26 +0100 Message-ID: <03c26cee1ec567804735a33047e625ef5ab7bfa8.1763492868.git.mchehab+huawei@kernel.org> X-Mailer: git-send-email 2.51.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Sender: Mauro Carvalho Chehab As we want to call Python code directly at the Sphinx extension, convert get_feat.pl to Python. The code was made to be (almost) bug-compatible with the Perl version, with two exceptions: 1. Currently, Perl script outputs a wrong table if arch is set to a non-existing value; 2. the ReST table output when --feat is used without --arch has an invalid format, as the number of characters for the table delimiters are wrong. Those two bugs were fixed while testing the conversion. Additionally, another caveat was solved: the output when --feat is used without arch and the feature doesn't exist doesn't contain an empty table anymore. Signed-off-by: Mauro Carvalho Chehab --- Documentation/sphinx/kernel_feat.py | 5 + tools/docs/get_feat.py | 225 +++++++++++ tools/lib/python/feat/parse_features.py | 494 ++++++++++++++++++++++++ 3 files changed, 724 insertions(+) create mode 100755 tools/docs/get_feat.py create mode 100755 tools/lib/python/feat/parse_features.py diff --git a/Documentation/sphinx/kernel_feat.py b/Documentation/sphinx/ker= nel_feat.py index 81c67ef23d8d..1dcbfe335a65 100644 --- a/Documentation/sphinx/kernel_feat.py +++ b/Documentation/sphinx/kernel_feat.py @@ -42,6 +42,11 @@ from docutils.statemachine import ViewList from docutils.parsers.rst import directives, Directive from sphinx.util.docutils import switch_source_input =20 +srctree =3D os.path.abspath(os.environ["srctree"]) +sys.path.insert(0, os.path.join(srctree, "tools/docs/lib")) + +from parse_features import ParseFeature # pylint: disable= =3DC0413 + def ErrorString(exc): # Shamelessly stolen from docutils return f'{exc.__class__.__name}: {exc}' =20 diff --git a/tools/docs/get_feat.py b/tools/docs/get_feat.py new file mode 100755 index 000000000000..2b5155a1f134 --- /dev/null +++ b/tools/docs/get_feat.py @@ -0,0 +1,225 @@ +#!/usr/bin/env python3 +# pylint: disable=3DR0902,R0911,R0912,R0914,R0915 +# Copyright(c) 2025: Mauro Carvalho Chehab . +# SPDX-License-Identifier: GPL-2.0 + + +""" +Parse the Linux Feature files and produce a ReST book. +""" + +import argparse +import os +import subprocess +import sys + +from pprint import pprint + +LIB_DIR =3D "../../tools/lib/python" +SRC_DIR =3D os.path.dirname(os.path.realpath(__file__)) + +sys.path.insert(0, os.path.join(SRC_DIR, LIB_DIR)) + +from feat.parse_features import ParseFeature # pylint: disa= ble=3DC0413 + +SRCTREE =3D os.path.join(os.path.dirname(os.path.realpath(__file__)), "../= ..") +DEFAULT_DIR =3D "Documentation/features" + + +class GetFeature: + """Helper class to parse feature parsing parameters""" + + @staticmethod + def get_current_arch(): + """Detects the current architecture""" + + proc =3D subprocess.run(["uname", "-m"], check=3DTrue, + capture_output=3DTrue, text=3DTrue) + + arch =3D proc.stdout.strip() + if arch in ["x86_64", "i386"]: + arch =3D "x86" + elif arch =3D=3D "s390x": + arch =3D "s390" + + return arch + + def run_parser(self, args): + """Execute the feature parser""" + + feat =3D ParseFeature(args.directory, args.debug, args.enable_fnam= e) + data =3D feat.parse() + + if args.debug > 2: + pprint(data) + + return feat + + def run_rest(self, args): + """ + Generate tables in ReST format. Three types of tables are + supported, depending on the calling arguments: + + - neither feature nor arch is passed: generates a full matrix; + - arch provided: generates a table of supported tables for the + guiven architecture, eventually filtered by feature; + - only feature provided: generates a table with feature details, + showing what architectures it is implemented. + """ + + feat =3D self.run_parser(args) + + if args.arch: + rst =3D feat.output_arch_table(args.arch, args.feat) + elif args.feat: + rst =3D feat.output_feature(args.feat) + else: + rst =3D feat.output_matrix() + + print(rst) + + def run_current(self, args): + """ + Instead of using a --arch parameter, get feature for the current + architecture. + """ + + args.arch =3D self.get_current_arch() + + self.run_rest(args) + + def run_list(self, args): + """ + Generate a list of features for a given architecture, in a format + parseable by other scripts. The output format is not ReST. + """ + + if not args.arch: + args.arch =3D self.get_current_arch() + + feat =3D self.run_parser(args) + msg =3D feat.list_arch_features(args.arch, args.feat) + + print(msg) + + def parse_arch(self, parser): + """Add a --arch parsing argument""" + + parser.add_argument("--arch", + help=3D"Output features for an specific" + " architecture, optionally filtering for = a " + "single specific feature.") + + def parse_feat(self, parser): + """Add a --feat parsing argument""" + + parser.add_argument("--feat", "--feature", + help=3D"Output features for a single specific " + "feature.") + + + def current_args(self, subparsers): + """Implementscurrent argparse subparser""" + + parser =3D subparsers.add_parser("current", + formatter_class=3Dargparse.RawTextH= elpFormatter, + description=3D"Output table in ReST= " + "compatible ASCII forma= t " + "with features for this= " + "machine's architecture= ") + + self.parse_feat(parser) + parser.set_defaults(func=3Dself.run_current) + + def rest_args(self, subparsers): + """Implement rest argparse subparser""" + + parser =3D subparsers.add_parser("rest", + formatter_class=3Dargparse.RawTextH= elpFormatter, + description=3D"Output table(s) in R= eST " + "compatible ASCII forma= t " + "with features in ReST " + "markup language. The " + "output is affected by " + "--arch or --feat/--fea= ture" + " flags.") + + self.parse_arch(parser) + self.parse_feat(parser) + parser.set_defaults(func=3Dself.run_rest) + + def list_args(self, subparsers): + """Implement list argparse subparser""" + + parser =3D subparsers.add_parser("list", + formatter_class=3Dargparse.RawTextH= elpFormatter, + description=3D"List features for th= is " + "machine's architecture= , " + "using an easier to par= se " + "format. The output is " + "affected by --arch fla= g.") + + self.parse_arch(parser) + self.parse_feat(parser) + parser.set_defaults(func=3Dself.run_list) + + def validate_args(self, subparsers): + """Implement validate argparse subparser""" + + parser =3D subparsers.add_parser("validate", + formatter_class=3Dargparse.RawTextH= elpFormatter, + description=3D"Validate the content= s of " + "the files under " + f"{DEFAULT_DIR}.") + + parser.set_defaults(func=3Dself.run_parser) + + def parser(self): + """ + Create an arparse with common options and several subparsers + """ + parser =3D argparse.ArgumentParser(formatter_class=3Dargparse.RawT= extHelpFormatter) + + parser.add_argument("-d", "--debug", action=3D"count", default=3D0, + help=3D"Put the script in verbose mode, useful= for " + "debugging. Can be called multiple times,= to " + "increase verbosity.") + + parser.add_argument("--directory", "--dir", default=3DDEFAULT_DIR, + help=3D"Changes the location of the Feature fi= les. " + f"By default, it uses the {DEFAULT_DIR} " + "directory.") + + parser.add_argument("--enable-fname", action=3D"store_true", + help=3D"Prints the file name of the feature fi= les. " + "This can be used in order to track " + "dependencies during documentation build.= ") + + subparsers =3D parser.add_subparsers() + + self.current_args(subparsers) + self.rest_args(subparsers) + self.list_args(subparsers) + self.validate_args(subparsers) + + args =3D parser.parse_args() + + return args + + +def main(): + """Main program""" + + feat =3D GetFeature() + + args =3D feat.parser() + + if "func" in args: + args.func(args) + else: + sys.exit(f"Please specify a valid command for {sys.argv[0]}") + + +# Call main method +if __name__ =3D=3D "__main__": + main() diff --git a/tools/lib/python/feat/parse_features.py b/tools/lib/python/fea= t/parse_features.py new file mode 100755 index 000000000000..b88c04d3e2fe --- /dev/null +++ b/tools/lib/python/feat/parse_features.py @@ -0,0 +1,494 @@ +#!/usr/bin/env python3 +# pylint: disable=3DR0902,R0911,R0912,R0914,R0915 +# Copyright(c) 2025: Mauro Carvalho Chehab . +# SPDX-License-Identifier: GPL-2.0 + + +""" +Library to parse the Linux Feature files and produce a ReST book. +""" + +import os +import re +import sys + +from glob import iglob + + +class ParseFeature: + """ + Parses Documentation/features, allowing to generate ReST documentation + from it. + """ + + h_name =3D "Feature" + h_kconfig =3D "Kconfig" + h_description =3D "Description" + h_subsys =3D "Subsystem" + h_status =3D "Status" + h_arch =3D "Architecture" + + # Sort order for status. Others will be mapped at the end. + status_map =3D { + "ok": 0, + "TODO": 1, + "N/A": 2, + # The only missing status is "..", which was mapped as "---", + # as this is an special ReST cell value. Let it get the + # default order (99). + } + + def __init__(self, prefix, debug=3D0, enable_fname=3DFalse): + """ + Sets internal variables + """ + + self.prefix =3D prefix + self.debug =3D debug + self.enable_fname =3D enable_fname + + self.data =3D {} + + # Initial maximum values use just the headers + self.max_size_name =3D len(self.h_name) + self.max_size_kconfig =3D len(self.h_kconfig) + self.max_size_description =3D len(self.h_description) + self.max_size_desc_word =3D 0 + self.max_size_subsys =3D len(self.h_subsys) + self.max_size_status =3D len(self.h_status) + self.max_size_arch =3D len(self.h_arch) + self.max_size_arch_with_header =3D self.max_size_arch + self.max_s= ize_arch + self.description_size =3D 1 + + self.msg =3D "" + + def emit(self, msg=3D"", end=3D"\n"): + self.msg +=3D msg + end + + def parse_error(self, fname, ln, msg, data=3DNone): + """ + Displays an error message, printing file name and line + """ + + if ln: + fname +=3D f"#{ln}" + + print(f"Warning: file {fname}: {msg}", file=3Dsys.stderr, end=3D"") + + if data: + data =3D data.rstrip() + print(f":\n\t{data}", file=3Dsys.stderr) + else: + print("", file=3Dsys.stderr) + + def parse_feat_file(self, fname): + """Parses a single arch-support.txt feature file""" + + if os.path.isdir(fname): + return + + base =3D os.path.basename(fname) + + if base !=3D "arch-support.txt": + if self.debug: + print(f"ignoring {fname}", file=3Dsys.stderr) + return + + subsys =3D os.path.dirname(fname).split("/")[-2] + self.max_size_subsys =3D max(self.max_size_subsys, len(subsys)) + + feature_name =3D "" + kconfig =3D "" + description =3D "" + comments =3D "" + arch_table =3D {} + + if self.debug > 1: + print(f"Opening {fname}", file=3Dsys.stderr) + + if self.enable_fname: + full_fname =3D os.path.abspath(fname) + self.emit(f".. FILE {full_fname}") + + with open(fname, encoding=3D"utf-8") as f: + for ln, line in enumerate(f, start=3D1): + line =3D line.strip() + + match =3D re.match(r"^\#\s+Feature\s+name:\s*(.*\S)", line) + if match: + feature_name =3D match.group(1) + + self.max_size_name =3D max(self.max_size_name, + len(feature_name)) + continue + + match =3D re.match(r"^\#\s+Kconfig:\s*(.*\S)", line) + if match: + kconfig =3D match.group(1) + + self.max_size_kconfig =3D max(self.max_size_kconfig, + len(kconfig)) + continue + + match =3D re.match(r"^\#\s+description:\s*(.*\S)", line) + if match: + description =3D match.group(1) + + self.max_size_description =3D max(self.max_size_descri= ption, + len(description)) + + words =3D re.split(r"\s+", line)[1:] + for word in words: + self.max_size_desc_word =3D max(self.max_size_desc= _word, + len(word)) + + continue + + if re.search(r"^\\s*$", line): + continue + + if re.match(r"^\s*\-+\s*$", line): + continue + + if re.search(r"^\s*\|\s*arch\s*\|\s*status\s*\|\s*$", line= ): + continue + + match =3D re.match(r"^\#\s*(.*)$", line) + if match: + comments +=3D match.group(1) + continue + + match =3D re.match(r"^\s*\|\s*(\S+):\s*\|\s*(\S+)\s*\|\s*$= ", line) + if match: + arch =3D match.group(1) + status =3D match.group(2) + + self.max_size_status =3D max(self.max_size_status, + len(status)) + self.max_size_arch =3D max(self.max_size_arch, len(arc= h)) + + if status =3D=3D "..": + status =3D "---" + + arch_table[arch] =3D status + + continue + + self.parse_error(fname, ln, "Line is invalid", line) + + if not feature_name: + self.parse_error(fname, 0, "Feature name not found") + return + if not subsys: + self.parse_error(fname, 0, "Subsystem not found") + return + if not kconfig: + self.parse_error(fname, 0, "Kconfig not found") + return + if not description: + self.parse_error(fname, 0, "Description not found") + return + if not arch_table: + self.parse_error(fname, 0, "Architecture table not found") + return + + self.data[feature_name] =3D { + "where": fname, + "subsys": subsys, + "kconfig": kconfig, + "description": description, + "comments": comments, + "table": arch_table, + } + + self.max_size_arch_with_header =3D self.max_size_arch + len(self.h= _arch) + + def parse(self): + """Parses all arch-support.txt feature files inside self.prefix""" + + path =3D os.path.expanduser(self.prefix) + + if self.debug > 2: + print(f"Running parser for {path}") + + example_path =3D os.path.join(path, "arch-support.txt") + + for fname in iglob(os.path.join(path, "**"), recursive=3DTrue): + if fname !=3D example_path: + self.parse_feat_file(fname) + + return self.data + + def output_arch_table(self, arch, feat=3DNone): + """ + Output feature(s) for a given architecture. + """ + + title =3D f"Feature status on {arch} architecture" + + self.emit("=3D" * len(title)) + self.emit(title) + self.emit("=3D" * len(title)) + self.emit() + + self.emit("=3D" * self.max_size_subsys + " ", end=3D"") + self.emit("=3D" * self.max_size_name + " ", end=3D"") + self.emit("=3D" * self.max_size_kconfig + " ", end=3D"") + self.emit("=3D" * self.max_size_status + " ", end=3D"") + self.emit("=3D" * self.max_size_description) + + self.emit(f"{self.h_subsys:<{self.max_size_subsys}} ", end=3D"") + self.emit(f"{self.h_name:<{self.max_size_name}} ", end=3D"") + self.emit(f"{self.h_kconfig:<{self.max_size_kconfig}} ", end=3D"") + self.emit(f"{self.h_status:<{self.max_size_status}} ", end=3D"") + self.emit(f"{self.h_description:<{self.max_size_description}}") + + self.emit("=3D" * self.max_size_subsys + " ", end=3D"") + self.emit("=3D" * self.max_size_name + " ", end=3D"") + self.emit("=3D" * self.max_size_kconfig + " ", end=3D"") + self.emit("=3D" * self.max_size_status + " ", end=3D"") + self.emit("=3D" * self.max_size_description) + + sorted_features =3D sorted(self.data.keys(), + key=3Dlambda x: (self.data[x]["subsys"], + x.lower())) + + for name in sorted_features: + if feat and name !=3D feat: + continue + + arch_table =3D self.data[name]["table"] + + if not arch in arch_table: + continue + + self.emit(f"{self.data[name]['subsys']:<{self.max_size_subsys}= } ", + end=3D"") + self.emit(f"{name:<{self.max_size_name}} ", end=3D"") + self.emit(f"{self.data[name]['kconfig']:<{self.max_size_kconfi= g}} ", + end=3D"") + self.emit(f"{arch_table[arch]:<{self.max_size_status}} ", + end=3D"") + self.emit(f"{self.data[name]['description']}") + + self.emit("=3D" * self.max_size_subsys + " ", end=3D"") + self.emit("=3D" * self.max_size_name + " ", end=3D"") + self.emit("=3D" * self.max_size_kconfig + " ", end=3D"") + self.emit("=3D" * self.max_size_status + " ", end=3D"") + self.emit("=3D" * self.max_size_description) + + return self.msg + + def output_feature(self, feat): + """ + Output a feature on all architectures + """ + + title =3D f"Feature {feat}" + + self.emit("=3D" * len(title)) + self.emit(title) + self.emit("=3D" * len(title)) + self.emit() + + if not feat in self.data: + return + + if self.data[feat]["subsys"]: + self.emit(f":Subsystem: {self.data[feat]['subsys']}") + if self.data[feat]["kconfig"]: + self.emit(f":Kconfig: {self.data[feat]['kconfig']}") + + desc =3D self.data[feat]["description"] + desc =3D desc[0].upper() + desc[1:] + desc =3D desc.rstrip(". \t") + self.emit(f"\n{desc}.\n") + + com =3D self.data[feat]["comments"].strip() + if com: + self.emit("Comments") + self.emit("--------") + self.emit(f"\n{com}\n") + + self.emit("=3D" * self.max_size_arch + " ", end=3D"") + self.emit("=3D" * self.max_size_status) + + self.emit(f"{self.h_arch:<{self.max_size_arch}} ", end=3D"") + self.emit(f"{self.h_status:<{self.max_size_status}}") + + self.emit("=3D" * self.max_size_arch + " ", end=3D"") + self.emit("=3D" * self.max_size_status) + + arch_table =3D self.data[feat]["table"] + for arch in sorted(arch_table.keys()): + self.emit(f"{arch:<{self.max_size_arch}} ", end=3D"") + self.emit(f"{arch_table[arch]:<{self.max_size_status}}") + + self.emit("=3D" * self.max_size_arch + " ", end=3D"") + self.emit("=3D" * self.max_size_status) + + return self.msg + + def matrix_lines(self, desc_size, max_size_status, header): + """ + Helper function to split element tables at the output matrix + """ + + if header: + ln_marker =3D "=3D" + else: + ln_marker =3D "-" + + self.emit("+" + ln_marker * self.max_size_name + "+", end=3D"") + self.emit(ln_marker * desc_size, end=3D"") + self.emit("+" + ln_marker * max_size_status + "+") + + def output_matrix(self): + """ + Generates a set of tables, groped by subsystem, containing + what's the feature state on each architecture. + """ + + title =3D "Feature status on all architectures" + + self.emit("=3D" * len(title)) + self.emit(title) + self.emit("=3D" * len(title)) + self.emit() + + desc_title =3D f"{self.h_kconfig} / {self.h_description}" + + desc_size =3D self.max_size_kconfig + 4 + if not self.description_size: + desc_size =3D max(self.max_size_description, desc_size) + else: + desc_size =3D max(self.description_size, desc_size) + + desc_size =3D max(self.max_size_desc_word, desc_size, len(desc_tit= le)) + + notcompat =3D "Not compatible" + self.max_size_status =3D max(self.max_size_status, len(notcompat)) + + min_status_size =3D self.max_size_status + self.max_size_arch + 4 + max_size_status =3D max(min_status_size, self.max_size_status) + + h_status_per_arch =3D "Status per architecture" + max_size_status =3D max(max_size_status, len(h_status_per_arch)) + + cur_subsys =3D None + for name in sorted(self.data.keys(), + key=3Dlambda x: (self.data[x]["subsys"], x.lowe= r())): + if not cur_subsys or cur_subsys !=3D self.data[name]["subsys"]: + if cur_subsys: + self.emit() + + cur_subsys =3D self.data[name]["subsys"] + + title =3D f"Subsystem: {cur_subsys}" + self.emit(title) + self.emit("=3D" * len(title)) + self.emit() + + self.matrix_lines(desc_size, max_size_status, 0) + + self.emit(f"|{self.h_name:<{self.max_size_name}}", end=3D"= ") + self.emit(f"|{desc_title:<{desc_size}}", end=3D"") + self.emit(f"|{h_status_per_arch:<{max_size_status}}|") + + self.matrix_lines(desc_size, max_size_status, 1) + + lines =3D [] + descs =3D [] + cur_status =3D "" + line =3D "" + + arch_table =3D sorted(self.data[name]["table"].items(), + key=3Dlambda x: (self.status_map.get(x[1],= 99), + x[0].lower())) + + for arch, status in arch_table: + if status =3D=3D "---": + status =3D notcompat + + if status !=3D cur_status: + if line !=3D "": + lines.append(line) + line =3D "" + line =3D f"- **{status}**: {arch}" + elif len(line) + len(arch) + 2 < max_size_status: + line +=3D f", {arch}" + else: + lines.append(line) + line =3D f" {arch}" + cur_status =3D status + + if line !=3D "": + lines.append(line) + + description =3D self.data[name]["description"] + while len(description) > desc_size: + desc_line =3D description[:desc_size] + + last_space =3D desc_line.rfind(" ") + if last_space !=3D -1: + desc_line =3D desc_line[:last_space] + descs.append(desc_line) + description =3D description[last_space + 1:] + else: + desc_line =3D desc_line[:-1] + descs.append(desc_line + "\\") + description =3D description[len(desc_line):] + + if description: + descs.append(description) + + while len(lines) < 2 + len(descs): + lines.append("") + + for ln, line in enumerate(lines): + col =3D ["", ""] + + if not ln: + col[0] =3D name + col[1] =3D f"``{self.data[name]['kconfig']}``" + else: + if ln >=3D 2 and descs: + col[1] =3D descs.pop(0) + + self.emit(f"|{col[0]:<{self.max_size_name}}", end=3D"") + self.emit(f"|{col[1]:<{desc_size}}", end=3D"") + self.emit(f"|{line:<{max_size_status}}|") + + self.matrix_lines(desc_size, max_size_status, 0) + + return self.msg + + def list_arch_features(self, arch, feat): + """ + Print a matrix of kernel feature support for the chosen architectu= re. + """ + self.emit("#") + self.emit(f"# Kernel feature support matrix of the '{arch}' archit= ecture:") + self.emit("#") + + # Sort by subsystem, then by feature name (case=E2=80=91insensitiv= e) + for name in sorted(self.data.keys(), + key=3Dlambda n: (self.data[n]["subsys"].lower(), + n.lower())): + if feat and name !=3D feat: + continue + + feature =3D self.data[name] + arch_table =3D feature["table"] + status =3D arch_table.get(arch, "") + status =3D " " * ((4 - len(status)) // 2) + status + + self.emit(f"{feature['subsys']:>{self.max_size_subsys + 1}}/ ", + end=3D"") + self.emit(f"{name:<{self.max_size_name}}: ", end=3D"") + self.emit(f"{status:<5}| ", end=3D"") + self.emit(f"{feature['kconfig']:>{self.max_size_kconfig}} ", + end=3D"") + self.emit(f"# {feature['description']}") + + return self.msg --=20 2.51.1 From nobody Tue Dec 2 02:31:02 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E2F992DEA9E; Tue, 18 Nov 2025 19:09:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763492982; cv=none; b=Sz7iUXgPdXmB63Wf/b6/SgyxKT597hMEx/a2TcmLinJrmuqe6IVHZwtmYhKzt9V48KUaWnYYawr5ih/qtkzuxtYYHFiQA8y2awxdViTYLr2bfguBHls6XuCrbPlg/2DzoKsF6KoFuVwMGUr9DL2o5tZvrZgCClW+O8yzRA2oGHI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763492982; c=relaxed/simple; bh=H8QhqiTsXW2qUcK8V7pElgFkRTd/fIAGToq3PYuFKW4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=bpEyqN+R3UCI5bj12PIYtSQhSv63RJjUwRDJgdKG3sgx4GJMTiggRnMUs07Re9QAkvzjhaxbVmXkdIALF5GjvvUdZxAcs7qQoUbyzT3xg3g8PRMCRvw+wm0xZEByDQ1LZpHbuWfVZnGtm1E5u3LWNLBXqrLenZDf157fc9GL8EU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=aKpyqsxl; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="aKpyqsxl" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 06C48C116B1; Tue, 18 Nov 2025 19:09:41 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1763492981; bh=H8QhqiTsXW2qUcK8V7pElgFkRTd/fIAGToq3PYuFKW4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=aKpyqsxlpxAjBDftT6ImEiL+ruEohSRVBVM2kCtNYGxccH2xf+5T9kelaej6kA8yr 7GncnKlONWYlnLSNUpMrIqFSQ49CTp3EfoyjH/M3RLitm3XZipIL7Bz7Jr1PXGDHH6 msJEl9/am7qfMbovVfhSZI9PFtojt9tQ2BoGTzvHGEUcYS/rNPzKxgOP3XixrF9dM3 iVPI082NPLpJULfHM8pT6wOQ5QP7efa5Vb5VWsGRXe0jwpu6ScRWH1y9xtM0dRIMB6 aDdbmmIEFsXeIywPJRXRAuIbCvO/ZKEa1j+SPZJrgABvrd9JxY5s3P4FaYiQyrYxJC o7h0HAQbgCC5g== Received: from mchehab by mail.kernel.org with local (Exim 4.99) (envelope-from ) id 1vLR58-000000042hU-1FdV; Tue, 18 Nov 2025 20:09:38 +0100 From: Mauro Carvalho Chehab To: "Jonathan Corbet" , Linux Doc Mailing List Cc: Mauro Carvalho Chehab , "Mauro Carvalho Chehab" , linux-kernel@vger.kernel.org Subject: [PATCH v2 2/3] Documentation/sphinx/kernel_feat.py: use class directly Date: Tue, 18 Nov 2025 20:09:27 +0100 Message-ID: X-Mailer: git-send-email 2.51.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Sender: Mauro Carvalho Chehab Content-Type: text/plain; charset="utf-8" Now that get_feat is in Python, we don't need to use subprocess to fork an executable file: we can use the feature classes directly. Signed-off-by: Mauro Carvalho Chehab --- Documentation/sphinx/kernel_feat.py | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/Documentation/sphinx/kernel_feat.py b/Documentation/sphinx/ker= nel_feat.py index 1dcbfe335a65..bdc0fef5c87f 100644 --- a/Documentation/sphinx/kernel_feat.py +++ b/Documentation/sphinx/kernel_feat.py @@ -34,7 +34,6 @@ import codecs import os import re -import subprocess import sys =20 from docutils import nodes, statemachine @@ -43,9 +42,9 @@ from docutils.parsers.rst import directives, Directive from sphinx.util.docutils import switch_source_input =20 srctree =3D os.path.abspath(os.environ["srctree"]) -sys.path.insert(0, os.path.join(srctree, "tools/docs/lib")) +sys.path.insert(0, os.path.join(srctree, "tools/lib/python")) =20 -from parse_features import ParseFeature # pylint: disable= =3DC0413 +from feat.parse_features import ParseFeature # pylint: disa= ble=3DC0413 =20 def ErrorString(exc): # Shamelessly stolen from docutils return f'{exc.__class__.__name}: {exc}' @@ -89,18 +88,16 @@ class KernelFeat(Directive): =20 srctree =3D os.path.abspath(os.environ["srctree"]) =20 - args =3D [ - os.path.join(srctree, 'tools/docs/get_feat.pl'), - 'rest', - '--enable-fname', - '--dir', - os.path.join(srctree, 'Documentation', self.arguments[0]), - ] + feature_dir =3D os.path.join(srctree, 'Documentation', self.argume= nts[0]) + + feat =3D ParseFeature(feature_dir, False, True) + feat.parse() =20 if len(self.arguments) > 1: - args.extend(['--arch', self.arguments[1]]) - - lines =3D subprocess.check_output(args, cwd=3Dos.path.dirname(doc.= current_source)).decode('utf-8') + arch =3D self.arguments[1] + lines =3D feat.output_arch_table(arch) + else: + lines =3D feat.output_matrix() =20 line_regex =3D re.compile(r"^\.\. FILE (\S+)$") =20 --=20 2.51.1 From nobody Tue Dec 2 02:31:02 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id AEC0D36921F; Tue, 18 Nov 2025 19:09:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763492982; cv=none; b=oPI1cqM/s9zwnT3fVbpYDeY1dmVk2beZcyKBJNzNZVt9N3BqpQ5QXGUhmtUjs8UeKJ5znRr370gH9/3JQ/ryVfAE9XTBym20Oi/Ohk5wbWF32QPZYTsOLs1GlnuRoJY5OZA2uu4XYf3C9OfNYdXaTQOWCj8FyND8deJoOcE73Cs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763492982; c=relaxed/simple; bh=pXSkk7JQqoaPjJMx/lQckHYi4LcLVPHAn7LMXiLemvk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=lV82me8Y9dW5GiOFOUNJbX46Al789c5UbqgCiNLVHgIQLfd90k91XqCICOfbDFMjYHI0UyYgJWodNz7CGtbqnqNwifjm4HStn3C3awBNuCJ8OpgqmHuFKFntgSpK9hz5r2qbbL/ezG7kpnoklPnORCuHHowM8nGrxA4H65Mjn8M= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=blFJkGEr; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="blFJkGEr" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 9E0D7C2BCB4; Tue, 18 Nov 2025 19:09:41 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1763492982; bh=pXSkk7JQqoaPjJMx/lQckHYi4LcLVPHAn7LMXiLemvk=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=blFJkGErJGBLtQ3KzI5a29EDJMwPtb1ej5iOMHM+UGYlgA+YdNEOzEMhDw430sxX7 wbFT2rAvhGCR3EETLSCYf4GHpgwKAGV/Ao46hb+5fnc/+GbU/IEGZyDPKFKVVlUq1a fl9MOwXr1pav1nZ7q3ONcRPkzJOLPKoA3abISy44AyrR63vDYGuamIyrXxtJ7mJQpX zn10jdSoFGB5ku0wCQP++b2F3pC7LD7++vrltmGEGRTS5jNKeKpoAN5vzE/T58rtUq zDNtV2jALEVoVoKZTw8lJHNMqCW+v+ZG88N+AUohGu0q7Bfu/NOw3byP5niqwcrqsr 8Qe+xcxO0xd4Q== Received: from mchehab by mail.kernel.org with local (Exim 4.99) (envelope-from ) id 1vLR58-000000042hY-1MMg; Tue, 18 Nov 2025 20:09:38 +0100 From: Mauro Carvalho Chehab To: "Jonathan Corbet" , Linux Doc Mailing List Cc: Mauro Carvalho Chehab , "Mauro Carvalho Chehab" , linux-kernel@vger.kernel.org Subject: [PATCH v2 3/3] get_feat.pl: remove it, as it got replaced by get_feat.py Date: Tue, 18 Nov 2025 20:09:28 +0100 Message-ID: <1f53e0fa48616af189ce98b45a65cc0c245e7aaf.1763492868.git.mchehab+huawei@kernel.org> X-Mailer: git-send-email 2.51.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Sender: Mauro Carvalho Chehab Content-Type: text/plain; charset="utf-8" Now that this was rewritten in Python, we can remove the old tool. Signed-off-by: Mauro Carvalho Chehab --- tools/docs/get_feat.pl | 641 ----------------------------------------- 1 file changed, 641 deletions(-) delete mode 100755 tools/docs/get_feat.pl diff --git a/tools/docs/get_feat.pl b/tools/docs/get_feat.pl deleted file mode 100755 index d75e7c85dc85..000000000000 --- a/tools/docs/get_feat.pl +++ /dev/null @@ -1,641 +0,0 @@ -#!/usr/bin/env perl -# SPDX-License-Identifier: GPL-2.0 - -use strict; -use Pod::Usage; -use Getopt::Long; -use File::Find; -use Fcntl ':mode'; -use Cwd 'abs_path'; - -my $help; -my $man; -my $debug; -my $arch; -my $feat; -my $enable_fname; - -my $basename =3D abs_path($0); -$basename =3D~ s,/[^/]+$,/,; - -my $prefix=3D$basename . "../../Documentation/features"; - -# Used only at for full features output. The script will auto-adjust -# such values for the minimal possible values -my $status_size =3D 1; -my $description_size =3D 1; - -GetOptions( - "debug|d+" =3D> \$debug, - "dir=3Ds" =3D> \$prefix, - 'help|?' =3D> \$help, - 'arch=3Ds' =3D> \$arch, - 'feat=3Ds' =3D> \$feat, - 'feature=3Ds' =3D> \$feat, - "enable-fname" =3D> \$enable_fname, - man =3D> \$man -) or pod2usage(2); - -pod2usage(1) if $help; -pod2usage(-exitstatus =3D> 0, -verbose =3D> 2) if $man; - -pod2usage(1) if (scalar @ARGV < 1 || @ARGV > 2); - -my ($cmd, $arg) =3D @ARGV; - -pod2usage(2) if ($cmd ne "current" && $cmd ne "rest" && $cmd ne "validate" - && $cmd ne "ls" && $cmd ne "list"); - -require Data::Dumper if ($debug); - -my %data; -my %archs; - -# -# Displays an error message, printing file name and line -# -sub parse_error($$$$) { - my ($file, $ln, $msg, $data) =3D @_; - - $data =3D~ s/\s+$/\n/; - - print STDERR "Warning: file $file#$ln:\n\t$msg"; - - if ($data ne "") { - print STDERR ". Line\n\t\t$data"; - } else { - print STDERR "\n"; - } -} - -# -# Parse a features file, storing its contents at %data -# - -my $h_name =3D "Feature"; -my $h_kconfig =3D "Kconfig"; -my $h_description =3D "Description"; -my $h_subsys =3D "Subsystem"; -my $h_status =3D "Status"; -my $h_arch =3D "Architecture"; - -my $max_size_name =3D length($h_name); -my $max_size_kconfig =3D length($h_kconfig); -my $max_size_description =3D length($h_description); -my $max_size_subsys =3D length($h_subsys); -my $max_size_status =3D length($h_status); - -my $max_size_arch =3D 0; -my $max_size_arch_with_header; -my $max_description_word =3D 0; - -sub parse_feat { - my $file =3D $File::Find::name; - - my $mode =3D (stat($file))[2]; - return if ($mode & S_IFDIR); - return if ($file =3D~ m,($prefix)/arch-support.txt,); - return if (!($file =3D~ m,arch-support.txt$,)); - - if ($enable_fname) { - printf ".. FILE %s\n", abs_path($file); - } - - my $subsys =3D ""; - $subsys =3D $2 if ( m,.*($prefix)/([^/]+).*,); - - if (length($subsys) > $max_size_subsys) { - $max_size_subsys =3D length($subsys); - } - - my $name; - my $kconfig; - my $description; - my $comments =3D ""; - my $last_status; - my $ln; - my %arch_table; - - print STDERR "Opening $file\n" if ($debug > 1); - open IN, $file; - - while() { - $ln++; - - if (m/^\#\s+Feature\s+name:\s*(.*\S)/) { - $name =3D $1; - if (length($name) > $max_size_name) { - $max_size_name =3D length($name); - } - next; - } - if (m/^\#\s+Kconfig:\s*(.*\S)/) { - $kconfig =3D $1; - if (length($kconfig) > $max_size_kconfig) { - $max_size_kconfig =3D length($kconfig); - } - next; - } - if (m/^\#\s+description:\s*(.*\S)/) { - $description =3D $1; - if (length($description) > $max_size_description) { - $max_size_description =3D length($description); - } - - foreach my $word (split /\s+/, $description) { - if (length($word) > $max_description_word) { - $max_description_word =3D length($word); - } - } - - next; - } - next if (m/^\\s*$/); - next if (m/^\s*\-+\s*$/); - next if (m/^\s*\|\s*arch\s*\|\s*status\s*\|\s*$/); - - if (m/^\#\s*(.*)/) { - $comments .=3D "$1\n"; - next; - } - if (m/^\s*\|\s*(\S+):\s*\|\s*(\S+)\s*\|\s*$/) { - my $a =3D $1; - my $status =3D $2; - - if (length($status) > $max_size_status) { - $max_size_status =3D length($status); - } - if (length($a) > $max_size_arch) { - $max_size_arch =3D length($a); - } - - $status =3D "---" if ($status =3D~ m/^\.\.$/); - - $archs{$a} =3D 1; - $arch_table{$a} =3D $status; - next; - } - - #Everything else is an error - parse_error($file, $ln, "line is invalid", $_); - } - close IN; - - if (!$name) { - parse_error($file, $ln, "Feature name not found", ""); - return; - } - - parse_error($file, $ln, "Subsystem not found", "") if (!$subsys); - parse_error($file, $ln, "Kconfig not found", "") if (!$kconfig); - parse_error($file, $ln, "Description not found", "") if (!$description); - - if (!%arch_table) { - parse_error($file, $ln, "Architecture table not found", ""); - return; - } - - $data{$name}->{where} =3D $file; - $data{$name}->{subsys} =3D $subsys; - $data{$name}->{kconfig} =3D $kconfig; - $data{$name}->{description} =3D $description; - $data{$name}->{comments} =3D $comments; - $data{$name}->{table} =3D \%arch_table; - - $max_size_arch_with_header =3D $max_size_arch + length($h_arch); -} - -# -# Output feature(s) for a given architecture -# -sub output_arch_table { - my $title =3D "Feature status on $arch architecture"; - - print "=3D" x length($title) . "\n"; - print "$title\n"; - print "=3D" x length($title) . "\n\n"; - - print "=3D" x $max_size_subsys; - print " "; - print "=3D" x $max_size_name; - print " "; - print "=3D" x $max_size_kconfig; - print " "; - print "=3D" x $max_size_status; - print " "; - print "=3D" x $max_size_description; - print "\n"; - printf "%-${max_size_subsys}s ", $h_subsys; - printf "%-${max_size_name}s ", $h_name; - printf "%-${max_size_kconfig}s ", $h_kconfig; - printf "%-${max_size_status}s ", $h_status; - printf "%-${max_size_description}s\n", $h_description; - print "=3D" x $max_size_subsys; - print " "; - print "=3D" x $max_size_name; - print " "; - print "=3D" x $max_size_kconfig; - print " "; - print "=3D" x $max_size_status; - print " "; - print "=3D" x $max_size_description; - print "\n"; - - foreach my $name (sort { - ($data{$a}->{subsys} cmp $data{$b}->{subsys}) || - ("\L$a" cmp "\L$b") - } keys %data) { - next if ($feat && $name ne $feat); - - my %arch_table =3D %{$data{$name}->{table}}; - printf "%-${max_size_subsys}s ", $data{$name}->{subsys}; - printf "%-${max_size_name}s ", $name; - printf "%-${max_size_kconfig}s ", $data{$name}->{kconfig}; - printf "%-${max_size_status}s ", $arch_table{$arch}; - printf "%-s\n", $data{$name}->{description}; - } - - print "=3D" x $max_size_subsys; - print " "; - print "=3D" x $max_size_name; - print " "; - print "=3D" x $max_size_kconfig; - print " "; - print "=3D" x $max_size_status; - print " "; - print "=3D" x $max_size_description; - print "\n"; -} - -# -# list feature(s) for a given architecture -# -sub list_arch_features { - print "#\n# Kernel feature support matrix of the '$arch' architecture:\n#= \n"; - - foreach my $name (sort { - ($data{$a}->{subsys} cmp $data{$b}->{subsys}) || - ("\L$a" cmp "\L$b") - } keys %data) { - next if ($feat && $name ne $feat); - - my %arch_table =3D %{$data{$name}->{table}}; - - my $status =3D $arch_table{$arch}; - $status =3D " " x ((4 - length($status)) / 2) . $status; - - printf " %${max_size_subsys}s/ ", $data{$name}->{subsys}; - printf "%-${max_size_name}s: ", $name; - printf "%-5s| ", $status; - printf "%${max_size_kconfig}s # ", $data{$name}->{kconfig}; - printf " %s\n", $data{$name}->{description}; - } -} - -# -# Output a feature on all architectures -# -sub output_feature { - my $title =3D "Feature $feat"; - - print "=3D" x length($title) . "\n"; - print "$title\n"; - print "=3D" x length($title) . "\n\n"; - - print ":Subsystem: $data{$feat}->{subsys} \n" if ($data{$feat}->{subsys}); - print ":Kconfig: $data{$feat}->{kconfig} \n" if ($data{$feat}->{kconfig}); - - my $desc =3D $data{$feat}->{description}; - $desc =3D~ s/^([a-z])/\U$1/; - $desc =3D~ s/\.?\s*//; - print "\n$desc.\n\n"; - - my $com =3D $data{$feat}->{comments}; - $com =3D~ s/^\s+//; - $com =3D~ s/\s+$//; - if ($com) { - print "Comments\n"; - print "--------\n\n"; - print "$com\n\n"; - } - - print "=3D" x $max_size_arch_with_header; - print " "; - print "=3D" x $max_size_status; - print "\n"; - - printf "%-${max_size_arch}s ", $h_arch; - printf "%-${max_size_status}s", $h_status . "\n"; - - print "=3D" x $max_size_arch_with_header; - print " "; - print "=3D" x $max_size_status; - print "\n"; - - my %arch_table =3D %{$data{$feat}->{table}}; - foreach my $arch (sort keys %arch_table) { - printf "%-${max_size_arch}s ", $arch; - printf "%-${max_size_status}s\n", $arch_table{$arch}; - } - - print "=3D" x $max_size_arch_with_header; - print " "; - print "=3D" x $max_size_status; - print "\n"; -} - -# -# Output all features for all architectures -# - -sub matrix_lines($$$) { - my $desc_size =3D shift; - my $status_size =3D shift; - my $header =3D shift; - my $fill; - my $ln_marker; - - if ($header) { - $ln_marker =3D "=3D"; - } else { - $ln_marker =3D "-"; - } - - $fill =3D $ln_marker; - - print "+"; - print $fill x $max_size_name; - print "+"; - print $fill x $desc_size; - print "+"; - print $ln_marker x $status_size; - print "+\n"; -} - -sub output_matrix { - my $title =3D "Feature status on all architectures"; - my $notcompat =3D "Not compatible"; - - print "=3D" x length($title) . "\n"; - print "$title\n"; - print "=3D" x length($title) . "\n\n"; - - my $desc_title =3D "$h_kconfig / $h_description"; - - my $desc_size =3D $max_size_kconfig + 4; - if (!$description_size) { - $desc_size =3D $max_size_description if ($max_size_description > $desc_s= ize); - } else { - $desc_size =3D $description_size if ($description_size > $desc_size); - } - $desc_size =3D $max_description_word if ($max_description_word > $desc_si= ze); - - $desc_size =3D length($desc_title) if (length($desc_title) > $desc_size); - - $max_size_status =3D length($notcompat) if (length($notcompat) > $max_siz= e_status); - - # Ensure that the status will fit - my $min_status_size =3D $max_size_status + $max_size_arch + 6; - $status_size =3D $min_status_size if ($status_size < $min_status_size); - - - my $cur_subsys =3D ""; - foreach my $name (sort { - ($data{$a}->{subsys} cmp $data{$b}->{subsys}) or - ("\L$a" cmp "\L$b") - } keys %data) { - - if ($cur_subsys ne $data{$name}->{subsys}) { - if ($cur_subsys ne "") { - printf "\n"; - } - - $cur_subsys =3D $data{$name}->{subsys}; - - my $title =3D "Subsystem: $cur_subsys"; - print "$title\n"; - print "=3D" x length($title) . "\n\n"; - - - matrix_lines($desc_size, $status_size, 0); - - printf "|%-${max_size_name}s", $h_name; - printf "|%-${desc_size}s", $desc_title; - - printf "|%-${status_size}s|\n", "Status per architecture"; - matrix_lines($desc_size, $status_size, 1); - } - - my %arch_table =3D %{$data{$name}->{table}}; - my $cur_status =3D ""; - - my (@lines, @descs); - my $line =3D ""; - foreach my $arch (sort { - ($arch_table{$b} cmp $arch_table{$a}) or - ("\L$a" cmp "\L$b") - } keys %arch_table) { - - my $status =3D $arch_table{$arch}; - - if ($status eq "---") { - $status =3D $notcompat; - } - - if ($status ne $cur_status) { - if ($line ne "") { - push @lines, $line; - $line =3D ""; - } - $line =3D "- **" . $status . "**: " . $arch; - } elsif (length($line) + length ($arch) + 2 < $status_size) { - $line .=3D ", " . $arch; - } else { - push @lines, $line; - $line =3D " " . $arch; - } - $cur_status =3D $status; - } - push @lines, $line if ($line ne ""); - - my $description =3D $data{$name}->{description}; - while (length($description) > $desc_size) { - my $d =3D substr $description, 0, $desc_size; - - # Ensure that it will end on a space - # if it can't, it means that the size is too small - # Instead of aborting it, let's print what we have - if (!($d =3D~ s/^(.*)\s+.*/$1/)) { - $d =3D substr $d, 0, -1; - push @descs, "$d\\"; - $description =3D~ s/^\Q$d\E//; - } else { - push @descs, $d; - $description =3D~ s/^\Q$d\E\s+//; - } - } - push @descs, $description; - - # Ensure that the full description will be printed - push @lines, "" while (scalar(@lines) < 2 + scalar(@descs)); - - my $ln =3D 0; - for my $line(@lines) { - if (!$ln) { - printf "|%-${max_size_name}s", $name; - printf "|%-${desc_size}s", "``" . $data{$name}->{kconfig} . "``"; - } elsif ($ln >=3D 2 && scalar(@descs)) { - printf "|%-${max_size_name}s", ""; - printf "|%-${desc_size}s", shift @descs; - } else { - printf "|%-${max_size_name}s", ""; - printf "|%-${desc_size}s", ""; - } - - printf "|%-${status_size}s|\n", $line; - - $ln++; - } - matrix_lines($desc_size, $status_size, 0); - } -} - - -# -# Parses all feature files located at $prefix dir -# -find({wanted =3D>\&parse_feat, no_chdir =3D> 1}, $prefix); - -print STDERR Data::Dumper->Dump([\%data], [qw(*data)]) if ($debug); - -# -# Handles the command -# -if ($cmd eq "current") { - $arch =3D qx(uname -m | sed 's/x86_64/x86/' | sed 's/i386/x86/' | sed 's/= s390x/s390/'); - $arch =3D~s/\s+$//; -} - -if ($cmd eq "ls" or $cmd eq "list") { - if (!$arch) { - $arch =3D qx(uname -m | sed 's/x86_64/x86/' | sed 's/i386/x86/' | sed 's= /s390x/s390/'); - $arch =3D~s/\s+$//; - } - - list_arch_features; - - exit; -} - -if ($cmd ne "validate") { - if ($arch) { - output_arch_table; - } elsif ($feat) { - output_feature; - } else { - output_matrix; - } -} - -__END__ - -=3Dhead1 NAME - -get_feat.pl - parse the Linux Feature files and produce a ReST book. - -=3Dhead1 SYNOPSIS - -B [--debug] [--man] [--help] [--dir=3D] [--arch=3D] - [--feature=3D|--feat=3D] [] - -Where can be: - -=3Dover 8 - -B - output table in ReST compatible ASCII format - with features for this machine's architecture - -B - output table(s) in ReST compatible ASCII format - with features in ReST markup language. The output - is affected by --arch or --feat/--feature flags. - -B - validate the contents of the files under - Documentation/features. - -B or B - list features for this machine's architecture, - using an easier to parse format. - The output is affected by --arch flag. - -=3Dback - -=3Dhead1 OPTIONS - -=3Dover 8 - -=3Ditem B<--arch> - -Output features for an specific architecture, optionally filtering for -a single specific feature. - -=3Ditem B<--feat> or B<--feature> - -Output features for a single specific feature. - -=3Ditem B<--dir> - -Changes the location of the Feature files. By default, it uses -the Documentation/features directory. - -=3Ditem B<--enable-fname> - -Prints the file name of the feature files. This can be used in order to -track dependencies during documentation build. - -=3Ditem B<--debug> - -Put the script in verbose mode, useful for debugging. Can be called multip= le -times, to increase verbosity. - -=3Ditem B<--help> - -Prints a brief help message and exits. - -=3Ditem B<--man> - -Prints the manual page and exits. - -=3Dback - -=3Dhead1 DESCRIPTION - -Parse the Linux feature files from Documentation/features (by default), -optionally producing results at ReST format. - -It supports output data per architecture, per feature or a -feature x arch matrix. - -When used with B command, it will use either one of the tree formats: - -If neither B<--arch> or B<--feature> arguments are used, it will output a -matrix with features per architecture. - -If B<--arch> argument is used, it will output the features availability for -a given architecture. - -If B<--feat> argument is used, it will output the content of the feature -file using ReStructured Text markup. - -=3Dhead1 BUGS - -Report bugs to Mauro Carvalho Chehab - -=3Dhead1 COPYRIGHT - -Copyright (c) 2019 by Mauro Carvalho Chehab . - -License GPLv2: GNU GPL version 2 . - -This is free software: you are free to change and redistribute it. -There is NO WARRANTY, to the extent permitted by law. - -=3Dcut --=20 2.51.1