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""" 8Parse the Linux Feature files and produce a ReST book. 9""" 10 11import argparse 12import os 13import subprocess 14import sys 15 16from pprint import pprint 17 18LIB_DIR = "../../tools/lib/python" 19SRC_DIR = os.path.dirname(os.path.realpath(__file__)) 20 21sys.path.insert(0, os.path.join(SRC_DIR, LIB_DIR)) 22 23from feat.parse_features import ParseFeature # pylint: disable=C0413 24 25SRCTREE = os.path.join(os.path.dirname(os.path.realpath(__file__)), "../..") 26DEFAULT_DIR = "Documentation/features" 27 28 29class 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 210def 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 224if __name__ == "__main__": 225 main() 226