Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1# coding=utf-8
2# SPDX-License-Identifier: MIT
3#
4# Copyright © 2016 Intel Corporation
5#
6# Permission is hereby granted, free of charge, to any person obtaining a
7# copy of this software and associated documentation files (the "Software"),
8# to deal in the Software without restriction, including without limitation
9# the rights to use, copy, modify, merge, publish, distribute, sublicense,
10# and/or sell copies of the Software, and to permit persons to whom the
11# Software is furnished to do so, subject to the following conditions:
12#
13# The above copyright notice and this permission notice (including the next
14# paragraph) shall be included in all copies or substantial portions of the
15# Software.
16#
17# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
23# IN THE SOFTWARE.
24#
25# Authors:
26# Jani Nikula <jani.nikula@intel.com>
27#
28
29import codecs
30import os
31import subprocess
32import sys
33import re
34import glob
35
36from docutils import nodes, statemachine
37from docutils.statemachine import ViewList
38from docutils.parsers.rst import directives, Directive
39import sphinx
40from sphinx.util.docutils import switch_source_input
41from sphinx.util import logging
42from pprint import pformat
43
44srctree = os.path.abspath(os.environ["srctree"])
45sys.path.insert(0, os.path.join(srctree, "tools/lib/python"))
46
47from kdoc.kdoc_files import KernelFiles
48from kdoc.kdoc_output import RestFormat
49
50# Used when verbose is active to show how to reproduce kernel-doc
51# issues via command line
52kerneldoc_bin = "tools/docs/kernel-doc"
53
54__version__ = '1.0'
55kfiles = None
56logger = logging.getLogger(__name__)
57
58def cmd_str(cmd):
59 """
60 Helper function to output a command line that can be used to produce
61 the same records via command line. Helpful to debug troubles at the
62 script.
63 """
64
65 cmd_line = ""
66
67 for w in cmd:
68 if w == "" or " " in w:
69 esc_cmd = "'" + w + "'"
70 else:
71 esc_cmd = w
72
73 if cmd_line:
74 cmd_line += " " + esc_cmd
75 continue
76 else:
77 cmd_line = esc_cmd
78
79 return cmd_line
80
81class KernelDocDirective(Directive):
82 """Extract kernel-doc comments from the specified file"""
83 required_argument = 1
84 optional_arguments = 4
85 option_spec = {
86 'doc': directives.unchanged_required,
87 'export': directives.unchanged,
88 'internal': directives.unchanged,
89 'identifiers': directives.unchanged,
90 'no-identifiers': directives.unchanged,
91 'functions': directives.unchanged,
92 }
93 has_content = False
94 verbose = 0
95
96 parse_args = {}
97 msg_args = {}
98
99 def handle_args(self):
100
101 env = self.state.document.settings.env
102 cmd = [kerneldoc_bin, '-rst', '-enable-lineno']
103
104 filename = env.config.kerneldoc_srctree + '/' + self.arguments[0]
105
106 # Arguments used by KernelFiles.parse() function
107 self.parse_args = {
108 "file_list": [filename],
109 "export_file": []
110 }
111
112 # Arguments used by KernelFiles.msg() function
113 self.msg_args = {
114 "enable_lineno": True,
115 "export": False,
116 "internal": False,
117 "symbol": [],
118 "nosymbol": [],
119 "no_doc_sections": False
120 }
121
122 export_file_patterns = []
123
124 verbose = os.environ.get("V")
125 if verbose:
126 try:
127 self.verbose = int(verbose)
128 except ValueError:
129 pass
130
131 # Tell sphinx of the dependency
132 env.note_dependency(os.path.abspath(filename))
133
134 self.tab_width = self.options.get('tab-width',
135 self.state.document.settings.tab_width)
136
137 # 'function' is an alias of 'identifiers'
138 if 'functions' in self.options:
139 self.options['identifiers'] = self.options.get('functions')
140
141 # FIXME: make this nicer and more robust against errors
142 if 'export' in self.options:
143 cmd += ['-export']
144 self.msg_args["export"] = True
145 export_file_patterns = str(self.options.get('export')).split()
146 elif 'internal' in self.options:
147 cmd += ['-internal']
148 self.msg_args["internal"] = True
149 export_file_patterns = str(self.options.get('internal')).split()
150 elif 'doc' in self.options:
151 func = str(self.options.get('doc'))
152 cmd += ['-function', func]
153 self.msg_args["symbol"].append(func)
154 elif 'identifiers' in self.options:
155 identifiers = self.options.get('identifiers').split()
156 if identifiers:
157 for i in identifiers:
158 i = i.rstrip("\\").strip()
159 if not i:
160 continue
161
162 cmd += ['-function', i]
163 self.msg_args["symbol"].append(i)
164 else:
165 cmd += ['-no-doc-sections']
166 self.msg_args["no_doc_sections"] = True
167
168 if 'no-identifiers' in self.options:
169 no_identifiers = self.options.get('no-identifiers').split()
170 if no_identifiers:
171 for i in no_identifiers:
172 i = i.rstrip("\\").strip()
173 if not i:
174 continue
175
176 cmd += ['-nosymbol', i]
177 self.msg_args["nosymbol"].append(i)
178
179 for pattern in export_file_patterns:
180 pattern = pattern.rstrip("\\").strip()
181 if not pattern:
182 continue
183
184 for f in glob.glob(env.config.kerneldoc_srctree + '/' + pattern):
185 env.note_dependency(os.path.abspath(f))
186 cmd += ['-export-file', f]
187 self.parse_args["export_file"].append(f)
188
189 # Export file is needed by both parse and msg, as kernel-doc
190 # cache exports.
191 self.msg_args["export_file"] = self.parse_args["export_file"]
192
193 cmd += [filename]
194
195 return cmd
196
197 def parse_msg(self, filename, node, out):
198 """
199 Handles a kernel-doc output for a given file
200 """
201
202 env = self.state.document.settings.env
203
204 lines = statemachine.string2lines(out, self.tab_width,
205 convert_whitespace=True)
206 result = ViewList()
207
208 lineoffset = 0;
209 line_regex = re.compile(r"^\.\. LINENO ([0-9]+)$")
210 for line in lines:
211 match = line_regex.search(line)
212 if match:
213 # sphinx counts lines from 0
214 lineoffset = int(match.group(1)) - 1
215 # we must eat our comments since the upset the markup
216 else:
217 doc = str(env.srcdir) + "/" + env.docname + ":" + str(self.lineno)
218 result.append(line, doc + ": " + filename, lineoffset)
219 lineoffset += 1
220
221 self.do_parse(result, node)
222
223 def run_kdoc(self, kfiles):
224 """
225 Execute kernel-doc classes directly instead of running as a separate
226 command.
227 """
228
229 env = self.state.document.settings.env
230
231 node = nodes.section()
232
233 kfiles.parse(**self.parse_args)
234 filenames = self.parse_args["file_list"]
235
236 for filename, out in kfiles.msg(**self.msg_args, filenames=filenames):
237 self.parse_msg(filename, node, out)
238
239 return node.children
240
241 def run(self):
242 cmd = self.handle_args()
243 if self.verbose >= 1:
244 logger.info(cmd_str(cmd))
245
246 try:
247 return self.run_kdoc(kfiles)
248 except Exception as e: # pylint: disable=W0703
249 logger.warning("kernel-doc '%s' processing failed with: %s" %
250 (cmd_str(cmd), pformat(e)))
251 return [nodes.error(None, nodes.paragraph(text = "kernel-doc missing"))]
252
253 def do_parse(self, result, node):
254 with switch_source_input(self.state, result):
255 self.state.nested_parse(result, 0, node, match_titles=1)
256
257def setup_kfiles(app):
258 global kfiles
259 out_style = RestFormat()
260 kfiles = KernelFiles(out_style=out_style, logger=logger)
261
262
263def setup(app):
264 app.add_config_value('kerneldoc_srctree', None, 'env')
265 app.add_config_value('kerneldoc_verbosity', 1, 'env')
266
267 app.add_directive('kernel-doc', KernelDocDirective)
268
269 app.connect('builder-inited', setup_kfiles)
270
271 return dict(
272 version = __version__,
273 parallel_read_safe = True,
274 parallel_write_safe = True
275 )