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