* [PATCH 0/3] Convert get_feat to Python
@ 2025-11-11 16:17 Mauro Carvalho Chehab
2025-11-11 16:17 ` [PATCH 1/3] tools/docs/get_feat.py: convert get_feat.pl " Mauro Carvalho Chehab
` (3 more replies)
0 siblings, 4 replies; 8+ messages in thread
From: Mauro Carvalho Chehab @ 2025-11-11 16:17 UTC (permalink / raw)
To: Linux Doc Mailing List, Jonathan Corbet
Cc: Mauro Carvalho Chehab, linux-kernel
Hi Jon,
That's the final series to complete the migration of documentation
build: it converts get_feat from Perl to Python.
With that, no Sphinx in-kernel extensions use fork anymore to call
ancillary scripts: everything is now importing Python methods
directly from the libraries.
As we don't have a definition yet for the location of the Python
libraries yet, I opted to place the parse features library inside
tools/docs/lib. It shouldn't be hard to move it elsewhere once we
define a better place for them.
There's nothing special on this conversion: it is a direct translation,
almost bug-compatible with the original version (*).
(*) I did solve two or three caveats on patch 1.
Most of the complexity of the script relies at the logic to produce
ReST tables. I do have here on my internal scripts a (somewhat) generic
formatter for ReST tables in Python. I was tempted to convert the logic
to use it, but, as this could cause regressions, I opted to not do it
right now, mainly because the matrix table logic is complex. Also,
I'm tempted to modify a little bit the output there, but extra tests
are required to see if PDF output would work with complex tables (I
remember I had a problem with that in the past). So, I'm postponing
such extra cleanup.
Mauro Carvalho Chehab (3):
tools/docs/get_feat.py: convert get_feat.pl to Python
Documentation/sphinx/kernel_feat.py: use class directly
get_feat.pl: remove it, as it got replaced by get_feat.py
Documentation/sphinx/kernel_feat.py | 24 +-
tools/docs/get_feat.pl | 641 ----------------------------
tools/docs/get_feat.py | 225 ++++++++++
/parse_features.py | 494 +++++++++++++++++++++
4 files changed, 732 insertions(+), 652 deletions(-)
delete mode 100755 tools/docs/get_feat.pl
create mode 100755 tools/docs/get_feat.py
create mode 100755 tools/docs/lib/parse_features.py
--
2.51.1
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 1/3] tools/docs/get_feat.py: convert get_feat.pl to Python
2025-11-11 16:17 [PATCH 0/3] Convert get_feat to Python Mauro Carvalho Chehab
@ 2025-11-11 16:17 ` Mauro Carvalho Chehab
2025-11-11 16:17 ` [PATCH 2/3] Documentation/sphinx/kernel_feat.py: use class directly Mauro Carvalho Chehab
` (2 subsequent siblings)
3 siblings, 0 replies; 8+ messages in thread
From: Mauro Carvalho Chehab @ 2025-11-11 16:17 UTC (permalink / raw)
To: Linux Doc Mailing List, Jonathan Corbet
Cc: Mauro Carvalho Chehab, linux-kernel
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 <mchehab+huawei@kernel.org>
---
Documentation/sphinx/kernel_feat.py | 5 +
tools/docs/get_feat.py | 225 +++++++++++++
tools/docs/lib/parse_features.py | 494 ++++++++++++++++++++++++++++
3 files changed, 724 insertions(+)
create mode 100755 tools/docs/get_feat.py
create mode 100755 tools/docs/lib/parse_features.py
diff --git a/Documentation/sphinx/kernel_feat.py b/Documentation/sphinx/kernel_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
+srctree = os.path.abspath(os.environ["srctree"])
+sys.path.insert(0, os.path.join(srctree, "tools/docs/lib"))
+
+from parse_features import ParseFeature # pylint: disable=C0413
+
def ErrorString(exc): # Shamelessly stolen from docutils
return f'{exc.__class__.__name}: {exc}'
diff --git a/tools/docs/get_feat.py b/tools/docs/get_feat.py
new file mode 100755
index 000000000000..91f9abe246cf
--- /dev/null
+++ b/tools/docs/get_feat.py
@@ -0,0 +1,225 @@
+#!/usr/bin/env python3
+# pylint: disable=R0902,R0911,R0912,R0914,R0915
+# Copyright(c) 2025: Mauro Carvalho Chehab <mchehab@kernel.org>.
+# 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 = "lib"
+SRC_DIR = os.path.dirname(os.path.realpath(__file__))
+
+sys.path.insert(0, os.path.join(SRC_DIR, LIB_DIR))
+
+from parse_features import ParseFeature # pylint: disable=C0413
+
+SRCTREE = os.path.join(os.path.dirname(os.path.realpath(__file__)), "../..")
+DEFAULT_DIR = "Documentation/features"
+
+
+class GetFeature:
+ """Helper class to parse feature parsing parameters"""
+
+ @staticmethod
+ def get_current_arch():
+ """Detects the current architecture"""
+
+ proc = subprocess.run(["uname", "-m"], check=True,
+ capture_output=True, text=True)
+
+ arch = proc.stdout.strip()
+ if arch in ["x86_64", "i386"]:
+ arch = "x86"
+ elif arch == "s390x":
+ arch = "s390"
+
+ return arch
+
+ def run_parser(self, args):
+ """Execute the feature parser"""
+
+ feat = ParseFeature(args.directory, args.debug, args.enable_fname)
+ data = 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 = self.run_parser(args)
+
+ if args.arch:
+ rst = feat.output_arch_table(args.arch, args.feat)
+ elif args.feat:
+ rst = feat.output_feature(args.feat)
+ else:
+ rst = feat.output_matrix()
+
+ print(rst)
+
+ def run_current(self, args):
+ """
+ Instead of using a --arch parameter, get feature for the current
+ architecture.
+ """
+
+ args.arch = 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 = self.get_current_arch()
+
+ feat = self.run_parser(args)
+ msg = 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="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="Output features for a single specific "
+ "feature.")
+
+
+ def current_args(self, subparsers):
+ """Implementscurrent argparse subparser"""
+
+ parser = subparsers.add_parser("current",
+ formatter_class=argparse.RawTextHelpFormatter,
+ description="Output table in ReST "
+ "compatible ASCII format "
+ "with features for this "
+ "machine's architecture")
+
+ self.parse_feat(parser)
+ parser.set_defaults(func=self.run_current)
+
+ def rest_args(self, subparsers):
+ """Implement rest argparse subparser"""
+
+ parser = subparsers.add_parser("rest",
+ formatter_class=argparse.RawTextHelpFormatter,
+ description="Output table(s) in ReST "
+ "compatible ASCII format "
+ "with features in ReST "
+ "markup language. The "
+ "output is affected by "
+ "--arch or --feat/--feature"
+ " flags.")
+
+ self.parse_arch(parser)
+ self.parse_feat(parser)
+ parser.set_defaults(func=self.run_rest)
+
+ def list_args(self, subparsers):
+ """Implement list argparse subparser"""
+
+ parser = subparsers.add_parser("list",
+ formatter_class=argparse.RawTextHelpFormatter,
+ description="List features for this "
+ "machine's architecture, "
+ "using an easier to parse "
+ "format. The output is "
+ "affected by --arch flag.")
+
+ self.parse_arch(parser)
+ self.parse_feat(parser)
+ parser.set_defaults(func=self.run_list)
+
+ def validate_args(self, subparsers):
+ """Implement validate argparse subparser"""
+
+ parser = subparsers.add_parser("validate",
+ formatter_class=argparse.RawTextHelpFormatter,
+ description="Validate the contents of "
+ "the files under "
+ f"{DEFAULT_DIR}.")
+
+ parser.set_defaults(func=self.run_parser)
+
+ def parser(self):
+ """
+ Create an arparse with common options and several subparsers
+ """
+ parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter)
+
+ parser.add_argument("-d", "--debug", action="count", default=0,
+ help="Put the script in verbose mode, useful for "
+ "debugging. Can be called multiple times, to "
+ "increase verbosity.")
+
+ parser.add_argument("--directory", "--dir", default=DEFAULT_DIR,
+ help="Changes the location of the Feature files. "
+ f"By default, it uses the {DEFAULT_DIR} "
+ "directory.")
+
+ parser.add_argument("--enable-fname", action="store_true",
+ help="Prints the file name of the feature files. "
+ "This can be used in order to track "
+ "dependencies during documentation build.")
+
+ subparsers = parser.add_subparsers()
+
+ self.current_args(subparsers)
+ self.rest_args(subparsers)
+ self.list_args(subparsers)
+ self.validate_args(subparsers)
+
+ args = parser.parse_args()
+
+ return args
+
+
+def main():
+ """Main program"""
+
+ feat = GetFeature()
+
+ args = 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__ == "__main__":
+ main()
diff --git a/tools/docs/lib/parse_features.py b/tools/docs/lib/parse_features.py
new file mode 100755
index 000000000000..b88c04d3e2fe
--- /dev/null
+++ b/tools/docs/lib/parse_features.py
@@ -0,0 +1,494 @@
+#!/usr/bin/env python3
+# pylint: disable=R0902,R0911,R0912,R0914,R0915
+# Copyright(c) 2025: Mauro Carvalho Chehab <mchehab@kernel.org>.
+# 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 = "Feature"
+ h_kconfig = "Kconfig"
+ h_description = "Description"
+ h_subsys = "Subsystem"
+ h_status = "Status"
+ h_arch = "Architecture"
+
+ # Sort order for status. Others will be mapped at the end.
+ status_map = {
+ "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=0, enable_fname=False):
+ """
+ Sets internal variables
+ """
+
+ self.prefix = prefix
+ self.debug = debug
+ self.enable_fname = enable_fname
+
+ self.data = {}
+
+ # Initial maximum values use just the headers
+ self.max_size_name = len(self.h_name)
+ self.max_size_kconfig = len(self.h_kconfig)
+ self.max_size_description = len(self.h_description)
+ self.max_size_desc_word = 0
+ self.max_size_subsys = len(self.h_subsys)
+ self.max_size_status = len(self.h_status)
+ self.max_size_arch = len(self.h_arch)
+ self.max_size_arch_with_header = self.max_size_arch + self.max_size_arch
+ self.description_size = 1
+
+ self.msg = ""
+
+ def emit(self, msg="", end="\n"):
+ self.msg += msg + end
+
+ def parse_error(self, fname, ln, msg, data=None):
+ """
+ Displays an error message, printing file name and line
+ """
+
+ if ln:
+ fname += f"#{ln}"
+
+ print(f"Warning: file {fname}: {msg}", file=sys.stderr, end="")
+
+ if data:
+ data = data.rstrip()
+ print(f":\n\t{data}", file=sys.stderr)
+ else:
+ print("", file=sys.stderr)
+
+ def parse_feat_file(self, fname):
+ """Parses a single arch-support.txt feature file"""
+
+ if os.path.isdir(fname):
+ return
+
+ base = os.path.basename(fname)
+
+ if base != "arch-support.txt":
+ if self.debug:
+ print(f"ignoring {fname}", file=sys.stderr)
+ return
+
+ subsys = os.path.dirname(fname).split("/")[-2]
+ self.max_size_subsys = max(self.max_size_subsys, len(subsys))
+
+ feature_name = ""
+ kconfig = ""
+ description = ""
+ comments = ""
+ arch_table = {}
+
+ if self.debug > 1:
+ print(f"Opening {fname}", file=sys.stderr)
+
+ if self.enable_fname:
+ full_fname = os.path.abspath(fname)
+ self.emit(f".. FILE {full_fname}")
+
+ with open(fname, encoding="utf-8") as f:
+ for ln, line in enumerate(f, start=1):
+ line = line.strip()
+
+ match = re.match(r"^\#\s+Feature\s+name:\s*(.*\S)", line)
+ if match:
+ feature_name = match.group(1)
+
+ self.max_size_name = max(self.max_size_name,
+ len(feature_name))
+ continue
+
+ match = re.match(r"^\#\s+Kconfig:\s*(.*\S)", line)
+ if match:
+ kconfig = match.group(1)
+
+ self.max_size_kconfig = max(self.max_size_kconfig,
+ len(kconfig))
+ continue
+
+ match = re.match(r"^\#\s+description:\s*(.*\S)", line)
+ if match:
+ description = match.group(1)
+
+ self.max_size_description = max(self.max_size_description,
+ len(description))
+
+ words = re.split(r"\s+", line)[1:]
+ for word in words:
+ self.max_size_desc_word = 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 = re.match(r"^\#\s*(.*)$", line)
+ if match:
+ comments += match.group(1)
+ continue
+
+ match = re.match(r"^\s*\|\s*(\S+):\s*\|\s*(\S+)\s*\|\s*$", line)
+ if match:
+ arch = match.group(1)
+ status = match.group(2)
+
+ self.max_size_status = max(self.max_size_status,
+ len(status))
+ self.max_size_arch = max(self.max_size_arch, len(arch))
+
+ if status == "..":
+ status = "---"
+
+ arch_table[arch] = 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] = {
+ "where": fname,
+ "subsys": subsys,
+ "kconfig": kconfig,
+ "description": description,
+ "comments": comments,
+ "table": arch_table,
+ }
+
+ self.max_size_arch_with_header = self.max_size_arch + len(self.h_arch)
+
+ def parse(self):
+ """Parses all arch-support.txt feature files inside self.prefix"""
+
+ path = os.path.expanduser(self.prefix)
+
+ if self.debug > 2:
+ print(f"Running parser for {path}")
+
+ example_path = os.path.join(path, "arch-support.txt")
+
+ for fname in iglob(os.path.join(path, "**"), recursive=True):
+ if fname != example_path:
+ self.parse_feat_file(fname)
+
+ return self.data
+
+ def output_arch_table(self, arch, feat=None):
+ """
+ Output feature(s) for a given architecture.
+ """
+
+ title = f"Feature status on {arch} architecture"
+
+ self.emit("=" * len(title))
+ self.emit(title)
+ self.emit("=" * len(title))
+ self.emit()
+
+ self.emit("=" * self.max_size_subsys + " ", end="")
+ self.emit("=" * self.max_size_name + " ", end="")
+ self.emit("=" * self.max_size_kconfig + " ", end="")
+ self.emit("=" * self.max_size_status + " ", end="")
+ self.emit("=" * self.max_size_description)
+
+ self.emit(f"{self.h_subsys:<{self.max_size_subsys}} ", end="")
+ self.emit(f"{self.h_name:<{self.max_size_name}} ", end="")
+ self.emit(f"{self.h_kconfig:<{self.max_size_kconfig}} ", end="")
+ self.emit(f"{self.h_status:<{self.max_size_status}} ", end="")
+ self.emit(f"{self.h_description:<{self.max_size_description}}")
+
+ self.emit("=" * self.max_size_subsys + " ", end="")
+ self.emit("=" * self.max_size_name + " ", end="")
+ self.emit("=" * self.max_size_kconfig + " ", end="")
+ self.emit("=" * self.max_size_status + " ", end="")
+ self.emit("=" * self.max_size_description)
+
+ sorted_features = sorted(self.data.keys(),
+ key=lambda x: (self.data[x]["subsys"],
+ x.lower()))
+
+ for name in sorted_features:
+ if feat and name != feat:
+ continue
+
+ arch_table = self.data[name]["table"]
+
+ if not arch in arch_table:
+ continue
+
+ self.emit(f"{self.data[name]['subsys']:<{self.max_size_subsys}} ",
+ end="")
+ self.emit(f"{name:<{self.max_size_name}} ", end="")
+ self.emit(f"{self.data[name]['kconfig']:<{self.max_size_kconfig}} ",
+ end="")
+ self.emit(f"{arch_table[arch]:<{self.max_size_status}} ",
+ end="")
+ self.emit(f"{self.data[name]['description']}")
+
+ self.emit("=" * self.max_size_subsys + " ", end="")
+ self.emit("=" * self.max_size_name + " ", end="")
+ self.emit("=" * self.max_size_kconfig + " ", end="")
+ self.emit("=" * self.max_size_status + " ", end="")
+ self.emit("=" * self.max_size_description)
+
+ return self.msg
+
+ def output_feature(self, feat):
+ """
+ Output a feature on all architectures
+ """
+
+ title = f"Feature {feat}"
+
+ self.emit("=" * len(title))
+ self.emit(title)
+ self.emit("=" * 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 = self.data[feat]["description"]
+ desc = desc[0].upper() + desc[1:]
+ desc = desc.rstrip(". \t")
+ self.emit(f"\n{desc}.\n")
+
+ com = self.data[feat]["comments"].strip()
+ if com:
+ self.emit("Comments")
+ self.emit("--------")
+ self.emit(f"\n{com}\n")
+
+ self.emit("=" * self.max_size_arch + " ", end="")
+ self.emit("=" * self.max_size_status)
+
+ self.emit(f"{self.h_arch:<{self.max_size_arch}} ", end="")
+ self.emit(f"{self.h_status:<{self.max_size_status}}")
+
+ self.emit("=" * self.max_size_arch + " ", end="")
+ self.emit("=" * self.max_size_status)
+
+ arch_table = self.data[feat]["table"]
+ for arch in sorted(arch_table.keys()):
+ self.emit(f"{arch:<{self.max_size_arch}} ", end="")
+ self.emit(f"{arch_table[arch]:<{self.max_size_status}}")
+
+ self.emit("=" * self.max_size_arch + " ", end="")
+ self.emit("=" * 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 = "="
+ else:
+ ln_marker = "-"
+
+ self.emit("+" + ln_marker * self.max_size_name + "+", end="")
+ self.emit(ln_marker * desc_size, end="")
+ 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 = "Feature status on all architectures"
+
+ self.emit("=" * len(title))
+ self.emit(title)
+ self.emit("=" * len(title))
+ self.emit()
+
+ desc_title = f"{self.h_kconfig} / {self.h_description}"
+
+ desc_size = self.max_size_kconfig + 4
+ if not self.description_size:
+ desc_size = max(self.max_size_description, desc_size)
+ else:
+ desc_size = max(self.description_size, desc_size)
+
+ desc_size = max(self.max_size_desc_word, desc_size, len(desc_title))
+
+ notcompat = "Not compatible"
+ self.max_size_status = max(self.max_size_status, len(notcompat))
+
+ min_status_size = self.max_size_status + self.max_size_arch + 4
+ max_size_status = max(min_status_size, self.max_size_status)
+
+ h_status_per_arch = "Status per architecture"
+ max_size_status = max(max_size_status, len(h_status_per_arch))
+
+ cur_subsys = None
+ for name in sorted(self.data.keys(),
+ key=lambda x: (self.data[x]["subsys"], x.lower())):
+ if not cur_subsys or cur_subsys != self.data[name]["subsys"]:
+ if cur_subsys:
+ self.emit()
+
+ cur_subsys = self.data[name]["subsys"]
+
+ title = f"Subsystem: {cur_subsys}"
+ self.emit(title)
+ self.emit("=" * len(title))
+ self.emit()
+
+ self.matrix_lines(desc_size, max_size_status, 0)
+
+ self.emit(f"|{self.h_name:<{self.max_size_name}}", end="")
+ self.emit(f"|{desc_title:<{desc_size}}", end="")
+ self.emit(f"|{h_status_per_arch:<{max_size_status}}|")
+
+ self.matrix_lines(desc_size, max_size_status, 1)
+
+ lines = []
+ descs = []
+ cur_status = ""
+ line = ""
+
+ arch_table = sorted(self.data[name]["table"].items(),
+ key=lambda x: (self.status_map.get(x[1], 99),
+ x[0].lower()))
+
+ for arch, status in arch_table:
+ if status == "---":
+ status = notcompat
+
+ if status != cur_status:
+ if line != "":
+ lines.append(line)
+ line = ""
+ line = f"- **{status}**: {arch}"
+ elif len(line) + len(arch) + 2 < max_size_status:
+ line += f", {arch}"
+ else:
+ lines.append(line)
+ line = f" {arch}"
+ cur_status = status
+
+ if line != "":
+ lines.append(line)
+
+ description = self.data[name]["description"]
+ while len(description) > desc_size:
+ desc_line = description[:desc_size]
+
+ last_space = desc_line.rfind(" ")
+ if last_space != -1:
+ desc_line = desc_line[:last_space]
+ descs.append(desc_line)
+ description = description[last_space + 1:]
+ else:
+ desc_line = desc_line[:-1]
+ descs.append(desc_line + "\\")
+ description = description[len(desc_line):]
+
+ if description:
+ descs.append(description)
+
+ while len(lines) < 2 + len(descs):
+ lines.append("")
+
+ for ln, line in enumerate(lines):
+ col = ["", ""]
+
+ if not ln:
+ col[0] = name
+ col[1] = f"``{self.data[name]['kconfig']}``"
+ else:
+ if ln >= 2 and descs:
+ col[1] = descs.pop(0)
+
+ self.emit(f"|{col[0]:<{self.max_size_name}}", end="")
+ self.emit(f"|{col[1]:<{desc_size}}", end="")
+ 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 architecture.
+ """
+ self.emit("#")
+ self.emit(f"# Kernel feature support matrix of the '{arch}' architecture:")
+ self.emit("#")
+
+ # Sort by subsystem, then by feature name (case‑insensitive)
+ for name in sorted(self.data.keys(),
+ key=lambda n: (self.data[n]["subsys"].lower(),
+ n.lower())):
+ if feat and name != feat:
+ continue
+
+ feature = self.data[name]
+ arch_table = feature["table"]
+ status = arch_table.get(arch, "")
+ status = " " * ((4 - len(status)) // 2) + status
+
+ self.emit(f"{feature['subsys']:>{self.max_size_subsys + 1}}/ ",
+ end="")
+ self.emit(f"{name:<{self.max_size_name}}: ", end="")
+ self.emit(f"{status:<5}| ", end="")
+ self.emit(f"{feature['kconfig']:>{self.max_size_kconfig}} ",
+ end="")
+ self.emit(f"# {feature['description']}")
+
+ return self.msg
--
2.51.1
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 2/3] Documentation/sphinx/kernel_feat.py: use class directly
2025-11-11 16:17 [PATCH 0/3] Convert get_feat to Python Mauro Carvalho Chehab
2025-11-11 16:17 ` [PATCH 1/3] tools/docs/get_feat.py: convert get_feat.pl " Mauro Carvalho Chehab
@ 2025-11-11 16:17 ` Mauro Carvalho Chehab
2025-11-11 16:17 ` [PATCH 3/3] get_feat.pl: remove it, as it got replaced by get_feat.py Mauro Carvalho Chehab
2025-11-11 17:09 ` [PATCH 0/3] Convert get_feat to Python Jonathan Corbet
3 siblings, 0 replies; 8+ messages in thread
From: Mauro Carvalho Chehab @ 2025-11-11 16:17 UTC (permalink / raw)
To: Linux Doc Mailing List, Jonathan Corbet
Cc: Mauro Carvalho Chehab, linux-kernel
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 <mchehab+huawei@kernel.org>
---
Documentation/sphinx/kernel_feat.py | 19 ++++++++-----------
1 file changed, 8 insertions(+), 11 deletions(-)
diff --git a/Documentation/sphinx/kernel_feat.py b/Documentation/sphinx/kernel_feat.py
index 1dcbfe335a65..59b3ca573021 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
from docutils import nodes, statemachine
@@ -89,18 +88,16 @@ class KernelFeat(Directive):
srctree = os.path.abspath(os.environ["srctree"])
- args = [
- os.path.join(srctree, 'tools/docs/get_feat.pl'),
- 'rest',
- '--enable-fname',
- '--dir',
- os.path.join(srctree, 'Documentation', self.arguments[0]),
- ]
+ feature_dir = os.path.join(srctree, 'Documentation', self.arguments[0])
+
+ feat = ParseFeature(feature_dir, False, True)
+ feat.parse()
if len(self.arguments) > 1:
- args.extend(['--arch', self.arguments[1]])
-
- lines = subprocess.check_output(args, cwd=os.path.dirname(doc.current_source)).decode('utf-8')
+ arch = self.arguments[1]
+ lines = feat.output_arch_table(arch)
+ else:
+ lines = feat.output_matrix()
line_regex = re.compile(r"^\.\. FILE (\S+)$")
--
2.51.1
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 3/3] get_feat.pl: remove it, as it got replaced by get_feat.py
2025-11-11 16:17 [PATCH 0/3] Convert get_feat to Python Mauro Carvalho Chehab
2025-11-11 16:17 ` [PATCH 1/3] tools/docs/get_feat.py: convert get_feat.pl " Mauro Carvalho Chehab
2025-11-11 16:17 ` [PATCH 2/3] Documentation/sphinx/kernel_feat.py: use class directly Mauro Carvalho Chehab
@ 2025-11-11 16:17 ` Mauro Carvalho Chehab
2025-11-11 17:09 ` [PATCH 0/3] Convert get_feat to Python Jonathan Corbet
3 siblings, 0 replies; 8+ messages in thread
From: Mauro Carvalho Chehab @ 2025-11-11 16:17 UTC (permalink / raw)
To: Linux Doc Mailing List, Jonathan Corbet
Cc: Mauro Carvalho Chehab, linux-kernel
Now that this was rewritten in Python, we can remove the old
tool.
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
---
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 = abs_path($0);
-$basename =~ s,/[^/]+$,/,;
-
-my $prefix=$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 = 1;
-my $description_size = 1;
-
-GetOptions(
- "debug|d+" => \$debug,
- "dir=s" => \$prefix,
- 'help|?' => \$help,
- 'arch=s' => \$arch,
- 'feat=s' => \$feat,
- 'feature=s' => \$feat,
- "enable-fname" => \$enable_fname,
- man => \$man
-) or pod2usage(2);
-
-pod2usage(1) if $help;
-pod2usage(-exitstatus => 0, -verbose => 2) if $man;
-
-pod2usage(1) if (scalar @ARGV < 1 || @ARGV > 2);
-
-my ($cmd, $arg) = @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) = @_;
-
- $data =~ 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 = "Feature";
-my $h_kconfig = "Kconfig";
-my $h_description = "Description";
-my $h_subsys = "Subsystem";
-my $h_status = "Status";
-my $h_arch = "Architecture";
-
-my $max_size_name = length($h_name);
-my $max_size_kconfig = length($h_kconfig);
-my $max_size_description = length($h_description);
-my $max_size_subsys = length($h_subsys);
-my $max_size_status = length($h_status);
-
-my $max_size_arch = 0;
-my $max_size_arch_with_header;
-my $max_description_word = 0;
-
-sub parse_feat {
- my $file = $File::Find::name;
-
- my $mode = (stat($file))[2];
- return if ($mode & S_IFDIR);
- return if ($file =~ m,($prefix)/arch-support.txt,);
- return if (!($file =~ m,arch-support.txt$,));
-
- if ($enable_fname) {
- printf ".. FILE %s\n", abs_path($file);
- }
-
- my $subsys = "";
- $subsys = $2 if ( m,.*($prefix)/([^/]+).*,);
-
- if (length($subsys) > $max_size_subsys) {
- $max_size_subsys = length($subsys);
- }
-
- my $name;
- my $kconfig;
- my $description;
- my $comments = "";
- my $last_status;
- my $ln;
- my %arch_table;
-
- print STDERR "Opening $file\n" if ($debug > 1);
- open IN, $file;
-
- while(<IN>) {
- $ln++;
-
- if (m/^\#\s+Feature\s+name:\s*(.*\S)/) {
- $name = $1;
- if (length($name) > $max_size_name) {
- $max_size_name = length($name);
- }
- next;
- }
- if (m/^\#\s+Kconfig:\s*(.*\S)/) {
- $kconfig = $1;
- if (length($kconfig) > $max_size_kconfig) {
- $max_size_kconfig = length($kconfig);
- }
- next;
- }
- if (m/^\#\s+description:\s*(.*\S)/) {
- $description = $1;
- if (length($description) > $max_size_description) {
- $max_size_description = length($description);
- }
-
- foreach my $word (split /\s+/, $description) {
- if (length($word) > $max_description_word) {
- $max_description_word = 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 .= "$1\n";
- next;
- }
- if (m/^\s*\|\s*(\S+):\s*\|\s*(\S+)\s*\|\s*$/) {
- my $a = $1;
- my $status = $2;
-
- if (length($status) > $max_size_status) {
- $max_size_status = length($status);
- }
- if (length($a) > $max_size_arch) {
- $max_size_arch = length($a);
- }
-
- $status = "---" if ($status =~ m/^\.\.$/);
-
- $archs{$a} = 1;
- $arch_table{$a} = $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} = $file;
- $data{$name}->{subsys} = $subsys;
- $data{$name}->{kconfig} = $kconfig;
- $data{$name}->{description} = $description;
- $data{$name}->{comments} = $comments;
- $data{$name}->{table} = \%arch_table;
-
- $max_size_arch_with_header = $max_size_arch + length($h_arch);
-}
-
-#
-# Output feature(s) for a given architecture
-#
-sub output_arch_table {
- my $title = "Feature status on $arch architecture";
-
- print "=" x length($title) . "\n";
- print "$title\n";
- print "=" x length($title) . "\n\n";
-
- print "=" x $max_size_subsys;
- print " ";
- print "=" x $max_size_name;
- print " ";
- print "=" x $max_size_kconfig;
- print " ";
- print "=" x $max_size_status;
- print " ";
- print "=" 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 "=" x $max_size_subsys;
- print " ";
- print "=" x $max_size_name;
- print " ";
- print "=" x $max_size_kconfig;
- print " ";
- print "=" x $max_size_status;
- print " ";
- print "=" 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 = %{$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 "=" x $max_size_subsys;
- print " ";
- print "=" x $max_size_name;
- print " ";
- print "=" x $max_size_kconfig;
- print " ";
- print "=" x $max_size_status;
- print " ";
- print "=" 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 = %{$data{$name}->{table}};
-
- my $status = $arch_table{$arch};
- $status = " " 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 = "Feature $feat";
-
- print "=" x length($title) . "\n";
- print "$title\n";
- print "=" 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 = $data{$feat}->{description};
- $desc =~ s/^([a-z])/\U$1/;
- $desc =~ s/\.?\s*//;
- print "\n$desc.\n\n";
-
- my $com = $data{$feat}->{comments};
- $com =~ s/^\s+//;
- $com =~ s/\s+$//;
- if ($com) {
- print "Comments\n";
- print "--------\n\n";
- print "$com\n\n";
- }
-
- print "=" x $max_size_arch_with_header;
- print " ";
- print "=" x $max_size_status;
- print "\n";
-
- printf "%-${max_size_arch}s ", $h_arch;
- printf "%-${max_size_status}s", $h_status . "\n";
-
- print "=" x $max_size_arch_with_header;
- print " ";
- print "=" x $max_size_status;
- print "\n";
-
- my %arch_table = %{$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 "=" x $max_size_arch_with_header;
- print " ";
- print "=" x $max_size_status;
- print "\n";
-}
-
-#
-# Output all features for all architectures
-#
-
-sub matrix_lines($$$) {
- my $desc_size = shift;
- my $status_size = shift;
- my $header = shift;
- my $fill;
- my $ln_marker;
-
- if ($header) {
- $ln_marker = "=";
- } else {
- $ln_marker = "-";
- }
-
- $fill = $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 = "Feature status on all architectures";
- my $notcompat = "Not compatible";
-
- print "=" x length($title) . "\n";
- print "$title\n";
- print "=" x length($title) . "\n\n";
-
- my $desc_title = "$h_kconfig / $h_description";
-
- my $desc_size = $max_size_kconfig + 4;
- if (!$description_size) {
- $desc_size = $max_size_description if ($max_size_description > $desc_size);
- } else {
- $desc_size = $description_size if ($description_size > $desc_size);
- }
- $desc_size = $max_description_word if ($max_description_word > $desc_size);
-
- $desc_size = length($desc_title) if (length($desc_title) > $desc_size);
-
- $max_size_status = length($notcompat) if (length($notcompat) > $max_size_status);
-
- # Ensure that the status will fit
- my $min_status_size = $max_size_status + $max_size_arch + 6;
- $status_size = $min_status_size if ($status_size < $min_status_size);
-
-
- my $cur_subsys = "";
- 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 = $data{$name}->{subsys};
-
- my $title = "Subsystem: $cur_subsys";
- print "$title\n";
- print "=" 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 = %{$data{$name}->{table}};
- my $cur_status = "";
-
- my (@lines, @descs);
- my $line = "";
- foreach my $arch (sort {
- ($arch_table{$b} cmp $arch_table{$a}) or
- ("\L$a" cmp "\L$b")
- } keys %arch_table) {
-
- my $status = $arch_table{$arch};
-
- if ($status eq "---") {
- $status = $notcompat;
- }
-
- if ($status ne $cur_status) {
- if ($line ne "") {
- push @lines, $line;
- $line = "";
- }
- $line = "- **" . $status . "**: " . $arch;
- } elsif (length($line) + length ($arch) + 2 < $status_size) {
- $line .= ", " . $arch;
- } else {
- push @lines, $line;
- $line = " " . $arch;
- }
- $cur_status = $status;
- }
- push @lines, $line if ($line ne "");
-
- my $description = $data{$name}->{description};
- while (length($description) > $desc_size) {
- my $d = 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 =~ s/^(.*)\s+.*/$1/)) {
- $d = substr $d, 0, -1;
- push @descs, "$d\\";
- $description =~ s/^\Q$d\E//;
- } else {
- push @descs, $d;
- $description =~ 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 = 0;
- for my $line(@lines) {
- if (!$ln) {
- printf "|%-${max_size_name}s", $name;
- printf "|%-${desc_size}s", "``" . $data{$name}->{kconfig} . "``";
- } elsif ($ln >= 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 =>\&parse_feat, no_chdir => 1}, $prefix);
-
-print STDERR Data::Dumper->Dump([\%data], [qw(*data)]) if ($debug);
-
-#
-# Handles the command
-#
-if ($cmd eq "current") {
- $arch = qx(uname -m | sed 's/x86_64/x86/' | sed 's/i386/x86/' | sed 's/s390x/s390/');
- $arch =~s/\s+$//;
-}
-
-if ($cmd eq "ls" or $cmd eq "list") {
- if (!$arch) {
- $arch = qx(uname -m | sed 's/x86_64/x86/' | sed 's/i386/x86/' | sed 's/s390x/s390/');
- $arch =~s/\s+$//;
- }
-
- list_arch_features;
-
- exit;
-}
-
-if ($cmd ne "validate") {
- if ($arch) {
- output_arch_table;
- } elsif ($feat) {
- output_feature;
- } else {
- output_matrix;
- }
-}
-
-__END__
-
-=head1 NAME
-
-get_feat.pl - parse the Linux Feature files and produce a ReST book.
-
-=head1 SYNOPSIS
-
-B<get_feat.pl> [--debug] [--man] [--help] [--dir=<dir>] [--arch=<arch>]
- [--feature=<feature>|--feat=<feature>] <COMAND> [<ARGUMENT>]
-
-Where <COMMAND> can be:
-
-=over 8
-
-B<current> - output table in ReST compatible ASCII format
- with features for this machine's architecture
-
-B<rest> - 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> - validate the contents of the files under
- Documentation/features.
-
-B<ls> or B<list> - list features for this machine's architecture,
- using an easier to parse format.
- The output is affected by --arch flag.
-
-=back
-
-=head1 OPTIONS
-
-=over 8
-
-=item B<--arch>
-
-Output features for an specific architecture, optionally filtering for
-a single specific feature.
-
-=item B<--feat> or B<--feature>
-
-Output features for a single specific feature.
-
-=item B<--dir>
-
-Changes the location of the Feature files. By default, it uses
-the Documentation/features directory.
-
-=item B<--enable-fname>
-
-Prints the file name of the feature files. This can be used in order to
-track dependencies during documentation build.
-
-=item B<--debug>
-
-Put the script in verbose mode, useful for debugging. Can be called multiple
-times, to increase verbosity.
-
-=item B<--help>
-
-Prints a brief help message and exits.
-
-=item B<--man>
-
-Prints the manual page and exits.
-
-=back
-
-=head1 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<rest> 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.
-
-=head1 BUGS
-
-Report bugs to Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
-
-=head1 COPYRIGHT
-
-Copyright (c) 2019 by Mauro Carvalho Chehab <mchehab+samsung@kernel.org>.
-
-License GPLv2: GNU GPL version 2 <http://gnu.org/licenses/gpl.html>.
-
-This is free software: you are free to change and redistribute it.
-There is NO WARRANTY, to the extent permitted by law.
-
-=cut
--
2.51.1
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH 0/3] Convert get_feat to Python
2025-11-11 16:17 [PATCH 0/3] Convert get_feat to Python Mauro Carvalho Chehab
` (2 preceding siblings ...)
2025-11-11 16:17 ` [PATCH 3/3] get_feat.pl: remove it, as it got replaced by get_feat.py Mauro Carvalho Chehab
@ 2025-11-11 17:09 ` Jonathan Corbet
2025-11-11 22:12 ` Mauro Carvalho Chehab
3 siblings, 1 reply; 8+ messages in thread
From: Jonathan Corbet @ 2025-11-11 17:09 UTC (permalink / raw)
To: Mauro Carvalho Chehab, Linux Doc Mailing List
Cc: Mauro Carvalho Chehab, linux-kernel
Mauro Carvalho Chehab <mchehab+huawei@kernel.org> writes:
> Hi Jon,
>
> That's the final series to complete the migration of documentation
> build: it converts get_feat from Perl to Python.
>
> With that, no Sphinx in-kernel extensions use fork anymore to call
> ancillary scripts: everything is now importing Python methods
> directly from the libraries.
This, of course, conflicts with my library move ... Maybe we review
that and I go first this time? :)
jon
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 0/3] Convert get_feat to Python
2025-11-11 17:09 ` [PATCH 0/3] Convert get_feat to Python Jonathan Corbet
@ 2025-11-11 22:12 ` Mauro Carvalho Chehab
2025-11-18 16:44 ` Jonathan Corbet
0 siblings, 1 reply; 8+ messages in thread
From: Mauro Carvalho Chehab @ 2025-11-11 22:12 UTC (permalink / raw)
To: Jonathan Corbet; +Cc: Linux Doc Mailing List, linux-kernel
Em Tue, 11 Nov 2025 10:09:30 -0700
Jonathan Corbet <corbet@lwn.net> escreveu:
> Mauro Carvalho Chehab <mchehab+huawei@kernel.org> writes:
>
> > Hi Jon,
> >
> > That's the final series to complete the migration of documentation
> > build: it converts get_feat from Perl to Python.
> >
> > With that, no Sphinx in-kernel extensions use fork anymore to call
> > ancillary scripts: everything is now importing Python methods
> > directly from the libraries.
>
> This, of course, conflicts with my library move ... Maybe we review
> that and I go first this time? :)
Sure, go ahead with the library move, while this is reviewed.
This one is trivial enough to be rebased on the top of it.
Thanks,
Mauro
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 0/3] Convert get_feat to Python
2025-11-11 22:12 ` Mauro Carvalho Chehab
@ 2025-11-18 16:44 ` Jonathan Corbet
2025-11-18 19:10 ` Mauro Carvalho Chehab
0 siblings, 1 reply; 8+ messages in thread
From: Jonathan Corbet @ 2025-11-18 16:44 UTC (permalink / raw)
To: Mauro Carvalho Chehab; +Cc: Linux Doc Mailing List, linux-kernel
Mauro Carvalho Chehab <mchehab+huawei@kernel.org> writes:
> Em Tue, 11 Nov 2025 10:09:30 -0700
> Jonathan Corbet <corbet@lwn.net> escreveu:
>
>> Mauro Carvalho Chehab <mchehab+huawei@kernel.org> writes:
>>
>> > Hi Jon,
>> >
>> > That's the final series to complete the migration of documentation
>> > build: it converts get_feat from Perl to Python.
>> >
>> > With that, no Sphinx in-kernel extensions use fork anymore to call
>> > ancillary scripts: everything is now importing Python methods
>> > directly from the libraries.
>>
>> This, of course, conflicts with my library move ... Maybe we review
>> that and I go first this time? :)
>
> Sure, go ahead with the library move, while this is reviewed.
>
> This one is trivial enough to be rebased on the top of it.
That time has come ... we should still be able to get this one into 6.19
if all goes well.
Thanks,
jon
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 0/3] Convert get_feat to Python
2025-11-18 16:44 ` Jonathan Corbet
@ 2025-11-18 19:10 ` Mauro Carvalho Chehab
0 siblings, 0 replies; 8+ messages in thread
From: Mauro Carvalho Chehab @ 2025-11-18 19:10 UTC (permalink / raw)
To: Jonathan Corbet; +Cc: Linux Doc Mailing List, linux-kernel
Em Tue, 18 Nov 2025 09:44:45 -0700
Jonathan Corbet <corbet@lwn.net> escreveu:
> Mauro Carvalho Chehab <mchehab+huawei@kernel.org> writes:
>
> > Em Tue, 11 Nov 2025 10:09:30 -0700
> > Jonathan Corbet <corbet@lwn.net> escreveu:
> >
> >> Mauro Carvalho Chehab <mchehab+huawei@kernel.org> writes:
> >>
> >> > Hi Jon,
> >> >
> >> > That's the final series to complete the migration of documentation
> >> > build: it converts get_feat from Perl to Python.
> >> >
> >> > With that, no Sphinx in-kernel extensions use fork anymore to call
> >> > ancillary scripts: everything is now importing Python methods
> >> > directly from the libraries.
> >>
> >> This, of course, conflicts with my library move ... Maybe we review
> >> that and I go first this time? :)
> >
> > Sure, go ahead with the library move, while this is reviewed.
> >
> > This one is trivial enough to be rebased on the top of it.
>
> That time has come ... we should still be able to get this one into 6.19
> if all goes well.
OK.
Just sent a rebased version on the top of current docs-next.
Thanks,
Mauro
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2025-11-18 19:10 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-11-11 16:17 [PATCH 0/3] Convert get_feat to Python Mauro Carvalho Chehab
2025-11-11 16:17 ` [PATCH 1/3] tools/docs/get_feat.py: convert get_feat.pl " Mauro Carvalho Chehab
2025-11-11 16:17 ` [PATCH 2/3] Documentation/sphinx/kernel_feat.py: use class directly Mauro Carvalho Chehab
2025-11-11 16:17 ` [PATCH 3/3] get_feat.pl: remove it, as it got replaced by get_feat.py Mauro Carvalho Chehab
2025-11-11 17:09 ` [PATCH 0/3] Convert get_feat to Python Jonathan Corbet
2025-11-11 22:12 ` Mauro Carvalho Chehab
2025-11-18 16:44 ` Jonathan Corbet
2025-11-18 19:10 ` Mauro Carvalho Chehab
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).