 80a046c51a
			
		
	
	
		80a046c51a
		
	
	
	
	
		
			
			Now that none of our input .hx files have STEXI/ETEXI blocks, we can remove the code in the Sphinx hxtool extension that supported parsing them. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20200306171749.10756-5-peter.maydell@linaro.org
		
			
				
	
	
		
			193 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			193 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| # coding=utf-8
 | |
| #
 | |
| # QEMU hxtool .hx file parsing extension
 | |
| #
 | |
| # Copyright (c) 2020 Linaro
 | |
| #
 | |
| # This work is licensed under the terms of the GNU GPLv2 or later.
 | |
| # See the COPYING file in the top-level directory.
 | |
| """hxtool is a Sphinx extension that implements the hxtool-doc directive"""
 | |
| 
 | |
| # The purpose of this extension is to read fragments of rST
 | |
| # from .hx files, and insert them all into the current document.
 | |
| # The rST fragments are delimited by SRST/ERST lines.
 | |
| # The conf.py file must set the hxtool_srctree config value to
 | |
| # the root of the QEMU source tree.
 | |
| # Each hxtool-doc:: directive takes one argument which is the
 | |
| # path of the .hx file to process, relative to the source tree.
 | |
| 
 | |
| import os
 | |
| import re
 | |
| from enum import Enum
 | |
| 
 | |
| from docutils import nodes
 | |
| from docutils.statemachine import ViewList
 | |
| from docutils.parsers.rst import directives, Directive
 | |
| from sphinx.errors import ExtensionError
 | |
| from sphinx.util.nodes import nested_parse_with_titles
 | |
| import sphinx
 | |
| 
 | |
| # Sphinx up to 1.6 uses AutodocReporter; 1.7 and later
 | |
| # use switch_source_input. Check borrowed from kerneldoc.py.
 | |
| Use_SSI = sphinx.__version__[:3] >= '1.7'
 | |
| if Use_SSI:
 | |
|     from sphinx.util.docutils import switch_source_input
 | |
| else:
 | |
|     from sphinx.ext.autodoc import AutodocReporter
 | |
| 
 | |
| __version__ = '1.0'
 | |
| 
 | |
| # We parse hx files with a state machine which may be in one of two
 | |
| # states: reading the C code fragment, or inside a rST fragment.
 | |
| class HxState(Enum):
 | |
|     CTEXT = 1
 | |
|     RST = 2
 | |
| 
 | |
| def serror(file, lnum, errtext):
 | |
|     """Raise an exception giving a user-friendly syntax error message"""
 | |
|     raise ExtensionError('%s line %d: syntax error: %s' % (file, lnum, errtext))
 | |
| 
 | |
| def parse_directive(line):
 | |
|     """Return first word of line, if any"""
 | |
|     return re.split('\W', line)[0]
 | |
| 
 | |
| def parse_defheading(file, lnum, line):
 | |
|     """Handle a DEFHEADING directive"""
 | |
|     # The input should be "DEFHEADING(some string)", though note that
 | |
|     # the 'some string' could be the empty string. If the string is
 | |
|     # empty we ignore the directive -- these are used only to add
 | |
|     # blank lines in the plain-text content of the --help output.
 | |
|     #
 | |
|     # Return the heading text. We strip out any trailing ':' for
 | |
|     # consistency with other headings in the rST documentation.
 | |
|     match = re.match(r'DEFHEADING\((.*?):?\)', line)
 | |
|     if match is None:
 | |
|         serror(file, lnum, "Invalid DEFHEADING line")
 | |
|     return match.group(1)
 | |
| 
 | |
| def parse_archheading(file, lnum, line):
 | |
|     """Handle an ARCHHEADING directive"""
 | |
|     # The input should be "ARCHHEADING(some string, other arg)",
 | |
|     # though note that the 'some string' could be the empty string.
 | |
|     # As with DEFHEADING, empty string ARCHHEADINGs will be ignored.
 | |
|     #
 | |
|     # Return the heading text. We strip out any trailing ':' for
 | |
|     # consistency with other headings in the rST documentation.
 | |
|     match = re.match(r'ARCHHEADING\((.*?):?,.*\)', line)
 | |
|     if match is None:
 | |
|         serror(file, lnum, "Invalid ARCHHEADING line")
 | |
|     return match.group(1)
 | |
| 
 | |
| class HxtoolDocDirective(Directive):
 | |
|     """Extract rST fragments from the specified .hx file"""
 | |
|     required_argument = 1
 | |
|     optional_arguments = 1
 | |
|     option_spec = {
 | |
|         'hxfile': directives.unchanged_required
 | |
|     }
 | |
|     has_content = False
 | |
| 
 | |
|     def run(self):
 | |
|         env = self.state.document.settings.env
 | |
|         hxfile = env.config.hxtool_srctree + '/' + self.arguments[0]
 | |
| 
 | |
|         # Tell sphinx of the dependency
 | |
|         env.note_dependency(os.path.abspath(hxfile))
 | |
| 
 | |
|         state = HxState.CTEXT
 | |
|         # We build up lines of rST in this ViewList, which we will
 | |
|         # later put into a 'section' node.
 | |
|         rstlist = ViewList()
 | |
|         current_node = None
 | |
|         node_list = []
 | |
| 
 | |
|         with open(hxfile) as f:
 | |
|             lines = (l.rstrip() for l in f)
 | |
|             for lnum, line in enumerate(lines, 1):
 | |
|                 directive = parse_directive(line)
 | |
| 
 | |
|                 if directive == 'HXCOMM':
 | |
|                     pass
 | |
|                 elif directive == 'SRST':
 | |
|                     if state == HxState.RST:
 | |
|                         serror(hxfile, lnum, 'expected ERST, found SRST')
 | |
|                     else:
 | |
|                         state = HxState.RST
 | |
|                 elif directive == 'ERST':
 | |
|                     if state == HxState.CTEXT:
 | |
|                         serror(hxfile, lnum, 'expected SRST, found ERST')
 | |
|                     else:
 | |
|                         state = HxState.CTEXT
 | |
|                 elif directive == 'DEFHEADING' or directive == 'ARCHHEADING':
 | |
|                     if directive == 'DEFHEADING':
 | |
|                         heading = parse_defheading(hxfile, lnum, line)
 | |
|                     else:
 | |
|                         heading = parse_archheading(hxfile, lnum, line)
 | |
|                     if heading == "":
 | |
|                         continue
 | |
|                     # Put the accumulated rST into the previous node,
 | |
|                     # and then start a fresh section with this heading.
 | |
|                     if len(rstlist) > 0:
 | |
|                         if current_node is None:
 | |
|                             # We had some rST fragments before the first
 | |
|                             # DEFHEADING. We don't have a section to put
 | |
|                             # these in, so rather than magicing up a section,
 | |
|                             # make it a syntax error.
 | |
|                             serror(hxfile, lnum,
 | |
|                                    'first DEFHEADING must precede all rST text')
 | |
|                         self.do_parse(rstlist, current_node)
 | |
|                         rstlist = ViewList()
 | |
|                     if current_node is not None:
 | |
|                         node_list.append(current_node)
 | |
|                     section_id = 'hxtool-%d' % env.new_serialno('hxtool')
 | |
|                     current_node = nodes.section(ids=[section_id])
 | |
|                     current_node += nodes.title(heading, heading)
 | |
|                 else:
 | |
|                     # Not a directive: put in output if we are in rST fragment
 | |
|                     if state == HxState.RST:
 | |
|                         # Sphinx counts its lines from 0
 | |
|                         rstlist.append(line, hxfile, lnum - 1)
 | |
| 
 | |
|         if current_node is None:
 | |
|             # We don't have multiple sections, so just parse the rst
 | |
|             # fragments into a dummy node so we can return the children.
 | |
|             current_node = nodes.section()
 | |
|             self.do_parse(rstlist, current_node)
 | |
|             return current_node.children
 | |
|         else:
 | |
|             # Put the remaining accumulated rST into the last section, and
 | |
|             # return all the sections.
 | |
|             if len(rstlist) > 0:
 | |
|                 self.do_parse(rstlist, current_node)
 | |
|             node_list.append(current_node)
 | |
|             return node_list
 | |
| 
 | |
|     # This is from kerneldoc.py -- it works around an API change in
 | |
|     # Sphinx between 1.6 and 1.7. Unlike kerneldoc.py, we use
 | |
|     # sphinx.util.nodes.nested_parse_with_titles() rather than the
 | |
|     # plain self.state.nested_parse(), and so we can drop the saving
 | |
|     # of title_styles and section_level that kerneldoc.py does,
 | |
|     # because nested_parse_with_titles() does that for us.
 | |
|     def do_parse(self, result, node):
 | |
|         if Use_SSI:
 | |
|             with switch_source_input(self.state, result):
 | |
|                 nested_parse_with_titles(self.state, result, node)
 | |
|         else:
 | |
|             save = self.state.memo.reporter
 | |
|             self.state.memo.reporter = AutodocReporter(result, self.state.memo.reporter)
 | |
|             try:
 | |
|                 nested_parse_with_titles(self.state, result, node)
 | |
|             finally:
 | |
|                 self.state.memo.reporter = save
 | |
| 
 | |
| def setup(app):
 | |
|     """ Register hxtool-doc directive with Sphinx"""
 | |
|     app.add_config_value('hxtool_srctree', None, 'env')
 | |
|     app.add_directive('hxtool-doc', HxtoolDocDirective)
 | |
| 
 | |
|     return dict(
 | |
|         version = __version__,
 | |
|         parallel_read_safe = True,
 | |
|         parallel_write_safe = True
 | |
|     )
 |