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