Loading...
1# -*- coding: utf-8; mode: python -*-
2# coding=utf-8
3# SPDX-License-Identifier: GPL-2.0
4#
5u"""
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
35import codecs
36import os
37import subprocess
38import sys
39import re
40import kernellog
41
42from docutils import nodes, statemachine
43from docutils.statemachine import ViewList
44from docutils.parsers.rst import directives, Directive
45from docutils.utils.error_reporting import ErrorString
46from sphinx.util.docutils import switch_source_input
47
48__version__ = '1.0'
49
50def setup(app):
51
52 app.add_directive("kernel-abi", KernelCmd)
53 return dict(
54 version = __version__
55 , parallel_read_safe = True
56 , parallel_write_safe = True
57 )
58
59class KernelCmd(Directive):
60
61 u"""KernelABI (``kernel-abi``) 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 "rst" : directives.unchanged
71 }
72
73 def run(self):
74 doc = self.state.document
75 if not doc.settings.file_insertion_enabled:
76 raise self.warning("docutils: file insertion disabled")
77
78 srctree = os.path.abspath(os.environ["srctree"])
79
80 args = [
81 os.path.join(srctree, 'scripts/get_abi.pl'),
82 'rest',
83 '--enable-lineno',
84 '--dir', os.path.join(srctree, 'Documentation', self.arguments[0]),
85 ]
86
87 if 'rst' in self.options:
88 args.append('--rst-source')
89
90 lines = subprocess.check_output(args, cwd=os.path.dirname(doc.current_source)).decode('utf-8')
91 nodeList = self.nestedParse(lines, self.arguments[0])
92 return nodeList
93
94 def nestedParse(self, lines, fname):
95 env = self.state.document.settings.env
96 content = ViewList()
97 node = nodes.section()
98
99 if "debug" in self.options:
100 code_block = "\n\n.. code-block:: rst\n :linenos:\n"
101 for l in lines.split("\n"):
102 code_block += "\n " + l
103 lines = code_block + "\n\n"
104
105 line_regex = re.compile(r"^\.\. LINENO (\S+)\#([0-9]+)$")
106 ln = 0
107 n = 0
108 f = fname
109
110 for line in lines.split("\n"):
111 n = n + 1
112 match = line_regex.search(line)
113 if match:
114 new_f = match.group(1)
115
116 # Sphinx parser is lazy: it stops parsing contents in the
117 # middle, if it is too big. So, handle it per input file
118 if new_f != f and content:
119 self.do_parse(content, node)
120 content = ViewList()
121
122 # Add the file to Sphinx build dependencies
123 env.note_dependency(os.path.abspath(f))
124
125 f = new_f
126
127 # sphinx counts lines from 0
128 ln = int(match.group(2)) - 1
129 else:
130 content.append(line, f, ln)
131
132 kernellog.info(self.state.document.settings.env.app, "%s: parsed %i lines" % (fname, n))
133
134 if content:
135 self.do_parse(content, node)
136
137 return node.children
138
139 def do_parse(self, content, node):
140 with switch_source_input(self.state, content):
141 self.state.nested_parse(content, 0, node, match_titles=1)
1# -*- coding: utf-8; mode: python -*-
2# coding=utf-8
3# SPDX-License-Identifier: GPL-2.0
4#
5u"""
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
35import codecs
36import os
37import subprocess
38import sys
39import re
40import kernellog
41
42from os import path
43
44from docutils import nodes, statemachine
45from docutils.statemachine import ViewList
46from docutils.parsers.rst import directives, Directive
47from docutils.utils.error_reporting import ErrorString
48from sphinx.util.docutils import switch_source_input
49
50__version__ = '1.0'
51
52def 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
66 optional_arguments = 2
67 has_content = False
68 final_argument_whitespace = True
69
70 option_spec = {
71 "debug" : directives.flag,
72 "rst" : directives.unchanged
73 }
74
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)
83 cmd = "get_abi.pl rest --enable-lineno --dir "
84 cmd += self.arguments[0]
85
86 if 'rst' in self.options:
87 cmd += " --rst-source"
88
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)
103 nodeList = self.nestedParse(lines, self.arguments[0])
104 return nodeList
105
106 def runCmd(self, cmd, **kwargs):
107 u"""Run command ``cmd`` and return its stdout as unicode."""
108
109 try:
110 proc = subprocess.Popen(
111 cmd
112 , stdout = subprocess.PIPE
113 , stderr = subprocess.PIPE
114 , **kwargs
115 )
116 out, err = proc.communicate()
117
118 out, err = codecs.decode(out, 'utf-8'), codecs.decode(err, 'utf-8')
119
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)))
128 return out
129
130 def nestedParse(self, lines, fname):
131 env = self.state.document.settings.env
132 content = ViewList()
133 node = nodes.section()
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
141 line_regex = re.compile("^\.\. LINENO (\S+)\#([0-9]+)$")
142 ln = 0
143 n = 0
144 f = fname
145
146 for line in lines.split("\n"):
147 n = n + 1
148 match = line_regex.search(line)
149 if match:
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
158 # Add the file to Sphinx build dependencies
159 env.note_dependency(os.path.abspath(f))
160
161 f = new_f
162
163 # sphinx counts lines from 0
164 ln = int(match.group(2)) - 1
165 else:
166 content.append(line, f, ln)
167
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)
172
173 return node.children
174
175 def do_parse(self, content, node):
176 with switch_source_input(self.state, content):
177 self.state.nested_parse(content, 0, node, match_titles=1)