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=R0902,R0903,R0904,R0911,R0912,R0913,R0914,R0915,R0917,R1702 6# pylint: disable=C0302,C0103,C0301 7# pylint: disable=C0116,C0115,W0511,W0613 8# 9# Converted from the kernel-doc script originally written in Perl 10# under GPLv2, copyrighted since 1998 by the following authors: 11# 12# Aditya Srivastava <yashsri421@gmail.com> 13# Akira Yokosawa <akiyks@gmail.com> 14# Alexander A. Klimov <grandmaster@al2klimov.de> 15# Alexander Lobakin <aleksander.lobakin@intel.com> 16# André Almeida <andrealmeid@igalia.com> 17# Andy Shevchenko <andriy.shevchenko@linux.intel.com> 18# Anna-Maria Behnsen <anna-maria@linutronix.de> 19# Armin Kuster <akuster@mvista.com> 20# Bart Van Assche <bart.vanassche@sandisk.com> 21# Ben Hutchings <ben@decadent.org.uk> 22# Borislav Petkov <bbpetkov@yahoo.de> 23# Chen-Yu Tsai <wenst@chromium.org> 24# Coco Li <lixiaoyan@google.com> 25# Conchúr Navid <conchur@web.de> 26# Daniel Santos <daniel.santos@pobox.com> 27# Danilo Cesar Lemes de Paula <danilo.cesar@collabora.co.uk> 28# Dan Luedtke <mail@danrl.de> 29# Donald Hunter <donald.hunter@gmail.com> 30# Gabriel Krisman Bertazi <krisman@collabora.co.uk> 31# Greg Kroah-Hartman <gregkh@linuxfoundation.org> 32# Harvey Harrison <harvey.harrison@gmail.com> 33# Horia Geanta <horia.geanta@freescale.com> 34# Ilya Dryomov <idryomov@gmail.com> 35# Jakub Kicinski <kuba@kernel.org> 36# Jani Nikula <jani.nikula@intel.com> 37# Jason Baron <jbaron@redhat.com> 38# Jason Gunthorpe <jgg@nvidia.com> 39# Jérémy Bobbio <lunar@debian.org> 40# Johannes Berg <johannes.berg@intel.com> 41# Johannes Weiner <hannes@cmpxchg.org> 42# Jonathan Cameron <Jonathan.Cameron@huawei.com> 43# Jonathan Corbet <corbet@lwn.net> 44# Jonathan Neuschäfer <j.neuschaefer@gmx.net> 45# Kamil Rytarowski <n54@gmx.com> 46# Kees Cook <kees@kernel.org> 47# Laurent Pinchart <laurent.pinchart@ideasonboard.com> 48# Levin, Alexander (Sasha Levin) <alexander.levin@verizon.com> 49# Linus Torvalds <torvalds@linux-foundation.org> 50# Lucas De Marchi <lucas.demarchi@profusion.mobi> 51# Mark Rutland <mark.rutland@arm.com> 52# Markus Heiser <markus.heiser@darmarit.de> 53# Martin Waitz <tali@admingilde.org> 54# Masahiro Yamada <masahiroy@kernel.org> 55# Matthew Wilcox <willy@infradead.org> 56# Mauro Carvalho Chehab <mchehab+huawei@kernel.org> 57# Michal Wajdeczko <michal.wajdeczko@intel.com> 58# Michael Zucchi 59# Mike Rapoport <rppt@linux.ibm.com> 60# Niklas Söderlund <niklas.soderlund@corigine.com> 61# Nishanth Menon <nm@ti.com> 62# Paolo Bonzini <pbonzini@redhat.com> 63# Pavan Kumar Linga <pavan.kumar.linga@intel.com> 64# Pavel Pisa <pisa@cmp.felk.cvut.cz> 65# Peter Maydell <peter.maydell@linaro.org> 66# Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com> 67# Randy Dunlap <rdunlap@infradead.org> 68# Richard Kennedy <richard@rsk.demon.co.uk> 69# Rich Walker <rw@shadow.org.uk> 70# Rolf Eike Beer <eike-kernel@sf-tec.de> 71# Sakari Ailus <sakari.ailus@linux.intel.com> 72# Silvio Fricke <silvio.fricke@gmail.com> 73# Simon Huggins 74# Tim Waugh <twaugh@redhat.com> 75# Tomasz Warniełło <tomasz.warniello@gmail.com> 76# Utkarsh Tripathi <utripathi2002@gmail.com> 77# valdis.kletnieks@vt.edu <valdis.kletnieks@vt.edu> 78# Vegard Nossum <vegard.nossum@oracle.com> 79# Will Deacon <will.deacon@arm.com> 80# Yacine Belkadi <yacine.belkadi.1@gmail.com> 81# Yujie Liu <yujie.liu@intel.com> 82 83# TODO: implement warning filtering 84 85""" 86kernel_doc 87========== 88 89Print formatted kernel documentation to stdout 90 91Read C language source or header FILEs, extract embedded 92documentation comments, and print formatted documentation 93to standard output. 94 95The documentation comments are identified by the "/**" 96opening comment mark. 97 98See Documentation/doc-guide/kernel-doc.rst for the 99documentation comment syntax. 100""" 101 102import argparse 103import logging 104import os 105import re 106import sys 107 108from datetime import datetime 109from pprint import pformat 110 111from dateutil import tz 112 113# Import Python modules 114 115LIB_DIR = "lib/kdoc" 116SRC_DIR = os.path.dirname(os.path.realpath(__file__)) 117 118sys.path.insert(0, os.path.join(SRC_DIR, LIB_DIR)) 119 120from kdoc_re import Re, NestedMatch 121 122 123# 124# Regular expressions used to parse kernel-doc markups at KernelDoc class. 125# 126# Let's declare them in lowercase outside any class to make easier to 127# convert from the python script. 128# 129# As those are evaluated at the beginning, no need to cache them 130# 131 132 133# Allow whitespace at end of comment start. 134doc_start = Re(r'^/\*\*\s*$', cache=False) 135 136doc_end = Re(r'\*/', cache=False) 137doc_com = Re(r'\s*\*\s*', cache=False) 138doc_com_body = Re(r'\s*\* ?', cache=False) 139doc_decl = doc_com + Re(r'(\w+)', cache=False) 140 141# @params and a strictly limited set of supported section names 142# Specifically: 143# Match @word: 144# @...: 145# @{section-name}: 146# while trying to not match literal block starts like "example::" 147# 148doc_sect = doc_com + \ 149 Re(r'\s*(\@[.\w]+|\@\.\.\.|description|context|returns?|notes?|examples?)\s*:([^:].*)?$', 150 flags=re.I, cache=False) 151 152doc_content = doc_com_body + Re(r'(.*)', cache=False) 153doc_block = doc_com + Re(r'DOC:\s*(.*)?', cache=False) 154doc_inline_start = Re(r'^\s*/\*\*\s*$', cache=False) 155doc_inline_sect = Re(r'\s*\*\s*(@\s*[\w][\w\.]*\s*):(.*)', cache=False) 156doc_inline_end = Re(r'^\s*\*/\s*$', cache=False) 157doc_inline_oneline = Re(r'^\s*/\*\*\s*(@[\w\s]+):\s*(.*)\s*\*/\s*$', cache=False) 158function_pointer = Re(r"([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)", cache=False) 159attribute = Re(r"__attribute__\s*\(\([a-z0-9,_\*\s\(\)]*\)\)", 160 flags=re.I | re.S, cache=False) 161 162# match expressions used to find embedded type information 163type_constant = Re(r"\b``([^\`]+)``\b", cache=False) 164type_constant2 = Re(r"\%([-_*\w]+)", cache=False) 165type_func = Re(r"(\w+)\(\)", cache=False) 166type_param = Re(r"\@(\w*((\.\w+)|(->\w+))*(\.\.\.)?)", cache=False) 167type_param_ref = Re(r"([\!~\*]?)\@(\w*((\.\w+)|(->\w+))*(\.\.\.)?)", cache=False) 168 169# Special RST handling for func ptr params 170type_fp_param = Re(r"\@(\w+)\(\)", cache=False) 171 172# Special RST handling for structs with func ptr params 173type_fp_param2 = Re(r"\@(\w+->\S+)\(\)", cache=False) 174 175type_env = Re(r"(\$\w+)", cache=False) 176type_enum = Re(r"\&(enum\s*([_\w]+))", cache=False) 177type_struct = Re(r"\&(struct\s*([_\w]+))", cache=False) 178type_typedef = Re(r"\&(typedef\s*([_\w]+))", cache=False) 179type_union = Re(r"\&(union\s*([_\w]+))", cache=False) 180type_member = Re(r"\&([_\w]+)(\.|->)([_\w]+)", cache=False) 181type_fallback = Re(r"\&([_\w]+)", cache=False) 182type_member_func = type_member + Re(r"\(\)", cache=False) 183 184export_symbol = Re(r'^\s*EXPORT_SYMBOL(_GPL)?\s*\(\s*(\w+)\s*\)\s*', cache=False) 185export_symbol_ns = Re(r'^\s*EXPORT_SYMBOL_NS(_GPL)?\s*\(\s*(\w+)\s*,\s*"\S+"\)\s*', cache=False) 186 187class KernelDoc: 188 # Parser states 189 STATE_NORMAL = 0 # normal code 190 STATE_NAME = 1 # looking for function name 191 STATE_BODY_MAYBE = 2 # body - or maybe more description 192 STATE_BODY = 3 # the body of the comment 193 STATE_BODY_WITH_BLANK_LINE = 4 # the body which has a blank line 194 STATE_PROTO = 5 # scanning prototype 195 STATE_DOCBLOCK = 6 # documentation block 196 STATE_INLINE = 7 # gathering doc outside main block 197 198 st_name = [ 199 "NORMAL", 200 "NAME", 201 "BODY_MAYBE", 202 "BODY", 203 "BODY_WITH_BLANK_LINE", 204 "PROTO", 205 "DOCBLOCK", 206 "INLINE", 207 ] 208 209 # Inline documentation state 210 STATE_INLINE_NA = 0 # not applicable ($state != STATE_INLINE) 211 STATE_INLINE_NAME = 1 # looking for member name (@foo:) 212 STATE_INLINE_TEXT = 2 # looking for member documentation 213 STATE_INLINE_END = 3 # done 214 STATE_INLINE_ERROR = 4 # error - Comment without header was found. 215 # Spit a warning as it's not 216 # proper kernel-doc and ignore the rest. 217 218 st_inline_name = [ 219 "", 220 "_NAME", 221 "_TEXT", 222 "_END", 223 "_ERROR", 224 ] 225 226 # Section names 227 228 section_default = "Description" # default section 229 section_intro = "Introduction" 230 section_context = "Context" 231 section_return = "Return" 232 233 undescribed = "-- undescribed --" 234 235 def __init__(self, config, fname): 236 """Initialize internal variables""" 237 238 self.fname = fname 239 self.config = config 240 241 # Initial state for the state machines 242 self.state = self.STATE_NORMAL 243 self.inline_doc_state = self.STATE_INLINE_NA 244 245 # Store entry currently being processed 246 self.entry = None 247 248 # Place all potential outputs into an array 249 self.entries = [] 250 251 def show_warnings(self, dtype, declaration_name): 252 # TODO: implement it 253 254 return True 255 256 # TODO: rename to emit_message 257 def emit_warning(self, ln, msg, warning=True): 258 """Emit a message""" 259 260 if warning: 261 self.config.log.warning("%s:%d %s", self.fname, ln, msg) 262 else: 263 self.config.log.info("%s:%d %s", self.fname, ln, msg) 264 265 def dump_section(self, start_new=True): 266 """ 267 Dumps section contents to arrays/hashes intended for that purpose. 268 """ 269 270 name = self.entry.section 271 contents = self.entry.contents 272 273 # TODO: we can prevent dumping empty sections here with: 274 # 275 # if self.entry.contents.strip("\n"): 276 # if start_new: 277 # self.entry.section = self.section_default 278 # self.entry.contents = "" 279 # 280 # return 281 # 282 # But, as we want to be producing the same output of the 283 # venerable kernel-doc Perl tool, let's just output everything, 284 # at least for now 285 286 if type_param.match(name): 287 name = type_param.group(1) 288 289 self.entry.parameterdescs[name] = contents 290 self.entry.parameterdesc_start_lines[name] = self.entry.new_start_line 291 292 self.entry.sectcheck += name + " " 293 self.entry.new_start_line = 0 294 295 elif name == "@...": 296 name = "..." 297 self.entry.parameterdescs[name] = contents 298 self.entry.sectcheck += name + " " 299 self.entry.parameterdesc_start_lines[name] = self.entry.new_start_line 300 self.entry.new_start_line = 0 301 302 else: 303 if name in self.entry.sections and self.entry.sections[name] != "": 304 # Only warn on user-specified duplicate section names 305 if name != self.section_default: 306 self.emit_warning(self.entry.new_start_line, 307 f"duplicate section name '{name}'\n") 308 self.entry.sections[name] += contents 309 else: 310 self.entry.sections[name] = contents 311 self.entry.sectionlist.append(name) 312 self.entry.section_start_lines[name] = self.entry.new_start_line 313 self.entry.new_start_line = 0 314 315# self.config.log.debug("Section: %s : %s", name, pformat(vars(self.entry))) 316 317 if start_new: 318 self.entry.section = self.section_default 319 self.entry.contents = "" 320 321 # TODO: rename it to store_declaration 322 def output_declaration(self, dtype, name, **args): 323 """ 324 Stores the entry into an entry array. 325 326 The actual output and output filters will be handled elsewhere 327 """ 328 329 # The implementation here is different than the original kernel-doc: 330 # instead of checking for output filters or actually output anything, 331 # it just stores the declaration content at self.entries, as the 332 # output will happen on a separate class. 333 # 334 # For now, we're keeping the same name of the function just to make 335 # easier to compare the source code of both scripts 336 337 if "declaration_start_line" not in args: 338 args["declaration_start_line"] = self.entry.declaration_start_line 339 340 args["type"] = dtype 341 342 # TODO: use colletions.OrderedDict 343 344 sections = args.get('sections', {}) 345 sectionlist = args.get('sectionlist', []) 346 347 # Drop empty sections 348 # TODO: improve it to emit warnings 349 for section in [ "Description", "Return" ]: 350 if section in sectionlist: 351 if not sections[section].rstrip(): 352 del sections[section] 353 sectionlist.remove(section) 354 355 self.entries.append((name, args)) 356 357 self.config.log.debug("Output: %s:%s = %s", dtype, name, pformat(args)) 358 359 def reset_state(self, ln): 360 """ 361 Ancillary routine to create a new entry. It initializes all 362 variables used by the state machine. 363 """ 364 365 self.entry = argparse.Namespace 366 367 self.entry.contents = "" 368 self.entry.function = "" 369 self.entry.sectcheck = "" 370 self.entry.struct_actual = "" 371 self.entry.prototype = "" 372 373 self.entry.parameterlist = [] 374 self.entry.parameterdescs = {} 375 self.entry.parametertypes = {} 376 self.entry.parameterdesc_start_lines = {} 377 378 self.entry.section_start_lines = {} 379 self.entry.sectionlist = [] 380 self.entry.sections = {} 381 382 self.entry.anon_struct_union = False 383 384 self.entry.leading_space = None 385 386 # State flags 387 self.state = self.STATE_NORMAL 388 self.inline_doc_state = self.STATE_INLINE_NA 389 self.entry.brcount = 0 390 391 self.entry.in_doc_sect = False 392 self.entry.declaration_start_line = ln 393 394 def push_parameter(self, ln, decl_type, param, dtype, 395 org_arg, declaration_name): 396 if self.entry.anon_struct_union and dtype == "" and param == "}": 397 return # Ignore the ending }; from anonymous struct/union 398 399 self.entry.anon_struct_union = False 400 401 param = Re(r'[\[\)].*').sub('', param, count=1) 402 403 if dtype == "" and param.endswith("..."): 404 if Re(r'\w\.\.\.$').search(param): 405 # For named variable parameters of the form `x...`, 406 # remove the dots 407 param = param[:-3] 408 else: 409 # Handles unnamed variable parameters 410 param = "..." 411 412 if param not in self.entry.parameterdescs or \ 413 not self.entry.parameterdescs[param]: 414 415 self.entry.parameterdescs[param] = "variable arguments" 416 417 elif dtype == "" and (not param or param == "void"): 418 param = "void" 419 self.entry.parameterdescs[param] = "no arguments" 420 421 elif dtype == "" and param in ["struct", "union"]: 422 # Handle unnamed (anonymous) union or struct 423 dtype = param 424 param = "{unnamed_" + param + "}" 425 self.entry.parameterdescs[param] = "anonymous\n" 426 self.entry.anon_struct_union = True 427 428 # Handle cache group enforcing variables: they do not need 429 # to be described in header files 430 elif "__cacheline_group" in param: 431 # Ignore __cacheline_group_begin and __cacheline_group_end 432 return 433 434 # Warn if parameter has no description 435 # (but ignore ones starting with # as these are not parameters 436 # but inline preprocessor statements) 437 if param not in self.entry.parameterdescs and not param.startswith("#"): 438 self.entry.parameterdescs[param] = self.undescribed 439 440 if self.show_warnings(dtype, declaration_name) and "." not in param: 441 if decl_type == 'function': 442 dname = f"{decl_type} parameter" 443 else: 444 dname = f"{decl_type} member" 445 446 self.emit_warning(ln, 447 f"{dname} '{param}' not described in '{declaration_name}'") 448 449 # Strip spaces from param so that it is one continuous string on 450 # parameterlist. This fixes a problem where check_sections() 451 # cannot find a parameter like "addr[6 + 2]" because it actually 452 # appears as "addr[6", "+", "2]" on the parameter list. 453 # However, it's better to maintain the param string unchanged for 454 # output, so just weaken the string compare in check_sections() 455 # to ignore "[blah" in a parameter string. 456 457 self.entry.parameterlist.append(param) 458 org_arg = Re(r'\s\s+').sub(' ', org_arg) 459 self.entry.parametertypes[param] = org_arg 460 461 def save_struct_actual(self, actual): 462 """ 463 Strip all spaces from the actual param so that it looks like 464 one string item. 465 """ 466 467 actual = Re(r'\s*').sub("", actual, count=1) 468 469 self.entry.struct_actual += actual + " " 470 471 def create_parameter_list(self, ln, decl_type, args, splitter, declaration_name): 472 473 # temporarily replace all commas inside function pointer definition 474 arg_expr = Re(r'(\([^\),]+),') 475 while arg_expr.search(args): 476 args = arg_expr.sub(r"\1#", args) 477 478 for arg in args.split(splitter): 479 # Strip comments 480 arg = Re(r'\/\*.*\*\/').sub('', arg) 481 482 # Ignore argument attributes 483 arg = Re(r'\sPOS0?\s').sub(' ', arg) 484 485 # Strip leading/trailing spaces 486 arg = arg.strip() 487 arg = Re(r'\s+').sub(' ', arg, count=1) 488 489 if arg.startswith('#'): 490 # Treat preprocessor directive as a typeless variable just to fill 491 # corresponding data structures "correctly". Catch it later in 492 # output_* subs. 493 494 # Treat preprocessor directive as a typeless variable 495 self.push_parameter(ln, decl_type, arg, "", 496 "", declaration_name) 497 498 elif Re(r'\(.+\)\s*\(').search(arg): 499 # Pointer-to-function 500 501 arg = arg.replace('#', ',') 502 503 r = Re(r'[^\(]+\(\*?\s*([\w\[\]\.]*)\s*\)') 504 if r.match(arg): 505 param = r.group(1) 506 else: 507 self.emit_warning(ln, f"Invalid param: {arg}") 508 param = arg 509 510 dtype = Re(r'([^\(]+\(\*?)\s*' + re.escape(param)).sub(r'\1', arg) 511 self.save_struct_actual(param) 512 self.push_parameter(ln, decl_type, param, dtype, 513 arg, declaration_name) 514 515 elif Re(r'\(.+\)\s*\[').search(arg): 516 # Array-of-pointers 517 518 arg = arg.replace('#', ',') 519 r = Re(r'[^\(]+\(\s*\*\s*([\w\[\]\.]*?)\s*(\s*\[\s*[\w]+\s*\]\s*)*\)') 520 if r.match(arg): 521 param = r.group(1) 522 else: 523 self.emit_warning(ln, f"Invalid param: {arg}") 524 param = arg 525 526 dtype = Re(r'([^\(]+\(\*?)\s*' + re.escape(param)).sub(r'\1', arg) 527 528 self.save_struct_actual(param) 529 self.push_parameter(ln, decl_type, param, dtype, 530 arg, declaration_name) 531 532 elif arg: 533 arg = Re(r'\s*:\s*').sub(":", arg) 534 arg = Re(r'\s*\[').sub('[', arg) 535 536 args = Re(r'\s*,\s*').split(arg) 537 if args[0] and '*' in args[0]: 538 args[0] = re.sub(r'(\*+)\s*', r' \1', args[0]) 539 540 first_arg = [] 541 r = Re(r'^(.*\s+)(.*?\[.*\].*)$') 542 if args[0] and r.match(args[0]): 543 args.pop(0) 544 first_arg.extend(r.group(1)) 545 first_arg.append(r.group(2)) 546 else: 547 first_arg = Re(r'\s+').split(args.pop(0)) 548 549 args.insert(0, first_arg.pop()) 550 dtype = ' '.join(first_arg) 551 552 for param in args: 553 if Re(r'^(\*+)\s*(.*)').match(param): 554 r = Re(r'^(\*+)\s*(.*)') 555 if not r.match(param): 556 self.emit_warning(ln, f"Invalid param: {param}") 557 continue 558 559 param = r.group(1) 560 561 self.save_struct_actual(r.group(2)) 562 self.push_parameter(ln, decl_type, r.group(2), 563 f"{dtype} {r.group(1)}", 564 arg, declaration_name) 565 566 elif Re(r'(.*?):(\w+)').search(param): 567 r = Re(r'(.*?):(\w+)') 568 if not r.match(param): 569 self.emit_warning(ln, f"Invalid param: {param}") 570 continue 571 572 if dtype != "": # Skip unnamed bit-fields 573 self.save_struct_actual(r.group(1)) 574 self.push_parameter(ln, decl_type, r.group(1), 575 f"{dtype}:{r.group(2)}", 576 arg, declaration_name) 577 else: 578 self.save_struct_actual(param) 579 self.push_parameter(ln, decl_type, param, dtype, 580 arg, declaration_name) 581 582 def check_sections(self, ln, decl_name, decl_type, sectcheck, prmscheck): 583 sects = sectcheck.split() 584 prms = prmscheck.split() 585 err = False 586 587 for sx in range(len(sects)): # pylint: disable=C0200 588 err = True 589 for px in range(len(prms)): # pylint: disable=C0200 590 prm_clean = prms[px] 591 prm_clean = Re(r'\[.*\]').sub('', prm_clean) 592 prm_clean = attribute.sub('', prm_clean) 593 594 # ignore array size in a parameter string; 595 # however, the original param string may contain 596 # spaces, e.g.: addr[6 + 2] 597 # and this appears in @prms as "addr[6" since the 598 # parameter list is split at spaces; 599 # hence just ignore "[..." for the sections check; 600 prm_clean = Re(r'\[.*').sub('', prm_clean) 601 602 if prm_clean == sects[sx]: 603 err = False 604 break 605 606 if err: 607 if decl_type == 'function': 608 dname = f"{decl_type} parameter" 609 else: 610 dname = f"{decl_type} member" 611 612 self.emit_warning(ln, 613 f"Excess {dname} '{sects[sx]}' description in '{decl_name}'") 614 615 def check_return_section(self, ln, declaration_name, return_type): 616 617 if not self.config.wreturn: 618 return 619 620 # Ignore an empty return type (It's a macro) 621 # Ignore functions with a "void" return type (but not "void *") 622 if not return_type or Re(r'void\s*\w*\s*$').search(return_type): 623 return 624 625 if not self.entry.sections.get("Return", None): 626 self.emit_warning(ln, 627 f"No description found for return value of '{declaration_name}'") 628 629 def dump_struct(self, ln, proto): 630 """ 631 Store an entry for an struct or union 632 """ 633 634 type_pattern = r'(struct|union)' 635 636 qualifiers = [ 637 "__attribute__", 638 "__packed", 639 "__aligned", 640 "____cacheline_aligned_in_smp", 641 "____cacheline_aligned", 642 ] 643 644 definition_body = r'\{(.*)\}\s*' + "(?:" + '|'.join(qualifiers) + ")?" 645 struct_members = Re(type_pattern + r'([^\{\};]+)(\{)([^\{\}]*)(\})([^\{\}\;]*)(\;)') 646 647 # Extract struct/union definition 648 members = None 649 declaration_name = None 650 decl_type = None 651 652 r = Re(type_pattern + r'\s+(\w+)\s*' + definition_body) 653 if r.search(proto): 654 decl_type = r.group(1) 655 declaration_name = r.group(2) 656 members = r.group(3) 657 else: 658 r = Re(r'typedef\s+' + type_pattern + r'\s*' + definition_body + r'\s*(\w+)\s*;') 659 660 if r.search(proto): 661 decl_type = r.group(1) 662 declaration_name = r.group(3) 663 members = r.group(2) 664 665 if not members: 666 self.emit_warning(ln, f"{proto} error: Cannot parse struct or union!") 667 self.config.errors += 1 668 return 669 670 if self.entry.identifier != declaration_name: 671 self.emit_warning(ln, 672 f"expecting prototype for {decl_type} {self.entry.identifier}. Prototype was for {decl_type} {declaration_name} instead\n") 673 return 674 675 args_pattern =r'([^,)]+)' 676 677 sub_prefixes = [ 678 (Re(r'\/\*\s*private:.*?\/\*\s*public:.*?\*\/', re.S | re.I), ''), 679 (Re(r'\/\*\s*private:.*', re.S| re.I), ''), 680 681 # Strip comments 682 (Re(r'\/\*.*?\*\/', re.S), ''), 683 684 # Strip attributes 685 (attribute, ' '), 686 (Re(r'\s*__aligned\s*\([^;]*\)', re.S), ' '), 687 (Re(r'\s*__counted_by\s*\([^;]*\)', re.S), ' '), 688 (Re(r'\s*__counted_by_(le|be)\s*\([^;]*\)', re.S), ' '), 689 (Re(r'\s*__packed\s*', re.S), ' '), 690 (Re(r'\s*CRYPTO_MINALIGN_ATTR', re.S), ' '), 691 (Re(r'\s*____cacheline_aligned_in_smp', re.S), ' '), 692 (Re(r'\s*____cacheline_aligned', re.S), ' '), 693 694 # Unwrap struct_group macros based on this definition: 695 # __struct_group(TAG, NAME, ATTRS, MEMBERS...) 696 # which has variants like: struct_group(NAME, MEMBERS...) 697 # Only MEMBERS arguments require documentation. 698 # 699 # Parsing them happens on two steps: 700 # 701 # 1. drop struct group arguments that aren't at MEMBERS, 702 # storing them as STRUCT_GROUP(MEMBERS) 703 # 704 # 2. remove STRUCT_GROUP() ancillary macro. 705 # 706 # The original logic used to remove STRUCT_GROUP() using an 707 # advanced regex: 708 # 709 # \bSTRUCT_GROUP(\(((?:(?>[^)(]+)|(?1))*)\))[^;]*; 710 # 711 # with two patterns that are incompatible with 712 # Python re module, as it has: 713 # 714 # - a recursive pattern: (?1) 715 # - an atomic grouping: (?>...) 716 # 717 # I tried a simpler version: but it didn't work either: 718 # \bSTRUCT_GROUP\(([^\)]+)\)[^;]*; 719 # 720 # As it doesn't properly match the end parenthesis on some cases. 721 # 722 # So, a better solution was crafted: there's now a NestedMatch 723 # class that ensures that delimiters after a search are properly 724 # matched. So, the implementation to drop STRUCT_GROUP() will be 725 # handled in separate. 726 727 (Re(r'\bstruct_group\s*\(([^,]*,)', re.S), r'STRUCT_GROUP('), 728 (Re(r'\bstruct_group_attr\s*\(([^,]*,){2}', re.S), r'STRUCT_GROUP('), 729 (Re(r'\bstruct_group_tagged\s*\(([^,]*),([^,]*),', re.S), r'struct \1 \2; STRUCT_GROUP('), 730 (Re(r'\b__struct_group\s*\(([^,]*,){3}', re.S), r'STRUCT_GROUP('), 731 732 # Replace macros 733 # 734 # TODO: it is better to also move those to the NestedMatch logic, 735 # to ensure that parenthesis will be properly matched. 736 737 (Re(r'__ETHTOOL_DECLARE_LINK_MODE_MASK\s*\(([^\)]+)\)', re.S), r'DECLARE_BITMAP(\1, __ETHTOOL_LINK_MODE_MASK_NBITS)'), 738 (Re(r'DECLARE_PHY_INTERFACE_MASK\s*\(([^\)]+)\)', re.S), r'DECLARE_BITMAP(\1, PHY_INTERFACE_MODE_MAX)'), 739 (Re(r'DECLARE_BITMAP\s*\(' + args_pattern + r',\s*' + args_pattern + r'\)', re.S), r'unsigned long \1[BITS_TO_LONGS(\2)]'), 740 (Re(r'DECLARE_HASHTABLE\s*\(' + args_pattern + r',\s*' + args_pattern + r'\)', re.S), r'unsigned long \1[1 << ((\2) - 1)]'), 741 (Re(r'DECLARE_KFIFO\s*\(' + args_pattern + r',\s*' + args_pattern + r',\s*' + args_pattern + r'\)', re.S), r'\2 *\1'), 742 (Re(r'DECLARE_KFIFO_PTR\s*\(' + args_pattern + r',\s*' + args_pattern + r'\)', re.S), r'\2 *\1'), 743 (Re(r'(?:__)?DECLARE_FLEX_ARRAY\s*\(' + args_pattern + r',\s*' + args_pattern + r'\)', re.S), r'\1 \2[]'), 744 (Re(r'DEFINE_DMA_UNMAP_ADDR\s*\(' + args_pattern + r'\)', re.S), r'dma_addr_t \1'), 745 (Re(r'DEFINE_DMA_UNMAP_LEN\s*\(' + args_pattern + r'\)', re.S), r'__u32 \1'), 746 ] 747 748 # Regexes here are guaranteed to have the end limiter matching 749 # the start delimiter. Yet, right now, only one replace group 750 # is allowed. 751 752 sub_nested_prefixes = [ 753 (re.compile(r'\bSTRUCT_GROUP\('), r'\1'), 754 ] 755 756 for search, sub in sub_prefixes: 757 members = search.sub(sub, members) 758 759 nested = NestedMatch() 760 761 for search, sub in sub_nested_prefixes: 762 members = nested.sub(search, sub, members) 763 764 # Keeps the original declaration as-is 765 declaration = members 766 767 # Split nested struct/union elements 768 # 769 # This loop was simpler at the original kernel-doc perl version, as 770 # while ($members =~ m/$struct_members/) { ... } 771 # reads 'members' string on each interaction. 772 # 773 # Python behavior is different: it parses 'members' only once, 774 # creating a list of tuples from the first interaction. 775 # 776 # On other words, this won't get nested structs. 777 # 778 # So, we need to have an extra loop on Python to override such 779 # re limitation. 780 781 while True: 782 tuples = struct_members.findall(members) 783 if not tuples: 784 break 785 786 for t in tuples: 787 newmember = "" 788 maintype = t[0] 789 s_ids = t[5] 790 content = t[3] 791 792 oldmember = "".join(t) 793 794 for s_id in s_ids.split(','): 795 s_id = s_id.strip() 796 797 newmember += f"{maintype} {s_id}; " 798 s_id = Re(r'[:\[].*').sub('', s_id) 799 s_id = Re(r'^\s*\**(\S+)\s*').sub(r'\1', s_id) 800 801 for arg in content.split(';'): 802 arg = arg.strip() 803 804 if not arg: 805 continue 806 807 r = Re(r'^([^\(]+\(\*?\s*)([\w\.]*)(\s*\).*)') 808 if r.match(arg): 809 # Pointer-to-function 810 dtype = r.group(1) 811 name = r.group(2) 812 extra = r.group(3) 813 814 if not name: 815 continue 816 817 if not s_id: 818 # Anonymous struct/union 819 newmember += f"{dtype}{name}{extra}; " 820 else: 821 newmember += f"{dtype}{s_id}.{name}{extra}; " 822 823 else: 824 arg = arg.strip() 825 # Handle bitmaps 826 arg = Re(r':\s*\d+\s*').sub('', arg) 827 828 # Handle arrays 829 arg = Re(r'\[.*\]').sub('', arg) 830 831 # Handle multiple IDs 832 arg = Re(r'\s*,\s*').sub(',', arg) 833 834 835 r = Re(r'(.*)\s+([\S+,]+)') 836 837 if r.search(arg): 838 dtype = r.group(1) 839 names = r.group(2) 840 else: 841 newmember += f"{arg}; " 842 continue 843 844 for name in names.split(','): 845 name = Re(r'^\s*\**(\S+)\s*').sub(r'\1', name).strip() 846 847 if not name: 848 continue 849 850 if not s_id: 851 # Anonymous struct/union 852 newmember += f"{dtype} {name}; " 853 else: 854 newmember += f"{dtype} {s_id}.{name}; " 855 856 members = members.replace(oldmember, newmember) 857 858 # Ignore other nested elements, like enums 859 members = re.sub(r'(\{[^\{\}]*\})', '', members) 860 861 self.create_parameter_list(ln, decl_type, members, ';', 862 declaration_name) 863 self.check_sections(ln, declaration_name, decl_type, 864 self.entry.sectcheck, self.entry.struct_actual) 865 866 # Adjust declaration for better display 867 declaration = Re(r'([\{;])').sub(r'\1\n', declaration) 868 declaration = Re(r'\}\s+;').sub('};', declaration) 869 870 # Better handle inlined enums 871 while True: 872 r = Re(r'(enum\s+\{[^\}]+),([^\n])') 873 if not r.search(declaration): 874 break 875 876 declaration = r.sub(r'\1,\n\2', declaration) 877 878 def_args = declaration.split('\n') 879 level = 1 880 declaration = "" 881 for clause in def_args: 882 883 clause = clause.strip() 884 clause = Re(r'\s+').sub(' ', clause, count=1) 885 886 if not clause: 887 continue 888 889 if '}' in clause and level > 1: 890 level -= 1 891 892 if not Re(r'^\s*#').match(clause): 893 declaration += "\t" * level 894 895 declaration += "\t" + clause + "\n" 896 if "{" in clause and "}" not in clause: 897 level += 1 898 899 self.output_declaration(decl_type, declaration_name, 900 struct=declaration_name, 901 module=self.entry.modulename, 902 definition=declaration, 903 parameterlist=self.entry.parameterlist, 904 parameterdescs=self.entry.parameterdescs, 905 parametertypes=self.entry.parametertypes, 906 sectionlist=self.entry.sectionlist, 907 sections=self.entry.sections, 908 purpose=self.entry.declaration_purpose) 909 910 def dump_enum(self, ln, proto): 911 912 # Ignore members marked private 913 proto = Re(r'\/\*\s*private:.*?\/\*\s*public:.*?\*\/', flags=re.S).sub('', proto) 914 proto = Re(r'\/\*\s*private:.*}', flags=re.S).sub('}', proto) 915 916 # Strip comments 917 proto = Re(r'\/\*.*?\*\/', flags=re.S).sub('', proto) 918 919 # Strip #define macros inside enums 920 proto = Re(r'#\s*((define|ifdef|if)\s+|endif)[^;]*;', flags=re.S).sub('', proto) 921 922 members = None 923 declaration_name = None 924 925 r = Re(r'typedef\s+enum\s*\{(.*)\}\s*(\w*)\s*;') 926 if r.search(proto): 927 declaration_name = r.group(2) 928 members = r.group(1).rstrip() 929 else: 930 r = Re(r'enum\s+(\w*)\s*\{(.*)\}') 931 if r.match(proto): 932 declaration_name = r.group(1) 933 members = r.group(2).rstrip() 934 935 if not members: 936 self.emit_warning(ln, f"{proto}: error: Cannot parse enum!") 937 self.config.errors += 1 938 return 939 940 if self.entry.identifier != declaration_name: 941 if self.entry.identifier == "": 942 self.emit_warning(ln, 943 f"{proto}: wrong kernel-doc identifier on prototype") 944 else: 945 self.emit_warning(ln, 946 f"expecting prototype for enum {self.entry.identifier}. Prototype was for enum {declaration_name} instead") 947 return 948 949 if not declaration_name: 950 declaration_name = "(anonymous)" 951 952 member_set = set() 953 954 members = Re(r'\([^;]*?[\)]').sub('', members) 955 956 for arg in members.split(','): 957 if not arg: 958 continue 959 arg = Re(r'^\s*(\w+).*').sub(r'\1', arg) 960 self.entry.parameterlist.append(arg) 961 if arg not in self.entry.parameterdescs: 962 self.entry.parameterdescs[arg] = self.undescribed 963 if self.show_warnings("enum", declaration_name): 964 self.emit_warning(ln, 965 f"Enum value '{arg}' not described in enum '{declaration_name}'") 966 member_set.add(arg) 967 968 for k in self.entry.parameterdescs: 969 if k not in member_set: 970 if self.show_warnings("enum", declaration_name): 971 self.emit_warning(ln, 972 f"Excess enum value '%{k}' description in '{declaration_name}'") 973 974 self.output_declaration('enum', declaration_name, 975 enum=declaration_name, 976 module=self.config.modulename, 977 parameterlist=self.entry.parameterlist, 978 parameterdescs=self.entry.parameterdescs, 979 sectionlist=self.entry.sectionlist, 980 sections=self.entry.sections, 981 purpose=self.entry.declaration_purpose) 982 983 def dump_declaration(self, ln, prototype): 984 if self.entry.decl_type == "enum": 985 self.dump_enum(ln, prototype) 986 return 987 988 if self.entry.decl_type == "typedef": 989 self.dump_typedef(ln, prototype) 990 return 991 992 if self.entry.decl_type in ["union", "struct"]: 993 self.dump_struct(ln, prototype) 994 return 995 996 # TODO: handle other types 997 self.output_declaration(self.entry.decl_type, prototype, 998 entry=self.entry) 999 1000 def dump_function(self, ln, prototype): 1001 1002 func_macro = False 1003 return_type = '' 1004 decl_type = 'function' 1005 1006 # Prefixes that would be removed 1007 sub_prefixes = [ 1008 (r"^static +", "", 0), 1009 (r"^extern +", "", 0), 1010 (r"^asmlinkage +", "", 0), 1011 (r"^inline +", "", 0), 1012 (r"^__inline__ +", "", 0), 1013 (r"^__inline +", "", 0), 1014 (r"^__always_inline +", "", 0), 1015 (r"^noinline +", "", 0), 1016 (r"^__FORTIFY_INLINE +", "", 0), 1017 (r"__init +", "", 0), 1018 (r"__init_or_module +", "", 0), 1019 (r"__deprecated +", "", 0), 1020 (r"__flatten +", "", 0), 1021 (r"__meminit +", "", 0), 1022 (r"__must_check +", "", 0), 1023 (r"__weak +", "", 0), 1024 (r"__sched +", "", 0), 1025 (r"_noprof", "", 0), 1026 (r"__printf\s*\(\s*\d*\s*,\s*\d*\s*\) +", "", 0), 1027 (r"__(?:re)?alloc_size\s*\(\s*\d+\s*(?:,\s*\d+\s*)?\) +", "", 0), 1028 (r"__diagnose_as\s*\(\s*\S+\s*(?:,\s*\d+\s*)*\) +", "", 0), 1029 (r"DECL_BUCKET_PARAMS\s*\(\s*(\S+)\s*,\s*(\S+)\s*\)", r"\1, \2", 0), 1030 (r"__attribute_const__ +", "", 0), 1031 1032 # It seems that Python support for re.X is broken: 1033 # At least for me (Python 3.13), this didn't work 1034# (r""" 1035# __attribute__\s*\(\( 1036# (?: 1037# [\w\s]+ # attribute name 1038# (?:\([^)]*\))? # attribute arguments 1039# \s*,? # optional comma at the end 1040# )+ 1041# \)\)\s+ 1042# """, "", re.X), 1043 1044 # So, remove whitespaces and comments from it 1045 (r"__attribute__\s*\(\((?:[\w\s]+(?:\([^)]*\))?\s*,?)+\)\)\s+", "", 0), 1046 ] 1047 1048 for search, sub, flags in sub_prefixes: 1049 prototype = Re(search, flags).sub(sub, prototype) 1050 1051 # Macros are a special case, as they change the prototype format 1052 new_proto = Re(r"^#\s*define\s+").sub("", prototype) 1053 if new_proto != prototype: 1054 is_define_proto = True 1055 prototype = new_proto 1056 else: 1057 is_define_proto = False 1058 1059 # Yes, this truly is vile. We are looking for: 1060 # 1. Return type (may be nothing if we're looking at a macro) 1061 # 2. Function name 1062 # 3. Function parameters. 1063 # 1064 # All the while we have to watch out for function pointer parameters 1065 # (which IIRC is what the two sections are for), C types (these 1066 # regexps don't even start to express all the possibilities), and 1067 # so on. 1068 # 1069 # If you mess with these regexps, it's a good idea to check that 1070 # the following functions' documentation still comes out right: 1071 # - parport_register_device (function pointer parameters) 1072 # - atomic_set (macro) 1073 # - pci_match_device, __copy_to_user (long return type) 1074 1075 name = r'[a-zA-Z0-9_~:]+' 1076 prototype_end1 = r'[^\(]*' 1077 prototype_end2 = r'[^\{]*' 1078 prototype_end = fr'\(({prototype_end1}|{prototype_end2})\)' 1079 1080 # Besides compiling, Perl qr{[\w\s]+} works as a non-capturing group. 1081 # So, this needs to be mapped in Python with (?:...)? or (?:...)+ 1082 1083 type1 = r'(?:[\w\s]+)?' 1084 type2 = r'(?:[\w\s]+\*+)+' 1085 1086 found = False 1087 1088 if is_define_proto: 1089 r = Re(r'^()(' + name + r')\s+') 1090 1091 if r.search(prototype): 1092 return_type = '' 1093 declaration_name = r.group(2) 1094 func_macro = True 1095 1096 found = True 1097 1098 if not found: 1099 patterns = [ 1100 rf'^()({name})\s*{prototype_end}', 1101 rf'^({type1})\s+({name})\s*{prototype_end}', 1102 rf'^({type2})\s*({name})\s*{prototype_end}', 1103 ] 1104 1105 for p in patterns: 1106 r = Re(p) 1107 1108 if r.match(prototype): 1109 1110 return_type = r.group(1) 1111 declaration_name = r.group(2) 1112 args = r.group(3) 1113 1114 self.create_parameter_list(ln, decl_type, args, ',', 1115 declaration_name) 1116 1117 found = True 1118 break 1119 if not found: 1120 self.emit_warning(ln, 1121 f"cannot understand function prototype: '{prototype}'") 1122 return 1123 1124 if self.entry.identifier != declaration_name: 1125 self.emit_warning(ln, 1126 f"expecting prototype for {self.entry.identifier}(). Prototype was for {declaration_name}() instead") 1127 return 1128 1129 prms = " ".join(self.entry.parameterlist) 1130 self.check_sections(ln, declaration_name, "function", 1131 self.entry.sectcheck, prms) 1132 1133 self.check_return_section(ln, declaration_name, return_type) 1134 1135 if 'typedef' in return_type: 1136 self.output_declaration(decl_type, declaration_name, 1137 function=declaration_name, 1138 typedef=True, 1139 module=self.config.modulename, 1140 functiontype=return_type, 1141 parameterlist=self.entry.parameterlist, 1142 parameterdescs=self.entry.parameterdescs, 1143 parametertypes=self.entry.parametertypes, 1144 sectionlist=self.entry.sectionlist, 1145 sections=self.entry.sections, 1146 purpose=self.entry.declaration_purpose, 1147 func_macro=func_macro) 1148 else: 1149 self.output_declaration(decl_type, declaration_name, 1150 function=declaration_name, 1151 typedef=False, 1152 module=self.config.modulename, 1153 functiontype=return_type, 1154 parameterlist=self.entry.parameterlist, 1155 parameterdescs=self.entry.parameterdescs, 1156 parametertypes=self.entry.parametertypes, 1157 sectionlist=self.entry.sectionlist, 1158 sections=self.entry.sections, 1159 purpose=self.entry.declaration_purpose, 1160 func_macro=func_macro) 1161 1162 def dump_typedef(self, ln, proto): 1163 typedef_type = r'((?:\s+[\w\*]+\b){1,8})\s*' 1164 typedef_ident = r'\*?\s*(\w\S+)\s*' 1165 typedef_args = r'\s*\((.*)\);' 1166 1167 typedef1 = Re(r'typedef' + typedef_type + r'\(' + typedef_ident + r'\)' + typedef_args) 1168 typedef2 = Re(r'typedef' + typedef_type + typedef_ident + typedef_args) 1169 1170 # Strip comments 1171 proto = Re(r'/\*.*?\*/', flags=re.S).sub('', proto) 1172 1173 # Parse function typedef prototypes 1174 for r in [typedef1, typedef2]: 1175 if not r.match(proto): 1176 continue 1177 1178 return_type = r.group(1).strip() 1179 declaration_name = r.group(2) 1180 args = r.group(3) 1181 1182 if self.entry.identifier != declaration_name: 1183 self.emit_warning(ln, 1184 f"expecting prototype for typedef {self.entry.identifier}. Prototype was for typedef {declaration_name} instead\n") 1185 return 1186 1187 decl_type = 'function' 1188 self.create_parameter_list(ln, decl_type, args, ',', declaration_name) 1189 1190 self.output_declaration(decl_type, declaration_name, 1191 function=declaration_name, 1192 typedef=True, 1193 module=self.entry.modulename, 1194 functiontype=return_type, 1195 parameterlist=self.entry.parameterlist, 1196 parameterdescs=self.entry.parameterdescs, 1197 parametertypes=self.entry.parametertypes, 1198 sectionlist=self.entry.sectionlist, 1199 sections=self.entry.sections, 1200 purpose=self.entry.declaration_purpose) 1201 return 1202 1203 # Handle nested parentheses or brackets 1204 r = Re(r'(\(*.\)\s*|\[*.\]\s*);$') 1205 while r.search(proto): 1206 proto = r.sub('', proto) 1207 1208 # Parse simple typedefs 1209 r = Re(r'typedef.*\s+(\w+)\s*;') 1210 if r.match(proto): 1211 declaration_name = r.group(1) 1212 1213 if self.entry.identifier != declaration_name: 1214 self.emit_warning(ln, f"expecting prototype for typedef {self.entry.identifier}. Prototype was for typedef {declaration_name} instead\n") 1215 return 1216 1217 self.output_declaration('typedef', declaration_name, 1218 typedef=declaration_name, 1219 module=self.entry.modulename, 1220 sectionlist=self.entry.sectionlist, 1221 sections=self.entry.sections, 1222 purpose=self.entry.declaration_purpose) 1223 return 1224 1225 self.emit_warning(ln, "error: Cannot parse typedef!") 1226 self.config.errors += 1 1227 1228 @staticmethod 1229 def process_export(function_table, line): 1230 """ 1231 process EXPORT_SYMBOL* tags 1232 1233 This method is called both internally and externally, so, it 1234 doesn't use self. 1235 """ 1236 1237 if export_symbol.search(line): 1238 symbol = export_symbol.group(2) 1239 function_table.add(symbol) 1240 1241 if export_symbol_ns.search(line): 1242 symbol = export_symbol_ns.group(2) 1243 function_table.add(symbol) 1244 1245 def process_normal(self, ln, line): 1246 """ 1247 STATE_NORMAL: looking for the /** to begin everything. 1248 """ 1249 1250 if not doc_start.match(line): 1251 return 1252 1253 # start a new entry 1254 self.reset_state(ln + 1) 1255 self.entry.in_doc_sect = False 1256 1257 # next line is always the function name 1258 self.state = self.STATE_NAME 1259 1260 def process_name(self, ln, line): 1261 """ 1262 STATE_NAME: Looking for the "name - description" line 1263 """ 1264 1265 if doc_block.search(line): 1266 self.entry.new_start_line = ln 1267 1268 if not doc_block.group(1): 1269 self.entry.section = self.section_intro 1270 else: 1271 self.entry.section = doc_block.group(1) 1272 1273 self.state = self.STATE_DOCBLOCK 1274 return 1275 1276 if doc_decl.search(line): 1277 self.entry.identifier = doc_decl.group(1) 1278 self.entry.is_kernel_comment = False 1279 1280 decl_start = str(doc_com) # comment block asterisk 1281 fn_type = r"(?:\w+\s*\*\s*)?" # type (for non-functions) 1282 parenthesis = r"(?:\(\w*\))?" # optional parenthesis on function 1283 decl_end = r"(?:[-:].*)" # end of the name part 1284 1285 # test for pointer declaration type, foo * bar() - desc 1286 r = Re(fr"^{decl_start}([\w\s]+?){parenthesis}?\s*{decl_end}?$") 1287 if r.search(line): 1288 self.entry.identifier = r.group(1) 1289 1290 # Test for data declaration 1291 r = Re(r"^\s*\*?\s*(struct|union|enum|typedef)\b\s*(\w*)") 1292 if r.search(line): 1293 self.entry.decl_type = r.group(1) 1294 self.entry.identifier = r.group(2) 1295 self.entry.is_kernel_comment = True 1296 else: 1297 # Look for foo() or static void foo() - description; 1298 # or misspelt identifier 1299 1300 r1 = Re(fr"^{decl_start}{fn_type}(\w+)\s*{parenthesis}\s*{decl_end}?$") 1301 r2 = Re(fr"^{decl_start}{fn_type}(\w+[^-:]*){parenthesis}\s*{decl_end}$") 1302 1303 for r in [r1, r2]: 1304 if r.search(line): 1305 self.entry.identifier = r.group(1) 1306 self.entry.decl_type = "function" 1307 1308 r = Re(r"define\s+") 1309 self.entry.identifier = r.sub("", self.entry.identifier) 1310 self.entry.is_kernel_comment = True 1311 break 1312 1313 self.entry.identifier = self.entry.identifier.strip(" ") 1314 1315 self.state = self.STATE_BODY 1316 1317 # if there's no @param blocks need to set up default section here 1318 self.entry.section = self.section_default 1319 self.entry.new_start_line = ln + 1 1320 1321 r = Re("[-:](.*)") 1322 if r.search(line): 1323 # strip leading/trailing/multiple spaces 1324 self.entry.descr = r.group(1).strip(" ") 1325 1326 r = Re(r"\s+") 1327 self.entry.descr = r.sub(" ", self.entry.descr) 1328 self.entry.declaration_purpose = self.entry.descr 1329 self.state = self.STATE_BODY_MAYBE 1330 else: 1331 self.entry.declaration_purpose = "" 1332 1333 if not self.entry.is_kernel_comment: 1334 self.emit_warning(ln, 1335 f"This comment starts with '/**', but isn't a kernel-doc comment. Refer Documentation/doc-guide/kernel-doc.rst\n{line}") 1336 self.state = self.STATE_NORMAL 1337 1338 if not self.entry.declaration_purpose and self.config.wshort_desc: 1339 self.emit_warning(ln, 1340 f"missing initial short description on line:\n{line}") 1341 1342 if not self.entry.identifier and self.entry.decl_type != "enum": 1343 self.emit_warning(ln, 1344 f"wrong kernel-doc identifier on line:\n{line}") 1345 self.state = self.STATE_NORMAL 1346 1347 if self.config.verbose: 1348 self.emit_warning(ln, 1349 f"Scanning doc for {self.entry.decl_type} {self.entry.identifier}", 1350 warning=False) 1351 1352 return 1353 1354 # Failed to find an identifier. Emit a warning 1355 self.emit_warning(ln, f"Cannot find identifier on line:\n{line}") 1356 1357 def process_body(self, ln, line): 1358 """ 1359 STATE_BODY and STATE_BODY_MAYBE: the bulk of a kerneldoc comment. 1360 """ 1361 1362 if self.state == self.STATE_BODY_WITH_BLANK_LINE: 1363 r = Re(r"\s*\*\s?\S") 1364 if r.match(line): 1365 self.dump_section() 1366 self.entry.section = self.section_default 1367 self.entry.new_start_line = line 1368 self.entry.contents = "" 1369 1370 if doc_sect.search(line): 1371 self.entry.in_doc_sect = True 1372 newsection = doc_sect.group(1) 1373 1374 if newsection.lower() in ["description", "context"]: 1375 newsection = newsection.title() 1376 1377 # Special case: @return is a section, not a param description 1378 if newsection.lower() in ["@return", "@returns", 1379 "return", "returns"]: 1380 newsection = "Return" 1381 1382 # Perl kernel-doc has a check here for contents before sections. 1383 # the logic there is always false, as in_doc_sect variable is 1384 # always true. So, just don't implement Wcontents_before_sections 1385 1386 # .title() 1387 newcontents = doc_sect.group(2) 1388 if not newcontents: 1389 newcontents = "" 1390 1391 if self.entry.contents.strip("\n"): 1392 self.dump_section() 1393 1394 self.entry.new_start_line = ln 1395 self.entry.section = newsection 1396 self.entry.leading_space = None 1397 1398 self.entry.contents = newcontents.lstrip() 1399 if self.entry.contents: 1400 self.entry.contents += "\n" 1401 1402 self.state = self.STATE_BODY 1403 return 1404 1405 if doc_end.search(line): 1406 self.dump_section() 1407 1408 # Look for doc_com + <text> + doc_end: 1409 r = Re(r'\s*\*\s*[a-zA-Z_0-9:\.]+\*/') 1410 if r.match(line): 1411 self.emit_warning(ln, f"suspicious ending line: {line}") 1412 1413 self.entry.prototype = "" 1414 self.entry.new_start_line = ln + 1 1415 1416 self.state = self.STATE_PROTO 1417 return 1418 1419 if doc_content.search(line): 1420 cont = doc_content.group(1) 1421 1422 if cont == "": 1423 if self.entry.section == self.section_context: 1424 self.dump_section() 1425 1426 self.entry.new_start_line = ln 1427 self.state = self.STATE_BODY 1428 else: 1429 if self.entry.section != self.section_default: 1430 self.state = self.STATE_BODY_WITH_BLANK_LINE 1431 else: 1432 self.state = self.STATE_BODY 1433 1434 self.entry.contents += "\n" 1435 1436 elif self.state == self.STATE_BODY_MAYBE: 1437 1438 # Continued declaration purpose 1439 self.entry.declaration_purpose = self.entry.declaration_purpose.rstrip() 1440 self.entry.declaration_purpose += " " + cont 1441 1442 r = Re(r"\s+") 1443 self.entry.declaration_purpose = r.sub(' ', 1444 self.entry.declaration_purpose) 1445 1446 else: 1447 if self.entry.section.startswith('@') or \ 1448 self.entry.section == self.section_context: 1449 if self.entry.leading_space is None: 1450 r = Re(r'^(\s+)') 1451 if r.match(cont): 1452 self.entry.leading_space = len(r.group(1)) 1453 else: 1454 self.entry.leading_space = 0 1455 1456 # Double-check if leading space are realy spaces 1457 pos = 0 1458 for i in range(0, self.entry.leading_space): 1459 if cont[i] != " ": 1460 break 1461 pos += 1 1462 1463 cont = cont[pos:] 1464 1465 # NEW LOGIC: 1466 # In case it is different, update it 1467 if self.entry.leading_space != pos: 1468 self.entry.leading_space = pos 1469 1470 self.entry.contents += cont + "\n" 1471 return 1472 1473 # Unknown line, ignore 1474 self.emit_warning(ln, f"bad line: {line}") 1475 1476 def process_inline(self, ln, line): 1477 """STATE_INLINE: docbook comments within a prototype.""" 1478 1479 if self.inline_doc_state == self.STATE_INLINE_NAME and \ 1480 doc_inline_sect.search(line): 1481 self.entry.section = doc_inline_sect.group(1) 1482 self.entry.new_start_line = ln 1483 1484 self.entry.contents = doc_inline_sect.group(2).lstrip() 1485 if self.entry.contents != "": 1486 self.entry.contents += "\n" 1487 1488 self.inline_doc_state = self.STATE_INLINE_TEXT 1489 # Documentation block end */ 1490 return 1491 1492 if doc_inline_end.search(line): 1493 if self.entry.contents not in ["", "\n"]: 1494 self.dump_section() 1495 1496 self.state = self.STATE_PROTO 1497 self.inline_doc_state = self.STATE_INLINE_NA 1498 return 1499 1500 if doc_content.search(line): 1501 if self.inline_doc_state == self.STATE_INLINE_TEXT: 1502 self.entry.contents += doc_content.group(1) + "\n" 1503 if not self.entry.contents.strip(" ").rstrip("\n"): 1504 self.entry.contents = "" 1505 1506 elif self.inline_doc_state == self.STATE_INLINE_NAME: 1507 self.emit_warning(ln, 1508 f"Incorrect use of kernel-doc format: {line}") 1509 1510 self.inline_doc_state = self.STATE_INLINE_ERROR 1511 1512 def syscall_munge(self, ln, proto): 1513 """ 1514 Handle syscall definitions 1515 """ 1516 1517 is_void = False 1518 1519 # Strip newlines/CR's 1520 proto = re.sub(r'[\r\n]+', ' ', proto) 1521 1522 # Check if it's a SYSCALL_DEFINE0 1523 if 'SYSCALL_DEFINE0' in proto: 1524 is_void = True 1525 1526 # Replace SYSCALL_DEFINE with correct return type & function name 1527 proto = Re(r'SYSCALL_DEFINE.*\(').sub('long sys_', proto) 1528 1529 r = Re(r'long\s+(sys_.*?),') 1530 if r.search(proto): 1531 proto = proto.replace(',', '(', count=1) 1532 elif is_void: 1533 proto = proto.replace(')', '(void)', count=1) 1534 1535 # Now delete all of the odd-numbered commas in the proto 1536 # so that argument types & names don't have a comma between them 1537 count = 0 1538 length = len(proto) 1539 1540 if is_void: 1541 length = 0 # skip the loop if is_void 1542 1543 for ix in range(length): 1544 if proto[ix] == ',': 1545 count += 1 1546 if count % 2 == 1: 1547 proto = proto[:ix] + ' ' + proto[ix+1:] 1548 1549 return proto 1550 1551 def tracepoint_munge(self, ln, proto): 1552 """ 1553 Handle tracepoint definitions 1554 """ 1555 1556 tracepointname = None 1557 tracepointargs = None 1558 1559 # Match tracepoint name based on different patterns 1560 r = Re(r'TRACE_EVENT\((.*?),') 1561 if r.search(proto): 1562 tracepointname = r.group(1) 1563 1564 r = Re(r'DEFINE_SINGLE_EVENT\((.*?),') 1565 if r.search(proto): 1566 tracepointname = r.group(1) 1567 1568 r = Re(r'DEFINE_EVENT\((.*?),(.*?),') 1569 if r.search(proto): 1570 tracepointname = r.group(2) 1571 1572 if tracepointname: 1573 tracepointname = tracepointname.lstrip() 1574 1575 r = Re(r'TP_PROTO\((.*?)\)') 1576 if r.search(proto): 1577 tracepointargs = r.group(1) 1578 1579 if not tracepointname or not tracepointargs: 1580 self.emit_warning(ln, 1581 f"Unrecognized tracepoint format:\n{proto}\n") 1582 else: 1583 proto = f"static inline void trace_{tracepointname}({tracepointargs})" 1584 self.entry.identifier = f"trace_{self.entry.identifier}" 1585 1586 return proto 1587 1588 def process_proto_function(self, ln, line): 1589 """Ancillary routine to process a function prototype""" 1590 1591 # strip C99-style comments to end of line 1592 r = Re(r"\/\/.*$", re.S) 1593 line = r.sub('', line) 1594 1595 if Re(r'\s*#\s*define').match(line): 1596 self.entry.prototype = line 1597 elif line.startswith('#'): 1598 # Strip other macros like #ifdef/#ifndef/#endif/... 1599 pass 1600 else: 1601 r = Re(r'([^\{]*)') 1602 if r.match(line): 1603 self.entry.prototype += r.group(1) + " " 1604 1605 if '{' in line or ';' in line or Re(r'\s*#\s*define').match(line): 1606 # strip comments 1607 r = Re(r'/\*.*?\*/') 1608 self.entry.prototype = r.sub('', self.entry.prototype) 1609 1610 # strip newlines/cr's 1611 r = Re(r'[\r\n]+') 1612 self.entry.prototype = r.sub(' ', self.entry.prototype) 1613 1614 # strip leading spaces 1615 r = Re(r'^\s+') 1616 self.entry.prototype = r.sub('', self.entry.prototype) 1617 1618 # Handle self.entry.prototypes for function pointers like: 1619 # int (*pcs_config)(struct foo) 1620 1621 r = Re(r'^(\S+\s+)\(\s*\*(\S+)\)') 1622 self.entry.prototype = r.sub(r'\1\2', self.entry.prototype) 1623 1624 if 'SYSCALL_DEFINE' in self.entry.prototype: 1625 self.entry.prototype = self.syscall_munge(ln, 1626 self.entry.prototype) 1627 1628 r = Re(r'TRACE_EVENT|DEFINE_EVENT|DEFINE_SINGLE_EVENT') 1629 if r.search(self.entry.prototype): 1630 self.entry.prototype = self.tracepoint_munge(ln, 1631 self.entry.prototype) 1632 1633 self.dump_function(ln, self.entry.prototype) 1634 self.reset_state(ln) 1635 1636 def process_proto_type(self, ln, line): 1637 """Ancillary routine to process a type""" 1638 1639 # Strip newlines/cr's. 1640 line = Re(r'[\r\n]+', re.S).sub(' ', line) 1641 1642 # Strip leading spaces 1643 line = Re(r'^\s+', re.S).sub('', line) 1644 1645 # Strip trailing spaces 1646 line = Re(r'\s+$', re.S).sub('', line) 1647 1648 # Strip C99-style comments to the end of the line 1649 line = Re(r"\/\/.*$", re.S).sub('', line) 1650 1651 # To distinguish preprocessor directive from regular declaration later. 1652 if line.startswith('#'): 1653 line += ";" 1654 1655 r = Re(r'([^\{\};]*)([\{\};])(.*)') 1656 while True: 1657 if r.search(line): 1658 if self.entry.prototype: 1659 self.entry.prototype += " " 1660 self.entry.prototype += r.group(1) + r.group(2) 1661 1662 self.entry.brcount += r.group(2).count('{') 1663 self.entry.brcount -= r.group(2).count('}') 1664 1665 self.entry.brcount = max(self.entry.brcount, 0) 1666 1667 if r.group(2) == ';' and self.entry.brcount == 0: 1668 self.dump_declaration(ln, self.entry.prototype) 1669 self.reset_state(ln) 1670 break 1671 1672 line = r.group(3) 1673 else: 1674 self.entry.prototype += line 1675 break 1676 1677 def process_proto(self, ln, line): 1678 """STATE_PROTO: reading a function/whatever prototype.""" 1679 1680 if doc_inline_oneline.search(line): 1681 self.entry.section = doc_inline_oneline.group(1) 1682 self.entry.contents = doc_inline_oneline.group(2) 1683 1684 if self.entry.contents != "": 1685 self.entry.contents += "\n" 1686 self.dump_section(start_new=False) 1687 1688 elif doc_inline_start.search(line): 1689 self.state = self.STATE_INLINE 1690 self.inline_doc_state = self.STATE_INLINE_NAME 1691 1692 elif self.entry.decl_type == 'function': 1693 self.process_proto_function(ln, line) 1694 1695 else: 1696 self.process_proto_type(ln, line) 1697 1698 def process_docblock(self, ln, line): 1699 """STATE_DOCBLOCK: within a DOC: block.""" 1700 1701 if doc_end.search(line): 1702 self.dump_section() 1703 self.output_declaration("doc", None, 1704 sectionlist=self.entry.sectionlist, 1705 sections=self.entry.sections, module=self.config.modulename) 1706 self.reset_state(ln) 1707 1708 elif doc_content.search(line): 1709 self.entry.contents += doc_content.group(1) + "\n" 1710 1711 def run(self): 1712 """ 1713 Open and process each line of a C source file. 1714 he parsing is controlled via a state machine, and the line is passed 1715 to a different process function depending on the state. The process 1716 function may update the state as needed. 1717 """ 1718 1719 cont = False 1720 prev = "" 1721 prev_ln = None 1722 1723 try: 1724 with open(self.fname, "r", encoding="utf8", 1725 errors="backslashreplace") as fp: 1726 for ln, line in enumerate(fp): 1727 1728 line = line.expandtabs().strip("\n") 1729 1730 # Group continuation lines on prototypes 1731 if self.state == self.STATE_PROTO: 1732 if line.endswith("\\"): 1733 prev += line.removesuffix("\\") 1734 cont = True 1735 1736 if not prev_ln: 1737 prev_ln = ln 1738 1739 continue 1740 1741 if cont: 1742 ln = prev_ln 1743 line = prev + line 1744 prev = "" 1745 cont = False 1746 prev_ln = None 1747 1748 self.config.log.debug("%d %s%s: %s", 1749 ln, self.st_name[self.state], 1750 self.st_inline_name[self.inline_doc_state], 1751 line) 1752 1753 # TODO: not all states allow EXPORT_SYMBOL*, so this 1754 # can be optimized later on to speedup parsing 1755 self.process_export(self.config.function_table, line) 1756 1757 # Hand this line to the appropriate state handler 1758 if self.state == self.STATE_NORMAL: 1759 self.process_normal(ln, line) 1760 elif self.state == self.STATE_NAME: 1761 self.process_name(ln, line) 1762 elif self.state in [self.STATE_BODY, self.STATE_BODY_MAYBE, 1763 self.STATE_BODY_WITH_BLANK_LINE]: 1764 self.process_body(ln, line) 1765 elif self.state == self.STATE_INLINE: # scanning for inline parameters 1766 self.process_inline(ln, line) 1767 elif self.state == self.STATE_PROTO: 1768 self.process_proto(ln, line) 1769 elif self.state == self.STATE_DOCBLOCK: 1770 self.process_docblock(ln, line) 1771 except OSError: 1772 self.config.log.error(f"Error: Cannot open file {self.fname}") 1773 self.config.errors += 1 1774 1775 1776class GlobSourceFiles: 1777 """ 1778 Parse C source code file names and directories via an Interactor. 1779 1780 """ 1781 1782 def __init__(self, srctree=None, valid_extensions=None): 1783 """ 1784 Initialize valid extensions with a tuple. 1785 1786 If not defined, assume default C extensions (.c and .h) 1787 1788 It would be possible to use python's glob function, but it is 1789 very slow, and it is not interactive. So, it would wait to read all 1790 directories before actually do something. 1791 1792 So, let's use our own implementation. 1793 """ 1794 1795 if not valid_extensions: 1796 self.extensions = (".c", ".h") 1797 else: 1798 self.extensions = valid_extensions 1799 1800 self.srctree = srctree 1801 1802 def _parse_dir(self, dirname): 1803 """Internal function to parse files recursively""" 1804 1805 with os.scandir(dirname) as obj: 1806 for entry in obj: 1807 name = os.path.join(dirname, entry.name) 1808 1809 if entry.is_dir(): 1810 yield from self._parse_dir(name) 1811 1812 if not entry.is_file(): 1813 continue 1814 1815 basename = os.path.basename(name) 1816 1817 if not basename.endswith(self.extensions): 1818 continue 1819 1820 yield name 1821 1822 def parse_files(self, file_list, file_not_found_cb): 1823 for fname in file_list: 1824 if self.srctree: 1825 f = os.path.join(self.srctree, fname) 1826 else: 1827 f = fname 1828 1829 if os.path.isdir(f): 1830 yield from self._parse_dir(f) 1831 elif os.path.isfile(f): 1832 yield f 1833 elif file_not_found_cb: 1834 file_not_found_cb(fname) 1835 1836 1837class KernelFiles(): 1838 1839 def parse_file(self, fname): 1840 1841 doc = KernelDoc(self.config, fname) 1842 doc.run() 1843 1844 return doc 1845 1846 def process_export_file(self, fname): 1847 try: 1848 with open(fname, "r", encoding="utf8", 1849 errors="backslashreplace") as fp: 1850 for line in fp: 1851 KernelDoc.process_export(self.config.function_table, line) 1852 1853 except IOError: 1854 print(f"Error: Cannot open fname {fname}", fname=sys.stderr) 1855 self.config.errors += 1 1856 1857 def file_not_found_cb(self, fname): 1858 self.config.log.error("Cannot find file %s", fname) 1859 self.config.errors += 1 1860 1861 def __init__(self, files=None, verbose=False, out_style=None, 1862 werror=False, wreturn=False, wshort_desc=False, 1863 wcontents_before_sections=False, 1864 logger=None, modulename=None, export_file=None): 1865 """Initialize startup variables and parse all files""" 1866 1867 1868 if not verbose: 1869 verbose = bool(os.environ.get("KBUILD_VERBOSE", 0)) 1870 1871 if not modulename: 1872 modulename = "Kernel API" 1873 1874 dt = datetime.now() 1875 if os.environ.get("KBUILD_BUILD_TIMESTAMP", None): 1876 # use UTC TZ 1877 to_zone = tz.gettz('UTC') 1878 dt = dt.astimezone(to_zone) 1879 1880 if not werror: 1881 kcflags = os.environ.get("KCFLAGS", None) 1882 if kcflags: 1883 match = re.search(r"(\s|^)-Werror(\s|$)/", kcflags) 1884 if match: 1885 werror = True 1886 1887 # reading this variable is for backwards compat just in case 1888 # someone was calling it with the variable from outside the 1889 # kernel's build system 1890 kdoc_werror = os.environ.get("KDOC_WERROR", None) 1891 if kdoc_werror: 1892 werror = kdoc_werror 1893 1894 # Set global config data used on all files 1895 self.config = argparse.Namespace 1896 1897 self.config.verbose = verbose 1898 self.config.werror = werror 1899 self.config.wreturn = wreturn 1900 self.config.wshort_desc = wshort_desc 1901 self.config.wcontents_before_sections = wcontents_before_sections 1902 self.config.modulename = modulename 1903 1904 self.config.function_table = set() 1905 self.config.source_map = {} 1906 1907 if not logger: 1908 self.config.log = logging.getLogger("kernel-doc") 1909 else: 1910 self.config.log = logger 1911 1912 self.config.kernel_version = os.environ.get("KERNELVERSION", 1913 "unknown kernel version'") 1914 self.config.src_tree = os.environ.get("SRCTREE", None) 1915 1916 self.out_style = out_style 1917 self.export_file = export_file 1918 1919 # Initialize internal variables 1920 1921 self.config.errors = 0 1922 self.results = [] 1923 1924 self.file_list = files 1925 self.files = set() 1926 1927 def parse(self): 1928 """ 1929 Parse all files 1930 """ 1931 1932 glob = GlobSourceFiles(srctree=self.config.src_tree) 1933 1934 # Let's use a set here to avoid duplicating files 1935 1936 for fname in glob.parse_files(self.file_list, self.file_not_found_cb): 1937 if fname in self.files: 1938 continue 1939 1940 self.files.add(fname) 1941 1942 res = self.parse_file(fname) 1943 self.results.append((res.fname, res.entries)) 1944 1945 if not self.files: 1946 sys.exit(1) 1947 1948 # If a list of export files was provided, parse EXPORT_SYMBOL* 1949 # from the ones not already parsed 1950 1951 if self.export_file: 1952 files = self.files 1953 1954 glob = GlobSourceFiles(srctree=self.config.src_tree) 1955 1956 for fname in glob.parse_files(self.export_file, 1957 self.file_not_found_cb): 1958 if fname not in files: 1959 files.add(fname) 1960 1961 self.process_export_file(fname) 1962 1963 def out_msg(self, fname, name, arg): 1964 # TODO: filter out unwanted parts 1965 1966 return self.out_style.msg(fname, name, arg) 1967 1968 def msg(self, enable_lineno=False, export=False, internal=False, 1969 symbol=None, nosymbol=None): 1970 1971 function_table = self.config.function_table 1972 1973 if symbol: 1974 for s in symbol: 1975 function_table.add(s) 1976 1977 # Output none mode: only warnings will be shown 1978 if not self.out_style: 1979 return 1980 1981 self.out_style.set_config(self.config) 1982 1983 self.out_style.set_filter(export, internal, symbol, nosymbol, 1984 function_table, enable_lineno) 1985 1986 for fname, arg_tuple in self.results: 1987 for name, arg in arg_tuple: 1988 if self.out_msg(fname, name, arg): 1989 ln = arg.get("ln", 0) 1990 dtype = arg.get('type', "") 1991 1992 self.config.log.warning("%s:%d Can't handle %s", 1993 fname, ln, dtype) 1994 1995 1996class OutputFormat: 1997 # output mode. 1998 OUTPUT_ALL = 0 # output all symbols and doc sections 1999 OUTPUT_INCLUDE = 1 # output only specified symbols 2000 OUTPUT_EXPORTED = 2 # output exported symbols 2001 OUTPUT_INTERNAL = 3 # output non-exported symbols 2002 2003 # Virtual member to be overriden at the inherited classes 2004 highlights = [] 2005 2006 def __init__(self): 2007 """Declare internal vars and set mode to OUTPUT_ALL""" 2008 2009 self.out_mode = self.OUTPUT_ALL 2010 self.enable_lineno = None 2011 self.nosymbol = {} 2012 self.symbol = None 2013 self.function_table = set() 2014 self.config = None 2015 2016 def set_config(self, config): 2017 self.config = config 2018 2019 def set_filter(self, export, internal, symbol, nosymbol, function_table, 2020 enable_lineno): 2021 """ 2022 Initialize filter variables according with the requested mode. 2023 2024 Only one choice is valid between export, internal and symbol. 2025 2026 The nosymbol filter can be used on all modes. 2027 """ 2028 2029 self.enable_lineno = enable_lineno 2030 2031 if symbol: 2032 self.out_mode = self.OUTPUT_INCLUDE 2033 function_table = symbol 2034 elif export: 2035 self.out_mode = self.OUTPUT_EXPORTED 2036 elif internal: 2037 self.out_mode = self.OUTPUT_INTERNAL 2038 else: 2039 self.out_mode = self.OUTPUT_ALL 2040 2041 if nosymbol: 2042 self.nosymbol = set(nosymbol) 2043 2044 if function_table: 2045 self.function_table = function_table 2046 2047 def highlight_block(self, block): 2048 """ 2049 Apply the RST highlights to a sub-block of text. 2050 """ 2051 2052 for r, sub in self.highlights: 2053 block = r.sub(sub, block) 2054 2055 return block 2056 2057 def check_doc(self, name): 2058 """Check if DOC should be output""" 2059 2060 if self.out_mode == self.OUTPUT_ALL: 2061 return True 2062 2063 if self.out_mode == self.OUTPUT_INCLUDE: 2064 if name in self.nosymbol: 2065 return False 2066 2067 if name in self.function_table: 2068 return True 2069 2070 return False 2071 2072 def check_declaration(self, dtype, name): 2073 if name in self.nosymbol: 2074 return False 2075 2076 if self.out_mode == self.OUTPUT_ALL: 2077 return True 2078 2079 if self.out_mode in [ self.OUTPUT_INCLUDE, self.OUTPUT_EXPORTED ]: 2080 if name in self.function_table: 2081 return True 2082 2083 if self.out_mode == self.OUTPUT_INTERNAL: 2084 if dtype != "function": 2085 return True 2086 2087 if name not in self.function_table: 2088 return True 2089 2090 return False 2091 2092 def check_function(self, fname, name, args): 2093 return True 2094 2095 def check_enum(self, fname, name, args): 2096 return True 2097 2098 def check_typedef(self, fname, name, args): 2099 return True 2100 2101 def msg(self, fname, name, args): 2102 2103 dtype = args.get('type', "") 2104 2105 if dtype == "doc": 2106 self.out_doc(fname, name, args) 2107 return False 2108 2109 if not self.check_declaration(dtype, name): 2110 return False 2111 2112 if dtype == "function": 2113 self.out_function(fname, name, args) 2114 return False 2115 2116 if dtype == "enum": 2117 self.out_enum(fname, name, args) 2118 return False 2119 2120 if dtype == "typedef": 2121 self.out_typedef(fname, name, args) 2122 return False 2123 2124 if dtype in ["struct", "union"]: 2125 self.out_struct(fname, name, args) 2126 return False 2127 2128 # Warn if some type requires an output logic 2129 self.config.log.warning("doesn't now how to output '%s' block", 2130 dtype) 2131 2132 return True 2133 2134 # Virtual methods to be overridden by inherited classes 2135 def out_doc(self, fname, name, args): 2136 pass 2137 2138 def out_function(self, fname, name, args): 2139 pass 2140 2141 def out_enum(self, fname, name, args): 2142 pass 2143 2144 def out_typedef(self, fname, name, args): 2145 pass 2146 2147 def out_struct(self, fname, name, args): 2148 pass 2149 2150 2151class RestFormat(OutputFormat): 2152 # """Consts and functions used by ReST output""" 2153 2154 highlights = [ 2155 (type_constant, r"``\1``"), 2156 (type_constant2, r"``\1``"), 2157 2158 # Note: need to escape () to avoid func matching later 2159 (type_member_func, r":c:type:`\1\2\3\\(\\) <\1>`"), 2160 (type_member, r":c:type:`\1\2\3 <\1>`"), 2161 (type_fp_param, r"**\1\\(\\)**"), 2162 (type_fp_param2, r"**\1\\(\\)**"), 2163 (type_func, r"\1()"), 2164 (type_enum, r":c:type:`\1 <\2>`"), 2165 (type_struct, r":c:type:`\1 <\2>`"), 2166 (type_typedef, r":c:type:`\1 <\2>`"), 2167 (type_union, r":c:type:`\1 <\2>`"), 2168 2169 # in rst this can refer to any type 2170 (type_fallback, r":c:type:`\1`"), 2171 (type_param_ref, r"**\1\2**") 2172 ] 2173 blankline = "\n" 2174 2175 sphinx_literal = Re(r'^[^.].*::$', cache=False) 2176 sphinx_cblock = Re(r'^\.\.\ +code-block::', cache=False) 2177 2178 def __init__(self): 2179 """ 2180 Creates class variables. 2181 2182 Not really mandatory, but it is a good coding style and makes 2183 pylint happy. 2184 """ 2185 2186 super().__init__() 2187 self.lineprefix = "" 2188 2189 def print_lineno (self, ln): 2190 """Outputs a line number""" 2191 2192 if self.enable_lineno and ln: 2193 print(f".. LINENO {ln}") 2194 2195 def output_highlight(self, args): 2196 input_text = args 2197 output = "" 2198 in_literal = False 2199 litprefix = "" 2200 block = "" 2201 2202 for line in input_text.strip("\n").split("\n"): 2203 2204 # If we're in a literal block, see if we should drop out of it. 2205 # Otherwise, pass the line straight through unmunged. 2206 if in_literal: 2207 if line.strip(): # If the line is not blank 2208 # If this is the first non-blank line in a literal block, 2209 # figure out the proper indent. 2210 if not litprefix: 2211 r = Re(r'^(\s*)') 2212 if r.match(line): 2213 litprefix = '^' + r.group(1) 2214 else: 2215 litprefix = "" 2216 2217 output += line + "\n" 2218 elif not Re(litprefix).match(line): 2219 in_literal = False 2220 else: 2221 output += line + "\n" 2222 else: 2223 output += line + "\n" 2224 2225 # Not in a literal block (or just dropped out) 2226 if not in_literal: 2227 block += line + "\n" 2228 if self.sphinx_literal.match(line) or self.sphinx_cblock.match(line): 2229 in_literal = True 2230 litprefix = "" 2231 output += self.highlight_block(block) 2232 block = "" 2233 2234 # Handle any remaining block 2235 if block: 2236 output += self.highlight_block(block) 2237 2238 # Print the output with the line prefix 2239 for line in output.strip("\n").split("\n"): 2240 print(self.lineprefix + line) 2241 2242 def out_section(self, args, out_reference=False): 2243 """ 2244 Outputs a block section. 2245 2246 This could use some work; it's used to output the DOC: sections, and 2247 starts by putting out the name of the doc section itself, but that 2248 tends to duplicate a header already in the template file. 2249 """ 2250 2251 sectionlist = args.get('sectionlist', []) 2252 sections = args.get('sections', {}) 2253 section_start_lines = args.get('section_start_lines', {}) 2254 2255 for section in sectionlist: 2256 # Skip sections that are in the nosymbol_table 2257 if section in self.nosymbol: 2258 continue 2259 2260 if not self.out_mode == self.OUTPUT_INCLUDE: 2261 if out_reference: 2262 print(f".. _{section}:\n") 2263 2264 if not self.symbol: 2265 print(f'{self.lineprefix}**{section}**\n') 2266 2267 self.print_lineno(section_start_lines.get(section, 0)) 2268 self.output_highlight(sections[section]) 2269 print() 2270 print() 2271 2272 def out_doc(self, fname, name, args): 2273 if not self.check_doc(name): 2274 return 2275 2276 self.out_section(args, out_reference=True) 2277 2278 def out_function(self, fname, name, args): 2279 2280 oldprefix = self.lineprefix 2281 signature = "" 2282 2283 func_macro = args.get('func_macro', False) 2284 if func_macro: 2285 signature = args['function'] 2286 else: 2287 if args.get('functiontype'): 2288 signature = args['functiontype'] + " " 2289 signature += args['function'] + " (" 2290 2291 parameterlist = args.get('parameterlist', []) 2292 parameterdescs = args.get('parameterdescs', {}) 2293 parameterdesc_start_lines = args.get('parameterdesc_start_lines', {}) 2294 2295 ln = args.get('ln', 0) 2296 2297 count = 0 2298 for parameter in parameterlist: 2299 if count != 0: 2300 signature += ", " 2301 count += 1 2302 dtype = args['parametertypes'].get(parameter, "") 2303 2304 if function_pointer.search(dtype): 2305 signature += function_pointer.group(1) + parameter + function_pointer.group(3) 2306 else: 2307 signature += dtype 2308 2309 if not func_macro: 2310 signature += ")" 2311 2312 if args.get('typedef') or not args.get('functiontype'): 2313 print(f".. c:macro:: {args['function']}\n") 2314 2315 if args.get('typedef'): 2316 self.print_lineno(ln) 2317 print(" **Typedef**: ", end="") 2318 self.lineprefix = "" 2319 self.output_highlight(args.get('purpose', "")) 2320 print("\n\n**Syntax**\n") 2321 print(f" ``{signature}``\n") 2322 else: 2323 print(f"``{signature}``\n") 2324 else: 2325 print(f".. c:function:: {signature}\n") 2326 2327 if not args.get('typedef'): 2328 self.print_lineno(ln) 2329 self.lineprefix = " " 2330 self.output_highlight(args.get('purpose', "")) 2331 print() 2332 2333 # Put descriptive text into a container (HTML <div>) to help set 2334 # function prototypes apart 2335 self.lineprefix = " " 2336 2337 if parameterlist: 2338 print(".. container:: kernelindent\n") 2339 print(f"{self.lineprefix}**Parameters**\n") 2340 2341 for parameter in parameterlist: 2342 parameter_name = Re(r'\[.*').sub('', parameter) 2343 dtype = args['parametertypes'].get(parameter, "") 2344 2345 if dtype: 2346 print(f"{self.lineprefix}``{dtype}``") 2347 else: 2348 print(f"{self.lineprefix}``{parameter}``") 2349 2350 self.print_lineno(parameterdesc_start_lines.get(parameter_name, 0)) 2351 2352 self.lineprefix = " " 2353 if parameter_name in parameterdescs and \ 2354 parameterdescs[parameter_name] != KernelDoc.undescribed: 2355 2356 self.output_highlight(parameterdescs[parameter_name]) 2357 print() 2358 else: 2359 print(f"{self.lineprefix}*undescribed*\n") 2360 self.lineprefix = " " 2361 2362 self.out_section(args) 2363 self.lineprefix = oldprefix 2364 2365 def out_enum(self, fname, name, args): 2366 2367 oldprefix = self.lineprefix 2368 name = args.get('enum', '') 2369 parameterlist = args.get('parameterlist', []) 2370 parameterdescs = args.get('parameterdescs', {}) 2371 ln = args.get('ln', 0) 2372 2373 print(f"\n\n.. c:enum:: {name}\n") 2374 2375 self.print_lineno(ln) 2376 self.lineprefix = " " 2377 self.output_highlight(args.get('purpose', '')) 2378 print() 2379 2380 print(".. container:: kernelindent\n") 2381 outer = self.lineprefix + " " 2382 self.lineprefix = outer + " " 2383 print(f"{outer}**Constants**\n") 2384 2385 for parameter in parameterlist: 2386 print(f"{outer}``{parameter}``") 2387 2388 if parameterdescs.get(parameter, '') != KernelDoc.undescribed: 2389 self.output_highlight(parameterdescs[parameter]) 2390 else: 2391 print(f"{self.lineprefix}*undescribed*\n") 2392 print() 2393 2394 self.lineprefix = oldprefix 2395 self.out_section(args) 2396 2397 def out_typedef(self, fname, name, args): 2398 2399 oldprefix = self.lineprefix 2400 name = args.get('typedef', '') 2401 ln = args.get('ln', 0) 2402 2403 print(f"\n\n.. c:type:: {name}\n") 2404 2405 self.print_lineno(ln) 2406 self.lineprefix = " " 2407 2408 self.output_highlight(args.get('purpose', '')) 2409 2410 print() 2411 2412 self.lineprefix = oldprefix 2413 self.out_section(args) 2414 2415 def out_struct(self, fname, name, args): 2416 2417 name = args.get('struct', "") 2418 purpose = args.get('purpose', "") 2419 declaration = args.get('definition', "") 2420 dtype = args.get('type', "struct") 2421 ln = args.get('ln', 0) 2422 2423 parameterlist = args.get('parameterlist', []) 2424 parameterdescs = args.get('parameterdescs', {}) 2425 parameterdesc_start_lines = args.get('parameterdesc_start_lines', {}) 2426 2427 print(f"\n\n.. c:{dtype}:: {name}\n") 2428 2429 self.print_lineno(ln) 2430 2431 oldprefix = self.lineprefix 2432 self.lineprefix += " " 2433 2434 self.output_highlight(purpose) 2435 print() 2436 2437 print(".. container:: kernelindent\n") 2438 print(f"{self.lineprefix}**Definition**::\n") 2439 2440 self.lineprefix = self.lineprefix + " " 2441 2442 declaration = declaration.replace("\t", self.lineprefix) 2443 2444 print(f"{self.lineprefix}{dtype} {name}" + ' {') 2445 print(f"{declaration}{self.lineprefix}" + "};\n") 2446 2447 self.lineprefix = " " 2448 print(f"{self.lineprefix}**Members**\n") 2449 for parameter in parameterlist: 2450 if not parameter or parameter.startswith("#"): 2451 continue 2452 2453 parameter_name = parameter.split("[", maxsplit=1)[0] 2454 2455 if parameterdescs.get(parameter_name) == KernelDoc.undescribed: 2456 continue 2457 2458 self.print_lineno(parameterdesc_start_lines.get(parameter_name, 0)) 2459 2460 print(f"{self.lineprefix}``{parameter}``") 2461 2462 self.lineprefix = " " 2463 self.output_highlight(parameterdescs[parameter_name]) 2464 self.lineprefix = " " 2465 2466 print() 2467 2468 print() 2469 2470 self.lineprefix = oldprefix 2471 self.out_section(args) 2472 2473 2474class ManFormat(OutputFormat): 2475 """Consts and functions used by man pages output""" 2476 2477 highlights = ( 2478 (type_constant, r"\1"), 2479 (type_constant2, r"\1"), 2480 (type_func, r"\\fB\1\\fP"), 2481 (type_enum, r"\\fI\1\\fP"), 2482 (type_struct, r"\\fI\1\\fP"), 2483 (type_typedef, r"\\fI\1\\fP"), 2484 (type_union, r"\\fI\1\\fP"), 2485 (type_param, r"\\fI\1\\fP"), 2486 (type_param_ref, r"\\fI\1\2\\fP"), 2487 (type_member, r"\\fI\1\2\3\\fP"), 2488 (type_fallback, r"\\fI\1\\fP") 2489 ) 2490 blankline = "" 2491 2492 def __init__(self): 2493 """ 2494 Creates class variables. 2495 2496 Not really mandatory, but it is a good coding style and makes 2497 pylint happy. 2498 """ 2499 2500 super().__init__() 2501 2502 dt = datetime.now() 2503 if os.environ.get("KBUILD_BUILD_TIMESTAMP", None): 2504 # use UTC TZ 2505 to_zone = tz.gettz('UTC') 2506 dt = dt.astimezone(to_zone) 2507 2508 self.man_date = dt.strftime("%B %Y") 2509 2510 def output_highlight(self, block): 2511 2512 contents = self.highlight_block(block) 2513 2514 if isinstance(contents, list): 2515 contents = "\n".join(contents) 2516 2517 for line in contents.strip("\n").split("\n"): 2518 line = Re(r"^\s*").sub("", line) 2519 2520 if line and line[0] == ".": 2521 print("\\&" + line) 2522 else: 2523 print(line) 2524 2525 def out_doc(self, fname, name, args): 2526 module = args.get('module') 2527 sectionlist = args.get('sectionlist', []) 2528 sections = args.get('sections', {}) 2529 2530 print(f'.TH "{module}" 9 "{module}" "{self.man_date}" "API Manual" LINUX') 2531 2532 for section in sectionlist: 2533 print(f'.SH "{section}"') 2534 self.output_highlight(sections.get(section)) 2535 2536 def out_function(self, fname, name, args): 2537 """output function in man""" 2538 2539 parameterlist = args.get('parameterlist', []) 2540 parameterdescs = args.get('parameterdescs', {}) 2541 sectionlist = args.get('sectionlist', []) 2542 sections = args.get('sections', {}) 2543 2544 print(f'.TH "{args['function']}" 9 "{args['function']}" "{self.man_date}" "Kernel Hacker\'s Manual" LINUX') 2545 2546 print(".SH NAME") 2547 print(f"{args['function']} \\- {args['purpose']}") 2548 2549 print(".SH SYNOPSIS") 2550 if args.get('functiontype', ''): 2551 print(f'.B "{args['functiontype']}" {args['function']}') 2552 else: 2553 print(f'.B "{args['function']}') 2554 2555 count = 0 2556 parenth = "(" 2557 post = "," 2558 2559 for parameter in parameterlist: 2560 if count == len(parameterlist) - 1: 2561 post = ");" 2562 2563 dtype = args['parametertypes'].get(parameter, "") 2564 if function_pointer.match(dtype): 2565 # Pointer-to-function 2566 print(f'".BI "{parenth}{function_pointer.group(1)}" " ") ({function_pointer.group(2)}){post}"') 2567 else: 2568 dtype = Re(r'([^\*])$').sub(r'\1 ', dtype) 2569 2570 print(f'.BI "{parenth}{dtype}" "{post}"') 2571 count += 1 2572 parenth = "" 2573 2574 if parameterlist: 2575 print(".SH ARGUMENTS") 2576 2577 for parameter in parameterlist: 2578 parameter_name = re.sub(r'\[.*', '', parameter) 2579 2580 print(f'.IP "{parameter}" 12') 2581 self.output_highlight(parameterdescs.get(parameter_name, "")) 2582 2583 for section in sectionlist: 2584 print(f'.SH "{section.upper()}"') 2585 self.output_highlight(sections[section]) 2586 2587 def out_enum(self, fname, name, args): 2588 2589 name = args.get('enum', '') 2590 parameterlist = args.get('parameterlist', []) 2591 sectionlist = args.get('sectionlist', []) 2592 sections = args.get('sections', {}) 2593 2594 print(f'.TH "{args['module']}" 9 "enum {args['enum']}" "{self.man_date}" "API Manual" LINUX') 2595 2596 print(".SH NAME") 2597 print(f"enum {args['enum']} \\- {args['purpose']}") 2598 2599 print(".SH SYNOPSIS") 2600 print(f"enum {args['enum']}" + " {") 2601 2602 count = 0 2603 for parameter in parameterlist: 2604 print(f'.br\n.BI " {parameter}"') 2605 if count == len(parameterlist) - 1: 2606 print("\n};") 2607 else: 2608 print(", \n.br") 2609 2610 count += 1 2611 2612 print(".SH Constants") 2613 2614 for parameter in parameterlist: 2615 parameter_name = Re(r'\[.*').sub('', parameter) 2616 print(f'.IP "{parameter}" 12') 2617 self.output_highlight(args['parameterdescs'].get(parameter_name, "")) 2618 2619 for section in sectionlist: 2620 print(f'.SH "{section}"') 2621 self.output_highlight(sections[section]) 2622 2623 def out_typedef(self, fname, name, args): 2624 module = args.get('module') 2625 typedef = args.get('typedef') 2626 purpose = args.get('purpose') 2627 sectionlist = args.get('sectionlist', []) 2628 sections = args.get('sections', {}) 2629 2630 print(f'.TH "{module}" 9 "{typedef}" "{self.man_date}" "API Manual" LINUX') 2631 2632 print(".SH NAME") 2633 print(f"typedef {typedef} \\- {purpose}") 2634 2635 for section in sectionlist: 2636 print(f'.SH "{section}"') 2637 self.output_highlight(sections.get(section)) 2638 2639 def out_struct(self, fname, name, args): 2640 module = args.get('module') 2641 struct_type = args.get('type') 2642 struct_name = args.get('struct') 2643 purpose = args.get('purpose') 2644 definition = args.get('definition') 2645 sectionlist = args.get('sectionlist', []) 2646 parameterlist = args.get('parameterlist', []) 2647 sections = args.get('sections', {}) 2648 parameterdescs = args.get('parameterdescs', {}) 2649 2650 print(f'.TH "{module}" 9 "{struct_type} {struct_name}" "{self.man_date}" "API Manual" LINUX') 2651 2652 print(".SH NAME") 2653 print(f"{struct_type} {struct_name} \\- {purpose}") 2654 2655 # Replace tabs with two spaces and handle newlines 2656 declaration = definition.replace("\t", " ") 2657 declaration = Re(r"\n").sub('"\n.br\n.BI "', declaration) 2658 2659 print(".SH SYNOPSIS") 2660 print(f"{struct_type} {struct_name} " + "{" +"\n.br") 2661 print(f'.BI "{declaration}\n' + "};\n.br\n") 2662 2663 print(".SH Members") 2664 for parameter in parameterlist: 2665 if parameter.startswith("#"): 2666 continue 2667 2668 parameter_name = re.sub(r"\[.*", "", parameter) 2669 2670 if parameterdescs.get(parameter_name) == KernelDoc.undescribed: 2671 continue 2672 2673 print(f'.IP "{parameter}" 12') 2674 self.output_highlight(parameterdescs.get(parameter_name)) 2675 2676 for section in sectionlist: 2677 print(f'.SH "{section}"') 2678 self.output_highlight(sections.get(section)) 2679 2680 2681# Command line interface 2682 2683 2684DESC = """ 2685Read C language source or header FILEs, extract embedded documentation comments, 2686and print formatted documentation to standard output. 2687 2688The documentation comments are identified by the "/**" opening comment mark. 2689 2690See Documentation/doc-guide/kernel-doc.rst for the documentation comment syntax. 2691""" 2692 2693EXPORT_FILE_DESC = """ 2694Specify an additional FILE in which to look for EXPORT_SYMBOL information. 2695 2696May be used multiple times. 2697""" 2698 2699EXPORT_DESC = """ 2700Only output documentation for the symbols that have been 2701exported using EXPORT_SYMBOL() and related macros in any input 2702FILE or -export-file FILE. 2703""" 2704 2705INTERNAL_DESC = """ 2706Only output documentation for the symbols that have NOT been 2707exported using EXPORT_SYMBOL() and related macros in any input 2708FILE or -export-file FILE. 2709""" 2710 2711FUNCTION_DESC = """ 2712Only output documentation for the given function or DOC: section 2713title. All other functions and DOC: sections are ignored. 2714 2715May be used multiple times. 2716""" 2717 2718NOSYMBOL_DESC = """ 2719Exclude the specified symbol from the output documentation. 2720 2721May be used multiple times. 2722""" 2723 2724FILES_DESC = """ 2725Header and C source files to be parsed. 2726""" 2727 2728WARN_CONTENTS_BEFORE_SECTIONS_DESC = """ 2729Warns if there are contents before sections (deprecated). 2730 2731This option is kept just for backward-compatibility, but it does nothing, 2732neither here nor at the original Perl script. 2733""" 2734 2735 2736class MsgFormatter(logging.Formatter): 2737 def format(self, record): 2738 record.levelname = record.levelname.capitalize() 2739 return logging.Formatter.format(self, record) 2740 2741def main(): 2742 """Main program""" 2743 2744 parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter, 2745 description=DESC) 2746 2747 # Normal arguments 2748 2749 parser.add_argument("-v", "-verbose", "--verbose", action="store_true", 2750 help="Verbose output, more warnings and other information.") 2751 2752 parser.add_argument("-d", "-debug", "--debug", action="store_true", 2753 help="Enable debug messages") 2754 2755 parser.add_argument("-M", "-modulename", "--modulename", 2756 help="Allow setting a module name at the output.") 2757 2758 parser.add_argument("-l", "-enable-lineno", "--enable_lineno", 2759 action="store_true", 2760 help="Enable line number output (only in ReST mode)") 2761 2762 # Arguments to control the warning behavior 2763 2764 parser.add_argument("-Wreturn", "--wreturn", action="store_true", 2765 help="Warns about the lack of a return markup on functions.") 2766 2767 parser.add_argument("-Wshort-desc", "-Wshort-description", "--wshort-desc", 2768 action="store_true", 2769 help="Warns if initial short description is missing") 2770 2771 parser.add_argument("-Wcontents-before-sections", 2772 "--wcontents-before-sections", action="store_true", 2773 help=WARN_CONTENTS_BEFORE_SECTIONS_DESC) 2774 2775 parser.add_argument("-Wall", "--wall", action="store_true", 2776 help="Enable all types of warnings") 2777 2778 parser.add_argument("-Werror", "--werror", action="store_true", 2779 help="Treat warnings as errors.") 2780 2781 parser.add_argument("-export-file", "--export-file", action='append', 2782 help=EXPORT_FILE_DESC) 2783 2784 # Output format mutually-exclusive group 2785 2786 out_group = parser.add_argument_group("Output format selection (mutually exclusive)") 2787 2788 out_fmt = out_group.add_mutually_exclusive_group() 2789 2790 out_fmt.add_argument("-m", "-man", "--man", action="store_true", 2791 help="Output troff manual page format.") 2792 out_fmt.add_argument("-r", "-rst", "--rst", action="store_true", 2793 help="Output reStructuredText format (default).") 2794 out_fmt.add_argument("-N", "-none", "--none", action="store_true", 2795 help="Do not output documentation, only warnings.") 2796 2797 # Output selection mutually-exclusive group 2798 2799 sel_group = parser.add_argument_group("Output selection (mutually exclusive)") 2800 sel_mut = sel_group.add_mutually_exclusive_group() 2801 2802 sel_mut.add_argument("-e", "-export", "--export", action='store_true', 2803 help=EXPORT_DESC) 2804 2805 sel_mut.add_argument("-i", "-internal", "--internal", action='store_true', 2806 help=INTERNAL_DESC) 2807 2808 sel_mut.add_argument("-s", "-function", "--symbol", action='append', 2809 help=FUNCTION_DESC) 2810 2811 # This one is valid for all 3 types of filter 2812 parser.add_argument("-n", "-nosymbol", "--nosymbol", action='append', 2813 help=NOSYMBOL_DESC) 2814 2815 parser.add_argument("files", metavar="FILE", 2816 nargs="+", help=FILES_DESC) 2817 2818 args = parser.parse_args() 2819 2820 if args.wall: 2821 args.wreturn = True 2822 args.wshort_desc = True 2823 args.wcontents_before_sections = True 2824 2825 logger = logging.getLogger() 2826 2827 if not args.debug: 2828 logger.setLevel(logging.INFO) 2829 else: 2830 logger.setLevel(logging.DEBUG) 2831 2832 formatter = MsgFormatter('%(levelname)s: %(message)s') 2833 2834 handler = logging.StreamHandler() 2835 handler.setFormatter(formatter) 2836 2837 logger.addHandler(handler) 2838 2839 if args.man: 2840 out_style = ManFormat() 2841 elif args.none: 2842 out_style = None 2843 else: 2844 out_style = RestFormat() 2845 2846 kfiles = KernelFiles(files=args.files, verbose=args.verbose, 2847 out_style=out_style, werror=args.werror, 2848 wreturn=args.wreturn, wshort_desc=args.wshort_desc, 2849 wcontents_before_sections=args.wcontents_before_sections, 2850 modulename=args.modulename, 2851 export_file=args.export_file) 2852 2853 kfiles.parse() 2854 2855 kfiles.msg(enable_lineno=args.enable_lineno, export=args.export, 2856 internal=args.internal, symbol=args.symbol, 2857 nosymbol=args.nosymbol) 2858 2859 2860# Call main method 2861if __name__ == "__main__": 2862 main() 2863