Merge branch 'net-enhancements-to-sk_user_data-field'
[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 from sphinx.util.docutils import switch_source_input
49
50 __version__  = '1.0'
51
52 def setup(app):
53
54     app.add_directive("kernel-abi", KernelCmd)
55     return dict(
56         version = __version__
57         , parallel_read_safe = True
58         , parallel_write_safe = True
59     )
60
61 class KernelCmd(Directive):
62
63     u"""KernelABI (``kernel-abi``) directive"""
64
65     required_arguments = 1
66     optional_arguments = 2
67     has_content = False
68     final_argument_whitespace = True
69
70     option_spec = {
71         "debug"     : directives.flag,
72         "rst"       : directives.unchanged
73     }
74
75     def run(self):
76
77         doc = self.state.document
78         if not doc.settings.file_insertion_enabled:
79             raise self.warning("docutils: file insertion disabled")
80
81         env = doc.settings.env
82         cwd = path.dirname(doc.current_source)
83         cmd = "get_abi.pl rest --enable-lineno --dir "
84         cmd += self.arguments[0]
85
86         if 'rst' in self.options:
87             cmd += " --rst-source"
88
89         srctree = path.abspath(os.environ["srctree"])
90
91         fname = cmd
92
93         # extend PATH with $(srctree)/scripts
94         path_env = os.pathsep.join([
95             srctree + os.sep + "scripts",
96             os.environ["PATH"]
97         ])
98         shell_env = os.environ.copy()
99         shell_env["PATH"]    = path_env
100         shell_env["srctree"] = srctree
101
102         lines = self.runCmd(cmd, shell=True, cwd=cwd, env=shell_env)
103         nodeList = self.nestedParse(lines, self.arguments[0])
104         return nodeList
105
106     def runCmd(self, cmd, **kwargs):
107         u"""Run command ``cmd`` and return its stdout as unicode."""
108
109         try:
110             proc = subprocess.Popen(
111                 cmd
112                 , stdout = subprocess.PIPE
113                 , stderr = subprocess.PIPE
114                 , **kwargs
115             )
116             out, err = proc.communicate()
117
118             out, err = codecs.decode(out, 'utf-8'), codecs.decode(err, 'utf-8')
119
120             if proc.returncode != 0:
121                 raise self.severe(
122                     u"command '%s' failed with return code %d"
123                     % (cmd, proc.returncode)
124                 )
125         except OSError as exc:
126             raise self.severe(u"problems with '%s' directive: %s."
127                               % (self.name, ErrorString(exc)))
128         return out
129
130     def nestedParse(self, lines, fname):
131         env = self.state.document.settings.env
132         content = ViewList()
133         node = nodes.section()
134
135         if "debug" in self.options:
136             code_block = "\n\n.. code-block:: rst\n    :linenos:\n"
137             for l in lines.split("\n"):
138                 code_block += "\n    " + l
139             lines = code_block + "\n\n"
140
141         line_regex = re.compile("^\.\. LINENO (\S+)\#([0-9]+)$")
142         ln = 0
143         n = 0
144         f = fname
145
146         for line in lines.split("\n"):
147             n = n + 1
148             match = line_regex.search(line)
149             if match:
150                 new_f = match.group(1)
151
152                 # Sphinx parser is lazy: it stops parsing contents in the
153                 # middle, if it is too big. So, handle it per input file
154                 if new_f != f and content:
155                     self.do_parse(content, node)
156                     content = ViewList()
157
158                     # Add the file to Sphinx build dependencies
159                     env.note_dependency(os.path.abspath(f))
160
161                 f = new_f
162
163                 # sphinx counts lines from 0
164                 ln = int(match.group(2)) - 1
165             else:
166                 content.append(line, f, ln)
167
168         kernellog.info(self.state.document.settings.env.app, "%s: parsed %i lines" % (fname, n))
169
170         if content:
171             self.do_parse(content, node)
172
173         return node.children
174
175     def do_parse(self, content, node):
176         with switch_source_input(self.state, content):
177             self.state.nested_parse(content, 0, node, match_titles=1)