1# coding=utf-8 2# SPDX-License-Identifier: GPL-2.0 3# 4u""" 5 kernel-feat 6 ~~~~~~~~~~~ 7 8 Implementation of the ``kernel-feat`` reST-directive. 9 10 :copyright: Copyright (C) 2016 Markus Heiser 11 :copyright: Copyright (C) 2016-2019 Mauro Carvalho Chehab 12 :maintained-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org> 13 :license: GPL Version 2, June 1991 see Linux/COPYING for details. 14 15 The ``kernel-feat`` (:py:class:`KernelFeat`) directive calls the 16 scripts/get_feat.pl script to parse the Kernel ABI files. 17 18 Overview of directive's argument and options. 19 20 .. code-block:: rst 21 22 .. kernel-feat:: <ABI directory location> 23 :debug: 24 25 The argument ``<ABI directory location>`` is required. It contains the 26 location of the ABI files to be parsed. 27 28 ``debug`` 29 Inserts a code-block with the *raw* reST. Sometimes it is helpful to see 30 what reST is generated. 31 32""" 33 34import codecs 35import os 36import subprocess 37import sys 38 39from os import path 40 41from docutils import nodes, statemachine 42from docutils.statemachine import ViewList 43from docutils.parsers.rst import directives, Directive 44from docutils.utils.error_reporting import ErrorString 45 46# 47# AutodocReporter is only good up to Sphinx 1.7 48# 49import sphinx 50 51Use_SSI = sphinx.__version__[:3] >= '1.7' 52if Use_SSI: 53 from sphinx.util.docutils import switch_source_input 54else: 55 from sphinx.ext.autodoc import AutodocReporter 56 57__version__ = '1.0' 58 59def setup(app): 60 61 app.add_directive("kernel-feat", KernelFeat) 62 return dict( 63 version = __version__ 64 , parallel_read_safe = True 65 , parallel_write_safe = True 66 ) 67 68class KernelFeat(Directive): 69 70 u"""KernelFeat (``kernel-feat``) directive""" 71 72 required_arguments = 1 73 optional_arguments = 2 74 has_content = False 75 final_argument_whitespace = True 76 77 option_spec = { 78 "debug" : directives.flag 79 } 80 81 def warn(self, message, **replace): 82 replace["fname"] = self.state.document.current_source 83 replace["line_no"] = replace.get("line_no", self.lineno) 84 message = ("%(fname)s:%(line_no)s: [kernel-feat WARN] : " + message) % replace 85 self.state.document.settings.env.app.warn(message, prefix="") 86 87 def run(self): 88 89 doc = self.state.document 90 if not doc.settings.file_insertion_enabled: 91 raise self.warning("docutils: file insertion disabled") 92 93 env = doc.settings.env 94 cwd = path.dirname(doc.current_source) 95 cmd = "get_feat.pl rest --dir " 96 cmd += self.arguments[0] 97 98 if len(self.arguments) > 1: 99 cmd += " --arch " + self.arguments[1] 100 101 srctree = path.abspath(os.environ["srctree"]) 102 103 fname = cmd 104 105 # extend PATH with $(srctree)/scripts 106 path_env = os.pathsep.join([ 107 srctree + os.sep + "scripts", 108 os.environ["PATH"] 109 ]) 110 shell_env = os.environ.copy() 111 shell_env["PATH"] = path_env 112 shell_env["srctree"] = srctree 113 114 lines = self.runCmd(cmd, shell=True, cwd=cwd, env=shell_env) 115 nodeList = self.nestedParse(lines, fname) 116 return nodeList 117 118 def runCmd(self, cmd, **kwargs): 119 u"""Run command ``cmd`` and return it's stdout as unicode.""" 120 121 try: 122 proc = subprocess.Popen( 123 cmd 124 , stdout = subprocess.PIPE 125 , stderr = subprocess.PIPE 126 , **kwargs 127 ) 128 out, err = proc.communicate() 129 130 out, err = codecs.decode(out, 'utf-8'), codecs.decode(err, 'utf-8') 131 132 if proc.returncode != 0: 133 raise self.severe( 134 u"command '%s' failed with return code %d" 135 % (cmd, proc.returncode) 136 ) 137 except OSError as exc: 138 raise self.severe(u"problems with '%s' directive: %s." 139 % (self.name, ErrorString(exc))) 140 return out 141 142 def nestedParse(self, lines, fname): 143 content = ViewList() 144 node = nodes.section() 145 146 if "debug" in self.options: 147 code_block = "\n\n.. code-block:: rst\n :linenos:\n" 148 for l in lines.split("\n"): 149 code_block += "\n " + l 150 lines = code_block + "\n\n" 151 152 for c, l in enumerate(lines.split("\n")): 153 content.append(l, fname, c) 154 155 buf = self.state.memo.title_styles, self.state.memo.section_level, self.state.memo.reporter 156 157 if Use_SSI: 158 with switch_source_input(self.state, content): 159 self.state.nested_parse(content, 0, node, match_titles=1) 160 else: 161 self.state.memo.title_styles = [] 162 self.state.memo.section_level = 0 163 self.state.memo.reporter = AutodocReporter(content, self.state.memo.reporter) 164 try: 165 self.state.nested_parse(content, 0, node, match_titles=1) 166 finally: 167 self.state.memo.title_styles, self.state.memo.section_level, self.state.memo.reporter = buf 168 169 return node.children 170