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