xref: /linux/Documentation/sphinx/kernel_abi.py (revision 5d7871d77f6d62406b3d459a58810c1ddb8904c2)
1# -*- coding: utf-8; mode: python -*-
2# coding=utf-8
3# SPDX-License-Identifier: GPL-2.0
4#
5u"""
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.py 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
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
43from sphinx.util import logging
44
45srctree = os.path.abspath(os.environ["srctree"])
46sys.path.insert(0, os.path.join(srctree, "scripts/lib/abi"))
47
48from abi_parser import AbiParser
49
50__version__ = "1.0"
51
52logger = logging.getLogger('kernel_abi')
53path = os.path.join(srctree, "Documentation/ABI")
54
55# Parse ABI symbols only once
56kernel_abi = AbiParser(path, logger=logger)
57kernel_abi.parse_abi()
58kernel_abi.check_issues()
59
60def setup(app):
61
62    app.add_directive("kernel-abi", KernelCmd)
63    return {
64        "version": __version__,
65        "parallel_read_safe": True,
66        "parallel_write_safe": True
67    }
68
69
70class KernelCmd(Directive):
71    u"""KernelABI (``kernel-abi``) directive"""
72
73    required_arguments = 1
74    optional_arguments = 3
75    has_content = False
76    final_argument_whitespace = True
77    parser = None
78
79    option_spec = {
80        "debug": directives.flag,
81        "no-symbols": directives.flag,
82        "no-files":  directives.flag,
83    }
84
85    def run(self):
86        doc = self.state.document
87        if not doc.settings.file_insertion_enabled:
88            raise self.warning("docutils: file insertion disabled")
89
90        env = self.state.document.settings.env
91        content = ViewList()
92        node = nodes.section()
93
94        abi_type = self.arguments[0]
95
96        if "no-symbols" in self.options:
97            show_symbols = False
98        else:
99            show_symbols = True
100
101        if "no-files" in self.options:
102            show_file = False
103        else:
104            show_file = True
105
106        tab_width = self.options.get('tab-width',
107                                     self.state.document.settings.tab_width)
108
109        old_f = None
110        n = 0
111        n_sym = 0
112        for msg, f, ln in kernel_abi.doc(show_file=show_file,
113                                            show_symbols=show_symbols,
114                                            filter_path=abi_type):
115            n_sym += 1
116            msg_list = statemachine.string2lines(msg, tab_width,
117                                                 convert_whitespace=True)
118            if "debug" in self.options:
119                lines = [
120                    "", "",  ".. code-block:: rst",
121                    "    :linenos:", ""
122                ]
123                for m in msg_list:
124                    lines.append("    " + m)
125            else:
126                lines = msg_list
127
128            for line in lines:
129                # sphinx counts lines from 0
130                content.append(line, f, ln - 1)
131                n += 1
132
133            if f != old_f:
134                # Add the file to Sphinx build dependencies
135                env.note_dependency(os.path.abspath(f))
136
137                old_f = f
138
139            # Sphinx doesn't like to parse big messages. So, let's
140            # add content symbol by symbol
141            if content:
142                self.do_parse(content, node)
143                content = ViewList()
144
145        if show_symbols and not show_file:
146            logger.verbose("%s ABI: %i symbols (%i ReST lines)" % (abi_type, n_sym, n))
147        elif not show_symbols and show_file:
148            logger.verbose("%s ABI: %i files (%i ReST lines)" % (abi_type, n_sym, n))
149        else:
150            logger.verbose("%s ABI: %i data (%i ReST lines)" % (abi_type, n_sym, n))
151
152        return node.children
153
154    def do_parse(self, content, node):
155        with switch_source_input(self.state, content):
156            self.state.nested_parse(content, 0, node, match_titles=1)
157