blob: ce5f3b0ae811e91975731752e13f100737db5744 [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
76 optional_arguments = 0
77 has_content = False
78 final_argument_whitespace = True
79
80 option_spec = {
81 "debug" : directives.flag
82 }
83
Mauro Carvalho Chehab9ca876f2020-10-30 08:40:31 +010084 def run(self):
85
86 doc = self.state.document
87 if not doc.settings.file_insertion_enabled:
88 raise self.warning("docutils: file insertion disabled")
89
90 env = doc.settings.env
91 cwd = path.dirname(doc.current_source)
Mauro Carvalho Chehab997b7c82020-10-30 08:40:34 +010092 cmd = "get_abi.pl rest --enable-lineno --dir "
Mauro Carvalho Chehab9ca876f2020-10-30 08:40:31 +010093 cmd += self.arguments[0]
94
95 srctree = path.abspath(os.environ["srctree"])
96
97 fname = cmd
98
99 # extend PATH with $(srctree)/scripts
100 path_env = os.pathsep.join([
101 srctree + os.sep + "scripts",
102 os.environ["PATH"]
103 ])
104 shell_env = os.environ.copy()
105 shell_env["PATH"] = path_env
106 shell_env["srctree"] = srctree
107
108 lines = self.runCmd(cmd, shell=True, cwd=cwd, env=shell_env)
Mauro Carvalho Chehab3c543d22020-10-30 08:40:35 +0100109 nodeList = self.nestedParse(lines, self.arguments[0])
Mauro Carvalho Chehab9ca876f2020-10-30 08:40:31 +0100110 return nodeList
111
112 def runCmd(self, cmd, **kwargs):
113 u"""Run command ``cmd`` and return it's stdout as unicode."""
114
115 try:
116 proc = subprocess.Popen(
117 cmd
118 , stdout = subprocess.PIPE
119 , stderr = subprocess.PIPE
Mauro Carvalho Chehab9ca876f2020-10-30 08:40:31 +0100120 , **kwargs
121 )
122 out, err = proc.communicate()
Mauro Carvalho Chehab823830d2020-10-30 08:40:32 +0100123
124 out, err = codecs.decode(out, 'utf-8'), codecs.decode(err, 'utf-8')
125
Mauro Carvalho Chehab9ca876f2020-10-30 08:40:31 +0100126 if proc.returncode != 0:
127 raise self.severe(
128 u"command '%s' failed with return code %d"
129 % (cmd, proc.returncode)
130 )
131 except OSError as exc:
132 raise self.severe(u"problems with '%s' directive: %s."
133 % (self.name, ErrorString(exc)))
Mauro Carvalho Chehab823830d2020-10-30 08:40:32 +0100134 return out
Mauro Carvalho Chehab9ca876f2020-10-30 08:40:31 +0100135
Mauro Carvalho Chehab3c543d22020-10-30 08:40:35 +0100136 def nestedParse(self, lines, fname):
Mauro Carvalho Chehab9ca876f2020-10-30 08:40:31 +0100137 content = ViewList()
Mauro Carvalho Chehab3c543d22020-10-30 08:40:35 +0100138 node = nodes.section()
Mauro Carvalho Chehab9ca876f2020-10-30 08:40:31 +0100139
140 if "debug" in self.options:
141 code_block = "\n\n.. code-block:: rst\n :linenos:\n"
142 for l in lines.split("\n"):
143 code_block += "\n " + l
144 lines = code_block + "\n\n"
145
Mauro Carvalho Chehab997b7c82020-10-30 08:40:34 +0100146 line_regex = re.compile("^#define LINENO (\S+)\#([0-9]+)$")
147 ln = 0
Mauro Carvalho Chehab3c543d22020-10-30 08:40:35 +0100148 n = 0
149 f = fname
Mauro Carvalho Chehab997b7c82020-10-30 08:40:34 +0100150
151 for line in lines.split("\n"):
Mauro Carvalho Chehab3c543d22020-10-30 08:40:35 +0100152 n = n + 1
Mauro Carvalho Chehab997b7c82020-10-30 08:40:34 +0100153 match = line_regex.search(line)
154 if match:
Mauro Carvalho Chehab3c543d22020-10-30 08:40:35 +0100155 new_f = match.group(1)
156
157 # Sphinx parser is lazy: it stops parsing contents in the
158 # middle, if it is too big. So, handle it per input file
159 if new_f != f and content:
160 self.do_parse(content, node)
161 content = ViewList()
162
163 f = new_f
164
Mauro Carvalho Chehab997b7c82020-10-30 08:40:34 +0100165 # sphinx counts lines from 0
166 ln = int(match.group(2)) - 1
167 else:
168 content.append(line, f, ln)
Mauro Carvalho Chehab9ca876f2020-10-30 08:40:31 +0100169
Mauro Carvalho Chehab3c543d22020-10-30 08:40:35 +0100170 kernellog.info(self.state.document.settings.env.app, "%s: parsed %i lines" % (fname, n))
Mauro Carvalho Chehabc830fa92020-10-30 08:40:33 +0100171
Mauro Carvalho Chehab3c543d22020-10-30 08:40:35 +0100172 if content:
173 self.do_parse(content, node)
174
175 return node.children
176
177 def do_parse(self, content, node):
Mauro Carvalho Chehabc830fa92020-10-30 08:40:33 +0100178 if Use_SSI:
179 with switch_source_input(self.state, content):
180 self.state.nested_parse(content, 0, node, match_titles=1)
181 else:
Mauro Carvalho Chehab3c543d22020-10-30 08:40:35 +0100182 buf = self.state.memo.title_styles, self.state.memo.section_level, self.state.memo.reporter
183
Mauro Carvalho Chehabc830fa92020-10-30 08:40:33 +0100184 self.state.memo.title_styles = []
185 self.state.memo.section_level = 0
186 self.state.memo.reporter = AutodocReporter(content, self.state.memo.reporter)
187 try:
188 self.state.nested_parse(content, 0, node, match_titles=1)
189 finally:
190 self.state.memo.title_styles, self.state.memo.section_level, self.state.memo.reporter = buf