| 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=C0103,R0915 |
| 6 | # |
| 7 | # Converted from the kernel-doc script originally written in Perl |
| 8 | # under GPLv2, copyrighted since 1998 by the following authors: |
| 9 | # |
| 10 | # Aditya Srivastava <yashsri421@gmail.com> |
| 11 | # Akira Yokosawa <akiyks@gmail.com> |
| 12 | # Alexander A. Klimov <grandmaster@al2klimov.de> |
| 13 | # Alexander Lobakin <aleksander.lobakin@intel.com> |
| 14 | # André Almeida <andrealmeid@igalia.com> |
| 15 | # Andy Shevchenko <andriy.shevchenko@linux.intel.com> |
| 16 | # Anna-Maria Behnsen <anna-maria@linutronix.de> |
| 17 | # Armin Kuster <akuster@mvista.com> |
| 18 | # Bart Van Assche <bart.vanassche@sandisk.com> |
| 19 | # Ben Hutchings <ben@decadent.org.uk> |
| 20 | # Borislav Petkov <bbpetkov@yahoo.de> |
| 21 | # Chen-Yu Tsai <wenst@chromium.org> |
| 22 | # Coco Li <lixiaoyan@google.com> |
| 23 | # Conchúr Navid <conchur@web.de> |
| 24 | # Daniel Santos <daniel.santos@pobox.com> |
| 25 | # Danilo Cesar Lemes de Paula <danilo.cesar@collabora.co.uk> |
| 26 | # Dan Luedtke <mail@danrl.de> |
| 27 | # Donald Hunter <donald.hunter@gmail.com> |
| 28 | # Gabriel Krisman Bertazi <krisman@collabora.co.uk> |
| 29 | # Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| 30 | # Harvey Harrison <harvey.harrison@gmail.com> |
| 31 | # Horia Geanta <horia.geanta@freescale.com> |
| 32 | # Ilya Dryomov <idryomov@gmail.com> |
| 33 | # Jakub Kicinski <kuba@kernel.org> |
| 34 | # Jani Nikula <jani.nikula@intel.com> |
| 35 | # Jason Baron <jbaron@redhat.com> |
| 36 | # Jason Gunthorpe <jgg@nvidia.com> |
| 37 | # Jérémy Bobbio <lunar@debian.org> |
| 38 | # Johannes Berg <johannes.berg@intel.com> |
| 39 | # Johannes Weiner <hannes@cmpxchg.org> |
| 40 | # Jonathan Cameron <Jonathan.Cameron@huawei.com> |
| 41 | # Jonathan Corbet <corbet@lwn.net> |
| 42 | # Jonathan Neuschäfer <j.neuschaefer@gmx.net> |
| 43 | # Kamil Rytarowski <n54@gmx.com> |
| 44 | # Kees Cook <kees@kernel.org> |
| 45 | # Laurent Pinchart <laurent.pinchart@ideasonboard.com> |
| 46 | # Levin, Alexander (Sasha Levin) <alexander.levin@verizon.com> |
| 47 | # Linus Torvalds <torvalds@linux-foundation.org> |
| 48 | # Lucas De Marchi <lucas.demarchi@profusion.mobi> |
| 49 | # Mark Rutland <mark.rutland@arm.com> |
| 50 | # Markus Heiser <markus.heiser@darmarit.de> |
| 51 | # Martin Waitz <tali@admingilde.org> |
| 52 | # Masahiro Yamada <masahiroy@kernel.org> |
| 53 | # Matthew Wilcox <willy@infradead.org> |
| 54 | # Mauro Carvalho Chehab <mchehab+huawei@kernel.org> |
| 55 | # Michal Wajdeczko <michal.wajdeczko@intel.com> |
| 56 | # Michael Zucchi |
| 57 | # Mike Rapoport <rppt@linux.ibm.com> |
| 58 | # Niklas Söderlund <niklas.soderlund@corigine.com> |
| 59 | # Nishanth Menon <nm@ti.com> |
| 60 | # Paolo Bonzini <pbonzini@redhat.com> |
| 61 | # Pavan Kumar Linga <pavan.kumar.linga@intel.com> |
| 62 | # Pavel Pisa <pisa@cmp.felk.cvut.cz> |
| 63 | # Peter Maydell <peter.maydell@linaro.org> |
| 64 | # Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com> |
| 65 | # Randy Dunlap <rdunlap@infradead.org> |
| 66 | # Richard Kennedy <richard@rsk.demon.co.uk> |
| 67 | # Rich Walker <rw@shadow.org.uk> |
| 68 | # Rolf Eike Beer <eike-kernel@sf-tec.de> |
| 69 | # Sakari Ailus <sakari.ailus@linux.intel.com> |
| 70 | # Silvio Fricke <silvio.fricke@gmail.com> |
| 71 | # Simon Huggins |
| 72 | # Tim Waugh <twaugh@redhat.com> |
| 73 | # Tomasz Warniełło <tomasz.warniello@gmail.com> |
| 74 | # Utkarsh Tripathi <utripathi2002@gmail.com> |
| 75 | # valdis.kletnieks@vt.edu <valdis.kletnieks@vt.edu> |
| 76 | # Vegard Nossum <vegard.nossum@oracle.com> |
| 77 | # Will Deacon <will.deacon@arm.com> |
| 78 | # Yacine Belkadi <yacine.belkadi.1@gmail.com> |
| 79 | # Yujie Liu <yujie.liu@intel.com> |
| 80 | |
| 81 | """ |
| 82 | kernel_doc |
| 83 | ========== |
| 84 | |
| 85 | Print formatted kernel documentation to stdout |
| 86 | |
| 87 | Read C language source or header FILEs, extract embedded |
| 88 | documentation comments, and print formatted documentation |
| 89 | to standard output. |
| 90 | |
| 91 | The documentation comments are identified by the "/**" |
| 92 | opening comment mark. |
| 93 | |
| 94 | See Documentation/doc-guide/kernel-doc.rst for the |
| 95 | documentation comment syntax. |
| 96 | """ |
| 97 | |
| 98 | import argparse |
| 99 | import logging |
| 100 | import os |
| 101 | import sys |
| 102 | |
| 103 | # Import Python modules |
| 104 | |
| 105 | LIB_DIR = "lib/kdoc" |
| 106 | SRC_DIR = os.path.dirname(os.path.realpath(__file__)) |
| 107 | |
| 108 | sys.path.insert(0, os.path.join(SRC_DIR, LIB_DIR)) |
| 109 | |
| 110 | from kdoc_files import KernelFiles # pylint: disable=C0413 |
| 111 | from kdoc_output import RestFormat, ManFormat # pylint: disable=C0413 |
| 112 | |
| 113 | DESC = """ |
| 114 | Read C language source or header FILEs, extract embedded documentation comments, |
| 115 | and print formatted documentation to standard output. |
| 116 | |
| 117 | The documentation comments are identified by the "/**" opening comment mark. |
| 118 | |
| 119 | See Documentation/doc-guide/kernel-doc.rst for the documentation comment syntax. |
| 120 | """ |
| 121 | |
| 122 | EXPORT_FILE_DESC = """ |
| 123 | Specify an additional FILE in which to look for EXPORT_SYMBOL information. |
| 124 | |
| 125 | May be used multiple times. |
| 126 | """ |
| 127 | |
| 128 | EXPORT_DESC = """ |
| 129 | Only output documentation for the symbols that have been |
| 130 | exported using EXPORT_SYMBOL() and related macros in any input |
| 131 | FILE or -export-file FILE. |
| 132 | """ |
| 133 | |
| 134 | INTERNAL_DESC = """ |
| 135 | Only output documentation for the symbols that have NOT been |
| 136 | exported using EXPORT_SYMBOL() and related macros in any input |
| 137 | FILE or -export-file FILE. |
| 138 | """ |
| 139 | |
| 140 | FUNCTION_DESC = """ |
| 141 | Only output documentation for the given function or DOC: section |
| 142 | title. All other functions and DOC: sections are ignored. |
| 143 | |
| 144 | May be used multiple times. |
| 145 | """ |
| 146 | |
| 147 | NOSYMBOL_DESC = """ |
| 148 | Exclude the specified symbol from the output documentation. |
| 149 | |
| 150 | May be used multiple times. |
| 151 | """ |
| 152 | |
| 153 | FILES_DESC = """ |
| 154 | Header and C source files to be parsed. |
| 155 | """ |
| 156 | |
| 157 | WARN_CONTENTS_BEFORE_SECTIONS_DESC = """ |
| 158 | Warns if there are contents before sections (deprecated). |
| 159 | |
| 160 | This option is kept just for backward-compatibility, but it does nothing, |
| 161 | neither here nor at the original Perl script. |
| 162 | """ |
| 163 | |
| 164 | |
| 165 | class MsgFormatter(logging.Formatter): |
| 166 | """Helper class to format warnings on a similar way to kernel-doc.pl""" |
| 167 | |
| 168 | def format(self, record): |
| 169 | record.levelname = record.levelname.capitalize() |
| 170 | return logging.Formatter.format(self, record) |
| 171 | |
| 172 | def main(): |
| 173 | """Main program""" |
| 174 | |
| 175 | parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter, |
| 176 | description=DESC) |
| 177 | |
| 178 | # Normal arguments |
| 179 | |
| 180 | parser.add_argument("-v", "-verbose", "--verbose", action="store_true", |
| 181 | help="Verbose output, more warnings and other information.") |
| 182 | |
| 183 | parser.add_argument("-d", "-debug", "--debug", action="store_true", |
| 184 | help="Enable debug messages") |
| 185 | |
| 186 | parser.add_argument("-M", "-modulename", "--modulename", |
| 187 | default="Kernel API", |
| 188 | help="Allow setting a module name at the output.") |
| 189 | |
| 190 | parser.add_argument("-l", "-enable-lineno", "--enable_lineno", |
| 191 | action="store_true", |
| 192 | help="Enable line number output (only in ReST mode)") |
| 193 | |
| 194 | # Arguments to control the warning behavior |
| 195 | |
| 196 | parser.add_argument("-Wreturn", "--wreturn", action="store_true", |
| 197 | help="Warns about the lack of a return markup on functions.") |
| 198 | |
| 199 | parser.add_argument("-Wshort-desc", "-Wshort-description", "--wshort-desc", |
| 200 | action="store_true", |
| 201 | help="Warns if initial short description is missing") |
| 202 | |
| 203 | parser.add_argument("-Wcontents-before-sections", |
| 204 | "--wcontents-before-sections", action="store_true", |
| 205 | help=WARN_CONTENTS_BEFORE_SECTIONS_DESC) |
| 206 | |
| 207 | parser.add_argument("-Wall", "--wall", action="store_true", |
| 208 | help="Enable all types of warnings") |
| 209 | |
| 210 | parser.add_argument("-Werror", "--werror", action="store_true", |
| 211 | help="Treat warnings as errors.") |
| 212 | |
| 213 | parser.add_argument("-export-file", "--export-file", action='append', |
| 214 | help=EXPORT_FILE_DESC) |
| 215 | |
| 216 | # Output format mutually-exclusive group |
| 217 | |
| 218 | out_group = parser.add_argument_group("Output format selection (mutually exclusive)") |
| 219 | |
| 220 | out_fmt = out_group.add_mutually_exclusive_group() |
| 221 | |
| 222 | out_fmt.add_argument("-m", "-man", "--man", action="store_true", |
| 223 | help="Output troff manual page format.") |
| 224 | out_fmt.add_argument("-r", "-rst", "--rst", action="store_true", |
| 225 | help="Output reStructuredText format (default).") |
| 226 | out_fmt.add_argument("-N", "-none", "--none", action="store_true", |
| 227 | help="Do not output documentation, only warnings.") |
| 228 | |
| 229 | # Output selection mutually-exclusive group |
| 230 | |
| 231 | sel_group = parser.add_argument_group("Output selection (mutually exclusive)") |
| 232 | sel_mut = sel_group.add_mutually_exclusive_group() |
| 233 | |
| 234 | sel_mut.add_argument("-e", "-export", "--export", action='store_true', |
| 235 | help=EXPORT_DESC) |
| 236 | |
| 237 | sel_mut.add_argument("-i", "-internal", "--internal", action='store_true', |
| 238 | help=INTERNAL_DESC) |
| 239 | |
| 240 | sel_mut.add_argument("-s", "-function", "--symbol", action='append', |
| 241 | help=FUNCTION_DESC) |
| 242 | |
| 243 | # Those are valid for all 3 types of filter |
| 244 | parser.add_argument("-n", "-nosymbol", "--nosymbol", action='append', |
| 245 | help=NOSYMBOL_DESC) |
| 246 | |
| 247 | parser.add_argument("-D", "-no-doc-sections", "--no-doc-sections", |
| 248 | action='store_true', help="Don't outputt DOC sections") |
| 249 | |
| 250 | parser.add_argument("files", metavar="FILE", |
| 251 | nargs="+", help=FILES_DESC) |
| 252 | |
| 253 | args = parser.parse_args() |
| 254 | |
| 255 | if args.wall: |
| 256 | args.wreturn = True |
| 257 | args.wshort_desc = True |
| 258 | args.wcontents_before_sections = True |
| 259 | |
| 260 | logger = logging.getLogger() |
| 261 | |
| 262 | if not args.debug: |
| 263 | logger.setLevel(logging.INFO) |
| 264 | else: |
| 265 | logger.setLevel(logging.DEBUG) |
| 266 | |
| 267 | formatter = MsgFormatter('%(levelname)s: %(message)s') |
| 268 | |
| 269 | handler = logging.StreamHandler() |
| 270 | handler.setFormatter(formatter) |
| 271 | |
| 272 | logger.addHandler(handler) |
| 273 | |
| 274 | python_ver = sys.version_info[:2] |
| 275 | if python_ver < (3,6): |
| 276 | logger.warning("Python 3.6 or later is required by kernel-doc") |
| 277 | |
| 278 | # Return 0 here to avoid breaking compilation |
| 279 | sys.exit(0) |
| 280 | |
| 281 | if python_ver < (3,7): |
| 282 | logger.warning("Python 3.7 or later is required for correct results") |
| 283 | |
| 284 | if args.man: |
| 285 | out_style = ManFormat(modulename=args.modulename) |
| 286 | elif args.none: |
| 287 | out_style = None |
| 288 | else: |
| 289 | out_style = RestFormat() |
| 290 | |
| 291 | kfiles = KernelFiles(verbose=args.verbose, |
| 292 | out_style=out_style, werror=args.werror, |
| 293 | wreturn=args.wreturn, wshort_desc=args.wshort_desc, |
| 294 | wcontents_before_sections=args.wcontents_before_sections) |
| 295 | |
| 296 | kfiles.parse(args.files, export_file=args.export_file) |
| 297 | |
| 298 | for t in kfiles.msg(enable_lineno=args.enable_lineno, export=args.export, |
| 299 | internal=args.internal, symbol=args.symbol, |
| 300 | nosymbol=args.nosymbol, export_file=args.export_file, |
| 301 | no_doc_sections=args.no_doc_sections): |
| 302 | msg = t[1] |
| 303 | if msg: |
| 304 | print(msg) |
| 305 | |
| 306 | error_count = kfiles.errors |
| 307 | if not error_count: |
| 308 | sys.exit(0) |
| 309 | |
| 310 | if args.werror: |
| 311 | print(f"{error_count} warnings as errors") |
| 312 | sys.exit(error_count) |
| 313 | |
| 314 | if args.verbose: |
| 315 | print(f"{error_count} errors") |
| 316 | |
| 317 | if args.none: |
| 318 | sys.exit(0) |
| 319 | |
| 320 | sys.exit(error_count) |
| 321 | |
| 322 | |
| 323 | # Call main method |
| 324 | if __name__ == "__main__": |
| 325 | main() |