Commit | Line | Data |
---|---|---|
9ca876f9 | 1 | # -*- coding: utf-8; mode: python -*- |
823830d4 | 2 | # coding=utf-8 |
9ca876f9 | 3 | # SPDX-License-Identifier: GPL-2.0 |
823830d4 | 4 | # |
9ca876f9 MCC |
5 | u""" |
6 | kernel-abi | |
7 | ~~~~~~~~~~ | |
8 | ||
9 | Implementation of the ``kernel-abi`` reST-directive. | |
10 | ||
11 | :copyright: Copyright (C) 2016 Markus Heiser | |
12 | :copyright: Copyright (C) 2016-2020 Mauro Carvalho Chehab | |
13 | :maintained-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org> | |
14 | :license: GPL Version 2, June 1991 see Linux/COPYING for details. | |
15 | ||
16 | The ``kernel-abi`` (:py:class:`KernelCmd`) directive calls the | |
17 | scripts/get_abi.pl script to parse the Kernel ABI files. | |
18 | ||
19 | Overview of directive's argument and options. | |
20 | ||
21 | .. code-block:: rst | |
22 | ||
23 | .. kernel-abi:: <ABI directory location> | |
24 | :debug: | |
25 | ||
26 | The argument ``<ABI directory location>`` is required. It contains the | |
27 | location of the ABI files to be parsed. | |
28 | ||
29 | ``debug`` | |
30 | Inserts a code-block with the *raw* reST. Sometimes it is helpful to see | |
31 | what reST is generated. | |
32 | ||
33 | """ | |
34 | ||
823830d4 | 35 | import codecs |
9ca876f9 | 36 | import os |
9ca876f9 | 37 | import subprocess |
c830fa9a | 38 | import sys |
997b7c8b | 39 | import re |
3c543d29 | 40 | import kernellog |
9ca876f9 | 41 | |
c830fa9a | 42 | from os import path |
9ca876f9 | 43 | |
c830fa9a | 44 | from docutils import nodes, statemachine |
9ca876f9 | 45 | from docutils.statemachine import ViewList |
c830fa9a | 46 | from docutils.parsers.rst import directives, Directive |
9ca876f9 | 47 | from docutils.utils.error_reporting import ErrorString |
f546ff0c | 48 | from sphinx.util.docutils import switch_source_input |
9ca876f9 MCC |
49 | |
50 | __version__ = '1.0' | |
51 | ||
9ca876f9 MCC |
52 | def setup(app): |
53 | ||
54 | app.add_directive("kernel-abi", KernelCmd) | |
55 | return dict( | |
56 | version = __version__ | |
57 | , parallel_read_safe = True | |
58 | , parallel_write_safe = True | |
59 | ) | |
60 | ||
61 | class KernelCmd(Directive): | |
62 | ||
63 | u"""KernelABI (``kernel-abi``) directive""" | |
64 | ||
65 | required_arguments = 1 | |
642514df | 66 | optional_arguments = 2 |
9ca876f9 MCC |
67 | has_content = False |
68 | final_argument_whitespace = True | |
69 | ||
70 | option_spec = { | |
642514df MCC |
71 | "debug" : directives.flag, |
72 | "rst" : directives.unchanged | |
9ca876f9 MCC |
73 | } |
74 | ||
9ca876f9 MCC |
75 | def run(self): |
76 | ||
77 | doc = self.state.document | |
78 | if not doc.settings.file_insertion_enabled: | |
79 | raise self.warning("docutils: file insertion disabled") | |
80 | ||
81 | env = doc.settings.env | |
82 | cwd = path.dirname(doc.current_source) | |
997b7c8b | 83 | cmd = "get_abi.pl rest --enable-lineno --dir " |
9ca876f9 MCC |
84 | cmd += self.arguments[0] |
85 | ||
642514df MCC |
86 | if 'rst' in self.options: |
87 | cmd += " --rst-source" | |
88 | ||
9ca876f9 MCC |
89 | srctree = path.abspath(os.environ["srctree"]) |
90 | ||
91 | fname = cmd | |
92 | ||
93 | # extend PATH with $(srctree)/scripts | |
94 | path_env = os.pathsep.join([ | |
95 | srctree + os.sep + "scripts", | |
96 | os.environ["PATH"] | |
97 | ]) | |
98 | shell_env = os.environ.copy() | |
99 | shell_env["PATH"] = path_env | |
100 | shell_env["srctree"] = srctree | |
101 | ||
102 | lines = self.runCmd(cmd, shell=True, cwd=cwd, env=shell_env) | |
3c543d29 | 103 | nodeList = self.nestedParse(lines, self.arguments[0]) |
9ca876f9 MCC |
104 | return nodeList |
105 | ||
106 | def runCmd(self, cmd, **kwargs): | |
32211146 | 107 | u"""Run command ``cmd`` and return its stdout as unicode.""" |
9ca876f9 MCC |
108 | |
109 | try: | |
110 | proc = subprocess.Popen( | |
111 | cmd | |
112 | , stdout = subprocess.PIPE | |
113 | , stderr = subprocess.PIPE | |
9ca876f9 MCC |
114 | , **kwargs |
115 | ) | |
116 | out, err = proc.communicate() | |
823830d4 MCC |
117 | |
118 | out, err = codecs.decode(out, 'utf-8'), codecs.decode(err, 'utf-8') | |
119 | ||
9ca876f9 MCC |
120 | if proc.returncode != 0: |
121 | raise self.severe( | |
122 | u"command '%s' failed with return code %d" | |
123 | % (cmd, proc.returncode) | |
124 | ) | |
125 | except OSError as exc: | |
126 | raise self.severe(u"problems with '%s' directive: %s." | |
127 | % (self.name, ErrorString(exc))) | |
823830d4 | 128 | return out |
9ca876f9 | 129 | |
3c543d29 | 130 | def nestedParse(self, lines, fname): |
b4541803 | 131 | env = self.state.document.settings.env |
9ca876f9 | 132 | content = ViewList() |
3c543d29 | 133 | node = nodes.section() |
9ca876f9 MCC |
134 | |
135 | if "debug" in self.options: | |
136 | code_block = "\n\n.. code-block:: rst\n :linenos:\n" | |
137 | for l in lines.split("\n"): | |
138 | code_block += "\n " + l | |
139 | lines = code_block + "\n\n" | |
140 | ||
92b6de17 | 141 | line_regex = re.compile("^\.\. LINENO (\S+)\#([0-9]+)$") |
997b7c8b | 142 | ln = 0 |
3c543d29 MCC |
143 | n = 0 |
144 | f = fname | |
997b7c8b MCC |
145 | |
146 | for line in lines.split("\n"): | |
3c543d29 | 147 | n = n + 1 |
997b7c8b MCC |
148 | match = line_regex.search(line) |
149 | if match: | |
3c543d29 MCC |
150 | new_f = match.group(1) |
151 | ||
152 | # Sphinx parser is lazy: it stops parsing contents in the | |
153 | # middle, if it is too big. So, handle it per input file | |
154 | if new_f != f and content: | |
155 | self.do_parse(content, node) | |
156 | content = ViewList() | |
157 | ||
b4541803 MCC |
158 | # Add the file to Sphinx build dependencies |
159 | env.note_dependency(os.path.abspath(f)) | |
160 | ||
3c543d29 MCC |
161 | f = new_f |
162 | ||
997b7c8b MCC |
163 | # sphinx counts lines from 0 |
164 | ln = int(match.group(2)) - 1 | |
165 | else: | |
166 | content.append(line, f, ln) | |
9ca876f9 | 167 | |
3c543d29 MCC |
168 | kernellog.info(self.state.document.settings.env.app, "%s: parsed %i lines" % (fname, n)) |
169 | ||
170 | if content: | |
171 | self.do_parse(content, node) | |
c830fa9a | 172 | |
3c543d29 MCC |
173 | return node.children |
174 | ||
175 | def do_parse(self, content, node): | |
f546ff0c JC |
176 | with switch_source_input(self.state, content): |
177 | self.state.nested_parse(content, 0, node, match_titles=1) |