[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date: Mon, 6 Jun 2016 18:32:24 +0200
From: Markus Heiser <markus.heiser@...marIT.de>
To: corbet@....net
Cc: jani.nikula@...el.com, daniel.vetter@...ll.ch,
grant.likely@...retlab.ca, mchehab@....samsung.com,
keithp@...thp.com, linux-kernel@...r.kernel.org,
linux-doc@...r.kernel.org, hverkuil@...all.nl,
"Heiser, Markus" <markus.heiser@...marIT.de>
Subject: [PATCH 6/7] kernel-doc directive: initial implementation
From: "Heiser, Markus" <markus.heiser@...marIT.de>
Implements the reST directive specified at
books/kernel-doc-HOWTO/kernel-doc-directive.rst [1]
Below is a short overview of the options (further reading [1]):
.. kernel-doc:: <filename>
:doc: <section title>
:export:
:internal:
:functions: <function [, functions [, ...]]>
:module: <prefix-id>
:snippets: <snippet [, snippets [, ...]]>
:language: <snippet-lang>
:linenos:
:debug:
The initial implementation was taken from the sphkerneldoc project [2]
[1] http://return42.github.io/sphkerneldoc/books/kernel-doc-HOWTO/kernel-doc-directive.html
[2] http://return42.github.io/sphkerneldoc
Signed-off-by: Markus Heiser <markus.heiser@...marIT.de>
---
scripts/site-python/linuxdoc/rstKernelDoc.py | 369 +++++++++++++++++++++++++++
1 file changed, 369 insertions(+)
create mode 100755 scripts/site-python/linuxdoc/rstKernelDoc.py
diff --git a/scripts/site-python/linuxdoc/rstKernelDoc.py b/scripts/site-python/linuxdoc/rstKernelDoc.py
new file mode 100755
index 0000000..8246a57
--- /dev/null
+++ b/scripts/site-python/linuxdoc/rstKernelDoc.py
@@ -0,0 +1,369 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8; mode: python -*-
+# pylint: disable=C0330, R0903
+
+u"""
+ rstKernelDoc
+ ~~~~~~~~~~~~
+
+ Implementation of the ``kernel-doc`` reST-directive.
+
+ :copyright: Copyright (C) 2016 Markus Heiser
+ :license: GPL V3.0, see LICENSE for details.
+
+ The ``kernel-doc`` (:py:class:`KernelDoc`) directive includes contens from
+ linux kernel source code comments.
+
+ Options:
+
+ * ``:doc: <section title>`` inserts the contents of the ``DOC:`` section
+ titled ``<section title>`` from ``<filename>``. Spaces are allowed in
+ ``<section title>``; do not quote the ``<section title>``.
+
+ * ``:export:`` inserts the documentation in ``<filename>`` of functions /
+ structs or whatever are exported using EXPORT_SYMBOL (``EXPORT_SYMBOL()``,
+ ``EXPORT_SYMBOL_GPL()`` & ``EXPORT_SYMBOL_GPL_FUTURE()``).
+
+ * ``:internal:`` is replaced by the documentation of functions, structs,
+ titles etc. that are documented, but not **not** exported using
+ EXPORT_SYMBOL.
+
+ * ``:functions: <function [, functions [, ...]]>`` is replaced by the
+ documentation of function, struct or whatever object/title is documented
+ <filename>.
+
+ * The option ``:module: <prefix-id>`` sets a module-name, this name is used
+ as prefix for automatic generated IDs (reference anchors).
+
+ The following example shows how to insert documention from the source file
+ ``/drivers/gpu/drm/drm_drv.c``. In this example the documention from the
+ ``DOC:`` section with the title "driver instance overview" and the
+ documentation of all exported symbols (EXPORT_SYMBOL) is included in the
+ reST tree.
+
+ .. code-block:: rst
+
+ .. kernel-doc:: drivers/gpu/drm/drm_drv.c
+ :export:
+ :doc: driver instance overview
+
+ An other example is to use only one function description.
+
+ .. kernel-doc:: include/media/i2c/tvp7002.h
+ :functions: tvp7002_config
+ :module: tvp7002
+
+ This will produce the follwing reST markup to include:
+
+ .. code-block:: rst
+
+ .. _`tvp514x.tvp514x_platform_data`:
+
+ struct tvp514x_platform_data
+ ============================
+
+ .. c:type:: tvp514x_platform_data
+
+
+ .. _`tvp514x.tvp514x_platform_data.definition`:
+
+ Definition
+ ----------
+
+ .. code-block:: c
+
+ struct tvp514x_platform_data {
+ bool clk_polarity;
+ bool hs_polarity;
+ bool vs_polarity;
+ }
+
+ .. _`tvp514x.tvp514x_platform_data.members`:
+
+ Members
+ -------
+
+ clk_polarity
+ Clock polarity of the current interface.
+
+ hs_polarity
+ HSYNC Polarity configuration for current interface.
+
+ vs_polarity
+ VSYNC Polarity configuration for current interface.
+
+ The last example illustrates, that the option ``:module: tvp514x`` is used
+ as a prefix for anchors. E.g. ```ref:`tvp514x.tvp514x_platform_data.members¸```
+ refers to the to the member description of ``struct tvp514x_platform_data``.
+
+
+"""
+
+# ==============================================================================
+# common globals
+# ==============================================================================
+
+# The version numbering follows numbering of the specification
+# (Documentation/books/kernel-doc-HOWTO).
+__version__ = '1.0'
+
+# ==============================================================================
+# imports
+# ==============================================================================
+
+from os import path
+from io import StringIO
+from docutils import nodes
+from docutils.parsers.rst import Directive, directives
+from docutils.utils import SystemMessage
+from docutils.statemachine import ViewList
+from sphinx.util.nodes import nested_parse_with_titles
+
+import kernel_doc as kerneldoc
+
+# ==============================================================================
+def setup(app):
+# ==============================================================================
+
+ app.add_config_value('kernel_doc_raise_error', False, 'env')
+ app.add_directive("kernel-doc", KernelDoc)
+
+# ==============================================================================
+class KernelDocParser(kerneldoc.Parser):
+# ==============================================================================
+
+ def __init__(self, app, *args, **kwargs):
+ super(KernelDocParser, self).__init__(*args, **kwargs)
+ self.app = app
+
+ # -------------------------------------------------
+ # bind the parser logging to the sphinx application
+ # -------------------------------------------------
+
+ def error(self, message, **replace):
+ self.errors += 1
+ self.app.warn(
+ message % replace
+ , location = "%s:%s [kernel-doc ERROR]" % (self.options.fname, self.ctx.line_no)
+ , prefix = "" )
+
+ def warn(self, message, **replace):
+ self.app.warn(
+ message % replace
+ , location = "%s:%s [kernel-doc WARN]" % (self.options.fname, self.ctx.line_no)
+ , prefix = "")
+
+ def info(self, message, **replace):
+ self.app.verbose(
+ "%s:%s: [kernel-doc INFO]: " %(self.options.fname, self.ctx.line_no)
+ + message % replace)
+
+ def debug(self, message, **replace):
+ if self.app.verbosity < 2:
+ return
+ replace.update(dict(fname=self.options.fname, line_no=self.ctx.line_no, logclass = "DEBUG"))
+ message = "%(fname)s:%(line_no)s [kernel-doc %(logclass)s] : " + message
+ self.app.debug(message, **replace)
+
+
+# ==============================================================================
+class KernelDoc(Directive):
+# ==============================================================================
+
+ u"""KernelDoc (``kernel-doc``) directive"""
+
+ required_arguments = 1
+ optional_arguments = 0
+ final_argument_whitespace = True
+
+ option_spec = {
+ "doc" : directives.unchanged_required # aka lines containing !P
+ , "export" : directives.flag # aka lines containing !E
+ , "internal" : directives.flag # aka lines containing !I
+ , "functions" : directives.unchanged_required # aka lines containing !F
+
+ , "debug" : directives.flag # insert generated reST as code-block
+
+ , "snippets" : directives.unchanged_required
+ , "language" : directives.unchanged_required
+ , "linenos" : directives.flag
+
+ # not yet supported:
+ #
+ # !C<filename> is replaced by nothing, but makes the tools check that
+ # all DOC: sections and documented functions, symbols, etc. are used.
+ # This makes sense to use when you use !F/!P only and want to verify
+ # that all documentation is included.
+ #
+ #, "check" : directives.flag # aka lines containing !C
+
+ # module name / used as id-prefix
+ , "module" : directives.unchanged_required
+
+ # The encoding of the source file with the kernel-doc comments. The
+ # default is the config.source_encoding from sphinx configuration and
+ # this default is utf-8-sig
+ , "encoding" : directives.encoding
+
+ }
+
+
+ def getOopsEntry(self, msg):
+ retVal = ("\n\n.. todo::"
+ "\n\n Oops: Document generation inconsistency."
+ "\n\n The template for this document tried to insert"
+ " structured comment at this point, but an error occoured."
+ " This dummy section is inserted to allow generation to continue.::"
+ "\n\n")
+
+ for l in msg.split("\n"):
+ retVal += " " + l + "\n"
+ retVal += "\n\n"
+ return retVal
+
+ def errMsg(self, msg, lev=4):
+ err = self.state_machine.reporter.severe(
+ msg
+ , nodes.literal_block(self.block_text, self.block_text)
+ , line=self.lineno )
+ return SystemMessage(err, lev)
+
+ def run(self):
+ doc = self.state.document
+ env = doc.settings.env
+
+ retVal = []
+ try:
+ retVal = self._run(doc, env)
+ except SystemMessage, exc:
+ if env.config.kernel_doc_raise_error:
+ raise
+ self.state_machine.insert_input(
+ self.getOopsEntry(unicode(exc)).split("\n")
+ , self.arguments[0])
+
+ finally:
+ pass
+ return retVal
+
+ def _run(self, document, env):
+
+ # do some checks
+
+ if not document.settings.file_insertion_enabled:
+ raise self.errMsg('File insertion disabled')
+
+ fname = self.arguments[0]
+ src_tree = kerneldoc.SRCTREE
+
+ if self.arguments[0].startswith("./"):
+ # the prefix "./" indicates a relative pathname
+ fname = self.arguments[0][2:]
+ src_tree = path.dirname(path.normpath(document.current_source))
+
+ if "internal" in self.options:
+ if "export" in self.options:
+ raise self.errMsg(
+ "Options 'export' and 'internal' are orthogonal,"
+ " can't use them togehter")
+
+ if "snippets" in self.options:
+ rest = set(self.options.keys()) - set(["snippets", "linenos", "language", "debug"])
+ if rest:
+ raise self.errMsg(
+ "kernel-doc 'snippets' has non of these options: %s"
+ % ",".join(rest))
+
+ # set parse adjustments
+
+ env.note_dependency(fname)
+ rstout = StringIO()
+ ctx = kerneldoc.ParserContext()
+ opts = kerneldoc.ParseOptions(
+ fname = fname
+ , src_tree = src_tree
+ , id_prefix = self.options.get("module", "").strip()
+ , out = rstout
+ , encoding = self.options.get("encoding", env.config.source_encoding)
+ , translator = kerneldoc.ReSTTranslator()
+ ,)
+
+ opts.set_defaults()
+ if not path.exists(opts.fname):
+ raise self.errMsg(
+ "kernel-doc refers to nonexisting document %s" % fname)
+
+ if self.options:
+ opts.skip_preamble = True
+ opts.skip_epilog = True
+
+ if "snippets" in self.options:
+ opts.translator = kerneldoc.ReSTTranslator()
+
+ if "doc" in self.options:
+ opts.use_names.append(self.options.get("doc"))
+
+ if "export" in self.options:
+ # gather exported symbols and add them to the list of names
+ kerneldoc.Parser.gather_context(kerneldoc.readFile(opts.fname), ctx)
+ opts.use_names.extend(ctx.exported_symbols)
+ opts.error_missing = False
+
+ if "internal" in self.options:
+ # gather exported symbols and add them to the ignore-list of names
+ kerneldoc.Parser.gather_context(kerneldoc.readFile(opts.fname), ctx)
+ opts.skip_names.extend(ctx.exported_symbols)
+
+ if "functions" in self.options:
+ opts.error_missing = True
+ opts.use_names.extend(
+ self.options["functions"].replace(","," ").split())
+
+ parser = KernelDocParser(env.app, opts)
+ env.app.info("parse kernel-doc comments from: %s" % fname)
+ parser.parse()
+
+ lines = rstout.getvalue().split("\n")
+
+ if "functions" in self.options:
+ selected = self.options["functions"].replace(","," ").split()
+ names = parser.ctx.translated_names
+ not_found = [ s for s in selected if s not in names]
+ if not_found:
+ raise self.errMsg(
+ "selected section(s) not found: %s" % ", ".join(not_found))
+
+ if "snippets" in self.options:
+ selected = self.options["snippets"].replace(","," ").split()
+ names = parser.ctx.snippets.keys()
+ not_found = [ s for s in selected if s not in names]
+ if not_found:
+ raise self.errMsg(
+ "selected snippets(s) not found: %s" % ", ".join(not_found))
+
+ lines = ["", ".. code-block:: %s"
+ % self.options.get("language", "c"), ]
+ if "linenos" in self.options:
+ lines.append(" :linenos:")
+ lines.append("")
+
+ while selected:
+ snippet = parser.ctx.snippets[selected.pop(0)].split("\n")
+ lines.extend([" " + l for l in snippet])
+ if selected:
+ # delemit snippets with two newlines
+ lines.extend(["",""])
+
+ if "debug" in self.options:
+ code_block = "\n.. code-block:: rst\n :linenos:\n\n".split("\n")
+ for l in lines:
+ code_block.append(" " + l)
+ lines = code_block
+
+ content = ViewList(lines)
+ node = nodes.section()
+ node.document = self.state.document
+ nested_parse_with_titles(self.state, content, node)
+
+ return node.children
+
--
v4.7-rc-2
Powered by blists - more mailing lists