Merge tag 'ntb-5.11' of git://github.com/jonmason/ntb
[linux-2.6-microblaze.git] / Documentation / sphinx / kernel_abi.py
1 # -*- coding: utf-8; mode: python -*-
2 # coding=utf-8
3 # SPDX-License-Identifier: GPL-2.0
4 #
5 u"""
6     kernel-abi
7     ~~~~~~~~~~
8
9     Implementation of the ``kernel-abi`` reST-directive.
10
11     :copyright:  Copyright (C) 2016  Markus Heiser
12     :copyright:  Copyright (C) 2016-2020  Mauro Carvalho Chehab
13     :maintained-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
14     :license:    GPL Version 2, June 1991 see Linux/COPYING for details.
15
16     The ``kernel-abi`` (:py:class:`KernelCmd`) directive calls the
17     scripts/get_abi.pl script to parse the Kernel ABI files.
18
19     Overview of directive's argument and options.
20
21     .. code-block:: rst
22
23         .. kernel-abi:: <ABI directory location>
24             :debug:
25
26     The argument ``<ABI directory location>`` is required. It contains the
27     location of the ABI files to be parsed.
28
29     ``debug``
30       Inserts a code-block with the *raw* reST. Sometimes it is helpful to see
31       what reST is generated.
32
33 """
34
35 import codecs
36 import os
37 import subprocess
38 import sys
39 import re
40 import kernellog
41
42 from os import path
43
44 from docutils import nodes, statemachine
45 from docutils.statemachine import ViewList
46 from docutils.parsers.rst import directives, Directive
47 from docutils.utils.error_reporting import ErrorString
48
49 #
50 # AutodocReporter is only good up to Sphinx 1.7
51 #
52 import sphinx
53
54 Use_SSI = sphinx.__version__[:3] >= '1.7'
55 if Use_SSI:
56     from sphinx.util.docutils import switch_source_input
57 else:
58     from sphinx.ext.autodoc import AutodocReporter
59
60 __version__  = '1.0'
61
62 def setup(app):
63
64     app.add_directive("kernel-abi", KernelCmd)
65     return dict(
66         version = __version__
67         , parallel_read_safe = True
68         , parallel_write_safe = True
69     )
70
71 class KernelCmd(Directive):
72
73     u"""KernelABI (``kernel-abi``) directive"""
74
75     required_arguments = 1
76     optional_arguments = 2
77     has_content = False
78     final_argument_whitespace = True
79
80     option_spec = {
81         "debug"     : directives.flag,
82         "rst"       : directives.unchanged
83     }
84
85     def run(self):
86
87         doc = self.state.document
88         if not doc.settings.file_insertion_enabled:
89             raise self.warning("docutils: file insertion disabled")
90
91         env = doc.settings.env
92         cwd = path.dirname(doc.current_source)
93         cmd = "get_abi.pl rest --enable-lineno --dir "
94         cmd += self.arguments[0]
95
96         if 'rst' in self.options:
97             cmd += " --rst-source"
98
99         srctree = path.abspath(os.environ["srctree"])
100
101         fname = cmd
102
103         # extend PATH with $(srctree)/scripts
104         path_env = os.pathsep.join([
105             srctree + os.sep + "scripts",
106             os.environ["PATH"]
107         ])
108         shell_env = os.environ.copy()
109         shell_env["PATH"]    = path_env
110         shell_env["srctree"] = srctree
111
112         lines = self.runCmd(cmd, shell=True, cwd=cwd, env=shell_env)
113         nodeList = self.nestedParse(lines, self.arguments[0])
114         return nodeList
115
116     def runCmd(self, cmd, **kwargs):
117         u"""Run command ``cmd`` and return it's stdout as unicode."""
118
119         try:
120             proc = subprocess.Popen(
121                 cmd
122                 , stdout = subprocess.PIPE
123                 , stderr = subprocess.PIPE
124                 , **kwargs
125             )
126             out, err = proc.communicate()
127
128             out, err = codecs.decode(out, 'utf-8'), codecs.decode(err, 'utf-8')
129
130             if proc.returncode != 0:
131                 raise self.severe(
132                     u"command '%s' failed with return code %d"
133                     % (cmd, proc.returncode)
134                 )
135         except OSError as exc:
136             raise self.severe(u"problems with '%s' directive: %s."
137                               % (self.name, ErrorString(exc)))
138         return out
139
140     def nestedParse(self, lines, fname):
141         content = ViewList()
142         node = nodes.section()
143
144         if "debug" in self.options:
145             code_block = "\n\n.. code-block:: rst\n    :linenos:\n"
146             for l in lines.split("\n"):
147                 code_block += "\n    " + l
148             lines = code_block + "\n\n"
149
150         line_regex = re.compile("^#define LINENO (\S+)\#([0-9]+)$")
151         ln = 0
152         n = 0
153         f = fname
154
155         for line in lines.split("\n"):
156             n = n + 1
157             match = line_regex.search(line)
158             if match:
159                 new_f = match.group(1)
160
161                 # Sphinx parser is lazy: it stops parsing contents in the
162                 # middle, if it is too big. So, handle it per input file
163                 if new_f != f and content:
164                     self.do_parse(content, node)
165                     content = ViewList()
166
167                 f = new_f
168
169                 # sphinx counts lines from 0
170                 ln = int(match.group(2)) - 1
171             else:
172                 content.append(line, f, ln)
173
174         kernellog.info(self.state.document.settings.env.app, "%s: parsed %i lines" % (fname, n))
175
176         if content:
177             self.do_parse(content, node)
178
179         return node.children
180
181     def do_parse(self, content, node):
182         if Use_SSI:
183             with switch_source_input(self.state, content):
184                 self.state.nested_parse(content, 0, node, match_titles=1)
185         else:
186             buf  = self.state.memo.title_styles, self.state.memo.section_level, self.state.memo.reporter
187
188             self.state.memo.title_styles  = []
189             self.state.memo.section_level = 0
190             self.state.memo.reporter      = AutodocReporter(content, self.state.memo.reporter)
191             try:
192                 self.state.nested_parse(content, 0, node, match_titles=1)
193             finally:
194                 self.state.memo.title_styles, self.state.memo.section_level, self.state.memo.reporter = buf