blob: f3da859c9878e09ec4da011a852c8644752bfc91 [file] [log] [blame]
Mauro Carvalho Chehab9ca876f2020-10-30 08:40:31 +01001# -*- coding: utf-8; mode: python -*-
Mauro Carvalho Chehab823830d2020-10-30 08:40:32 +01002# coding=utf-8
Mauro Carvalho Chehab9ca876f2020-10-30 08:40:31 +01003# SPDX-License-Identifier: GPL-2.0
Mauro Carvalho Chehab823830d2020-10-30 08:40:32 +01004#
Mauro Carvalho Chehab9ca876f2020-10-30 08:40:31 +01005u"""
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
Mauro Carvalho Chehab823830d2020-10-30 08:40:32 +010035import codecs
Mauro Carvalho Chehab9ca876f2020-10-30 08:40:31 +010036import os
Mauro Carvalho Chehab9ca876f2020-10-30 08:40:31 +010037import subprocess
Mauro Carvalho Chehabc830fa92020-10-30 08:40:33 +010038import sys
Mauro Carvalho Chehab997b7c82020-10-30 08:40:34 +010039import re
Mauro Carvalho Chehab3c543d22020-10-30 08:40:35 +010040import kernellog
Mauro Carvalho Chehab9ca876f2020-10-30 08:40:31 +010041
Mauro Carvalho Chehabc830fa92020-10-30 08:40:33 +010042from os import path
Mauro Carvalho Chehab9ca876f2020-10-30 08:40:31 +010043
Mauro Carvalho Chehabc830fa92020-10-30 08:40:33 +010044from docutils import nodes, statemachine
Mauro Carvalho Chehab9ca876f2020-10-30 08:40:31 +010045from docutils.statemachine import ViewList
Mauro Carvalho Chehabc830fa92020-10-30 08:40:33 +010046from docutils.parsers.rst import directives, Directive
Mauro Carvalho Chehab9ca876f2020-10-30 08:40:31 +010047from docutils.utils.error_reporting import ErrorString
48
Mauro Carvalho Chehabc830fa92020-10-30 08:40:33 +010049#
50# AutodocReporter is only good up to Sphinx 1.7
51#
52import sphinx
53
54Use_SSI = sphinx.__version__[:3] >= '1.7'
55if Use_SSI:
56 from sphinx.util.docutils import switch_source_input
57else:
58 from sphinx.ext.autodoc import AutodocReporter
Mauro Carvalho Chehab9ca876f2020-10-30 08:40:31 +010059
60__version__ = '1.0'
61
Mauro Carvalho Chehab9ca876f2020-10-30 08:40:31 +010062def setup(app):
63
64 app.add_directive("kernel-abi", KernelCmd)
65 return dict(
66 version = __version__
67 , parallel_read_safe = True
68 , parallel_write_safe = True
69 )
70
71class KernelCmd(Directive):
72
73 u"""KernelABI (``kernel-abi``) directive"""
74
75 required_arguments = 1
Mauro Carvalho Chehab642514d2020-10-30 08:40:41 +010076 optional_arguments = 2
Mauro Carvalho Chehab9ca876f2020-10-30 08:40:31 +010077 has_content = False
78 final_argument_whitespace = True
79
80 option_spec = {
Mauro Carvalho Chehab642514d2020-10-30 08:40:41 +010081 "debug" : directives.flag,
82 "rst" : directives.unchanged
Mauro Carvalho Chehab9ca876f2020-10-30 08:40:31 +010083 }
84
Mauro Carvalho Chehab9ca876f2020-10-30 08:40:31 +010085 def run(self):
86
87 doc = self.state.document
88 if not doc.settings.file_insertion_enabled:
89 raise self.warning("docutils: file insertion disabled")
90
91 env = doc.settings.env
92 cwd = path.dirname(doc.current_source)
Mauro Carvalho Chehab997b7c82020-10-30 08:40:34 +010093 cmd = "get_abi.pl rest --enable-lineno --dir "
Mauro Carvalho Chehab9ca876f2020-10-30 08:40:31 +010094 cmd += self.arguments[0]
95
Mauro Carvalho Chehab642514d2020-10-30 08:40:41 +010096 if 'rst' in self.options:
97 cmd += " --rst-source"
98
Mauro Carvalho Chehab9ca876f2020-10-30 08:40:31 +010099 srctree = path.abspath(os.environ["srctree"])
100
101 fname = cmd
102
103 # extend PATH with $(srctree)/scripts
104 path_env = os.pathsep.join([
105 srctree + os.sep + "scripts",
106 os.environ["PATH"]
107 ])
108 shell_env = os.environ.copy()
109 shell_env["PATH"] = path_env
110 shell_env["srctree"] = srctree
111
112 lines = self.runCmd(cmd, shell=True, cwd=cwd, env=shell_env)
Mauro Carvalho Chehab3c543d22020-10-30 08:40:35 +0100113 nodeList = self.nestedParse(lines, self.arguments[0])
Mauro Carvalho Chehab9ca876f2020-10-30 08:40:31 +0100114 return nodeList
115
116 def runCmd(self, cmd, **kwargs):
117 u"""Run command ``cmd`` and return it's stdout as unicode."""
118
119 try:
120 proc = subprocess.Popen(
121 cmd
122 , stdout = subprocess.PIPE
123 , stderr = subprocess.PIPE
Mauro Carvalho Chehab9ca876f2020-10-30 08:40:31 +0100124 , **kwargs
125 )
126 out, err = proc.communicate()
Mauro Carvalho Chehab823830d2020-10-30 08:40:32 +0100127
128 out, err = codecs.decode(out, 'utf-8'), codecs.decode(err, 'utf-8')
129
Mauro Carvalho Chehab9ca876f2020-10-30 08:40:31 +0100130 if proc.returncode != 0:
131 raise self.severe(
132 u"command '%s' failed with return code %d"
133 % (cmd, proc.returncode)
134 )
135 except OSError as exc:
136 raise self.severe(u"problems with '%s' directive: %s."
137 % (self.name, ErrorString(exc)))
Mauro Carvalho Chehab823830d2020-10-30 08:40:32 +0100138 return out
Mauro Carvalho Chehab9ca876f2020-10-30 08:40:31 +0100139
Mauro Carvalho Chehab3c543d22020-10-30 08:40:35 +0100140 def nestedParse(self, lines, fname):
Mauro Carvalho Chehab9ca876f2020-10-30 08:40:31 +0100141 content = ViewList()
Mauro Carvalho Chehab3c543d22020-10-30 08:40:35 +0100142 node = nodes.section()
Mauro Carvalho Chehab9ca876f2020-10-30 08:40:31 +0100143
144 if "debug" in self.options:
145 code_block = "\n\n.. code-block:: rst\n :linenos:\n"
146 for l in lines.split("\n"):
147 code_block += "\n " + l
148 lines = code_block + "\n\n"
149
Mauro Carvalho Chehab997b7c82020-10-30 08:40:34 +0100150 line_regex = re.compile("^#define LINENO (\S+)\#([0-9]+)$")
151 ln = 0
Mauro Carvalho Chehab3c543d22020-10-30 08:40:35 +0100152 n = 0
153 f = fname
Mauro Carvalho Chehab997b7c82020-10-30 08:40:34 +0100154
155 for line in lines.split("\n"):
Mauro Carvalho Chehab3c543d22020-10-30 08:40:35 +0100156 n = n + 1
Mauro Carvalho Chehab997b7c82020-10-30 08:40:34 +0100157 match = line_regex.search(line)
158 if match:
Mauro Carvalho Chehab3c543d22020-10-30 08:40:35 +0100159 new_f = match.group(1)
160
161 # Sphinx parser is lazy: it stops parsing contents in the
162 # middle, if it is too big. So, handle it per input file
163 if new_f != f and content:
164 self.do_parse(content, node)
165 content = ViewList()
166
167 f = new_f
168
Mauro Carvalho Chehab997b7c82020-10-30 08:40:34 +0100169 # sphinx counts lines from 0
170 ln = int(match.group(2)) - 1
171 else:
172 content.append(line, f, ln)
Mauro Carvalho Chehab9ca876f2020-10-30 08:40:31 +0100173
Mauro Carvalho Chehab3c543d22020-10-30 08:40:35 +0100174 kernellog.info(self.state.document.settings.env.app, "%s: parsed %i lines" % (fname, n))
Mauro Carvalho Chehabc830fa92020-10-30 08:40:33 +0100175
Mauro Carvalho Chehab3c543d22020-10-30 08:40:35 +0100176 if content:
177 self.do_parse(content, node)
178
179 return node.children
180
181 def do_parse(self, content, node):
Mauro Carvalho Chehabc830fa92020-10-30 08:40:33 +0100182 if Use_SSI:
183 with switch_source_input(self.state, content):
184 self.state.nested_parse(content, 0, node, match_titles=1)
185 else:
Mauro Carvalho Chehab3c543d22020-10-30 08:40:35 +0100186 buf = self.state.memo.title_styles, self.state.memo.section_level, self.state.memo.reporter
187
Mauro Carvalho Chehabc830fa92020-10-30 08:40:33 +0100188 self.state.memo.title_styles = []
189 self.state.memo.section_level = 0
190 self.state.memo.reporter = AutodocReporter(content, self.state.memo.reporter)
191 try:
192 self.state.nested_parse(content, 0, node, match_titles=1)
193 finally:
194 self.state.memo.title_styles, self.state.memo.section_level, self.state.memo.reporter = buf