GNU Linux-libre 6.9-gnu
[releases.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 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-abi", KernelCmd)
53     return dict(
54         version = __version__
55         , parallel_read_safe = True
56         , parallel_write_safe = True
57     )
58
59 class KernelCmd(Directive):
60
61     u"""KernelABI (``kernel-abi``) 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         "rst"       : directives.unchanged
71     }
72
73     def run(self):
74         doc = self.state.document
75         if not doc.settings.file_insertion_enabled:
76             raise self.warning("docutils: file insertion disabled")
77
78         srctree = os.path.abspath(os.environ["srctree"])
79
80         args = [
81             os.path.join(srctree, 'scripts/get_abi.pl'),
82             'rest',
83             '--enable-lineno',
84             '--dir', os.path.join(srctree, 'Documentation', self.arguments[0]),
85         ]
86
87         if 'rst' in self.options:
88             args.append('--rst-source')
89
90         lines = subprocess.check_output(args, cwd=os.path.dirname(doc.current_source)).decode('utf-8')
91         nodeList = self.nestedParse(lines, self.arguments[0])
92         return nodeList
93
94     def nestedParse(self, lines, fname):
95         env = self.state.document.settings.env
96         content = ViewList()
97         node = nodes.section()
98
99         if "debug" in self.options:
100             code_block = "\n\n.. code-block:: rst\n    :linenos:\n"
101             for l in lines.split("\n"):
102                 code_block += "\n    " + l
103             lines = code_block + "\n\n"
104
105         line_regex = re.compile(r"^\.\. LINENO (\S+)\#([0-9]+)$")
106         ln = 0
107         n = 0
108         f = fname
109
110         for line in lines.split("\n"):
111             n = n + 1
112             match = line_regex.search(line)
113             if match:
114                 new_f = match.group(1)
115
116                 # Sphinx parser is lazy: it stops parsing contents in the
117                 # middle, if it is too big. So, handle it per input file
118                 if new_f != f and content:
119                     self.do_parse(content, node)
120                     content = ViewList()
121
122                     # Add the file to Sphinx build dependencies
123                     env.note_dependency(os.path.abspath(f))
124
125                 f = new_f
126
127                 # sphinx counts lines from 0
128                 ln = int(match.group(2)) - 1
129             else:
130                 content.append(line, f, ln)
131
132         kernellog.info(self.state.document.settings.env.app, "%s: parsed %i lines" % (fname, n))
133
134         if content:
135             self.do_parse(content, node)
136
137         return node.children
138
139     def do_parse(self, content, node):
140         with switch_source_input(self.state, content):
141             self.state.nested_parse(content, 0, node, match_titles=1)