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 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 52 53def setup(app): 54 55 app.add_directive("kernel-abi", KernelCmd) 56 return { 57 "version": __version__, 58 "parallel_read_safe": True, 59 "parallel_write_safe": True 60 } 61 62 63class KernelCmd(Directive): 64 u"""KernelABI (``kernel-abi``) directive""" 65 66 required_arguments = 1 67 optional_arguments = 2 68 has_content = False 69 final_argument_whitespace = True 70 logger = logging.getLogger('kernel_abi') 71 72 option_spec = { 73 "debug": directives.flag, 74 } 75 76 def run(self): 77 doc = self.state.document 78 if not doc.settings.file_insertion_enabled: 79 raise self.warning("docutils: file insertion disabled") 80 81 path = os.path.join(srctree, "Documentation", self.arguments[0]) 82 parser = AbiParser(path, logger=self.logger) 83 parser.parse_abi() 84 parser.check_issues() 85 86 msg = "" 87 for m in parser.doc(enable_lineno=True, show_file=True): 88 msg += m 89 90 node = self.nested_parse(msg, self.arguments[0]) 91 return node 92 93 def nested_parse(self, lines, fname): 94 env = self.state.document.settings.env 95 content = ViewList() 96 node = nodes.section() 97 98 if "debug" in self.options: 99 code_block = "\n\n.. code-block:: rst\n :linenos:\n" 100 for line in lines.split("\n"): 101 code_block += "\n " + line 102 lines = code_block + "\n\n" 103 104 line_regex = re.compile(r"^\.\. LINENO (\S+)\#([0-9]+)$") 105 ln = 0 106 n = 0 107 f = fname 108 109 for line in lines.split("\n"): 110 n = n + 1 111 match = line_regex.search(line) 112 if match: 113 new_f = match.group(1) 114 115 # Sphinx parser is lazy: it stops parsing contents in the 116 # middle, if it is too big. So, handle it per input file 117 if new_f != f and content: 118 self.do_parse(content, node) 119 content = ViewList() 120 121 # Add the file to Sphinx build dependencies 122 env.note_dependency(os.path.abspath(f)) 123 124 f = new_f 125 126 # sphinx counts lines from 0 127 ln = int(match.group(2)) - 1 128 else: 129 content.append(line, f, ln) 130 131 self.logger.info("%s: parsed %i lines" % (fname, n)) 132 133 if content: 134 self.do_parse(content, node) 135 136 return node.children 137 138 def do_parse(self, content, node): 139 with switch_source_input(self.state, content): 140 self.state.nested_parse(content, 0, node, match_titles=1) 141