blob: efe760e410c457dac2e99f5bff73cfbc4f2a4a78 [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
Jonathan Corbetf546ff02021-02-01 16:26:25 -070048from sphinx.util.docutils import switch_source_input
Mauro Carvalho Chehab9ca876f2020-10-30 08:40:31 +010049
50__version__ = '1.0'
51
Mauro Carvalho Chehab9ca876f2020-10-30 08:40:31 +010052def 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
61class KernelCmd(Directive):
62
63 u"""KernelABI (``kernel-abi``) directive"""
64
65 required_arguments = 1
Mauro Carvalho Chehab642514d2020-10-30 08:40:41 +010066 optional_arguments = 2
Mauro Carvalho Chehab9ca876f2020-10-30 08:40:31 +010067 has_content = False
68 final_argument_whitespace = True
69
70 option_spec = {
Mauro Carvalho Chehab642514d2020-10-30 08:40:41 +010071 "debug" : directives.flag,
72 "rst" : directives.unchanged
Mauro Carvalho Chehab9ca876f2020-10-30 08:40:31 +010073 }
74
Mauro Carvalho Chehab9ca876f2020-10-30 08:40:31 +010075 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)
Mauro Carvalho Chehab997b7c82020-10-30 08:40:34 +010083 cmd = "get_abi.pl rest --enable-lineno --dir "
Mauro Carvalho Chehab9ca876f2020-10-30 08:40:31 +010084 cmd += self.arguments[0]
85
Mauro Carvalho Chehab642514d2020-10-30 08:40:41 +010086 if 'rst' in self.options:
87 cmd += " --rst-source"
88
Mauro Carvalho Chehab9ca876f2020-10-30 08:40:31 +010089 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)
Mauro Carvalho Chehab3c543d22020-10-30 08:40:35 +0100103 nodeList = self.nestedParse(lines, self.arguments[0])
Mauro Carvalho Chehab9ca876f2020-10-30 08:40:31 +0100104 return nodeList
105
106 def runCmd(self, cmd, **kwargs):
107 u"""Run command ``cmd`` and return it's stdout as unicode."""
108
109 try:
110 proc = subprocess.Popen(
111 cmd
112 , stdout = subprocess.PIPE
113 , stderr = subprocess.PIPE
Mauro Carvalho Chehab9ca876f2020-10-30 08:40:31 +0100114 , **kwargs
115 )
116 out, err = proc.communicate()
Mauro Carvalho Chehab823830d2020-10-30 08:40:32 +0100117
118 out, err = codecs.decode(out, 'utf-8'), codecs.decode(err, 'utf-8')
119
Mauro Carvalho Chehab9ca876f2020-10-30 08:40:31 +0100120 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)))
Mauro Carvalho Chehab823830d2020-10-30 08:40:32 +0100128 return out
Mauro Carvalho Chehab9ca876f2020-10-30 08:40:31 +0100129
Mauro Carvalho Chehab3c543d22020-10-30 08:40:35 +0100130 def nestedParse(self, lines, fname):
Mauro Carvalho Chehab9ca876f2020-10-30 08:40:31 +0100131 content = ViewList()
Mauro Carvalho Chehab3c543d22020-10-30 08:40:35 +0100132 node = nodes.section()
Mauro Carvalho Chehab9ca876f2020-10-30 08:40:31 +0100133
134 if "debug" in self.options:
135 code_block = "\n\n.. code-block:: rst\n :linenos:\n"
136 for l in lines.split("\n"):
137 code_block += "\n " + l
138 lines = code_block + "\n\n"
139
Mauro Carvalho Chehab997b7c82020-10-30 08:40:34 +0100140 line_regex = re.compile("^#define LINENO (\S+)\#([0-9]+)$")
141 ln = 0
Mauro Carvalho Chehab3c543d22020-10-30 08:40:35 +0100142 n = 0
143 f = fname
Mauro Carvalho Chehab997b7c82020-10-30 08:40:34 +0100144
145 for line in lines.split("\n"):
Mauro Carvalho Chehab3c543d22020-10-30 08:40:35 +0100146 n = n + 1
Mauro Carvalho Chehab997b7c82020-10-30 08:40:34 +0100147 match = line_regex.search(line)
148 if match:
Mauro Carvalho Chehab3c543d22020-10-30 08:40:35 +0100149 new_f = match.group(1)
150
151 # Sphinx parser is lazy: it stops parsing contents in the
152 # middle, if it is too big. So, handle it per input file
153 if new_f != f and content:
154 self.do_parse(content, node)
155 content = ViewList()
156
157 f = new_f
158
Mauro Carvalho Chehab997b7c82020-10-30 08:40:34 +0100159 # sphinx counts lines from 0
160 ln = int(match.group(2)) - 1
161 else:
162 content.append(line, f, ln)
Mauro Carvalho Chehab9ca876f2020-10-30 08:40:31 +0100163
Mauro Carvalho Chehab3c543d22020-10-30 08:40:35 +0100164 kernellog.info(self.state.document.settings.env.app, "%s: parsed %i lines" % (fname, n))
Mauro Carvalho Chehabc830fa92020-10-30 08:40:33 +0100165
Mauro Carvalho Chehab3c543d22020-10-30 08:40:35 +0100166 if content:
167 self.do_parse(content, node)
168
169 return node.children
170
171 def do_parse(self, content, node):
Jonathan Corbetf546ff02021-02-01 16:26:25 -0700172 with switch_source_input(self.state, content):
173 self.state.nested_parse(content, 0, node, match_titles=1)