Commit | Line | Data |
---|---|---|
b9721225 MCC |
1 | # coding=utf-8 |
2 | # SPDX-License-Identifier: GPL-2.0 | |
3 | # | |
4 | u""" | |
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 | ||
34 | import codecs | |
35 | import os | |
85999f03 | 36 | import re |
b9721225 MCC |
37 | import subprocess |
38 | import sys | |
39 | ||
40 | from os import path | |
41 | ||
42 | from docutils import nodes, statemachine | |
43 | from docutils.statemachine import ViewList | |
44 | from docutils.parsers.rst import directives, Directive | |
45 | from docutils.utils.error_reporting import ErrorString | |
f546ff0c | 46 | from sphinx.util.docutils import switch_source_input |
b9721225 MCC |
47 | |
48 | __version__ = '1.0' | |
49 | ||
50 | def 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 | ||
59 | class 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) | |
85999f03 | 86 | cmd = "get_feat.pl rest --enable-fname --dir " |
b9721225 MCC |
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) | |
85999f03 MCC |
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) | |
b9721225 MCC |
122 | return nodeList |
123 | ||
124 | def runCmd(self, cmd, **kwargs): | |
32211146 | 125 | u"""Run command ``cmd`` and return its stdout as unicode.""" |
b9721225 MCC |
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 | ||
f546ff0c JC |
163 | with switch_source_input(self.state, content): |
164 | self.state.nested_parse(content, 0, node, match_titles=1) | |
b9721225 MCC |
165 | |
166 | return node.children |