Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

scripts/kernel-doc.py: move KernelFiles class to a separate file

The KernelFiles class is the main dispatcher which parses each
source file.

In preparation for letting kerneldoc Sphinx extension to import
Python libraries, move regex ancillary classes to a separate
file.

Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
Signed-off-by: Jonathan Corbet <corbet@lwn.net>
Link: https://lore.kernel.org/r/80bc855e128a9ff0a11df5afe9ba71775dfc9a0f.1744106241.git.mchehab+huawei@kernel.org

authored by

Mauro Carvalho Chehab and committed by
Jonathan Corbet
ee13b3f3 d966dc65

+271 -219
+1 -219
scripts/kernel-doc.py
··· 119 119 120 120 from kdoc_parser import KernelDoc, type_param 121 121 from kdoc_re import Re 122 + from kdoc_files import KernelFiles 122 123 123 124 function_pointer = Re(r"([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)", cache=False) 124 125 ··· 143 142 type_member = Re(r"\&([_\w]+)(\.|->)([_\w]+)", cache=False) 144 143 type_fallback = Re(r"\&([_\w]+)", cache=False) 145 144 type_member_func = type_member + Re(r"\(\)", cache=False) 146 - 147 - class GlobSourceFiles: 148 - """ 149 - Parse C source code file names and directories via an Interactor. 150 - 151 - """ 152 - 153 - def __init__(self, srctree=None, valid_extensions=None): 154 - """ 155 - Initialize valid extensions with a tuple. 156 - 157 - If not defined, assume default C extensions (.c and .h) 158 - 159 - It would be possible to use python's glob function, but it is 160 - very slow, and it is not interactive. So, it would wait to read all 161 - directories before actually do something. 162 - 163 - So, let's use our own implementation. 164 - """ 165 - 166 - if not valid_extensions: 167 - self.extensions = (".c", ".h") 168 - else: 169 - self.extensions = valid_extensions 170 - 171 - self.srctree = srctree 172 - 173 - def _parse_dir(self, dirname): 174 - """Internal function to parse files recursively""" 175 - 176 - with os.scandir(dirname) as obj: 177 - for entry in obj: 178 - name = os.path.join(dirname, entry.name) 179 - 180 - if entry.is_dir(): 181 - yield from self._parse_dir(name) 182 - 183 - if not entry.is_file(): 184 - continue 185 - 186 - basename = os.path.basename(name) 187 - 188 - if not basename.endswith(self.extensions): 189 - continue 190 - 191 - yield name 192 - 193 - def parse_files(self, file_list, file_not_found_cb): 194 - for fname in file_list: 195 - if self.srctree: 196 - f = os.path.join(self.srctree, fname) 197 - else: 198 - f = fname 199 - 200 - if os.path.isdir(f): 201 - yield from self._parse_dir(f) 202 - elif os.path.isfile(f): 203 - yield f 204 - elif file_not_found_cb: 205 - file_not_found_cb(fname) 206 - 207 - 208 - class KernelFiles(): 209 - 210 - def parse_file(self, fname): 211 - 212 - doc = KernelDoc(self.config, fname) 213 - doc.run() 214 - 215 - return doc 216 - 217 - def process_export_file(self, fname): 218 - try: 219 - with open(fname, "r", encoding="utf8", 220 - errors="backslashreplace") as fp: 221 - for line in fp: 222 - KernelDoc.process_export(self.config.function_table, line) 223 - 224 - except IOError: 225 - print(f"Error: Cannot open fname {fname}", fname=sys.stderr) 226 - self.config.errors += 1 227 - 228 - def file_not_found_cb(self, fname): 229 - self.config.log.error("Cannot find file %s", fname) 230 - self.config.errors += 1 231 - 232 - def __init__(self, files=None, verbose=False, out_style=None, 233 - werror=False, wreturn=False, wshort_desc=False, 234 - wcontents_before_sections=False, 235 - logger=None, modulename=None, export_file=None): 236 - """Initialize startup variables and parse all files""" 237 - 238 - 239 - if not verbose: 240 - verbose = bool(os.environ.get("KBUILD_VERBOSE", 0)) 241 - 242 - if not modulename: 243 - modulename = "Kernel API" 244 - 245 - dt = datetime.now() 246 - if os.environ.get("KBUILD_BUILD_TIMESTAMP", None): 247 - # use UTC TZ 248 - to_zone = tz.gettz('UTC') 249 - dt = dt.astimezone(to_zone) 250 - 251 - if not werror: 252 - kcflags = os.environ.get("KCFLAGS", None) 253 - if kcflags: 254 - match = re.search(r"(\s|^)-Werror(\s|$)/", kcflags) 255 - if match: 256 - werror = True 257 - 258 - # reading this variable is for backwards compat just in case 259 - # someone was calling it with the variable from outside the 260 - # kernel's build system 261 - kdoc_werror = os.environ.get("KDOC_WERROR", None) 262 - if kdoc_werror: 263 - werror = kdoc_werror 264 - 265 - # Set global config data used on all files 266 - self.config = argparse.Namespace 267 - 268 - self.config.verbose = verbose 269 - self.config.werror = werror 270 - self.config.wreturn = wreturn 271 - self.config.wshort_desc = wshort_desc 272 - self.config.wcontents_before_sections = wcontents_before_sections 273 - self.config.modulename = modulename 274 - 275 - self.config.function_table = set() 276 - self.config.source_map = {} 277 - 278 - if not logger: 279 - self.config.log = logging.getLogger("kernel-doc") 280 - else: 281 - self.config.log = logger 282 - 283 - self.config.kernel_version = os.environ.get("KERNELVERSION", 284 - "unknown kernel version'") 285 - self.config.src_tree = os.environ.get("SRCTREE", None) 286 - 287 - self.out_style = out_style 288 - self.export_file = export_file 289 - 290 - # Initialize internal variables 291 - 292 - self.config.errors = 0 293 - self.results = [] 294 - 295 - self.file_list = files 296 - self.files = set() 297 - 298 - def parse(self): 299 - """ 300 - Parse all files 301 - """ 302 - 303 - glob = GlobSourceFiles(srctree=self.config.src_tree) 304 - 305 - # Let's use a set here to avoid duplicating files 306 - 307 - for fname in glob.parse_files(self.file_list, self.file_not_found_cb): 308 - if fname in self.files: 309 - continue 310 - 311 - self.files.add(fname) 312 - 313 - res = self.parse_file(fname) 314 - self.results.append((res.fname, res.entries)) 315 - 316 - if not self.files: 317 - sys.exit(1) 318 - 319 - # If a list of export files was provided, parse EXPORT_SYMBOL* 320 - # from the ones not already parsed 321 - 322 - if self.export_file: 323 - files = self.files 324 - 325 - glob = GlobSourceFiles(srctree=self.config.src_tree) 326 - 327 - for fname in glob.parse_files(self.export_file, 328 - self.file_not_found_cb): 329 - if fname not in files: 330 - files.add(fname) 331 - 332 - self.process_export_file(fname) 333 - 334 - def out_msg(self, fname, name, arg): 335 - # TODO: filter out unwanted parts 336 - 337 - return self.out_style.msg(fname, name, arg) 338 - 339 - def msg(self, enable_lineno=False, export=False, internal=False, 340 - symbol=None, nosymbol=None): 341 - 342 - function_table = self.config.function_table 343 - 344 - if symbol: 345 - for s in symbol: 346 - function_table.add(s) 347 - 348 - # Output none mode: only warnings will be shown 349 - if not self.out_style: 350 - return 351 - 352 - self.out_style.set_config(self.config) 353 - 354 - self.out_style.set_filter(export, internal, symbol, nosymbol, 355 - function_table, enable_lineno) 356 - 357 - for fname, arg_tuple in self.results: 358 - for name, arg in arg_tuple: 359 - if self.out_msg(fname, name, arg): 360 - ln = arg.get("ln", 0) 361 - dtype = arg.get('type', "") 362 - 363 - self.config.log.warning("%s:%d Can't handle %s", 364 - fname, ln, dtype) 365 145 366 146 367 147 class OutputFormat:
+270
scripts/lib/kdoc/kdoc_files.py
··· 1 + #!/usr/bin/env python3 2 + # SPDX-License-Identifier: GPL-2.0 3 + # Copyright(c) 2025: Mauro Carvalho Chehab <mchehab@kernel.org>. 4 + # 5 + # pylint: disable=R0903,R0913,R0914,R0917 6 + 7 + # TODO: implement warning filtering 8 + 9 + """ 10 + Parse lernel-doc tags on multiple kernel source files. 11 + """ 12 + 13 + import argparse 14 + import logging 15 + import os 16 + import re 17 + import sys 18 + from datetime import datetime 19 + 20 + from dateutil import tz 21 + 22 + from kdoc_parser import KernelDoc 23 + 24 + 25 + class GlobSourceFiles: 26 + """ 27 + Parse C source code file names and directories via an Interactor. 28 + """ 29 + 30 + def __init__(self, srctree=None, valid_extensions=None): 31 + """ 32 + Initialize valid extensions with a tuple. 33 + 34 + If not defined, assume default C extensions (.c and .h) 35 + 36 + It would be possible to use python's glob function, but it is 37 + very slow, and it is not interactive. So, it would wait to read all 38 + directories before actually do something. 39 + 40 + So, let's use our own implementation. 41 + """ 42 + 43 + if not valid_extensions: 44 + self.extensions = (".c", ".h") 45 + else: 46 + self.extensions = valid_extensions 47 + 48 + self.srctree = srctree 49 + 50 + def _parse_dir(self, dirname): 51 + """Internal function to parse files recursively""" 52 + 53 + with os.scandir(dirname) as obj: 54 + for entry in obj: 55 + name = os.path.join(dirname, entry.name) 56 + 57 + if entry.is_dir(): 58 + yield from self._parse_dir(name) 59 + 60 + if not entry.is_file(): 61 + continue 62 + 63 + basename = os.path.basename(name) 64 + 65 + if not basename.endswith(self.extensions): 66 + continue 67 + 68 + yield name 69 + 70 + def parse_files(self, file_list, file_not_found_cb): 71 + """ 72 + Define an interator to parse all source files from file_list, 73 + handling directories if any 74 + """ 75 + 76 + for fname in file_list: 77 + if self.srctree: 78 + f = os.path.join(self.srctree, fname) 79 + else: 80 + f = fname 81 + 82 + if os.path.isdir(f): 83 + yield from self._parse_dir(f) 84 + elif os.path.isfile(f): 85 + yield f 86 + elif file_not_found_cb: 87 + file_not_found_cb(fname) 88 + 89 + 90 + class KernelFiles(): 91 + """ 92 + Parse lernel-doc tags on multiple kernel source files. 93 + """ 94 + 95 + def parse_file(self, fname): 96 + """ 97 + Parse a single Kernel source. 98 + """ 99 + 100 + doc = KernelDoc(self.config, fname) 101 + doc.run() 102 + 103 + return doc 104 + 105 + def process_export_file(self, fname): 106 + """ 107 + Parses EXPORT_SYMBOL* macros from a single Kernel source file. 108 + """ 109 + try: 110 + with open(fname, "r", encoding="utf8", 111 + errors="backslashreplace") as fp: 112 + for line in fp: 113 + KernelDoc.process_export(self.config.function_table, line) 114 + 115 + except IOError: 116 + print(f"Error: Cannot open fname {fname}", fname=sys.stderr) 117 + self.config.errors += 1 118 + 119 + def file_not_found_cb(self, fname): 120 + """ 121 + Callback to warn if a file was not found. 122 + """ 123 + 124 + self.config.log.error("Cannot find file %s", fname) 125 + self.config.errors += 1 126 + 127 + def __init__(self, files=None, verbose=False, out_style=None, 128 + werror=False, wreturn=False, wshort_desc=False, 129 + wcontents_before_sections=False, 130 + logger=None, modulename=None, export_file=None): 131 + """ 132 + Initialize startup variables and parse all files 133 + """ 134 + 135 + if not verbose: 136 + verbose = bool(os.environ.get("KBUILD_VERBOSE", 0)) 137 + 138 + if not modulename: 139 + modulename = "Kernel API" 140 + 141 + dt = datetime.now() 142 + if os.environ.get("KBUILD_BUILD_TIMESTAMP", None): 143 + # use UTC TZ 144 + to_zone = tz.gettz('UTC') 145 + dt = dt.astimezone(to_zone) 146 + 147 + if not werror: 148 + kcflags = os.environ.get("KCFLAGS", None) 149 + if kcflags: 150 + match = re.search(r"(\s|^)-Werror(\s|$)/", kcflags) 151 + if match: 152 + werror = True 153 + 154 + # reading this variable is for backwards compat just in case 155 + # someone was calling it with the variable from outside the 156 + # kernel's build system 157 + kdoc_werror = os.environ.get("KDOC_WERROR", None) 158 + if kdoc_werror: 159 + werror = kdoc_werror 160 + 161 + # Set global config data used on all files 162 + self.config = argparse.Namespace 163 + 164 + self.config.verbose = verbose 165 + self.config.werror = werror 166 + self.config.wreturn = wreturn 167 + self.config.wshort_desc = wshort_desc 168 + self.config.wcontents_before_sections = wcontents_before_sections 169 + self.config.modulename = modulename 170 + 171 + self.config.function_table = set() 172 + self.config.source_map = {} 173 + 174 + if not logger: 175 + self.config.log = logging.getLogger("kernel-doc") 176 + else: 177 + self.config.log = logger 178 + 179 + self.config.kernel_version = os.environ.get("KERNELVERSION", 180 + "unknown kernel version'") 181 + self.config.src_tree = os.environ.get("SRCTREE", None) 182 + 183 + self.out_style = out_style 184 + self.export_file = export_file 185 + 186 + # Initialize internal variables 187 + 188 + self.config.errors = 0 189 + self.results = [] 190 + 191 + self.file_list = files 192 + self.files = set() 193 + 194 + def parse(self): 195 + """ 196 + Parse all files 197 + """ 198 + 199 + glob = GlobSourceFiles(srctree=self.config.src_tree) 200 + 201 + # Let's use a set here to avoid duplicating files 202 + 203 + for fname in glob.parse_files(self.file_list, self.file_not_found_cb): 204 + if fname in self.files: 205 + continue 206 + 207 + self.files.add(fname) 208 + 209 + res = self.parse_file(fname) 210 + self.results.append((res.fname, res.entries)) 211 + 212 + if not self.files: 213 + sys.exit(1) 214 + 215 + # If a list of export files was provided, parse EXPORT_SYMBOL* 216 + # from the ones not already parsed 217 + 218 + if self.export_file: 219 + files = self.files 220 + 221 + glob = GlobSourceFiles(srctree=self.config.src_tree) 222 + 223 + for fname in glob.parse_files(self.export_file, 224 + self.file_not_found_cb): 225 + if fname not in files: 226 + files.add(fname) 227 + 228 + self.process_export_file(fname) 229 + 230 + def out_msg(self, fname, name, arg): 231 + """ 232 + Output messages from a file name using the output style filtering. 233 + 234 + If output type was not handled by the syler, return False. 235 + """ 236 + 237 + # NOTE: we can add rules here to filter out unwanted parts, 238 + # although OutputFormat.msg already does that. 239 + 240 + return self.out_style.msg(fname, name, arg) 241 + 242 + def msg(self, enable_lineno=False, export=False, internal=False, 243 + symbol=None, nosymbol=None): 244 + """ 245 + Interacts over the kernel-doc results and output messages. 246 + """ 247 + 248 + function_table = self.config.function_table 249 + 250 + if symbol: 251 + for s in symbol: 252 + function_table.add(s) 253 + 254 + # Output none mode: only warnings will be shown 255 + if not self.out_style: 256 + return 257 + 258 + self.out_style.set_config(self.config) 259 + 260 + self.out_style.set_filter(export, internal, symbol, nosymbol, 261 + function_table, enable_lineno) 262 + 263 + for fname, arg_tuple in self.results: 264 + for name, arg in arg_tuple: 265 + if self.out_msg(fname, name, arg): 266 + ln = arg.get("ln", 0) 267 + dtype = arg.get('type', "") 268 + 269 + self.config.log.warning("%s:%d Can't handle %s", 270 + fname, ln, dtype)