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