| 1 | #!/usr/bin/env python3 |
| 2 | # SPDX-License-Identifier: GPL-2.0 |
| 3 | # Copyright(c) 2025: Mauro Carvalho Chehab <[email protected]>. |
| 4 | # |
| 5 | # pylint: disable=C0103,R0912,R0914,R0915 |
| 6 | # |
| 7 | # NOTE: While kernel-doc requires at least version 3.6 to run, the |
| 8 | # command line should work with Python 3.2+ (tested with 3.4). |
| 9 | # The rationale is that it shall fail gracefully during Kernel |
| 10 | # compilation with older Kernel versions. Due to that: |
| 11 | # - encoding line is needed here; |
| 12 | # - f-strings cannot be used in this file. |
| 13 | # - libraries that require newer versions can only be included |
| 14 | # after the Python version has been checked. |
| 15 | # |
| 16 | # Converted from the kernel-doc script originally written in Perl |
| 17 | # under GPLv2, copyrighted since 1998 by the following authors: |
| 18 | # |
| 19 | # Aditya Srivastava <[email protected]> |
| 20 | # Akira Yokosawa <[email protected]> |
| 21 | # Alexander A. Klimov <[email protected]> |
| 22 | # Alexander Lobakin <[email protected]> |
| 23 | # André Almeida <[email protected]> |
| 24 | # Andy Shevchenko <[email protected]> |
| 25 | # Anna-Maria Behnsen <[email protected]> |
| 26 | # Armin Kuster <[email protected]> |
| 27 | # Bart Van Assche <[email protected]> |
| 28 | # Ben Hutchings <[email protected]> |
| 29 | # Borislav Petkov <[email protected]> |
| 30 | # Chen-Yu Tsai <[email protected]> |
| 31 | # Coco Li <[email protected]> |
| 32 | # Conchúr Navid <[email protected]> |
| 33 | # Daniel Santos <[email protected]> |
| 34 | # Danilo Cesar Lemes de Paula <[email protected]> |
| 35 | # Dan Luedtke <[email protected]> |
| 36 | # Donald Hunter <[email protected]> |
| 37 | # Gabriel Krisman Bertazi <[email protected]> |
| 38 | # Greg Kroah-Hartman <[email protected]> |
| 39 | # Harvey Harrison <[email protected]> |
| 40 | # Horia Geanta <[email protected]> |
| 41 | # Ilya Dryomov <[email protected]> |
| 42 | # Jakub Kicinski <[email protected]> |
| 43 | # Jani Nikula <[email protected]> |
| 44 | # Jason Baron <[email protected]> |
| 45 | # Jason Gunthorpe <[email protected]> |
| 46 | # Jérémy Bobbio <[email protected]> |
| 47 | # Johannes Berg <[email protected]> |
| 48 | # Johannes Weiner <[email protected]> |
| 49 | # Jonathan Cameron <[email protected]> |
| 50 | # Jonathan Corbet <[email protected]> |
| 51 | # Jonathan Neuschäfer <[email protected]> |
| 52 | # Kamil Rytarowski <[email protected]> |
| 53 | # Kees Cook <[email protected]> |
| 54 | # Laurent Pinchart <[email protected]> |
| 55 | # Levin, Alexander (Sasha Levin) <[email protected]> |
| 56 | # Linus Torvalds <[email protected]> |
| 57 | # Lucas De Marchi <[email protected]> |
| 58 | # Mark Rutland <[email protected]> |
| 59 | # Markus Heiser <[email protected]> |
| 60 | # Martin Waitz <[email protected]> |
| 61 | # Masahiro Yamada <[email protected]> |
| 62 | # Matthew Wilcox <[email protected]> |
| 63 | # Mauro Carvalho Chehab <[email protected]> |
| 64 | # Michal Wajdeczko <[email protected]> |
| 65 | # Michael Zucchi |
| 66 | # Mike Rapoport <[email protected]> |
| 67 | # Niklas Söderlund <[email protected]> |
| 68 | # Nishanth Menon <[email protected]> |
| 69 | # Paolo Bonzini <[email protected]> |
| 70 | # Pavan Kumar Linga <[email protected]> |
| 71 | # Pavel Pisa <[email protected]> |
| 72 | # Peter Maydell <[email protected]> |
| 73 | # Pierre-Louis Bossart <[email protected]> |
| 74 | # Randy Dunlap <[email protected]> |
| 75 | # Richard Kennedy <[email protected]> |
| 76 | # Rich Walker <[email protected]> |
| 77 | # Rolf Eike Beer <[email protected]> |
| 78 | # Sakari Ailus <[email protected]> |
| 79 | # Silvio Fricke <[email protected]> |
| 80 | # Simon Huggins |
| 81 | # Tim Waugh <[email protected]> |
| 82 | # Tomasz Warniełło <[email protected]> |
| 83 | # Utkarsh Tripathi <[email protected]> |
| 84 | # [email protected] <[email protected]> |
| 85 | # Vegard Nossum <[email protected]> |
| 86 | # Will Deacon <[email protected]> |
| 87 | # Yacine Belkadi <[email protected]> |
| 88 | # Yujie Liu <[email protected]> |
| 89 | |
| 90 | """ |
| 91 | Print formatted kernel documentation to stdout. |
| 92 | |
| 93 | Read C language source or header FILEs, extract embedded |
| 94 | documentation comments, and print formatted documentation |
| 95 | to standard output. |
| 96 | |
| 97 | The documentation comments are identified by the ``/**`` |
| 98 | opening comment mark. |
| 99 | |
| 100 | See Documentation/doc-guide/kernel-doc.rst for the |
| 101 | documentation comment syntax. |
| 102 | """ |
| 103 | |
| 104 | import argparse |
| 105 | import logging |
| 106 | import os |
| 107 | import sys |
| 108 | |
| 109 | # Import Python modules |
| 110 | |
| 111 | LIB_DIR = "../lib/python" |
| 112 | SRC_DIR = os.path.dirname(os.path.realpath(__file__)) |
| 113 | |
| 114 | sys.path.insert(0, os.path.join(SRC_DIR, LIB_DIR)) |
| 115 | |
| 116 | WERROR_RETURN_CODE = 3 |
| 117 | |
| 118 | DESC = """ |
| 119 | Read C language source or header FILEs, extract embedded documentation comments, |
| 120 | and print formatted documentation to standard output. |
| 121 | |
| 122 | The documentation comments are identified by the "/**" opening comment mark. |
| 123 | |
| 124 | See Documentation/doc-guide/kernel-doc.rst for the documentation comment syntax. |
| 125 | """ |
| 126 | |
| 127 | EXPORT_FILE_DESC = """ |
| 128 | Specify an additional FILE in which to look for EXPORT_SYMBOL information. |
| 129 | |
| 130 | May be used multiple times. |
| 131 | """ |
| 132 | |
| 133 | EXPORT_DESC = """ |
| 134 | Only output documentation for symbols that have been |
| 135 | exported using EXPORT_SYMBOL() and related macros in any input |
| 136 | FILE or -export-file FILE. |
| 137 | """ |
| 138 | |
| 139 | INTERNAL_DESC = """ |
| 140 | Only output documentation for symbols that have NOT been |
| 141 | exported using EXPORT_SYMBOL() and related macros in any input |
| 142 | FILE or -export-file FILE. |
| 143 | """ |
| 144 | |
| 145 | FUNCTION_DESC = """ |
| 146 | Only output documentation for the given function or DOC: section |
| 147 | title. All other functions and DOC: sections are ignored. |
| 148 | |
| 149 | May be used multiple times. |
| 150 | """ |
| 151 | |
| 152 | NOSYMBOL_DESC = """ |
| 153 | Exclude the specified symbol from the output documentation. |
| 154 | |
| 155 | May be used multiple times. |
| 156 | """ |
| 157 | |
| 158 | FILES_DESC = """ |
| 159 | Header and C source files to be parsed. |
| 160 | """ |
| 161 | |
| 162 | WARN_CONTENTS_BEFORE_SECTIONS_DESC = """ |
| 163 | Warn if there are contents before sections (deprecated). |
| 164 | |
| 165 | This option is kept just for backward-compatibility, but it does nothing, |
| 166 | neither here nor at the original Perl script. |
| 167 | """ |
| 168 | |
| 169 | EPILOG = """ |
| 170 | The return value is: |
| 171 | |
| 172 | - 0: success or Python version is not compatible with |
| 173 | kernel-doc. If -Werror is not used, it will also |
| 174 | return 0 if there are issues at kernel-doc markups; |
| 175 | |
| 176 | - 1: an abnormal condition happened; |
| 177 | |
| 178 | - 2: argparse issued an error; |
| 179 | |
| 180 | - 3: When -Werror is used, it means that one or more unfiltered parse |
| 181 | warnings happened. |
| 182 | """ |
| 183 | |
| 184 | class MsgFormatter(logging.Formatter): |
| 185 | """ |
| 186 | Helper class to capitalize errors and warnings, the same way |
| 187 | the venerable (now retired) kernel-doc.pl used to do. |
| 188 | """ |
| 189 | |
| 190 | def format(self, record): |
| 191 | record.levelname = record.levelname.capitalize() |
| 192 | return logging.Formatter.format(self, record) |
| 193 | |
| 194 | def main(): |
| 195 | """ |
| 196 | Main program. |
| 197 | |
| 198 | """ |
| 199 | |
| 200 | parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter, |
| 201 | description=DESC, epilog=EPILOG) |
| 202 | |
| 203 | # |
| 204 | # Normal arguments |
| 205 | # |
| 206 | parser.add_argument("-v", "-verbose", "--verbose", action="store_true", |
| 207 | help="Verbose output, more warnings and other information.") |
| 208 | |
| 209 | parser.add_argument("-d", "-debug", "--debug", action="store_true", |
| 210 | help="Enable debug messages") |
| 211 | |
| 212 | parser.add_argument("-M", "-modulename", "--modulename", |
| 213 | default="Kernel API", |
| 214 | help="Allow setting a module name at the output.") |
| 215 | |
| 216 | parser.add_argument("-l", "-enable-lineno", "--enable_lineno", |
| 217 | action="store_true", |
| 218 | help="Enable line number output (only in ReST mode)") |
| 219 | |
| 220 | # |
| 221 | # Arguments to control the warning behavior |
| 222 | # |
| 223 | parser.add_argument("-Wreturn", "--wreturn", action="store_true", |
| 224 | help="Warns about the lack of a return markup on functions.") |
| 225 | |
| 226 | parser.add_argument("-Wshort-desc", "-Wshort-description", "--wshort-desc", |
| 227 | action="store_true", |
| 228 | help="Warns if initial short description is missing") |
| 229 | |
| 230 | parser.add_argument("-Wcontents-before-sections", |
| 231 | "--wcontents-before-sections", action="store_true", |
| 232 | help=WARN_CONTENTS_BEFORE_SECTIONS_DESC) |
| 233 | |
| 234 | parser.add_argument("-Wall", "--wall", action="store_true", |
| 235 | help="Enable all types of warnings") |
| 236 | |
| 237 | parser.add_argument("-Werror", "--werror", action="store_true", |
| 238 | help="Treat warnings as errors.") |
| 239 | |
| 240 | parser.add_argument("-export-file", "--export-file", action='append', |
| 241 | help=EXPORT_FILE_DESC) |
| 242 | |
| 243 | # |
| 244 | # Output format mutually-exclusive group |
| 245 | # |
| 246 | out_group = parser.add_argument_group("Output format selection (mutually exclusive)") |
| 247 | |
| 248 | out_fmt = out_group.add_mutually_exclusive_group() |
| 249 | |
| 250 | out_fmt.add_argument("-m", "-man", "--man", action="store_true", |
| 251 | help="Output troff manual page format.") |
| 252 | out_fmt.add_argument("-r", "-rst", "--rst", action="store_true", |
| 253 | help="Output reStructuredText format (default).") |
| 254 | out_fmt.add_argument("-N", "-none", "--none", action="store_true", |
| 255 | help="Do not output documentation, only warnings.") |
| 256 | |
| 257 | # |
| 258 | # Output selection mutually-exclusive group |
| 259 | # |
| 260 | sel_group = parser.add_argument_group("Output selection (mutually exclusive)") |
| 261 | sel_mut = sel_group.add_mutually_exclusive_group() |
| 262 | |
| 263 | sel_mut.add_argument("-e", "-export", "--export", action='store_true', |
| 264 | help=EXPORT_DESC) |
| 265 | |
| 266 | sel_mut.add_argument("-i", "-internal", "--internal", action='store_true', |
| 267 | help=INTERNAL_DESC) |
| 268 | |
| 269 | sel_mut.add_argument("-s", "-function", "--symbol", action='append', |
| 270 | help=FUNCTION_DESC) |
| 271 | |
| 272 | # |
| 273 | # Those are valid for all 3 types of filter |
| 274 | # |
| 275 | parser.add_argument("-n", "-nosymbol", "--nosymbol", action='append', |
| 276 | help=NOSYMBOL_DESC) |
| 277 | |
| 278 | parser.add_argument("-D", "-no-doc-sections", "--no-doc-sections", |
| 279 | action='store_true', help="Don't output DOC sections") |
| 280 | |
| 281 | parser.add_argument("files", metavar="FILE", |
| 282 | nargs="+", help=FILES_DESC) |
| 283 | |
| 284 | args = parser.parse_args() |
| 285 | |
| 286 | if args.wall: |
| 287 | args.wreturn = True |
| 288 | args.wshort_desc = True |
| 289 | args.wcontents_before_sections = True |
| 290 | |
| 291 | logger = logging.getLogger() |
| 292 | |
| 293 | if not args.debug: |
| 294 | logger.setLevel(logging.INFO) |
| 295 | else: |
| 296 | logger.setLevel(logging.DEBUG) |
| 297 | |
| 298 | formatter = MsgFormatter('%(levelname)s: %(message)s') |
| 299 | |
| 300 | handler = logging.StreamHandler() |
| 301 | handler.setFormatter(formatter) |
| 302 | |
| 303 | logger.addHandler(handler) |
| 304 | |
| 305 | python_ver = sys.version_info[:2] |
| 306 | if python_ver < (3,6): |
| 307 | # |
| 308 | # Depending on the Kernel configuration, kernel-doc --none is called at |
| 309 | # build time. As we don't want to break compilation due to the |
| 310 | # usage of an old Python version, return 0 here. |
| 311 | # |
| 312 | if args.none: |
| 313 | logger.error("Python 3.6 or later is required by kernel-doc. Skipping checks") |
| 314 | sys.exit(0) |
| 315 | |
| 316 | sys.exit("Python 3.6 or later is required by kernel-doc. Aborting.") |
| 317 | |
| 318 | if python_ver < (3,7): |
| 319 | logger.warning("Python 3.7 or later is required for correct results") |
| 320 | |
| 321 | # |
| 322 | # Import kernel-doc libraries only after checking the Python version |
| 323 | # |
| 324 | from kdoc.kdoc_files import KernelFiles # pylint: disable=C0415 |
| 325 | from kdoc.kdoc_output import RestFormat, ManFormat # pylint: disable=C0415 |
| 326 | |
| 327 | if args.man: |
| 328 | out_style = ManFormat(modulename=args.modulename) |
| 329 | elif args.none: |
| 330 | out_style = None |
| 331 | else: |
| 332 | out_style = RestFormat() |
| 333 | |
| 334 | kfiles = KernelFiles(verbose=args.verbose, |
| 335 | out_style=out_style, werror=args.werror, |
| 336 | wreturn=args.wreturn, wshort_desc=args.wshort_desc, |
| 337 | wcontents_before_sections=args.wcontents_before_sections) |
| 338 | |
| 339 | kfiles.parse(args.files, export_file=args.export_file) |
| 340 | |
| 341 | for t in kfiles.msg(enable_lineno=args.enable_lineno, export=args.export, |
| 342 | internal=args.internal, symbol=args.symbol, |
| 343 | nosymbol=args.nosymbol, export_file=args.export_file, |
| 344 | no_doc_sections=args.no_doc_sections): |
| 345 | msg = t[1] |
| 346 | if msg: |
| 347 | print(msg) |
| 348 | |
| 349 | error_count = kfiles.errors |
| 350 | if not error_count: |
| 351 | sys.exit(0) |
| 352 | |
| 353 | if args.werror: |
| 354 | print("%s warnings as errors" % error_count) # pylint: disable=C0209 |
| 355 | sys.exit(WERROR_RETURN_CODE) |
| 356 | |
| 357 | if args.verbose: |
| 358 | print("%s errors" % error_count) # pylint: disable=C0209 |
| 359 | |
| 360 | sys.exit(0) |
| 361 | |
| 362 | # |
| 363 | # Call main method |
| 364 | # |
| 365 | if __name__ == "__main__": |
| 366 | main() |