Skip to content

Commit caa642b

Browse files
mchehabJonathan Corbet
authored andcommitted
tools/docs/get_feat.py: convert get_feat.pl to Python
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> Signed-off-by: Jonathan Corbet <corbet@lwn.net> Message-ID: <03c26cee1ec567804735a33047e625ef5ab7bfa8.1763492868.git.mchehab+huawei@kernel.org>
1 parent 55fb2d5 commit caa642b

3 files changed

Lines changed: 724 additions & 0 deletions

File tree

Documentation/sphinx/kernel_feat.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@
4242
from docutils.parsers.rst import directives, Directive
4343
from sphinx.util.docutils import switch_source_input
4444

45+
srctree = os.path.abspath(os.environ["srctree"])
46+
sys.path.insert(0, os.path.join(srctree, "tools/docs/lib"))
47+
48+
from parse_features import ParseFeature # pylint: disable=C0413
49+
4550
def ErrorString(exc): # Shamelessly stolen from docutils
4651
return f'{exc.__class__.__name}: {exc}'
4752

tools/docs/get_feat.py

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
#!/usr/bin/env python3
2+
# pylint: disable=R0902,R0911,R0912,R0914,R0915
3+
# Copyright(c) 2025: Mauro Carvalho Chehab <mchehab@kernel.org>.
4+
# SPDX-License-Identifier: GPL-2.0
5+
6+
7+
"""
8+
Parse the Linux Feature files and produce a ReST book.
9+
"""
10+
11+
import argparse
12+
import os
13+
import subprocess
14+
import sys
15+
16+
from pprint import pprint
17+
18+
LIB_DIR = "../../tools/lib/python"
19+
SRC_DIR = os.path.dirname(os.path.realpath(__file__))
20+
21+
sys.path.insert(0, os.path.join(SRC_DIR, LIB_DIR))
22+
23+
from feat.parse_features import ParseFeature # pylint: disable=C0413
24+
25+
SRCTREE = os.path.join(os.path.dirname(os.path.realpath(__file__)), "../..")
26+
DEFAULT_DIR = "Documentation/features"
27+
28+
29+
class GetFeature:
30+
"""Helper class to parse feature parsing parameters"""
31+
32+
@staticmethod
33+
def get_current_arch():
34+
"""Detects the current architecture"""
35+
36+
proc = subprocess.run(["uname", "-m"], check=True,
37+
capture_output=True, text=True)
38+
39+
arch = proc.stdout.strip()
40+
if arch in ["x86_64", "i386"]:
41+
arch = "x86"
42+
elif arch == "s390x":
43+
arch = "s390"
44+
45+
return arch
46+
47+
def run_parser(self, args):
48+
"""Execute the feature parser"""
49+
50+
feat = ParseFeature(args.directory, args.debug, args.enable_fname)
51+
data = feat.parse()
52+
53+
if args.debug > 2:
54+
pprint(data)
55+
56+
return feat
57+
58+
def run_rest(self, args):
59+
"""
60+
Generate tables in ReST format. Three types of tables are
61+
supported, depending on the calling arguments:
62+
63+
- neither feature nor arch is passed: generates a full matrix;
64+
- arch provided: generates a table of supported tables for the
65+
guiven architecture, eventually filtered by feature;
66+
- only feature provided: generates a table with feature details,
67+
showing what architectures it is implemented.
68+
"""
69+
70+
feat = self.run_parser(args)
71+
72+
if args.arch:
73+
rst = feat.output_arch_table(args.arch, args.feat)
74+
elif args.feat:
75+
rst = feat.output_feature(args.feat)
76+
else:
77+
rst = feat.output_matrix()
78+
79+
print(rst)
80+
81+
def run_current(self, args):
82+
"""
83+
Instead of using a --arch parameter, get feature for the current
84+
architecture.
85+
"""
86+
87+
args.arch = self.get_current_arch()
88+
89+
self.run_rest(args)
90+
91+
def run_list(self, args):
92+
"""
93+
Generate a list of features for a given architecture, in a format
94+
parseable by other scripts. The output format is not ReST.
95+
"""
96+
97+
if not args.arch:
98+
args.arch = self.get_current_arch()
99+
100+
feat = self.run_parser(args)
101+
msg = feat.list_arch_features(args.arch, args.feat)
102+
103+
print(msg)
104+
105+
def parse_arch(self, parser):
106+
"""Add a --arch parsing argument"""
107+
108+
parser.add_argument("--arch",
109+
help="Output features for an specific"
110+
" architecture, optionally filtering for a "
111+
"single specific feature.")
112+
113+
def parse_feat(self, parser):
114+
"""Add a --feat parsing argument"""
115+
116+
parser.add_argument("--feat", "--feature",
117+
help="Output features for a single specific "
118+
"feature.")
119+
120+
121+
def current_args(self, subparsers):
122+
"""Implementscurrent argparse subparser"""
123+
124+
parser = subparsers.add_parser("current",
125+
formatter_class=argparse.RawTextHelpFormatter,
126+
description="Output table in ReST "
127+
"compatible ASCII format "
128+
"with features for this "
129+
"machine's architecture")
130+
131+
self.parse_feat(parser)
132+
parser.set_defaults(func=self.run_current)
133+
134+
def rest_args(self, subparsers):
135+
"""Implement rest argparse subparser"""
136+
137+
parser = subparsers.add_parser("rest",
138+
formatter_class=argparse.RawTextHelpFormatter,
139+
description="Output table(s) in ReST "
140+
"compatible ASCII format "
141+
"with features in ReST "
142+
"markup language. The "
143+
"output is affected by "
144+
"--arch or --feat/--feature"
145+
" flags.")
146+
147+
self.parse_arch(parser)
148+
self.parse_feat(parser)
149+
parser.set_defaults(func=self.run_rest)
150+
151+
def list_args(self, subparsers):
152+
"""Implement list argparse subparser"""
153+
154+
parser = subparsers.add_parser("list",
155+
formatter_class=argparse.RawTextHelpFormatter,
156+
description="List features for this "
157+
"machine's architecture, "
158+
"using an easier to parse "
159+
"format. The output is "
160+
"affected by --arch flag.")
161+
162+
self.parse_arch(parser)
163+
self.parse_feat(parser)
164+
parser.set_defaults(func=self.run_list)
165+
166+
def validate_args(self, subparsers):
167+
"""Implement validate argparse subparser"""
168+
169+
parser = subparsers.add_parser("validate",
170+
formatter_class=argparse.RawTextHelpFormatter,
171+
description="Validate the contents of "
172+
"the files under "
173+
f"{DEFAULT_DIR}.")
174+
175+
parser.set_defaults(func=self.run_parser)
176+
177+
def parser(self):
178+
"""
179+
Create an arparse with common options and several subparsers
180+
"""
181+
parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter)
182+
183+
parser.add_argument("-d", "--debug", action="count", default=0,
184+
help="Put the script in verbose mode, useful for "
185+
"debugging. Can be called multiple times, to "
186+
"increase verbosity.")
187+
188+
parser.add_argument("--directory", "--dir", default=DEFAULT_DIR,
189+
help="Changes the location of the Feature files. "
190+
f"By default, it uses the {DEFAULT_DIR} "
191+
"directory.")
192+
193+
parser.add_argument("--enable-fname", action="store_true",
194+
help="Prints the file name of the feature files. "
195+
"This can be used in order to track "
196+
"dependencies during documentation build.")
197+
198+
subparsers = parser.add_subparsers()
199+
200+
self.current_args(subparsers)
201+
self.rest_args(subparsers)
202+
self.list_args(subparsers)
203+
self.validate_args(subparsers)
204+
205+
args = parser.parse_args()
206+
207+
return args
208+
209+
210+
def main():
211+
"""Main program"""
212+
213+
feat = GetFeature()
214+
215+
args = feat.parser()
216+
217+
if "func" in args:
218+
args.func(args)
219+
else:
220+
sys.exit(f"Please specify a valid command for {sys.argv[0]}")
221+
222+
223+
# Call main method
224+
if __name__ == "__main__":
225+
main()

0 commit comments

Comments
 (0)