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