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.pl 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 "rst" : directives.unchanged 71 } 72 73 def run(self): 74 doc = self.state.document 75 if not doc.settings.file_insertion_enabled: 76 raise self.warning("docutils: file insertion disabled") 77 78 srctree = os.path.abspath(os.environ["srctree"]) 79 80 args = [ 81 os.path.join(srctree, 'scripts/get_abi.pl'), 82 'rest', 83 '--enable-lineno', 84 '--dir', os.path.join(srctree, 'Documentation', self.arguments[0]), 85 ] 86 87 if 'rst' in self.options: 88 args.append('--rst-source') 89 90 lines = subprocess.check_output(args, cwd=os.path.dirname(doc.current_source)).decode('utf-8') 91 nodeList = self.nestedParse(lines, self.arguments[0]) 92 return nodeList 93 94 def nestedParse(self, lines, fname): 95 env = self.state.document.settings.env 96 content = ViewList() 97 node = nodes.section() 98 99 if "debug" in self.options: 100 code_block = "\n\n.. code-block:: rst\n :linenos:\n" 101 for line in lines.split("\n"): 102 code_block += "\n " + line 103 lines = code_block + "\n\n" 104 105 line_regex = re.compile(r"^\.\. LINENO (\S+)\#([0-9]+)$") 106 ln = 0 107 n = 0 108 f = fname 109 110 for line in lines.split("\n"): 111 n = n + 1 112 match = line_regex.search(line) 113 if match: 114 new_f = match.group(1) 115 116 # Sphinx parser is lazy: it stops parsing contents in the 117 # middle, if it is too big. So, handle it per input file 118 if new_f != f and content: 119 self.do_parse(content, node) 120 content = ViewList() 121 122 # Add the file to Sphinx build dependencies 123 env.note_dependency(os.path.abspath(f)) 124 125 f = new_f 126 127 # sphinx counts lines from 0 128 ln = int(match.group(2)) - 1 129 else: 130 content.append(line, f, ln) 131 132 self.logger.info("%s: parsed %i lines" % (fname, n)) 133 134 if content: 135 self.do_parse(content, node) 136 137 return node.children 138 139 def do_parse(self, content, node): 140 with switch_source_input(self.state, content): 141 self.state.nested_parse(content, 0, node, match_titles=1) 142