Back to home page

OSCL-LXR

 
 

    


0001 # -*- coding: utf-8; mode: python -*-
0002 # coding=utf-8
0003 # SPDX-License-Identifier: GPL-2.0
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         # extend PATH with $(srctree)/scripts
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                 # Sphinx parser is lazy: it stops parsing contents in the
0153                 # middle, if it is too big. So, handle it per input file
0154                 if new_f != f and content:
0155                     self.do_parse(content, node)
0156                     content = ViewList()
0157 
0158                     # Add the file to Sphinx build dependencies
0159                     env.note_dependency(os.path.abspath(f))
0160 
0161                 f = new_f
0162 
0163                 # sphinx counts lines from 0
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)