xref: /linux/Documentation/sphinx/kernel_feat.py (revision caa642bf3b767c4b33a62c6e2c2708fafb88ea6c)
1# coding=utf-8
2# SPDX-License-Identifier: GPL-2.0
3#
4"""
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    tools/docs/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
34import codecs
35import os
36import re
37import subprocess
38import sys
39
40from docutils import nodes, statemachine
41from docutils.statemachine import ViewList
42from docutils.parsers.rst import directives, Directive
43from sphinx.util.docutils import switch_source_input
44
45srctree = os.path.abspath(os.environ["srctree"])
46sys.path.insert(0, os.path.join(srctree, "tools/docs/lib"))
47
48from parse_features import ParseFeature                # pylint: disable=C0413
49
50def ErrorString(exc):  # Shamelessly stolen from docutils
51    return f'{exc.__class__.__name}: {exc}'
52
53__version__  = '1.0'
54
55def setup(app):
56
57    app.add_directive("kernel-feat", KernelFeat)
58    return dict(
59        version = __version__
60        , parallel_read_safe = True
61        , parallel_write_safe = True
62    )
63
64class KernelFeat(Directive):
65
66    """KernelFeat (``kernel-feat``) directive"""
67
68    required_arguments = 1
69    optional_arguments = 2
70    has_content = False
71    final_argument_whitespace = True
72
73    option_spec = {
74        "debug"     : directives.flag
75    }
76
77    def warn(self, message, **replace):
78        replace["fname"]   = self.state.document.current_source
79        replace["line_no"] = replace.get("line_no", self.lineno)
80        message = ("%(fname)s:%(line_no)s: [kernel-feat WARN] : " + message) % replace
81        self.state.document.settings.env.app.warn(message, prefix="")
82
83    def run(self):
84        doc = self.state.document
85        if not doc.settings.file_insertion_enabled:
86            raise self.warning("docutils: file insertion disabled")
87
88        env = doc.settings.env
89
90        srctree = os.path.abspath(os.environ["srctree"])
91
92        args = [
93            os.path.join(srctree, 'tools/docs/get_feat.pl'),
94            'rest',
95            '--enable-fname',
96            '--dir',
97            os.path.join(srctree, 'Documentation', self.arguments[0]),
98        ]
99
100        if len(self.arguments) > 1:
101            args.extend(['--arch', self.arguments[1]])
102
103        lines = subprocess.check_output(args, cwd=os.path.dirname(doc.current_source)).decode('utf-8')
104
105        line_regex = re.compile(r"^\.\. FILE (\S+)$")
106
107        out_lines = ""
108
109        for line in lines.split("\n"):
110            match = line_regex.search(line)
111            if match:
112                fname = match.group(1)
113
114                # Add the file to Sphinx build dependencies
115                env.note_dependency(os.path.abspath(fname))
116            else:
117                out_lines += line + "\n"
118
119        nodeList = self.nestedParse(out_lines, self.arguments[0])
120        return nodeList
121
122    def nestedParse(self, lines, fname):
123        content = ViewList()
124        node    = nodes.section()
125
126        if "debug" in self.options:
127            code_block = "\n\n.. code-block:: rst\n    :linenos:\n"
128            for l in lines.split("\n"):
129                code_block += "\n    " + l
130            lines = code_block + "\n\n"
131
132        for c, l in enumerate(lines.split("\n")):
133            content.append(l, fname, c)
134
135        buf  = self.state.memo.title_styles, self.state.memo.section_level, self.state.memo.reporter
136
137        with switch_source_input(self.state, content):
138            self.state.nested_parse(content, 0, node, match_titles=1)
139
140        return node.children
141