xref: /linux/Documentation/sphinx/kernel_feat.py (revision 6093a688a07da07808f0122f9aa2a3eed250d853)
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    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
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
45def ErrorString(exc):  # Shamelessly stolen from docutils
46    return f'{exc.__class__.__name}: {exc}'
47
48__version__  = '1.0'
49
50def 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
59class KernelFeat(Directive):
60
61    """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        doc = self.state.document
80        if not doc.settings.file_insertion_enabled:
81            raise self.warning("docutils: file insertion disabled")
82
83        env = doc.settings.env
84
85        srctree = os.path.abspath(os.environ["srctree"])
86
87        args = [
88            os.path.join(srctree, 'scripts/get_feat.pl'),
89            'rest',
90            '--enable-fname',
91            '--dir',
92            os.path.join(srctree, 'Documentation', self.arguments[0]),
93        ]
94
95        if len(self.arguments) > 1:
96            args.extend(['--arch', self.arguments[1]])
97
98        lines = subprocess.check_output(args, cwd=os.path.dirname(doc.current_source)).decode('utf-8')
99
100        line_regex = re.compile(r"^\.\. FILE (\S+)$")
101
102        out_lines = ""
103
104        for line in lines.split("\n"):
105            match = line_regex.search(line)
106            if match:
107                fname = match.group(1)
108
109                # Add the file to Sphinx build dependencies
110                env.note_dependency(os.path.abspath(fname))
111            else:
112                out_lines += line + "\n"
113
114        nodeList = self.nestedParse(out_lines, self.arguments[0])
115        return nodeList
116
117    def nestedParse(self, lines, fname):
118        content = ViewList()
119        node    = nodes.section()
120
121        if "debug" in self.options:
122            code_block = "\n\n.. code-block:: rst\n    :linenos:\n"
123            for l in lines.split("\n"):
124                code_block += "\n    " + l
125            lines = code_block + "\n\n"
126
127        for c, l in enumerate(lines.split("\n")):
128            content.append(l, fname, c)
129
130        buf  = self.state.memo.title_styles, self.state.memo.section_level, self.state.memo.reporter
131
132        with switch_source_input(self.state, content):
133            self.state.nested_parse(content, 0, node, match_titles=1)
134
135        return node.children
136