summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--docs/devel/qapi-code-gen.txt2
-rw-r--r--qapi-schema.json13
-rw-r--r--qmp.c5
-rw-r--r--scripts/qapi.py107
-rwxr-xr-xscripts/qapi2texi.py65
-rw-r--r--tests/Makefile.include3
-rw-r--r--tests/qapi-schema/doc-bad-section.err0
-rw-r--r--tests/qapi-schema/doc-bad-section.exit1
-rw-r--r--tests/qapi-schema/doc-bad-section.json11
-rw-r--r--tests/qapi-schema/doc-bad-section.out13
-rw-r--r--tests/qapi-schema/doc-good.json1
-rw-r--r--tests/qapi-schema/doc-good.out4
-rw-r--r--tests/qapi-schema/doc-good.texi11
-rw-r--r--tests/qapi-schema/test-qapi.py6
14 files changed, 111 insertions, 131 deletions
diff --git a/docs/devel/qapi-code-gen.txt b/docs/devel/qapi-code-gen.txt
index f04c63fe82..06ab699066 100644
--- a/docs/devel/qapi-code-gen.txt
+++ b/docs/devel/qapi-code-gen.txt
@@ -63,7 +63,7 @@ Comment text starting with '=' is a section title:
 
 Double the '=' for a subsection title:
 
-    # == Subection title
+    # == Subsection title
 
 '|' denotes examples:
 
diff --git a/qapi-schema.json b/qapi-schema.json
index 18457954a8..5c06745c79 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -1046,17 +1046,6 @@
 { 'command': 'system_powerdown' }
 
 ##
-# @cpu:
-#
-# This command is a nop that is only provided for the purposes of compatibility.
-#
-# Since: 0.14.0
-#
-# Notes: Do not use this command.
-##
-{ 'command': 'cpu', 'data': {'index': 'int'} }
-
-##
 # @cpu-add:
 #
 # Adds CPU with specified ID
@@ -3188,7 +3177,7 @@
 #
 # Show Virtual Machine Generation ID
 #
-# Since 2.9
+# Since: 2.9
 ##
 { 'command': 'query-vm-generation-id', 'returns': 'GuidInfo' }
 
diff --git a/qmp.c b/qmp.c
index e8c303116a..52cfd2d81c 100644
--- a/qmp.c
+++ b/qmp.c
@@ -113,11 +113,6 @@ void qmp_system_powerdown(Error **erp)
     qemu_system_powerdown_request();
 }
 
-void qmp_cpu(int64_t index, Error **errp)
-{
-    /* Just do nothing */
-}
-
 void qmp_cpu_add(int64_t id, Error **errp)
 {
     MachineClass *mc;
diff --git a/scripts/qapi.py b/scripts/qapi.py
index 62dc52ed6e..43a54bf40f 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -106,13 +106,10 @@ class QAPIDoc(object):
             # optional section name (argument/member or section name)
             self.name = name
             # the list of lines for this section
-            self.content = []
+            self.text = ''
 
         def append(self, line):
-            self.content.append(line)
-
-        def __repr__(self):
-            return '\n'.join(self.content).strip()
+            self.text += line.rstrip() + '\n'
 
     class ArgSection(Section):
         def __init__(self, name):
@@ -123,11 +120,11 @@ class QAPIDoc(object):
             self.member = member
 
     def __init__(self, parser, info):
-        # self.parser is used to report errors with QAPIParseError.  The
+        # self._parser is used to report errors with QAPIParseError.  The
         # resulting error position depends on the state of the parser.
         # It happens to be the beginning of the comment.  More or less
         # servicable, but action at a distance.
-        self.parser = parser
+        self._parser = parser
         self.info = info
         self.symbol = None
         self.body = QAPIDoc.Section()
@@ -136,7 +133,7 @@ class QAPIDoc(object):
         # a list of Section
         self.sections = []
         # the current section
-        self.section = self.body
+        self._section = self.body
 
     def has_section(self, name):
         """Return True if we have a section with this name."""
@@ -153,20 +150,20 @@ class QAPIDoc(object):
             return
 
         if line[0] != ' ':
-            raise QAPIParseError(self.parser, "Missing space after #")
+            raise QAPIParseError(self._parser, "Missing space after #")
         line = line[1:]
 
         # FIXME not nice: things like '#  @foo:' and '# @foo: ' aren't
         # recognized, and get silently treated as ordinary text
         if self.symbol:
             self._append_symbol_line(line)
-        elif not self.body.content and line.startswith('@'):
+        elif not self.body.text and line.startswith('@'):
             if not line.endswith(':'):
-                raise QAPIParseError(self.parser, "Line should end with :")
+                raise QAPIParseError(self._parser, "Line should end with :")
             self.symbol = line[1:-1]
             # FIXME invalid names other than the empty string aren't flagged
             if not self.symbol:
-                raise QAPIParseError(self.parser, "Invalid name")
+                raise QAPIParseError(self._parser, "Invalid name")
         else:
             self._append_freeform(line)
 
@@ -192,53 +189,48 @@ class QAPIDoc(object):
     def _start_args_section(self, name):
         # FIXME invalid names other than the empty string aren't flagged
         if not name:
-            raise QAPIParseError(self.parser, "Invalid parameter name")
+            raise QAPIParseError(self._parser, "Invalid parameter name")
         if name in self.args:
-            raise QAPIParseError(self.parser,
+            raise QAPIParseError(self._parser,
                                  "'%s' parameter name duplicated" % name)
         if self.sections:
-            raise QAPIParseError(self.parser,
+            raise QAPIParseError(self._parser,
                                  "'@%s:' can't follow '%s' section"
                                  % (name, self.sections[0].name))
         self._end_section()
-        self.section = QAPIDoc.ArgSection(name)
-        self.args[name] = self.section
+        self._section = QAPIDoc.ArgSection(name)
+        self.args[name] = self._section
 
-    def _start_section(self, name=''):
+    def _start_section(self, name=None):
         if name in ('Returns', 'Since') and self.has_section(name):
-            raise QAPIParseError(self.parser,
+            raise QAPIParseError(self._parser,
                                  "Duplicated '%s' section" % name)
         self._end_section()
-        self.section = QAPIDoc.Section(name)
-        self.sections.append(self.section)
+        self._section = QAPIDoc.Section(name)
+        self.sections.append(self._section)
 
     def _end_section(self):
-        if self.section:
-            contents = str(self.section)
-            if self.section.name and (not contents or contents.isspace()):
-                raise QAPIParseError(self.parser, "Empty doc section '%s'"
-                                     % self.section.name)
-            self.section = None
+        if self._section:
+            text = self._section.text = self._section.text.strip()
+            if self._section.name and (not text or text.isspace()):
+                raise QAPIParseError(self._parser, "Empty doc section '%s'"
+                                     % self._section.name)
+            self._section = None
 
     def _append_freeform(self, line):
-        in_arg = isinstance(self.section, QAPIDoc.ArgSection)
-        if (in_arg and self.section.content
-                and not self.section.content[-1]
+        in_arg = isinstance(self._section, QAPIDoc.ArgSection)
+        if (in_arg and self._section.text.endswith('\n\n')
                 and line and not line[0].isspace()):
             self._start_section()
-        if (in_arg or not self.section.name
-                or not self.section.name.startswith('Example')):
+        if (in_arg or not self._section.name
+                or not self._section.name.startswith('Example')):
             line = line.strip()
         match = re.match(r'(@\S+:)', line)
         if match:
-            raise QAPIParseError(self.parser,
+            raise QAPIParseError(self._parser,
                                  "'%s' not allowed in free-form documentation"
                                  % match.group(1))
-        # TODO Drop this once the dust has settled
-        if (isinstance(self.section, QAPIDoc.ArgSection)
-                and '#optional' in line):
-            raise QAPISemError(self.info, "Please drop the #optional tag")
-        self.section.append(line)
+        self._section.append(line)
 
     def connect_member(self, member):
         if member.name not in self.args:
@@ -265,8 +257,7 @@ class QAPISchemaParser(object):
 
     def __init__(self, fp, previously_included=[], incl_info=None):
         abs_fname = os.path.abspath(fp.name)
-        fname = fp.name
-        self.fname = fname
+        self.fname = fp.name
         previously_included.append(abs_fname)
         self.incl_info = incl_info
         self.src = fp.read()
@@ -277,21 +268,21 @@ class QAPISchemaParser(object):
         self.line_pos = 0
         self.exprs = []
         self.docs = []
-        self.cur_doc = None
         self.accept()
+        cur_doc = None
 
         while self.tok is not None:
-            info = {'file': fname, 'line': self.line,
+            info = {'file': self.fname, 'line': self.line,
                     'parent': self.incl_info}
             if self.tok == '#':
-                self.reject_expr_doc()
-                self.cur_doc = self.get_doc(info)
-                self.docs.append(self.cur_doc)
+                self.reject_expr_doc(cur_doc)
+                cur_doc = self.get_doc(info)
+                self.docs.append(cur_doc)
                 continue
 
             expr = self.get_expr(False)
             if 'include' in expr:
-                self.reject_expr_doc()
+                self.reject_expr_doc(cur_doc)
                 if len(expr) != 1:
                     raise QAPISemError(info, "Invalid 'include' directive")
                 include = expr['include']
@@ -301,7 +292,7 @@ class QAPISchemaParser(object):
                 self._include(include, info, os.path.dirname(abs_fname),
                               previously_included)
             elif "pragma" in expr:
-                self.reject_expr_doc()
+                self.reject_expr_doc(cur_doc)
                 if len(expr) != 1:
                     raise QAPISemError(info, "Invalid 'pragma' directive")
                 pragma = expr['pragma']
@@ -313,22 +304,22 @@ class QAPISchemaParser(object):
             else:
                 expr_elem = {'expr': expr,
                              'info': info}
-                if self.cur_doc:
-                    if not self.cur_doc.symbol:
+                if cur_doc:
+                    if not cur_doc.symbol:
                         raise QAPISemError(
-                            self.cur_doc.info,
-                            "Expression documentation required")
-                    expr_elem['doc'] = self.cur_doc
+                            cur_doc.info, "Expression documentation required")
+                    expr_elem['doc'] = cur_doc
                 self.exprs.append(expr_elem)
-            self.cur_doc = None
-        self.reject_expr_doc()
+            cur_doc = None
+        self.reject_expr_doc(cur_doc)
 
-    def reject_expr_doc(self):
-        if self.cur_doc and self.cur_doc.symbol:
+    @staticmethod
+    def reject_expr_doc(doc):
+        if doc and doc.symbol:
             raise QAPISemError(
-                self.cur_doc.info,
+                doc.info,
                 "Documentation for '%s' is not followed by the definition"
-                % self.cur_doc.symbol)
+                % doc.symbol)
 
     def _include(self, include, info, base_dir, previously_included):
         incl_abs_fname = os.path.join(base_dir, include)
diff --git a/scripts/qapi2texi.py b/scripts/qapi2texi.py
index a317526e51..92e2af2cd6 100755
--- a/scripts/qapi2texi.py
+++ b/scripts/qapi2texi.py
@@ -13,7 +13,6 @@ MSG_FMT = """
 @deftypefn {type} {{}} {name}
 
 {body}
-
 @end deftypefn
 
 """.format
@@ -22,7 +21,6 @@ TYPE_FMT = """
 @deftp {{{type}}} {name}
 
 {body}
-
 @end deftp
 
 """.format
@@ -74,7 +72,7 @@ def texi_format(doc):
     - 1. or 1): generates an @enumerate @item
     - */-: generates an @itemize list
     """
-    lines = []
+    ret = ''
     doc = subst_braces(doc)
     doc = subst_vars(doc)
     doc = subst_emph(doc)
@@ -100,32 +98,32 @@ def texi_format(doc):
             line = '@subsection ' + line[3:]
         elif re.match(r'^([0-9]*\.) ', line):
             if not inlist:
-                lines.append('@enumerate')
+                ret += '@enumerate\n'
                 inlist = 'enumerate'
+            ret += '@item\n'
             line = line[line.find(' ')+1:]
-            lines.append('@item')
         elif re.match(r'^[*-] ', line):
             if not inlist:
-                lines.append('@itemize %s' % {'*': '@bullet',
-                                              '-': '@minus'}[line[0]])
+                ret += '@itemize %s\n' % {'*': '@bullet',
+                                          '-': '@minus'}[line[0]]
                 inlist = 'itemize'
-            lines.append('@item')
+            ret += '@item\n'
             line = line[2:]
         elif lastempty and inlist:
-            lines.append('@end %s\n' % inlist)
+            ret += '@end %s\n\n' % inlist
             inlist = ''
 
         lastempty = empty
-        lines.append(line)
+        ret += line + '\n'
 
     if inlist:
-        lines.append('@end %s\n' % inlist)
-    return '\n'.join(lines)
+        ret += '@end %s\n\n' % inlist
+    return ret
 
 
 def texi_body(doc):
     """Format the main documentation body"""
-    return texi_format(str(doc.body)) + '\n'
+    return texi_format(doc.body.text)
 
 
 def texi_enum_value(value):
@@ -149,15 +147,16 @@ def texi_members(doc, what, base, variants, member_func):
     items = ''
     for section in doc.args.itervalues():
         # TODO Drop fallbacks when undocumented members are outlawed
-        if section.content:
-            desc = texi_format(str(section))
+        if section.text:
+            desc = texi_format(section.text)
         elif (variants and variants.tag_member == section.member
               and not section.member.type.doc_type()):
             values = section.member.type.member_names()
-            desc = 'One of ' + ', '.join(['@t{"%s"}' % v for v in values])
+            members_text = ', '.join(['@t{"%s"}' % v for v in values])
+            desc = 'One of ' + members_text + '\n'
         else:
-            desc = 'Not documented'
-        items += member_func(section.member) + desc + '\n'
+            desc = 'Not documented\n'
+        items += member_func(section.member) + desc
     if base:
         items += '@item The members of @code{%s}\n' % base.doc_type()
     if variants:
@@ -180,16 +179,13 @@ def texi_sections(doc):
     """Format additional sections following arguments"""
     body = ''
     for section in doc.sections:
-        name, doc = (section.name, str(section))
-        func = texi_format
-        if name.startswith('Example'):
-            func = texi_example
-
-        if name:
+        if section.name:
             # prefer @b over @strong, so txt doesn't translate it to *Foo:*
-            body += '\n\n@b{%s:}\n' % name
-
-        body += func(doc)
+            body += '\n@b{%s:}\n' % section.name
+        if section.name and section.name.startswith('Example'):
+            body += texi_example(section.text)
+        else:
+            body += texi_format(section.text)
     return body
 
 
@@ -210,8 +206,6 @@ class QAPISchemaGenDocVisitor(qapi.QAPISchemaVisitor):
 
     def visit_enum_type(self, name, info, values, prefix):
         doc = self.cur_doc
-        if self.out:
-            self.out += '\n'
         self.out += TYPE_FMT(type='Enum',
                              name=doc.symbol,
                              body=texi_entity(doc, 'Values',
@@ -221,16 +215,12 @@ class QAPISchemaGenDocVisitor(qapi.QAPISchemaVisitor):
         doc = self.cur_doc
         if base and base.is_implicit():
             base = None
-        if self.out:
-            self.out += '\n'
         self.out += TYPE_FMT(type='Object',
                              name=doc.symbol,
                              body=texi_entity(doc, 'Members', base, variants))
 
     def visit_alternate_type(self, name, info, variants):
         doc = self.cur_doc
-        if self.out:
-            self.out += '\n'
         self.out += TYPE_FMT(type='Alternate',
                              name=doc.symbol,
                              body=texi_entity(doc, 'Members'))
@@ -238,11 +228,10 @@ class QAPISchemaGenDocVisitor(qapi.QAPISchemaVisitor):
     def visit_command(self, name, info, arg_type, ret_type,
                       gen, success_response, boxed):
         doc = self.cur_doc
-        if self.out:
-            self.out += '\n'
         if boxed:
             body = texi_body(doc)
-            body += '\n@b{Arguments:} the members of @code{%s}' % arg_type.name
+            body += ('\n@b{Arguments:} the members of @code{%s}\n'
+                     % arg_type.name)
             body += texi_sections(doc)
         else:
             body = texi_entity(doc, 'Arguments')
@@ -252,13 +241,13 @@ class QAPISchemaGenDocVisitor(qapi.QAPISchemaVisitor):
 
     def visit_event(self, name, info, arg_type, boxed):
         doc = self.cur_doc
-        if self.out:
-            self.out += '\n'
         self.out += MSG_FMT(type='Event',
                             name=doc.symbol,
                             body=texi_entity(doc, 'Arguments'))
 
     def symbol(self, doc, entity):
+        if self.out:
+            self.out += '\n'
         self.cur_doc = doc
         entity.visit(self)
         self.cur_doc = None
diff --git a/tests/Makefile.include b/tests/Makefile.include
index b4bcc872f2..f8e20d9f5d 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -416,6 +416,7 @@ qapi-schema += command-int.json
 qapi-schema += comments.json
 qapi-schema += doc-bad-alternate-member.json
 qapi-schema += doc-bad-command-arg.json
+qapi-schema += doc-bad-section.json
 qapi-schema += doc-bad-symbol.json
 qapi-schema += doc-bad-union-member.json
 qapi-schema += doc-before-include.json
@@ -433,10 +434,10 @@ qapi-schema += doc-invalid-end2.json
 qapi-schema += doc-invalid-return.json
 qapi-schema += doc-invalid-section.json
 qapi-schema += doc-invalid-start.json
-qapi-schema += doc-missing.json
 qapi-schema += doc-missing-colon.json
 qapi-schema += doc-missing-expr.json
 qapi-schema += doc-missing-space.json
+qapi-schema += doc-missing.json
 qapi-schema += doc-no-symbol.json
 qapi-schema += double-data.json
 qapi-schema += double-type.json
diff --git a/tests/qapi-schema/doc-bad-section.err b/tests/qapi-schema/doc-bad-section.err
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/doc-bad-section.err
diff --git a/tests/qapi-schema/doc-bad-section.exit b/tests/qapi-schema/doc-bad-section.exit
new file mode 100644
index 0000000000..573541ac97
--- /dev/null
+++ b/tests/qapi-schema/doc-bad-section.exit
@@ -0,0 +1 @@
+0
diff --git a/tests/qapi-schema/doc-bad-section.json b/tests/qapi-schema/doc-bad-section.json
new file mode 100644
index 0000000000..560df4b087
--- /dev/null
+++ b/tests/qapi-schema/doc-bad-section.json
@@ -0,0 +1,11 @@
+# = section within an expression comment
+# BUG: not rejected
+
+##
+# @Enum:
+# == Produces *invalid* texinfo
+# @one: The _one_ {and only}
+#
+# @two is undocumented
+##
+{ 'enum': 'Enum', 'data': [ 'one', 'two' ] }
diff --git a/tests/qapi-schema/doc-bad-section.out b/tests/qapi-schema/doc-bad-section.out
new file mode 100644
index 0000000000..089bde1381
--- /dev/null
+++ b/tests/qapi-schema/doc-bad-section.out
@@ -0,0 +1,13 @@
+enum Enum ['one', 'two']
+enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool']
+    prefix QTYPE
+object q_empty
+doc symbol=Enum
+    body=
+== Produces *invalid* texinfo
+    arg=one
+The _one_ {and only}
+    arg=two
+
+    section=None
+@two is undocumented
diff --git a/tests/qapi-schema/doc-good.json b/tests/qapi-schema/doc-good.json
index cfdc0a8a81..97ab4625ff 100644
--- a/tests/qapi-schema/doc-good.json
+++ b/tests/qapi-schema/doc-good.json
@@ -51,7 +51,6 @@
 
 ##
 # @Enum:
-# == Produces *invalid* texinfo
 # @one: The _one_ {and only}
 #
 # @two is undocumented
diff --git a/tests/qapi-schema/doc-good.out b/tests/qapi-schema/doc-good.out
index 63ca25a8b9..1d2c250527 100644
--- a/tests/qapi-schema/doc-good.out
+++ b/tests/qapi-schema/doc-good.out
@@ -77,12 +77,12 @@ Examples:
 - {braces}
 doc symbol=Enum
     body=
-== Produces *invalid* texinfo
+
     arg=one
 The _one_ {and only}
     arg=two
 
-    section=
+    section=None
 @two is undocumented
 doc symbol=Base
     body=
diff --git a/tests/qapi-schema/doc-good.texi b/tests/qapi-schema/doc-good.texi
index c410626e4a..1778312581 100644
--- a/tests/qapi-schema/doc-good.texi
+++ b/tests/qapi-schema/doc-good.texi
@@ -76,7 +76,7 @@ Examples:
 
 @deftp {Enum} Enum
 
-@subsection Produces @strong{invalid} texinfo
+
 
 @b{Values:}
 @table @asis
@@ -101,7 +101,6 @@ Not documented
 the first member
 @end table
 
-
 @end deftp
 
 
@@ -118,7 +117,6 @@ Another paragraph (but no @code{var}: line)
 Not documented
 @end table
 
-
 @end deftp
 
 
@@ -127,7 +125,6 @@ Not documented
 
 
 
-
 @end deftp
 
 
@@ -143,7 +140,6 @@ Not documented
 @item The members of @code{Variant2} when @code{base1} is @t{"two"}
 @end table
 
-
 @end deftp
 
 
@@ -160,7 +156,6 @@ One of @t{"one"}, @t{"two"}
 @item @code{data: Variant2} when @code{type} is @t{"two"}
 @end table
 
-
 @end deftp
 
 
@@ -182,7 +177,6 @@ argument
 Not documented
 @end table
 
-
 @b{Note:}
 @code{arg3} is undocumented
 
@@ -209,14 +203,12 @@ Duis aute irure dolor
 <- out
 @end example
 
-
 @b{Examples:}
 @example
 - *verbatim*
 - @{braces@}
 @end example
 
-
 @b{Since:}
 2.10
 
@@ -237,7 +229,6 @@ If you're bored enough to read this, go see a video of boxed cats
 <- out
 @end example
 
-
 @end deftypefn
 
 
diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py
index c7724d3437..fe0ca08d78 100644
--- a/tests/qapi-schema/test-qapi.py
+++ b/tests/qapi-schema/test-qapi.py
@@ -61,8 +61,8 @@ for doc in schema.docs:
         print 'doc symbol=%s' % doc.symbol
     else:
         print 'doc freeform'
-    print '    body=\n%s' % doc.body
+    print '    body=\n%s' % doc.body.text
     for arg, section in doc.args.iteritems():
-        print '    arg=%s\n%s' % (arg, section)
+        print '    arg=%s\n%s' % (arg, section.text)
     for section in doc.sections:
-        print '    section=%s\n%s' % (section.name, section)
+        print '    section=%s\n%s' % (section.name, section.text)