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 45from sphinx.util.docutils import switch_source_input 46 47__version__ = '1.0' 48 49def setup(app): 50 51 app.add_directive("kernel-feat", KernelFeat) 52 return dict( 53 version = __version__ 54 , parallel_read_safe = True 55 , parallel_write_safe = True 56 ) 57 58class KernelFeat(Directive): 59 60 u"""KernelFeat (``kernel-feat``) directive""" 61 62 required_arguments = 1 63 optional_arguments = 2 64 has_content = False 65 final_argument_whitespace = True 66 67 option_spec = { 68 "debug" : directives.flag 69 } 70 71 def warn(self, message, **replace): 72 replace["fname"] = self.state.document.current_source 73 replace["line_no"] = replace.get("line_no", self.lineno) 74 message = ("%(fname)s:%(line_no)s: [kernel-feat WARN] : " + message) % replace 75 self.state.document.settings.env.app.warn(message, prefix="") 76 77 def run(self): 78 79 doc = self.state.document 80 if not doc.settings.file_insertion_enabled: 81 raise self.warning("docutils: file insertion disabled") 82 83 env = doc.settings.env 84 cwd = path.dirname(doc.current_source) 85 cmd = "get_feat.pl rest --dir " 86 cmd += self.arguments[0] 87 88 if len(self.arguments) > 1: 89 cmd += " --arch " + self.arguments[1] 90 91 srctree = path.abspath(os.environ["srctree"]) 92 93 fname = cmd 94 95 # extend PATH with $(srctree)/scripts 96 path_env = os.pathsep.join([ 97 srctree + os.sep + "scripts", 98 os.environ["PATH"] 99 ]) 100 shell_env = os.environ.copy() 101 shell_env["PATH"] = path_env 102 shell_env["srctree"] = srctree 103 104 lines = self.runCmd(cmd, shell=True, cwd=cwd, env=shell_env) 105 nodeList = self.nestedParse(lines, fname) 106 return nodeList 107 108 def runCmd(self, cmd, **kwargs): 109 u"""Run command ``cmd`` and return it's stdout as unicode.""" 110 111 try: 112 proc = subprocess.Popen( 113 cmd 114 , stdout = subprocess.PIPE 115 , stderr = subprocess.PIPE 116 , **kwargs 117 ) 118 out, err = proc.communicate() 119 120 out, err = codecs.decode(out, 'utf-8'), codecs.decode(err, 'utf-8') 121 122 if proc.returncode != 0: 123 raise self.severe( 124 u"command '%s' failed with return code %d" 125 % (cmd, proc.returncode) 126 ) 127 except OSError as exc: 128 raise self.severe(u"problems with '%s' directive: %s." 129 % (self.name, ErrorString(exc))) 130 return out 131 132 def nestedParse(self, lines, fname): 133 content = ViewList() 134 node = nodes.section() 135 136 if "debug" in self.options: 137 code_block = "\n\n.. code-block:: rst\n :linenos:\n" 138 for l in lines.split("\n"): 139 code_block += "\n " + l 140 lines = code_block + "\n\n" 141 142 for c, l in enumerate(lines.split("\n")): 143 content.append(l, fname, c) 144 145 buf = self.state.memo.title_styles, self.state.memo.section_level, self.state.memo.reporter 146 147 with switch_source_input(self.state, content): 148 self.state.nested_parse(content, 0, node, match_titles=1) 149 150 return node.children 151