xref: /OK3568_Linux_fs/kernel/Documentation/sphinx/kerneldoc.py (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun# coding=utf-8
2*4882a593Smuzhiyun#
3*4882a593Smuzhiyun# Copyright © 2016 Intel Corporation
4*4882a593Smuzhiyun#
5*4882a593Smuzhiyun# Permission is hereby granted, free of charge, to any person obtaining a
6*4882a593Smuzhiyun# copy of this software and associated documentation files (the "Software"),
7*4882a593Smuzhiyun# to deal in the Software without restriction, including without limitation
8*4882a593Smuzhiyun# the rights to use, copy, modify, merge, publish, distribute, sublicense,
9*4882a593Smuzhiyun# and/or sell copies of the Software, and to permit persons to whom the
10*4882a593Smuzhiyun# Software is furnished to do so, subject to the following conditions:
11*4882a593Smuzhiyun#
12*4882a593Smuzhiyun# The above copyright notice and this permission notice (including the next
13*4882a593Smuzhiyun# paragraph) shall be included in all copies or substantial portions of the
14*4882a593Smuzhiyun# Software.
15*4882a593Smuzhiyun#
16*4882a593Smuzhiyun# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17*4882a593Smuzhiyun# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18*4882a593Smuzhiyun# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19*4882a593Smuzhiyun# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20*4882a593Smuzhiyun# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21*4882a593Smuzhiyun# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22*4882a593Smuzhiyun# IN THE SOFTWARE.
23*4882a593Smuzhiyun#
24*4882a593Smuzhiyun# Authors:
25*4882a593Smuzhiyun#    Jani Nikula <jani.nikula@intel.com>
26*4882a593Smuzhiyun#
27*4882a593Smuzhiyun# Please make sure this works on both python2 and python3.
28*4882a593Smuzhiyun#
29*4882a593Smuzhiyun
30*4882a593Smuzhiyunimport codecs
31*4882a593Smuzhiyunimport os
32*4882a593Smuzhiyunimport subprocess
33*4882a593Smuzhiyunimport sys
34*4882a593Smuzhiyunimport re
35*4882a593Smuzhiyunimport glob
36*4882a593Smuzhiyun
37*4882a593Smuzhiyunfrom docutils import nodes, statemachine
38*4882a593Smuzhiyunfrom docutils.statemachine import ViewList
39*4882a593Smuzhiyunfrom docutils.parsers.rst import directives, Directive
40*4882a593Smuzhiyun
41*4882a593Smuzhiyun#
42*4882a593Smuzhiyun# AutodocReporter is only good up to Sphinx 1.7
43*4882a593Smuzhiyun#
44*4882a593Smuzhiyunimport sphinx
45*4882a593Smuzhiyun
46*4882a593SmuzhiyunUse_SSI = sphinx.__version__[:3] >= '1.7'
47*4882a593Smuzhiyunif Use_SSI:
48*4882a593Smuzhiyun    from sphinx.util.docutils import switch_source_input
49*4882a593Smuzhiyunelse:
50*4882a593Smuzhiyun    from sphinx.ext.autodoc import AutodocReporter
51*4882a593Smuzhiyun
52*4882a593Smuzhiyunimport kernellog
53*4882a593Smuzhiyun
54*4882a593Smuzhiyun__version__  = '1.0'
55*4882a593Smuzhiyun
56*4882a593Smuzhiyunclass KernelDocDirective(Directive):
57*4882a593Smuzhiyun    """Extract kernel-doc comments from the specified file"""
58*4882a593Smuzhiyun    required_argument = 1
59*4882a593Smuzhiyun    optional_arguments = 4
60*4882a593Smuzhiyun    option_spec = {
61*4882a593Smuzhiyun        'doc': directives.unchanged_required,
62*4882a593Smuzhiyun        'export': directives.unchanged,
63*4882a593Smuzhiyun        'internal': directives.unchanged,
64*4882a593Smuzhiyun        'identifiers': directives.unchanged,
65*4882a593Smuzhiyun        'no-identifiers': directives.unchanged,
66*4882a593Smuzhiyun        'functions': directives.unchanged,
67*4882a593Smuzhiyun    }
68*4882a593Smuzhiyun    has_content = False
69*4882a593Smuzhiyun
70*4882a593Smuzhiyun    def run(self):
71*4882a593Smuzhiyun        env = self.state.document.settings.env
72*4882a593Smuzhiyun        cmd = [env.config.kerneldoc_bin, '-rst', '-enable-lineno']
73*4882a593Smuzhiyun
74*4882a593Smuzhiyun	# Pass the version string to kernel-doc, as it needs to use a different
75*4882a593Smuzhiyun	# dialect, depending what the C domain supports for each specific
76*4882a593Smuzhiyun	# Sphinx versions
77*4882a593Smuzhiyun        cmd += ['-sphinx-version', sphinx.__version__]
78*4882a593Smuzhiyun
79*4882a593Smuzhiyun        filename = env.config.kerneldoc_srctree + '/' + self.arguments[0]
80*4882a593Smuzhiyun        export_file_patterns = []
81*4882a593Smuzhiyun
82*4882a593Smuzhiyun        # Tell sphinx of the dependency
83*4882a593Smuzhiyun        env.note_dependency(os.path.abspath(filename))
84*4882a593Smuzhiyun
85*4882a593Smuzhiyun        tab_width = self.options.get('tab-width', self.state.document.settings.tab_width)
86*4882a593Smuzhiyun
87*4882a593Smuzhiyun        # 'function' is an alias of 'identifiers'
88*4882a593Smuzhiyun        if 'functions' in self.options:
89*4882a593Smuzhiyun            self.options['identifiers'] = self.options.get('functions')
90*4882a593Smuzhiyun
91*4882a593Smuzhiyun        # FIXME: make this nicer and more robust against errors
92*4882a593Smuzhiyun        if 'export' in self.options:
93*4882a593Smuzhiyun            cmd += ['-export']
94*4882a593Smuzhiyun            export_file_patterns = str(self.options.get('export')).split()
95*4882a593Smuzhiyun        elif 'internal' in self.options:
96*4882a593Smuzhiyun            cmd += ['-internal']
97*4882a593Smuzhiyun            export_file_patterns = str(self.options.get('internal')).split()
98*4882a593Smuzhiyun        elif 'doc' in self.options:
99*4882a593Smuzhiyun            cmd += ['-function', str(self.options.get('doc'))]
100*4882a593Smuzhiyun        elif 'identifiers' in self.options:
101*4882a593Smuzhiyun            identifiers = self.options.get('identifiers').split()
102*4882a593Smuzhiyun            if identifiers:
103*4882a593Smuzhiyun                for i in identifiers:
104*4882a593Smuzhiyun                    cmd += ['-function', i]
105*4882a593Smuzhiyun            else:
106*4882a593Smuzhiyun                cmd += ['-no-doc-sections']
107*4882a593Smuzhiyun
108*4882a593Smuzhiyun        if 'no-identifiers' in self.options:
109*4882a593Smuzhiyun            no_identifiers = self.options.get('no-identifiers').split()
110*4882a593Smuzhiyun            if no_identifiers:
111*4882a593Smuzhiyun                for i in no_identifiers:
112*4882a593Smuzhiyun                    cmd += ['-nosymbol', i]
113*4882a593Smuzhiyun
114*4882a593Smuzhiyun        for pattern in export_file_patterns:
115*4882a593Smuzhiyun            for f in glob.glob(env.config.kerneldoc_srctree + '/' + pattern):
116*4882a593Smuzhiyun                env.note_dependency(os.path.abspath(f))
117*4882a593Smuzhiyun                cmd += ['-export-file', f]
118*4882a593Smuzhiyun
119*4882a593Smuzhiyun        cmd += [filename]
120*4882a593Smuzhiyun
121*4882a593Smuzhiyun        try:
122*4882a593Smuzhiyun            kernellog.verbose(env.app,
123*4882a593Smuzhiyun                              'calling kernel-doc \'%s\'' % (" ".join(cmd)))
124*4882a593Smuzhiyun
125*4882a593Smuzhiyun            p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
126*4882a593Smuzhiyun            out, err = p.communicate()
127*4882a593Smuzhiyun
128*4882a593Smuzhiyun            out, err = codecs.decode(out, 'utf-8'), codecs.decode(err, 'utf-8')
129*4882a593Smuzhiyun
130*4882a593Smuzhiyun            if p.returncode != 0:
131*4882a593Smuzhiyun                sys.stderr.write(err)
132*4882a593Smuzhiyun
133*4882a593Smuzhiyun                kernellog.warn(env.app,
134*4882a593Smuzhiyun                               'kernel-doc \'%s\' failed with return code %d' % (" ".join(cmd), p.returncode))
135*4882a593Smuzhiyun                return [nodes.error(None, nodes.paragraph(text = "kernel-doc missing"))]
136*4882a593Smuzhiyun            elif env.config.kerneldoc_verbosity > 0:
137*4882a593Smuzhiyun                sys.stderr.write(err)
138*4882a593Smuzhiyun
139*4882a593Smuzhiyun            lines = statemachine.string2lines(out, tab_width, convert_whitespace=True)
140*4882a593Smuzhiyun            result = ViewList()
141*4882a593Smuzhiyun
142*4882a593Smuzhiyun            lineoffset = 0;
143*4882a593Smuzhiyun            line_regex = re.compile("^#define LINENO ([0-9]+)$")
144*4882a593Smuzhiyun            for line in lines:
145*4882a593Smuzhiyun                match = line_regex.search(line)
146*4882a593Smuzhiyun                if match:
147*4882a593Smuzhiyun                    # sphinx counts lines from 0
148*4882a593Smuzhiyun                    lineoffset = int(match.group(1)) - 1
149*4882a593Smuzhiyun                    # we must eat our comments since the upset the markup
150*4882a593Smuzhiyun                else:
151*4882a593Smuzhiyun                    doc = env.srcdir + "/" + env.docname + ":" + str(self.lineno)
152*4882a593Smuzhiyun                    result.append(line, doc + ": " + filename, lineoffset)
153*4882a593Smuzhiyun                    lineoffset += 1
154*4882a593Smuzhiyun
155*4882a593Smuzhiyun            node = nodes.section()
156*4882a593Smuzhiyun            self.do_parse(result, node)
157*4882a593Smuzhiyun
158*4882a593Smuzhiyun            return node.children
159*4882a593Smuzhiyun
160*4882a593Smuzhiyun        except Exception as e:  # pylint: disable=W0703
161*4882a593Smuzhiyun            kernellog.warn(env.app, 'kernel-doc \'%s\' processing failed with: %s' %
162*4882a593Smuzhiyun                           (" ".join(cmd), str(e)))
163*4882a593Smuzhiyun            return [nodes.error(None, nodes.paragraph(text = "kernel-doc missing"))]
164*4882a593Smuzhiyun
165*4882a593Smuzhiyun    def do_parse(self, result, node):
166*4882a593Smuzhiyun        if Use_SSI:
167*4882a593Smuzhiyun            with switch_source_input(self.state, result):
168*4882a593Smuzhiyun                self.state.nested_parse(result, 0, node, match_titles=1)
169*4882a593Smuzhiyun        else:
170*4882a593Smuzhiyun            save = self.state.memo.title_styles, self.state.memo.section_level, self.state.memo.reporter
171*4882a593Smuzhiyun            self.state.memo.reporter = AutodocReporter(result, self.state.memo.reporter)
172*4882a593Smuzhiyun            self.state.memo.title_styles, self.state.memo.section_level = [], 0
173*4882a593Smuzhiyun            try:
174*4882a593Smuzhiyun                self.state.nested_parse(result, 0, node, match_titles=1)
175*4882a593Smuzhiyun            finally:
176*4882a593Smuzhiyun                self.state.memo.title_styles, self.state.memo.section_level, self.state.memo.reporter = save
177*4882a593Smuzhiyun
178*4882a593Smuzhiyun
179*4882a593Smuzhiyundef setup(app):
180*4882a593Smuzhiyun    app.add_config_value('kerneldoc_bin', None, 'env')
181*4882a593Smuzhiyun    app.add_config_value('kerneldoc_srctree', None, 'env')
182*4882a593Smuzhiyun    app.add_config_value('kerneldoc_verbosity', 1, 'env')
183*4882a593Smuzhiyun
184*4882a593Smuzhiyun    app.add_directive('kernel-doc', KernelDocDirective)
185*4882a593Smuzhiyun
186*4882a593Smuzhiyun    return dict(
187*4882a593Smuzhiyun        version = __version__,
188*4882a593Smuzhiyun        parallel_read_safe = True,
189*4882a593Smuzhiyun        parallel_write_safe = True
190*4882a593Smuzhiyun    )
191