Merge tag 'for-5.18/block-2022-04-01' of git://git.kernel.dk/linux-block
[linux-2.6-microblaze.git] / Documentation / sphinx / kernel_feat.py
1 # coding=utf-8
2 # SPDX-License-Identifier: GPL-2.0
3 #
4 u"""
5     kernel-feat
6     ~~~~~~~~~~~
7
8     Implementation of the ``kernel-feat`` reST-directive.
9
10     :copyright:  Copyright (C) 2016  Markus Heiser
11     :copyright:  Copyright (C) 2016-2019  Mauro Carvalho Chehab
12     :maintained-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
13     :license:    GPL Version 2, June 1991 see Linux/COPYING for details.
14
15     The ``kernel-feat`` (:py:class:`KernelFeat`) directive calls the
16     scripts/get_feat.pl script to parse the Kernel ABI files.
17
18     Overview of directive's argument and options.
19
20     .. code-block:: rst
21
22         .. kernel-feat:: <ABI directory location>
23             :debug:
24
25     The argument ``<ABI directory location>`` is required. It contains the
26     location of the ABI files to be parsed.
27
28     ``debug``
29       Inserts a code-block with the *raw* reST. Sometimes it is helpful to see
30       what reST is generated.
31
32 """
33
34 import codecs
35 import os
36 import re
37 import subprocess
38 import sys
39
40 from os import path
41
42 from docutils import nodes, statemachine
43 from docutils.statemachine import ViewList
44 from docutils.parsers.rst import directives, Directive
45 from docutils.utils.error_reporting import ErrorString
46 from sphinx.util.docutils import switch_source_input
47
48 __version__  = '1.0'
49
50 def setup(app):
51
52     app.add_directive("kernel-feat", KernelFeat)
53     return dict(
54         version = __version__
55         , parallel_read_safe = True
56         , parallel_write_safe = True
57     )
58
59 class KernelFeat(Directive):
60
61     u"""KernelFeat (``kernel-feat``) directive"""
62
63     required_arguments = 1
64     optional_arguments = 2
65     has_content = False
66     final_argument_whitespace = True
67
68     option_spec = {
69         "debug"     : directives.flag
70     }
71
72     def warn(self, message, **replace):
73         replace["fname"]   = self.state.document.current_source
74         replace["line_no"] = replace.get("line_no", self.lineno)
75         message = ("%(fname)s:%(line_no)s: [kernel-feat WARN] : " + message) % replace
76         self.state.document.settings.env.app.warn(message, prefix="")
77
78     def run(self):
79
80         doc = self.state.document
81         if not doc.settings.file_insertion_enabled:
82             raise self.warning("docutils: file insertion disabled")
83
84         env = doc.settings.env
85         cwd = path.dirname(doc.current_source)
86         cmd = "get_feat.pl rest --enable-fname --dir "
87         cmd += self.arguments[0]
88
89         if len(self.arguments) > 1:
90             cmd += " --arch " + self.arguments[1]
91
92         srctree = path.abspath(os.environ["srctree"])
93
94         fname = cmd
95
96         # extend PATH with $(srctree)/scripts
97         path_env = os.pathsep.join([
98             srctree + os.sep + "scripts",
99             os.environ["PATH"]
100         ])
101         shell_env = os.environ.copy()
102         shell_env["PATH"]    = path_env
103         shell_env["srctree"] = srctree
104
105         lines = self.runCmd(cmd, shell=True, cwd=cwd, env=shell_env)
106
107         line_regex = re.compile("^\.\. FILE (\S+)$")
108
109         out_lines = ""
110
111         for line in lines.split("\n"):
112             match = line_regex.search(line)
113             if match:
114                 fname = match.group(1)
115
116                 # Add the file to Sphinx build dependencies
117                 env.note_dependency(os.path.abspath(fname))
118             else:
119                 out_lines += line + "\n"
120
121         nodeList = self.nestedParse(out_lines, fname)
122         return nodeList
123
124     def runCmd(self, cmd, **kwargs):
125         u"""Run command ``cmd`` and return its stdout as unicode."""
126
127         try:
128             proc = subprocess.Popen(
129                 cmd
130                 , stdout = subprocess.PIPE
131                 , stderr = subprocess.PIPE
132                 , **kwargs
133             )
134             out, err = proc.communicate()
135
136             out, err = codecs.decode(out, 'utf-8'), codecs.decode(err, 'utf-8')
137
138             if proc.returncode != 0:
139                 raise self.severe(
140                     u"command '%s' failed with return code %d"
141                     % (cmd, proc.returncode)
142                 )
143         except OSError as exc:
144             raise self.severe(u"problems with '%s' directive: %s."
145                               % (self.name, ErrorString(exc)))
146         return out
147
148     def nestedParse(self, lines, fname):
149         content = ViewList()
150         node    = nodes.section()
151
152         if "debug" in self.options:
153             code_block = "\n\n.. code-block:: rst\n    :linenos:\n"
154             for l in lines.split("\n"):
155                 code_block += "\n    " + l
156             lines = code_block + "\n\n"
157
158         for c, l in enumerate(lines.split("\n")):
159             content.append(l, fname, c)
160
161         buf  = self.state.memo.title_styles, self.state.memo.section_level, self.state.memo.reporter
162
163         with switch_source_input(self.state, content):
164             self.state.nested_parse(content, 0, node, match_titles=1)
165
166         return node.children