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