0001
0002
0003
0004
0005 u"""
0006 kernel-abi
0007 ~~~~~~~~~~
0008
0009 Implementation of the ``kernel-abi`` reST-directive.
0010
0011 :copyright: Copyright (C) 2016 Markus Heiser
0012 :copyright: Copyright (C) 2016-2020 Mauro Carvalho Chehab
0013 :maintained-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
0014 :license: GPL Version 2, June 1991 see Linux/COPYING for details.
0015
0016 The ``kernel-abi`` (:py:class:`KernelCmd`) directive calls the
0017 scripts/get_abi.pl script to parse the Kernel ABI files.
0018
0019 Overview of directive's argument and options.
0020
0021 .. code-block:: rst
0022
0023 .. kernel-abi:: <ABI directory location>
0024 :debug:
0025
0026 The argument ``<ABI directory location>`` is required. It contains the
0027 location of the ABI files to be parsed.
0028
0029 ``debug``
0030 Inserts a code-block with the *raw* reST. Sometimes it is helpful to see
0031 what reST is generated.
0032
0033 """
0034
0035 import codecs
0036 import os
0037 import subprocess
0038 import sys
0039 import re
0040 import kernellog
0041
0042 from os import path
0043
0044 from docutils import nodes, statemachine
0045 from docutils.statemachine import ViewList
0046 from docutils.parsers.rst import directives, Directive
0047 from docutils.utils.error_reporting import ErrorString
0048 from sphinx.util.docutils import switch_source_input
0049
0050 __version__ = '1.0'
0051
0052 def setup(app):
0053
0054 app.add_directive("kernel-abi", KernelCmd)
0055 return dict(
0056 version = __version__
0057 , parallel_read_safe = True
0058 , parallel_write_safe = True
0059 )
0060
0061 class KernelCmd(Directive):
0062
0063 u"""KernelABI (``kernel-abi``) directive"""
0064
0065 required_arguments = 1
0066 optional_arguments = 2
0067 has_content = False
0068 final_argument_whitespace = True
0069
0070 option_spec = {
0071 "debug" : directives.flag,
0072 "rst" : directives.unchanged
0073 }
0074
0075 def run(self):
0076
0077 doc = self.state.document
0078 if not doc.settings.file_insertion_enabled:
0079 raise self.warning("docutils: file insertion disabled")
0080
0081 env = doc.settings.env
0082 cwd = path.dirname(doc.current_source)
0083 cmd = "get_abi.pl rest --enable-lineno --dir "
0084 cmd += self.arguments[0]
0085
0086 if 'rst' in self.options:
0087 cmd += " --rst-source"
0088
0089 srctree = path.abspath(os.environ["srctree"])
0090
0091 fname = cmd
0092
0093
0094 path_env = os.pathsep.join([
0095 srctree + os.sep + "scripts",
0096 os.environ["PATH"]
0097 ])
0098 shell_env = os.environ.copy()
0099 shell_env["PATH"] = path_env
0100 shell_env["srctree"] = srctree
0101
0102 lines = self.runCmd(cmd, shell=True, cwd=cwd, env=shell_env)
0103 nodeList = self.nestedParse(lines, self.arguments[0])
0104 return nodeList
0105
0106 def runCmd(self, cmd, **kwargs):
0107 u"""Run command ``cmd`` and return its stdout as unicode."""
0108
0109 try:
0110 proc = subprocess.Popen(
0111 cmd
0112 , stdout = subprocess.PIPE
0113 , stderr = subprocess.PIPE
0114 , **kwargs
0115 )
0116 out, err = proc.communicate()
0117
0118 out, err = codecs.decode(out, 'utf-8'), codecs.decode(err, 'utf-8')
0119
0120 if proc.returncode != 0:
0121 raise self.severe(
0122 u"command '%s' failed with return code %d"
0123 % (cmd, proc.returncode)
0124 )
0125 except OSError as exc:
0126 raise self.severe(u"problems with '%s' directive: %s."
0127 % (self.name, ErrorString(exc)))
0128 return out
0129
0130 def nestedParse(self, lines, fname):
0131 env = self.state.document.settings.env
0132 content = ViewList()
0133 node = nodes.section()
0134
0135 if "debug" in self.options:
0136 code_block = "\n\n.. code-block:: rst\n :linenos:\n"
0137 for l in lines.split("\n"):
0138 code_block += "\n " + l
0139 lines = code_block + "\n\n"
0140
0141 line_regex = re.compile("^\.\. LINENO (\S+)\#([0-9]+)$")
0142 ln = 0
0143 n = 0
0144 f = fname
0145
0146 for line in lines.split("\n"):
0147 n = n + 1
0148 match = line_regex.search(line)
0149 if match:
0150 new_f = match.group(1)
0151
0152
0153
0154 if new_f != f and content:
0155 self.do_parse(content, node)
0156 content = ViewList()
0157
0158
0159 env.note_dependency(os.path.abspath(f))
0160
0161 f = new_f
0162
0163
0164 ln = int(match.group(2)) - 1
0165 else:
0166 content.append(line, f, ln)
0167
0168 kernellog.info(self.state.document.settings.env.app, "%s: parsed %i lines" % (fname, n))
0169
0170 if content:
0171 self.do_parse(content, node)
0172
0173 return node.children
0174
0175 def do_parse(self, content, node):
0176 with switch_source_input(self.state, content):
0177 self.state.nested_parse(content, 0, node, match_titles=1)