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